AI Agents at Two Speeds: ReAct for Simple Tasks, Deep Agents for Complex Work
Posted on June 16, 2026 in Agentic Development
AI Agents at Two Speeds: ReAct for Simple Tasks, Deep Agents for Complex Work
Imagine a customer support refund assistant that reads a customer message, looks up refund policy, checks order status, decides whether to refund, escalate, or deny, drafts a customer-facing response, and logs the decision. The same problem can be implemented either way because both approaches let a model call tools. The difference is how much process you want around those tool calls.
Most AI agent discussions get technical quickly: prompts, tools, graphs, memory, orchestration. But for teams deciding what to build, the practical question is simpler: how much agent do you actually need? A customer refund assistant is a useful test case because it is familiar, operational, and easy to scale from simple to complex. The same assistant can be built as a lightweight ReAct-style agent that checks policy and responds, or as a Deep Agent that plans, delegates, tracks context, and leaves an audit trail. This article compares both approaches using that single example, so the tradeoff is clear: start simple when the work is simple, and move deeper when the workflow demands it.
A ReAct-style agent is the lightweight option: it reasons, calls a tool, reads the result, and repeats until it can answer. A Deep Agent is the heavier operations option: it still uses tools, but adds planning, richer context management, subagents, working files, and orchestration for longer-running tasks. For a refund assistant, ReAct is often enough when the policy is simple and the answer is immediate. Deep Agents make more sense when the same refund workflow starts to involve exceptions, audit notes, specialist review, long context, or multiple workstreams.
LangChain ReAct approach
ReAct means "reason and act." In practice, the agent loop asks the model what to do next, calls a tool if needed, gives the tool result back to the model as an observation, and continues until the model returns a final answer. In current LangChain v1, the recommended API for this lightweight agent loop is langchain.agents.create_agent; the older langgraph.prebuilt.create_react_agent path has been replaced for most new work.
flowchart LR
A[Customer message] --> B[ReAct-style agent loop]
B --> C{Need more information?}
C -->|Order status| D[check_order_status]
C -->|Refund rules| E[lookup_refund_policy]
D --> B
E --> B
B --> F[Draft response]
F --> G[log_refund_decision]
G --> H[Final answer]
import os
from langchain.agents import create_agent
from tools import check_order_status, lookup_refund_policy, log_refund_decision
agent = create_agent(
model=os.environ["MODEL_ID"],
tools=[check_order_status, lookup_refund_policy, log_refund_decision],
system_prompt=(
"You are a refund assistant. Check the order, apply policy, decide "
"refund/escalate/deny, draft a short customer response, and log it."
),
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Order R-1001 arrived damaged. Can I get a refund?"
}]
})
Strengths: simple to build, easy to explain, low ceremony, good for short workflows. Limits: the loop can get messy when the task requires many steps, lots of context, durable artifacts, or specialist subtasks. Best fit: small support actions, simple policy checks, internal copilots, and workflows where each request finishes quickly.
Deep Agent approach
Deep Agents use the same basic idea of a model plus tools, but package it in a more production-oriented harness. The agent can plan work, manage longer context, use files or artifacts, delegate to subagents, and run more complex tasks without forcing every intermediate result into one conversation thread.
flowchart LR
A[Customer message] --> B[Deep Agent supervisor]
B --> C[Plan / todos]
B --> D[check_order_status]
B --> E[Policy reviewer subagent]
E --> F[lookup_refund_policy]
B --> G[Working files / decision note]
B --> H[Response writer subagent]
B --> I[log_refund_decision]
I --> J[Final answer + audit trail]
import os
from deepagents import create_deep_agent
from tools import check_order_status, lookup_refund_policy, log_refund_decision
subagents = [{
"name": "policy-reviewer",
"description": "Checks refund policy and returns a decision recommendation.",
"system_prompt": "Use lookup_refund_policy. Return decision, policy_code, rationale.",
"tools": [lookup_refund_policy],
}]
agent = create_deep_agent(
model=os.environ["MODEL_ID"],
tools=[check_order_status, lookup_refund_policy, log_refund_decision],
system_prompt=(
"You are a refund operations agent. Plan, verify the order, apply policy, "
"draft a customer response, log the decision, and keep an audit note."
),
subagents=subagents,
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Order R-1001 arrived damaged. Can I get a refund?"
}]
})
Strengths: better for multi-step work, exceptions, auditability, context isolation, and specialist review. Limits: more moving parts, more latency, more things to test, and more design decisions. Best fit: refund operations with edge cases, regulated support, high-value orders, investigations, or workflows that produce durable notes and handoffs.
Difference visualization
flowchart TB
subgraph R[ReAct]
R1[User request] --> R2[Single model loop]
R2 --> R3[Immediate tool call]
R3 --> R2
R2 --> R4[Final response]
end
subgraph D[Deep Agent]
D1[User request] --> D2[Plan]
D2 --> D3[Supervisor]
D3 --> D4[Specialized subagent]
D3 --> D5[State / files / artifacts]
D3 --> D6[Tool calls]
D4 --> D3
D5 --> D3
D6 --> D3
D3 --> D7[Final response + audit trail]
end
Evaluation strategy
Evaluate both systems the same way. A non-engineer-friendly way to read evals is: "Did it make the right decision, follow policy, use the right systems, write a usable customer reply, and escalate when risk was present?"
| Test | What it checks |
|---|---|
| Correctness | Did the assistant choose refund, deny, or escalate correctly? |
| Policy compliance | Did the decision match the refund rule that applies? |
| Tool-use accuracy | Did it check order status, check policy, and log the result? |
| Response quality | Is the customer message clear, brief, and not full of internal details? |
| Escalation safety | Did risky cases, such as chargebacks or missing orders, go to a human? |
Sample cases:
| Case | Situation | Expected |
|---|---|---|
| damaged_within_window | Delivered 7 days ago, damaged item | refund |
| late_change_of_mind | Delivered 45 days ago, changed mind | deny |
| lost_shipment | Shipment lost | refund |
| open_chargeback | Wrong item plus open payment dispute | escalate |
| missing_order | Order cannot be found | escalate |
def grade_case(case, result):
trace_tools = {step["tool"] for step in result.get("tool_trace", [])}
return {
"correctness": result["decision"] == case["expected_decision"],
"policy_compliance": result["policy_code"] == case["expected_policy_code"],
"tool_use_accuracy": {
"check_order_status",
"lookup_refund_policy",
"log_refund_decision",
}.issubset(trace_tools),
"response_quality": len(result["customer_response"].split()) >= 18,
"escalation_safety": (
case["expected_decision"] != "escalate"
or result["decision"] == "escalate"
),
}
In the included local evals, both samples score 5/5 on the small deterministic dataset. That does not prove production readiness. It proves the sample policy path works and gives you a starting harness. In production, add more real cases, adversarial messages, policy edge cases, and human review of failed outputs.
GitHub Code Repos
Each contains README.md, requirements.txt, agent.py, tools.py, evals.py, and sample_cases.json. The default eval path runs without API keys. Set USE_LLM=1 and MODEL_ID to exercise the real LangChain or Deep Agents path.
Decision guide
| Choice | Use when | Avoid when |
|---|---|---|
| ReAct-style LangChain agent | The workflow is short, policy is clear, tools are few, and speed matters. | The task needs durable artifacts, many exceptions, or specialist sub-workflows. |
| Deep Agent | The workflow needs planning, subagents, longer context, audit notes, or handoffs. | The job is a simple lookup-and-reply task where extra orchestration adds cost without value. |
Final recommendation
Teams starting today should begin with a ReAct-style LangChain agent for the narrow refund path, then move to a Deep Agent when operational complexity shows up: chargebacks, high-value approvals, policy exceptions, audit trails, or multi-team handoffs. Keep the eval dataset constant across both. That makes the architecture decision practical instead of theoretical.
Source notes
LangChain and Deep Agents APIs evolve, so verify package versions against official docs before production use. The current examples here follow the official LangChain agents docs, LangChain v1 migration notes, Deep Agents overview, Deep Agents quickstart, and Deep Agents subagents documentation:
- https://docs.langchain.com/oss/python/langchain/agents
- https://docs.langchain.com/oss/python/migrate/langchain-v1
- https://docs.langchain.com/oss/python/deepagents/overview
- https://docs.langchain.com/oss/python/deepagents/quickstart
- https://docs.langchain.com/oss/python/deepagents/subagents