第十节 智能编程工具

下列工具默认在工作区目录范围内可用,互相协同支撑助手在你的代码仓库里"干活"。

10.1 读文件

支持按行或按字节读取,自动处理大文件分段。读取时会附带内容指纹,确保后续编辑基于"刚刚读过"的版本,防止并发覆写。

10.2 写文件 / 编辑

  • 覆盖写:完整重写整个文件,会校验"读过"前提。
  • 片段编辑:基于内容指纹做精确替换,AI 必须先读、再改,杜绝幻觉式覆盖。

10.3 目录列表 / 文件查找

  • 列目录支持过滤、按时间排序。
  • 支持通配符匹配文件名,自动跳过 .git / node_modules 等无关目录。

10.4 内容搜索

  • 支持正则表达式、文件名通配过滤。
  • 自动跳过二进制文件,启发式判断编码。

10.5 终端执行

  • 同步执行:单次命令运行后等待完成,输出回到对话流。
  • 后台执行:长任务(构建、压测)转后台,期间可拉取实时输出、随时停止。

每一次终端调用默认都受工具权限管控:可放行、可询问、可禁用。

10.6 版本控制

提供基础 Git 操作:仓库状态查询、新建 / 切换分支、新建仓库,以及暂存 / 提交 / 合并 / 变基等分支写操作;推送仍由助手通过终端执行类工具完成。这些操作默认与其他工具一致为「启用」(不额外弹询问);对合并、推送等高风险操作,可在智能体管理器 →「配置工具」里按工作模式把对应工具设为「询问」或「禁用」(见 §13.1)。

10.7 网络检索(可选)

  • web.bing:Bing RSS 端点搜索,返回标题 / 摘要 / URL。
  • web.fetch:抓取指定 URL 并自动清洗为 Markdown / 文本 / HTML。

检索来源与内容均会作为引用卡片展示在消息流。

10.8 时间与上下文

  • time.now:获取当前时间(含时区),方便助手为日志、TODO、文件名打标。
  • TodoWrite:写入 / 更新待办列表,单次最多 50 条;详见第十六节。

10.9 上下文截断

任何读取或检索类工具的单次返回都有"软上限 + 硬上限",超出会自动截断并标注,方便助手分批续读,避免一次性灌爆模型上下文。

工具结果瘦身,更省额度:工作区工具的返回结果做了精简 —— 文件路径改为相对工作区根显示、错误信息剥除冗长的根路径前缀、去除重复的行哈希、分页记账仅在结果被截断时才输出。整体减少无谓的 token 占用,长任务更省额度(也不再把绝对根路径泄露到结果里)。

10.10 代码智能工具组(code.*,基于语言服务器)

新增一组让 AI 像 IDE 一样理解代码的工具:基于业界标准的 LSP(Language Server Protocol),把语言服务器的"找定义 / 找引用 / 重命名 / 编译诊断"等能力直接交给 AI 使用 — 做事更准、更省来回、不再靠正则猜。

工具 用途
code.definition 跳转到定义:找到某符号在哪个文件第几行真正定义
code.references 查找引用:列出某函数 / 类型 / 变量被哪里调用
code.hover 看签名 / 类型 / 文档:等同 IDE 鼠标悬停
code.symbols 文件大纲:列出文件里的函数 / 类型 / 方法
code.workspace_symbols 全仓按名搜索符号
code.diagnostics 编译错误 / 警告:拿到编译器级别的报错,不是靠日志猜
code.call_hierarchy 调用图:某函数被谁调用 / 调用了谁
code.repo_map 项目骨架:文件树 + 各文件顶层符号,token 友好的全仓概览
code.rename 跨文件安全重命名 — 真正改名而不是文本替换
code.code_action 快速修复:列出 / 应用语言服务器给出的 quick fix;配合 code.diagnostics 形成"看错 → 修错"自愈闭环
code.lsp_status 看本工作区 LSP server 池的实时状态
code.implementation 找实现:从接口 / 抽象方法跳到它的各个实现
code.type_definition 跳类型定义:从变量或表达式跳到它的类型声明处
code.document_highlight 本文件内高亮:某符号在当前文件里的全部读写与引用点,比全仓找引用轻量
code.completion 补全候选:给出某个光标位置的代码补全建议
code.signature_help 函数签名提示:调用函数时给出形参列表并标出当前参数
code.formatting 格式化整篇:按语言服务器规则格式化并写回文件(无改动则不写)

