news 2026/9/11 6:20:25

Void 代码库完全指南:AI 编辑器架构、双进程模型、LLM 消息管线与 Apply 机制深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Void 代码库完全指南:AI 编辑器架构、双进程模型、LLM 消息管线与 Apply 机制深度解析

Void 代码库完全指南:AI 编辑器架构、双进程模型、LLM 消息管线与 Apply 机制深度解析

【免费下载链接】void开源AI代码编辑器,Cursor的替代方案。项目地址: https://gitcode.com/GitHub_Trending/void2/void

Void 是一款基于 VSCode 二次开发的开源 AI 代码编辑器,其核心业务代码几乎全部集中在src/vs/workbench/contrib/void/目录下。本指南以官方《Void Codebase Guide》为骨架,结合当前仓库源码逐层拆解 Void 的代码组织方式、Electron 双进程模型、Service/Action 架构、LLM 内部消息管线,以及 Fast Apply / Slow Apply 的完整实现原理,帮助你快速具备在 Void 代码库中定位与开发的能力。

Void 代码在哪里:目录速览

Void 的代码库并不可怕,绝大多数 Void 自己的代码都存放在src/vs/workbench/contrib/void/这一目录下。该目录下继续分为三个子目录,对应三种运行环境:

  • browser/:运行在渲染进程(浏览器进程)中的代码,例如 editCodeService.ts、void.contribution.ts、autocompleteService.ts 以及大量 React 界面组件(browser/react/src/下的 sidebar、quick-edit、diff、settings 等)。
  • common/:可被任一进程使用的共享代码,例如 voidSettingsService.ts、voidModelService.ts、modelCapabilities.ts、sendLLMMessageService.ts、mcpService.ts 等类型定义与服务。
  • electron-main/:运行在主进程中的代码,例如 sendLLMMessageChannel.ts、llmMessage/sendLLMMessage.ts 等。

如需构建、运行等工程化指令,请参考 HOW_TO_CONTRIBUTE.md(本文聚焦代码库本身的架构讲解)。

VSCode 运行机制速览:Electron 双进程模型

Void 继承自 VSCode,本质上是一个 Electron 应用。理解 Electron 的双进程模型是进入 Void 代码库的第一道门槛:

  • 主进程(main process):负责应用内部事务,可以自由import node_modules,拥有完整的 Node.js 能力。Void 的electron-main/目录下的代码都运行在这里。
  • 浏览器进程(browser process):这里的 "browser" 指代 HTML 渲染环境,而不特指"网页浏览器"。Void 的界面、编辑器渲染都发生在这个进程。browser/目录下的代码可以使用window等浏览器对象。

由此衍生出三个约定俗成的目录规则:

  1. browser/目录下的代码永远运行在浏览器进程,可以使用window等浏览器对象;
  2. electron-main/目录下的代码永远运行在主进程,可以import node_modules
  3. common/目录下的代码两个进程都可以用,但不获得任何特殊导入能力。

浏览器进程不能 import node_modules:两种绕过方案

浏览器进程被禁止直接import node_modules。Void 为此设计了两套解决方案,二者在仓库中都有实际落点:

  1. 把 node_module 代码打包进浏览器:React 就是这么做的。相关构建配置可参考 void/browser/react/build.js 与 tsup.config.js,React 组件会被打包成浏览器可用的产物(browser/react/out/)。
  2. 在主进程实现逻辑,再通过 channel 建立主进程 ↔ 浏览器进程的通信sendLLMMessage正是如此。浏览器进程侧的 sendLLMMessageService.ts 通过mainProcessService.getChannel('void-channel-llmMessage')拿到 channel,把请求转交给主进程侧的 sendLLMMessageChannel.ts 执行真实网络请求,再通过onTextonFinalMessageonErroronAbort等事件钩子把结果流式回传。

核心术语:Editor、Model、URI 与 Workbench

