Skip to content

zzszmyf/codeact

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CodeAct

Python 3.10+ License: Apache-2.0

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.

✨ Features

  • Single universal toolexec_python replaces an entire tool suite
  • Sandboxed execution — code runs in subprocess with configurable timeout
  • SQLite guard — monkey-patched sqlite3.connect prevents 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

🤔 Why CodeAct?

"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"

Concrete Example

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:...>>> marker

One action. One round-trip. Done.


🚀 Quick Start

Installation

pip install codeact

Or from source:

git clone https://github.com/codeact-org/codeact.git
cd codeact
pip install -e ".[dev]"

Basic Usage

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())

SQLite Analyst

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.

🏗️ Architecture

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

Key Components

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

⚙️ Configuration

Environment Variables

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

Programmatic Config

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,
)

🔒 Security

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

🧪 Testing

pytest

🤝 Contributing

See CONTRIBUTING.md.

📄 License

Licensed under the Apache License 2.0.

Copyright 2025 The CodeAct Authors.

About

A Code-as-Action agent framework built on OpenAI Agents SDK

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages