nanobot 故障定位表
对应源码
| 故障边界 | 源码 |
|---|---|
| 入口和配置 | nanobot/cli/commands.py、nanobot/config/loader.py、nanobot/config/paths.py |
| Agent Loop 和 Runner | nanobot/agent/loop.py、nanobot/agent/runner.py |
| Provider retry/fallback | nanobot/providers/base.py、nanobot/providers/fallback_provider.py |
| Tool discovery/validation | nanobot/agent/tools/loader.py、nanobot/agent/tools/registry.py |
| Session 和原子保存 | nanobot/session/manager.py |
| Channel/API/WebUI | nanobot/channels/、nanobot/api/server.py、nanobot/webui/、webui/src/ |
五问法
遇到问题先回答五个问题:
- 入口到了吗? CLI/API/Channel 是否真的构造了请求?
- Session 对吗? 实际 session key 与 workspace 是否符合预期?
- 停在哪层? AgentLoop state、Runner、Provider、Tool、Channel 还是 UI?
- 证据是什么? 日志、state trace、Runtime Event、Session 文件和 wire frame 分别说明什么?
- 最小复现是什么? 能否去掉 WebUI、真实 Channel 或非必要 Tool?
flowchart LR A["入口"] --> B["AgentLoop"] B --> C["AgentRunner"] C --> D["Provider / Tool"] B --> E["Session"] B --> F["Outbound / Event"] F --> G["Channel / API / SDK / WebUI"]
常见症状
| 症状 | 第一检查边界 | 看什么证据 | 常见原因 | 下一步 |
|---|---|---|---|---|
| 命令无法启动 | CLI 与配置加载 | traceback、实际配置路径、Python 环境 | 依赖未安装、配置解析失败、${VAR} 缺失、端口占用 | 运行最小 nanobot --help/status,再查 nanobot/config/loader.py |
| DeepSeek 返回 401/403 | Provider 凭据 | HTTP 状态、active provider;不打印 key | 环境变量不在当前进程、key 无效、Provider 选错 | 只验证 DEEPSEEK_API_KEY 是否存在,再核对 preset/provider |
| Provider 返回 429/5xx | Provider retry/fallback | retry 日志、_retry_wait、最终 error | 限流、暂时服务故障、重试策略不合适 | 区分 retry 与 fallback,读 providers/base.py、fallback_provider.py |
| 请求一直不结束 | Runner 与超时层 | state trace、最后一次 delta、超时名称 | Tool 未返回、流空闲、持续目标、外层 LLM 超时 | 区分 API timeout、LLM timeout、stream idle timeout |
| Tool 没出现在模型 schema | Tool discovery/registry | 注册工具列表、loader 日志 | 未发现模块/entry point、名称冲突、实例无效、作用域不匹配 | 查 loader.py 和 test_tool_loader_* |
| Tool 被调用但参数报错 | Tool schema | arguments、validation error | parameters 与执行签名不一致、required/类型/额外字段错误 | 用 ToolRegistry.prepare_call() 或 Tool.validate_params() 的相邻测试复现 |
| Tool 执行成功但模型不知道结果 | Runner 回填 | tool result message、call id、截断信息 | 结果未序列化、call id 不匹配、超过字符上限 | 查 AgentRunner 的 Tool result 组装 |
| MCP 启动失败 | transport 建连 | command/url、stderr、连接日志 | Windows 命令归一化、cwd/env 错、服务未启动、SSRF 阻断 | 分开验证 stdio 进程或 HTTP endpoint,再读 mcp.py |
| MCP 有连接但没有工具 | enabledTools/注册 | server 工具清单、包装后名称 | enabledTools=[]、名称 allowlist 不匹配、初始化未完成 | 对照 raw 名与 mcp_<server>_<tool> 包装名 |
| 新消息读不到旧上下文 | Session key 与回放 | 实际 key、Session JSONL、loaded messages | Channel/chat 改变、override、unified session、历史压缩/上限 | 查 session/keys.py 和 SessionManager |
| Session 文件损坏或内容缺失 | SAVE | 临时文件、原文件、保存异常日志 | 进程异常、磁盘错误、非原子写入路径、并发误用 | 先保留现场,不手工覆盖;查 atomic save tests |
/stop 后上下文异常 | 取消/checkpoint | cancellation 日志、checkpoint、Session 尾部 | 取消发生在 Tool/Provider 边界、恢复失败 | 查 test_task_cancel.py 与 test_stop_preserves_context.py |
| CLI 正常但 Channel 无回复 | 出站投递 | OutboundMessage、ChannelManager 重试日志 | Channel 配置/凭据、路由错、平台 API 失败 | 用直接调用或 fake Channel 隔离核心 |
| 有最终回复但没有流式进度 | hook/Channel 配置 | callback 是否传入、metadata | _wants_stream 缺失、sendProgress=false、Channel 不实现 delta | 查 progress_hook.py 与 Channel 的 send_delta() |
| 流式内容重复或拼错 | segment/合并 | _stream_id、_stream_end、sequence | 不同 segment 被合并、delta 重放、UI reducer 错 | 从 WebSocket frame 到 React state 逐层核对 |
| WebUI 显示一直运行中 | Runtime Event/WebSocket | TurnRunStatusChanged、TurnCompleted、frame | completion 未发布、订阅器异常、断线/replay 状态错 | 先确认核心已结束,再查 coordinator/adapter |
| 配置改了却没生效 | config → runtime snapshot | 实际加载路径、active preset/model | 改错配置、preset 覆盖、进程未刷新/重启、别名错误 | 查 配置索引 |
| 文件 Tool 拒绝 workspace 外路径 | workspace policy | resolved path、access mode | restrictToWorkspace、路径逃逸或 symlink 检查 | 不绕过限制;确认任务是否应在 workspace 内完成 |
| Windows 配了 sandbox 但命令仍未隔离 | OS sandbox | 启动 warning、sandbox provider/enforced 标志 | bwrap 路径依赖 Linux/container,Windows 不支持该执行边界 | 把它视为未隔离,改用外部容器/VM 或更低权限环境 |
| Web 抓取被拒绝 | SSRF | URL、解析 IP、redirect 链 | localhost/内网/保留地址、DNS 结果或重定向被阻断 | 不关闭全局保护;仅审查后添加最小可信 CIDR |
| Memory 没变化 | Memory/触发条件 | Memory 文件、工具调用、Dream/Cron 日志 | 未调用记忆工具、Dream 未启用/未触发、workspace 错 | 区分 Session 历史与 Memory |
| Cron 没触发 | 调度/时区 | job store、next run、timezone、status | job disabled、时区错误、Gateway 未常驻、保护规则 | 单独检查 CronService,不要先改 AgentRunner |
最小隔离顺序
WebUI 问题
→ 先用 SDK/direct call 验证核心
→ 再看 Runtime Event 或 progress callback
→ 再看 WebSocket frame
→ 最后看 React state
Channel 问题
→ 先用 CLI/direct call 验证 Provider 和 Agent
→ 再构造一条 InboundMessage
→ 再看 OutboundMessage
→ 最后调用真实平台
Tool 问题
→ 直接实例化 Tool
→ 验证 schema
→ 通过 ToolRegistry 执行
→ 用 fake Provider 触发 Tool Call
→ 最后做真实模型 smoke test不要这样排障
- 不要把 key、完整配置或带凭据的 URL 粘进日志;
- 不要一开始同时启用 WebUI、真实 Channel、MCP 和多个 Tool;
- 不要仅因错误在 UI 出现,就从前端组件开始改;
- 不要用无限重试掩盖认证、schema 或确定性逻辑错误;
- 不要为了通过测试关闭 workspace 或 SSRF 保护;
- 不要直接改 Session 文件后继续判断原始故障。
证据仍不足时
明确记录:
- 最小输入;
- 使用的入口和 session key(不含敏感值);
- AgentLoop 最后状态;
- active Provider/model;
- 是否发生 Tool Call;
- 最后一个可观察事件;
- 是否能用 fake Provider 或 direct call 复现。
然后回到 日志、RuntimeEvent 与测试策略 选择下一层证据。
返回:速查参考