Skip to content

Evaluation, Observability and Architecture Patterns

A production agent architecture is not complete until failures can be reconstructed and changes can be evaluated before and after deployment. Because behavior emerges from model + context + skill + tool + state + policy + environment, observability and evaluation must cover the whole execution path rather than only the final generated text.

A useful trace hierarchy is:

Run
├── Step
│   ├── Context build
│   ├── Model call
│   ├── Policy decision
│   ├── Capability/tool call
│   ├── Retrieval call
│   └── State transition
├── Step
└── Final outcome

The architecture should make this structure observable by design.

What should be observable?

For each run, it should be possible to answer:

What goal was requested?
Which runtime/skill/policy/model versions were used?
What state did the run start from?
Which observations/evidence were available?
Which action was proposed?
Was it allowed/denied/approved?
Which tool/capability actually executed?
What result came back?
How did state change?
Why did the run stop?
How much time/cost did it consume?

This does not require storing hidden chain-of-thought. Structured action decisions, inputs/outputs, evidence references and state transitions are more useful and safer operationally.

Trace identifiers

Use stable IDs throughout the system:

trace_id
run_id
step_id
model_call_id
capability_call_id
observation_id
approval_id
artifact_id

Propagate them across queues, workers and external adapters when possible.

This allows a log line from an MCP adapter to be connected to the agent step that caused it.

Structured event model

Instead of free-form logging only, emit events such as:

RunStarted
ContextBuilt
ModelDecisionReceived
ActionRejectedByPolicy
ActionApproved
CapabilityCallStarted
CapabilityCallSucceeded
ObservationRecorded
RunCheckpointed
RunCompleted
RunFailed

Each event can have typed fields.

Example:

{
  "event": "CapabilityCallSucceeded",
  "run_id": "run_123",
  "step_id": "step_8",
  "capability": "pull_request.create",
  "version": "1",
  "latency_ms": 840,
  "effect": "WRITE",
  "result_status": "SUCCESS"
}

Sensitive-data logging

Observability must not become a new data leak.

Avoid blindly logging:

secrets
full customer records
entire prompts
private documents
raw tool responses
access tokens

Prefer references, hashes, redaction and structured summaries where appropriate.

Trace design should include data classification and retention policy.

Model call observability

Useful metadata:

provider
model
model profile/version
input token count
output token count
latency
cost estimate
structured-output validity
tool-choice outcome
retry count
fallback used

For debugging, store the exact allowed/sanitized context when policy permits, or enough references to reconstruct it from canonical sources.

Context observability

Context quality strongly affects model behavior.

Record:

context strategy version
included source types
evidence IDs
memory IDs
freshness metadata
token allocation per section
items dropped due to budget
trust classifications

This makes questions such as "Did the model miss the policy because retrieval failed, or because the context builder dropped it?" answerable.

Capability/tool observability

For each action:

requested capability
validated arguments (redacted as needed)
policy result
approval result
adapter/provider
latency
retry/reconciliation behavior
normalized result/failure
side-effect identifier

Side-effect IDs are especially valuable for reconciling uncertain outcomes.

State-transition observability

A run is effectively a state machine, so transitions should be explicit:

RUNNING
→ WAITING_FOR_APPROVAL
→ RUNNING
→ COMPLETED

Record:

old state
new state
state version
trigger event
step/run ID

Unexpected transition attempts should be visible as errors.

Evaluation levels

Do not use one monolithic "agent accuracy" score.

Evaluate layers separately.

Model/decision evaluation

did it classify correctly?
did it choose an appropriate action?
did structured output match schema?

Skill evaluation

did the skill follow its contract?
did it find relevant issues?
did it use allowed capabilities efficiently?

Retrieval evaluation

Recall@k
Precision@k
ranking quality
grounding coverage
ACL leakage

Tool/capability evaluation

schema correctness
failure normalization
idempotency behavior
authorization enforcement

Loop/trajectory evaluation

success rate
steps to completion
unnecessary calls
replans
retries
premature stop
loop explosion

End-to-end outcome evaluation

was the user/business goal actually achieved?
was the result correct, safe and useful?

Layered evaluation localizes regressions.

Offline eval pipeline

A useful release flow:

Code / prompt / skill change
        ↓
Unit + contract tests
        ↓
Deterministic integration tests
        ↓
Offline agent eval set
        ↓
Safety / policy evals
        ↓
Cost + latency comparison
        ↓
Regression gate
        ↓
Canary / production

Agent changes should be reviewed with the same discipline as code changes.

Eval datasets

Use representative cases, including failures and edge cases.

happy paths
ambiguous requests
missing information
conflicting evidence
provider failures
stale observations
prompt injection attempts
permission denial
approval required
long-running/resume cases
budget exhaustion

A dataset made only of clean demo prompts will not predict production reliability.

Deterministic vs model-based graders

Prefer deterministic graders when the property is deterministic.

Examples:

JSON schema valid
correct API endpoint selected
forbidden tool not called
file exists
tests pass
no cross-tenant document retrieved
budget not exceeded

Use model-based graders for semantic qualities such as:

response usefulness
reasoning quality summary
review completeness
tone
semantic relevance

Model graders should themselves be calibrated and versioned.

