跟踪一个无工具 Turn
实践目标
在不修改源码的前提下,从 CLI 单次消息跟踪到最终 Session 保存,能够亲自解释八个 TurnState 枚举值的顺序,并说明为什么 DONE 没有对应的 state handler。
前置知识
- 已完成真实 DeepSeek 请求;
- 已读 AgentLoop 状态机和 AgentRunner;
- IDE 能以
python -m nanobot启动当前源码 checkout。
需要阅读的代码
按顺序只读这些位置:
nanobot/cli/commands.py::agentnanobot/agent/loop.py::AgentLoop.from_confignanobot/agent/loop.py::process_directnanobot/agent/loop.py::_process_messagenanobot/agent/loop.py::_state_restore到_state_respondnanobot/agent/runner.py::runnanobot/session/manager.py::save
先写下预测
运行前回答:
- CLI 单次模式会不会启动
MessageBus.consume_inbound()? - Session lock 在哪里取得?
- 无 Tool Call 时 Runner 迭代几次?
SAVE在RESPOND之前还是之后?- 默认 Session key 是什么?
实践步骤
1. 设置断点
建议断点:
AgentLoop.process_direct()调用_process_message前;_process_message()调用 state handler 前;AgentRunner._request_model();_state_save();SessionManager.save()。
2. 用固定 Session 运行
假设前文已设置:
$nanobotLab = Join-Path $env:TEMP "nanobot-learning"从 example/nanobot/code-EN 以调试方式运行等价命令:
python -m nanobot agent `
--config "$nanobotLab\config.json" `
--workspace "$nanobotLab\workspace" `
--session "learning:no-tool" `
--message "不要使用任何工具,只回复 TURN_OK"若模型仍尝试使用 Tool,不要修改代码;换成更确定的消息再试,并在记录中说明真实模型决策不是硬保证。
3. 每个 state 记录四个字段
| 字段 | 要观察什么 |
|---|---|
ctx.state | 当前状态 |
ctx.session.key | 是否为 learning:no-tool |
ctx.history / ctx.initial_messages | Session 历史何时变为本轮模型输入 |
ctx.all_messages / ctx.final_content | Runner 返回后保存边界拿到了什么 |
ctx.stop_reason / ctx.outbound | 运行结果何时转成 OutboundMessage |
4. 查看 trace
Turn 结束前检查 ctx.trace:
- state 顺序;
duration_ms;- event;
- error。
正常主路径的 trace 有七条 handler 记录;DONE 是循环退出条件,不执行 _state_done()。
5. 查看持久化文件
不要猜文件名,先列出:
Get-ChildItem -LiteralPath "$nanobotLab\workspace\sessions" -File |
Sort-Object LastWriteTime -Descending |
Select-Object -First 3 Name, LastWriteTime打开最新 JSONL,确认:
- 第一行是 metadata;
- 后续至少有 user 和 assistant;
- 没有凭据;
- Session key 正确。
预期观察
主路径应为:
RESTORE → COMPACT → COMMAND → BUILD → RUN → SAVE → RESPOND → DONE同时应看到:
- CLI 单次模式直接调用
process_direct(),不依赖 AgentLoop 的 bus 消费循环; process_direct()仍复用_session_locks;- BUILD 产生送往 Provider 的完整 messages;
- Runner 只有一轮文本返回时不进入工具执行;
- SAVE 先持久化,再由 RESPOND 构造响应;
process_direct()的finally把 Runtime status 清回 idle。
完成条件
- 画出实际调用链,至少包含 7 个函数;
- 保存一张 state/关键字段观察表;
- 能解释 CLI direct 与 Gateway bus path 的共同核心和不同入口;
- 从 Session JSONL 找到此次 user/assistant 记录。
验证方法
用同一个 Session 再发一条消息:
python -m nanobot agent `
--config "$nanobotLab\config.json" `
--workspace "$nanobotLab\workspace" `
--session "learning:no-tool" `
--message "上一条消息要求你回复什么标记?只回复标记。"若回答能利用上一轮,说明 Session replay 生效;再从 BUILD messages 证明它如何进入模型上下文。
复盘问题
- 为什么 direct path 仍需要 Session lock?
- trace 的 RUN 耗时能否完全等同于 Provider latency?
ctx.messages与session.messages是否是同一用途?- 若在 SAVE 前崩溃,哪些内容可能只存在于内存/checkpoint?