·3 min read

We Gave Our Agent a Third Tool. Then We Deleted It.

build-in-publicailanggraphai-engineeringllmopspythontool-useagent-design|
PostLinkedIn
Trade-off card comparing the chosen two-tool agent design against the rejected bound self-check tool, showing the measured cost and accuracy regressions.

Melio — the AI nutrition agent I work on — generates day meal plans through a LangGraph ReAct loop. Yesterday we closed the hardening program for its tooling layer, and the most valuable result wasn't a feature we shipped. It was a tool we deleted.

The two-tool baseline

Exactly two tools are advertised to the LLM:

  • lookup_usda_batch — a schema-enforced, batch-per-meal USDA lookup. The server caches the full 59-nutrient panel; the model only ever sees a 4-macro hint (calories, protein, fat, carbs). The Anthropic API is stateless and re-sends the whole conversation every turn, so those trimmed payloads are exactly why the loop is cheap.
  • DayPlanLLMSubmit — a synthetic, strict-schema exit tool with no nutrition fields. It's bound as a tool but never executes; calling it is the day plan. The server computes every number from the cached USDA panels, so the model literally cannot hallucinate a calorie.
Everything else is machinery: a deterministic 7-line router, a dedup-aware force-commit guard that ends browsing after 6 billable tool rounds, a telemetry-wrapped ToolNode. The bet: reliability comes from two tools + machinery, not from more tools + model judgment.

The tempting third tool

We had a verify_meal_against_target self-check tool sitting unbound in the codebase — a deterministic deviation check with a fix hint. Back in the Haiku days, binding it made the model oscillate on adjust-and-re-verify, so it stayed off. But the deployed model is Sonnet now, and the argument kept coming back: surely a smarter model would use one polite verify call before submitting.

So we bound it and ran the eval:

  • Tool calls per plan: 2 → 10.7
  • Force-commit rate: 0 → 1.0 — every single day hit the round budget and had to be forced to submit
  • Redundant call rate: 0 → 0.375
  • Cost per plan: $0.34 → $1.20 (3.5×)
  • Calorie deviation: worse, 1.36% → 3.62%
Sonnet failed the same way Haiku did: a verify → adjust → re-verify spiral until the budget slammed the door. The kicker is that the server-side portion solver already performs the same adjustment deterministically, for free, on every plan. The model does not need to self-check what the server already checks deterministically.

Delete, don't disable

The bound self-check tool was rejected — code, flag, budget and telemetry carve-outs, all deleted — and the rejection is written into the layer docs as part of the final design: don't re-add a self-check tool without a bounded verify-round cap designed in from the start and a fresh eval pass. Before the experiment, the two-tool surface was generating plans at $0.34 with zero force-commits. It still is.

The lesson

Two things turned this into a 30-minute decision instead of a week of debate. First, the design thesis was explicit and written down, so the experiment had a falsifiable claim to test. Second, the eval harness: without cost_per_plan and force_commit_rate as measured metrics, "the verify tool feels helpful" would have won the argument. Every tool you bind is a cost the model pays on every turn — and numbers, not taste, are what let you delete code with confidence.

Oleksandr Yusypenko

Oleksandr Yusypenko

Senior Full-Stack + AI Engineer. Building in public — AI agents, LangGraph, production systems.