1:1 mentoring with Big Tech AI engineers
01
10 questions

Fundamentals & Architecture

What an agent actually is, when a loop earns its cost, and how to pick between ReAct, Planner-Executor, and multi-agent.

Asked at

The kind of question these teams are known for — not a record of one specific interview.

Q1
What's the difference between a chatbot and an agent?
AgentsArchitectureTool Use

Commonly asked at Anthropic · OpenAI · Sierra

open question
How to Answer

"A chatbot is a single LLM call — input in, text out, stateless. An agent is an LLM inside a loop. The loop gives it tools, memory, and the ability to take actions in the world. The agent decides what to do next based on observations. The key difference is autonomy — an agent can reason, act, observe, and iterate until a task is complete. The 'agent' is actually the while-loop your code runs around the LLM, not the LLM itself."

What the loop actually looks like

Same model in both rows. The only difference is what your code does with the model's output — return it, or feed it back in.

Chatbot vs agent — the difference is the loop, not the model
CHATBOT — ONE PASS USER LLM · 1 CALL no tools · no memory TEXT OUT stateless — forgets you AGENT — LOOP UNTIL DONE USER LLM · DECIDE next action, each iteration sees memory + tool results TOOL CALL API · DB · search · code ANSWER or final action MEMORY / STATE persists across steps 1 · action(tool + args) 2 · observation(result) — repeat 3 · when the model says done: emit final answer re-read every iteration — this is what makes step N aware of step N−1 chatbot: 1 call · ~600ms · ~$0.001   |   agent: 3–8 calls · seconds · cents — you pay for autonomy in latency and tokens

Side-by-side, the way an interviewer wants it

DimensionChatbotAgent
Control flowFixed by your code: one call, text outChosen by the model each iteration — it decides the next step
StateStateless (or raw chat history re-sent)Working memory carried across steps
ToolsNone, or retrieval onlyCalls APIs, databases, code — can write to the world
Latency / cost~600ms, ~$0.001 per question3–8 loop iterations, seconds, cents per task
Failure modeWrong answer — user reads it and moves onWrong action — refund issued, email sent. Needs guardrails
Ship it when…Q&A, FAQ deflection, draftingMulti-step tasks with side effects
REAL SYSTEM

A support copilot runs both, tiered. The FAQ tier is a chatbot — one call, ~400 tokens, ~600ms, ~$0.001/question — it deflects “what’s your refund policy?”. The resolution tier is an agent: for “refund my last order” it loops lookup_order → check_policy → issue_refund → send_confirmation — ~5 iterations, ~6K tokens, ~8s, ~$0.03 — but it closes the ticket end-to-end. Same base model; the wrapper decides which one it is. Routing between the two tiers is where most of the money is saved.

FOLLOW-UP TRAP

“So is RAG an agent?” — No. Vanilla RAG is a fixed pipeline: retrieve → stuff → generate, same path every time. It becomes agentic when the model decides whether to retrieve, what to retrieve, and when to stop. Quick litmus test: if your control flow lives in Python if statements, it’s a pipeline; if it lives in the model’s next-token distribution, it’s an agent.

Learn it in full: The Agent Loop: Think → Act → Observe — the loop in fifteen lines, why messages[] only grows, and the guards that stop it.

Q2
When would you NOT use an agent? When is a simple RAG pipeline enough?
AgentsRAGArchitectureCost Optimization

Commonly asked at Anthropic · Google · Glean

open question
How to Answer

"If the task is single-turn retrieval + generation — user asks a question, you find the answer in docs — RAG is cheaper, faster, and more predictable. I'd reach for agents only when:

  • (1)the task requires multiple steps
  • (2)it needs tool use (write operations, calculations, API calls), or
  • (3)the solution path is not known upfront and requires reasoning. An agent adds 3-10x the cost and latency of RAG. The tradeoff is autonomy vs predictability."

The three gates

Ask them in order and stop at the first yes. Most “we need an agent” requests fail all three — they are a retrieval problem wearing a loop.

Three gates — any single yes means you need an agent
ASK IN ORDER — THE FIRST YES ENDS THE QUESTION TASK MULTI-STEP? more than one hop WRITE ACTIONS? refunds, emails, tickets PATH UNKNOWN? steps depend on findings RAG is enough no no no yes yes yes AGENT loop · tools · memory SAME 1,000 REQUESTS ON SONNET 5.5 — WHAT AUTONOMY COSTS RAG $9 · 0.8s Agent — 5 iterations, 3 tool calls $45 · 8s the bill is the small part — you are really trading predictability for autonomy