在 Void/VSCode 代码库中工作时,以下术语会高频出现:

  • Editor(编辑器):你敲代码的那个东西。打开 10 个标签页,其实只是一个editor!Editor 内部包含标签页(即 model)。也就是说,Editor 是"容器",Model 是"内容"。
  • Model(模型):文件内容的内部表示,在多个 editor 之间共享。例如按Cmd+\新建一个分栏 editor 时,A.ts的 model 会被两个 editor 共享——两个 editor、一个 model,这正是改动能够双向同步的原因。
  • URI:每个 model 对应一个 URI(如/Users/.../my_file.txt)。在 VSCode 语境中,URI(也叫 "resource")通常就是一个路径。
  • Workbench(工作台):包裹所有 editor、终端、文件系统树等 UI 的外壳。
  • 类型:通常用ITextModel表示 model,用ICodeEditor表示 editor。在多数场景下,你并不需要关心太多其他类型。

Service:单例注册与依赖注入

VSCode 按 "Service" 组织代码。一个 Service 就是一个只挂载一次(单例)的类,你可以用registerSingleton注册它,之后在任何构造器里用@<Service>装饰器注入使用。

Void 提供了一个完整的示例文件 _dummyContrib.ts,展示了标准的三件套写法,且"每次注册都一样":

// 1. 定义接口 + 装饰器 export interface IDummyService { readonly _serviceBrand: undefined; // services 需要这个,保持 undefined 即可 } export const IDummyService = createDecorator<IDummyService>('DummyService'); // 2. 实现类 class DummyService extends Disposable implements IWorkbenchContribution, IDummyService { static readonly ID = 'workbench.contrib.void.dummy' // workbenchContributions 需要,services 不需要 _serviceBrand: undefined; constructor(@ICodeEditorService codeEditorService: ICodeEditorService) { super(); } } // 3. 注册(二选一) registerSingleton(IDummyService, DummyService, InstantiationType.Eager); registerWorkbenchContribution2(DummyService.ID, DummyService, WorkbenchPhase.BlockRestore);

Void 的核心服务几乎都遵循同样的模式,例如voidSettingsService(voidSettingsService.ts)以registerSingleton(IVoidSettingsService, VoidSettingsService, InstantiationType.Eager)注册,voidModelService(voidModelService.ts)亦然。

Action / Command:可被用户与代码共同调用的函数

"Actions" 是你注册到 VSCode 上的函数,之后你或用户都可以调用它,它们也叫 "Commands"。两种触发方式:

  • 用户触发:按Cmd+Shift+P打开命令面板(command palette)执行;
  • 代码触发:通过commandService按 ID 调用。

Void 用 Action 注册了Cmd+LCmd+K等按键监听。Action 最大的好处是:用户可以自由修改键位绑定。参考_dummyContrib.tsregisterAction2的写法:

registerAction2(class extends Action2 { constructor() { super({ f1: true, id: 'void.dummy', title: localize2('dummy', 'dummy: Init'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Digit0, weight: KeybindingWeight.VoidExtension, } }); } async run(accessor: ServicesAccessor): Promise<void> { /* ... */ } });

Void 还把所有 Action ID 集中定义在 actionIDs.ts 中(如VOID_ACCEPT_DIFF_ACTION_IDVOID_REJECT_DIFF_ACTION_ID)。

内部 LLM 消息管线(Internal LLM Message Pipeline)

从你在 Void 侧边栏发出第一条消息,到请求真正到达你的模型提供商,中间经过了一整条依赖链。Void 选择在主进程发送 LLM 消息,原因有二:

  1. 规避本地提供商的CSP(内容安全策略)问题;
  2. 可以更方便地使用node_modules中的 SDK 与 HTTP 库。

其核心调用链在仓库中可完整追踪:

  1. 用户在侧边栏 / 快捷编辑界面触发发送;
  2. 浏览器进程侧的 sendLLMMessageService.ts(ILLMMessageService)校验modelSelection是否为空、消息是否为空,然后生成requestIdgenerateUuid()),把消息参数、settingsOfProvidermcpTools一起通过 IPC channelvoid-channel-llmMessage发给主进程;
  3. 主进程侧的 sendLLMMessageChannel.ts 与 llmMessage/sendLLMMessage.ts 真正组装 payload、向模型提供商发起请求;
  4. 流式结果通过onText/onFinalMessage/onError事件回传到浏览器进程的 hooks 表,最终渲染到界面。

值得特别注意的是modelCapabilities(modelCapabilities.ts):这是必须在新模型发布时同步更新的关键文件。它定义了每个模型/提供商的静态能力描述VoidStaticModelInfo,包括:

