Blog
Engineering

Agentic AI Architecture, Part 1: How to Design Reliable AI Agents

A practical guide to agentic AI architecture: what AI agents are, where LLMs fit, and how to design reliable agent systems with state, tools, retrieval, guardrails, and evaluation.

Agentic AI is often described as an LLM that can call tools. That definition is technically true, but it is too shallow for production work.

A reliable AI agent is not just a prompt with access to APIs. It is a controlled software system where a language model makes bounded decisions, updates structured state, calls validated tools, gathers evidence, and produces an answer that can be traced and evaluated.

The important word is bounded. The model should help the system handle ambiguity. It should not own every part of the system.

User intent
  -> workflow routing
  -> structured state
  -> planner decision
  -> validated tools and retrieval
  -> evidence checks
  -> final answer
  -> trace, metrics, and evals

This first part explains the architecture: the layers, responsibilities, and design principles that make agentic AI systems usable outside demos.

Production agent system at a glance
flowchart LR
U[User request] --> R[Workflow router]
R --> S[Structured state]
S --> P[Planner / controller]
P --> V{Runtime validation}
V -->|approved| T[Tools and retrieval]
V -->|missing info| Q[Clarifying question]
T --> E[Evidence and result evaluator]
E --> A[Final answer]
A --> O[Trace, metrics, evals]
E -->|weak evidence| Repair[Target-local repair]
Repair --> T
classDef accent fill:#79863c,color:#fff,stroke:#79863c;
class R,P,E accent;

What “agentic AI” means in practice

An agentic AI system can plan, call tools, use intermediate results, ask clarifying questions, and continue a task across multiple steps. That does not mean it should be free to improvise every action.

A practical definition:

Agent = model + instructions + tools + state + runtime policy + evaluation loop

A production definition:

Agent = bounded decision controller
        operating over structured state
        calling validated tools
        under evidence, cost, latency, and safety constraints

The difference shows up immediately in system design.

A naive agent flow looks like this:

User asks
  -> LLM decides what to do
  -> maybe calls a tool
  -> LLM answers

A production-grade flow is more explicit:

User asks
  -> router identifies the workflow
  -> agent updates structured state
  -> planner returns an action packet
  -> runtime validates the action
  -> tools execute deterministic operations
  -> evidence and quality are checked
  -> answer is synthesized
  -> trace, cost, latency, and failures are logged

This is the central shift: the agent is not the whole product. The agent is one decision layer inside a larger runtime.

The core principle: LLMs should reason, not own the system

Language models are useful because they handle messy input. They can recognize intent, choose a workflow, interpret unclear language, decide which tool may help, and synthesize a clear response from partial information.

They are not reliable as the sole authority for facts, permissions, calculations, business rules, database state, retry logic, cost control, or auditability.

Use this division of labor:

LLM:
  intent recognition
  planning
  tool selection
  clarification
  synthesis

Runtime:
  validation
  retries
  budgets
  tool execution
  state transitions
  traces

Tools:
  deterministic operations
  APIs
  database queries
  calculations
  file operations

Retrieval:
  evidence discovery
  source ranking
  context packing
  coverage checks

Evaluation:
  regression tests
  workflow quality
  grounding
  cost and latency

The strongest agent systems are not the ones where the model is allowed to do the most. They are the ones where the model has the right context, the right tools, and the right constraints.

A useful mental model: agent as controller

The agent should behave like a controller in a software system.

observe
  -> update state
  -> decide next action
  -> call tool or retrieve evidence
  -> inspect result
  -> continue, ask, hand off, or answer

This loop is useful because it separates decision-making from execution. The model proposes the next action. The runtime decides whether that action is valid. The tool performs a bounded operation. The evaluator checks whether the result is good enough.

Agent controller loop
stateDiagram-v2
[*] --> Observe
Observe --> UpdateState
UpdateState --> DecideAction
DecideAction --> ExecuteTool: tool or retrieval needed
DecideAction --> AskClarification: missing critical input
DecideAction --> Answer: enough evidence
ExecuteTool --> EvaluateResult
EvaluateResult --> DecideAction: continue
EvaluateResult --> Answer: supported
EvaluateResult --> Repair: partial evidence
Repair --> ExecuteTool
AskClarification --> [*]
Answer --> [*]

That separation is what makes the system debuggable.

If an agent gives a wrong answer, you should be able to inspect:

  • which workflow was selected
  • what state was known at the time
  • which tool calls were proposed
  • which tool calls were approved
  • what evidence was retrieved
  • which claims were allowed in the final answer
  • where cost, latency, or failure occurred

Without that trace, the system is only fluent. It is not reliable.

The architecture of a reliable agent system

A strong agentic architecture usually has these layers:

