数据截至 (上游 commit 6fb43a175495)
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 的 payload | lua/codecompanion/interactions/chat/tool_registry.lua |
| ACP | 把外部 CLI agent 当子进程,用 JSON-RPC 对话 | lua/codecompanion/acp/ |
| Config | 一张巨表:工具组、适配器默认值、所有设置 | lua/codecompanion/config.lua |
主线走一遍(高层,不进代码):
以「在聊天窗里让 agent 改一个文件」为例:
- 你在聊天 buffer 里打字,按回车 →
Chat:submit(chat/init.lua:1305)解析 buffer 文本、拼出messages+ 工具schemas,发给 HTTP client。 - LLM 流式回话,每个 chunk 经适配器
parse_chat解析,文本实时写回 buffer;若 LLM 想调工具,chunk 里带tool_calls。 - 请求结束 →
Chat:done(chat/init.lua:1441)。若有工具调用,交给Tools:execute(tools/init.lua:265)。 - 工具系统把每个调用推进一个队列,
Orchestrator逐个setup_next_tool(orchestrator.lua:286):需要审批的先弹审批框,通过后Runner真正跑工具函数。 insert_edit_into_file工具用 7 级降级匹配把 LLM 给的oldText在文件里定位,替换成newText,弹 diff 让你确认。- 工具输出被
add_tool_output(chat/init.lua:1852)写回消息历史,自动再submit一轮,让 LLM 看到工具结果继续推进——直到任务完成。
3. 怎么读这套文档(阅读地图)
这是个大项目,文档拆成 5 章,建议按顺序读:
- 01-chat-loop — 先搞懂主循环。
submit → 流式 → done是一切的骨架,工具/上下文都挂在它上面。 - 02-tools — 工具系统。LLM 怎么「请求」工具、队列+Runner 怎么串行跑、审批怎么插进来。
- 03-edit-application — 全库精华。把 LLM 给的旧代码精确落到真实文件的 7 级降级匹配,值得任何写编码 agent 的人借鉴。
- 04-adapters — 适配器模式。一张 handler 表 怎么把 20+ 家 LLM 抹成一个接口。
- 05-acp — ACP。完全不同的另一条路:不发 HTTP,而是把 Claude Code 当子进程对话。
4. 代码地图(导航索引)
| 主题 | 文件 | 符号 |
|---|---|---|
| 对外入口 | lua/codecompanion/init.lua | CodeCompanion.chat、CodeCompanion.inline、CodeCompanion.setup |
| 聊天主循环 | lua/codecompanion/interactions/chat/init.lua | Chat:submit、Chat:done、Chat:_submit_http |
| HTTP 客户端 | lua/codecompanion/http.lua | Client:send、Client:request |
| 工具协调 | lua/codecompanion/interactions/chat/tools/init.lua | Tools:execute、Tools.resolve |
| 工具状态机 | lua/codecompanion/interactions/chat/tools/orchestrator.lua | Orchestrator:setup_next_tool、Orchestrator:execute_tool |
| 模糊改文件 | …/tools/builtin/insert_edit_into_file/strategies.lua | M.find_best_match、M.select_best_match |
| 适配器工厂 | lua/codecompanion/adapters/init.lua | M.resolve、M.call_handler |
| ACP 连接 | lua/codecompanion/acp/init.lua | Connection.new、Connection:connect_and_authenticate |