# 调试 / 排障(trace · 回放 · 体检 · 注入) > 从 README 搬出来(README 太长了)。这份是给**想深挖某条消息为什么没回**的人看的;只想让它跑起来,看 [README](../README.md) 就够。 > 下面提到的控制台都在 `control/`(默认 `127.0.0.1:8799`),面板里点「调试台」会自动拉起。 每条入站消息都有一个 **traceId**,每个决策点(**包括每一次"不回复/丢弃/降级"**)都会留下 `stage + ok + reason + 耗时`。这是本版本的核心设计理念:出问题时不用猜。 | 想看什么 | 在哪看 | |---|---| | **6 条硬约束此刻达标吗** | 控制台最上面「**硬约束验收台**」:6 行状态灯 + 机器上现有的证据 + 该点哪里的提示,每 30 秒自动重算 | | 单条消息的完整决策链 | 控制台「实时事件流」点任意一行 → 「决策链」显示时间轴、停在哪一步、为什么 | | 为什么机器人不回复 | 「实时事件流」筛"仅被拒/失败",最常见原因直接列出(如 `mention: 群聊未 @ 机器人`) | | 每个端口的占用与在线 | 控制台「端口 / 进程」(6700 的连接数即机器人在线) | | 一键排查 | 控制台「一键体检」:15-19 项 pass/fail + 修复建议(依赖路径、端口、链路、快照、错误、闸门拒绝…) | | 生效配置(为什么功能没生效) | 控制台「运行快照」里的"关闭中的开关";完整字段在 `qq-runtime.json` 的 `features`(不含任何密钥) | | 打包给别人看 | 「导出诊断包」→ 一个 zip(事件流/审计/桥日志/宿主日志/运行快照/体检报告/环境信息) | | 文件级排查 | `qq-trace.jsonl`(结构化事件,可 `jq`/grep)、`qq-bridge-debug.log`、`qq-actions.log`(写操作审计)、`qq-host-out.log`/`err.log` | 相关配置:`traceEnabled`(默认开)、`traceLevel`(`debug` 全量 / `warn` 只留问题)、`traceMemorySize`、`traceFile`。 调试接口(控制台,token + Origin 双重校验):`/api/trace`、`/api/stream`(SSE)、`/api/runtime`、`/api/diagnose`、`/api/export`。 ### 录制 · 离线回放 · 事件注入(v0.4 阶段 3) 前两项解决了"现在发生了什么",这三项解决"这条消息当时为什么这样、换成别的输入会怎样"。 | 能力 | 怎么用 | 说明 | |---|---|---| | 录制 | 自动 | 桥收到的每条消息/通知/请求都追加到 `qq-inbox.jsonl`(可 JSON 逐行解析、按大小轮转),只写本机、不改任何回复行为 | | 离线回放 | 控制台「录制 · 回放 · 注入」→ 某行「回放这条」/「回放最近 5 条」 | 在**沙箱目录**里用**真实桥代码**重跑这条消息:dry-run 拦截所有出站、不建任何 QQ 连接、源目录一个字节都不改。结论逐条给出"会回复/静默/出错 + 原因 + 会发送什么" | | 事件注入 | 控制台填 群号/QQ 号/文本 → 「注入」 | 写一行到 `qq-inject.jsonl`,桥按 `injectIntervalMs`(默认 2s)轮询后**走真实管线**处理;`injectDryRun`(默认开)下所有出站调用被拦截并计数,注入内容永远不会真的发到 QQ | 回放的保真度来自**运行时快照里的线上决策配置**(白名单、安静时段、各功能开关等 28 项,见 `qq-runtime.json` 的 `replay`):否则插件默认值里"白名单为空 = 拒绝一切"会让回放全部判成静默。回放里**不含真实模型输出**——模型那一轮用一条带 `[回放]` 前缀的模拟回复代替,用来验证链路与分支,不验证措辞。 排障要点: - 注入通道未开启时会**直接报错并说明开关名**(`injectEnabled`),不会静默丢进队列; - 注入触发的 **agent 回合是异步的**:dry-run 窗口只在同步阶段开着,所以模型真正的回复会在事件流里被单独拦下(`注入回合的模型回复已被拦截(dry-run,未发送):<原文>`)——注入既能走真实管线,又不会漏发一条;真人消息不受影响(收到真实消息立即解除该标记); - 回放的帧在事件流里标为 `replay: 离线回放(沙箱 + dry-run,不碰 QQ)`,注入的帧标为 `inject: 来自注入器`,两者不会互相误判; - 桥启动时若队列里已有历史行,会跳过它们并在事件流里记一条"本次启动跳过 N 行(只处理启动后新增的行)",避免重启后重放旧注入; - 注入的帧不会被二次录制(否则回放/注入会互相激发); - 回放沙箱默认保留最近 5 次,更旧的**移入回收站**(`qq-replay/_trash/<日期>/`),不做物理删除。 相关配置:`recordInbound`(默认开)、`inboxFile`、`inboxRedact`、`injectEnabled`(默认关)、`injectFile`、`injectDryRun`(默认开)、`injectIntervalMs`。 调试接口:`/api/inbox`、`/api/replay`、`/api/inject`、`/api/queue/clear`。 ### 硬约束验收台(v0.4 阶段 4) 上面两条解决了"现在发生了什么"和"当时为什么这样"。验收台解决第三个问题:**设计理念有没有真的落地**。 `GET /api/acceptance` 把 6 条硬约束逐条用**机器上现有的产物**算成 `✅ 达标 / ⚠️ 有提示 / ❌ 不达标 / ❔ 证据不足`,并给出证据与下一步: | 约束 | 判定用的证据 | |---|---| | ① 无静默分支 | 最近 500 条事件里所有 `ok:false`(被拒/失败)事件是否都带非空 reason;缺的会点名 stage | | ② 可关联(traceId) | 消息级事件的 traceId 覆盖率、有多少条消息真正走完 `inbound→reply` | | ③ 可回放 | 录制条数 + 最近一次回放的统计与**安全保证**(dry-run 开、QQ 连接 0、cwd 已沙箱化);dry-run 被关掉直接判不达标 | | ④ 可体检 | 体检通过数/失败数/blocker 数与结论,失败项点名 | | ⑤ 可导出 | 诊断包会收集的产物有几类在位(或最近一次导出的体积与文件名) | | ⑥ 可注入 | 通道开关、dry-run 开关(关掉→不达标,因为会真发 QQ)、已消费行数、以及**拦下过几次异步 agent 回合的回复** | 每一项都能点「去看 →」跳到对应卡片(事件流/决策链/回放结果/体检/日志/注入);结论为 `all-green / partial / unknown / broken` 四种。 纯函数实现(`control/lib/acceptance.mjs`),所以每条分支都有断言覆盖;面板每 30 秒自动刷新,回放结束后立刻重算。