User

Router

Workflow agent

Structured state manager

Planner / controller

Tool runtime
  ├── deterministic tools
  ├── retrieval engine
  ├── SQL / APIs
  ├── validators
  ├── rule engines
  └── external services

Evidence and result evaluator

Final synthesizer

Trace store, session store, evals

Each layer has a job:

LayerResponsibility
RouterSelects the workflow or specialist based on user intent and current state
Workflow agentOwns the task logic for a domain or user journey
State managerStores compact workflow state instead of replaying full chat history
PlannerProduces structured action decisions
Tool runtimeValidates and executes bounded operations
Retrieval engineFinds and packs evidence for the task
EvaluatorChecks coverage, grounding, validity, and failure modes
SynthesizerWrites the final user-facing answer from approved results
Observability layerRecords traces, metrics, cost, latency, and regressions

This architecture avoids a common failure mode: asking one model call to understand the user, plan the work, retrieve evidence, validate the result, and write the final answer all at once.

Layer ownership
flowchart TB
subgraph Intelligence
  LLM[LLM: intent, planning, synthesis]
end
subgraph Control
  Runtime[Runtime: validation, budgets, retries]
  State[State manager: workflow memory]
end
subgraph Execution
  Tools[Tools: APIs, SQL, calculations]
  Retrieval[Retrieval: evidence discovery]
end
subgraph Quality
  Evals[Evals: regression and grounding]
  Traces[Traces: cost, latency, failures]
end
LLM --> Runtime
Runtime --> State
Runtime --> Tools
Runtime --> Retrieval
Tools --> Traces
Retrieval --> Evals
Evals --> LLM

Runtime modes keep cost under control

Not every request needs the same level of reasoning. A production agent should have explicit runtime modes.

ModeUse whenTypical pathRisk
FastThe turn is simple, local, or only updates stateRouter or planner -> response/toolLow
BalancedThe answer needs evidence or tool usePlanner -> tools/retrieval -> synthesisMedium
DeepEvidence is partial, conflicting, or high impactPlanner -> parallel work -> evaluator -> repair -> synthesisHigh

Fast mode

Use fast mode for simple requests, state updates, or clarification turns.

planner or router
  -> direct response or deterministic tool

Good examples:

  • the user answers a clarifying question
  • no retrieval is needed
  • the action is low risk
  • the system only needs to update state

Balanced mode

Balanced mode should be the default for useful work.

planner
  -> tools or retrieval
  -> final grounded synthesis

Use it when the answer needs evidence, multiple tools, structured output, or workflow state.

Deep mode

Deep mode is for higher-impact or ambiguous work.

planner
  -> parallel tools and retrieval
  -> evaluator
  -> targeted repair
  -> final synthesis

Use it when retrieval coverage is weak, evidence conflicts, the answer affects a decision, or the task requires multi-step reasoning.

Deep mode should be earned by the request. If every request uses deep mode, the system becomes slow and expensive without necessarily becoming more accurate.

Structured state beats long chat history

Many early agent prototypes treat memory as “send the whole conversation back to the model.” That works for demos and breaks down in real products.

Full chat history is noisy. It grows without a stable shape. It makes behavior harder to reproduce. It also forces the model to infer workflow state from prose instead of reading state directly.

Use compact structured state:

{
  "active_workflow": "document_analysis",
  "workflow_stage": "evidence_collection",
  "known_facts": {
    "document_type": "contract",
    "requested_output": "risk summary"
  },
  "unknown_fields": ["jurisdiction"],
  "last_evidence_ids": ["doc:17:clause:4"],
  "handoff_allowed": false,
  "summary": "User wants a risk-focused review of a contract."
}

This gives the runtime a stable representation of the task.

Structured state helps with:

  • routing follow-up questions correctly
  • preventing repeated questions
  • resuming after failures
  • deciding when a handoff is allowed
  • keeping prompts smaller
  • debugging workflow behavior

The rule is simple: conversation history is context; structured state is memory.

Sticky workflow routing

Follow-up messages should usually remain inside the active workflow until the workflow explicitly allows a handoff.

Bad behavior:

Turn 1: user starts document review
Turn 2: system routes to document review
Turn 3: user asks "what about this clause?"
Turn 4: router treats it as a generic legal question

Better behavior:

{
  "active_workflow": "document_review",
  "workflow_stage": "clause_analysis",
  "handoff_allowed": false
}

Routing rule:

if state.active_workflow and not state.handoff_allowed:
    route_to(state.active_workflow)
else:
    route_normally()

Sticky routing prevents tool drift. It also makes the agent feel more coherent because it remembers the actual task, not just the latest sentence.

Planner outputs should be action packets

