AI Engineer Dojo Contents
Chapter 10

Production Agents

Everything so far produces an agent that works. Production means it keeps working at 3 a.m., under load and on a budget, while nobody's watching. That takes tracing of every step, firm limits on cost and time, and ordinary infrastructure that turns a clever loop into a service.

Case study

Continuum was blind in production

Continuum's agent passed its test suite at 82% and completed only about 64% of real tickets. Real tickets were messier, dependencies hit rate limits under load, and when something went wrong the team had no record of what the agent had done. Every incident was guesswork.

They added full traces of every step, tool call and decision; live monitoring of completion, steps and escalations; and regular sampling of real tickets into the test suite. Time to diagnose an incident dropped from hours to minutes, and the live metrics caught regressions the offline suite missed.

Tracing

An agent failure happens somewhere along a path, so a single "request failed" log line doesn't help. For every run, record each step: what was sent to the model, what came back (including tool calls), each tool result, tokens and time per step, and why the run ended. When a user reports a strange answer, you open the trace and see which step went wrong: a bad lookup at step 3, a timeout at step 5, an invented argument at step 7.

The minimum trace

A run ID, and for each step: messages sent, the model's reply and tool calls, tool results, input and output tokens, time taken, and the final stop reason. Store it as structured records you can query.

Controlling cost

Prompt caching. The system prompt and tool definitions are large, identical every turn, and resent every turn. Caching them means you pay full price once and much less on later turns. For an agent with a 2,000-token block of instructions and tools, it's often the biggest single saving available, for very little work.

Hard budgets. On top of the step limit from Chapter 3, set a token or dollar budget per request. If a run passes it, stop and return what you have. That turns the risk of a runaway request from unlimited into a known maximum.

Latency

People wait more patiently when they can see progress. Stream updates like "searching…" and "found 3 results, comparing…" so a 12-second task feels like work in progress. Run independent tool calls in parallel (Chapter 2), and cache tool results that don't change during a session.

Concurrency and rate limits

In production many agents run at once, and one request can turn into dozens of model and tool calls. You'll hit the provider's rate limits and those of your own services. The answers are standard: limit concurrency, use queues, back off on 429 responses, and set per-customer quotas so one heavy user can't slow down everyone else.

Try it · ~25 minFree · no API key

Trace a run and enforce a token budget

Add per-step tracing and a hard token budget to the loop. Show that the trace lets you find the step that caused a bad answer, and that the budget stops a runaway. The recorded run has one bad step and one runaway.

Starter code

STEPS = [   # (tool, tokens_this_step, latency_s, note)
  ("search",     900, 1.1, "ok"),
  ("read_doc",  1400, 1.4, "ok"),
  ("summarize", 1200, 1.2, "retrieved WRONG doc"),   # the real bug
  ("search",    1600, 1.5, "ok"),
  ("summarize", 1800, 1.6, "ok"),
  ("search",    2000, 1.7, "ok"),                    # would exceed budget
]
BUDGET = 6000     # total input tokens allowed per request

def run_traced():
    trace, total = [], 0
    for i,(tool,tok,lat,note) in enumerate(STEPS, 1):
        if total + tok > BUDGET:
            return trace, f"stopped: budget at step {i}"
        total += tok
        trace.append({"step":i,"tool":tool,"tokens":tok,"lat":lat,"note":note})
    return trace, "natural"

# TODO: print the trace rows, the stop reason, and the step whose note
# explains a bad final answer.
Worked solution
trace, reason = run_traced()
for r in trace: print(r)
print(reason)
bad = [r for r in trace if "WRONG" in r["note"]]
print("blame step:", bad[0]["step"] if bad else "none")
# => ... rows ...
#    stopped: budget at step 5      (cumulative tokens crossed 6000)
#    blame step: 3

The run stops at the step where the running total passes 6,000 tokens, returning a partial result instead of continuing indefinitely. The trace makes the real defect obvious: step 3 fetched the wrong document, which explains the bad answer even though no step raised an error. To go further, compute cost per run from the trace and look at the spread across many runs. The median and the 99th percentile are what capacity planning and pricing depend on.

Running case · Ridgeline

Ridgeline keeps a full trace for every ticket and watches live completion and escalation rates. When completion dips after a tool change, the traces show which step broke, and it's fixed the same day.

If you're starting out

Add tracing to your portfolio agent, even if it's just a JSON file per run. Being able to open a trace in an interview and walk through where something went wrong is very persuasive. Look up prompt caching in your model provider's documentation, too; it's a common interview topic because it saves real money.

If you run the team

Ask whether the team can pull up a full record of what the agent did on any given request. If not, every customer complaint will take hours to investigate. Ask what the most expensive single request can cost, and whether there's a per-request budget. And ask for completion, escalation and cost per task on a weekly dashboard, so you see trends before customers do.

Quiz · Chapter 10

  1. Why is a single "request failed" log line useless for debugging an agent?
  2. Why does prompt caching help agents in particular?
  3. What is a per-request token budget for?
  4. What does streaming progress updates mainly improve?
  5. What should the minimum trace record for each step?
Back Continue

The AI Agent Engineer · aiengineerdojo.com