从零实现 GeekAgent —— Day4 文件工具与注册表
内容提要
本文介绍GeekAgent开发第4天:将文件读写从shell中拆出,新增ls、read、glob三个免确认只读工具,以及write、patch两个展示diff后确认的写工具,并统一注册到工具注册表。通过唯一文本片段锚定修改,用前缀/后缀修剪生成diff预览,实现“读→改→验证”闭环,代码净增211行。
延伸解读
为什么读和写要分开
文章指出,把读和写都放在同一个 shell 工具里,会导致无害的读操作频繁被确认,而危险的写操作却难以预览。将读操作拆成免确认的只读工具,写操作拆成展示 diff 的专用工具,既提高了效率,又让确认更有针对性。这种设计思路对构建安全的工具调用系统有参考价值。
patch 锚点的选择
patch 工具使用唯一文本片段作为锚点,而不是行号或正则。这是因为模型刚读过文件,直接引用原文更可靠,且能避免行号偏移和正则转义的麻烦。实现时通过检查片段在文件中出现的次数来保证唯一性,找不到或匹配多处都会返回明确错误,引导模型调整锚点。
简单 diff 的取舍
文章没有实现复杂的 diff 算法,而是用前缀/后缀修剪来生成预览。这种方法只覆盖最常见的单处修改场景,对于多处分散修改会降低可读性,但信息不失真。同时设置了 100 行上限防止刷屏。这种“够用就好”的务实态度,避免了过度设计。
工具注册表的价值
随着工具数量增加,平铺数组容易导致重名覆盖和入口混乱。引入注册表后,新增工具必须显式注册,重名直接报错,且所有访问都通过统一出口,保证了清单与分发的一致性。这为后续扩展工具集打下了良好基础。
Q&A
GeekAgent Day4 新增了哪些文件工具?
Day4 新增了五个文件工具:三个只读工具 ls、read、glob,以及两个写工具 write、patch。
为什么 GeekAgent 要把读写从 shell 中拆出来?
因为 shell 工具 run_shell 需要确认,读文件这种无害操作也被频繁确认,而写操作通过 shell 无法预览 diff,确认粒度粗且改错代价静默。拆出后,读工具免确认,写工具展示 diff 再确认,更安全高效。
GeekAgent 的 patch 工具如何定位要修改的位置?
patch 工具使用唯一文本片段作为锚点,要求每个 old 片段在文件中恰好出现一次。找不到或匹配到多处都会报错,提示模型调整锚点。
GeekAgent 的 diff 预览是如何实现的?
diff 预览采用前缀/后缀修剪的简单算法:将新旧文本按行拆分,砍掉完全相同的公共前缀和后缀,中间部分即为改动,前后各留 3 行上下文,并限制最多展示 100 行。
GeekAgent 的工具注册表有什么作用?
工具注册表将工具统一收口,新增工具必须通过 registerTool 注册,重名会抛错,避免静默覆盖。toOpenAITools 和 execTool 两个出口都从注册表读取,保证清单与分发一致。
GeekAgent 的 write 和 patch 工具在写入前如何确认?
两者都通过 commitWrite 函数,先读取旧内容,用 simpleDiff 生成 diff 预览,然后调用 confirm 请求用户确认,输入 y 后才真正写盘。
GeekAgent Day4 明确没有实现哪些功能?
Day4 没有实现权限与目录隔离、撤销/回滚、标准 diff 能力(仅前缀后缀修剪)、以及二进制文件支持(只处理 UTF-8 文本)。