What you give up when you add the loop

DimensionRAG pipelineAgent
Control flowFixed in your code: retrieve → stuff → generateChosen by the model at every step
LatencyOne round trip, p50 ≈ 0.8s3–8 round trips, seconds
Cost1× — one call, ~3K in / 300 out ≈ $0.0093–10× — history re-sent: 5 calls, ~18K in / 900 out ≈ $0.045
ReproducibilitySame input, same path, every timePath varies per run — you debug traces, not code
Failure modeWrong answer; the user reads it and moves onWrong action; the refund is already issued
Eval effortRetrieval hit rate + answer accuracyOutcome and trajectory scoring, per task type

The middle ground most teams skip

Between the two there is a pipeline that keeps its fixed shape but lets the model make one bounded decision:

  • Conditional retrieval — the model decides whether to search before answering. One extra call, no loop.
  • Query rewriting — one rewrite pass before retrieval; fixes most “retrieval missed it” complaints for ~$0.0001 on Haiku 5.5.
  • Bounded re-query — if the top chunk scores below threshold, retrieve once more with a different query. Hard cap of two.
  • Read-only tools — a loop with lookups but no writes. You get multi-step reasoning with none of the blast radius.
REAL SYSTEM

An internal docs assistant handles ~60K questions/month. Classification showed 82% are single-hop lookups — those run as plain RAG on Sonnet 5.5 at ~$0.009 and ~0.8s. The remaining 18% (“compare our Q2 and Q3 policy and tell me what changed for contractors”) route to an agent at ~$0.045 and ~7s. Running everything through the agent would have cost ~$2.7K/month for ~$930 of actual work, and pushed p50 latency from 0.8s to 7s for the 82% who never needed it.

FOLLOW-UP TRAP

“Retrieval keeps missing the answer — won’t an agent fix that?” — No. An agent that searches a broken index just pays 5× to fail more slowly, and now it fails non-deterministically so you cannot reproduce the bug. Fix chunking, add hybrid keyword + vector search, and measure recall@5 first. Reach for the loop when recall is good and the task still needs several steps — not when retrieval is bad.

Learn it in full: The Agentic Spectrum: Workflows to Autonomy — chaining, routing and orchestrator-workers between the two, and how to choose the lowest level that works.

Q3
How do you decide between ReAct, Planner-Executor, and Multi-Agent?
ReActPlanningMulti-AgentArchitecture

Commonly asked at OpenAI · Microsoft · Sierra

open question
How to Answer

"Decision tree: ReAct when the task is exploratory and the path isn't known upfront (research, diagnosis). Planner-Executor when the task has clear phases and I want auditability — the plan is a human-readable artifact I can approve before execution. Great for compliance-heavy workflows. Multi-Agent only when there are genuinely separable domains of expertise — e.g., a researcher who searches the web and an analyst who runs SQL shouldn't share a context window. Multi-agent is a tool, not a default — it adds coordination overhead and debugging complexity."

The three shapes, side by side

Three control-flow shapes — pick the one the task already has
REACT — EXPLORE LLM · DECIDE next step from last result TOOL CALL one per step act observe → decide again 3 to 15 steps — you find out at runtime PLANNER-EXECUTOR — APPROVE FIRST PLAN human-readable artifact HUMAN APPROVES EXECUTE step 1 → 2 → 3, no re-planning the plan is the audit artifact MULTI-AGENT — SPLIT CONTEXTS ORCHESTRATOR RESEARCHER web search own context ANALYST SQL own context two context windows that never mix you pay in handoffs and debugging WHAT TRIGGERS EACH → path unknown: research, diagnosis → phased work you must sign off → expertise that must stay separate start at ReAct, add a planner when you need auditability, split into agents only when contexts genuinely conflict

Choose on the failure you can live with

PatternUse whenTrade-off
ReActThe path is discovered as you go — diagnosis, research, triageUnbounded steps; latency and cost you cannot quote in advance. Fails: wanders, re-calls tools, stops at the iteration cap
Planner-ExecutorPhases are known and a human must sign off before anything runsOne extra planning call, plus human wait time in the middle. Fails: a wrong plan is executed faithfully
Multi-AgentGenuinely separate expertise whose contexts should not mixN× the tokens, plus handoffs and cross-agent tracing. Fails: facts lost in handoffs; no one arbitrates a disagreement

Why the plan artifact is the whole point

