Code-as-Action Agent Framework — let LLMs write and execute Python code to accomplish tasks.
CodeAct replaces fine-grained ReAct tool suites with a single exec_python tool. Instead of giving an agent 10+ specific tools (search_db, get_stats, generate_chart, etc.), you give it one tool: write Python code. The LLM composes SQL + Python analysis in a single action, eliminating tool-selection hallucinations and enabling complex multi-step reasoning.
- Single universal tool —
exec_pythonreplaces an entire tool suite - Sandboxed execution — code runs in subprocess with configurable timeout
- SQLite guard — monkey-patched
sqlite3.connectprevents unauthorized DB access - Multi-provider support — OpenAI, DeepSeek, OpenRouter, and any OpenAI-compatible API
- Auto-failover — probes providers at startup, auto-switches on network errors
- Streaming & pseudo-streaming — native SSE for compatible providers, chunked emission for others
- Declarative charts —
<<<CHART:{...}>>>JSON markers for frontend rendering - Zero business logic — pure framework, bring your own database and instructions
"CodeAct outperforms widely used alternatives (JSON / text) with up to 20% higher success rate and up to 30% fewer interactions." — Wang et al., ICML 2024
Traditional LLM agents are prompted to produce actions as JSON or text in a pre-defined format. This approach suffers from two fundamental limitations:
| Problem | JSON/Text Tool Calling | CodeAct (this framework) |
|---|---|---|
| Constrained action space | You must pre-define every tool (search_db, get_stats, plot_chart...) |
One universal tool: exec_python. The LLM writes code to do anything |
| No composition | One action = one tool call. Chaining tools requires multiple turns | Control & data flow: variables, for loops, if statements. One action can compose multiple tools |
| Reinvent the wheel | Hand-craft wrappers for every new capability | Leverage existing software: import pandas, import sklearn. The entire Python ecosystem is your toolset |
| Debugging is hard | Silent failures or opaque errors | Self-debugging: execution errors (tracebacks) are fed back to the LLM as observations. It fixes its own code in multi-turn interactions |
| Unnatural for LLMs | JSON schema is an artificial format | Code is familiar: modern LLMs are pre-trained on massive code corpora. Python is their "native language" |
User asks: "For each product category, find the top-3 best-selling items and plot a bar chart."
With JSON/text tools, the agent needs ~6–8 turns: call list_categories → call get_sales (×N categories) → call rank_items (×N) → call generate_chart.
With CodeAct, the agent writes one Python script:
sales = query("SELECT category, item, SUM(quantity) as total FROM orders GROUP BY category, item")
# ... aggregate with pandas-like logic ...
# ... emit a single <<<CHART:...>>> markerOne action. One round-trip. Done.
pip install codeactOr from source:
git clone https://github.com/codeact-org/codeact.git
cd codeact
pip install -e ".[dev]"import asyncio
from codeact import CodeActAgent, CodeActConfig
config = CodeActConfig(
api_key="sk-your-api-key",
model="gpt-4o",
)
agent = CodeActAgent(
config=config,
instructions="You are a math assistant. Use exec_python to show your work.",
)
async def main():
reply = await agent.chat("What is the sum of the first 100 prime numbers?")
print(reply)
asyncio.run(main())from codeact import CodeActAgent, CodeActConfig, build_sqlite_preamble
config = CodeActConfig.from_env()
preamble = build_sqlite_preamble("/path/to/data.db")
agent = CodeActAgent(
config=config,
instructions="""You are a data analyst.
Use exec_python with the pre-injected query()/query_one()/show_table() helpers.
Base all answers on actual database queries.""",
preamble=preamble,
)
reply = await agent.chat("What are the top 10 products by revenue?")See examples/ for complete runnable examples.
User Query
↓
CodeActAgent.chat() / .chat_stream()
↓
AgentRunner.run() / .run_streamed()
↓
OpenAI Agents SDK Runner.run(agent, query, max_turns=40)
↓
┌─────────────────────────────────────────────┐
│ SDK-managed ReAct loop: │
│ LLM → exec_python(code) → subprocess.run() │
│ → stdout/stderr → LLM → ... → final answer │
└─────────────────────────────────────────────┘
↓
Chart markers extracted → clean text reply
| Module | Purpose |
|---|---|
codeact.agent.CodeActAgent |
High-level API: create, chat, stream |
codeact.executor.ExecPythonTool |
The exec_python tool — sandboxed subprocess execution |
codeact.executor.build_sqlite_preamble() |
Build a safe SQLite preamble with query helpers |
codeact.runner.AgentRunner |
Provider-aware streaming (native vs pseudo) |
codeact.provider.LLMService |
Multi-provider management with auto-probing & failover |
codeact.chart |
Extract <<<CHART:{...}>>> markers from agent output |
codeact.config.CodeActConfig |
Unified configuration |
| Variable | Default | Description |
|---|---|---|
CODEACT_API_KEY |
— | API key for the LLM provider |
CODEACT_BASE_URL |
https://api.openai.com/v1 |
Provider base URL |
CODEACT_MODEL |
gpt-4o |
Default model |
CODEACT_FAST_MODEL |
same as model | Model for fast tasks |
CODEACT_SLOW_MODEL |
same as model | Model for analysis tasks |
CODEACT_MAX_TURNS |
40 |
Max agent turns |
CODEACT_EXEC_TIMEOUT |
30 |
exec_python timeout (seconds) |
CODEACT_LOG_LEVEL |
INFO |
Logging level |
from codeact import CodeActConfig
config = CodeActConfig(
provider_name="deepseek",
api_key="sk-...",
base_url="https://api.deepseek.com",
model="deepseek-chat",
max_turns=20,
exec_timeout=30,
)| Mechanism | Description |
|---|---|
| Subprocess isolation | Agent code runs in python3 subprocess, not the main process |
| Timeout | Configurable timeout (default 30s) prevents runaway execution |
| SQLite guard | sqlite3.connect is monkey-patched to reject unauthorized databases |
| No write access | Preamble helpers only read; no INSERT/UPDATE/DELETE helpers |
| Temp file cleanup | Executed scripts are deleted in finally block |
| Output truncation | stdout/stderr truncated to prevent token overflow |
pytestSee CONTRIBUTING.md.
Licensed under the Apache License 2.0.
Copyright 2025 The CodeAct Authors.