架构大战
在这篇文章写作之前的工具调用是由 Gateway 负责发起的。这相当不合理:
鹅鹅鹅鹅: 06-28 20:23:09
能不能给我看看你想的架构图
chenjintang: 06-28 20:24:30
行,我稍后画一个
chenjintang: 06-28 20:24:42
不过我这一层没什么好说的呀
chenjintang: 06-28 20:24:45
不就是所有工具平铺开来吗?
鹅鹅鹅鹅: 06-28 20:25:05
主要是我不知道你说的是什么问题
chenjintang: 06-28 20:25:08
无论是AI还是人,最后都要调用这一套
鹅鹅鹅鹅: 06-28 20:25:15
我得理解一下
chenjintang: 06-28 20:25:27
我看Deep Wiki上说
chenjintang: 06-28 20:25:34
你AI调用工具,要写入文件
chenjintang: 06-28 20:25:44
这个请求经过审核会发到你的native工具层那里去
chenjintang: 06-28 20:26:02
相当于是你Gateway每接到一个要写入文件这种请求,都会拉起那个程序去写一次文件
鹅鹅鹅鹅: 06-28 20:26:09
嗯对
chenjintang: 06-28 20:26:18
那问题在于,有些时候是磁盘上的内容出现了变动
chenjintang: 06-28 20:26:21
你AI又不知道这一点
chenjintang: 06-28 20:26:32
你总不能说延迟到下一次。工具调用,再告诉他这一点
chenjintang: 06-28 20:26:40
你提的每次一起来,直接去刷新全局状态,那更不可能
chenjintang: 06-28 20:26:44
首先第一,AI轮询非常浪费token
chenjintang: 06-28 20:26:54
其次是这种东西的并发效率也会非常低
鹅鹅鹅鹅: 06-28 20:26:55
那你要怎么解决
chenjintang: 06-28 20:26:57
光是Git,你就得等个两三秒
chenjintang: 06-28 20:27:09
所以我说工具层和上一层的Gateway之间肯定是要有联系的
chenjintang: 06-28 20:27:23
它不仅仅只是Gateway调用工具层那么简单,工具层应该也能将当前系统的一个真正的状态,去反馈给在网上的那些Agents啦,或者Editor啦等等
chenjintang: 06-28 20:27:31
也就是说,相当于是工具层类似于承担了在JetBrains IDE里面叫做VFS的东西
鹅鹅鹅鹅: 06-28 20:28:03
额
鹅鹅鹅鹅: 06-28 20:28:16
我架构图和落地好像
鹅鹅鹅鹅: 06-28 20:28:21
出偏差了
鹅鹅鹅鹅: 06-28 20:28:47
gateway不应该承担这么多
鹅鹅鹅鹅: 06-28 20:29:08
卧槽了傻逼GLM5.1
chenjintang: 06-28 20:29:22
总不能是Deep Wiki出错了吧?
chenjintang: 06-28 20:29:29
我问过好几次Deep Wiki了,它都跟我是这么说的
chenjintang: 06-28 20:29:34
我也没想到能这么实现
鹅鹅鹅鹅: 06-28 20:29:45
我自己画架构和落地出偏差了正因为如此,我们计划重构工具层。
目标
2.1 架构
如 图 1 ,清晰明了。
2.2 要实现的工具
2.2.1 最关键的:文件写入相关
文件读取
- 指定文件名,偏移(按行)以及要读取的行数
- 没有指定行数的,最多150行
- 没有指定偏移的,从文件最头部开始
- 一个特殊模式:输入一系列行号,返回每一行的HashTag
- 返回整个文件的Hash
文件写入
- 实现 HashTag 写入:每一行计算出一个2byte(4字母)的哈希,根据hashtag来定位和写入。支持:插入(某两行(包括)之间)、删除(某两行(包括)之间),替换(可批量)
- 而且写入时也需要指定此时文件整体的HASH。
例如:
源文件:
1|AF #include<iostream> 2|ZH using namespace std; 3|4D int main() 4|2B { 5|8C cout<<"Hello WOrld!"<<endl; 6|7F }进行的patch
[ { "type" : "replace", "startline": "8C", "endline" : "8C", //同一行 "content" : "cout<<\"Hello World!\"<<endl;" } ]结果:
1|AF #include<iostream> 2|ZH using namespace std; 3|4D int main() 4|2B { 5|6D cout<<"Hello World!"<<endl; 6|7F }
文件内全文查找(暴力版本)
- 包装 ripgrep 即可。返回文件名+行号的组合
文件删除
- 必须同时指定文件名和hash防止误删
模糊匹配文件名工具
- 基于nucleo的实现鉴于C#没有类似的东西,祝我好运
- 基于nucleo的实现
2.2.2 代码语义相关
CodeGraph 工具集:
- 原样 wrap CodeGraph 原版。
LSP 工具集
- 精确重命名符号
- 快速类型检查
- 在不用编译/执行的情况下获取错误
2.2.3 版本控制相关
实现一整套与 JetBrains git类似的工具集
git 基本功 add commit push pull clone rebase forcepush merge reset 不做过多赘述
- 注意要提供dryrun结果方便审核
词级别高亮:
将会返回:原始内容,当行更改后的内容(如果有),两行之间词级别的更改,没有就返回空;同时也包括两侧的 Tag
某一行的状态渲染:
- 将会综合目前工作树、index 区和 HEAD 的更改。
- 返回:这一行对比的状态即:
- 第一是这一行的状态(添加/更改/删除)
- 第二是这一行与 index 区的一致性
- 第三是 index 与 head 在这一行的一致性
- 都是逐词高亮 diff
标记 Git Diff Blok 用于分批提交,有这些:
- 标记某一行/某几行属于哪些更改
- 拿到某一行/某几行的更改类别
- 拿到所有类别
- 拿到指定类别对应的“更改行”的行号
逐行暂存:
根据给定的选择直接自动合成直接写入 index
- 如果没有 select lines 跟 git add . 的效果是一样的
同时支持 git hook 和自定义的代码检查(自动返回结果)
专门的处理合并冲突的工具:
- 调用时会先尝试自动解决(来自 rebased)
- 提供查询冲突位置的工具方便快速跳转
- 给定冲突块编号可以用列行号/Tag表的方式决定取舍
- 确定更改
gh CLI 内置兼容,用来开issues啥的
2.2.4 其他工具
网络搜索,大概是EXA
终端命令执行
- 拦截用来写入文件的
cat和echo(防止吸猫) - 沙箱机制
- 拦截用来写入文件的
用户装的 MCP 也在这一层透传
工具调用契约
统一走stdin/stdout
具体格式如下:
发送请求:
{
"tool_id": "string",
"session_id": "string",
"toolcall_id": 1,
"approved": true,
"params": {/*...*/}
}请求回复:
{
"call_id": 1, //对应调用的toolcall_id
"success": true, //状态
"result": {/*...*/}
}还有一些特殊的工具:
list 工具
- 列出所有工具的tool_id和一段描述
info 工具
- 列出工具需要的params
- 顺便带上这工具的当前状态