跳到主要内容

avante.nvim — 架构与原理

30 秒导读: avante.nvim 是一个 Neovim 插件,把 Cursor 那种「在编辑器里跟 AI 聊、AI 直接改你的文件、你一键接受/拒绝」的体验搬进了 Vim。它在 Neovim 里跑一个完整的 agentic 循环(模型边想边调工具),并把 AI 给的改动用「带高亮的 diff」直接铺在你的真实 buffer 上让你过目。

1. 这是什么(零基础也能懂)

一句话定义: avante.nvim = Neovim 里的「AI 结对编程 + 一键应用补丁」插件,行为对标 Cursor IDE。

解决什么问题 / 给谁用:

假设你在 Neovim 里写一个大项目,想让 AI 帮你「把这个函数加上错误处理」「按这个需求重构这几个文件」。

  • 普通做法:打开 ChatGPT 网页 → 复制代码过去 → 让它改 → 把结果再贴回来 → 手动对齐。来回切窗口、手动粘贴,很碎。
  • avante 做法:在 Neovim 侧边栏里直接说需求,AI 自己读文件、自己想步骤、自己调「编辑工具」,改动以 diff 形式直接出现在你的代码 buffer 上,你按一个键接受或拒绝。全程不离开 Neovim。

给谁用:重度 Vim/Neovim 用户——既想要 Cursor 的 AI 能力,又不想离开自己几十个插件和肌肉记忆的终端编辑器。

它能做什么(功能):

  • 多轮对话式问答(:AvanteAsk / :AvanteChat),AI 能看你当前文件、选中区、整个项目。
  • Agentic 模式:AI 自主调用工具(读文件、grep、跑 bash、改文件……)一步步完成任务,而不只是吐一段建议。
  • 一键应用 + 逐块审阅:AI 的改动以「绿色新增 / 删除虚行」高亮叠在真实 buffer 上,光标移到某块时显示 OURS / THEIRS / PREV / NEXT 快捷键。
  • 多模型供应商(Claude / OpenAI / Gemini / Copilot / Bedrock / Ollama …)。
  • ACP:还能直接外接 claude-code / gemini-cli / codex 等现成 Agent CLI 当后端(见第 5 章)。
  • Zen Mode:把整个 Neovim 伪装成一个「Vibe Coding CLI」,但底层全是 Neovim。

用起来什么样: 最小使用——给命令起个别名,进 Neovim 就直接是 avante 的 Zen 界面(摘自 README.md):

# 像用 claude code 一样:终端里敲 avante 就进入 Avante Zen Mode
alias avante='nvim -c "lua vim.defer_fn(function()require(\"avante.api\").zen_mode()end, 100)"'

或在已有 Neovim 里:选中几行代码 → 按 <leader>ae(AvanteEdit)→ 输入「加上输入校验」→ AI 改 → 你按 co(theirs)接受。

一句话直觉/类比: 把 avante 想成「Cursor 的 agent 大脑 + 一个住在你 Neovim buffer 里的三方合并工具(merge tool)」。大脑负责想和调工具,merge 工具负责把改动安全地、可逐块撤销地落到你真实文件上。

2. 顶层全景(它大概怎么转)

一次「你提问 → AI 改完代码」大致是这样流动的(详见 01-architecture.md):

你在侧边栏输入需求


