引言
Agent系统的故障排查比传统应用复杂得多——一个"回复质量下降"的问题可能涉及Prompt变化、模型版本更新、工具API变更、记忆检索质量下降等多个因素。没有系统化的排查方法论,工程师可能花费数小时甚至数天才能定位根因。
本文基于大量实战经验,提供一套系统化的Agent故障排查方法论。
故障排查金字塔
┌─────────────┐
│ 用户反馈 │
│ "回复不对" │
└──────┬──────┘
│
┌──────▼──────┐
│ 指标异常 │
│ 质量评分下降 │
└──────┬──────┘
│
┌──────▼──────┐
│ 链路追踪 │
│ 定位慢步骤 │
└──────┬──────┘
│
┌──────▼──────┐
│ 日志分析 │
│ 找到错误日志 │
└──────┬──────┘
│
┌──────▼──────┐
│ 根因定位 │
│ Prompt/模型 │
│ /数据/代码 │
└─────────────┘
故障分类
Agent系统故障分类
│
├── 功能故障
│ ├── 响应错误(幻觉、曲解意图)
│ ├── 工具调用失败
│ ├── 路由错误
│ └── 超时/死循环
│
├── 性能故障
│ ├── 响应变慢
│ ├── 吞吐量下降
│ └── 资源耗尽
│
├── 质量故障
│ ├── 回复质量下降
│ ├── 用户满意度降低
│ └── Token消耗异常
│
└── 成本故障
├── Token消耗突增
├── 基础设施成本异常
└── API调用费用超标
排查流程
Step 1:收集症状
class SymptomCollector:
"""症状收集器"""
async def collect(self, incident_id: str) -> dict:
"""收集故障症状"""
symptoms = {
"incident_id": incident_id,
"reported_at": datetime.now().isoformat(),
"reported_by": None,
"description": None,
"affected_users": [],
"affected_services": [],
"start_time": None,
"error_rate": None,
"quality_score": None,
"recent_changes": [],
}
# 从多个数据源收集
symptoms["recent_deployments"] = await self._get_recent_deployments()
symptoms["recent_config_changes"] = await self._get_recent_config_changes()
symptoms["recent_model_updates"] = await self._get_recent_model_updates()
symptoms["error_logs"] = await self._get_recent_errors()
symptoms["metrics_anomalies"] = await self._get_metrics_anomalies()
return symptoms
Step 2:复现问题
class IssueReproducer:
"""问题复现器"""
async def reproduce(
self,
session_id: str,
user_input: str
) -> dict:
"""复现问题"""
reproduction = {
"original_session": session_id,
"input": user_input,
"attempts": [],
"reproduced": False,
"reproduction_rate": 0,
}
# 尝试复现3次
for i in range(3):
try:
result = await self.agent.process(user_input)
reproduction["attempts"].append({
"attempt": i + 1,
"response": result["response"],
"quality_score": result.get("quality_score"),
"full_trace": result.get("trace"),
})
# 判断是否复现
if self._is_similar_issue(result, user_input):
reproduction["reproduced"] = True
except Exception as e:
reproduction["attempts"].append({
"attempt": i + 1,
"error": str(e),
})
reproduction["reproduction_rate"] = sum(
1 for a in reproduction["attempts"]
if a.get("error") or self._is_similar_issue(a, user_input)
) / len(reproduction["attempts"])
return reproduction
Step 3:分析日志
class LogAnalyzer:
"""日志分析器"""
async def analyze_for_incident(
self,
incident: dict,
time_window_minutes: int = 60
) -> dict:
"""分析故障相关日志"""
analysis = {
"error_patterns": [],
"timeline": [],
"affected_components": set(),
"suspected_root_cause": None,
}
# 获取时间窗口内的日志
start = incident["start_time"] - timedelta(minutes=10)
end = incident["start_time"] + timedelta(minutes=time_window_minutes)
logs = await self.log_store.query(
time_range=(start, end),
filters={
"level": ["ERROR", "CRITICAL"],
"service": incident.get("affected_services", [])
}
)
# 分析错误模式
error_counts = {}
for log in logs:
error_key = f"{log['service']}:{log['message'][:50]}"
error_counts[error_key] = error_counts.get(error_key, 0) + 1
analysis["affected_components"].add(log["service"])
analysis["timeline"].append({
"timestamp": log["timestamp"],
"service": log["service"],
"message": log["message"],
})
# 排序错误频率
analysis["error_patterns"] = sorted(
[{"pattern": k, "count": v} for k, v in error_counts.items()],
key=lambda x: x["count"],
reverse=True
)
# 推断根因
if analysis["error_patterns"]:
top_error = analysis["error_patterns"][0]
analysis["suspected_root_cause"] = {
"type": "error_pattern",
"pattern": top_error["pattern"],
"frequency": top_error["count"],
"confidence": min(top_error["count"] / len(logs), 1.0)
}
return analysis
Step 4:链路追踪分析
class TraceAnalyzer:
"""Trace分析器"""
async def find_slow_spans(
self,
trace_id: str,
threshold_ms: int = 1000
) -> list:
"""找到慢Span"""
trace = await self.jaeger.get_trace(trace_id)
spans = self._flatten_spans(trace)
slow_spans = [
{
"operation": span["operationName"],
"service": span["process"]["serviceName"],
"duration_ms": span["duration"],
"tags": span.get("tags", {}),
}
for span in spans
if span["duration"] > threshold_ms * 1000 # 转换为微秒
]
return sorted(slow_spans, key=lambda s: s["duration_ms"], reverse=True)
async def find_error_spans(self, trace_id: str) -> list:
"""找到错误Span"""
trace = await self.jaeger.get_trace(trace_id)
spans = self._flatten_spans(trace)
error_spans = [
{
"operation": span["operationName"],
"service": span["process"]["serviceName"],
"error": span.get("tags", {}).get("error", True),
"logs": span.get("logs", []),
}
for span in spans
if span.get("tags", {}).get("error")
]
return error_spans
Step 5:根因分析
class RootCauseAnalyzer:
"""根因分析器"""
async def analyze(self, incident: dict) -> dict:
"""分析根因"""
hypotheses = []
# 假设1:最近的部署导致
recent_deploy = await self._check_recent_deployment(incident)
if recent_deploy:
hypotheses.append({
"hypothesis": "Recent deployment caused the issue",
"evidence": recent_deploy,
"confidence": 0.8,
"verify_command": f"kubectl rollback deployment {recent_deploy['deployment']}"
})
# 假设2:模型行为变化
model_change = await self._check_model_change(incident)
if model_change:
hypotheses.append({
"hypothesis": "Model behavior changed",
"evidence": model_change,
"confidence": 0.7,
"verify_command": f"Compare outputs with previous model version"
})
# 假设3:Prompt被修改
prompt_change = await self._check_prompt_change(incident)
if prompt_change:
hypotheses.append({
"hypothesis": "Prompt template was modified",
"evidence": prompt_change,
"confidence": 0.9,
"verify_command": f"git diff {prompt_change['commit']} prompts/"
})
# 假设4:工具API变更
tool_change = await self._check_tool_api_change(incident)
if tool_change:
hypotheses.append({
"hypothesis": "Tool API behavior changed",
"evidence": tool_change,
"confidence": 0.6,
"verify_command": f"Test tool {tool_change['tool']} with previous inputs"
})
# 按置信度排序
hypotheses.sort(key=lambda h: h["confidence"], reverse=True)
return {
"incident_id": incident["id"],
"hypotheses": hypotheses,
"recommended_first_check": hypotheses[0] if hypotheses else None,
}
常见故障排查
故障1:响应质量下降
class QualityDropTroubleshooter:
"""回复质量下降排查"""
CHECKLIST = [
{
"name": "Check model version",
"command": "kubectl get configmap agent-config -o jsonpath='{.data.model_version}'",
"fix": "Rollback model version if recently changed"
},
{
"name": "Check prompt template",
"command": "git log --oneline -10 prompts/",
"fix": "Revert prompt changes if quality dropped after commit"
},
{
"name": "Check tool success rate",
"command": "rate(agent_tool_calls_total{status='success'}[1h])",
"fix": "Debug failing tools, may affect response quality"
},
{
"name": "Check memory retrieval quality",
"command": "Analyze recall@5 for recent queries",
"fix": "Reindex vector database if recall dropped"
},
{
"name": "Check for model drift",
"command": "Compare quality scores across time",
"fix": "Consider model retraining or fine-tuning"
},
]
故障2:Token消耗突增
class TokenSpikeTroubleshooter:
"""Token消耗突增排查"""
async def diagnose(self) -> dict:
diagnosis = {
"possible_causes": [],
"recommendations": [],
}
# 检查是否有循环
cycles = await self._check_for_cycles()
if cycles:
diagnosis["possible_causes"].append("Agent entering tool call loops")
diagnosis["recommendations"].append("Enable cycle detection and break logic")
# 检查Prompt是否变长
prompt_length = await self._get_avg_prompt_length()
if prompt_length > self.baseline_prompt_length * 1.5:
diagnosis["possible_causes"].append("Prompt length increased significantly")
diagnosis["recommendations"].append("Optimize prompt template, use summarization")
# 检查是否启用了缓存
cache_hit_rate = await self._get_cache_hit_rate()
if cache_hit_rate < 0.3:
diagnosis["possible_causes"].append("Cache hit rate too low")
diagnosis["recommendations"].append("Investigate cache configuration")
return diagnosis
排查工具箱
class TroubleshootingToolkit:
"""排障工具箱"""
TOOLS = {
"log_query": {
"description": "Query logs with Loki",
"example": 'logcli query \'{service="agent"} |~ "ERROR"\' --since=1h',
},
"trace_query": {
"description": "Find traces with Jaeger",
"example": 'jaeger query --service=agent --lookback=1h --minDuration=1s',
},
"metrics_query": {
"description": "Query metrics with PromQL",
"example": 'rate(agent_requests_total[5m])',
},
"config_check": {
"description": "Check K8s config",
"example": 'kubectl get configmap agent-config -o yaml',
},
"rollback": {
"description": "Rollback deployment",
"example": 'kubectl rollout undo deployment agent-service',
},
"compare_versions": {
"description": "Compare model/prompt versions",
"example": 'ab compare --model-a=gpt-4o --model-b=gpt-4o-mini --samples=100',
}
}
总结
Agent系统故障排查需要系统化的方法论——从收集症状开始,通过复现问题、分析日志、追踪链路,最终定位根因。关键在于将模糊的"回复不对"拆解成可度量的指标异常,再通过对指标的分析定位到具体的组件和行为。
核心原则:好的排障不是"猜对了",而是"推理对了"。每一次排障都应该记录假设、证据和验证过程,形成知识库,让下一次排障更快。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
