跟踪 Tool Call 与 Session

实践目标

跟踪一次 read_file Tool Call,观察 Provider response、ToolRegistry、checkpoint、下一轮模型输入和最终 Session 之间的数据变化。

前置知识

  • 完成上一个无工具 Turn;
  • 理解 OpenAI function schema;
  • 使用独立实验 workspace。

需要阅读的代码

  1. nanobot/agent/tools/filesystem.py::ReadFileTool
  2. nanobot/agent/tools/registry.py::ToolRegistry.execute
  3. nanobot/agent/runner.py::_execute_tools
  4. nanobot/agent/runner.py::_run_tool
  5. nanobot/agent/runner.py::_emit_checkpoint
  6. nanobot/agent/loop.py::_state_save
  7. tests/agent/test_runner_tool_execution.py
  8. tests/agent/test_runner_persistence.py

先写下预测

  • Provider 第一次 response 中 Tool Call 长什么样?
  • tool result 以哪个 role 加入 messages?
  • Tool 执行后是否立即保存完整 Session?
  • 第二次 Provider 请求比第一次多哪些消息?
  • 最终用户会不会同时收到 tool result 和自然语言答复?

实践步骤

1. 准备只读 fixture

$nanobotLab = Join-Path $env:TEMP "nanobot-learning"
$fixture = Join-Path "$nanobotLab\workspace" "turn-observation.txt"
Set-Content -LiteralPath $fixture -Encoding utf8 -Value "marker=NANOBOT_TOOL_OK"

这里只写实验 workspace,不修改 nanobot 源码。

2. 设置断点

  • AgentRunner._request_model() 返回后;
  • _execute_tools();
  • ToolRegistry.execute() 校验前后;
  • ReadFileTool.execute();
  • _emit_checkpoint();
  • 下一次 _request_model();
  • _state_save()。

3. 发出明确请求

python -m nanobot agent `
  --config "$nanobotLab\config.json" `
  --workspace "$nanobotLab\workspace" `
  --session "learning:tool-call" `
  --message "必须使用 read_file 读取 turn-observation.txt,然后只回复 marker 的值。"

真实模型仍可能不按预期调用。若发生,保留该观察,再用测试中的 fake Provider 完成确定性跟踪。

4. 做一张消息形态表

时刻role关键字段
初始usercontent
第一次 Provider responseassistanttool_calls
工具执行后tooltool_call_id、name、content
第二次 Provider responseassistantfinal content

不要复制全部 prompt;只记录结构和简短摘要。

5. 查看 checkpoint

在 _emit_checkpoint() 后检查 Session metadata 中运行 checkpoint:

  • phase;
  • iteration;
  • model;
  • assistant_message;
  • completed_tool_results;
  • pending_tool_calls。

然后在正常 SAVE 后确认 checkpoint 是否被清理或物化为正式历史。

6. 查看 Tool schema

在第一次 Provider 请求前查看:

spec.tools.get_definitions()

找到 read_file,比较其 schema 与模型返回 arguments。

预期观察

sequenceDiagram
    participant P as Provider
    participant R as AgentRunner
    participant T as ToolRegistry
    participant S as Session

    R->>P: messages + tool schemas
    P-->>R: assistant(tool_calls)
    R->>S: checkpoint awaiting_tools
    R->>T: execute read_file
    T-->>R: marker=NANOBOT_TOOL_OK
    R->>S: checkpoint tools_completed
    R->>P: + assistant tool_call + tool result
    P-->>R: final text
    R-->>S: SAVE final turn

关键结论:

  • Tool schema 在模型请求前生成;
  • Registry 在 Python 侧再次校验参数;
  • Tool result 是下一轮模型的观察,不是默认直接回复用户;
  • checkpoint 支持取消/中断恢复;
  • 正常 SAVE 将本轮合法消息写入 Session。

完成条件

  • 能指出两次 Provider 请求的 messages 差异;
  • 能从 Tool Call id 关联 assistant call 与 tool result;
  • 能解释 checkpoint 和最终 Session 的区别;
  • 最终答复为 NANOBOT_TOOL_OK;
  • 没有修改源码。

验证方法

运行确定性测试:

Set-Location "example\nanobot\code-EN"
python -m pytest tests\agent\test_runner_tool_execution.py -q
python -m pytest tests\agent\test_runner_persistence.py -q

选择一个 test 用调试器单步,与真实 DeepSeek 调用的对象结构对照。

复盘问题

  1. Tool schema 为什么既给模型看,又在本地校验?
  2. 如果 Tool 已经写文件后 Runner 取消,checkpoint 能否回滚文件?
  3. 为什么 final assistant message 不能省略?
  4. Tool result 过大时会怎样影响下一次模型请求?

下一篇:复现流式、取消与重试