The Most Common Reasons Agents Fail
In Part 2 we covered tool-call orchestration and planning. In production, the real reasons agents fail are usually the absence of an evaluation system and how hard they are to debug.
Agent Evaluation Framework
You cannot properly evaluate an agent by scoring output quality alone. You also need to measure the execution path, tool selection, and efficiency.
from dataclasses import dataclass
@dataclass
class AgentTrace:
task: str
steps: list[dict] # : {thought, action, observation}
final_answer: str
total_tokens: int
elapsed_ms: int
tool_calls: list[str]
success: bool
class AgentEvaluator:
def evaluate(self, trace: AgentTrace, golden: dict) -> dict:
return {
"task_success": self._check_success(trace.final_answer, golden["answer"]),
"tool_precision": self._tool_precision(trace.tool_calls, golden["expected_tools"]),
"efficiency_score": self._efficiency(trace),
"hallucination_rate": self._check_hallucination(trace),
"step_count": len(trace.steps),
"token_efficiency": golden.get("min_tokens", 500) / max(trace.total_tokens, 1),
}
def _tool_precision(self, used: list, expected: list) -> float:
if not expected:
return 1.0
correct = sum(1 for t in used if t in expected)
return correct / len(used) if used else 0.0
def _efficiency(self, trace: AgentTrace) -> float:
# path
optimal_steps = 3 # settings
return min(1.0, optimal_steps / max(len(trace.steps), 1))Structured Tracing
import contextvars
import uuid
from datetime import datetime
trace_id_var = contextvars.ContextVar("trace_id")
class AgentTracer:
def __init__(self, backend="langfuse"):
self.backend = backend
def start_trace(self, task: str) -> str:
tid = str(uuid.uuid4())
trace_id_var.set(tid)
self._emit({
"event": "trace_start",
"trace_id": tid,
"task": task,
"timestamp": datetime.utcnow().isoformat(),
})
return tid
def log_step(self, step_type: str, content: dict):
self._emit({
"event": "step",
"trace_id": trace_id_var.get(),
"step_type": step_type, # thought | action | observation | answer
"content": content,
"timestamp": datetime.utcnow().isoformat(),
})
def end_trace(self, success: bool, final_answer: str):
self._emit({
"event": "trace_end",
"trace_id": trace_id_var.get(),
"success": success,
"final_answer": final_answer,
"timestamp": datetime.utcnow().isoformat(),
})Multi-Agent Orchestrator
A pattern where specialized agents collaborate, going beyond the limits of a single agent:
class OrchestratorAgent:
""" """
AGENTS = {
"research": ResearchAgent, # ·
"code": CodeAgent, # ·run
"data": DataAnalysisAgent, #
"writer": WriterAgent, #
}
async def execute(self, task: str) -> str:
# 1.
subtasks = await self.decompose(task)
# 2. run
results = {}
independent = [s for s in subtasks if not s.dependencies]
parallel_results = await asyncio.gather(*[
self.AGENTS[s.agent_type].run(s.prompt)
for s in independent
])
for s, r in zip(independent, parallel_results):
results[s.id] = r
# 3. run
dependent = [s for s in subtasks if s.dependencies]
for s in dependent:
context = {dep: results[dep] for dep in s.dependencies}
results[s.id] = await self.AGENTS[s.agent_type].run(
s.prompt, context=context
)
# 4. Result
return await self.synthesize(task, results)Human-in-the-Loop Pattern
A design that brings a human in only at the points an autonomous agent actually needs:
class HumanInLoopAgent:
HIGH_RISK_ACTIONS = {
"delete_records", "send_email", "execute_payment", "deploy_code"
}
async def execute_action(self, action: str, params: dict) -> dict:
if action in self.HIGH_RISK_ACTIONS:
approved = await self.request_approval(
action=action,
params=params,
context=self.current_trace(),
timeout_seconds=300,
)
if not approved:
return {"status": "rejected", "action": action}
return await self.tool_registry.call(action, **params)
async def request_approval(self, **kwargs) -> bool:
# Slack / / Request
approval_id = await self.notify_reviewer(**kwargs)
return await self.wait_for_approval(approval_id, kwargs["timeout_seconds"])Agent Reliability Metrics (Production SLA)
| Metric | Target | Alert threshold |
|---|---|---|
| Task success rate | ≥ 90% | < 85% |
| Average step count | ≤ 8 | > 12 |
| Tool precision | ≥ 85% | < 75% |
| P95 latency | ≤ 30s | > 45s |
| Hallucination rate | ≤ 5% | > 8% |
This three-part series completes the LLM Agent Deep-Dive Master Guide (fundamentals → tool orchestration → evaluation and operations).
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.