源码阅读与调试最低要求

学习目标

读完后,你应当能够:

  • 从入口而不是类名猜测开始追踪源码;
  • 使用 rg 找定义、调用方、配置和测试;
  • 运行最接近问题的 pytest 测试;
  • 选择有意义的断点和观察变量;
  • 区分已证实事实、合理推断和未知项。

前置知识

  • 能使用 PowerShell;
  • 能运行 Python;
  • 能阅读基本 traceback。

1. 先提出一个可跟踪的问题

不要从“读完整个 agent/ 目录”开始。选择一个具体问题,例如:

  • CLI 的一条消息如何进入 AgentLoop?
  • session key 在哪里决定?
  • Tool Call 在哪里被执行?
  • API streaming 在哪里关闭?
  • 同一 session 为什么不会同时写入?

问题越具体,阅读范围越小,得到的调用链越可靠。

2. 从入口和调用方开始

推荐顺序:

入口
  → 创建了哪些核心对象
  → 调用了哪个公共方法
  → 公共方法如何进入状态机或循环
  → 状态在哪里保存
  → 输出交给谁

例如,追踪 CLI 单次消息:

  1. 在 nanobot/cli/commands.py 找 agent 命令;
  2. 找 process_direct 调用;
  3. 进入 AgentLoop.process_direct();
  4. 进入 _process_message();
  5. 查看 TurnState 转换;
  6. 进入 _run_agent_loop();
  7. 查看 AgentRunner.run();
  8. 回到 _state_save() 和 _state_respond()。

3. 使用 rg 搜索

在 example/nanobot/code-EN 中运行:

rg -n "class AgentLoop|def process_direct" nanobot
rg -n "process_direct\\(" nanobot tests
rg -n "session_key" nanobot/session nanobot/agent tests
rg -n "should_execute_tools" nanobot tests

常见搜索顺序:

  1. 定义;
  2. 所有调用方;
  3. 配置字段;
  4. 相关测试;
  5. 错误信息或日志文本。

搜索日志文本

当运行时出现一条明确日志或错误时,直接搜索其中稳定的一小段文本,通常比猜函数名更快。

4. 测试是可执行的实现说明

测试可以证明某个边界已经被项目明确保护。例如:

想理解的问题先看测试
AgentLoop 与 Runner 如何衔接tests/agent/test_loop_runner_integration.py
Tool Call 如何执行tests/agent/test_runner_tool_execution.py
Runner 如何处理错误tests/agent/test_runner_errors.py
Session 是否原子保存tests/agent/test_session_atomic.py
Runtime Event 顺序tests/bus/test_runtime_events.py
Channel 插件如何发现tests/channels/test_channel_plugins.py
Subagent 生命周期tests/agent/test_subagent_lifecycle.py

运行一个测试文件:

Set-Location "example\nanobot\code-EN"
pytest tests/agent/test_loop_runner_integration.py -q

运行一个测试函数:

pytest tests/agent/test_runner_tool_execution.py::test_name -q

实际函数名应先用 rg -n "^def test_|^async def test_" 核对,不要凭文档猜测。

5. 断点要放在状态边界

对于一次普通 Turn,优先观察:

位置观察内容
AgentLoop.process_direct 或 _dispatch入口、session key、metadata
_process_messageTurnContext 初始状态
_state_restoresession、checkpoint、媒体
_state_buildhistory、initial_messages、tool context
AgentRunner._run_coreiteration、response、tool_calls
_execute_tools工具选择、参数、并发策略
_state_save新消息、stop_reason、latency
_state_respondOutboundMessage

不要一开始进入 Provider SDK 或 WebUI 组件内部。先确认核心状态在哪一步偏离预期。

6. 同时观察三份消息

调试上下文问题时,至少区分:

  1. Session.messages:持久化会话;
  2. ctx.initial_messages:进入 AgentRunner 的初始输入;
  3. messages_for_model:治理、裁剪后实际发给模型的消息。

很多“模型忘记了”“工具结果丢了”问题,根因就在这三者的差异中。

7. 记录证据,而不是只写结论

推荐使用下面的记录格式:

### 观察
 
- 入口:`nanobot/cli/commands.py::agent`
- 调用:`AgentLoop.process_direct`
- 状态:`BUILD`
- 变量:`ctx.initial_messages`
- 测试:`tests/agent/test_loop_runner_integration.py`
 
### 结论
 
源码事实:……
 
### 尚未证明
 
- 是否所有 Channel 都使用相同 metadata;
- 是否外部插件保证相同生命周期。

8. 不要在学习阶段修改源码来“验证猜测”

核心学习阶段优先使用:

  • 断点;
  • 现有日志;
  • 临时交互检查;
  • 现有测试;
  • 最小只读脚本。

只有进入“源码学习实践”的扩展任务后,才在 learning-CN/examples 或隔离副本中进行明确、局部、可验证的修改;code-EN 源码快照始终保持只读。

Windows 注意事项

当前工作区使用 PowerShell。阅读和运行 nanobot 时注意:

  • 使用 pathlib.Path 思考路径,不假设 /;
  • nanobot 的 ExecTool 在 Windows 使用 cmd /c;
  • CLI 会处理 stdout/stderr 的 UTF-8;
  • MCP stdio 命令存在 Windows 路径归一化;
  • 不要把 Linux 的 bubblewrap 行为套用到 Windows。

相关说明:

  • example/nanobot/code-EN/.agent/gotchas.md
  • example/nanobot/code-EN/.agent/security.md

自检任务

不修改代码,完成以下搜索:

  1. 找到 AgentLoop.process_direct 的所有生产调用方;
  2. 找到 InboundMessage.session_key 的定义;
  3. 找到 ToolLoader 扫描外部 entry point 的位置;
  4. 找到 Session 原子替换目标文件的位置;
  5. 找到对应的至少一个测试。

完成后,你已经具备开始追踪 nanobot 的最低源码阅读能力。

下一步

返回 学习路线总览,进入“运行与源码地图”。