Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
lebsral avatar

Dspy React

  • 6 installs
  • 11 repo stars
  • Updated June 28, 2026
  • lebsral/dspy-programming-not-prompting-lms-skills

Helps with frontend development tasks.

About

dspy-react is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted coding.

  • dspy-react
  • Frontend Development
  • AI-coding skill

Dspy React by the numbers

  • 6 all-time installs (skills.sh)
  • Ranked #1,782 of 2,245 Frontend Development 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-react

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs6
repo stars11
Last updatedJune 28, 2026
Repositorylebsral/dspy-programming-not-prompting-lms-skills

What it does

Helps with frontend development tasks.

Files

SKILL.mdMarkdownGitHub ↗

Build Tool-Using Agents with dspy.ReAct

Guide the user through building agents that reason step-by-step and call tools to accomplish tasks. dspy.ReAct implements the Reasoning-Action-Observation loop -- the agent thinks about what to do, calls a tool, observes the result, and repeats until it has an answer.

What is ReAct

dspy.ReAct implements the Reasoning-Action-Observation loop as an optimizable module. The agent reasons about what to do, calls a tool, observes the result, and repeats until it has enough information to answer. DSPy handles the loop mechanics and prompt construction.

When to use ReAct

Use ReAct when...Use something else when...
The agent needs to call external tools (search, APIs, databases)You just need input -> output with no tools (dspy.ChainOfThought)
Multi-step reasoning with real-world dataThe task is purely computational / code-heavy (dspy.CodeAct)
You want the agent to decide which tools to call and in what orderYou have a fixed pipeline of steps (dspy.Module with sub-modules)
You need an interpretable trace of reasoning + actionsYou need agents coordinating with each other (see /ai-coordinating-agents)

Defining tools

Tools are Python functions with type hints and docstrings. DSPy uses the function signature and docstring to tell the agent what each tool does and how to call it.

def search(query: str) -> str:
    """Search the web for information about a topic."""
    # Your search implementation here
    return "search results..."

def calculate(expression: str) -> float:
    """Evaluate a mathematical expression and return the result."""
    return eval(expression)  # use a safe evaluator in production

def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    # Your weather API call here
    return f"72°F and sunny in {city}"

Tool requirements:

  • Type hints on all parameters and the return type -- DSPy uses these to generate the tool schema
  • Docstring explaining what the tool does -- the agent reads this to decide when to use it
  • Return a string (or something that converts to string) -- the result becomes the Observation

Keep tools focused on one thing. A search tool should search, not search-and-summarize.

Basic ReAct agent

import dspy

lm = dspy.LM("openai/gpt-4o-mini")  # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)

def search(query: str) -> str:
    """Search for information about a topic."""
    return "DSPy is a framework for programming language models."

agent = dspy.ReAct("question -> answer", tools=[search])
result = agent(question="What is DSPy?")
print(result.answer)

That's it. The agent will: 1. Read the question 2. Decide whether to call search 3. Use the search result to formulate an answer

Constructor parameters

dspy.ReAct(
    signature,      # str | Signature -- required, defines inputs/outputs
    tools,          # list[Callable | dspy.Tool] -- required, available tools
    max_iters=20,   # int -- max reasoning-action cycles
)
ParameterTypeDefaultDescription
signature`str \type[Signature]`required
tools`list[Callable \dspy.Tool]`required
max_itersint20Max Thought-Action-Observation cycles before forcing an answer

The max_iters parameter

max_iters controls how many Thought-Action-Observation cycles the agent can take before it must produce an answer:

# Simple lookup -- 1-2 tool calls usually enough
agent = dspy.ReAct("question -> answer", tools=[search], max_iters=3)

# Complex research -- may need many tool calls
agent = dspy.ReAct("question -> answer", tools=[search, lookup], max_iters=8)

Guidelines:

  • Default is 20 -- usually fine for most tasks
  • Set it lower (2-5) for simple lookups where one or two tool calls suffice
  • Keep it at 20 for complex multi-step research tasks
  • If the agent hits max_iters without finishing, it returns its best answer so far

Multi-tool agents

Give the agent multiple tools and it decides which to use and when:

import dspy

def search(query: str) -> str:
    """Search the web for general information."""
    return "search results..."

def lookup_user(email: str) -> str:
    """Look up a user account by email address."""
    return '{"name": "Alice", "plan": "pro", "status": "active"}'

def check_order(order_id: str) -> str:
    """Check the status of an order by its ID."""
    return '{"order_id": "12345", "status": "shipped", "eta": "March 20"}'

agent = dspy.ReAct(
    "question -> answer",
    tools=[search, lookup_user, check_order],
    max_iters=5,
)

# The agent picks the right tool based on the question
result = agent(question="What's the status of order 12345?")
print(result.answer)  # Uses check_order

result = agent(question="What plan is alice@example.com on?")
print(result.answer)  # Uses lookup_user

The agent can also chain tools -- call lookup_user first, then use the result to call check_order.

Wrapping ReAct in a custom module

For production use, wrap dspy.ReAct inside a dspy.Module to add pre-processing, context, or post-processing:

class SupportAgent(dspy.Module):
    def __init__(self):
        self.agent = dspy.ReAct(
            "question, context -> answer",
            tools=[search, lookup_user, check_order],
            max_iters=6,
        )

    def forward(self, question):
        context = (
            "You are a customer support agent. "
            "Use lookup_user for account questions, "
            "check_order for order questions, "
            "and search for general questions."
        )
        return self.agent(question=question, context=context)