It wins compliance reviews: the plan is inspectable before anything runs — a diff to approve, not a trace to read later:

{
  "task": "close out the Q3 vendor invoice dispute",
  "plan": [
    { "step": 1, "tool": "fetch_invoices", "args": { "vendor_id": "V-8841" },
      "reads": ["billing.invoices"], "writes": [] },
    { "step": 2, "tool": "diff_against_contract", "args": { "contract_id": "C-119" },
      "reads": ["legal.contracts"], "writes": [] },
    { "step": 3, "tool": "open_credit_memo", "args": { "amount_cap_usd": 5000 },
      "writes": ["billing.credit_memos"], "requires_approval": true }
  ],
  "approval": { "because": "step 3 moves money", "approver_role": "ap_manager" }
}

Every step declares what it reads and writes, so the approval gate is derived, not remembered. ReAct cannot write this: step 3 does not exist until step 2 returns.

REAL SYSTEM

An incident-triage system started as three agents — logs, metrics, deploy history — behind an orchestrator. Median resolution burned ~31K tokens and ~48s, and 1 run in 5 lost a fact across a handoff: the log agent found the bad deploy, the orchestrator’s summary dropped the timestamp. One ReAct agent holding all three tools took it to ~12K tokens and ~19s, with lost-context failures near zero. Multi-agent came back for one case: the customer-data agent, which had to run under a different service account.

FOLLOW-UP TRAP

“More specialists must be better — why not five agents?” — Because every handoff is a lossy summary. One agent passes full observations from step N to step N+1; a handoff passes only what the orchestrator wrote down. Split when contexts must not mix — different credentials, different data residency — not because the org chart has specialists.

Learn it in full: Planner-Executor: Divide & Conquer — the plan as checkable data, and when ReAct fits.

Q4
Your agent is taking 15 tool calls to complete a task that should take 3. What do you do?
Tool UseDebuggingAgentsOptimization

Commonly asked at Sierra · Cursor · Anthropic

open question
How to Answer

"I'd diagnose in this order:

  • (1)Check tool descriptions — are they ambiguous? If the agent can't tell which tool to use, it tries them all. Fix the descriptions.
  • (2)Check if it's re-calling the same tool — add deduplication.
  • (3)Check context bloat — after 10 calls, the context is so long the model loses track. Add observation summarization.
  • (4)Consider a planner step — if the agent is wandering, a plan upfront constrains the path.
  • (5)Check the system prompt — add explicit guidance like 'you should need at most 5 tool calls for this type of task.'"

Read the trace before you touch the prompt

Fifteen calls is not one bug. Pull ten traces and count what each call contributed — the shape of the waste names the fix.

The same task, traced twice — twelve of fifteen calls did no work
BEFORE — 15 CALLS, 3 OF THEM USEFUL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1–2 real work 3–7 wrong tool, retried 8 real work 9–14 same call, same args 15 answer AFTER — 3 CALLS, IDENTICAL OUTCOME 1 2 3 dedup on (tool, args) · rewritten descriptions · 5-call budget in the prompt an agent that wanders is almost always a description problem, not a reasoning problem

Five shapes of waste and what each one means

What you see in the traceRoot causeFix
Same tool, same arguments, three timesNo dedup — the answer is in context but buriedCache by (tool, sorted(args)); reuse the result
Cycles through four similar-sounding toolsDescriptions overlap; it is guessing between near-tiesAdd a “not for X — use Y” clause to each
Calls get vaguer after step 8Context bloat — raw output crowded out the goalSummarize observations to 2–3 lines; re-state the goal
Never takes the same path twiceNo plan — every step is an independent decisionPlan first, execute it, re-plan only on failure
Only ever stops at the iteration capNo stop condition the model can reachState the budget in the prompt and cap it in code

Two guards, both in your code

def step(state, call):
    sig = (call.tool, json.dumps(call.args, sort_keys=True))

    if sig in state.seen:                       # never pay twice for one question
        return Obs(state.seen[sig], note="identical args — reusing the earlier result")

    if state.calls >= HARD_CAP:                 # a cap the model cannot argue with
        return Obs(None, note="tool budget spent — answer from what you have")

    state.seen[sig] = execute(call)
    state.calls += 1
    return summarize(state.seen[sig], max_lines=3)

The note matters as much as the guard — told only “no result”, the model retries with a cosmetic argument change.

REAL SYSTEM

