The big idea
Strip away the hype and every agent is five lines: plan β decide β call tool β observe β stop. Reviewing one means reviewing those five moves β specifically tool contracts, idempotency, budgets, and what gets folded back into context. Frameworks, multi-agent topologies, 'planners' β all of it is elaboration on those five lines.
Deep dive
The five-line loop
Every agent, no matter how it's dressed up, runs some version of this:
``` state = plan(goal) while not done(state) and within_budget(state): action = decide(state) result = call_tool(action) state = observe(state, result) stop(reason = goal_met | budget_hit | timeout | error_floor) ```
That's it. When you read about frameworks, orchestrators, or planner-worker splits, you're reading about scaffolding built on top of this shape.
The practical implication: if your framework hides these five moves behind abstraction, your review surface hides with them. Know which line of your codebase maps to each step.
The four things you're actually reviewing
When you audit an agent, you're really checking four things β everything else is noise.
**1. Tool contracts.** Each tool is a typed function: name, input schema, output schema, error modes, and whether it has side-effects. Your MCP server is exactly this contract layer. If the contract is ambiguous, the model will fill the gap with a guess.
**2. Idempotency of side-effects.** Every mutating tool needs an idempotency key (a client-generated request ID the server deduplicates on) or a check-then-act guard. Read-only tools get a pass. Anything that spends money does not.
**3. Budgets.** Hard ceilings on steps, tokens, wall-clock time, and dollar spend β enforced in code, not in prompts. 'Please stop after 10 steps' is a suggestion. `if steps > 10: halt` is a control.
**4. The observe step.** What actually gets folded back into context is a designed artifact. Raw API dumps bury the signal. Over-summarised results hide the error the model needed to see. Neither end of that spectrum is safe by default.
Framework choice β what actually matters
Framework debates eat a lot of oxygen. Here's what the choice actually buys you:
**Graph-style (LangGraph-archetype)** works well for explicit state machines and human-approval interrupts. You get their state model and debugging surface β and you take on their mental model too.
**Typed-agent (Pydantic AI-archetype)** suits a review-everything discipline: contracts as types, validation at the boundary, minimal magic. Good fit when you want to explain every moving part to a client.
**Crew/role frameworks** optimise for multi-agent role-play. Skip until you have a proven multi-agent need β which is rarer than the marketing implies.
**SDK-direct** is roughly 150 lines you fully own. Often the right v1 for a consultant whose pitch is 'I can explain every line.'
One decision that does matter: all serious frameworks now speak MCP natively. Build your tools once and you can swap the loop harness freely. The dangerous decisions live in the tool surface, not the framework dropdown.
How to run it
- Tool surface is the security boundary. Every dangerous action must be literally impossible to call, not merely discouraged in the prompt. If the action shouldn't happen, it shouldn't exist as a callable tool.
- Idempotency on every mutating tool. An idempotency key or check-then-act guard exists for anything that spends money, writes data, or sends a message. Retries are assumed, not exceptional.
- Four budgets, enforced in code. Steps, wall-clock time, token spend, and dollar spend all have hard ceilings checked in code, with a named halt reason when breached.
- Observe step is a designed artifact. What re-enters context is an explicit reduction, not a raw dump or a lossy summary. Raw results are archived in the trace regardless of what the model sees.
- Every finish is logged with a reason. goal_met, budget_exhausted, timeout, and error_floor are distinguishable in logs β 'it just stopped' is not an acceptable terminal state.
In the field
Task: 'Audit this ad account and propose (don't execute) budget reallocations.' Spec the loop before code β Tool surface: get_campaigns, get_ad_performance(range), get_audience_breakdown (all read-only). propose_change(doc) writes to a proposals table, not the ads API β the loop physically cannot spend money. Budgets: max 15 tool calls, 60s wall-clock, $0.50 model spend; on breach halt with budget_exhausted. Observe step: performance responses reduced to (campaign, spend, conversions, CPL, Ξ vs prior period) before re-entering context; raw JSON stays in the trace. Stop condition: model calls finish(report) β finishing is itself a tool call, so it appears in the trace and can be evaluated. The dangerous decision β what the loop can do β was made in code before the model saw a single token.
When not to use it
If the workflow is enumerable in advance, it's a pipeline, not an agent. n8n will do it deterministically at a fraction of the cost with none of the review burden. The most common over-engineering failure in current client work is shipping an agent where a three-node workflow was the honest answer. A useful middle shape: a workflow with one agentic node β deterministic everywhere determinism is available, one bounded LLM step where it isn't. Default to single-agent; multi-agent only for (a) context sharding when one window can't hold what's needed, or (b) genuinely different tool permissions per role (analyst vs executor as a security design, not an architecture fashion).
Pitfalls and takeaways
- Prompt-enforced safety. 'Never modify live campaigns' in the system prompt with a mutating tool still exposed. The tool surface is the security boundary; the prompt is a preference.
- Non-idempotent retries. Timeout β framework retries β duplicate side-effect. Symptom: 'it created two of them, sometimes.'
- Context flooding. Raw responses appended verbatim; by step 8 the model can't find its own plan. Symptom: quality degrades with loop length.
- Silent budget absence. Works in the 3-step demo, spirals in production when a pathological account produces 200 steps.
- Un-observable finishes. The loop 'just stops' with no reason code β you can't tell goal-met from give-up in the logs.
- Tool surface is the security boundary; prompts are preferences.
- Mutating tools require idempotency keys or check-then-act guards.
- Budgets live in code as four ceilings: steps, tokens, wall-clock, spend.
- Every finish is a tool call so you can evaluate it in the trace.
Do the work
Goal: Write a complete agent-loop spec for a real MCP tool surface, then stress-test it by feeding it to an AI and reviewing the generated loop line by line against your own spec. (60 min)
- List every tool and tag it read vs mutating. Write down each tool's name, its input schema, its output shape, and whether it has a side-effect. For every mutating tool, note whether it's naturally idempotent (safe to call twice with the same input) or needs a key.
- Design the observe-step reduction rule for each tool. For every tool's raw output, write the exact reduction that gets folded back into context β which fields survive, which get dropped, and where the raw response is archived for the trace.
- Set all four budgets as code-enforced ceilings. Specify max steps, max wall-clock time, max dollar spend, and max tokens. Write the exact halt behavior and reason code for each ceiling being hit.
- Define every stop condition with a distinct reason code. List all the ways the loop can end: goal_met, budget_exhausted, timeout, error_floor, user_cancelled. Each should be distinguishable later in a trace.
- Generate the loop from your spec with an AI, then diff it line-by-line. Hand your written spec to an AI assistant and ask it to implement the loop harness. Do not accept it β go through the generated code and mark every place it deviates from your spec.
- Write a one-paragraph review memo. Summarize what the AI got right, what it missed, and what you changed before accepting the code. This memo is what you'd hand a client as evidence of review discipline.
Deliverable: A written agent-loop spec (tool table, observe rules, budgets, stop conditions) plus the AI-generated implementation and a diff memo noting every deviation you caught and fixed.
Sources
- Anthropic, "Building Effective Agents" (engineering blog, 2024)
- OpenAI, "A Practical Guide to Building Agents" (2024)
- Model Context Protocol specification, modelcontextprotocol.io
- LangGraph documentation, "Human-in-the-loop" and state persistence guides