Noemic
Back to Writing

Building AI Agents That Actually Work

·Shahid·7 min read
agentsengineering

There is a particular kind of disappointment that only AI engineers know. You build an agent that navigates a complex workflow flawlessly in a demo. It reasons through ambiguity, calls the right tools, recovers from mistakes. You show it to the team, and everyone agrees: this is the future. Then you deploy it, and within 48 hours it has hallucinated a database migration, called an API 9,000 times in a loop, and politely informed a customer that their account has been deleted when it hasn't.

The gap between an agent that works in a demo and an agent that works in production is not a gap in model capability. It is a gap in engineering discipline. Here is what we have learned closing that gap.

The Demo-to-Production Chasm

Demo agents operate in controlled environments with predictable inputs, short horizons, and a human watching the screen. Production agents face adversarial inputs, long-running sessions, concurrent execution, partial failures, and nobody watching anything until something breaks.

The fundamental issue is that most agent architectures are designed around the happy path. They assume the model will choose the right tool, that the tool will succeed, that the output will parse cleanly, and that the next step in the plan still makes sense given the result. In production, every one of those assumptions fails regularly. Not occasionally -- regularly.

Building production agents means designing for the unhappy path first. Every decision the agent makes should have a bounded blast radius. Every external call should have a timeout, a retry policy, and a fallback. The system should be more suspicious of its own outputs than any user will ever be.

Tool Use Architecture and Why It Matters

The way you expose tools to an agent determines most of its failure modes. A common mistake is giving the agent a large, flat list of tools and trusting the model to pick the right one. This works in demos with five tools. It falls apart in production with thirty.

Tool architecture should be hierarchical and scoped. Group tools into capability domains. Gate access to destructive operations behind confirmation steps or separate agent roles. Make tool descriptions precise and unambiguous -- the model is selecting tools based on natural language descriptions, so vague descriptions produce vague behavior.

Most importantly, design your tool interfaces to be idempotent wherever possible. If an agent retries a failed step, the tool should not create a duplicate record, send a duplicate email, or charge a customer twice. Idempotency is not a nice-to-have in agent systems. It is load-bearing infrastructure.

Evaluation Beyond Vibes

The most dangerous phrase in agent development is "it seems to work." Vibes-based testing -- running the agent a few times and eyeballing the output -- is how broken agents reach production.

Rigorous agent evaluation requires three layers. First, unit-level evaluation of individual tool calls: given this context, does the agent select the right tool with the right parameters? This is testable, deterministic, and fast. Second, trajectory evaluation: given a task, does the agent take a reasonable path to completion? This requires defining what "reasonable" means for your domain, which forces you to think carefully about acceptable behavior. Third, outcome evaluation: did the agent actually accomplish the goal, and did it do so without unacceptable side effects?

Build evaluation datasets from real production traces, not synthetic examples. Synthetic data tells you whether the agent can handle the cases you imagined. Production traces tell you whether the agent can handle the cases that actually occur, which are invariably stranger.

Multi-Agent Orchestration Patterns

Single-agent architectures hit a ceiling quickly. The context window fills up, the model loses track of earlier reasoning, and the tool namespace becomes unwieldy. Multi-agent systems solve this by decomposing complex tasks across specialized agents.

The patterns that work in production are simpler than the academic literature suggests. A supervisor agent that delegates to specialist agents, each with their own scoped tools and focused system prompts, handles the majority of real-world use cases. The supervisor maintains the high-level plan and state. The specialists execute bounded subtasks and return structured results.

The critical design decision is the communication protocol between agents. Passing raw natural language between agents compounds hallucination risk at every hop. Instead, define structured message schemas for inter-agent communication. The supervisor sends a typed task definition. The specialist returns a typed result. Natural language stays inside each agent's reasoning; it never becomes the wire format.

Avoid the temptation to build elaborate multi-agent topologies with agents negotiating, debating, or voting. These patterns are fascinating in research papers and fragile in production. Keep the orchestration graph simple, directed, and deterministic. The intelligence lives in the individual agents. The orchestration should be boring.

Error Handling and Graceful Degradation

Production agent systems fail constantly. APIs return 500 errors. Models produce malformed JSON. Context windows overflow. Rate limits hit. The question is not whether your agent will encounter errors, but whether it will handle them with the same grace it handles the happy path.

Every tool call should be wrapped in error handling that distinguishes between retryable failures and terminal failures. A network timeout is retryable. A permissions error is not. An agent that retries a permissions error ten times is wasting resources and time. An agent that gives up on a network timeout after one attempt is unnecessarily fragile.

Beyond individual error handling, design the overall system for graceful degradation. If the specialist agent fails, the supervisor should be able to attempt a simpler version of the task, or escalate to a human, or skip the subtask and continue with the rest of the workflow. The worst outcome is not a failed step. The worst outcome is a failed step that cascades into a frozen system with no path forward.

Implement circuit breakers around external dependencies. If a downstream API is failing consistently, stop calling it. Serve a cached result, a default value, or an honest error message. Do not let a single broken dependency turn your entire agent system into a queue of failing requests.

The Case for Deterministic Fallbacks

This is the least glamorous and most important principle in production agent work: every AI-driven decision should have a deterministic fallback.

If the model cannot classify a customer request, route it to a default queue. If the agent cannot generate a valid API call, use a hardcoded template. If the planning step fails, execute a predefined standard workflow. The fallback does not need to be intelligent. It needs to be reliable.

Deterministic fallbacks serve two purposes. First, they guarantee baseline functionality. Your system does something reasonable even when the AI components fail entirely. Second, they provide a performance floor for evaluation. If your agent cannot outperform the deterministic fallback on a given task, that task should not be handled by an agent at all.

The teams that ship reliable agent systems are not the ones with the most sophisticated prompts or the newest models. They are the teams that treat AI as a powerful but unreliable component within a larger system -- a system that is engineered, tested, monitored, and designed to fail well. The agent is the most interesting part of the system. The engineering around it is the part that matters.


Interested in working together?

Get in touch