实现一个只读 Tool 插件
实践目标
理解并运行一个独立的 text_stats Tool plugin:先测 Tool 契约,再验证 entry point 发现,最后用 DeepSeek 完成一次真实 Tool Call。全程不修改 nanobot core。
前置知识
- 已完成 Workspace Skill 实践;
- 能创建/使用独立 Python 虚拟环境;
- 已读 ToolLoader 和 ToolRegistry;
- 使用实验 workspace 和 config。
示例位置
本学习库提供可运行示例:
example/nanobot/learning-CN/examples/text-stats-tool-plugin/
├── pyproject.toml
├── nanobot_text_stats/
│ ├── __init__.py
│ └── tool.py
└── tests/
└── test_tool.py需要阅读的代码
先读示例,再对照:
nanobot/agent/tools/base.py::Toolnanobot/agent/tools/schema.pynanobot/agent/tools/loader.py::ToolLoadernanobot/agent/tools/registry.py::ToolRegistrytests/agent/test_tool_loader_entrypoints.py
先写下预测
- entry point key 与
Tool.name是否必须相同? read_only=True会影响什么?- 缺少
text参数时,是execute()还是 Registry 返回错误? - plugin 与内置 Tool 同名时谁优先?
实践步骤
1. 阅读 Tool
TextStatsTool:
- 名称为
text_stats; - 参数
text必填,长度 1–20000; - 返回 JSON 字符串;
- 不读取文件或网络;
read_only=True,因此在非 exclusive 情况下可并发。
先用你自己的话解释每个设计选择。
2. 安装源码 checkout
在专用虚拟环境中:
Set-Location "example\nanobot\code-EN"
python -m pip install -e .3. 安装 plugin
python -m pip install -e "..\learning-CN\examples\text-stats-tool-plugin" --no-deps--no-deps 是因为上一步已经安装当前源码 checkout,避免又从 PyPI 安装另一份 nanobot。
4. 运行 Tool 单元测试
从 example/nanobot/learning-CN/examples/text-stats-tool-plugin:
python -m pytest tests -q5. 验证 entry point
python -c "from nanobot.agent.tools.loader import ToolLoader; print(sorted(ToolLoader()._discover_plugins()))"预期包含:
text-stats注意:这是 entry point key;模型调用的函数名是 text_stats。
6. 验证 Registry
可在调试器或短暂 Python REPL 中:
- 创建
ToolContext; - 创建
ToolRegistry; ToolLoader().load(ctx, registry);- 检查
registry.has("text_stats"); - 执行合法与非法参数。
7. 真实 DeepSeek smoke test
重新启动 nanobot 进程,让 entry point 重新发现。发送:
必须使用 text_stats 工具统计这段文本,并告诉我 characters、non_whitespace_characters、lines:
第一行
second line在 Runner 断点确认 Tool Call name 与 arguments。不要只凭最终自然语言判断工具确实被调用。
预期观察
- ToolLoader 能发现独立 package;
- Provider tool schema 中出现
text_stats; - 缺失/超长参数在 Registry 层失败;
- 合法结果先作为 tool message 回给模型;
- Plugin 未修改
AgentLoop; read_only使基类concurrency_safe为真。
完成条件
- 示例 tests 通过;
- entry point 可发现;
- Registry 能执行;
- 至少一次真实模型 Tool Call;
- 能解释 entry point key、class name 和 Tool name 三者;
example/nanobot/code-EN无源码 diff。
验证方法
额外增加一个测试:
- 空字符串必须失败;
- 或 20001 字符必须失败;
- 或验证换行计数。
这是允许修改的示例区域。修改后重新运行 plugin tests,并检查 diff 只落在示例目录。
复盘问题
- 为什么示例选择纯函数只读 Tool?
read_only=True是否由框架自动证明?- 为什么要在真实请求前先测 Registry?
- 如果将来加入 URL 参数,需要补哪些安全机制?
下一篇:综合扩展设计评审