Vane 贡献指南:本地开发环境搭建与搜索、Widget、模型 Provider 的源码定位地图
本文为想给 Vane(一款具备高级搜索能力的 AI 对话应用)做代码贡献的开发者而写。它会带你完整走一遍本地开发环境的搭建流程(依赖安装、启动、迁移、UI 配置),并给出一张经过源码逐条核对的"改动定位地图":搜索行为在哪个文件、如何注册新的研究工具、Widget 如何与调研并行执行、新模型 Provider 如何接入注册表。读完后,你能够独立判断一个需求应该改在哪里,并了解每一处机制在源码中的真实实现。
本地开发环境搭建
Vane 采用"UI 内配置"的模式:代码本身不要求预先准备 .env 或 API key,核心步骤只有三步(引自 CONTRIBUTING.md):
- 安装依赖:
npm install - 启动开发服务:
npm run dev - 打开 http://localhost:3000,在界面中完成初始设置——填入模型 API key、选择聊天/嵌入模型、配置搜索后端(SearXNG)URL 等
关于脚本的实际定义,可对照 package.json:dev 对应 next dev,build 对应 next build --webpack,start 对应 next start,另有 lint(next lint)和 format:write(prettier . --write)。
数据库迁移:启动时自动应用
CONTRIBUTING.md 明确说明"Database migrations are applied automatically on startup(数据库迁移在启动时自动应用)"。这一行为在源码中有对应实现:Next.js 的 instrumentation 钩子 src/instrumentation.ts 在 Node.js 运行时执行注册时,先导入 src/lib/db/migrate.ts 执行迁移,再导入配置模块:
export const register = async () => {
if (process.env.NEXT_RUNTIME === 'nodejs') {
try {
console.log('Running database migrations...');
await import('./lib/db/migrate');
console.log('Database migrations completed successfully');
} catch (error) {
console.error('Failed to run database migrations:', error);
}
await import('./lib/config/index');
}
};
因此本地开发不需要手动执行迁移命令。迁移 SQL 脚本本身保存在 drizzle 目录(如 drizzle/0000_fuzzy_randall.sql 等,由 drizzle-kit 管理,元数据见 drizzle/meta/_journal.json),ORM 为 drizzle-orm + better-sqlite3,Schema 定义在 src/lib/db/schema.ts。
开发模式与 Docker 的分工
CONTRIBUTING.md 特别提示:Docker 配置面向生产环境,npm run dev 用于开发。仓库根目录提供了 Dockerfile、Dockerfile.slim、docker-compose.yaml 与 entrypoint.sh,完整的安装选项(Docker / 非 Docker)以仓库 README 中的安装指南为准,docs/installation/UPDATING.md 则覆盖更新场景。
项目结构总览:UI、路由与后端逻辑的分层
CONTRIBUTING.md 对项目结构的描述可以归纳为三层,以下逐条对照源码核实:
UI 层(src/components 与 src/app)
- 可复用组件:
src/components下包含聊天窗口(ChatWindow.tsx)、消息渲染(MessageRenderer/)、设置面板(Settings/)、Widget 渲染(Widgets/,含 Weather、Stock、Calculation 三个组件)等。 - 页面与路由:
src/app采用 Next.js App Router 结构,主路由为首页/(src/app/page.tsx)、聊天/c/[chatId](src/app/c/[chatId]/page.tsx)、发现页/discover(src/app/discover/page.tsx)、库/library(src/app/library/page.tsx),与 CONTRIBUTING.md 列出的路由完全一致。 - API 路由:
src/app/api下的 route handler 包括chat、search、providers、weather、images、videos、discover、uploads、suggestions、reconnect等,分别对应聊天、可编程搜索 API、Provider 管理、Widget 数据源等能力。
后端逻辑层(src/lib)
| 模块 | 路径 | 职责 |
|---|---|---|
| 搜索系统 | src/lib/agents/search |
分类、研究、Widget、写作的完整管线 |
| 数据库 | src/lib/db |
schema、迁移、连接 |
| 模型 Provider | src/lib/models/providers |
各厂商的 LLM/Embedding 实现 |
| 模型注册表 | src/lib/models/registry.ts |
Provider 加载与实例管理 |
| 提示词模板 | src/lib/prompts |
分类、研究、写作等 prompt |
| SearXNG 集成 | src/lib/searxng.ts |
元搜索后端客户端 |
| 上传检索 | src/lib/uploads |
文件解析与语义检索 |
改动定位地图:搜索行为、研究工具与 Widget
CONTRIBUTING.md 的 "Where to make changes" 一节是贡献者最重要的导航。下面结合源码逐条展开。
搜索行为与推理逻辑在哪里
src/lib/agents/search是核心聊天与搜索管线。- 分类器 src/lib/agents/search/classifier.ts 决定"是否需要研究、跑哪些搜索、是否显示 Widget"。它通过 LLM 的结构化输出(
generateObject)返回一个 zod schema,字段包括skipSearch、personalSearch、academicSearch、discussionSearch,以及showWeatherWidget、showStockWidget、showCalculationWidget三个 Widget 开关,外加standaloneFollowUp——一个把追问改写为独立完整问句的字段。 - 研究者(
researcher/)在后台收集信息。src/lib/agents/search/researcher/index.ts 中的Researcher.research是一个带工具调用的迭代循环:它按optimizationMode设置最大迭代次数(speed为 2、balanced为 6、quality为 25),每轮让 LLM 从ActionRegistry获取可用工具并流式产生toolCallChunk,再批量执行工具并把结果作为tool消息回注历史;当模型最后调用done工具或不再产生工具调用时退出循环。调研结束后,搜索结果会按 URL 去重(同 URL 的内容合并),并以sourceblock 的形式推送到会话。
如何添加或修改搜索能力(研究工具)
研究工具(web、academic、social/discussions、uploads、scrape)都位于 src/lib/agents/search/researcher/actions,并在 src/lib/agents/search/researcher/actions/index.ts 中统一注册:
ActionRegistry.register(webSearchAction);
ActionRegistry.register(doneAction);
ActionRegistry.register(planAction);
ActionRegistry.register(scrapeURLAction);
ActionRegistry.register(uploadsSearchAction);
ActionRegistry.register(academicSearchAction);
ActionRegistry.register(socialSearchAction);
添加新工具的路径是:在 actions/(或 actions/search/)下新建工具实现,然后在这里向 ActionRegistry 注册。需要注意工具的实际可用性是动态过滤的——ActionRegistry.getAvailableActionTools 接收 classification、fileIds、mode、sources 等参数(见 researcher/index.ts),也就是说分类器的输出、用户是否上传了文件、当前优化模式和来源开关,都会决定哪些工具暴露给模型。新增工具时建议参考 webSearch.ts 的 getAvailableFor / 描述生成方式来声明自己的可见条件。
如何添加或修改 Widget
Widget 位于 src/lib/agents/search/widgets,注册入口是 src/lib/agents/search/widgets/index.ts:当前注册了 weatherWidget、calculationWidget、stockWidget 三个。执行器 src/lib/agents/search/widgets/executor.ts 的 executeAll 用 Promise.all 并行运行所有 Widget——每个 Widget 先由自身的 shouldExecute(classification) 判断是否需要执行(通常由分类器输出的 showXxxWidget 字段决定),有输出的才会进入结果集。这印证了 CONTRIBUTING.md 中"Widgets run in parallel with research and show structured results in the UI"的说法:Widget 与调研并行,结构化结果直接呈现在界面上(对应 src/components/Widgets/ 下的 Renderer 等组件)。同时依据 docs/architecture/WORKING.md 的说明,Widget 是回答的辅助上下文,但不属于模型应引用的来源。
模型集成:Provider 目录与注册表
CONTRIBUTING.md 指出 Provider 位于 src/lib/models/providers,新增 Provider 后要"接入模型注册表使其在应用中可见"。
src/lib/models/providers/index.ts 维护了类型到 Provider 构造器的映射,当前内置 8 家:
export const providers: Record<string, ProviderConstructor<any>> = {
openai: OpenAIProvider,
ollama: OllamaProvider,
gemini: GeminiProvider,
transformers: TransformersProvider,
groq: GroqProvider,
lemonade: LemonadeProvider,
anthropic: AnthropicProvider,
lmstudio: LMStudioProvider,
};
每个 Provider 目录下包含 LLM 实现(如 openaiLLM.ts)、可选的 Embedding 实现(如 openaiEmbedding.ts)与 index.ts 入口,基类在 src/lib/models/base/。
注册表 src/lib/models/registry.ts 的 ModelRegistry 类是接入的关键:构造时调用 initializeActiveProviders,从配置(getConfiguredModelProviders)读出已配置的 Provider,按 type 在 providers 映射中查构造器并 createProviderInstance 实例化,失败的会打印日志但不阻断启动。运行期的增删改(addProvider / removeProvider / updateProvider / addProviderModel)也都经过这里,并与 configManager(src/lib/config)持久化联动。因此"新增一个 Provider"的完整动作是:在 providers/ 下实现并导出、在 providers/index.ts 的映射表中登记;配置表单字段则由该 Provider 的 getProviderConfigFields() 提供,getModelProvidersUIConfigSection() 会据此生成设置界面的表单。
SearXNG 集成与上传检索
SearXNG。元搜索后端客户端在 src/lib/searxng.ts。searchSearxng(query, opts) 从配置读取后端地址(getSearxngURL),请求 ${searxngURL}/search?format=json,支持 categories、engines、language、pageno 等查询参数(数组参数以逗号连接),并带 10 秒 AbortController 超时、区分超时与其他错误的处理。仓库内置了配套的 SearXNG 服务端配置(searxng/settings.yml、searxng/limiter.toml、searxng/uwsgi.ini),便于自行部署一个可用的搜索后端。可编程搜索 API 的请求与响应格式详见 docs/API/SEARCH.md。
上传检索。src/lib/uploads 负责用户文件的语义检索。src/lib/uploads/manager.ts 的 UploadManager 以嵌入模型为构造依赖,文件存储于 data/uploads,记录写入 uploaded_files.json;支持的 MIME 类型为 PDF、Word(.docx)与纯文本,分别由 pdf-parse、officeparser 等依赖解析,再经 splitText 分块做语义索引。对应的研究工具是 researcher/actions/uploadsSearch.ts。
架构文档与 API 文档入口
CONTRIBUTING.md 指向的两份架构文档是理解全局的最佳起点:
- 高层组件概览:docs/architecture/README.md —— 列出 UI、API 路由(
POST /api/chat、POST /api/search、GET /api/providers)、Agent 编排、搜索后端、LLM、嵌入模型与存储七大组件。 - 高层流程:docs/architecture/WORKING.md —— 描述"分类 → 研究/Widget 并行 → 带引用的写作"三步,以及
optimizationMode(speed/balanced/quality)在速度与质量间的取舍、引用机制、/api/images与/api/videos的图搜/视频搜端点。 - 搜索 API 文档:docs/API/SEARCH.md。
编码与贡献规范
提交改动前的检查清单(引自 CONTRIBUTING.md):
- 充分自测:确保代码功能正确后再提交。
- 统一格式化:提交前始终运行
npm run format:write(即prettier . --write,见 package.json),保持代码风格一致;建议同时跑npm run lint(next lint)。 - 社区互动:项目暂无成文的行为准则(code of conduct),作者表示正在制定中;在此之前请以友好的方式参与社区交流。
小结:从需求到改动路径速查
| 你的改动目标 | 起始位置 |
|---|---|
| 调整搜索行为 / 推理 | src/lib/agents/search/classifier.ts、src/lib/agents/search/researcher/index.ts |
| 新增研究工具 | src/lib/agents/search/researcher/actions,并在 actions/index.ts 注册 |
| 新增/修改 Widget | src/lib/agents/search/widgets,并在 widgets/index.ts 注册 |
| 接入新模型 Provider | src/lib/models/providers + providers/index.ts 映射表,注册表见 src/lib/models/registry.ts |
| 调整提示词 | src/lib/prompts(classifier / researcher / writer 等) |
| 调整搜索后端行为 | src/lib/searxng.ts 与 searxng/ 服务端配置 |
| 数据库表结构 | src/lib/db/schema.ts、drizzle 迁移脚本 |
按这张地图定位、按上述规范提交,你的贡献就能以最小的沟通成本融入 Vane 的代码库。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00