Advanced Agent Topics
An advanced agent is not simply a collection of models. The useful upgrade is to leave uncertain judgement to the model while code owns permissions, state, budgets, and acceptance. When the task path is known in advance, a deterministic workflow is usually cheaper, faster, and easier to debug.
Decide whether the task needs an agent
| Task characteristic | Recommended structure | Reason |
|---|---|---|
| Fixed steps, clear rules, predictable failures | Code or deterministic workflow | Maximum control; the model handles only ambiguous nodes |
| Inputs fall into stable categories | Router plus specialist flows | Each class gets its own tools, prompt, and evals |
| Subtasks are independent | Parallel or MapReduce | Reduce total latency and aggregate once |
| Output needs repeated generation and review | Evaluator–Optimizer | A rubric drives bounded iteration |
| The path cannot be enumerated before execution | Agent loop | The model chooses tools from environmental feedback |
Anthropic distinguishes workflows, where code defines the path, from agents, where the model directs the process. Start with the simplest structure and add autonomy only when real tasks show fixed paths are insufficient.
ordinary function → one LLM call → workflow → tool-using agent → manager + specialists
Every step to the right adds capability, cost, latency, and failure surface.
Seven orchestration patterns
| Pattern | Data path | Good fit | Main risk |
|---|---|---|---|
| Prompt Chaining | A → B → C | Document pipelines and staged review | Upstream errors propagate |
| Routing | Classify → specialist branch | Support, tickets, and content triage | Silent failure after misrouting |
| Parallelization | A, B, and C run together | Multi-source research and independent checks | Conflicting or duplicated results |
| Orchestrator–Workers | Manager → workers → synthesis | Dynamic numbers of subtasks | Manager over-decomposes work |
| Evaluator–Optimizer | Generate → score → revise | Deliverables with a clear rubric | Unbounded optimisation loops |
| Handoff | Current agent → specialist | Conversational specialist ownership | Lost context and responsibility |
| Autonomous Loop | Observe → decide → act → observe | Open environments and unknown paths | Uncontrolled cost and side effects |
Do not make multi-agent architecture the default. If one agent can use a small, well-defined tool set, splitting the job into five roles creates more handoffs, duplicated context, and ambiguous failures.
Model messages are not the run database
Persist an explicit production run:
type AgentRun = {
runId: string;
taskId: string;
status: 'queued' | 'running' | 'waiting_approval' | 'verified' | 'failed';
objective: string;
step: number;
maxSteps: number;
budget: { maxTokens: number; maxCostUsd: number; deadlineMs: number };
observations: ObservationRef[];
pendingAction?: ProposedAction;
idempotencyKeys: string[];
finalEvidence?: EvidenceRef[];
};
Store references and compact summaries in observations, not copies of every raw response. Large artefacts belong in object storage or the source system and should be retrieved by stable ID.
A minimal controlled loop
async function runAgent(run: AgentRun) {
while (run.status === 'running') {
assertWithinBudget(run);
const decision = await decideNextAction({
objective: run.objective,
recentObservations: await loadWorkingSet(run),
availableTools: await toolsForTask(run.taskId)
});
if (decision.type === 'finish') {
const evidence = await verifyOutcome(decision.claims);
run.status = evidence.every(item => item.valid) ? 'verified' : 'failed';
run.finalEvidence = evidence;
break;
}
validateToolCall(decision.tool, decision.arguments);
if (requiresApproval(decision)) {
run.status = 'waiting_approval';
run.pendingAction = decision;
break;
}
const result = await executeInBoundary(decision, {
timeoutMs: 20_000,
idempotencyKey: `${run.runId}:${run.step}`
});
await appendObservation(run, summarizeToolResult(result));
run.step += 1;
}
}
The important part is not the while loop. Every iteration checks budget, arguments, policy, idempotency, and the finish claim.
Tool contracts define the agent's ceiling
const sendRefund = {
name: 'send_refund',
description:
'Refund one captured payment after approval. Do not use for pending payments or goodwill credit.',
inputSchema: {
type: 'object',
properties: {
paymentId: { type: 'string' },
amountCents: { type: 'integer', minimum: 1 },
approvalId: { type: 'string' },
reasonCode: { type: 'string', enum: ['duplicate', 'service_failure'] }
},
required: ['paymentId', 'amountCents', 'approvalId', 'reasonCode']
}
};
A mutation should return the provider request ID, whether it was already applied, rollback availability, and an evidence URL. success: true is not enough for recovery or audit.
Permissions and human approval
| Risk | Example | Default policy |
|---|---|---|
| Read-only | Search documents, read an order | Automatic within a scoped dataset |
| Reversible write | Create a draft, add a tag | Automatic or sampled review with undo |
| External communication | Send email or chat | Show recipient and content before approval |
| Money or production | Refund, deploy, delete | Mandatory approval, idempotency, provider read-back |
| Irreversible or high-impact | Bulk deletion, public publication | Two-person approval or prohibit direct execution |
The approval view should show target, arguments, expected impact, source evidence, and rollback plan. “The agent wants to call a tool” is not enough information.
Stop loops before they become incidents
Define maximum steps, tokens, cost, and wall-clock time. Stop repeated calls with identical arguments, consecutive identical errors, exploration after the goal is met, and runs waiting for missing approval or credentials.
function detectLoop(history: ToolCall[]) {
const recent = history.slice(-3).map(call => JSON.stringify([call.name, call.arguments]));
return recent.length === 3 && new Set(recent).size === 1;
}
Return a diagnostic state such as blocked: missing_approval, not a generic failure.
Manager and handoff models
A manager owns the final response and invokes specialists as tools. Use it for one voice, one safety policy, and cross-domain synthesis. A handoff transfers the conversation to a specialist who needs to ask follow-up questions and own the user interaction.
{
"objective": "Resolve duplicate charge",
"facts": ["payment_1 captured", "payment_2 captured"],
"evidenceIds": ["trace_87", "invoice_42"],
"completed": ["identity verified"],
"openDecision": "refund which payment",
"prohibitedActions": ["send refund without approval"]
}
Do not forward the entire conversation by default. The specialist needs the smallest sufficient context and a clear responsibility boundary.
Tracing and evaluation
Trace run ID, model and configuration versions, tool arguments, latency, errors, tokens, cost, approvals, and provider receipts.
| Metric | Question |
|---|---|
| Task success | Did the user outcome happen? |
| Tool selection | Was the correct tool selected? |
| Argument validity | Did arguments satisfy schema and business rules? |
| Side-effect precision | Were unnecessary writes avoided? |
| Recovery rate | Can the run safely resume after timeout or crash? |
| Cost and latency | What does one successful resolution consume? |
A fluent answer paired with two refunds is still a severe failure.
Common mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Sending every task to an autonomous agent | Larger latency and failure surface | Move predictable tasks back to workflows |
| Overlapping tool responsibilities | Unstable tool choice | Rewrite boundaries and narrow the active tool set |
| Letting the model declare success | Premature completion | Verify through code or provider read-back |
| Retrying writes without idempotency | Duplicate payments, messages, or publications | Stable task ID plus provider request ID |
| Sharing full history with every specialist | Context growth and instruction conflicts | Structured handoff payloads |
Practice: duplicate-charge investigation
- Mark deterministic steps and decisions that need a model.
- Define boundaries for order, payment, identity, and refund tools.
- Add an eight-step limit, 30-second tool timeout, and loop detection.
- Produce an approval diff before refunding.
- Crash after approval and verify restart does not refund twice.
- Mark success only after reading the provider state back.
Definition of done
- I can explain why the task needs an agent instead of a workflow.
- Every mutation has an approval or explicit automation policy.
- The loop has step, cost, time, and repetition limits.
- Run state survives process restart.
- Completion is verified by external evidence.
Related reading
- Tool Design Principles
- Evaluation, Quality, and Monitoring
- Long-Running Agent Harness
- Context Engineering & Memory
Official references
📚 Related resources
❓ Common questions
Open a question to review the practical answer.
How do I pick between ReAct, Plan-and-execute, and Tree of Thoughts agent styles?
Match style to task shape. ReAct (reasoning + tool use interleaved) fits search/code tasks where you read tool results and decide the next step. Plan-and-execute (plan first, then execute) suits longer tasks — committing to a plan upfront stays more stable than re-deciding every ReAct step. Tree of Thoughts / Reflexion fits tasks needing multi-option exploration + self-critique + best-pick (puzzles, complex math). Router agents dispatch requests by intent/domain to specialized sub-agents, saving tokens.
How do I stop an Agent from looping or calling tools forever?
Through stop conditions — at least three caps: max steps (e.g., 10), max tool calls (e.g., 20), max time (e.g., 60s); any trigger breaks. Loop detection can hash the last N (tool, args) pairs and abort on repeats. The chapter pseudocode is the standard pattern: `while not done: ... if stop_condition(): break`. In production, also add: distinct exit codes for completion vs timeout vs kill (upstream needs to know), cancel-rate monitoring, and routing loop cases to a trace store for post-mortem.
What's dual-model verification, and why use cheap-model + expensive-model review?
Dual-model verification: a cheap model (e.g., GPT-4o-mini) runs the agent's main loop and tool calls; an expensive model (e.g., Claude Sonnet 4.5 / GPT-5) only reviews at the end — checks whether the answer cites tool outputs, follows instructions, and contains no hallucinations. The rationale: 80% of steps don't need top-tier reasoning, so the most expensive compute lands at the highest-ROI "quality gate." Combined with self-critique (agent verifies its own answer vs instructions/citations), this lifts production quality measurably while adding only 10-20% cost.
How do I add human approval for Agent's high-risk actions (sending emails, prod changes)?
Three layers: (1) tool-level allowlist for external domains/APIs, blockwrites by default unless explicitly permitted; (2) guardrails run PII / dangerous-action checks before and after tool calls; (3) human approval steps gate risky actions (emails, transactions, prod changes) behind a node that requires user confirmation. Frameworks supporting human-in-the-loop (like LangGraph) can pause at an approval node and wait for the user. OpenAI Agent Mode follows the same logic — deliver to the final channel and wait for confirmation before sensitive/irreversible steps.
What fields must Agent observability logs at least capture?
Per-run trace must capture: step index, chosen tool, inputs/outputs, duration, errors (code + message), tokens consumed. At the metrics layer, at least four: success rate, avg steps, tool error rate, cancel rate. Replay is critical — store deterministic inputs (same input reproduces) plus same seed/config for offline reproduction. Running a production Agent without traces is flying blind — when bugs hit, you can't tell whether the plan was wrong, the tool died, or the model hallucinated.