第十节 智能编程工具
下列工具默认在工作区目录范围内可用,互相协同支撑助手在你的代码仓库里"干活"。
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/.venv、conda(含 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.search传expand=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);也可手动指定framework或command完全覆盖。 - 结构化失败:跑完把输出解析成
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」,挑机检抓不到的逻辑 / 语义 / 安全意图错 |
每个检查器都可调:
- mode:
block(默认,注入失败强制修) /report(只通报不拦) - submode:
test:changed(默认,只测改动的)/full(全跑)lint:strict(警告也算红)/lenientsast:quick(限大文件加速)/deep(完整)
- scope:
changed(默认,本轮改动文件)/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 semgrep 或 brew install semgrep |
macOS GUI 进程的 PATH 可能不含 shell(
.zshrc)里 pip / npm 装的路径,这里是主进程可见性的尽力探测;真正运行仍由工作区 interpreter 在工作区上下文跑,可能成功 — 不必拘泥于此处的 ✗。
10.11.5 lint.run 与 sast.run — 也可单独被 AI 调用
lint.run 与 sast.run 不只是自检门禁的后端,AI 也能在 plan / assess 等只读模式下直接调用做静态审计 — 它们是只读分析(不改源、不执行项目代码),不在 read-only 拒绝列表里。
lint.run:
- 自动识别 linter:
go.mod→ golangci-lint /.eslintrc·eslint.config→ eslint /pyproject.toml·ruff.toml→ ruff;也可linter/command手动覆盖。 - 结构化发现:
findings[](每条{file, line, rule, severity, message}),与test.run同形。 - submode:
strict(如 eslint 警告也算失败)/lenient。
sast.run:
- 后端:semgrep(
--config auto默认,需联网拉规则;离线请用config指定本地.semgrep.yml)。 - 结构化发现:
findings[]同形。 - submode:
quick(限大文件加速)/deep(完整)。
10.12 工具自省 tools.list / tools.describe / tools.load
AI 可以枚举自己当前可用的工具及其用法,减少「不知道有没有某个能力」的猜测:
| 工具 | 用途 |
|---|---|
tools.list |
列本会话可调用的工具(名 + 描述);prefix 可按命名空间过滤(如 fs / code / test) |
tools.describe |
返回指定工具的完整 schema(描述 + 参数定义) |
tools.load |
按分类加载工具;不带参数时返回分类目录,每类附一句话能力说明与工具名清单 |
列出的结果会按本会话实际可用的范围裁剪(受工具三态与分类延迟加载影响),并提示"当前表里看不见并不代表不存在,可以先加载对应分类"。