A billing-support agent averaged 14.6 tool calls on a 3-call task. Traces showed 61% were exact duplicates and 22% were get_account vs fetch_customer ping-pong — descriptions differing by one adjective. Dedup, merged descriptions, and a stated 5-call budget took it to 3.4 calls, p50 9.0s → 2.1s, $0.21 → $0.05 per task. Success went up, 68% to 94%: each extra call was another chance to derail, not to succeed.

FOLLOW-UP TRAP

“Just cap it at 3 calls — problem solved?” — A cap alone turns a slow success into a fast failure: the agent hits the wall mid-task and answers from partial data, which is worse because nothing looks broken. Cap and fix the cause — dedup so the budget is not spent on repeats, and make the cap a graceful exit that names what it could not finish.

Learn it in full: Agent Failure Modes — naming a failing run from its trace, the detector for each failure, and fixing by blast radius.

Q5
When do you fine-tune instead of adding retrieval or changing the prompt?
Fine-TuningRAGPrompt EngineeringArchitecture

Commonly asked at OpenAI · Databricks · Scale AI

open question
How to Answer

“Three levers, and they fix different failures. Prompting fixes instruction-following — the model can do the task, it just isn’t doing it your way. Retrieval fixes missing knowledge — the model doesn’t know your data, or your data changes daily. Fine-tuning fixes consistency — the model knows how, but drifts on format or tone across thousands of calls, or you need a smaller model to hit a latency and cost target.

The order matters: prompt, then retrieval, then fine-tune. Most teams reach for fine-tuning first because it sounds like the serious answer, spend six weeks building a dataset, and find out the real problem was a vague system prompt.

In production they combine. Fine-tune for shape, retrieve for facts. What I never do is fine-tune facts into a model, because then every fact change is a retraining job.”

Match the lever to the failure

Three levers, three different failures — in the order you should try them
TRY THEM IN THIS ORDER 1 · Prompting fixes: not following instructions fixes: wrong shape on a good day cost: an afternoon reversible: instantly 2 · Retrieval fixes: doesn’t know your data fixes: facts change daily cost: days, plus an index to run reversible: reindex 3 · Fine-tuning fixes: tone/format drift at scale fixes: frontier latency and price cost: weeks, 2–10K labelled rows reversible: retrain None of the three add reasoning the base model does not have If the model cannot do the task once, by hand, with the answer sitting in front of it — that is a model choice, not a tuning problem. Most teams skip to 3 because it sounds serious, then find the bug was in 1.

The symptom tells you which one you need

The mistake is almost always reaching one rung too high.

SymptomWhat teams reach forWhat actually fixes it
Output format wandersfine-tuneschema-constrained decoding plus two examples
Answers are a week stalefine-tuneretrieval with a freshness filter
Cites documents that don’t existfine-tune on the docsretrieval with the id checked against the index
Tone is off-brand 1 reply in 5more prompt rulesfine-tune on 2–5K approved replies
p95 is 4s on a frontier modelcachingdistil to a small fine-tuned model
Cannot do the domain reasoning at allfine-tunea stronger base model — tuning does not add reasoning

What a fine-tune actually buys

The honest case for fine-tuning is rarely quality. It is holding quality while the model gets small enough to be fast and cheap.

Frontier model, prompted8B model, fine-tuned
Format compliance~71%~98%
p95 latency3.4s0.9s
Cost / 1K replies$4.10$0.38
Time to change behaviourminutesa retrain cycle
Facts come fromretrievalretrieval — still
REAL SYSTEM

A support-reply system had a tone problem: about one reply in five read wrong for the brand, and no amount of prompt rules held it. We fine-tuned an 8B model on ~12K human-approved replies — format compliance went ~71% → ~98%, p95 3.4s → 0.9s, and cost per thousand replies $4.10 → $0.38. Every fact in those replies still came from retrieval, so a pricing change is a reindex, not a retrain.

FOLLOW-UP TRAP

“Why not fine-tune on your docs and drop RAG entirely?” — because you retrain on every document change, you cannot cite a source, you cannot apply per-user permissions at retrieval time, and you cannot delete a fact when a customer asks you to. Fine-tuning teaches shape; retrieval supplies facts. A model with the facts baked in is confidently wrong the day the facts move.

Learn it in full: When to Fine-Tune: The Decision Framework — the prompt → retrieval → fine-tune ladder, the three questions that settle it, and why facts stay out of the weights.

Q6
Walk me through your RAG pipeline. What chunk size and overlap, and why?
RAGEmbeddingsRetrievalArchitecture

Commonly asked at Sierra · Databricks · Glean

