稻草人新闻RSS 聚合阅读

← 返回 💻 编程 & 软件工程

chat.nvim v1.10.0:懒加载工具发现与 find_tool

Eric's Blog 9月13日 wsdjeg.net

chat.nvim v1.10.0 发布了(2026-09-11),距 v1.9.0 约一个月。这个版本的核心主题是省钱和干净——用懒加载工具发现机制砍掉请求里冗余的工具 schema,再用一轮 UTF-8 卫生修复堵住脏数据。

本版共 44 个提交:12 feat + 14 fix + 4 docs + 7 test + 2 ci + 2 refactor + 3 chore。发布时 56 个测试文件、1193 个用例全绿。

懒加载工具发现:本版重点

问题:工具越多,token 越贵

chat.nvim 的内置工具已经积累到几十个:文件读写、git 系列、web 搜索、zettelkasten、日程任务……每次请求,所有工具的 JSON schema 都会被塞进请求体。但你一次对话往往只用其中三五个,其余几十个 schema 纯属浪费——这些 token 每轮请求都在计费。

这个问题的想法最早记在今年 2 月的笔记里,当时的方案是在配置里写死一个静态列表:

tools = {'find_files', 'read_file'},  -- 只加载这两个工具

静态列表的缺点很明显:你得提前猜这次对话需要什么工具,猜少了 AI 就”断手断脚”,猜多了等于没省。

v1.10.0 落地的是一个动态方案:默认只发送 essential 工具集 + find_tool,其余工具由 AI 按需发现。

工作机制

每轮请求实际发送的工具,按这个顺序组装:

  1. essential 工具集(默认 5 个):read_file / list_directory / search_text / find_files / get_time,覆盖最常见的文件操作
  2. 当前回合历史中已调用的工具(自愈,见下文)
  3. 本会话已激活的懒加载工具
  4. find_tool(始终在最后兜底)

find_tool 内嵌全部工具的实时目录——每个工具的名称加一行简介。AI 需要目录之外的能力时,用名称或关键词查询 find_tool:

  • 精确名称匹配(不区分大小写):返回完整 schema 并激活该工具,下一轮响应即可调用
  • 唯一部分匹配:直接返回 schema,省一轮往返
  • 多个候选:返回候选列表让 AI 细化
  • 无匹配:返回全目录辅助查询
  • 查询 list / all / catalog 或留空:返回全目录

MCP 工具也在目录里,同样可经 find_tool 发现。

回合生命周期

懒加载的激活状态是回合制的:

用户提问
  → 请求只带 essential + find_tool
  → AI 需要 git 工具 → 查询 find_tool
  → git 工具 schema 激活并入请求
  → AI 调用 git 工具完成任务
  → AI 输出纯文本回复(回合结束)
  → 激活状态清空
  → 下回合从 essential 重新开始

关键实现是 on_progress_done:一旦 AI 返回纯文本回复(没有 tool_calls),就清空已激活的工具。激活状态只存内存、永不落盘——既省 token,又不污染会话历史。

自愈机制

工具调用循环有个脆弱点:如果请求中断或 Neovim 重启,AI 正在用的工具可能不在 essential 里,直接重发会报”工具不存在”。

scan_history_tool_names 解决了这个问题:扫描会话历史,把当前回合(尚未以纯文本回复结束)内调用过的工具重新纳入请求。遇到纯文本回复即清空累计。也就是说,中断恢复后工具调用循环可以继续跑,不会卡死在半个回合上。

配置

默认开箱即用,无需任何配置:

require('chat').setup({
  tools = {
    lazy = true,  -- 默认值,开启懒加载
    essential = { 'read_file', 'list_directory', 'search_text', 'find_files', 'get_time' },
  },
})

tools.essential 可以自定义——比如你常写 git 相关的任务,可以把 git_status、git_diff 常驻。想回退到老行为,设 tools.lazy = false 即恢复全量发送。

另外一个小细节:find_tool 自己被排除在工具目录之外,避免”目录嵌目录”的递归;它的输出和 MCP 工具的 schema 同样经过 UTF-8 清洗,保证请求体内所有 schema 合法。

完整文档在 docs/tools/find_tool.md。

