Agent 执行是非确定性的,传统断点调试完全失效;需捕获完整执行路径——每次工具调用、模型决策、上下文状态转换——才能定位问题根因。
调试 Claude Code 智能体:阅读转录文件、追踪工具调用、定位智能体出错位置
本文在人类监督和审核下借助 AI 辅助撰写。
大多数智能体调试问题源于将 AI 执行视为同步代码。开发者使用 console.log、用调试器逐步执行,却困惑于为什么智能体在生产环境失败而在开发环境正常。执行模型有根本性差异:智能体在多次 LLM 调用中做出非确定性决策,每次决策都受到上下文影响,而上下文在每次运行间都在变化。
传统调试假设确定性行为。设置断点、检查状态、复现问题。智能体执行打破了这三个假设。同样的输入产生不同的工具调用。上下文窗口无声溢出。模型会产生你 schema 中根本不存在的字段名幻觉。等到错误显现时,引向错误的决策轨迹早已消失。
解决方案需要捕获完整执行路径:每一次工具调用、每一次模型决策、每一次上下文状态转换。智能体需要执行转录文件,不仅展示发生了什么,还要展示智能体选择每个动作的原因。这个区别至关重要。没有推理链,调试就变成了考古学——在日志中挖掘以重建本质上具有概率性的决策。
这个差异将调试从被动救火转变为系统性根因分析。本文涵盖关键模式:阅读 Claude Code 转录文件、追踪工具执行、识别常见失败模式,以及构建可在问题进入生产环境前就捕获问题的可观测性系统。
智能体调试需要捕获完整执行路径,而非仅仅最终输出——每一次工具调用、推理步骤和上下文状态都必须被追踪以定位根因。
三个最常见的智能体失败是:上下文溢出(无声超出 token 限制)、字段幻觉(模型编造 schema 属性)和推理循环(智能体反复重试相同失败方案)。
LangSmith、Arize Phoenix 和 Braintrust 等生产可观测性工具提供不同权衡:LangSmith 擅长追踪检查,Phoenix 擅长本地开发迭代,Braintrust 擅长评估驱动调试。
使用 TypeScript 构建的自定义追踪分析器让团队完全掌控哪些信号重要,能够自动检测特定领域的失败模式。
使用 LLM 进行元分析可以识别人类会遗漏的数千条追踪中的模式,但需要结构化提示词将症状描述与根因推断分开。
阅读 Claude Code 转录文件:完整执行路径
智能体转录文件揭示从用户输入到最终输出的完整决策序列。每条转录包含对话历史、工具调用及其输入输出,以及模型在每一步的推理。有效阅读这些需要理解 Claude Code 捕获了什么、遗漏了什么。
转录文件结构遵循轮次的线性序列。每轮包含用户消息或带有可选工具调用的助手消息。工具调用包括函数名、参数和结果。关键信息位于三处:助手在调用工具前的推理、揭示模型理解的工具参数,以及展示执行是否成功的工具结果。