字段含义
contextWindow输入上下文窗口大小(token 数)
reservedOutputTokenSpace为输出预留的 token 空间,为null时默认 4096
supportsSystemMessage系统消息支持方式:false/'system-role'/'developer-role'/'separated'(separated 表示以独立字段传递,如 Anthropic)
specialToolFormat工具调用格式:'openai-style'/'anthropic-style'/'gemini-style'
supportsFIM是否支持 FIM(fill-in-middle)格式,即是否可用于自动补全
reasoningCapabilities推理能力描述:是否支持推理、能否关闭、能否输出思考内容、预算/努力程度滑块、开源模型的 think 标签等
additionalOpenAIPayload追加到 OpenAI 兼容请求体中的额外字段
cost/downloadable每百万 token 价格信息与模型是否可下载(仅信息展示,不参与请求组装)

该文件还实现了modelOptionsFallback兜底逻辑:对于未能精确匹配的模型名,通过字符串包含规则(如lower.includes('claude')lower.includes('deepseek')lower.includes('qwen3')等)回退到最接近的已知模型能力配置,确保任何未知模型也能以合理默认值工作。

Apply 机制:Fast Apply 与 Slow Apply

Void 有两种 Apply:Fast Apply(基于 Search/Replace)与Slow Apply(重写整个文件)。

当用户点击 Apply 且启用了 Fast Apply 时,Void 会提示 LLM 输出如下格式的 Search/Replace 块:

<<<<<<< ORIGINAL // 原始代码 ======= // 替换后的代码 >>>>>>> UPDATED

这就是 Void 能在上千行的文件上快速应用改动的原因——它本质上等同于让 LLM 替用户按 Ctrl+F 做一次"查找并替换"查询。相关的提示词模板可以在 prompts.ts 中看到(searchReplaceGivenDescription_systemMessage/searchReplaceGivenDescription_userMessage),而 Search/Replace 块的解析逻辑位于 extractCodeFromResult.ts(extractSearchReplaceBlocksExtractedSearchReplaceBlock)。

Apply 内部实现:DiffZone、DiffArea 与流式 Diff

editCodeService(editCodeService.ts)是 Apply 的执行者。同一份代码同时服务于三条路径:LLM 调用 Edit 工具、用户点击 Apply、以及用户提交Cmd+K——三者只是 Fast/Slow Apply 模式不同。

关键术语

  • DiffZone:一个{startLine, endLine}的文本区域,Void 在其中计算并展示红色/绿色的Diff。当文件发生任何改动时,Void 会遍历该文件上的所有 DiffArea 并刷新其 Diff。
  • DiffArea:更一般化的抽象,只追踪行号,像 DiffZone 一样。
  • 唯一支持"流式"的 DiffArea 是 DiffZone:每个正在流式的 DiffZone 都持有一个llmCancelToken

这些数据结构在 editCodeServiceTypes.ts 中有完整定义:DiffZone_streamStateisStreamingstreamRequestIdRef、当前流式行号line)与_diffOfId映射;DiffComputedDiff派生,分为edit/insertion/deletion三种类型;还有CtrlKZone(承载 Cmd+K 输入框)、TrackingZone(携带任意元数据)、VoidFileSnapshot(用于快照审批状态)等。

Apply 的工作流程

  1. 点击 Apply 时:Void 在整个文件上创建一个DiffZone,这样 LLM 产生的任何改动都会以红/绿 diff 显示出来,随后开始流式传输改动;
  2. LLM 调用 Edit 时:本质上是调用了 Apply;
  3. 提交 Cmd+K 时:与 Apply 相同,只是创建一个更小的 DiffZone(而非整个文件)。

Diff 计算与定位的核心辅助函数在 helpers/findDiffs.ts,负责把流式文本与文件内容对比、找出精确的 diff 行区间。editCodeService.ts中还包含了 Search/Replace 块的错误反馈逻辑:当ORIGINAL块在文件中找不到完全匹配、出现多次匹配、或与另一个ORIGINAL块重叠时,会生成人类可读的错误信息回传给 LLM 并中止本次 Apply,保证快速应用不会误改代码。

写文件的内部机制:voidModelService

