v1.3 Registry 实现重构 + 第一把真实工具

- Registry 从纯接口升级为完整实现:Register/Execute/路由查找/错误自愈
- 新增 BaseTool 接口,统一工具契约
- 新增 ReadFileTool,首个真实工具实现(路径防穿越 + 截断保护)
- 新增 String() 方法,Registry/Engine 调试可视化
- 新增 ToolCall 执行状态日志
- cmd/claw 切换为真实 Registry + ReadFileTool
This commit is contained in:
zhuyongxin
2026-05-15 11:24:42 +08:00
parent daa89531f5
commit b94460083a
6 changed files with 240 additions and 53 deletions
+25
View File
@@ -65,6 +65,31 @@ func main() {
## 版本历史
### v1.3 — Registry 实现重构与第一把真实工具
#### 变更
- **Registry 完整实现** — 从纯接口升级为 `registryImpl`,基于 `map[string]BaseTool` 实现 O(1) 路由查找、动态注册、错误自愈
- **BaseTool 接口** — 定义工具的通用契约(`Name()` / `Definition()` / `Execute()`),所有具体工具统一实现
- **首个真实工具 ReadFileTool** — 支持读取工作区文件,含路径穿越防护和 8000 字节截断
- **调试可视化** — `AgentEngine` 和 `registryImpl` 实现 `String()` 方法,替代 16 进制内存地址
- **ToolCall 执行日志** — 每轮工具执行后输出一行状态日志(`📋 ToolCall xxx: ✅ / ❌, 结果: ...`)
- **cmd/claw 真实化** — 从 mockRegistry 切换到 `tools.NewRegistry()` + `ReadFileTool`
#### 踩坑记录
| 问题 | 原因 | 解决 |
|---|---|---|
| `fmt.Println(registry)` 输出 16 进制地址 | Go 默认打印指针/接口类型为内存地址,不展示内容 | 实现 `fmt.Stringer` 接口,自定义 `String()` 方法 |
| Registry 接口加 `Register` 后旧代码编译失败 | `main.go` 的 mockRegistry 没有实现新增的 `Register` 方法 | 为 mockRegistry 补充空实现 `Register(tool tools.BaseTool) {}` |
| 修改代码时需要同时兼容旧 mock 和新实现 | demo 代码和正式代码共用同一套接口 | 保留 mock 的兼容性(无操作 Register),新演示代码使用真实实现 |
#### 经验教训
1. **接口先于实现,但实现要跟上** — v1.0 就定义了 `Registry` 接口,但一直没有真实实现,导致 cmd 只能靠 mock 跑。抽象要尽早落地
2. **工具系统用 map 路由天然适合 Agent** — 大模型输出工具名 → 直接 map key 查找 → O(1) 路由,简单高效,也方便运行时动态挂载工具
3. **`String()` 是 Go 调试的性价比之王** — 三行代码换来看日志时不用猜内存地址,投入产出比极高
### v1.2 — 真实模型接入与 Thinking 死循环修复
#### 变更