大多数调试失败发生在开发者跳过推理步骤时。他们看到工具调用参数错误就假设模型做出了坏决策。推理揭示了真正的问题:模型缺乏关于有效参数值的上下文,或工具描述存在歧义,或前一个工具结果包含了误导性信息。
上下文溢出在转录中表现为模型忘记早期指令或工具结果。转录显示所有消息,但 Claude Code 不会指示上下文窗口何时接近其限制。开发者必须手动计算 token 数量并观察症状:模型重复已问过的问题、忽略对话早期的工具结果,或做出与既定上下文矛盾的决策。
这里的含义是转录长度与调试难度相关。3-5 次工具调用的短对话分析 straightforward。20+ 次工具调用的对话需要系统性分析:识别执行可能分歧的决策点,检查每个工具结果是否影响了下一个决策,并验证关键上下文在整个过程中是否保持可访问。
追踪工具调用:输入、输出以及问题出在哪里
工具调用追踪捕获智能体执行偏离预期行为的确切时刻。工具名、参数和结果构成一个三元组,揭示智能体尝试了什么以及是否成功。有效的追踪需要结构化日志记录,在整个执行过程中保留这个三元组。
interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
result: {
success: boolean;
data?: unknown;
error?: string;
};
timestamp: number;
contextTokens: number;
}
class AgentTracer {
private calls: ToolCall[] = [];
logToolCall(call: ToolCall): void {
this.calls.push(call);
// Detect immediate failure patterns
if (!call.result.success) {
this.analyzeFailure(call);
}
// Detect hallucinated arguments
const schema = this.getToolSchema(call.name);
const invalidArgs = this.findInvalidArguments(call.arguments, schema);
if (invalidArgs.length > 0) {
console.warn(`Hallucinated arguments in ${call.name}:`, invalidArgs);
}
}
private analyzeFailure(call: ToolCall): void {
const recentCalls = this.calls.slice(-5);
const sameToolFailures = recentCalls.filter(
c => c.name === call.name && !c.result.success
);
if (sameToolFailures.length >= 2) {
console.error(`Reasoning loop detected: ${call.name} failed ${sameToolFailures.length} times`);
}
}
private findInvalidArguments(
args: Record<string, unknown>,
schema: Record<string, { type: string; required?: boolean }>
): string[] {
return Object.keys(args).filter(key => !(key in schema));
}
getExecutionSummary(): string {
const total = this.calls.length;
const failed = this.calls.filter(c => !c.result.success).length;
const avgTokens = this.calls.reduce((sum, c) => sum + c.contextTokens, 0) / total;
return `${total} tool calls, ${failed} failures, ${avgTokens.toFixed(0)} avg tokens`;
}
}
追踪器在工具调用发生时捕获它们,并立即检查两种常见失败模式:同一工具反复失败以及不存在于工具 schema 中的参数。这两种模式都表明智能体陷入困境,若无干预不太可能恢复。
工具参数幻觉发生在模型编造看起来合理但与 schema 不匹配的字段名时。模型看到带 query 参数的 searchDocuments,就假设一定存在 requireUnique 或 maxResults,因为类似工具有它们。工具执行因验证错误而失败,但模型将错误解读为查询问题而非 schema 误解。
这种失败模式隐蔽但代价高昂。智能体用不同的查询值重试,消耗 token 和延迟,而实际修复需要移除幻觉的字段。要 early 发现需要在下执行前将参数与已知 schema 比较,并在出现意外字段时发出警告。
常见智能体失败模式:上下文溢出、字段幻觉和推理循环
三种失败模式占大多数生产环境智能体问题:导致模型忘记关键信息的上下文溢出、验证失败的字段幻觉,以及智能体反复重试相同破坏性方案的推理循环。
上下文溢出发生在对话历史加工具结果超出模型上下文窗口时。Claude Code 不会在此发生时抛出错误。相反,早期消息被静默截断。模型继续处理,但无法访问更早的上下文。依赖该上下文的决策变得不连贯。
症状表现为不一致行为:智能体询问已收到的信息、忽略初始 prompt 中指定的约束,或做出与对话开始时工具结果相矛盾的决策。开发者看到这些症状就假设模型不可靠,而实际问题却是机械性的:上下文容量不足。
修复需要在整个执行过程中监控上下文 token,并在接近限制时实施摘要策略。当 token 接近最大值的 75% 时,将早期消息摘要为保留关键信息的精简上下文。这很重要,因为上下文溢出是可预测的——token 计数是确定性的——但如果没有显式追踪就不可见。
幻觉字段出现在工具 schema 描述不足,或模型遇到具有不同 schema 的相似工具时。模型根据训练期间学习的模式生成听起来合理的参数,但这些模式与实际工具接口不匹配。
interface ToolSchema {
name: string;
description: string;
parameters: {
type: "object";
properties: Record<string, {
type: string;
description: string;
enum?: string[];
}>;
required: string[];
};
}
function validateToolCall(
call: { name: string; arguments: Record<string, unknown> },
schema: ToolSchema
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
const validProps = new Set(Object.keys(schema.parameters.properties));
// Check for hallucinated arguments
Object.keys(call.arguments).forEach(arg => {
if (!validProps.has(arg)) {
errors.push(`Unexpected argument '${arg}' not in schema for ${call.name}`);
}
});
// Check for missing required arguments
schema.parameters.required.forEach(req => {
if (!(req in call.arguments)) {
errors.push(`Missing required argument '${req}' in ${call.name}`);
}
});
// Check enum violations
Object.entries(call.arguments).forEach(([key, value]) => {
const prop = schema.parameters.properties[key];
if (prop?.enum && !prop.enum.includes(String(value))) {
errors.push(`Invalid value '${value}' for ${key}, must be one of: ${prop.enum.join(", ")}`);
}
});
return { valid: errors.length === 0, errors };
}
执行前的验证可防止幻觉参数到达工具。智能体立即收到关于 schema 违规的反馈,而不是神秘的执行错误。这将调试时间从分析错误信息缩短到修复 schema 描述或约束参数生成。
推理循环发生在智能体遇到故障、以最小更改重试、再次失败并继续这种模式时。每次迭代都会消耗 token 和延迟,而不会向解决方案取得进展。循环继续直到上下文溢出或用户干预。
检测需要追踪近期历史中的工具调用模式。如果相同工具以类似参数失败三次,智能体可能卡住了。打破循环需要外部干预:注入一条系统消息,明确禁止进一步重试该工具,或升级到可以提供替代上下文的人工操作员。
使用 LLM 调试智能体轨迹:元分析模式
LLM 擅长在大容量轨迹中进行模式识别。开发者可以使用第二个 LLM 来分析轨迹并识别常见失败模式,而不是手动审查数百次失败的智能体运行。这种元分析模式需要结构化提示,将症状描述与根因推断分开。
interface TraceAnalysisPrompt {
systemPrompt: string;
traceContext: {
toolCalls: ToolCall[];
conversationLength: number;
failurePoint: number;
};
analysisType: "failure_root_cause" | "optimization_opportunity" | "pattern_detection";
}
async function analyzeTrace(
trace: ToolCall[],
failureMessage: string
): Promise<{ rootCause: string; recommendation: string }> {
const prompt: TraceAnalysisPrompt = {
systemPrompt: `You are analyzing agent execution traces to identify root causes of failures.
Focus on: context overflow, hallucinated arguments, reasoning loops, and schema mismatches.
Provide specific evidence from the trace, not general observations.`,
traceContext: {
toolCalls: trace,
conversationLength: trace.length,
failurePoint: trace.findIndex(c => !c.result.success)
},
analysisType: "failure_root_cause"
};
const analysis = await callLLM({
model: "claude-3-5-sonnet-20241022",
system: prompt.systemPrompt,
messages: [{
role: "user",
content: `Analyze this agent execution trace that failed with: "${failureMessage}"
Tool calls:
${JSON.stringify(trace, null, 2)}
Identify the root cause and provide an actionable recommendation.`
}]
});
return parseAnalysisResponse(analysis.content);
}
元分析提示约束 LLM 专注于已知失败模式,而不是生成推测性解释。轨迹上下文提供具体证据:表明溢出的 token 计数、不匹配 schema 的参数名、表示循环的重复工具调用。
这种模式在分析批量相似失败时效果最好。单个轨迹可能因特殊原因失败。十个以相同方式失败的轨迹揭示了一个系统性问题:令人困惑的工具描述、对有效值的上下文不足,或缺少对边缘情况的护栏。
局限性在于 LLM 分析引入了另一层非确定性。元分析可能错过模式或幻觉出看似合理但与实际失败不匹配的根因。开发者在实施修复前必须根据原始轨迹验证建议。这个验证步骤至关重要——将 LLM 分析视为ground truth会导致调试死胡同。
生产可观测性:LangSmith、Arize Phoenix 和 Braintrust for Claude Code
生产可观测性需要捕获轨迹而不降低智能体性能的工具。三个平台服务于不同的用例:LangSmith 用于全面的轨迹检查,Arsize Phoenix 用于本地开发迭代,Braintrust 用于评估驱动的调试。
LangSmith 提供最详细的轨迹可视化。每次运行显示完整的对话历史、带计时信息的工具调用,以及每步的 token 使用情况。该平台永久存储轨迹并支持按元数据过滤:用户 ID、对话 ID、工具名称、成功/失败状态。

