Ch36: AnythingLLM 源码走读——Collector 与适配器
Part 9 · 前置:Ch19 AnythingLLM 精读 · 后续:Ch37 五平台对照实验
结论先行:AnythingLLM 大师读 server/(Express 业务)+ collector/(解析向量化独立进程)+ server/utils/vectorDbProviders/(向量适配器)。代码量小于 WeKnora,适合 一天读完架构——学 减法设计 与 适配器模式。与 WeKnora 对比:AnythingLLM 故意不做 BM25/图谱/Rerank;WeKnora 十库 VectorStore 接口 与 AnythingLLM 十一 Provider 文件同构,见 Ch30。
一、日常类比
AnythingLLM 像 随身瑞士军刀:一个 Electron 壳、本地 SQLite、默认 LanceDB——开箱即用,换「刀片」(向量库/LLM)只换卡槽不拆整机。WeKnora 像 专业工具箱:BM25、图谱、Rerank 都有,更重但企业检索更稳。大师学 AnythingLLM 的 接口边界 与 零配置默认,不必复制其检索深度。
二、Commit / 版本 Pin 说明
| 项 | 建议 |
|---|---|
| 仓库 | Mintplex-Labs/anything-llm |
| 分支 | master + clone 日 2026-06 commit |
| 运行 | yarn setup → Desktop 或 server/ + frontend/ 分离 dev |
git clone https://github.com/Mintplex-Labs/anything-llm.git
cd anything-llm && git rev-parse HEAD > ~/anythingllm-pin.txt
cd server && yarn && yarn dev
Pin 与 Ch37 元数据「AnythingLLM commit」列一致。
三、目录地图
server/
endpoints/ # REST:workspace, document, chat
utils/vectorDbProviders/ # LanceDB, Chroma, Qdrant... 各一文件
utils/chats/ # 默认 RAG vs @agent 分支
models/ # Sequelize + SQLite
collector/
processSingleFile/ # 解析 → chunk → embed(独立 Node 进程)
utils/extensions/ # 格式扩展
frontend/ # React SPA
document-storage/ # 原始文件
| 对比项 | AnythingLLM | WeKnora |
|---|---|---|
| 向量抽象 | BaseVectorDb + 子类文件 |
VectorStore interface + driver |
| 隔离单位 | Workspace ID metadata | kb_id / tenant filter |
| 检索 | 纯向量 similarSearch | BM25 + 向量 + PMI + RRF |
四、一条具体调用链(上传文档 → 向量检索 → 回答)
server/endpoints/api/document/index.js # POST 上传
→ server/utils/files/multer.js # 存 document-storage/
→ server/utils/collectorApi/index.js # HTTP 调 collector 进程
→ collector/processSingleFile/index.js # processFile()
→ collector/utils/extensions/PDFLoader/... # 按扩展名解析
→ collector/utils/textSplitter/index.js # chunk
→ collector/utils/embedder/index.js # 调 embedding API
→ server/utils/vectorDbProviders/lance/index.js # addDocument(chunks)
# metadata.workspaceId = 当前 Workspace
--- 用户提问 ---
→ server/endpoints/api/workspaceThread/chat.js
→ server/utils/chats/stream.js # 默认 RAG 路径
→ server/utils/vectorDbProviders/lance/index.js # similarSearch(query, k, {workspaceId})
→ server/utils/AiProviders/openAi/index.js # LLM 生成
输入 @agent 时分支到 server/utils/agents/——「默认 RAG / 按需 Agent」渐进复杂度。
五、grep 起手式
cd anything-llm
rg -n "class LanceDb|BaseVectorDb" server/utils/vectorDbProviders/
rg -n "workspaceId|workspace_id" server/ collector/
rg -n "processFile|processSingleFile" collector/
rg -n "@agent|agentHandler" server/utils/
rg -l "similaritySearch|similarSearch" server/utils/vectorDbProviders/
WeKnora 对照:
# 在 WeKnora 仓库
rg -n "type VectorStore interface" --glob '*.go'
rg -n "kb_id|KnowledgeBaseID" internal/ -g '*vector*'
六、适配器模式:与 WeKnora 同构点
// AnythingLLM 概念 — server/utils/vectorDbProviders/base.js
class BaseVectorDb {
async connect() {}
async addDocument(chunks) {}
async similaritySearch(query, k, filter) {}
}
// WeKnora — internal/.../vectorstore 同职能
type VectorStore interface {
Upsert(ctx, vectors) error
Search(ctx, query, topK, filter) ([]Hit, error)
}
差异:AnythingLLM 新库 = 新 JS 文件;WeKnora = 新 Go driver。共性:业务层不 import 具体 SDK。见 Ch30、case-1633。
七、动手练习(45–60 分钟)
| 时段 | 任务 |
|---|---|
| 0–15 min | 起 server+collector,建 Workspace A/B |
| 15–30 min | 各上传 weknora-arch.md,确认 B 搜不到 A 的 chunk |
| 30–45 min | 读 lance/index.js 的 similarSearch filter 构造 |
| 45–60 min | 对比 Ch30:2 个故意不做的 RAG 能力及理由 |
章末自检
- 画出 Collector 与 Server 的进程边界与通信方式
- 列举 2 个 AnythingLLM 故意不做的 RAG 能力及 trade-off
- 指出
workspaceId进入向量写入与检索 filter 的文件各 1 个 - 说明 BaseVectorDb 与 WeKnora VectorStore 的三项同构方法