┌─────────────┐ 组装 system prompt(Jinja 模板)+ 历史消息 + 工具清单
│ sidebar.lua │──────────────────────────────────────────────┐
│ handle_submit│ │
└─────────────┘ ▼
│ ┌──────────────────┐
│ 回调(on_chunk/on_messages_add/on_stop) │ llm.lua │
│◀─────────────────────────────────────────────│ M.stream/_stream │
▼ │ (agentic 循环) │
┌─────────────┐ └──────────────────┘
│ 渲染:聊天流 │ │ ▲
│ + buffer 上 │ ▼ │ reason==tool_use → 再跑一轮
│ 的 diff 高亮 │ ┌────────────────────┐│
└─────────────┘ │ providers/*.lua ││
▲ │ 拼 HTTP / 解析 SSE ││
│ 应用改动落盘 └────────────────────┘│
┌─────────────────────┐ │ │
│ llm_tools/*.lua │◀────────────────────────────────┘ │
│ 执行工具(读/grep/ │ 工具结果作为 tool_result 喂回 ────────────┘
│ bash/改文件…) │
└─────────────────────┘
│ 改文件时

┌─────────────────────────────┐ ┌───────────────────────────┐
│ replace_in_file.lua │ 调用 │ Rust crates(可选加速) │
│ 五级模糊匹配 + 流式 diff 预览 │ │ repo-map / templates / │
└─────────────────────────────┘ │ tokenizers / html2md │
└───────────────────────────┘

部件一句话职责:

部件干什么在哪个文件
入口 / 命令注册 :AvanteAsk 等命令与 keymap、zen_modelua/avante/init.lualua/avante/api.lua
侧边栏 UI聊天窗、输入框、选中代码容器、渲染消息流、提交请求lua/avante/sidebar.lua
Agentic 大脑核心:递归的「调模型 → 跑工具 → 再调模型」循环lua/avante/llm.lua(M._stream)
工具系统工具注册表 + 分发执行 + 权限确认lua/avante/llm_tools/(init.luahelpers.lua)
改代码引擎SEARCH/REPLACE 块 → 模糊定位 → buffer 上铺 diff → 接受落盘lua/avante/llm_tools/replace_in_file.lualua/avante/utils/init.lua(fuzzy_match)
供应商拼每家 API 的请求体、解析流式响应、归一成工具调用lua/avante/providers/*.lua
Prompt 模板Jinja 模板渲染 system prompt(随模式/模型变化)lua/avante/templates/*.avanterules + crates/avante-templates
Rust 加速层tree-sitter 抽符号(repo map)、Jinja、tiktoken 分词、HTML→MDcrates/avante-repo-map 等 4 个 crate
ACP 客户端把外部 Agent CLI(claude-code/gemini-cli…)当后端lua/avante/libs/acp_client.lualua/avante/llm.lua(M._stream_acp)

主线走一遍(高层): 你在输入框敲需求并提交 → Sidebar:handle_submit(sidebar.lua:2795)收集选中文件/选区,挂上一堆回调 → 进 M.stream(llm.lua:2143)→ M._stream(llm.lua:1765)用模板生成 prompt 并发 HTTP → provider 解析流式响应 → 若模型决定调工具,_streamon_stop 收到 reason == "tool_use",串行跑工具,把结果作为 tool_result 消息追加,然后递归再调一次模型 → 直到模型调 attempt_completion 或不再调工具 → 期间「改文件」类工具把 diff 铺到你的 buffer,你审阅接受 → 落盘。

3. 阅读地图(建议顺序)

  1. 01-architecture.md — 顶层全景与一次请求的端到端路径。先看这章建立大盘。
  2. 02-agentic-loop.md_stream 的递归循环、pending tools、补救提醒、attempt_completion 收尾。这是项目的心脏。
  3. 03-tools-and-permissions.md — 工具注册表、process_tool_use 分发、权限确认与自动批准、可取消执行。
  4. 04-edit-apply.md — 改代码绝活:SEARCH/REPLACE 协议、fuzzy_match 五级降级、流式 diff 预览、Morph fast-apply。精华最多的一章。
  5. 05-rust-crates-and-acp.md — Rust 加速层细节 + ACP 外接 agent 架构。

4. 巧妙之处(先尝一口,细节在各章)

  • 「先匹配旧文本」而非「按行号 patch」:模型给的旧代码经常和真实文件差几个空格/转义,avante 用 fuzzy_match 五级降级(精确 → 去尾空白 → 去全部空白 → 去转义 → 去转义+空白)来兜底定位,而不是相信行号。见 04 章
  • 参数名故意叫 the_diff 不叫 diff:因为有些模型按字母序流式吐参数,叫 diff 会让 pathdiff 之后才到,破坏「边收边铺 diff」的流式预览。一个被注释明确记录的反直觉决定(replace_in_file.lua:33102)。
  • 改动落在真实 buffer + 真实 undo:不是另开预览窗,而是直接 nvim_buf_set_lines 改你的 buffer、用 extmark 画删除虚行,并 undojoin 让一次 AI 改动成为一步可撤销。见 04 章
  • 递归即循环:整个 agent loop 不是 while,而是 _streamon_stop调用自己——工具跑完就再发一次请求。见 02 章
  • 重活下沉 Rust:符号抽取、Jinja 渲染、分词都做成 mlua 模块,Lua 侧只调函数。见 05 章

5. 横向对比(同 shelf)

avante.nvim 在 coding-agents 区里是少见的「编辑器内嵌(in-editor)」形态,而不是 CLI。它和兄弟项目的核心分歧:

  • 改代码协议:avante 用 SEARCH/REPLACE 块 + 模糊匹配(类似 aider 的 diff 风格),并额外支持 Morph fast-apply 模型(把粗略 edit 交给一个小模型补全)。
  • 应用方式:它的差异化在「把 diff 当三方合并铺进真实 buffer」——这是编辑器内嵌形态独有的体验,CLI agent 做不到。
  • 后端可插拔到极致:既能自己跑循环,又能通过 ACP 把 claude-code / codex 等现成 agent 当后端,等于「我只做 UI + diff 体验,大脑外包」。

各章末尾有更细的对比与代码地图。