优势在于事后分析。当用户报告问题时,开发者按对话 ID 查询 LangSmith,可以准确看到智能体做了什么。轨迹显示故障是工具错误、上下文问题还是推理错误。这种可见性将调试时间从数小时缩短到数分钟。
Arize Phoenix 面向本地开发。该平台作为 localhost 服务运行,从开发中的智能体捕获轨迹。开发者迭代提示或工具 schema,并立即看到更改如何影响轨迹质量。反馈循环紧密:修改工具描述、运行测试对话、检查轨迹、重复。
Phoenix 在构建新智能体能力时表现出色。本地优先方法意味着轨迹上传无网络延迟,也不必担心将开发轨迹暴露给外部服务。权衡是 Phoenix 将轨迹存储在内存中——重启服务历史消失。这适用于开发但不适用于生产监控。
Braintrust 专注于评估驱动的调试。该平台将轨迹视为评估输入。开发者从生产失败创建测试数据集,运行重放这些场景的评估,并追踪代码更改是否提高成功率。工作流暴露回归:如果提示更改修复了一个场景但破坏了另外两个,评估失败。
这很重要,因为智能体更改通常具有非局部效应。为一个用例改进工具描述可能会在不同上下文中混淆模型。基于评估的工作流在部署前捕获这些回归。构建评估数据集的投资通过减少生产事故获得回报。
选择取决于团队工作流。快速原型设计的团队受益于 Phoenix 的紧密迭代循环。具有成熟智能体和生产流量的团队需要 LangSmith 的轨迹保留。实践测试驱动智能体开发的团队应该使用 Braintrust 的评估框架。许多团队三者都用:开发用 Phoenix,CI 用 Braintrust,生产用 LangSmith。
用 TypeScript 构建自定义轨迹分析器
自定义分析器提供对特定领域重要信号的控制。通用可观测性平台捕获所有内容。自定义分析器突出对业务重要的模式:合规违规、成本异常、用户体验下降。
interface AnalysisRule {
name: string;
check: (trace: ToolCall[]) => { triggered: boolean; severity: "low" | "medium" | "high"; details: string };
}
class CustomTraceAnalyzer {
private rules: AnalysisRule[] = [];
addRule(rule: AnalysisRule): void {
this.rules.push(rule);
}
analyze(trace: ToolCall[]): { violations: Array<{ rule: string; severity: string; details: string }> } {
const violations = this.rules
.map(rule => {
const result = rule.check(trace);
return result.triggered ? { rule: rule.name, severity: result.severity, details: result.details } : null;
})
.filter((v): v is NonNullable<typeof v> => v !== null);
return { violations };
}
}
// 示例:检测对昂贵服务的过度 API 调用
const costControl: AnalysisRule = {
name: "excessive_expensive_api_calls",
check: (trace) => {
const expensiveTools = ["searchDatabase", "generateReport"];
const calls = trace.filter(c => expensiveTools.includes(c.name));
if (calls.length > 5) {
const cost = calls.length * 0.25; // 每次调用 $0.25
return {
triggered: true,
severity: "high",
details: `发出了 ${calls.length} 次昂贵 API 调用(预估费用 $${cost.toFixed(2)})`
};
}
return { triggered: false, severity: "low", details: "" };
}
};
// 示例:检测潜在的 PII 泄露
const piiExposure: AnalysisRule = {
name: "pii_in_tool_arguments",
check: (trace) => {
const piiPattern = /\b\d{3}-\d{2}-\d{4}\b|\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i;
for (const call of trace) {
const argsString = JSON.stringify(call.arguments);
if (piiPattern.test(argsString)) {
return {
triggered: true,
severity: "high",
details: `在 ${call.name} 参数中发现潜在 PII`
};
}
}
return { triggered: false, severity: "low", details: "" };
}
};
// 用法
const analyzer = new CustomTraceAnalyzer();
analyzer.addRule(costControl);
analyzer.addRule(piiExposure);
const traceToAnalyze: ToolCall[] = [
{ id: "1", name: "searchDatabase", arguments: { query: "user@example.com" }, result: { success: true }, timestamp: Date.now(), contextTokens: 1500 }
];
const analysis = analyzer.analyze(traceToAnalyze);
console.log(analysis.violations);
// 输出: [{ rule: "pii_in_tool_arguments", severity: "high", details: "在 searchDatabase 参数中发现潜在 PII" }]
分析器对每条 trace 运行规则并返回违规记录。每条规则封装了领域知识:什么样的 API 使用构成过度调用、哪些数据模式表示合规风险、什么样的时间阈值意味着用户体验问题。
这种模式可以与现有可观测性平台集成。LangSmith 或 Phoenix 捕获的 trace 会流入自定义分析器,后者应用特定领域的规则并暴露违规问题。关注点分离很有效:平台负责 trace 的捕获和存储,分析器负责特定领域的检测。
这里的可扩展性使得能够快速响应新的故障模式。当生产事故揭示出通用工具遗漏的模式时,开发人员可以编写新规则并立即部署。该规则对历史 trace 运行,检测该问题是否在之前就发生过。这种回顾性分析经常揭示所谓的"新"问题其实已经持续了数周。
常见问题解答
如何判断上下文溢出是否导致了智能体故障?
计算对话历史中所有消息和工具结果的总 token 数——如果总和接近 200K token(Claude 的限制),且智能体开始与早期决策相矛盾或忘记工具结果,则上下文溢出是可能的原因。在每轮对话中实现 token 跟踪,并在容量达到 75% 时设置警报。
幻觉字段和 schema 验证错误有什么区别?
幻觉字段发生在模型编造出不存在于工具 schema 中的参数名时(例如给搜索函数添加 requireUnique)。Schema 验证错误发生在模型使用了正确的字段名但提供了错误类型或超出允许范围的值时——两者都导致验证失败,但幻觉表明模型没有理解可用的参数。
我可以用 Claude 来调试 Claude 智能体的 trace 吗?
可以,使用 LLM 进行元分析在跨 trace 模式检测方面效果很好,但需要结构化提示词将分析限制在已知故障模式上(上下文溢出、循环、幻觉),并在实施修复前始终根据原始 trace 数据验证建议。
我应该构建自定义分析器还是使用可观测性平台?
从 LangSmith 等平台开始进行 trace 捕获和基本检查,然后当你发现特定领域的模式(成本阈值、合规规则、业务逻辑违规)通用工具无法检测时,添加自定义分析器——大多数生产环境同时使用两者。
如何在消耗完 token 预算之前检测推理循环?
在内存中追踪最近 3-5 次工具调用,检查同一工具名是否在失败结果中出现超过两次——如果检测到,则注入系统消息禁止对该工具的进一步重试,或升级到人工干预,因为循环很少能自我纠正,会持续消耗 token 直到上下文溢出。
结语:从被动调试到主动智能体健康监控
智能体调试的成功在于团队从调查个别失败转变为监控所有运行的执行健康状况。被动调试回答"为什么这个特定运行失败了?"主动监控回答"什么模式可以在用户遇到之前预测失败?"
本文涵盖的模式构成了一层调试栈:用于理解个别失败的 transcript 检查、用于捕获决策序列的工具调用追踪、用于系统性问题的故障模式检测、用于跨运行洞察的元分析、用于生产环境可见性的可观测性平台,以及用于特定领域规则的自定义分析器。每一层都建立在前一层之上。
实施这一栈的生产团队报告了两个结果:用户报告的问题减少