« Phase 02 README · Warmup · Hitchhiker's · Deep Dive · Core Contributor · Staff Notes

Phase 02 — Principal Deep Dive: Tool Calling, Structured Output & the Repair Loop

The Deep Dive treated the validator as a mechanism. This document treats it as an architectural component of a production agent platform — the place where authorization, reliability, cost, security, and observability all converge. The thesis: the tool schema is simultaneously the authorization surface and the reliability gate, and how you design the tool layer moves system-level metrics more than any prompt or model choice.

The schema is the authorization surface and the reliability gate

Two roles collapse onto one artifact. As an authorization surface, the schema is the first predicate that decides whether a model-proposed action is even eligible to run. Nothing reaches issue_refund unless its arguments satisfy the contract — types, ranges, enums, no unexpected fields. As a reliability gate, that same predicate converts a malformed call from a crash into a recoverable, structured observation the loop can repair. One object, checked once, doing both jobs. This is why "validate before execute" is not a coding nicety but the load-bearing architectural decision of the phase.

Reliability compounds — tie it back to p^n

Phase 00 established that an agent's end-to-end success is roughly the product of its per-step successes: an \(n\)-step task at per-step reliability \(p\) succeeds about \(p^n\) of the time. A 90%-reliable step over ten steps is \(0.9^{10} \approx 0.35\) — a coin flip that loses. Push the step to 99% and it is \(0.99^{10} \approx 0.90\).

The tool layer is where you buy those percentage points. A validated-and-repaired call that would otherwise have crashed is a step that succeeds instead of fails. If validation-plus-repair rescues even a fraction of the calls that a naive stack would drop, per-step \(p\) rises, and because reliability compounds multiplicatively, a small local gain is a large end-to-end gain. This is the quantitative case for spending engineering effort here rather than on prompt tinkering: the exponent is unforgiving, and the tool layer is the cheapest lever on the base.

Tool design is the single biggest reliability lever

More than the loop, more than the model, the shape of the tool surface determines whether the agent works. The principal-level design rules:

  • Few, well-scoped tools beat many overlapping ones. A model facing 40 similar tools picks the wrong one; the same model with 6 sharp tools picks right. Every ambiguous pair of tools is a coin flip inserted into the trajectory, and coin flips compound. Tool-surface minimalism is reliability engineering.
  • Names and descriptions are the model's spec. The model has no source code — the description is its documentation. get_order_by_id / "Look up an order by its numeric ID; returns status and total" produces correct calls; orders / "order stuff" produces wrong ones. You are writing a spec for a probabilistic caller, and it will do exactly what the spec implies.
  • Errors must be model-actionable. Return "order 12345 not found; check the ID", never a bare KeyError. The error is an input to the next generation; the model can only recover from what you tell it.
  • Pass IDs and enums, not free text. Do not make the model reconstruct a string it must get byte-exact, or re-parse prose you returned. Hand it stable identifiers and closed value sets — things it is good at echoing.
  • Idempotency for retry-safety. A tool safe to call twice can be retried without double-charging. This is the bridge to durable execution (Phase 08): idempotency keys make the whole layer retry-safe, which is what lets the repair loop and orchestration retries be aggressive without fear.

None of this is glamorous. All of it is the difference between an agent that works 60% of the time and one that works 95%.

The repair loop: a cost and latency budget with a stopping rule

Repair is not free. Each round is another model round trip — tokens billed, latency added. Treat it as a budget, not an unbounded retry. The latency math is direct: if a single generation is \(L\) and you allow \(k\) repair rounds, the worst-case tool-resolution latency is \((k+1)\cdot L\) plus validation (negligible). With \(L \approx 1\text{s}\) and max_repairs = 2, a pathological call costs ~3 seconds before you give up — acceptable for an interactive turn, ruinous if it happens on every call. So the stopping rule is architectural: bound the loop, and when the bound is hit, surface to a human or a fallback rather than looping. A model missing data it does not possess will never converge; the cap is what protects your latency SLO and your token bill from an impossible call.

Parallel tool calls: a max, not a sum

When a turn emits several independent calls ("weather in three cities"), the executor should run them concurrently. The latency is then the \(\max\) of the calls, not the \(\sum\) — the same parallel-step win as ReWOO in Phase 01. Three 1-second calls cost 1 second concurrently, 3 seconds serially.

The architectural sharp edge: only genuinely independent calls parallelize. If call B consumes call A's result, they are sequential by data dependency, and a model that emits them in parallel has made an orchestration error your executor must catch — detect the dependency (or the impossibility of B without A's output) and serialize, rather than firing B against a missing input. Parallelism is a latency optimization the orchestration layer owns, not a blank check the model gets to write.

Security: validation is the first gate

The tool boundary is where adversarial input first meets real code. A prompt-injection payload (forward-ref Phase 10) that convinces the model to emit issue_refund(amount_cents: 999999999) is stopped not by the model's good intentions but by a maximum bound in the schema. Validation is the first security gate: malformed, out-of-range, or structurally hostile arguments are rejected before they reach a function with side effects.

Frame the blast radius concretely. An unvalidated argument flowing into a payment API is not a bug ticket — it is a wrong-amount charge, a refund to the wrong order, a duplicated transaction. The schema's type, minimum, maximum, enum, and additionalProperties: false are the difference between "the model proposed something wrong and we rejected it" and "the model proposed something wrong and we did it." Defense in depth still applies — schema validation is necessary, not sufficient; business-rule checks and authorization (Phase 13) sit behind it — but it is the gate that fails first and cheapest.

Observability: validation failures are a health signal

Instrument the tool layer as a first-class signal source. Validation-failure rate per tool tells you which tool's schema or description is confusing the model — a spiking rejection rate on one tool is a design defect, not noise. Repair-round distribution tells you how often the model needs a second try and whether any calls are hitting max_repairs (an impossible-call or bad-schema smell). Double-encoding parse errors flag a provider-format regression. These metrics turn the tool layer from a black box into a diagnosable subsystem: you can see the agent's reliability degrade at the tool, attribute it to a specific schema, and fix the contract rather than guessing at the prompt.

The "looks wrong but is intentional" decisions

Three choices look like over-engineering to a reviewer and are deliberate:

  • Writing your own validator's error UX rather than surfacing raw type errors. The error string is a prompt to the next generation; investing in "collect all errors, path each one, phrase them for a model" is investing directly in repair success and therefore in \(p\).
  • Collecting all errors instead of failing fast. It looks less efficient (you keep walking after the first violation) but it collapses a three-round repair into one, cutting latency and tokens net.
  • Rejecting bools where integers are required. It looks like paranoia against a value that "is basically 1" — until true reaches arithmetic or a payment field. The strict check closes a real schema bypass.

Each reads as extra work; each is the principal-level call that the tool layer is where reliability, cost, and security are decided, and is worth the rigor.