当 Void 要修改你的代码时,它只是往一个文本 model 里写入内容。这意味着:要向文件写入内容,你只需要知道它的 URI,而不需要手动 load、save 等。为了让这套机制正常工作,背后有一些烦人的 URI/model 生命周期问题,Void 全部封装在了voidModelService(voidModelService.ts)中:

  • initializeModel(uri):通过ITextModelService.createModelReference(uri)创建 model 引用,并保存在_modelRefOfURI[uri.fsPath]中持有强引用,防止被垃圾回收;
  • getModel(uri)/getModelFromFsPath(fsPath):同步取回{ model, editorModel }
  • getModelSafe(uri):若 model 尚未初始化则先初始化再返回;
  • saveModel(uri):调用ITextFileService.save并设置skipSaveParticipants: true,避免触发扩展的格式化参与器额外改动文件(否则撤销栈里会多出无关条目)。注释中明确说明这样可以让"我们的改动 → 保存"被当作同一次变更处理。

Void 设置内部机制:voidSettingsService

Void 有一个voidSettingsService(voidSettingsService.ts)存储所有 Void 设置——提供商、模型、全局 Void 设置等。可以把它想象成任何核心 Void 服务的隐式依赖:所有核心服务都直接或间接读取它。其状态结构VoidSettingsState包含:

  • settingsOfProvider:每个提供商的设置(API Key、endpoint、模型列表等);
  • modelSelectionOfFeature:每个功能当前选择的模型;
  • optionsOfModelSelection:每个功能/提供商/模型的选项(如推理预算);
  • overridesOfModel:用户对模型能力的覆盖;
  • globalSettings:全局设置;
  • mcpUserStateOfName:MCP 服务器的用户开关状态;
  • _modelOptions:由以上数据计算出的可选模型列表。

设置术语表

  • FeatureNameChat|Ctrl+K|Autocomplete|Apply|SCM(源码 voidSettingsTypes.ts 中featureNames定义,含"提交信息生成器"SCM 功能);
  • ModelSelection:一个{providerName, modelName}对;
  • ProviderName:提供商名称,如'ollama''openAI''anthropic''gemini''deepseek''openRouter''groq''xAI''mistral''vLLM''lmStudio''liteLLM''openAICompatible''googleVertex''microsoftAzure''awsBedrock'
  • ModelName:模型名字符串,如'gpt-4o''claude-3-7-sonnet-latest'
  • RefreshProvider:会被反复 ping 以更新模型列表的提供商(本地提供商ollamavLLMlmStudio支持自动探测,setAutodetectedModels负责把探测结果合并进模型列表);
  • ChatModenormal|gather|agent(源码中默认值为'agent')。

持久化:加密存储与状态迁移

voidSettingsService的存储实现非常值得借鉴:所有状态通过IEncryptionService加密后,以VOID_SETTINGS_STORAGE_KEY(见 storageKeys.ts)写入IStorageServiceStorageScope.APPLICATIONStorageTarget.USER)。每次状态变更都会经过_validatedModelState校验——自动重算_didFillInProviderSettings、重建可选项列表、并在用户所选模型失效时自动回退到列表第一项或null。代码中还包含多条版本迁移逻辑(如 1.0.3 增加includeToolLintErrors、1.2.5 将autoApprove从布尔改为对象、1.3.5 增加 SCM 功能等),保证旧版本用户升级后状态结构自动对齐。

全局设置默认值

源码defaultGlobalSettings给出的默认值如下:

设置项默认值含义
enableFastApplytrue启用 Fast Apply(Search/Replace)
chatMode'agent'对话模式(normal / gather / agent)
enableAutocompletefalse是否启用自动补全
autoRefreshModelstrue自动刷新模型列表
syncApplyToChattrueApply 跟随 Chat 的模型选择
syncSCMToChattrueSCM 跟随 Chat 的模型选择
showInlineSuggestionstrue显示内联建议
includeToolLintErrorstrue工具 lint 错误是否纳入上下文
disableSystemMessagefalse是否禁用系统消息
autoAcceptLLMChangesfalse是否自动接受 LLM 的改动
autoApprove{}按工具类型配置的自动审批
aiInstructions''自定义 AI 指令
isOnboardingCompletefalse新手引导是否完成

提供商默认端点

