Building Reliable AI Agents That Don't Fall Over
Hard-won lessons on tool design, retries, and observability for autonomous agents that run unattended in production.
Most agent demos work once and then quietly rot. The gap between a flashy notebook and an agent you trust to run unattended is mostly boring engineering — the same discipline you'd apply to any distributed system.
The failure modes nobody shows you
When I started shipping agents at scale, three things broke first:
- Tool calls that silently return garbage. The model happily continues.
- Infinite loops where the agent re-tries the same broken action.
- Cost blowups from runaway token usage on a single stuck task.
Design tools like APIs, not chat
A tool is a contract. Give it a tight schema, validate inputs, and return structured errors the model can actually reason about.
type ToolResult<T> =
| { ok: true; data: T }
| { ok: false; error: string; retryable: boolean };
async function searchDocs(query: string): Promise<ToolResult<Doc[]>> {
if (!query.trim()) {
return { ok: false, error: "query was empty", retryable: false };
}
// ...
}
The retryable flag matters: it lets your orchestration layer decide whether
to back off or hand control back to the model.
Budget everything
Every agent run gets a hard ceiling:
- max tool calls
- max wall-clock time
- max tokens
An agent without a budget is a denial-of-service attack you wrote yourself.
Observability is the whole game
You cannot debug what you cannot see. I log every step as a structured event:
- the prompt sent
- the tool chosen and its arguments
- the raw result
- the model's reasoning trace (when available)
With that, a failed run becomes a replayable trace instead of a mystery.
Takeaways
- Treat tools as validated APIs with structured errors.
- Put hard budgets on every run.
- Make every step observable and replayable.
Reliability isn't a model problem. It's a systems problem — and that's good news, because we already know how to build reliable systems.
Building AI agents
Related reading
Comments
Comments are enabled once giscus is configured.