跳到主要内容

CodeCompanion.nvim — 架构与原理

30 秒导读: CodeCompanion 是一个 Neovim 插件,让你不离开编辑器就能和 LLM 聊天、让 AI agent 改你的代码。它的独特之处是:聊天窗口就是一个普通的 Neovim buffer(你能像编辑任何文件一样编辑对话),而「让 AI 改文件」这件最难的事,靠一套 7 级降级的模糊匹配把 LLM 说的旧代码精确定位到真实文件上。

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

一句话定义: CodeCompanion 是装在 Neovim 里的「AI 编程助手 + agent 宿主」——它既能像 ChatGPT 那样陪你聊代码,也能让 LLM 真正动手改你磁盘上的文件、跑命令。

解决什么问题 / 给谁用: 假设你在 Neovim 里写代码,想问「这个函数为什么报错」或者「帮我把这个模块重构成异步的」。你不想切到浏览器、不想复制粘贴。CodeCompanion 让你在编辑器内:选中代码 → 一个命令丢给 AI → AI 回话,甚至直接帮你把改动应用进文件,你审一眼 diff 再决定接不接受。

它能做什么(功能):

  • 聊天缓冲区(Chat): 一个可编辑的对话窗口,和 Anthropic / OpenAI / Gemini / Copilot / Ollama 等 20+ 家模型对话。
  • 内联改写(Inline): 不开聊天窗,直接对当前 buffer 下指令,AI 把代码写进光标处。
  • Agent / 工具(Tools): 给 LLM 一套工具(读文件、改文件、跑命令、搜代码),它能多轮自主地完成一个任务。
  • ACP agent: 直接把 Claude Code、Codex 这类 CLI agent 当外部进程接进来对话。
  • 斜杠命令 / 编辑器上下文:/buffer/file#diagnostics 等把当前编辑环境喂给 LLM。

用起来什么样: 一个最小例子——选中一段代码,执行:

" 把选中的代码加进聊天窗,然后问问题
:'<,'>CodeCompanionChat
" 或者直接内联改写当前 buffer
:CodeCompanion 把这个循环改成 vim.iter

这两个命令最终都落到 lua/codecompanion/init.lua 里的 CodeCompanion.chat(init.lua:119)和 CodeCompanion.inline(init.lua:39)。

一句话直觉/类比: 把聊天窗口想成一个「活的 Markdown 文件」——你和 LLM 的每一句话都是这个文件里的一段文本,带 ## Me / ## CodeCompanion 这样的标题。插件做的事,本质上是:解析这个 buffer 的文本 → 发给 LLM → 把流式回话再写回这个 buffer。工具调用、上下文、审批,都是围绕这个 buffer 转的边角料。

本节不碰代码细节。记住一句:「对话 = 一个 buffer」是理解整个项目的钥匙。

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

CodeCompanion 有三种「交互方式」(interactions),但它们共用同一套底层:适配器(抹平各家 LLM)+ 工具系统(让 LLM 动手)。

用户在 Neovim 里 把各家 LLM/agent
┌──────────────┐ 抹平成一个接口
│ :CodeCompanion│ ┌──────────────────┐
│ Chat / 命令 │ │ Adapters │
└──────┬───────┘ │ http/ (Anthropic│
│ │ OpenAI…) │
▼ │ acp/ (ClaudeCode│
┌──────────────────────┐ payload │ Codex…) │
│ Interactions(交互) │ ───────────▶ └─────────┬────────┘
│ · chat (聊天 buffer)│ │ 流式 chunk
│ · inline (内联改写) │ ◀──────────────────────┘
│ · cmd / cli │ parse_chat 解析回来
└──────┬───────────────┘
│ LLM 要调工具时

┌──────────────────────┐
│ Tools(工具系统) │ 队列 + Runner 状态机
│ · orchestrator 排队 │ 逐个执行,可插入审批
│ · builtin/ 各工具 │ read_file / run_command
│ insert_edit_into_file ← 改文件靠 7 级模糊匹配
└──────────────────────┘