open question
How to Answer

“Six stages — chunk, embed, index, retrieve, rerank, generate. The interesting decisions are at the two ends.

Chunking: I start at about 512 tokens with 10–15% overlap for prose, but I don’t chunk structured documents on a token count at all. I chunk them on their own boundaries — a section, a whole table, a function. The failure everyone hits once is splitting a table so that neither half means anything.

Retrieval: dense alone misses exact strings — part numbers, error codes, names. So hybrid. BM25 and vector search in parallel, fused with reciprocal rank fusion, then a cross-encoder rerank over the top 50 down to the five that go in the prompt.

And the number I actually track isn’t similarity score, it’s whether the chunk containing the answer made it into the context. I measure recall on a labelled set before I tune anything else.”

Where quality is lost

Six stages — but quality is lost in three of them
THE PIPELINE, LEFT TO RIGHT chunk embed index retrieve rerank generate Chunking loses it first a table split in half means nothing a claim without its qualifier inverts fix: chunk on the document’s own boundaries, not on a token count Dense retrieval loses exact terms embeddings blur part numbers, error codes and proper nouns together fix: BM25 and vector in parallel, fused with reciprocal rank fusion The prompt loses the answer score order is not usefulness order a long context buries its middle fix: cross-encoder rerank, then cut to the five that fit The metric that matters is not similarity score — it is whether the answer-bearing chunk is in the context at all.

Chunk on the boundary, not on the number

512 tokens is a default for prose and wrong for everything else.

ContentChunk onWhy
Policies, prose docsheading + paragraph, ~512 tok, 64 overlapkeeps a claim with its qualifier
API referenceone endpoint, whatever its sizehalf an endpoint is worse than none
Tables, sheetsthe whole table + captiona split row loses its header row
Codea whole function or classa signature without its body retrieves nothing
Transcriptsturn windows, 6–10 turnspronouns need their antecedent in scope

Retrieve wide, rerank narrow

Each stage buys recall; the rerank spends a little of it back to buy prompt space.

StageReturnsAdded p50Answer chunk present
BM25 only5012 ms71%
Vector only5028 ms78%
Hybrid, fused with RRF5034 ms91%
+ cross-encoder → top 55170 ms88%

The last row gives up three points of recall and cuts prompt tokens roughly tenfold. That is the trade, and it is usually worth taking.

REAL SYSTEM

An enterprise policy corpus — ~240K chunks, heavy on tables. Recall@5 sat at ~61% and the agent’s answers were right ~68% of the time. Two changes: stop splitting tables (re-chunk on document structure) and add BM25 alongside the vector search with a cross-encoder rerank. Recall@5 went to ~88% and answer accuracy to ~84%, with ~200 ms added to p50. No prompt or model change was involved.

FOLLOW-UP TRAP

“Context windows are huge now — why not skip retrieval and paste everything in?” — cost scales linearly with tokens on every single call, attention degrades in the middle of a long context, and you lose the two things retrieval gives you for free: a citation, and a place to enforce per-user permissions. Retrieval is an access-control boundary, not only a cost trick.

Learn it in full: Chunking: Cut on Structure, Size for the Embedder — cutting on the document’s own units, fitting heading path and overlap in the embedder’s window, and measuring the cut by recall@k.

Q7
How do you get reliable structured output? What happens when the JSON doesn't parse?
Structured OutputReliabilityTool UseProduction

Commonly asked at OpenAI · Stripe · Amazon

open question
How to Answer

“First, don’t ask for JSON in prose and hope. Use the provider’s constrained decoding — attach a schema to the request so invalid tokens are masked at sampling time and the output parses by construction.

That buys you syntactic validity, not semantic validity. The model will happily return a perfectly-shaped object with a date in the wrong format, an enum value it invented, or line items that don’t add up to the total it also gave you. So there are two layers: constrained decoding for shape, then my own validator for meaning.

When validation fails, I retry exactly once with the validation error echoed back — ‘currency must be one of USD, EUR, GBP; you sent US Dollars’. That fixes most of them. If the second attempt fails, it goes to a fallback path or a human. It does not loop.”

Two gates, two different failures

Constrained decoding gets you shape. It does not get you meaning.
TWO GATES, TWO DIFFERENT FAILURES Gate 1 — shape schema-constrained decoding: invalid tokens masked at sampling time catches: unparseable JSON, missing required fields, wrong types Gate 2 — meaning your validator, after it parses catches: invented enums, wrong units, totals that don’t reconcile, ids that don’t exist, arrays quietly cut short parses Fail → retry once with the error echoed back → then stop “field currency must be one of USD, EUR, GBP — you sent ‘US Dollars’” · a second failure falls back to a human; it does not loop