The planner should not write long internal essays. It should return a structured decision that the runtime can validate.

Weak planner output:

The user seems to be asking about contract risk. I should probably search the document and maybe summarize the relevant sections.

Useful planner output:

{
  "action": "retrieve",
  "workflow_stage": "clause_analysis",
  "state_delta": {
    "focus": "termination clause"
  },
  "retrieval_targets": [
    {
      "target_id": "termination_terms",
      "query": "termination notice cure period liability",
      "must_cover": true,
      "max_chunks": 4
    }
  ],
  "clarification_question": null,
  "handoff_allowed": false
}

This lets the runtime do real engineering work:

  • validate required fields
  • reject unsafe actions
  • apply cost budgets
  • execute tools
  • retry failures
  • log decisions
  • test planner behavior with evals

The planner proposes. The runtime enforces.

Tool design is part of agent design

Tools are not just API wrappers. They are the boundary between model reasoning and system action.

A good tool has:

  • a narrow purpose
  • explicit input schema
  • clear error semantics
  • deterministic behavior where possible
  • traceable output IDs
  • permission checks
  • timeout and retry policy

Weak tool description:

Search the database.

Better tool description:

Search normalized document chunks for evidence supporting a specific retrieval target.
Return chunk IDs, source titles, section paths, snippets, and ranking scores.
Use this only after the planner has created a retrieval target.
Do not generate the final answer.

Tool descriptions are behavior controls. If they are vague, the model will call tools inconsistently. If they are too verbose, they waste tokens and encourage over-calling.

Retrieval should be a shared intelligence layer

Do not let every agent invent its own retrieval logic.

Bad architecture:

Contract agent has custom retrieval.
Support agent has custom retrieval.
Research agent has custom retrieval.
Every workflow tunes top_k differently.

Better architecture:

Every workflow creates a RetrievalTask.
One shared retrieval engine executes it.

A retrieval task should describe the evidence need, not the retrieval implementation.

{
  "task_type": "risk_analysis",
  "targets": [
    {
      "target_id": "liability_cap",
      "text": "liability cap and exclusions",
      "queries": ["liability cap exclusions indirect damages"],
      "must_cover": true,
      "max_chunks": 4
    }
  ],
  "retrieval_depth": "balanced",
  "filters": {
    "document_id": "contract_042"
  }
}

The retrieval engine can then handle the implementation:

keyword search
dense vector search
sparse retrieval
hybrid fusion
deduplication
section expansion
source-fit scoring
context packing
trace logging

This keeps the agent focused on what evidence is needed while the retrieval system decides how to find it.

Naive RAG often looks like this:

embed query
  -> retrieve top_k chunks
  -> stuff context
  -> answer

That is rarely enough for serious agent workflows.

Production retrieval should be target-driven:

understand task
  -> decompose into evidence targets
  -> retrieve per target
  -> combine lexical and semantic candidates
  -> remove duplicates
  -> expand useful parent context
  -> check source fit
  -> pack context by budget
  -> synthesize only supported claims

This matters because agent tasks often need different kinds of evidence. A contract review may need exact clause wording. A research assistant may need broad coverage across sources. A support agent may need the latest policy and the customer’s account state. One retrieval strategy will not serve all three.

Guardrails should be layered

Guardrails are not a single moderation step. They are a set of checks across the workflow.

Use layers:

input checks
workflow routing policy
tool-call validation
permission checks
schema validation
retrieval coverage checks
final answer grounding
human review for high-impact cases
audit logs

The most important guardrails are often ordinary software controls: schemas, permissions, allowlists, budgets, and deterministic validators.

The model can help identify risk. It should not be the only thing preventing risk.

What to avoid

The most common agentic AI failures are architectural, not linguistic.

Avoid:

  • one giant prompt that does everything
  • unbounded ReAct-style loops
  • tool calls without validation
  • full chat history replay forever
  • hidden keyword rules pretending to be reasoning
  • agent-specific retrieval hacks
  • always-on critics and rerankers
  • multi-agent chat for simple tasks
  • fluent answers with no evidence trail
  • unsupported final claims

The hard trap is that a weak architecture can still produce an impressive answer. Fluency is not evidence of correctness.

The production doctrine

A strong agentic AI architecture has a clear philosophy:

LLM handles ambiguity.
Runtime handles control.
Tools handle execution.
Data stores handle truth.
Retrieval handles evidence.
Evaluation handles improvement.

Do not make an agent smarter by making it freer. Make it smarter by giving it better state, better tools, better evidence, better constraints, and better feedback loops.

Part 2 turns this architecture into an implementation playbook: orchestration patterns, multi-agent design, observability, evaluation, cost control, and a practical repository structure for production AI agents.

Back to Blog