各部件一句话职责:

部件干什么在哪个文件
入口 API对外暴露 chat/inline/cmd/cli,被 user command 调用lua/codecompanion/init.lua
Chat聊天缓冲区;解析 buffer → 发请求 → 把回话写回lua/codecompanion/interactions/chat/init.lua
Inline内联改写;让 LLM 返回 JSON,把代码落到光标处lua/codecompanion/interactions/inline/init.lua
HTTP Client用 plenary.curl 发流式/同步请求lua/codecompanion/http.lua
Adapters每家 LLM 一张 handler 表,把请求/响应格式抹平lua/codecompanion/adapters/
Tools工具协调器:解析、排队、串行执行 LLM 的工具调用lua/codecompanion/interactions/chat/tools/
ToolRegistry把工具的 schema 注入发给 LLM 的 payloadlua/codecompanion/interactions/chat/tool_registry.lua
ACP把外部 CLI agent 当子进程,用 JSON-RPC 对话lua/codecompanion/acp/
Config一张巨表:工具组、适配器默认值、所有设置lua/codecompanion/config.lua

主线走一遍(高层,不进代码):

以「在聊天窗里让 agent 改一个文件」为例:

  1. 你在聊天 buffer 里打字,按回车 → Chat:submit(chat/init.lua:1287)解析 buffer 文本、拼出 messages + 工具 schemas,发给 HTTP client。
  2. LLM 流式回话,每个 chunk 经适配器 parse_chat 解析,文本实时写回 buffer;若 LLM 想调工具,chunk 里带 tool_calls
  3. 请求结束 → Chat:done(chat/init.lua:1421)。若有工具调用,交给 Tools:execute(tools/init.lua:265)。
  4. 工具系统把每个调用推进一个队列,Orchestrator 逐个 setup_next_tool(orchestrator.lua:258):需要审批的先弹审批框,通过后 Runner 真正跑工具函数。
  5. insert_edit_into_file 工具用 7 级降级匹配把 LLM 给的 oldText 在文件里定位,替换成 newText,弹 diff 让你确认。
  6. 工具输出被 add_tool_output(chat/init.lua:1824)写回消息历史,自动再 submit 一轮,让 LLM 看到工具结果继续推进——直到任务完成。

3. 怎么读这套文档(阅读地图)

这是个大项目,文档拆成 5 章,建议按顺序读:

  1. 01-chat-loop — 先搞懂主循环。submit → 流式 → done 是一切的骨架,工具/上下文都挂在它上面。
  2. 02-tools — 工具系统。LLM 怎么「请求」工具、队列+Runner 怎么串行跑、审批怎么插进来。
  3. 03-edit-application — 全库精华。把 LLM 给的旧代码精确落到真实文件的 7 级降级匹配,值得任何写编码 agent 的人借鉴。
  4. 04-adapters — 适配器模式。一张 handler 表怎么把 20+ 家 LLM 抹成一个接口。
  5. 05-acp — ACP。完全不同的另一条路:不发 HTTP,而是把 Claude Code 当子进程对话。

4. 代码地图(导航索引)

主题文件符号
对外入口lua/codecompanion/init.luaCodeCompanion.chatCodeCompanion.inlineCodeCompanion.setup
聊天主循环lua/codecompanion/interactions/chat/init.luaChat:submitChat:doneChat:_submit_http
HTTP 客户端lua/codecompanion/http.luaClient:sendClient:request
工具协调lua/codecompanion/interactions/chat/tools/init.luaTools:executeTools.resolve
工具状态机lua/codecompanion/interactions/chat/tools/orchestrator.luaOrchestrator:setup_next_toolOrchestrator:execute_tool
模糊改文件…/tools/builtin/insert_edit_into_file/strategies.luaM.find_best_matchM.select_best_match
适配器工厂lua/codecompanion/adapters/init.luaM.resolveM.call_handler
ACP 连接lua/codecompanion/acp/init.luaConnection.newConnection:connect_and_authenticate