Article

2026-06-28 开发日志:关于工具的讨论

TinadecOffice 开发日志

架构大战

在这篇文章写作之前的工具调用是由 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 

图 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#没有类似的东西,祝我好运

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

  • 终端命令执行

    • 拦截用来写入文件的catecho(防止吸猫)
    • 沙箱机制
  • 用户装的 MCP 也在这一层透传

工具调用契约

统一走stdin/stdout

具体格式如下:

发送请求:

{
    "tool_id": "string",
    "session_id": "string",
    "toolcall_id": 1,
    "approved": true,
    "params": {/*...*/}
}

请求回复:

{
    "call_id": 1, //对应调用的toolcall_id
    "success": true, //状态
    "result": {/*...*/}
}

还有一些特殊的工具:

  1. list 工具

    • 列出所有工具的tool_id和一段描述
  2. info 工具

    • 列出工具需要的params
    • 顺便带上这工具的当前状态

PDF 版本

下载本文的 PDF 版本用于离线阅读

下载 PDF