其他新特性

write_file 支持 fileformat

write_file 工具新增 fileformat 参数,可选 unix / dos / mac 三种行尾。省略时自动检测并保留现有文件的行尾,新文件默认 unix。改 Windows 项目的 CRLF 文件不再被悄悄换成 LF。

Markdown 感知消息分块器

所有 IM 集成(Discord、Telegram、Slack、Lark、钉钉、企微、微信,共 7 个)共享一个新的分块器:按 Markdown 结构切分长消息,不会把代码块或表格从中间砍断。

HTTP API 增强

  • 新增 /skills 端点,含用户自定义 skill
  • 新增 GET/DELETE /logs:GET 支持 level / name / tail 过滤(可组合),DELETE 清空运行时日志
  • /sessions 系列端点返回 session token 用量统计,/session/new 的创建响应也含 usage——配合懒加载,token 省没省一目了然

脏工作区自动 nudge

AI 回复后如果工作区仍有未处理的改动(比如忘了 commit),chat.nvim 会自动提醒 AI 处理。async 工具未完成时跳过 nudge,避免干扰进行中的任务。

两个 skill 重命名

  • /disable-force-push → /toggle-force-push:按会话阻止 AI 执行 force push,语义更准确(原来名字暗示永久禁用)
  • /messages → /nvim-messages:捕获 :messages 的报错信息交给 LLM 分析修复,重命名避免歧义

另外,未安装任何集成时,/list 现在会显示可用的集成列表,而不是一片空白。

UTF-8 卫生:请求前的最后防线

14 个 bug 修复里,最有体系的是 UTF-8 卫生系列。上游 API(比如返回 NonUTF8Body 错误的那些)对请求体的字节合法性要求严格,v1.10.0 在请求链路上做了四道清洗:

  • sessions:请求前清理消息内容中的非法 UTF-8(55b738a)
  • providers:请求体编码前 sanitize(0515532)
  • tools:MCP 工具 schema 清洗为合法 UTF-8(ada483a)
  • find_tool:目录简介按 UTF-8 边界截断,多字节字符(中文)不撕裂(3094494)

集成方面也修了一批实际问题:Discord GET 请求不再在 stdin 上永久挂起;微信 send_jobid 为 nil 导致最后一条消息未发出的崩溃;Lark/Slack/企微/钉钉/Telegram 的排队分块正确传递。

工程建设

  • 新增 make coverage:luacov 行覆盖,支持 COV_THRESHOLD 门槛(如 make coverage COV_THRESHOLD=80),报告写入 coverage.log
  • junit 测试结果体系:所有 CI matrix 任务上传 junit artifacts;make junit 支持 ARTIFACT_NAME 参数;artifact 下载有三重回退(unzip → python3 zipfile → busybox);本地复现 CI 测试:make junit ARTIFACT_ID=9277505915 ARTIFACT_NAME=junit-Windows-stable
  • 发布时 56 个测试文件、1193 个用例全绿

一个花絮:官方 CHANGELOG 只列出 38 条提交,另外 6 个(含 junit 体系和懒加载流程文档 2a0cc7f)因为 release-please 默认不输出 ci/chore 段落、以及 rebase 时序问题被漏掉了——本文的数据来自逐个 commit 核对,比 CHANGELOG 更全。

升级

已安装用户直接更新即可:

{
  'wsdjeg/chat.nvim',
  -- lazy.nvim 会自动拉取最新 tag
}

懒加载默认开启,升级后无需改配置就能生效。如果发现某个工具 AI 总要”绕一圈”才能找到,把它加进 tools.essential 即可。

总结

v1.10.0 的懒加载工具发现是个典型的”用约束换效率”设计:AI 能力不减(所有工具仍可发现),但每轮请求只携带当下需要的 schema。配合回合制激活和自愈机制,这个方案在省 token 的同时没有牺牲可靠性。

从 2 月笔记里的静态配置列表,到 9 月落地的动态发现,这个功能走了七个月——好的设计值得等。

相关链接:

在原文站打开 ↗

Cloudflare Workers 每 3 分钟抓一批,9 批轮完最快约 27 分钟 · 点右上 ↻ 立刻全量抓一次