编辑
2026-07-29
技术
00

目录

知识库系统从 0 到 1:本地语义搜索的轻量方案
为什么要自己搭知识库
架构总览
组件清单
为什么是 ChromaDB
Embedding 模型的选择
kb-api:检索服务设计
语义搜索
知识检索(供 Agent 调用)
混合搜索策略
kb-watch:增量索引
数据源:knowledge-fill 管道
与 Agent 系统的集成
踩坑记录
适合谁抄作业
未来

知识库系统从 0 到 1:本地语义搜索的轻量方案

为什么要自己搭知识库

市面上知识库方案很多:Notion、飞书文档、Confluence……每个都能用。但我的需求比较特殊:

  1. 数据隐私 — 知识库里包含商业数据(千川投放策略、业务数据),不想走云服务
  2. AI 原生 — 知识库的消费方不是人,是 AI Agent。需要 API 接口,不是 GUI
  3. 语义搜索 — 精确关键词命中不够,要能"意思差不多也能搜到"
  4. 增量更新 — 知识在持续增长,不能每次重建索引
  5. 本地运行 — 没有云预算,全靠一台带 RTX4060Ti 的台式机

基于这些条件,我搭了一套全本地化的知识库系统。


架构总览

数据源层 索引层 检索层 ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │ 医学手册(MSD)├───────→│ │ │ │ │ 中文精要 │ │ ChromaDB │←───────│ kb-api │ │ 转录文档 │ │ (向量数据库) │ │ (8120) │ │ 项目笔记 │───────→│ │ │ │ └─────────────┘ └──────┬───────┘ └────────────┘ │ ┌─────▼──────┐ │ embedding │ │ 模型(Silicon│ │ Flow API) │ └────────────┘ 监控层 ┌─────────────┐ │ kb-watch │ │ 文件变动→自动│ │ 增量索引 │ └─────────────┘

组件清单

组件技术选型端口功能
向量数据库ChromaDB存储 embeddings + 元数据
EmbeddingOllama(bge-m3) → SiliconFlow API文本转向量
检索服务kb-api8120RESTful 语义搜索
文件监控kb-watch目录变化感知 + 自动索引

为什么是 ChromaDB

对比了几款主流方案:

方案部署复杂度语义搜索持久化社区资源占用
ChromaDB★☆☆ 极简活跃
Qdrant★★★ 适中活跃
Milvus★★★★★ 重大厂
Pinecone云服务托管
Weaviate★★★ 适中活跃

ChromaDB 胜在极简部署——pip install chromadb 就能跑。API 设计也干净,针对我这个"本地运行、文档数量万级"的场景刚刚好。

实际规模:2827 个文件,49237 个 chunk,约 192MB——ChromaDB 对这种体量处理得毫无压力。


Embedding 模型的选择

Embedding 质量直接影响搜索结果。踩了几天坑,最后选了这条路:

初期:Ollama 本地模型bge-m3 跑本地 embedding。好处是纯离线,缺点也很明显——8GB 显存里跑 embedding 和其他模型抢资源,推理速度也慢(单条几百毫秒)。

生产:SiliconFlow API 切换到 SiliconFlow 的云端 embedding API:

  • 延迟低(几十毫秒)
  • 质量稳定
  • 价格便宜(embedding 本身就很便宜)
  • 不占本地 GPU 资源

兜底:Ollama 本地 万一联网断了,自动 fallback 回 Ollama 本地模型,保持服务不中断。


kb-api:检索服务设计

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 }, ... ] }

知识检索(供 Agent 调用)

POST /query { "query": "千川的品广加热怎么操作" } → 结构化的上下文摘要,含来源引用

混合搜索策略

纯语义搜索有一个问题:匹配"意思"而不是"关键词"。有时候用户/Agent 确实需要精确命中。

最终分数 = 0.7 × 语义相似度 + 0.3 × 关键词命中 # 关键词命中 = BM25分数归一化 # 语义相似度 = Cosine(查询向量, 文档向量)

权重可调,对知识密集场景(如医学词汇)会加大关键词权重。


kb-watch:增量索引

知识库最怕的是——文档改了,但搜索还是旧内容。

kb-watch 解决的:

  • 监控 knowledge-fill/ 目录的 inotify 事件
  • 文件新增/修改 → 重新 chunk → 重新 embed → 更新 ChromaDB
  • 文件删除 → 清理对应索引

增量更新一般在 秒级 完成(仅处理变化部分),不影响服务可用性。


数据源:knowledge-fill 管道

知识库的数据来自 knowledge-fill 项目:

数据源:

  • MSD Manual(英文学手册) — 爬取整理的专业医学参考
  • 中文医学精要 — 翻译编译的中文版
  • 转录文档 — 千川投放培训课的语音转录
  • 项目笔记 — 日常操作中沉淀的 markdown 笔记

处理流程:

原始文档 → 清理格式化 → 智能 chunk(~512 tokens)→ 提取元数据 → 写入 ChromaDB

Chunk 策略很重要:

  • 太短(<100 tokens)→ 上下文不足,搜索出来读不懂
  • 太长(>1000 tokens)→ 语义模糊,容易 scope drift
  • 重叠 10-20% → 避免边界截断

与 Agent 系统的集成

知识库的最终消费者是 AI Agent。Agent 在需要时通过 kb-api 获取上下文:

python
def 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


适合谁抄作业

  • 有本地部署需求的个人/小团队
  • 需要给 AI 应用挂知识库的开发者
  • 数据敏感不想上云的内容运营

整套方案零云成本,依赖就是一台有 Python 环境的 Linux 机器。ChromaDB + kb-api 加起来不到 500 行核心代码,维护成本很低。


未来

  • 多模态 — 支持图片、PDF 的语义检索
  • 知识图谱 — 在向量之上叠加实体关系
  • 自动摘要 — 检索结果由 Agent 自动摘要,提升阅读效率

本文作者:丘丘

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!