为什么Agent调试如此困难
与传统软件不同,AI Agent的执行路径不是确定性的——同样的输入可能产生不同的执行路径和结果。这种非确定性使得传统调试方法(断点、单步执行)效果有限。Agent调试需要一套全新的方法论。
Agent调试的独特挑战
| 传统软件 | AI Agent |
|---|---|
| 确定性执行路径 | 非确定性,同一输入不同输出 |
| 错误即崩溃 | 错误可能"静默"——不崩溃但结果错误 |
| 逻辑可推断 | 决策基于LLM推理,难以追溯 |
| 单一系统 | 多工具调用、多轮对话、外部依赖 |
| 单元测试覆盖 | 需要语义级别的评估 |
Agent调试的三层体系
第一层:日志(Logs)—— 发生了什么
第二层:追踪(Traces)—— 为什么发生
第三层:评估(Evaluation)—— 发生得对不对
第一层:结构化日志体系
Agent日志设计原则
import json
import time
import uuid
from enum import Enum
from datetime import datetime
class LogLevel(Enum):
DEBUG = "DEBUG"
INFO = "INFO"
WARN = "WARN"
ERROR = "ERROR"
class AgentLogger:
"""生产级Agent结构化日志"""
def __init__(self, agent_name):
self.agent_name = agent_name
def log(self, level, event, **fields):
entry = {
"timestamp": datetime.utcnow().isoformat(),
"level": level.value,
"agent": self.agent_name,
"event": event,
"trace_id": fields.get("trace_id"),
"span_id": fields.get("span_id"),
**fields
}
print(json.dumps(entry, ensure_ascii=False, default=str))
# 使用示例
logger = AgentLogger("research_agent")
logger.log(LogLevel.INFO, "tool_call",
trace_id="tr_abc123",
span_id="sp_001",
tool_name="web_search",
tool_input={"query": "2026 AI芯片市场"},
tool_output={"results_count": 5},
duration_ms=1200,
tokens_used=150
)
关键日志事件类型
class AgentEventTypes:
"""Agent生命周期中的关键事件"""
# 规划阶段
PLAN_CREATED = "plan_created" # Agent制定了执行计划
PLAN_REVISED = "plan_revised" # 计划被修改
GOAL_DECOMPOSED = "goal_decomposed" # 目标被分解
# 执行阶段
TOOL_CALL_START = "tool_call_start" # 工具调用开始
TOOL_CALL_END = "tool_call_end" # 工具调用结束
TOOL_CALL_ERROR = "tool_call_error" # 工具调用失败
TOOL_CALL_RETRY = "tool_call_retry" # 工具调用重试
# 推理阶段
LLM_CALL_START = "llm_call_start" # LLM调用开始
LLM_CALL_END = "llm_call_end" # LLM调用结束
REASONING_STEP = "reasoning_step" # 推理步骤
DECISION_MADE = "decision_made" # 做出决策
# 状态管理
CONTEXT_UPDATED = "context_updated" # 上下文更新
MEMORY_READ = "memory_read" # 读取记忆
MEMORY_WRITE = "memory_write" # 写入记忆
# 错误与异常
HALLUCINATION_DETECTED = "hallucination_detected"
LOOP_DETECTED = "loop_detected" # 检测到循环
BUDGET_EXCEEDED = "budget_exceeded" # 预算超限
MAX_STEPS_REACHED = "max_steps_reached" # 达到最大步数
日志分析常见模式
def analyze_agent_logs(logs):
"""分析Agent日志,识别常见问题模式"""
patterns = {
# 模式1:工具调用循环
"tool_loop": detect_tool_loops(logs),
# 模式2:LLM调用失败率
"llm_failure_rate": calculate_llm_failure_rate(logs),
# 模式3:Token消耗异常
"token_anomaly": detect_token_anomalies(logs),
# 模式4:延迟热点
"latency_hotspots": find_latency_hotspots(logs),
# 模式5:幻觉信号
"hallucination_signals": detect_hallucination_signals(logs),
}
return patterns
def detect_tool_loops(logs):
"""检测工具调用循环——Agent反复调用同一工具"""
tool_calls = [l for l in logs if l["event"] == "tool_call_end"]
loops = []
window = 5 # 检查窗口
for i in range(len(tool_calls) - window):
window_calls = tool_calls[i:i+window]
tool_names = [c["tool_name"] for c in window_calls]
# 如果同一工具在窗口内被调用3次以上
from collections import Counter
counts = Counter(tool_names)
for tool, count in counts.items():
if count >= 3:
loops.append({
"tool": tool,
"count": count,
"window_start": window_calls[0]["timestamp"],
"severity": "high" if count >= 4 else "medium",
})
return loops
第二层:全链路追踪
Agent追踪架构
class AgentTracer:
"""Agent全链路追踪系统"""
def __init__(self):
self.spans = []
def start_trace(self, agent_name, user_input, context=None):
"""开始一个新的追踪"""
trace_id = f"tr_{uuid.uuid4().hex[:12]}"
return {
"trace_id": trace_id,
"agent_name": agent_name,
"user_input": user_input,
"context": context,
"start_time": time.time(),
"spans": [],
}
def start_span(self, trace, span_name, span_type, parent_id=None):
"""开始一个Span"""
span_id = f"sp_{uuid.uuid4().hex[:8]}"
span = {
"span_id": span_id,
"parent_id": parent_id,
"name": span_name,
"type": span_type, # llm, tool, reasoning, memory
"start_time": time.time(),
"inputs": None,
"outputs": None,
"error": None,
}
trace["spans"].append(span)
return span_id
def end_span(self, trace, span_id, outputs=None, error=None):
"""结束一个Span"""
for span in trace["spans"]:
if span["span_id"] == span_id:
span["end_time"] = time.time()
span["duration_ms"] = (span["end_time"] - span["start_time"]) * 1000
span["outputs"] = outputs
span["error"] = error
break
# 追踪使用示例
tracer = AgentTracer()
trace = tracer.start_trace("research_agent", "分析2026年AI芯片市场")
root_span = tracer.start_span(trace, "research_task", "root")
# 规划阶段
plan_span = tracer.start_span(trace, "planning", "reasoning", root_span)
plan = agent.plan("分析2026年AI芯片市场")
tracer.end_span(trace, plan_span, outputs={"plan": plan})
# 执行阶段
for step in plan.steps:
if step.type == "tool_call":
tool_span = tracer.start_span(trace, f"tool:{step.tool}", "tool", root_span)
result = agent.call_tool(step.tool, step.params)
tracer.end_span(trace, tool_span, outputs=result)
tracer.end_span(trace, root_span, outputs={"answer": agent.final_answer})
追踪可视化:执行树
Trace: tr_abc123 | research_agent | 总耗时: 12.3s
│
├── [reasoning] planning (0.8s)
│ └── 计划: 1.搜索数据 2.分析数据 3.生成报告
│
├── [tool] web_search (1.2s)
│ ├── query: "2026 AI芯片市场份额"
│ └── results: 5条
│
├── [tool] web_search (1.1s)
│ ├── query: "NVIDIA Blackwell vs 国产芯片"
│ └── results: 8条
│
├── [llm] data_analysis (3.5s)
│ ├── input_tokens: 2048
│ ├── output_tokens: 1024
│ └── cost: $0.03
│
├── [tool] write_file (0.3s)
│ └── output: report.md
│
└── [llm] final_answer (5.4s)
├── input_tokens: 4096
├── output_tokens: 2048
└── cost: $0.06
这种可视化让Agent的完整执行过程一目了然,是定位问题的关键工具。
分布式追踪与OpenTelemetry集成
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
class OTelAgentTracer:
"""与OpenTelemetry集成的Agent追踪"""
def __init__(self, service_name="ai_agent"):
provider = TracerProvider()
trace.set_tracer_provider(provider)
self.tracer = trace.get_tracer(service_name)
async def trace_agent_run(self, agent, user_input):
"""追踪完整的Agent执行"""
with self.tracer.start_as_current_span("agent_run") as root:
root.set_attribute("agent.name", agent.name)
root.set_attribute("agent.input", user_input)
async for event in agent.run(user_input):
if event.type == "tool_call":
with self.tracer.start_as_current_span(
f"tool:{event.tool_name}"
) as tool_span:
tool_span.set_attribute("tool.input", str(event.tool_input))
tool_span.set_attribute("tool.output", str(event.tool_output))
tool_span.set_attribute("tool.duration_ms", event.duration_ms)
elif event.type == "llm_call":
with self.tracer.start_as_current_span(
f"llm:{event.purpose}"
) as llm_span:
llm_span.set_attribute("llm.input_tokens", event.input_tokens)
llm_span.set_attribute("llm.output_tokens", event.output_tokens)
llm_span.set_attribute("llm.model", event.model)
第三层:评估闭环
Agent评估框架
class AgentEvaluator:
"""Agent质量评估框架"""
def evaluate(self, agent, test_cases):
"""
在测试集上评估Agent表现
test_cases: [{"input": "...", "expected": "...", "criteria": [...]}]
"""
results = []
for case in test_cases:
# 运行Agent
agent_response = agent.run(case["input"])
# 多维度评估
scores = {
"任务完成度": self._eval_task_completion(
agent_response, case["expected"]
),
"工具使用效率": self._eval_tool_efficiency(
agent_response.trace
),
"推理质量": self._eval_reasoning_quality(
agent_response.trace, case["input"]
),
"安全性": self._eval_safety(agent_response),
"延迟": self._eval_latency(agent_response.trace),
"成本": self._eval_cost(agent_response.trace),
}
# 综合评分
scores["总分"] = sum(scores.values()) / len(scores)
results.append({
"input": case["input"],
"response": agent_response,
"scores": scores,
})
return self._generate_report(results)
def _eval_tool_efficiency(self, trace):
"""评估工具使用效率:是否用了不必要的工具?"""
tool_calls = [s for s in trace.spans if s.type == "tool"]
# 评分标准
if len(tool_calls) == 0:
return 0.0 # 没用任何工具(可能直接回答了)
# 检查是否有冗余调用
tool_names = [c.name for c in tool_calls]
unique_tools = set(tool_names)
redundancy_ratio = 1 - len(unique_tools) / len(tool_names)
# 检查是否有失败重试
failed_calls = [c for c in tool_calls if c.error]
retry_penalty = len(failed_calls) * 0.1
score = 1.0 - redundancy_ratio - retry_penalty
return max(0.0, min(1.0, score))
def _eval_reasoning_quality(self, trace, user_input):
"""评估推理质量:决策是否合理?"""
reasoning_steps = [s for s in trace.spans if s.type == "reasoning"]
# 使用LLM-as-Judge评估推理质量
judge_prompt = f"""
用户目标:{user_input}
Agent的推理步骤:
{self._format_reasoning_steps(reasoning_steps)}
请评估Agent的推理质量(0-10分):
1. 目标理解是否准确?
2. 计划是否合理?
3. 决策是否有依据?
4. 是否有明显的逻辑错误?
"""
score = llm_judge.evaluate(judge_prompt)
return score / 10.0
在线评估:实时质量监控
class OnlineAgentMonitor:
"""Agent在线质量监控"""
def __init__(self):
self.metrics = {
"success_rate": SlidingWindowMetric(window=100),
"avg_steps": SlidingWindowMetric(window=100),
"avg_cost": SlidingWindowMetric(window=100),
"avg_latency": SlidingWindowMetric(window=100),
"tool_error_rate": SlidingWindowMetric(window=100),
"user_satisfaction": SlidingWindowMetric(window=50),
}
def record_execution(self, result):
"""记录每次Agent执行"""
self.metrics["success_rate"].update(result.success)
self.metrics["avg_steps"].update(result.step_count)
self.metrics["avg_cost"].update(result.total_cost)
self.metrics["avg_latency"].update(result.total_duration_ms)
self.metrics["tool_error_rate"].update(result.has_tool_error)
# 检查告警条件
self._check_alerts()
def _check_alerts(self):
"""告警检查"""
alerts = []
if self.metrics["success_rate"].value() < 0.85:
alerts.append({
"level": "critical",
"message": "Agent成功率低于85%",
"value": self.metrics["success_rate"].value(),
})
if self.metrics["avg_steps"].value() > 15:
alerts.append({
"level": "warning",
"message": "平均执行步数过多,可能存在循环",
"value": self.metrics["avg_steps"].value(),
})
if self.metrics["tool_error_rate"].value() > 0.2:
alerts.append({
"level": "warning",
"message": "工具调用错误率超过20%",
"value": self.metrics["tool_error_rate"].value(),
})
for alert in alerts:
self._send_alert(alert)
常见Agent问题诊断手册
问题1:Agent陷入循环
def diagnose_agent_loop(trace):
"""诊断Agent循环问题"""
steps = trace.spans
# 检测重复模式
for window_size in [3, 5, 7]:
for i in range(len(steps) - window_size * 2):
pattern1 = [s.name for s in steps[i:i+window_size]]
pattern2 = [s.name for s in steps[i+window_size:i+window_size*2]]
if pattern1 == pattern2:
return {
"diagnosis": "agent_loop",
"pattern": pattern1,
"repeated_count": len(steps[i:]) // window_size,
"root_cause": analyze_loop_cause(steps[i:i+window_size]),
"fix": "增加循环检测机制或调整Agent的stop条件",
}
return {"diagnosis": "no_loop_detected"}
问题2:工具选择错误
症状:Agent选择了不合适的工具
诊断:
1. 检查工具描述是否清晰
2. 检查工具选择时的Prompt
3. 检查是否有工具描述冲突
修复:
1. 优化工具描述,增加使用场景说明
2. 在系统Prompt中加入工具选择示例
3. 实现工具选择验证层
问题3:Agent过早终止
症状:任务未完成但Agent停止执行
诊断:
1. 检查stop条件是否触发
2. 检查LLM是否生成了停止信号
3. 检查是否达到token限制
修复:
1. 调整stop条件
2. 增加完成度检查
3. 实现任务完成验证
问题4:上下文丢失
def diagnose_context_loss(trace):
"""诊断Agent上下文丢失问题"""
context_lengths = []
for span in trace.spans:
if span.type == "llm":
context_lengths.append({
"step": span.name,
"input_tokens": span.inputs.get("token_count", 0),
"context_included": span.inputs.get("includes_context", False),
})
# 检查上下文是否被截断
if len(context_lengths) > 3:
early_context = context_lengths[0]["input_tokens"]
late_context = context_lengths[-1]["input_tokens"]
if late_context < early_context * 0.3:
return {
"diagnosis": "context_truncation",
"early_tokens": early_context,
"late_tokens": late_context,
"fix": "实现上下文压缩或摘要策略,保留关键信息",
}
return {"diagnosis": "no_context_loss"}
调试工具推荐
| 工具 | 类型 | 用途 |
|---|---|---|
| LangSmith | 追踪+评估 | LLM应用全链路追踪与评估 |
| Langfuse | 追踪+评估 | 开源LLM可观测性平台 |
| Phoenix | 追踪 | Arize的AI可观测性工具 |
| Braintrust | 评估 | AI实验评估与对比 |
| AgentOps | Agent专用 | Agent执行监控与调试 |
最佳实践总结
- 从第一天开始就建立日志体系:不要等到出问题才加日志
- 每个Agent执行都有唯一trace_id:便于关联所有相关日志
- 实现自动循环检测:在Agent运行时实时检测,而非事后分析
- 建立评估基准:定期在标准测试集上评估Agent表现
- 收集用户反馈:用户满意度是最真实的评估指标
- 实施金丝雀发布:新版本Agent先在5%流量上验证
- 保留失败案例:构建"失败案例库"用于持续改进
Agent调试不是一次性工作,而是一个持续迭代的过程。通过建立日志、追踪、评估三层闭环,可以将Agent从"黑盒"变为"白盒",让每次迭代都有据可依。