What still goes wrong after the JSON parses

FailureWhat it looks likeGuard
Invented enum"status": "in-progress-ish"literal enum in the schema; reject and echo the allowed values
Ambiguous unit"amount": 1250 — cents or dollars?require an explicit currency and minor-unit field
Doesn’t reconcileline items sum to 1180, total says 1250compute the total yourself; never trust the model’s arithmetic
Hallucinated idan order_id that has never existedresolve every id against the system of record before acting on it
Short array3 of 47 line items, and it parsesreconcile the row count against the document; a reply cut off at max_tokens shows in the stop reason

The retry that works

One repair attempt, carrying the specific error. Not a loop, and not a bare retry of the same prompt — that just resamples the same mistake.

def extract(doc, attempt=1, repair_hint=None):
    resp = client.messages.create(
        model=MODEL, max_tokens=16000,
        output_config={"format": {"type": "json_schema",
                                  "schema": INVOICE_SCHEMA}},
        messages=build(doc, repair_hint))
    if resp.stop_reason in ("refusal", "max_tokens"):
        raise Incomplete(doc.id, resp.stop_reason)   # may not match the schema

    text = "".join(b.text for b in resp.content if b.type == "text")
    obj = json.loads(text)                  # shape already guaranteed
    errors = check_business_rules(obj, doc) # enums, units, totals, ids, row count
    if not errors:
        return obj
    if attempt == 2:
        return escalate(doc, errors)        # human queue; do not keep looping
    return extract(doc, attempt + 1, repair_hint=render(errors))
REAL SYSTEM

Invoice extraction, ~40K documents a month. Moving from “respond in JSON” to schema-constrained decoding took hard parse failures from ~4.2% to zero. The business-rule validator then caught another ~6.1% that parsed perfectly — mostly invented currency codes and totals that didn’t match the line items. One repair retry cleared ~81% of those, leaving ~1.2% for a human. The row-count check was added after a month of invoices that were quietly missing their last rows.

FOLLOW-UP TRAP

“If decoding is constrained, why validate at all?” — the grammar only guarantees the shape you asked for. Its most dangerous failure is the one that looks like success: a well-formed object with three of forty-seven rows, because the model stopped listing early. It parses, it validates against the schema, and it is wrong. Reconcile counts against the source, and check the stop reason on every call — a refusal or a max_tokens cut-off may not match the schema at all.

Learn it in full: Structured Output: JSON Every Time — output_config.format, messages.parse(), strict tools, and what a schema cannot say.

Q8
When would you not use an agent framework and write the loop yourself?
ArchitectureAgentsTool UseProduction

Commonly asked at Anthropic · Cursor · Sierra

open question
How to Answer

“The agent loop is about forty lines — call the model, if it asked for tools run them, append the results, call again, stop on a final answer or a step cap. That’s not the hard part, and adopting a framework doesn’t remove the hard part.

What a framework does give you is the machinery around the loop: retries, streaming, tracing, checkpointing, human-in-the-loop pauses, durable state. Writing all of that yourself is real work, and that’s the honest argument for using one.

What it costs you is a layer between you and the model. When tool selection goes wrong you need to see the exact request that went over the wire, and that’s harder through three abstractions — especially when a version bump quietly edits the prompt underneath you.

So: own the loop and own the prompt, adopt libraries for the plumbing. If a framework won’t show me what the model was actually sent, that’s disqualifying.”

What is actually in the loop

The loop is about forty lines. The machinery around it is the real work.
WHAT IS ACTUALLY IN THE LOOP 1 call model messages + tool schemas 2 tool calls? none → return the answer 3 execute your timeouts, your retries 4 append results go back as messages Step cap of 8. Exceeding it raises — it is a bug to investigate, not a condition to retry. Everything hard lives in step 3 and in the prompt that shapes step 1.

Decide per concern, not per framework

This is not an all-or-nothing choice, and treating it as one is how teams end up rewriting a workflow engine badly.

ConcernDefaultWhy
Loop control and step capownforty lines, and it is where your policy lives
System prompt, tool descriptionsownthis is the product — no upgrade should edit it
Retries, backoff, rate limitsadoptsolved, boring, and easy to get subtly wrong
Tracing and spansadoptyou want OpenTelemetry, not a bespoke logger
Durable state, resume after crashadopta real workflow engine beats a status column
Human-in-the-loop pausesplitadopt the mechanism, own the policy that triggers it