位置参数对外 1-based(与读文件一致 / 同 cat -n),不必心算偏移。

按语言服务器实际支持情况自动降级:并非每门语言的服务器都实现了全部能力。是否支持以实际调用结果为准,而不是听服务器自报,避免它其实能做却被误判拒绝;判定出"不支持"后会记住,同一台服务器在后续会话里不再重复试探。

10.10.1 内置 22 门语言 + 按需自动下载

内置覆盖主流语言:Go、TypeScript / JavaScript、Python、C / C++、Rust、Java、Lua、Bash、YAML、PHP、Ruby、Vue、Zig、Dart、Kotlin、Clojure、Elixir、Haskell、F# / C#、Gleam、Astro 等 22 门。

  • 缺哪门语言的服务器会按需自动下载(安全托管、带紧急开关),下载进度有 toast 提示。
  • 设置 → 代码智能 / LSP 面板能看到正在运行的服务器与安装状态。

10.10.2 用 lsp.json / lsp.yaml 覆盖默认

工作区根目录或 ~/.avlcode/ 放一个 lsp.yaml(推荐,兼容旧 lsp.json)就能覆盖默认设置 — 改启动命令、改 root marker、禁用某门语言、增加自定义 server 都行。改完热重载、不用重启工作区;服务器进程崩了 / SSH 掉线自动重启

10.10.3 远程工作区同样可用

LSP 在 SSH 远端启动:远端缺什么语言的服务器按需自动下载到远端,跨系统的路径与读写都已正确处理,远程工作区的代码智能与本地等同。

10.10.4 Python 虚拟环境(venv)自动遵循

AI 在工作区里跑 Python 相关命令时自动探测并使用工作区的虚拟环境,不必每次手动 activate

  • 覆盖venv / .venvconda(含 Windows 上 Scripts/python.exe 布局)、poetry(含项目目录之外的环境)、pipenv
  • 机制:把首命令改写成 venv 里的绝对路径,并注入 VIRTUAL_ENV / PATH,保持工具链一致;探测不到 venv 时零行为变化(命令原样执行)。
  • 缓存:项目外的发现(poetry / pipenv)有 5 分钟缓存 + 15 秒探测超时,避免每轮都 fork。
  • 逃生口:环境变量 AVLCODE_DISABLE_VENV_AUTODETECT 非空即全局关闭自动感知。
  • pyright 语言服务也接入 venv 感知,本地和 SSH 远端都生效,代码智能里的类型推导走的就是你真实的 venv。

