市面上知识库方案很多:Notion、飞书文档、Confluence……每个都能用。但我的需求比较特殊:
基于这些条件,我搭了一套全本地化的知识库系统。
数据源层 索引层 检索层 ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │ 医学手册(MSD)├───────→│ │ │ │ │ 中文精要 │ │ ChromaDB │←───────│ kb-api │ │ 转录文档 │ │ (向量数据库) │ │ (8120) │ │ 项目笔记 │───────→│ │ │ │ └─────────────┘ └──────┬───────┘ └────────────┘ │ ┌─────▼──────┐ │ embedding │ │ 模型(Silicon│ │ Flow API) │ └────────────┘ 监控层 ┌─────────────┐ │ kb-watch │ │ 文件变动→自动│ │ 增量索引 │ └─────────────┘
| 组件 | 技术选型 | 端口 | 功能 |
|---|---|---|---|
| 向量数据库 | ChromaDB | — | 存储 embeddings + 元数据 |
| Embedding | Ollama(bge-m3) → SiliconFlow API | — | 文本转向量 |
| 检索服务 | kb-api | 8120 | RESTful 语义搜索 |
| 文件监控 | kb-watch | — | 目录变化感知 + 自动索引 |
对比了几款主流方案:
| 方案 | 部署复杂度 | 语义搜索 | 持久化 | 社区 | 资源占用 |
|---|---|---|---|---|---|
| ChromaDB | ★☆☆ 极简 | ✅ | ✅ | 活跃 | 低 |
| Qdrant | ★★★ 适中 | ✅ | ✅ | 活跃 | 中 |
| Milvus | ★★★★★ 重 | ✅ | ✅ | 大厂 | 高 |
| Pinecone | 云服务 | ✅ | ✅ | — | 托管 |
| Weaviate | ★★★ 适中 | ✅ | ✅ | 活跃 | 中 |
ChromaDB 胜在极简部署——pip install chromadb 就能跑。API 设计也干净,针对我这个"本地运行、文档数量万级"的场景刚刚好。
实际规模:2827 个文件,49237 个 chunk,约 192MB——ChromaDB 对这种体量处理得毫无压力。
Embedding 质量直接影响搜索结果。踩了几天坑,最后选了这条路:
初期:Ollama 本地模型
用 bge-m3 跑本地 embedding。好处是纯离线,缺点也很明显——8GB 显存里跑 embedding 和其他模型抢资源,推理速度也慢(单条几百毫秒)。
生产:SiliconFlow API 切换到 SiliconFlow 的云端 embedding API:
兜底:Ollama 本地 万一联网断了,自动 fallback 回 Ollama 本地模型,保持服务不中断。
kb-api 跑在 8120 端口,提供两个核心端点:
POST /search { "query": "铁观音跟播ROI设置", // 自然语言查询 "top_k": 5, // 返回条数 "threshold": 0.35 // 相似度阈值 } → { "results": [ { "content": "跟播方案:铁观音账号每天09:00-17:00, ROI ≥ 10 预算不限...", "metadata": { "source": "铁观音跟播方案.md", "category": "千川投放" }, "score": 0.82 }, ... ] }
POST /query { "query": "千川的品广加热怎么操作" } → 结构化的上下文摘要,含来源引用
纯语义搜索有一个问题:匹配"意思"而不是"关键词"。有时候用户/Agent 确实需要精确命中。
最终分数 = 0.7 × 语义相似度 + 0.3 × 关键词命中 # 关键词命中 = BM25分数归一化 # 语义相似度 = Cosine(查询向量, 文档向量)
权重可调,对知识密集场景(如医学词汇)会加大关键词权重。
知识库最怕的是——文档改了,但搜索还是旧内容。
kb-watch 解决的:
knowledge-fill/ 目录的 inotify 事件增量更新一般在 秒级 完成(仅处理变化部分),不影响服务可用性。
知识库的数据来自 knowledge-fill 项目:
数据源:
处理流程:
原始文档 → 清理格式化 → 智能 chunk(~512 tokens)→ 提取元数据 → 写入 ChromaDB
Chunk 策略很重要:
知识库的最终消费者是 AI Agent。Agent 在需要时通过 kb-api 获取上下文:
pythondef search_knowledge(query: str) -> str:
"""Agent 调用知识库的接口"""
resp = requests.post("http://127.0.0.1:8120/search", json={
"query": query,
"top_k": 3
})
contexts = resp.json()["results"]
# 组装成 LLM 友好的格式
return "\n\n".join([
f"[来源:{c['metadata']['source']}]\n{c['content']}"
for c in contexts
])
效果:Agent 回答千川相关问题时的准确率从 ~60% 提升到 ~85%(目测),尤其是那些"我记得说过但不确定在哪"的知识点。
1. ChromaDB 并发写 多进程同时写入会报锁错误。解决方案:写操作串行化,读操作放开并发。
2. Chunk 重叠导致重复 前后 chunk 各 50% 重叠,遇到短文档会产出几乎相同的多个 chunk。加了去重逻辑——ChromaDB 同一 source 下的 content hash 去重。
3. Embedding 维度不一致 切换 embedding 模型后发现向量维度变了(bge-m3 是 1024,另一个模型是 768)。解决办法:一个 collection 只绑一个 embedding 模型,切换模型时新建 collection。
4. kb-watch inotify 限制
监控超 8192 个文件时需要调大 /proc/sys/fs/inotify/max_user_watches。
整套方案零云成本,依赖就是一台有 Python 环境的 Linux 机器。ChromaDB + kb-api 加起来不到 500 行核心代码,维护成本很低。
本文作者:丘丘
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!