The loop, in full

def run(messages, tools, max_steps=8):
    for _ in range(max_steps):
        reply = model.call(messages, tools=tools)   # your prompt, your schemas
        messages.append(reply)
        if not reply.tool_calls:
            return reply.text
        for call in reply.tool_calls:
            messages.append(execute(call))          # timeouts and retries here
    raise StepBudgetExceeded(max_steps)             # never spin forever
REAL SYSTEM

A support agent on an off-the-shelf framework was picking the wrong tool about 9% of the time and nobody could say why — the framework was injecting its own tool-choice preamble ahead of ours. Dropping to a hand-written loop (~60 lines) made the request inspectable, and the actual fix was one sentence in a tool description. Selection errors fell to ~2%. Retries, tracing and the workflow store all stayed on libraries; the loop and the prompt came in-house.

FOLLOW-UP TRAP

“So frameworks are bad?” — no. The bad version is adopting one so you don’t have to understand the loop. If you can’t draw what goes over the wire on each step, the framework is a liability the first time production misbehaves. Teams that understand the loop use frameworks well, and switch off the parts that fight them.

Learn it in full: Who Owns the Loop: Harness vs Deployment — harness versus deployment, the two different things called “the SDK”, and why “I need control” rarely means writing the loop.

Q9
What's the difference between the agent loop and the harness, and which one do you actually engineer?
AgentsArchitectureGuardrailsProduction

Commonly asked at Anthropic · Cursor · OpenAI

open question
How to Answer

“The loop is the small part. Call the model, run whatever tools it asked for, append the results, call it again, stop when it says it’s done or I hit a cap. That’s fifteen lines, every team’s version looks the same, and the Tool Runner or the Agent SDK will write it for me.

The harness is the loop plus everything that decides what the model sees, what it can touch and when it’s allowed to stop: the instructions and context it loads, the tools and permissions, the guardrails on risky calls, the checks that tell it whether it actually finished, the state it leaves for the next run, and the turn and budget limits.

That’s the part I engineer, because the model is the same for everybody and the harness is where the results come from. When an agent declares victory early, the fix is a test that decides ‘done’, not a better prompt. When it forgets conventions, it’s standing instructions. Each piece answers a failure I’ve actually seen.

And I don’t hand-write the loop just to get control — approval gates, result rewriting and run limits are already hooks in the SDKs.”

The loop sits inside the harness

Same model, same fifteen lines for every team. What differs is the boundary around them.

The loop is fifteen lines; the harness around it is where the results come from
HARNESS — EVERYTHING INSIDE THE DASHED LINE IS YOURS CONTEXT what it knows CLAUDE.md, prompt, compaction TOOLS & PERMISSIONS what it can touch allow / ask / deny, scoped creds GUARDRAILS what it may not do PreToolUse gate, sandbox MODEL same for every team THE LOOP · ~15 LINES call → run tool_use → append tool_result → repeat until end_turn 1 · asks 2 · results FEEDBACK how it checks itself PostToolUse lint, Stop runs tests STATE what it leaves behind progress file, git, run ledger LIMITS when it must stop max_turns, max_budget_usd swap the model and the loop does not change; swap the harness and the results do — that is the part you engineer

Build each part from a failure you have seen

Add a part when the failure shows up, not before, and re-test them all when you change models. The last column names where each part lives in the Agent SDK and Claude Code.

FailurePartLives in
Declares “done” earlyFeedback: tests decide donea Stop hook that runs the suite; a feature list that starts failing
Forgets the house rulesContext: rules loaded every sessionCLAUDE.md, loaded every session, kept short
Runs something dangerousAccess rules, gatesdeny rules, can_use_tool / PreToolUse, a sandbox
Drowns in its own contextContext: isolationa subagent for side work; server-side compaction
Redoes work after a restartStateprogress file and a git commit per session; a run ledger
Spins or overspendsLimitsmax_turns and a dollar cap per run
REAL SYSTEM

Illustrative example: a team’s coding agent on Opus 5.5 finished 58 of 120 tasks on its internal eval (48%). Swapping the model for Fable 5.1 — 2.5× the price per token — took it to 65 (54%). Keeping Opus 5.5 and adding two harness parts instead, a Stop hook that runs the tests and a progress file each session starts from, took it to 85 (71%). The loop code did not change in either experiment.

FOLLOW-UP TRAP