10.10.5 全库代码检索与问答(code.search / code.ask

在「找定义 / 找引用」这种精确点查之外,新增两个全库语义检索工具,回答「登录逻辑在哪、怎么走的」这类问题:

工具 用途
code.search 用自然语言 / 关键词在整个项目里检索符号(函数 / 类型 / 方法),返回排好序、文件:行 的命中
code.ask code.search 基础上,直接基于检索到的片段合成「人话答案 + 文件:行 引用」,找不到会直说、不编造

特点:

  • 纯本地、零向量 / 零 embedding,可离线(air-gap):用 BM25 + 标识符切分(驼峰 / 下划线 / 连字符自动拆词)+ 中日韩二元切分(中文注释、中文提问同样能命中),不依赖任何模型或联网。
  • 命中可解释:每条结果都给出 文件:行命中的关键词,看得清为什么是这条。
  • 索引自动跟上:首次调用自动建索引;之后按文件 mtime 增量刷新(改 / 增 / 删自动更新)。本地索引落在工作区 .avlcode/rag/。建索引与启动语言服务的进度以单条原地更新的进度条显示(不再刷一屏 toast)。
  • 超大仓库如实告知:索引最多覆盖 4000 个文件。超过时会明确告诉你有多少文件没被索引,而不是悄悄少搜一部分 —— 「没搜到」到底是真不存在,还是没覆盖到,一看便知。
  • 结构感知重排:结合中心度调用关系把「被调用更多 = 更核心」的符号排前(code.searchexpand=true 显式启用;code.ask 现在默认就做这道智能重排,回答代码问题更聚焦相关内容)。增量刷新的作用域也已修正,文件变更后更准,不多刷、不漏刷。
  • 远程也可用:SSH 远程工作区同样支持(远端枚举 / 读取,索引在内存);code.ask 复用当前会话的模型

这两个工具与上表的 code.*(LSP 精确查询)互补:精确定位用 code.definition / code.references,「这事在哪、怎么实现的」用 code.search / code.ask

10.11 测试运行工具 test.run

AI 现在能自己跑测试、读懂失败、再回去修,形成「跑测试 → 看失败 → 改代码 → 再跑验证」的闭环。

  • 自动识别测试框架:按项目文件判断 — go.mod → Go / pyproject.toml·pytest.ini → pytest / Cargo.toml → cargo / package.json → Node(vitest、jest);也可手动指定 frameworkcommand 完全覆盖。
  • 结构化失败:跑完把输出解析成 failures[](每条含 test 名 / 文件 / 第几行 / 错误信息),最多回灌 50 条 + 一小段原始 tail,不把整篇测试日志灌进对话 — 省 token
  • 未知框架兜底:解析失败时退回到退出码 + 末尾输出,保证至少能告诉你失败了什么。
  • 范围限定:可传 path 限定(Go 包模式如 ./pkg/...、pytest 目录或文件、cargo 包名)。
  • 远程也能用:本地 + SSH 远端工作区一视同仁。
  • plan(筹划)模式禁用:跑测试 = 执行项目代码,会产生写副作用,与"只想不做"的定位冲突。assess(评估)模式可用 — 验收阶段本就需要实际跑一遍。

10.11.1 自检门禁(opt-in,四类检查器)

自检门禁」是测试门禁的泛化:开启后 AI 每轮干完活后自动跑一组检查;如果有失败,把结构化失败信息回灌给它并强制接着修,直到全绿 — 不用你手动催「再跑下测试」。

六类检查器(可单选 / 组合):

检查器 用途 后端 / 工具
test 跑测试 test.run(Go / pytest / cargo / Node)
diagnostics 编译 / 类型错误 code.diagnostics(LSP,无需额外工具)
lint 代码风格 / 潜在 bug lint.run(golangci-lint / eslint / ruff)
sast 安全静态分析 sast.run(semgrep)
testgen 改动的源文件缺测试 → 自动补(opt-in) fs.glob 经 zMCP 网关查约定路径(Go / Python / JS / TS)
judge LLM 收尾自审(opt-in,默认 report 独立 fresh-context 模型调用,只看「目标 + 本轮 diff」,挑机检抓不到的逻辑 / 语义 / 安全意图错

每个检查器都可调

  • modeblock(默认,注入失败强制修) / report(只通报不拦)
  • submode
    • testchanged(默认,只测改动的)/ full(全跑)
    • lintstrict(警告也算红)/ lenient
    • sastquick(限大文件加速)/ deep(完整)
  • scopechanged(默认,本轮改动文件)/ workspace(全库)
  • path / linters / config:精细化覆盖

testgen / judge 不在默认集:要显式把 testgen / judge 加进 checks 才生效;前者只判 Go / Python / JS / TS 这些有明确测试约定的语言,跳过测试文件本身与生成物(如 .pb.go),共用门禁的 3 次注入硬上限。

judge 永不误拦:模型乱报 / JSON 脏 / 超时一律视为「非权威」放行;门禁里 judge 安排在所有便宜检查器之后、且仅当 block 检查器全绿时才跑(省一次模型调用,也不在客观失败上堆主观意见)。few-shot 示例 + 当轮诊断背景注入到 judge prompt,判断更准。

为什么是独立模型调用:judge 在全新上下文里评审,不带 agent 自己的对话历史,消除「自己查自己」的自我合理化偏见 — 这正是 LLM-as-judge 区别于「让 agent 自我反思」的核心。

省开销:仅在本轮真正改过文件后才跑;没有编辑的轮次整组跳过。 结构化展示:失败在前端用结构化面板展示,不是一坨裸 JSON。 不会死循环:同会话连续注入硬上限(默认 3 次)— 撞到即停门禁、交回常规流程,避免 flaky / 不可修复的检查把 AI 困死;真正的用户介入立即清零。

10.11.2 「继承 + 覆盖」三级开关

来源 作用
全局默认 设置 → 代码测试与自检 tab(落到 ~/.config/avlcode/checkgate-default.yaml 没配工作区时的默认检查集
工作区默认 工作区 .avlcode/testgate.yaml(侧栏 / Header 工作区菜单一键开关 + Strip 全参数编辑器) 新会话默认值;按当前工作模式(plan / prepare / execute / assess)还可单独覆盖
会话覆盖 会话菜单的检查项勾选清单 单个会话可逐条勾选哪些检查器启用,工作区开了仍能把某条单独关掉,反之亦然

会话菜单和工作区菜单显示的都是实际生效状态,所见即所得。

10.11.3 CheckGateStrip — 输入框上方的全参数编辑器

工作区开启自检门禁后,输入框上方会出现一条 CheckGateStrip

  • 一行 chip 显示每个检查器的当前生效态(mode / submode / scope)。
  • 点 chip 展开全参数编辑器,可改 mode / submode / scope / path / linters / config / timeout,所改即时写入工作区配置。
  • 关掉门禁后 Strip 隐藏,不挡视线。

10.11.4 工具安装状态

设置 → 代码测试与自检 页底部展示外部工具的安装状态(主进程 PATH 可见性):

类别 工具 缺失时的安装指引
test go / pytest / npm / cargo 装相应工具链
lint golangci-lint / eslint / ruff go install … / npm i … / pip install ruff
sast semgrep pip install semgrepbrew install semgrep

macOS GUI 进程的 PATH 可能不含 shell(.zshrc)里 pip / npm 装的路径,这里是主进程可见性的尽力探测;真正运行仍由工作区 interpreter 在工作区上下文跑,可能成功 — 不必拘泥于此处的 ✗。

10.11.5 lint.run 与 sast.run — 也可单独被 AI 调用

lint.runsast.run 不只是自检门禁的后端,AI 也能在 plan / assess 等只读模式下直接调用做静态审计 — 它们是只读分析(不改源、不执行项目代码),不在 read-only 拒绝列表里。

lint.run

  • 自动识别 lintergo.mod → golangci-lint / .eslintrc·eslint.config → eslint / pyproject.toml·ruff.toml → ruff;也可 linter / command 手动覆盖。
  • 结构化发现findings[](每条 {file, line, rule, severity, message}),与 test.run 同形。
  • submodestrict(如 eslint 警告也算失败)/ lenient

sast.run

  • 后端:semgrep(--config auto 默认,需联网拉规则;离线请用 config 指定本地 .semgrep.yml)。
  • 结构化发现findings[] 同形。
  • submodequick(限大文件加速)/ deep(完整)。

10.12 工具自省 tools.list / tools.describe / tools.load

AI 可以枚举自己当前可用的工具及其用法,减少「不知道有没有某个能力」的猜测:

工具 用途
tools.list 列本会话可调用的工具(名 + 描述);prefix 可按命名空间过滤(如 fs / code / test
tools.describe 返回指定工具的完整 schema(描述 + 参数定义)
tools.load 按分类加载工具;不带参数时返回分类目录,每类附一句话能力说明与工具名清单

列出的结果会按本会话实际可用的范围裁剪(受工具三态与分类延迟加载影响),并提示"当前表里看不见并不代表不存在,可以先加载对应分类"。