def support_reward(args, pred):
    if len(pred.answer) > 20:
        return 1.0
    return 0.0  # Response too short — not detailed enough

validated_support = dspy.Refine(
    module=SupportAgent(),
    N=3,
    reward_fn=support_reward,
    threshold=1.0,
)

This pattern lets you:

  • Pass extra context or instructions to the agent
  • Add reward-based quality constraints with dspy.Refine
  • Optimize the agent with DSPy optimizers (they tune the inner ReAct module)
  • Save and load the optimized state

Using class-based signatures

For agents with typed inputs and outputs, use a class-based signature:

from typing import Literal

class ResearchTask(dspy.Signature):
    """Research a topic and provide a comprehensive answer with sources."""
    question: str = dspy.InputField(desc="The research question")
    answer: str = dspy.OutputField(desc="A thorough answer to the question")
    confidence: Literal["high", "medium", "low"] = dspy.OutputField(
        desc="Confidence level based on the sources found"
    )

agent = dspy.ReAct(ResearchTask, tools=[search], max_iters=5)
result = agent(question="What are the main features of DSPy?")
print(result.answer)
print(result.confidence)

ReAct vs CodeAct

Both are agent modules, but they act differently:

ReActCodeAct
How it actsCalls tools by name with argumentsWrites and executes Python code
Best forAPI calls, database lookups, searchData manipulation, calculations, file I/O
InterpretabilityClear tool call traceFull code trace
Tool styleFunction callsPython expressions
Use whenYou have specific tools to callThe task is better solved by writing code
# ReAct -- calls tools
agent = dspy.ReAct("question -> answer", tools=[search, calculate])

# CodeAct -- writes code
agent = dspy.CodeAct("question -> answer", tools=[search, calculate])

If you're unsure, start with ReAct. Switch to CodeAct if the agent needs to do math, string manipulation, or data transformations between tool calls.

Error handling

Tools can fail. Handle errors inside your tools so the agent gets a useful message instead of a crash:

import requests

def search(query: str) -> str:
    """Search the web for information."""
    try:
        response = requests.get(
            "https://api.example.com/search",
            params={"q": query},
            timeout=5,
        )
        response.raise_for_status()
        return response.json()["results"]
    except requests.Timeout:
        return "Error: Search timed out. Try a simpler query."
    except requests.HTTPError as e:
        return f"Error: Search failed with status {e.response.status_code}."
    except Exception as e:
        return f"Error: {str(e)}"

When a tool returns an error string, the agent sees it as an Observation and can decide to retry with different arguments, try a different tool, or give a partial answer.

For module-level error handling, wrap the agent call:

class SafeAgent(dspy.Module):
    def __init__(self):
        self.agent = dspy.ReAct("question -> answer", tools=[search], max_iters=5)
        self.fallback = dspy.ChainOfThought("question -> answer")

    def forward(self, question):
        try:
            return self.agent(question=question)
        except Exception:
            # Fall back to answering without tools
            return self.fallback(question=question)

Optimizing ReAct agents

ReAct agents are optimizable like any DSPy module. The optimizer tunes the reasoning prompts so the agent makes better tool-calling decisions:

def answer_metric(example, prediction, trace=None):
    return prediction.answer.strip().lower() == example.answer.strip().lower()

# BootstrapFewShot for quick optimization
optimizer = dspy.BootstrapFewShot(metric=answer_metric, max_bootstrapped_demos=4)
optimized_agent = optimizer.compile(agent, trainset=trainset)

# MIPROv2 for better prompt optimization
optimizer = dspy.MIPROv2(metric=answer_metric, auto="medium")
optimized_agent = optimizer.compile(agent, trainset=trainset)

# Save and load
optimized_agent.save("optimized_agent.json")

Debugging

Inspect what the agent is doing:

# See the last few LM calls (thoughts, tool calls, observations)
dspy.inspect_history(n=5)

# Print the module structure
print(agent)

inspect_history shows you the full Thought-Action-Observation trace, which is invaluable for understanding why the agent called certain tools or gave a wrong answer.

Gotchas

1. Claude sets `max_iters=5` but the default is 20. Claude habitually passes max_iters=5 which cuts off complex multi-step tasks too early. The actual default is 20. Only lower it when you want to constrain simple tasks (2-3 for lookups). For complex research, the default of 20 is appropriate. 2. Tool errors are passed back as observations -- make your error messages informative so the agent can recover (e.g., "No user found with that email" not just "Error"). 3. ReAct is slow by design -- each iteration is a separate LM call. Use CodeAct for computation-heavy tasks where the agent can do work in code between tool calls. 4. Tool function docstrings become part of the prompt -- write clear, concise docstrings. Verbose docstrings waste tokens every iteration. 5. Claude ignores the `trajectory` in the return value. ReAct returns a dspy.Prediction with a .trajectory dict containing the full Thought-Action-Observation trace. Access result.trajectory to log or debug the agent's reasoning path. Claude often discards this and only uses result.answer.

Additional resources

Cross-references

Install any skill: npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill <name>
  • Defining tools in detail -- see /dspy-tools
  • CodeAct for code-based agents -- see /dspy-codeact
  • Building custom modules to wrap ReAct -- see /dspy-modules
  • Action-taking AI from a problem-first perspective -- see /ai-taking-actions
  • Multi-agent coordination -- see /ai-coordinating-agents
  • 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

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.