
Dspy Codeact
- 5 installs
- 11 repo stars
- Updated June 28, 2026
- lebsral/dspy-programming-not-prompting-lms-skills
Helps with ai & agent building tasks.
About
dspy-codeact is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- dspy-codeact
- AI & Agent Building
- AI-coding skill
Dspy Codeact by the numbers
- 5 all-time installs (skills.sh)
- Ranked #13,065 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lebsral/dspy-programming-not-prompting-lms-skills --skill dspy-codeactAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 11 |
| Last updated | June 28, 2026 |
| Repository | lebsral/dspy-programming-not-prompting-lms-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Build Agents That Write and Execute Code with dspy.CodeAct
Guide the user through building DSPy agents that solve problems by generating and running Python code, rather than calling tools through a fixed interface.
What is CodeAct
dspy.CodeAct is a DSPy module that creates agents which write Python code to accomplish tasks. Instead of selecting from a predefined set of tool calls (like ReAct), CodeAct generates executable code snippets that use provided tools as Python functions.
The agent works in a loop:
1. Generate -- the LM writes a Python code snippet using the available tools 2. Execute -- the code runs in a sandboxed interpreter 3. Observe -- the agent sees the output and decides whether the task is done 4. Repeat -- if not done, writes more code incorporating previous results
CodeAct inherits from both ReAct and ProgramOfThought, combining reasoning-and-acting with code generation.
How CodeAct differs from ReAct
| ReAct | CodeAct | |
|---|---|---|
| How it acts | Calls tools by name with arguments | Writes Python code that calls tools |
| Composition | One tool call per step | Can chain multiple tool calls, use loops, variables, conditionals in a single step |
| Data manipulation | Limited to what tools return | Can transform, filter, aggregate data in code |
| Best for | Simple tool orchestration | Complex computation, data processing, multi-step logic |
| Overhead | Lower -- just picks a tool | Higher -- generates and executes code |
Rule of thumb: If your agent needs to do math, transform data, or chain several operations together, CodeAct is a better fit. If it just needs to look things up and combine results, ReAct is simpler.
When to use CodeAct
Use CodeAct when the agent needs to:
- Do computation -- math, aggregations, statistics, string processing
- Transform data -- reshape, filter, combine results from multiple tool calls
- Write multi-step logic -- loops, conditionals, variable assignment between steps
- Solve problems programmatically -- tasks where the approach itself needs to be figured out
Avoid CodeAct when:
- Simple tool calls are sufficient (use ReAct instead)
- You need tight control over exactly which tools are called and in what order
- The execution environment cannot support sandboxed code execution
- You need to use external libraries like numpy or pandas inside tools (CodeAct tools cannot import external libraries)
Basic usage
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
# Define tools as pure functions with type hints and docstrings
def factorial(n: int) -> int:
"""Calculate the factorial of n."""
if n <= 1:
return 1
return n * factorial(n - 1)
def fibonacci(n: int) -> int:
"""Return the nth Fibonacci number."""
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
# Create the CodeAct agent
agent = dspy.CodeAct(
"question -> answer",
tools=[factorial, fibonacci],
max_iters=5,
)
result = agent(question="What is the factorial of 10 plus the 15th Fibonacci number?")
print(result.answer)Constructor parameters
dspy.CodeAct(
signature, # str or dspy.Signature -- defines input/output fields
tools, # list[callable] -- pure functions the agent can call in code
max_iters=5, # int -- max generate-execute cycles before stopping
interpreter=None, # PythonInterpreter or None -- custom interpreter (creates one if None)
)Parameter details
- `signature` -- same as any DSPy module. Defines what the agent receives and what it should produce.
- `tools` -- a list of Python functions. Must be pure functions (not callable objects or class instances). All dependencies must be self-contained within each function -- tools cannot import external libraries or reference outside state.
- `max_iters` -- safety limit on how many code-generation-and-execution cycles the agent runs. Default is 5. Increase for complex multi-step tasks, decrease for simple ones.
- `interpreter` -- optionally pass a pre-configured
dspy.PythonInterpreter. IfNone, CodeAct creates a fresh one. The interpreter runs code in a sandboxed Deno-based environment.
Tool requirements
CodeAct is strict about what tools it accepts:
# OK -- pure function
def search(query: str) -> str:
"""Search for information."""
return "results..."
# OK -- function with multiple parameters
def lookup(table: str, key: str) -> str:
"""Look up a value in a table."""
return "value..."
# NOT OK -- callable object (will be rejected)
class MyTool:
def __call__(self, query: str) -> str:
return "results..."
# NOT OK -- tool that imports external libraries
def analyze(data: str) -> str:
"""Analyze data."""
import pandas as pd # This will fail in the sandbox
return str(pd.read_csv(data))Rules for tools:
1. Must be plain functions (not callable objects, not class methods) 2. Must have type hints and a docstring 3. Cannot import external libraries (numpy, pandas, requests, etc.) inside the function body 4. All logic must be self-contained -- no references to external classes or global state 5. Dependencies must be explicitly passed as tools if the agent needs them
Code execution environment
CodeAct runs generated code in a sandboxed Deno-based Python interpreter, not your system's Python. This means:
- Isolation -- code cannot access your filesystem, network, or environment variables
- No external imports -- standard library only within generated code; no pip packages
- Tool access -- the agent calls your tool functions, which execute in your normal Python environment. Only the glue code between tool calls runs in the sandbox.
- State persistence -- variables persist across iterations within a single agent call, so the agent can build up results incrementally
The sandbox provides security boundaries, but your tool functions themselves run in your normal Python process. If a tool accesses a database or API, that access is real.
Safety considerations
1. Tool functions are the trust boundary. The sandbox constrains the generated glue code, but tool functions execute with full privileges. Keep tool functions minimal and validate their inputs.
2. Set `max_iters` appropriately. A runaway agent burns tokens. Start with max_iters=5 and increase only if you see the agent running out of steps on legitimate tasks.
3. Validate outputs. Use dspy.Refine as a wrapper to check that the agent's answer meets your requirements via a reward function.
4. Don't expose dangerous operations as tools. If you pass a tool that deletes files or sends emails, the agent can and will call it. Only expose tools you're comfortable with the agent using autonomously.
class SafeCodeAgent(dspy.Module):
def __init__(self, tools):
self.agent = dspy.CodeAct(
"task -> result",
tools=tools,
max_iters=5,
)
def forward(self, task):
return self.agent(task=task)
def non_empty_reward(args, pred):
if len(pred.result.strip()) > 0:
return 1.0
return 0.0 # Agent must produce a non-empty result
validated_agent = dspy.Refine(
module=SafeCodeAgent(tools=[]),
N=3,
reward_fn=non_empty_reward,
threshold=1.0,
)Using CodeAct inside a custom module
Wrap CodeAct in a dspy.Module to add pre-processing, post-processing, or combine it with other DSPy modules:
class AnalysisAgent(dspy.Module):
def __init__(self):
self.planner = dspy.ChainOfThought("task -> plan")
self.executor = dspy.CodeAct(
"task, plan -> result",
tools=[compute_stats, format_table],
max_iters=8,
)
self.summarize = dspy.ChainOfThought("task, result -> summary")
def forward(self, task):
plan = self.planner(task=task)
execution = self.executor(task=task, plan=plan.plan)
return self.summarize(task=task, result=execution.result)Optimizing CodeAct agents
CodeAct agents are optimizable like any DSPy module:
def task_metric(example, prediction, trace=None):
return prediction.answer.strip() == example.answer.strip()
# BootstrapFewShot works well -- the agent learns from successful code traces
optimizer = dspy.BootstrapFewShot(metric=task_metric, max_bootstrapped_demos=4)
optimized = optimizer.compile(agent, trainset=trainset)
# MIPROv2 can also tune the instructions for code generation
optimizer = dspy.MIPROv2(metric=task_metric, auto="medium")
optimized = optimizer.compile(agent, trainset=trainset)
# Save and load
optimized.save("optimized_codeact.json")When to use CodeAct vs ReAct
| Scenario | Use |
|---|---|
| Look up facts and combine them | ReAct |
| Calculate, aggregate, or transform data | CodeAct |
| Simple API calls (weather, stock price) | ReAct |
| Multi-step data processing pipeline | CodeAct |
| Tasks where approach varies per input | CodeAct |
| Quick prototype with many tools | ReAct |
| Math-heavy or logic-heavy problems | CodeAct |
| Tasks needing external libraries in tools | ReAct (more flexible tool format) |
Gotchas
- Claude passes callable objects or class methods as tools. CodeAct only accepts plain functions — not callable objects (
__call__), not bound methods, not lambdas. If you need to wrap state, define a closure that captures the state and pass the inner function. - Claude writes tool functions that import external libraries. Tools execute in your Python process, but the glue code between tool calls runs in the Deno sandbox which has no pip packages. If the LM-generated code tries to
import pandasbetween tool calls, it fails. Move all library usage inside the tool function itself, not in the generated code. - Claude forgets to pass dependent functions as tools. If tool A calls helper function B internally, B runs fine (it executes in your Python process). But if the agent needs to call B directly in generated code, B must be in the
toolslist. Claude often defines helper functions but forgets to register them. - Claude uses CodeAct for simple lookup tasks where ReAct is better. CodeAct adds overhead — code generation, sandbox execution, iteration. For tasks that just call one or two tools and combine results, ReAct is simpler and faster. Reserve CodeAct for computation, data transformation, and multi-step logic.
- Claude sets `max_iters` too low for complex tasks. The default
max_iters=5is fine for simple computation, but data analysis tasks that fetch multiple sources and process results often need 8-10 iterations. Watch for "max iterations reached" and increase accordingly.
Additional resources
- CodeAct API docs
- reference.md — constructor parameters, tool requirements, execution model
- examples.md — data analysis, file processing, math agents, optimization
Cross-references
Install any skill: npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill <name>- ReAct for tool-calling agents -- see
/ai-taking-actions - Tools and tool patterns -- see
/ai-taking-actions - Multi-agent coordination -- see
/ai-coordinating-agents - Modules for composing CodeAct with other modules -- see
/dspy-modules - Signatures for defining agent inputs/outputs -- see
/dspy-signatures - For worked examples, see examples.md
- Install `/ai-do` if you do not have it — it routes any AI problem to the right skill and is the fastest way to work:
npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill ai-do
[
{
"prompt": "I need an AI agent that can analyze sales data by writing Python code — fetching quarterly numbers, computing averages, and finding trends. How do I build this with DSPy?",
"expected_output": "Uses dspy.CodeAct with tool functions for data access and lets the agent write computation code",
"assertions": [
"Uses dspy.CodeAct (not dspy.ReAct) since the task involves computation and data transformation",
"Defines tool functions as plain functions with type hints and docstrings",
"Does NOT pass callable objects or class instances as tools",
"Sets max_iters appropriately (5-10 for data analysis tasks)"
]
},
{
"prompt": "What is the difference between CodeAct and ReAct? When should I use each one?",
"expected_output": "Explains that CodeAct writes and executes code while ReAct makes tool calls, with guidance on when each is better",
"assertions": [
"Explains CodeAct writes Python code while ReAct selects tools by name",
"Recommends CodeAct for computation, data transformation, and multi-step logic",
"Recommends ReAct for simple tool orchestration and lookup tasks",
"Notes CodeAct tool restrictions (plain functions only, no external library imports in generated code)"
]
},
{
"prompt": "My CodeAct agent keeps failing because the generated code tries to import pandas. How do I fix this?",
"expected_output": "Explains the sandbox limitation and suggests moving library usage inside tool functions",
"assertions": [
"Explains that generated code runs in a sandbox with no pip packages",
"Suggests moving pandas logic inside a tool function (which runs in normal Python)",
"Does NOT suggest installing packages in the sandbox",
"Notes that tool functions themselves can use any library since they run in the host Python process"
]
}
]
CodeAct Examples
Data Analysis Agent
An agent that answers questions about data by writing computation code:
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
# Simulate a data source -- in production, this could query a database or API
SALES_DATA = {
"Q1": {"revenue": 150000, "units": 1200, "returns": 45},
"Q2": {"revenue": 210000, "units": 1800, "returns": 62},
"Q3": {"revenue": 185000, "units": 1500, "returns": 38},
"Q4": {"revenue": 290000, "units": 2400, "returns": 71},
}
def get_quarterly_data(quarter: str) -> str:
"""Get sales data for a specific quarter (Q1, Q2, Q3, or Q4).
Returns revenue, units sold, and number of returns.
"""
if quarter not in SALES_DATA:
return f"No data for {quarter}. Valid quarters: Q1, Q2, Q3, Q4"
data = SALES_DATA[quarter]
return f"Revenue: ${data['revenue']}, Units: {data['units']}, Returns: {data['returns']}"
def get_all_quarters() -> str:
"""List all available quarters."""
return ", ".join(SALES_DATA.keys())
agent = dspy.CodeAct(
"question -> answer",
tools=[get_quarterly_data, get_all_quarters],
max_iters=8,
)
# The agent writes code to fetch each quarter, compute averages, find trends
result = agent(
question="What was the average revenue per unit across all quarters, "
"and which quarter had the best ratio?"
)
print(result.answer)
# Another query -- the agent figures out the computation approach
result = agent(
question="What is the total return rate (returns/units) for the year, "
"and how does Q3 compare to the yearly average?"
)
print(result.answer)Why CodeAct fits here: the agent needs to fetch data from multiple quarters, do arithmetic across them, and compare ratios. Writing code to loop, divide, and compare is more natural than making individual tool calls.
File Processing Agent
An agent that processes structured text content:
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
# Simulated file store -- in production, read from disk or object storage
FILE_STORE = {
"employees.csv": "name,department,salary\nAlice,Engineering,120000\nBob,Marketing,95000\nCarol,Engineering,135000\nDave,Marketing,88000\nEve,Engineering,110000",
"config.txt": "max_retries=3\ntimeout=30\nlog_level=INFO\napi_url=https://api.example.com",
"report.txt": "Monthly Report\n\nTotal sales: 45000\nNew customers: 120\nChurn rate: 2.3%\nNPS score: 72",
}
def read_file(filename: str) -> str:
"""Read the contents of a file. Returns the full text content."""
if filename not in FILE_STORE:
available = ", ".join(FILE_STORE.keys())
return f"File not found: {filename}. Available files: {available}"
return FILE_STORE[filename]
def list_files() -> str:
"""List all available files."""
return "\n".join(f"- {name}" for name in FILE_STORE.keys())
def write_result(filename: str, content: str) -> str:
"""Write processed content to a result file."""
FILE_STORE[filename] = content
return f"Wrote {len(content)} characters to {filename}"
agent = dspy.CodeAct(
"task -> result",
tools=[read_file, list_files, write_result],
max_iters=8,
)
# The agent reads the CSV, parses it in code, and computes the answer
result = agent(
task="Read employees.csv and calculate the average salary per department. "
"Write the results to summary.txt."
)
print(result.result)
print("Generated file:", FILE_STORE.get("summary.txt", "not created"))Why CodeAct fits here: parsing CSV data, splitting strings, aggregating by group, and formatting output are all natural code operations. ReAct would struggle to do this with just tool calls.
Math and Computation Agent
An agent that solves math problems by writing code to work through them:
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
def is_prime(n: int) -> bool:
"""Check if a number is prime."""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
def gcd(a: int, b: int) -> int:
"""Calculate the greatest common divisor of two numbers."""
while b:
a, b = b, a % b
return a
def factorial(n: int) -> int:
"""Calculate the factorial of n."""
if n <= 1:
return 1
result = 1
for i in range(2, n + 1):
result *= i
return result
agent = dspy.CodeAct(
"problem -> answer",
tools=[is_prime, gcd, factorial],
max_iters=6,
)
# The agent writes code to iterate and test primes
result = agent(
problem="Find the sum of all prime numbers between 1 and 50."
)
print(f"Sum of primes 1-50: {result.answer}")
# Multi-step computation
result = agent(
problem="What is the GCD of factorial(8) and factorial(6)? "
"Express the answer as a product of prime factors."
)
print(f"Answer: {result.answer}")
# Complex logic the agent figures out on its own
result = agent(
problem="Find the smallest number greater than 100 that is prime "
"and whose digits sum to a prime number."
)
print(f"Answer: {result.answer}")Why CodeAct fits here: math problems often require writing loops, conditionals, and combining multiple operations. The agent can write a search loop to find numbers meeting complex criteria -- something that would be awkward with individual tool calls.
Wrapping CodeAct in a Module with Safety Checks
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
def compute(expression: str) -> str:
"""Evaluate a mathematical expression and return the result as a string."""
try:
result = eval(expression, {"__builtins__": {}})
return str(result)
except Exception as e:
return f"Error: {e}"
class MathAgent(dspy.Module):
def __init__(self):
self.agent = dspy.CodeAct(
"problem -> answer: str",
tools=[compute],
max_iters=5,
)
def forward(self, problem):
return self.agent(problem=problem)
def math_answer_reward(args, pred):
"""Hard constraint: answer must be non-empty. Soft constraint: no error messages."""
if not pred.answer.strip():
return 0.0
score = 1.0
if "error" in pred.answer.lower():
score -= 0.3
return score
agent = dspy.Refine(module=MathAgent(), N=3, reward_fn=math_answer_reward, threshold=0.7)
result = agent(problem="What is 2^10 + 3^7?")
print(result.answer)Optimizing a CodeAct Agent
import dspy
from dspy.evaluate import Evaluate
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
def is_prime(n: int) -> bool:
"""Check if a number is prime."""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
agent = dspy.CodeAct("problem -> answer", tools=[is_prime], max_iters=5)
# Training data
trainset = [
dspy.Example(
problem="How many prime numbers are between 10 and 30?",
answer="6",
).with_inputs("problem"),
dspy.Example(
problem="What is the sum of the first 5 prime numbers?",
answer="28",
).with_inputs("problem"),
dspy.Example(
problem="Is 97 prime? Answer yes or no.",
answer="yes",
).with_inputs("problem"),
# Add more examples for better optimization...
]
def metric(example, prediction, trace=None):
return prediction.answer.strip().lower() == example.answer.strip().lower()
# Optimize
optimizer = dspy.BootstrapFewShot(metric=metric, max_bootstrapped_demos=3)
optimized = optimizer.compile(agent, trainset=trainset)
# Evaluate
evaluator = Evaluate(devset=trainset, metric=metric, num_threads=2)
score = evaluator(optimized)
print(f"Score: {score}")
# Save for production
optimized.save("math_codeact_agent.json")Condensed from dspy.ai/api/modules/CodeAct/. Verify against upstream for latest.
dspy.CodeAct — API Reference
Constructor
dspy.CodeAct(
signature, # str | type[Signature] (required)
tools, # list[Callable] (required)
max_iters=5, # int
interpreter=None, # PythonInterpreter | None
)| Parameter | Type | Default | Description |
|---|---|---|---|
signature | `str | type[Signature]` | required |
tools | list[Callable] | required | Pure functions the agent can call in generated code. Must be plain functions — not callable objects, class methods, or lambdas. |
max_iters | int | 5 | Maximum generate-execute cycles before stopping. |
interpreter | `PythonInterpreter | None` | None |
Inheritance
CodeAct inherits from both ReAct and ProgramOfThought, combining tool-based agentic reasoning with code generation.
Key methods
All standard dspy.Module methods are available:
| Method | Signature | Description |
|---|---|---|
__call__ | agent(**inputs) | Run the agent on the given inputs. Returns a Prediction. |
batch | agent.batch(examples, ...) | Run on multiple inputs in parallel. |
save | agent.save(path) | Save optimized agent to JSON. |
load | agent.load(path) | Load a previously saved agent. |
set_lm | agent.set_lm(lm) | Override the LM for this module. |
Tool requirements
1. Must be plain functions — not callable objects (__call__), not bound methods, not lambdas 2. Must have type hints and a docstring (the agent reads these to understand how to call them) 3. Cannot import external libraries (numpy, pandas, requests, etc.) in the generated glue code — but tool functions themselves run in your normal Python process and can use any library 4. All logic must be self-contained — no references to external classes or global state from generated code 5. Dependent functions must be explicitly passed as tools if the agent needs to call them directly
Execution model
1. Generate — the LM writes a Python code snippet using the available tools as functions 2. Execute — code runs in a sandboxed Deno-based Python interpreter (not your system Python) 3. Observe — the agent sees stdout/stderr output 4. Repeat — if not done, writes more code incorporating previous results (up to max_iters)
Sandbox constraints
- No filesystem, network, or environment variable access from generated code
- Standard library only in generated code — no pip packages
- Tool functions execute in your normal Python process with full privileges
- Variables persist across iterations within a single agent call
PythonInterpreter
dspy.PythonInterpreter()The sandboxed code executor used by CodeAct. Created automatically if not provided. Uses Deno under the hood for isolation.