Trajectory evaluation

Two agents can produce the same final answer but have very different operational quality.

Agent A:
3 steps, 2 reads, success

Agent B:
18 steps, 9 redundant reads, 3 retries, success

Outcome-only evaluation calls both successful. Trajectory evaluation reveals the architecture problem.

Useful trajectory metrics:

steps per successful run
model calls per success
tool calls per success
repeated-action rate
replan rate
no-progress rate
human escalation rate
cost per success

Production metrics

Architecture-level metrics can include:

run success/failure/block rate
p95 run latency
queue wait time
cost per successful run
provider fallback rate
capability error rate
approval rate
retrieval miss rate
budget exhaustion rate
resume-after-failure success
stuck-run count
security/policy denial count

Break them down by:

workload
skill
model profile
runtime version
tenant
capability

without exposing sensitive data.

Regression gates

A production change should be blocked if key thresholds regress materially.

Example:

critical-task success >= 95%
forbidden-write rate = 0
schema validity >= 99.9%
p95 cost increase <= 15%
retrieval ACL leakage = 0

Not every metric must improve, but trade-offs should be explicit.

Shadow and replay testing

Historical traces can be replayed against a new model/skill/runtime without executing real side effects.

recorded input + observations
        ↓
new runtime/model
        ↓
proposed trajectory
        ↓
compare

For write actions, replace real adapters with simulators/mocks.

This is useful for model upgrades and prompt/skill changes.

Failure injection

Test architecture under controlled failures:

model timeout
rate limit
tool 503
queue redelivery
worker crash after side effect
stale state conflict
retrieval unavailable
approval expires
sandbox crash

A system that only works when every dependency succeeds is not production-ready.

Architecture patterns that repeatedly work

Deterministic shell around probabilistic decisions

validate → model decides → validate → authorize → execute → verify

Ports and adapters

business capability
→ port
→ provider adapter

Durable orchestration

canonical run state + checkpoints + event-driven resume

Explicit context builder

canonical sources
→ trust/freshness/budget filters
→ temporary model context

Capability registry with policy filtering

all capabilities
→ identity/task/risk filter
→ effective tool set

Deterministic workflows with agentic islands

Autonomy stays bounded inside known business processes.

Evidence-first verification

Use tests, tools and systems of record rather than repeated self-reflection.

Architecture anti-patterns

Giant prompt architecture

Business rules, security, workflow and provider configuration are all embedded in one prompt.

Symptoms:

prompt changes break unrelated features
rules are duplicated
hard to test individual responsibilities

Monolithic Agent class

Agent
├── prompts
├── database
├── GitHub
├── billing
├── memory
├── RAG
├── retries
└── authorization

This is a normal God Object with an AI name.

Hidden state

Correct execution depends on facts stored only in conversation/context.

Provider SDK everywhere

OpenAI/vector DB/MCP-specific types leak into domain and application code.

Unrestricted tool access

Every run sees every capability and credentials are overly broad.

Agent everywhere

Simple deterministic operations are wrapped in LLM decisions, increasing cost and failure rate.

Vector database as application database

Operational state, memory and document retrieval are collapsed into one similarity-search store.

Multi-agent by default

Coordination complexity is introduced without measurable quality gain.

Observability as raw transcript dumping

Huge logs are expensive, sensitive and still fail to show structured state transitions.

Architecture review questions

When reviewing an AI system, ask:

Where is canonical state?
Who owns authorization?
What can the model actually decide?
Which side effects require approval?
Where are provider-specific dependencies?
How are capabilities typed and versioned?
How does context get built?
How is untrusted evidence separated from instruction authority?
Can a run resume after worker failure?
How are retries/idempotency handled?
How is tenant isolation enforced?
Can we trace one run end to end?
Can we evaluate a change before production?
Why is each service/agent boundary necessary?

If these answers are unclear, the system likely has hidden coupling.

Completion mental model

A maintainable production agent architecture can be summarized as:

Business capabilities and domain rules
          ↓
Application use cases / ports
          ↓
Durable agent runtime
  ├── canonical state
  ├── explicit context builder
  ├── policy / authorization
  ├── skills / capabilities
  ├── budgets / recovery
  └── observability
          ↓
Infrastructure adapters
  ├── models
  ├── retrieval
  ├── MCP/connectors
  ├── queues
  ├── databases
  └── external services

The AI-specific parts fit into a normal software architecture instead of replacing it.

Engineering takeaways

  1. Trace the entire run, not only model calls or final text.
  2. Use structured events and stable IDs across workers, tools, retrieval and state transitions.
  3. Evaluate layers independently so regressions can be localized.
  4. Prefer deterministic graders for deterministic properties and calibrated model graders for semantic ones.
  5. Evaluate trajectory efficiency as well as final outcome.
  6. Gate production changes on safety, quality, cost and latency regressions.
  7. Use replay, shadow testing and failure injection for model/runtime upgrades.
  8. Watch for giant prompts, God-agent classes, hidden state, provider leakage, unrestricted tools and unjustified multi-agent complexity.
  9. Observability must respect privacy and secret boundaries.
  10. Good agent architecture is recognizable as good software architecture with explicit probabilistic components.