modelCapabilities.ts中的defaultProviderSettings给出了各提供商的默认配置(本地服务端点,便于开箱即用):

  • Ollama:http://127.0.0.1:11434
  • vLLM:http://localhost:8000
  • LM Studio:http://localhost:1234
  • LiteLLM:http://localhost:4000
  • OpenAI-Compatible:endpoint为空,需自行填写 baseURL(如https://my-website.com/v1),支持headersJSON自定义请求头
  • Google Vertex:默认region: 'us-west2'
  • Microsoft Azure:默认azureApiVersion: '2024-05-01-preview'
  • AWS Bedrock:默认region: 'us-east-1'

审批状态(Approval State)

editCodeService的数据结构中包含了用户需要审查的全部改动信息,但这些信息不是以方便消费的格式存放的。因此 Void 专门编写了一个派生服务,把这些原始数据转换为更实用的"审批状态",供界面(如 diff 上的接受/拒绝按钮)直接消费。相关动作 ID 定义在 actionIDs.ts(VOID_ACCEPT_DIFF_ACTION_IDVOID_REJECT_DIFF_ACTION_ID),快照结构VoidFileSnapshotDiffAreaSnapshotEntry则定义在 editCodeServiceTypes.ts——快照只保留diffAreaSnapshotKeys中列出的关键字段(type、diffareaid、originalCode、startLine、endLine、editorId),用于在流式过程中记录"改动前的文件状态"。

构建流程

关于 Void 的构建管线,仓库内注释说明其由独立的构建仓库(void-builder)负责,Void 主仓库专注于源码。这意味着发布流程、打包、签名、自动更新等工程化环节与主仓库解耦,主仓库的 CI 相关配置可参考根目录的 CodeQL.yml 与 gulpfile.js。

VSCode 代码库学习路线

Void 团队为想要深入 VSCode 生态的开发者整理了一份分级参考清单(原文为外部链接,此处转为可操作的知识点摘要):

  • 入门必读:VSCode 用户界面指南(auxbar、面板等)与 UX 设计指南(Container、View、Item 等抽象)。
  • 贡献者必读:VSCode 源码组织方式——这是整份清单中最重要的一篇,解释了入口文件在哪里、browser/common/的含义等;VSCode 内置样式变量,即var(--vscode-{theme 名,点替换为横线})形式的 CSS 变量,以及 Webview 主题化指南。
  • 杂项:VSCode 内置命令全集(不常用,仅作参考)。值得注意的是:VSCode 的仓库就是 Monaco 编辑器的源码来源——一个 "editor" 就是一个 Monaco 编辑器,它共享ITextModel等代码。
  • 扩展 API(历史遗留):Void 已经不再是一个扩展,因此这些链接不再是必需,但如果未来重新构建扩展可能会用到——包括扩展所需文件清单、扩展package.json的 schema、"contributes"挂载机制、完整扩展 API(重点看页面底部的 cancellation tokens、events、disposables 模式)、以及package.json中的 activation events。

结语

从整体看,Void 的架构哲学非常清晰:主进程负责重活(发请求、操作文件系统),浏览器进程负责 UI 与状态,common/承载共享逻辑,Service 单例负责全局状态,Action 负责可重绑定的命令入口。无论你是想为 Void 新增一个提供商、调试 Apply 的 diff 逻辑,还是理解它如何管理模型能力,都可以从 void/ 目录出发,沿着voidSettingsService(配置)→sendLLMMessageService(请求管线)→editCodeService(代码修改)这条主线快速上手。

【免费下载链接】void开源AI代码编辑器,Cursor的替代方案。项目地址: https://gitcode.com/GitHub_Trending/void2/void

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 6:19:21

Upscayl免费图像放大:3步上手的完整指南

Upscayl免费图像放大&#xff1a;3步上手的完整指南 【免费下载链接】upscayl &#x1f199; Upscayl - #1 Free and Open Source AI Image Upscaler for Linux, MacOS and Windows. 项目地址: https://gitcode.com/GitHub_Trending/up/upscayl Upscayl是一款免费开源的…

作者头像 李华
网站建设 2026/9/11 6:19:14

LLM开发实战:环境变量、提示工程与RAG数据流调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 6:18:14

基于Spark与LSTM的地铁客流量预测系统设计与实现

1. 项目背景与核心价值 地铁客流量预测是城市智慧交通建设中的关键环节。随着城市化进程加速&#xff0c;早晚高峰期的地铁拥挤问题日益突出。传统基于人工统计和经验模型的方法已经难以应对复杂多变的客流变化&#xff0c;而大数据和机器学习技术为解决这一难题提供了全新思路…

作者头像 李华