If you've built APIs, data pipelines, or internal tools, you already have most of the intuition you need to build AI agents. The gap is knowing when an agent is the right abstraction, how to design tools and instructions that make it reliable, and what to do when it inevitably behaves in ways you didn't predict.
This guide walks you through each of those decisions. You'll learn how to decide whether your problem actually needs an agent, how to structure agent logic in TypeScript, and how to ship agents that hold up in production with guardrails, evals, and observability.
When to build an agent
Your first decision is whether an agent is the right tool for the job. Agents add complexity. They burn tokens. They introduce non-determinism. Reaching for one when a simple function call or a rules engine would suffice is a common and expensive mistake.
Tasks that benefit from autonomous multi-step reasoning
You should consider an agent when your workflow involves open-ended reasoning over multiple steps, where the number and order of operations can't be fully specified in advance. A few signals point toward agent-shaped problems:
-
Nuanced judgment calls: the task requires weighing ambiguous context rather than matching predefined rules, like triaging a customer support ticket that could involve billing, technical issues, or account security
-
Unstructured input: the workflow processes natural language documents, emails, or free-form user queries where meaning must be interpreted before any action is taken
-
Brittle rule systems: you already have a decision tree or rules engine that has grown unwieldy, and every new edge case demands another branch
The common thread is that these tasks resist hardcoding. A rules engine for payment fraud, for example, checks preset thresholds. An agent evaluates context, considers subtle patterns, and catches suspicious activity even when no specific rule was violated.
Tasks where deterministic code is still the better choice
Not every automation problem needs a language model. You should stick with conventional code when your workflow has a fixed, well-defined set of steps, the inputs are structured, and correctness is binary.
Examples include CRUD operations against a database, form validation, scheduled data exports, and most ETL pipelines. These tasks require reliability, not judgment. Adding an LLM introduces latency, cost, and the possibility of wrong answers for problems that already have right ones.
A useful heuristic: if you can write a flowchart with no ambiguous decision nodes, you don't need an agent. Start with deterministic code and only introduce an agent at the specific points where human-like reasoning is required.
Agent design foundations
Once you've decided an agent is the right fit, your next step is assembling its core components. Every time you build AI agents, regardless of framework, you work with three things: a model for reasoning, tools for taking action, and instructions for guiding behavior.
Selecting your model
You don't need to pick one model and commit. Different steps in your workflow may warrant different models, and the ability to swap models quickly matters more than the initial choice.
Start with the most capable AI model available to establish a quality baseline. Use a reasoning model like GPT-4o or Claude Sonnet 4 while you're still validating whether your agent can solve the problem at all. Once it works, try routing simpler subtasks to smaller, faster models. Classification, structured extraction, and simple lookups rarely need top-tier reasoning. Gemini 2.5 Pro is worth evaluating for workflows with long context requirements, given its one-million-token context window.
Key tradeoffs to keep in mind:
-
Larger models produce higher-quality outputs but cost more and add latency
-
Reasoning models allocate internal compute before responding, which improves accuracy on complex tasks but slows down simple ones; most let you dial reasoning effort down to "low" for faster responses
-
Context windows vary widely, from 128k tokens up to one million tokens for Gemini 2.5 Pro and Claude Sonnet 4
The principle is practical: make it work, then make it fast and cheap. Using a model routing library lets you swap providers with a one-line change instead of ripping out an entire SDK integration.
Defining tools
Tools are functions your agent can call to interact with the outside world: querying a database, sending an email, hitting a third-party API. Designing your tools well is arguably the most important step when you build AI agents that need to be reliable.
Think about it the way you'd brief a human analyst. If you asked an analyst to research a topic, you wouldn't hand them a single massive database dump. You'd give them specific queries and operations: search by keyword, filter by date range, pull up a particular record. Well-defined tool integrations give your agent the same kind of focused access.
Follow these principles when defining tools:
-
Name each tool semantically so the model understands its purpose (searchOrdersByCustomerId, not doLookup)
-
Write a clear description covering both what the tool does and when to call it
-
Define strict input and output schemas so the model knows exactly what data to provide and what it will receive
-
Keep each tool focused on a single operation rather than bundling multiple actions into one
As your tool count grows, watch for overlap. The model struggles less with 15 distinct tools than with 8 that sound similar. If you find tool selection degrading, it's time to split your agent into multiple specialized agents rather than adding more tool descriptions.
Configuring instructions
Your agent's system prompt is its operating manual. Vague instructions produce vague behavior. Clear, specific instructions, informed by the same principles as prompt engineering, produce agents that follow predictable routines.
Structure your system prompt in layers:
-
Role and scope: tell the model who it is and what domain it operates in ("You are a senior account manager for a SaaS billing platform")
-
Explicit constraints: define what the agent must never do ("Never issue a refund exceeding $500 without escalating")
-
Step-by-step routines: break complex workflows into numbered steps the model can follow
-
Edge case handling: anticipate common failure modes and tell the agent how to respond ("If the customer cannot provide an order number, ask for the email address associated with the account")
Different models respond to different formatting cues. Claude tends to follow instructions better when given structural scaffolding like XML-style tags. GPT models respond well to markdown-style syntax and delimiter cues. Test your prompts against the specific model you're using.
If you're starting from existing standard operating procedures or support scripts, you can feed those documents into a capable model and ask it to convert them into agent-friendly instructions. This gives you a strong starting point to iterate from.
Orchestration patterns
Your orchestration pattern determines how work flows through your system. You have two broad options: a single agent with many tools, or multiple specialized agents coordinating with each other.
Single-agent systems are the right starting point for most projects. One agent, one system prompt, a growing set of tools. You can use prompt templates with variables to handle variations across use cases without duplicating agents. This keeps evaluation simple and avoids coordination overhead.
Multi-agent systems make sense when a single agent's prompt gets too long, tool selection degrades, or you need clear separation of concerns between domains. Two common patterns emerge in multi-agent systems:
-
Supervisor pattern: a central "manager" agent delegates to specialized subagents via tool calls and synthesizes their outputs into a unified response
-
Handoff pattern: peer agents transfer execution directly to each other based on the task at hand, with no central coordinator
The key insight from Mastra's Principles of Building AI Agents is that the best architectures are discovered by iterating, not designed upfront. Start with one agent solving one problem. When it becomes unwieldy, split it. When you have multiple agents, add routing logic. You never want to build a "master agent" that does mediocre everything.
Building guardrails for AI agents
Your agent will encounter adversarial inputs, ambiguous edge cases, and situations where the safest action is to stop and ask a human. Guardrails are the constraints that keep your agent from doing something it shouldn't.
Types of guardrails
You can think of guardrails as a layered defense. No single guardrail catches everything, but combining several creates a resilient system. Common categories include:
-
Input guardrails: intercept incoming messages before the LLM processes them, checking for prompt injection, jailbreak attempts, and PII
-
Output guardrails: screen generated responses for data leakage, hallucination, bias, or off-brand content before they reach the user
-
Tool safeguards: assign risk ratings (low, medium, high) to each tool based on factors like write access, reversibility, and financial impact, then gate high-risk tools behind additional checks
-
Rules-based protections: deterministic checks like blocklists, input length limits, and regex filters that catch known threats without needing a model call
-
Relevance classifiers: lightweight models that flag off-topic queries before the main agent wastes tokens on them
Guardrails map one-to-one to the vulnerabilities they address. Name them by what they do: "prompt injection guard," "PII filter," "refund amount validator." This makes your system auditable and easy to extend.
Prompt injection and output sanitization
You should treat prompt injection as a first-class security concern. Models have gotten better at resisting direct jailbreaks, but injection attacks have grown more sophisticated as agents have gained more autonomy. An agent that browses the web or reads uploaded documents can encounter malicious instructions embedded in that content.
Simon Willison describes a "lethal trifecta": when an agent combines access to private data, exposure to untrusted content, and the ability to communicate externally, an attacker can use prompt injection to exfiltrate sensitive information. This exploit has been used successfully against production systems including Microsoft Copilot, Cursor, Jira, and Zendesk.
The fix is to remove one leg of the triangle. The easiest to remove is usually the exfiltration vector: constrain your agent so that untrusted input cannot trigger actions with negative side effects. Add input processors to intercept and modify messages before they reach the model, and inspect output chunks during streaming plus the complete response afterward.
Human-in-the-loop checkpoints
Agent performance can be uneven. There are often classes of tasks where your agent will continue to underperform even after extensive prompt tuning. The right pattern depends on how much latency your workflow can tolerate and how consequential errors are.
| Pattern | How it works | Best for |
|---|---|---|
| In-the-loop | The agent pauses mid-execution, asks a human for input or clarification, then resumes with that context | High-stakes decisions where real-time human judgment is required |
| Review before delivery | The agent produces a draft, a human reviews and approves or revises it, then it goes to the end user | Content or communications workflows where quality matters more than speed |
| Deferred tool execution | The agent collects feedback asynchronously without blocking its own execution | Production workflows where humans shouldn't need to monitor the agent in real time |
In almost any human-in-the-loop architecture, the human becomes the bottleneck. Design your system to queue work gracefully. For high-stakes or irreversible actions (deleting production data, issuing large refunds), always require explicit human approval until you have strong evidence that the agent handles those cases correctly.
Build AI agents in TypeScript on Mastra
If you're working in TypeScript, Mastra gives you a structured way to build AI agents without stitching together a dozen separate libraries. It's an open-source framework (Apache 2.0) that works with Vercel's AI SDK, providing agents, workflows, memory, evals, and observability in a single package. Mastra's team authored Principles of Building AI Agents, and this guide draws from many of the patterns covered there.
You define an agent with a model, instructions, and tools. Model routing across hundreds of models across dozens of providers through a single interface is built in, so swapping from OpenAI to Anthropic to Google is a config change. Workflows let you chain deterministic steps with .then(), add branching with .branch(), and fan out work with .parallel(). You compose workflows using createWorkflow and finalize them with .commit(). The suspend/resume primitive handles human-in-the-loop patterns natively, letting you persist workflow state and pick up exactly where you left off when external input arrives.
Mastra also includes a RAG pipeline with built-in document chunking, embedding, and vector search for knowledge-base retrieval. Its eval framework supports LLM-as-judge, tool-call accuracy, and task-completion metrics, so you can measure agent quality from day one. Tracing via OpenTelemetry is integrated out of the box, giving you full visibility into every agent step, tool call, and model interaction during development and in production. The local dev environment at localhost:4111 lets you inspect traces, test prompts, and iterate on agent behavior before deploying.
Build your first TypeScript agent on Mastra.
Observability, evals, and debugging agent runs
Your agent will ship bugs that return 200 OK. Unlike traditional software, where failures are loud, agent failures are subtle: a wrong tool choice, a hallucinated fact, a slow drift in output quality. The only way to catch these is to instrument everything and measure continuously.
Tracing agent steps and tool calls
You need visibility into every step your agent takes. A trace captures the full tree of spans across a run: which tools were called, what inputs they received, what the model returned, how long each step took, and how much it cost.
The standard format is OpenTelemetry (OTel). A good tracing setup gives you:
-
Trace view: a timeline showing each pipeline step and its duration
-
Input/output inspection: the exact JSON flowing into and out of every LLM call and tool invocation
-
Call metadata: status codes, latency, token counts, helping you scan for anomalies
Mastra surfaces traces in its local development environment at localhost:4111, where you can inspect agent decision-making, review prompts and responses, and debug function calls without deploying anything.
In production, export your OTel traces to a collector and build dashboards around latency, error rates, and token spend. Some startups have learned the hard way that a viral agent can rack up hundreds of thousands of dollars in token bills in a single month. Trace-level cost attribution is not optional.
Running evals against agent outputs
Traditional tests have binary pass/fail conditions. Agent outputs are non-deterministic, which means you need a different approach: evals.
The most versatile technique is LLM-as-judge, where you pass the agent's output, the original input, and any retrieved context to a separate model with a scoring rubric. It works well, scales, and handles cases with no single right answer.
One caveat: judge models tend to prefer longer answers, so consider using a judge from a different model family than the one powering your agent.
Other eval types you should implement:
-
Tool calling evals: did the agent call the right tools with the right arguments? Similar to expect(fn).toBeCalled in Jest.
-
Multi-turn evals: run the agent through a full conversation and grade the entire trajectory for context maintenance, error recovery, and task adherence.
-
Task completion evals: the most important measure. Did the agent finish the job?
For building eval datasets, mix three sources: hand-curated examples (forces clear thinking about what "good" looks like), synthetic generation (fast, but models tend to generate easy cases), and production logs (highest signal, but only available once you're live). A mature dataset combines all three.
Monitoring agents in production
Your eval suite shouldn't stop running after you deploy. Production data is not static. Users will find use cases you never anticipated, and output quality can degrade as input patterns shift.
Run online evals against sampled production traffic using binary or categorical grading. LLMs are better at literacy than numeracy, so avoid numerical scoring scales. Combine automated eval results with periodic expert review: have domain specialists classify failure modes and label data points so you can cross-reference failures against your north-star business metrics.
This review process creates a feedback loop. Subject matter experts review production outputs, a PM prioritizes failure modes by business impact, and engineers experiment with prompt changes and model swaps against prebuilt failure-mode datasets. The PM validates changes against historical data before shipping. This cycle is how you move from a demo agent to a production system.
Multi-agent architectures and workflow orchestration
You will eventually hit a ceiling with a single agent. When your system prompt exceeds a few thousand words, tool selection accuracy drops, or your agent needs to handle tasks that span different domains, it's time to decompose. Knowing how to build AI agents that coordinate with each other is the next step.
When to split work across multiple agents
Your agent is telling you it needs to be split when you see specific symptoms:
-
Tool selection accuracy degrades as you add more tools
-
The system prompt contains deeply nested conditional logic
-
Different parts of the workflow require different models, such as a fast, cheap model for classification and a reasoning model for complex decisions
-
You need clear ownership boundaries for different teams to maintain different parts of the system
Design your agents the way you'd design an organization. Group related capabilities into a job description. A sales workflow, for example, might decompose into customer research, account synthesis, and next-step recommendations, each handled by a focused subagent with its own toolset.
Orchestrator-subagent patterns
You can start with the most common multi-agent pattern: a supervisor agent that delegates to specialized subagents via tool calls. You wrap each subagent as a tool available to the supervisor, and the supervisor decides which subagent to invoke based on the current state of the task.
This pattern keeps control centralized. The supervisor sees all context, routes work, and synthesizes results. It works well for workflows where a single coherent output is required, like a publisher agent coordinating a copywriter and an editor.
You can also pass entire agentic workflows as tools to agents. Each workflow provides deterministic structure (ordered steps, branching, merging) while the agent provides the reasoning about which workflow to invoke and how to interpret results. This hybrid approach gives you the reliability of structured orchestration with the flexibility of autonomous agents.
One trap to watch for: parallelized subagents that are unaware of each other's work can produce conflicting outputs. If Subagent A designs a UI with one aesthetic and Subagent B writes copy with a contradictory tone, the supervisor is stuck reconciling incompatible results. When tasks are tightly coupled, use a single-threaded linear agent that preserves continuous context instead of parallelizing.
Agentic AI in practice: coding agents and knowledge-base retrieval
Agentic AI systems don't all look the same. A coding agent, for example, can write, test, and iterate on code autonomously. Tools like Claude Code and Codex operate this way, calling terminal commands, running tests, and patching failures in a loop. On the retrieval side, AI agents that query a knowledge base apply similar principles: they break a question into sub-queries, retrieve relevant chunks, synthesize an answer, and check it for accuracy before returning it.
Both patterns share the same underlying structure: a model, a set of tools, and a loop. The difference is the tool set and the domain. Understanding this commonality helps you reuse agent infrastructure across use cases rather than building from scratch each time. LangChain popularized this pattern, and you'll find the same loop concept across every major AI agent framework. MCP servers are another way to give agents standardized access to external tools without custom integration code for each one.
Tools don't just cover internal systems. Agents commonly make API calls to third-party services. A support agent might query a ticketing system, while a sales agent might pull lead data from a CRM or enrich contacts from an external database.
Low-code and no-code options, including visual builder platforms and dedicated AI agent builder tools, have lowered the barrier to prototyping AI automation workflows. For many teams, these are a useful starting point. But serious production deployments still benefit from code-first AI agent frameworks that give you full observability, version control, and the ability to run evals. Think of no-code options as useful for demonstrating what's possible to stakeholders, and code-first frameworks as what you reach for when you need something reliable.
Multi-agent systems introduce coordination overhead that single-agent setups don't have. When you decompose work across multiple agents, you need a clear contract between them: what each agent produces, what format it uses, and what happens when it fails. Large language models are generally capable of following these contracts when they're specified clearly in the system prompt, but you should still write integration tests that verify the handoff behaves correctly end-to-end. Frameworks like CrewAI provide role-based abstractions that make these contracts more explicit.
More complex agentic systems, those spanning many specialized agents, long-running workflows, and external data sources, benefit most from suspend/resume primitives and durable state management.
Durable execution and suspend/resume
Your production agents will often need to pause while waiting for something outside their control: a human approval, a webhook from an external service, or a long-running computation. Keeping a running process alive while waiting is fragile and expensive.
The better pattern is suspend/resume. Persist the workflow state to a durable store, shut down the process, and pick up exactly where you left off when the external event arrives. This is conceptually similar to durable execution engines like Temporal and Inngest, but applied to agent workflows.
In practice, you need to ensure that your workflow state is serializable and that your storage layer survives server restarts. A suspended workflow that lives only in memory won't survive a deploy.
Streaming also matters here. One of the keys to making LLM applications feel fast is showing users incremental progress. Stream model output tokens as they're generated and send status updates from each step in multi-step workflows. Users will tolerate a 30-second agent run if they can see what's happening. They won't tolerate 10 seconds of silence.
Getting started
The fastest way to build AI agents that hold up beyond a demo is to start small, instrument everything, and expand only when you have evidence that a single agent can't handle the load. Pick one well-scoped problem, wire up tracing and evals from day one, and let production data guide your architecture decisions rather than designing for complexity you don't yet need.