“So to engineer the harness you write your own loop?” — No. The Tool Runner and the Agent SDK give you the loop and still let you own the harness: can_use_tool and PreToolUse for approvals, PostToolUse to rewrite a result, max_turns and max_budget_usd for limits; Managed Agents adds hosting. Hand-write the loop only for control flow the hooks cannot express.

Learn it in full: Who Owns the Loop: Harness vs Deployment — harness versus deployment, and which of the four ways to build gives you what. Then read Harness Engineering: Building the System Around a Coding Agent — the harness parts in Claude Code, file by file.

Q10
When is a multi-agent system worse than a single agent?
Multi-AgentArchitectureCost OptimizationAgents

Commonly asked at Anthropic · Cursor · Sierra

open question
How to Answer

“Most of the time, honestly. A multi-agent system is a context-management strategy: workers burn their own windows on the reading and hand the lead a short report. You pay for that in tokens — Anthropic measured roughly four times a chat for one agent and fifteen times for a team — so it only wins when the work is broad, parallel and worth that bill.

It’s worse when the steps are coupled. Every action carries implicit decisions, so two workers editing the same module make conflicting ones, and the merge is worse than either. It’s worse when the task fits in one window anyway, and worse when the task is cheap, because a router in front would have sent it down a one-call path.

It’s also worse to operate. Every hop loses context, results come back partial, retries multiply per layer, and a run that says ‘success’ at the root can have two failed children under it.

So I start with one agent and only split when an eval shows context pollution or tool confusion in it — and then a star, two levels, parallel reads, serialised writes.”

Two questions decide it: is the work separable, and is it worth 15×?

Multi-agent wins in one quadrant: high-value work that splits into independent reads
WHERE A TEAM PAYS FOR ITSELF high valueper task low valueper task coupled: steps share decisions and writes separable: independent reads ONE AGENT, MORE EFFORT a refactor, a multi-file bug fix parallel writers make conflicting decisions spend on effort or a bigger model MULTI-AGENT PAYS broad research, a codebase-wide audit each worker reads in its own window ~15× chat tokens, bought on purpose ONE CALL OR ONE AGENT classify, extract, a short Q&A fits in one window anyway a team here is pure overhead FAN-OUT IN CODE the same prompt over 1,000 documents a loop or the Batch API no orchestrator deciding anything three of four quadrants want one agent or plain code — split only when an eval shows the single agent failing

Where a team loses to one agent

Team losesGoes wrongOne agent
Parallel writestwo workers edit one module; each passes its tests, the merge failsone writer per artifact; parallelise only the investigation
Sequential stepsworker B waits on A’s report; no parallelism, extra hopsone loop keeps the full context
Lossy briefs“no issues found” means “could not read the gateway”no hand-off to lose; if you split, a gaps field
Stragglersfan-in waits for the slowest worker’s deadlineone timeline, one timeout
Nested retriesSDK × app retries: a failing leaf makes 9 calls, 27 at three layersretry at one layer
Debuggingroot says success; two children failed underneathone transcript to read

Price it per finished task, not per call

SetupPer taskPer finished
One agent, Opus 5.555K in · 5K out · $0.32 · 60% succeed$0.53
Team: Opus 5.5 lead + 4 Sonnet 5.5 workers205K in · 21K out (3.8×) · $0.76 · 80% succeed$0.95

Illustrative. Lead: 45K in, 5K out = $0.28. Each worker: 40K in, 4K out = $0.12. The team is 33% more likely to finish and still 1.8× the price per finished task — worth it only when a miss costs more than the gap.

REAL SYSTEM

Illustrative example: a four-agent bug-fix team (planner, coder, tester, reviewer) passed 122 of 200 issues (61%) on its eval at 3.1× the tokens of one agent, which passed 132 (66%). Most losses were coder and tester disagreeing about an interface. The team collapsed it to one agent plus a read-only review subagent, and kept a fan-out only for “find every call site” across the monorepo — the one step that was broad, read-only and parallel.

FOLLOW-UP TRAP

“Anthropic’s research system beat a single agent by 90.2%.” — On breadth-first research, where reads split cleanly, and in the same write-up token usage alone explained 80% of the variance on BrowseComp. Much of the win is the tokens. So the honest baseline is not one agent on default settings; it is one agent at higher effort, on the same budget. Beat that before you pay for a team.

Learn it in full: Hierarchical Delegation: Multi-Agent Teams — context isolation, the delegation brief, the 4× and 15× token bill and the coordination failures.