关于 debug-agent
debug-agent 是一套要求依据运行时证据排查软件问题的指令技能,面向编码 Agent,涵盖假设、日志、复现、分析和验证。
主要功能
- 01基于运行时证据的调试流程
- 02按假设布置日志
- 03NDJSON 日志服务配置
- 04基于日志验证修复
- 05保留调试日志的规则
适用场景
- 01排查仅凭代码难以诊断的问题
- 02复现时并行验证多个原因
- 03通过修复前后的运行日志确认结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ millionco/ expect --skill debug-agent安装后在 Claude Code 中输入/debug-agent确认已加载。
详细介绍
调试模式
你现在处于调试模式。必须使用运行时证据进行调试。
采用这种方法的原因:传统 AI Agent 会贸然修复问题,并声称有 100% 的把握,但由于缺少运行时信息,最终往往会失败。 它们只根据代码进行猜测。你不能也绝不能这样修复 Bug——你需要实际的运行时数据。
系统化工作流程:
- 提出 3-5 个关于 Bug 发生原因的准确假设(详细说明,宁多勿少)
- 在代码中添加日志(参见“日志记录”部分),并行验证所有假设
- 请用户重现 Bug。提供清晰、带编号的重现步骤。提醒用户:如果添加日志的文件已缓存或打包,请重启相关应用/服务。请用户完成后予以确认。
- 分析日志:根据日志行证据,对每个假设作出评估(已确认/已排除/无法确定)
- 仅在有 100% 把握且有日志证据时修复;此时不要移除日志记录
- 通过日志验证:请用户再次运行,并对比修复前后的日志,引用相应日志条目
- **如果日志证明问题已解决,且用户确认:**移除日志记录并进行说明。**如果修复失败:**先移除基于已排除假设所做的任何代码更改(保留日志记录和已证实的修复),然后根据不同子系统提出新假设,并添加更多日志记录
- **确认成功后:**说明问题所在,并简要总结修复内容(1-2 行)
重要约束:
- 在获得运行时证据之前,绝不要修复
- 始终结合运行时信息和代码进行判断(绝不能只看代码)
- 在修复后的验证日志证明成功,且用户确认没有其他问题之前,不要移除日志记录
- 修复往往会失败;预期会反复迭代,并优先选择迭代。花更多时间收集更多数据,才能得到更好、更准确的修复结果
日志记录
STEP 0:启动日志服务器(必须在添加任何日志记录之前完成)
重要:服务器是一个长时间运行的进程。必须在后台运行。
在添加任何日志记录之前,先将调试服务器作为后台进程运行。整个调试会话期间服务器都必须保持运行——这不是一次性命令。
npx debug-agent 2>&1 &
**必须在后台运行此命令。**不要在前台运行,也不要等待命令完成——它永远不会完成,因为它是一个持久运行的服务器。请使用 &(Shell 后台运行)、nohup,或 Agent 的后台/异步命令执行功能。如果 Agent 平台支持 block_until_ms: 0 或等效功能,请使用该功能。如果支持在单独的终端中运行命令,请这样做。此命令绝不能阻塞工作流程。
服务器启动时会向标准输出打印一行 JSON:
{
"sessionId": "a1b2c3",
"port": 54321,
"endpoint": "http://127.0.0.1:54321/ingest/a1b2c3",
"logPath": "/tmp/debug-agent/debug-a1b2c3.log"
}
捕获并记住以下值:
- 服务器端点:
endpoint的值(通过 POST 请求发送日志的 HTTP 端点 URL) - 日志路径:
logPath的值(NDJSON 日志写入此路径) - 会话 ID:
sessionId的值(此调试会话的唯一标识符)
如果服务器启动失败,请立即停止并告知用户。
- 未获得有效的日志配置时,不要继续添加日志记录。
- 服务器具有幂等性——如果已有服务器在运行,它会返回现有服务器的信息,而不会启动重复实例。
- 无需预先创建日志文件;首次写入日志时会自动创建。
STEP 1:了解日志格式
- 日志以 NDJSON 格式(每行一个 JSON 对象)写入日志路径指定的文件。
- 对于 JavaScript/TypeScript,运行时会通过 POST 请求将日志发送到服务器端点,日志服务器会将其作为 NDJSON 行写入日志路径指定的文件。
- 对于其他语言(Python、Go、Rust、Java、C/C++、Ruby 等),建议使用该语言的标准库文件 I/O,直接将 NDJSON 行追加到日志路径指定的文件中。
日志条目示例:
{
"sessionId": "a1b2c3",
"id": "log_1733456789_abc",
"timestamp": 1733456789000,
"location": "test.js:42",
"message": "User score",
"data": { "userId": 5, "score": 85 },
"runId": "run1",
"hypothesisId": "A"
}
步骤 2:插入埋点日志
- 在 JavaScript/TypeScript 文件中,使用此单行 fetch 模板(将
ENDPOINT和SESSION_ID替换为步骤 0 中的值),即使可以访问文件系统也不例外:
fetch('ENDPOINT',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'SESSION_ID',location:'file.js:LINE',message:'desc',data:{k:v},timestamp:Date.now()})}).catch(()=>{});
-
在 非 JavaScript 语言(Python、Go、Rust、Java、C、C++、Ruby)中,通过标准库文件 I/O 以追加模式打开日志路径,写入一行包含有效负载的 NDJSON,然后关闭文件。尽可能让这些代码片段保持精简简短(最好一行,或仅几行)。
-
根据正在调查的代码复杂度和待验证的假设,决定插入多少条埋点日志。如果问题高度集中,放置一条合适的日志可能就足够;复杂的多步流程可能需要更多。目标是用最少的日志确认或排除所有假设。指南:
- 至少需要 1 条日志;绝不可完全跳过埋点
- 不超过 10 条日志——如果你认为需要更多,先缩小假设范围
- 通常为 2-6 条,但请自行判断
-
根据假设,从以下类别中选择相关的日志位置:
- 函数入口处的参数
- 函数出口处的返回值
- 关键操作之前的值
- 关键操作之后的值
- 分支执行路径(执行了哪个 if/else 分支)
- 疑似错误或边缘情况的值
- 状态变更和中间值
-
每条日志必须至少对应一个假设(在有效负载中包含
hypothesisId)。 -
使用此有效负载结构:
{sessionId, runId, hypothesisId, location, message, data, timestamp} -
**必须:**将每条调试日志都包裹在可折叠的代码区域中:
- 使用适合语言的区域语法(例如,JS/TS 中的
// #region debug log、// #endregion) - 这样编辑器会自动折叠调试埋点,保持代码整洁
- 使用适合语言的区域语法(例如,JS/TS 中的
-
**禁止:**记录机密信息(令牌、密码、API 密钥、个人身份信息)
步骤 3:每次运行前清除之前的日志文件(强制)
- 在让用户运行之前,删除日志路径中的文件。
- 如果无法删除或删除失败:告知用户手动删除日志文件。
- 这样可确保新运行的日志干净,不会与旧数据混杂。
- 不要使用 Shell 命令(rm、touch 等);只能使用 delete_file 工具。
- 清除日志文件不等于移除埋点;此处不要从代码中移除任何调试日志。
- **重要:**只能删除你自己的日志文件(步骤 0 中的日志路径)。绝不可删除、修改或覆盖属于其他调试会话的日志文件。其他会话可能在同一目录中使用不同的会话 ID 文件名保存日志——请勿触碰。
步骤 4:用户运行程序后读取日志
- 用户运行程序并在其界面中确认完成后,不要让他们输入“完成”;然后读取日志路径中的文件。
- 日志文件将包含埋点生成的 NDJSON 条目(每行一个 JSON 对象)。
- 分析这些日志以评估假设并找出根本原因。
- 如果日志文件为空或缺失:告知用户复现可能未成功,并请他们重试。
步骤 5:修复期间保留日志
- 实施修复时,暂时不要移除调试日志。
- 验证运行期间必须保留日志。
- 你可以将日志标记为
runId="post-fix",以区分验证运行与初始调试运行。 - 禁止:在分析修复后的验证日志之前,或用户明确确认成功之前,移除或修改任何文件中先前添加的日志。
- 只有在修复后的验证运行成功(有基于日志的证据)或用户明确要求移除时,才移除日志。
关键提醒(必须遵守)
- 修复期间保持埋点处于启用状态;直到验证成功或用户明确确认前,不要移除或修改日志。
- 禁止:使用
setTimeout、sleep或人为延迟作为“修复”;应使用适当的响应性机制、事件或生命周期。 - 禁止:在分析修复后的验证日志或收到用户明确确认之前移除埋点。
- 验证必须对比修复前后的日志,并引用日志行;没有日志证据时不得声称修复成功。
- 不要手动创建日志文件;它会自动创建。
- 清空日志文件不等于移除埋点。
- 绝不要删除或修改不属于本次会话的日志文件。只能操作步骤 0 中的确切路径所指向的日志文件。
- 始终优先通过提出新假设,并利用日志证据来提供修复方案。
- 如果所有假设都被否定,必须提出更多假设,并据此添加更多埋点。
- **移除被否定假设对应的代码更改:**当日志证明某个假设有误时,撤销为该假设所做的代码更改。不要累积防御性保护、推测性修复或未经证实的更改。只保留有运行时证据支持的修改。
- 优先复用现有架构、模式和实用工具;避免过度设计。修复应精准、针对性强,并尽可能精简,同时最大化效果。
服务端 API 参考
| 方法 | 效果 |
|---|---|
POST /ingest/:sessionId | 将 JSON 请求体作为 NDJSON 行追加到日志文件 |
GET /ingest/:sessionId | 读取完整的日志文件内容 |
DELETE /ingest/:sessionId | 清空日志文件 |