搜 索

自建知识库从入门到放弃

  • 24阅读
  • 2026年01月25日
  • 0评论
首页 / AI/大数据 / 正文

前言:为什么要自建知识库?

事情是这样的。

老板说:"我们需要一个知识库,把公司文档都丢进去,员工问什么都能答。"

产品经理说:"最好能像 ChatGPT 一样智能,还要有知识图谱,要酷炫。"

我说:"好的,预计两周完成。"

三个月后,我决定写下这篇文章。

一、知识库到底是个什么东西?

在动手之前,我们先搞清楚一个"智能知识库"到底要做什么。

1.1 传统知识库 vs 智能知识库

graph LR subgraph 传统知识库 A[用户搜索] --> B[关键词匹配] B --> C[返回文档列表] C --> D[用户自己找答案] end subgraph 智能知识库 E[用户提问] --> F[语义理解] F --> G[多路召回] G --> H[大模型生成答案] H --> I[引用溯源] end

简单来说:

对比维度传统知识库智能知识库
输入关键词自然语言问题
匹配方式字面匹配语义匹配
输出文档列表直接答案 + 来源
用户体验自己翻文档直接获得答案
技术复杂度⭐⭐⭐⭐⭐⭐⭐

看到最后一行了吗?对,这就是为什么要写"从入门到放弃"。

1.2 我们要实现的功能

既然要做,就做个全的:

mindmap root((智能知识库)) 文档管理 多格式支持 自动解析 智能分块 版本管理 智能问答 多轮对话 引用溯源 多模型切换 混合检索 知识图谱 实体抽取 关系构建 图谱可视化 图谱问答 系统能力 权限控制 使用统计 模型监控 增量更新

二、技术选型:选择困难症发作现场

2.1 整体架构

先上一张让老板觉得你很专业的架构图:

flowchart TB subgraph 用户层 UI[Web 界面] API_CLIENT[API 调用] end subgraph 接入层 GW[API Gateway] end subgraph 服务层 DOC[文档服务] QA[问答服务] GRAPH[图谱服务] CONFIG[配置服务] end subgraph 核心引擎 INGEST[文档处理管道] RAG[RAG 引擎] KG[知识图谱构建器] ROUTER[模型路由器] end subgraph 存储层 CHROMA[(Chroma 向量存储)] MYSQL[(MySQL 元数据)] OSS[对象存储 原始文档] end subgraph 模型层 EMB[Embedding 模型] LLM_DS[DeepSeek] LLM_QW[Qwen] LLM_GM[Gemini] end UI --> GW API_CLIENT --> GW GW --> DOC & QA & GRAPH & CONFIG DOC --> INGEST QA --> RAG GRAPH --> KG CONFIG --> ROUTER INGEST --> CHROMA & MYSQL & OSS INGEST --> KG RAG --> CHROMA & ROUTER KG --> MYSQL INGEST --> EMB ROUTER --> LLM_DS & LLM_QW & LLM_GM

2.2 技术栈选择

经过无数次纠结和重构,最终选定:

组件选型选择理由备选方案
向量数据库Chroma轻量、嵌入式、上手快Qdrant, Milvus
关系数据库MySQL公司标准、团队熟悉PostgreSQL
RAG 框架LlamaIndex专注 RAG、文档处理强LangChain
大模型DeepSeek/Qwen/Gemini多模型兜底、成本可控GPT-4
图谱可视化PyvisPython 原生、效果不错D3.js, ECharts
后端框架FastAPI异步、自动文档、类型安全Flask

为什么不用 GPT-4?

因为穷。DeepSeek 百万 token 几块钱,GPT-4 要几十美元。老板看到账单会让你从入门到离职。

2.3 向量数据库深度对比

这是个关键决策,值得多说几句:

quadrantChart title "向量数据库选型象限图" x-axis "部署复杂度低" --> "部署复杂度高" y-axis "功能简单" --> "功能丰富" quadrant-1 "企业级首选" quadrant-2 "重型方案" quadrant-3 "快速验证" quadrant-4 "平衡之选" "Chroma": [0.2, 0.3] "Qdrant": [0.4, 0.7] "Milvus": [0.8, 0.9] "pgvector": [0.3, 0.4] "Weaviate": [0.6, 0.8]

Chroma 的甜点区:10 万条以内的文档,它就是最优解。超过这个数,老老实实换 Qdrant。

三、核心流程设计

3.1 文档处理管道

文档从上传到可被检索,要经历九九八十一难:

flowchart LR subgraph 文档接入 A[文档上传] --> B{格式识别} B -->|PDF| C1[PDF 解析器] B -->|Word| C2[DOCX 解析器] B -->|网页| C3[HTML 解析器] B -->|Markdown| C4[MD 解析器] end subgraph 文档处理 C1 & C2 & C3 & C4 --> D[文本清洗] D --> E[智能分块] E --> F[元数据提取] end subgraph 索引构建 F --> G[Embedding 生成] G --> H[向量入库] F --> I[元数据入库] end subgraph 图谱构建 F --> J[实体抽取] J --> K[关系抽取] K --> L[图谱入库] end H & I & L --> M[索引完成]

智能分块策略

分块是 RAG 效果的关键,分得不好,检索出来的都是残句断章。

flowchart TB subgraph 分块策略 A[原始文档] --> B{文档类型} B -->|技术文档| C[按标题层级分块] B -->|法律合同| D[按条款分块] B -->|对话记录| E[按对话轮次分块] B -->|通用文本| F[滑动窗口分块] C & D & E & F --> G[块大小校验] G -->|过大| H[递归分割] G -->|过小| I[合并相邻块] G -->|合适| J[生成最终块] H & I --> J end

分块参数建议

文档类型chunk_sizechunk_overlap分块策略
技术文档51250按标题层级
FAQ25620按问答对
长篇报告1024100滑动窗口
代码文档512100按函数/类

3.2 RAG 检索流程

RAG(Retrieval-Augmented Generation)是整个系统的灵魂:

sequenceDiagram participant U as 用户 participant API as API 服务 participant QR as 查询改写 participant VR as 向量检索 participant KR as 关键词检索 participant GR as 图谱检索 participant RK as 重排序 participant LLM as 大模型 U->>API: 提问:"NPSS 的清算周期是多久?" API->>QR: 查询改写 Note over QR: 生成多个检索 query:
1. NPSS 清算周期
2. NPSS settlement cycle
3. Aani 清算时间 par 多路召回 QR->>VR: 向量相似检索 QR->>KR: 关键词检索 QR->>GR: 图谱实体检索 end VR->>RK: Top 20 结果 KR->>RK: Top 10 结果 GR->>RK: 相关实体 + 关系 RK->>RK: Rerank 重排序 Note over RK: 使用 bge-reranker
筛选 Top 5 RK->>LLM: Context + Query LLM->>API: 生成答案 + 引用 API->>U: 返回答案

混合检索的威力

单纯的向量检索有个致命问题:对专业术语不敏感

比如用户问"IBAN 迁移方案",向量检索可能会返回"银行账户变更流程"这种语义相似但不相关的结果。

flowchart LR subgraph 混合检索 Q[用户 Query] --> V[向量检索] Q --> K[关键词检索] Q --> G[图谱检索] V -->|语义相似| R1[结果集 1] K -->|精确匹配| R2[结果集 2] G -->|实体关联| R3[结果集 3] R1 & R2 & R3 --> M[结果融合] M --> RR[Rerank 重排序] RR --> F[最终结果] end

融合策略:RRF(Reciprocal Rank Fusion)

RRF_score = Σ 1/(k + rank_i)

其中 k 通常取 60,rank_i 是文档在第 i 个检索器中的排名。

3.3 知识图谱构建

知识图谱是这个系统的"创新点"(也是最容易让你放弃的点)。

flowchart TB subgraph 图谱构建流程 A[文档块] --> B[实体识别] B --> C{识别方式} C -->|规则匹配| D1[正则 + 词典] C -->|NER 模型| D2[BERT-NER] C -->|LLM 抽取| D3[Prompt 抽取] D1 & D2 & D3 --> E[实体融合] E --> F[实体链接] F --> G[关系抽取] G --> H[图谱存储] end subgraph 实体类型 E1[人物 Person] E2[组织 Organization] E3[技术 Technology] E4[概念 Concept] E5[产品 Product] E6[时间 Time] end

LLM 抽取 Prompt 设计

这是个 Prompt Engineering 的活儿:

从以下文本中抽取实体和关系。

实体类型:Person, Organization, Technology, Concept, Product
关系类型:属于, 使用, 开发, 依赖, 包含, 负责

要求:
1. 实体名称使用原文表述
2. 关系必须有明确依据
3. 不确定的不要抽取

输出 JSON 格式:
{
  "entities": [{"name": "xxx", "type": "xxx"}],
  "relations": [{"source": "xxx", "relation": "xxx", "target": "xxx"}]
}

文本:{text}

图谱可视化效果

用 Pyvis 可以生成这样的交互式图谱:

graph LR subgraph 支付系统知识图谱示例 NPSS[NPSS] -->|包含| AANI[Aani] NPSS -->|使用| IBAN[IBAN] AANI -->|支持| IPS[即时支付] AANI -->|对接| CBUAE[央行] VIS[VIS] -->|管理| VA[虚拟账户] VA -->|关联| IBAN DEPOSIT[Deposit] -->|提供| TOPUP[充值服务] TOPUP -->|通过| AANI JOEY((Joey)) -->|负责| NPSS JOEY -->|负责| VIS JOEY -->|负责| DEPOSIT end style JOEY fill:#ff6b6b style NPSS fill:#4ecdc4 style VIS fill:#4ecdc4 style DEPOSIT fill:#4ecdc4

3.4 多模型路由

为什么要支持多模型?因为:

  1. 成本优化:简单问题用便宜模型,复杂问题用强模型
  2. 可用性兜底:一个挂了还有备选
  3. 效果对比:A/B 测试哪个模型更好
flowchart TB subgraph 模型路由策略 Q[用户 Query] --> A{复杂度评估} A -->|简单问答| B[DeepSeek] A -->|复杂推理| C[Qwen-Max] A -->|多模态| D[Gemini] B & C & D --> E{响应检查} E -->|成功| F[返回结果] E -->|失败/超时| G[降级到备选模型] G --> E end

复杂度评估维度

维度简单复杂
Query 长度< 50 字> 200 字
检索结果高相关 (>0.8)低相关 (<0.5)
问题类型事实查询推理分析
上下文需求单轮多轮

四、创新点与技术亮点

4.1 查询改写(Query Rewriting)

用户的问题往往不是最优检索 query。

flowchart LR subgraph 查询改写 A["用户问题:
NPSS 怎么用?"] --> B[LLM 改写] B --> C["改写 1:
NPSS 使用指南"] B --> D["改写 2:
NPSS 接入流程"] B --> E["改写 3:
NPSS API 调用方法"] B --> F["改写 4:
National Payment System 教程"] end

改写策略

策略说明示例
同义词扩展补充专业术语的其他说法NPSS → National Payment System
子问题拆解复杂问题拆成多个简单问题"对比 A 和 B" → "A 是什么" + "B 是什么"
假设文档法生成理想答案的文档标题"xxx 是什么" → "xxx 详解/指南"
多语言扩展中英文互译检索清算周期 → settlement cycle

4.2 答案溯源(Citation)

这是企业级知识库的刚需:回答必须有据可查。

flowchart TB subgraph 答案溯源 A[生成答案] --> B[句子切分] B --> C{每句话} C --> D[匹配检索结果] D --> E{匹配度} E -->|高| F[添加引用标记] E -->|低| G[标记为模型生成] F & G --> H[组装最终答案] end subgraph 输出示例 I["NPSS 的清算周期为 T+0[1],
支持 7x24 小时实时清算[1]。
目前已对接 15 家银行[2]。

[1] NPSS技术规范v2.3.pdf, P12
[2] 2024年Q1接入报告.docx, P5"] end

4.3 增量更新机制

文档更新后不需要全量重建索引:

flowchart TB subgraph 增量更新 A[文档变更] --> B{变更类型} B -->|新增| C[直接追加索引] B -->|修改| D[计算 diff] B -->|删除| E[标记删除] D --> F{变更范围} F -->|< 20%| G[局部更新] F -->|> 20%| H[全量重建] C & G & E --> I[更新元数据版本] H --> I end

4.4 对话记忆与上下文管理

多轮对话需要维护上下文:

sequenceDiagram participant U as 用户 participant M as 记忆管理器 participant R as RAG 引擎 U->>M: Q1: NPSS 是什么? M->>R: 检索 + 生成 R->>M: A1: NPSS 是阿联酋国家支付系统... M->>M: 存储 (Q1, A1) U->>M: Q2: 它支持哪些银行? Note over M: "它" 指代消解 → NPSS M->>R: 改写 Query: NPSS 支持哪些银行 R->>M: A2: NPSS 目前支持 FAB, ADCB... M->>M: 存储 (Q2, A2) U->>M: Q3: 第一个银行的接入时间? Note over M: "第一个银行" → FAB M->>R: 改写 Query: FAB 接入 NPSS 的时间 R->>M: A3: FAB 于 2023年10月接入...

五、踩坑实录

5.1 Embedding 模型选择的坑

症状解决方案
维度不匹配换模型后检索全挂换模型 = 重建索引,没有捷径
中文效果差英文模型检索中文文档效果稀烂bge-large-zh 等中文模型
长文本截断超过 512 token 的内容被丢弃用支持长文本的模型或先分块

5.2 分块策略的坑

flowchart LR subgraph 分块过大 A1[问题:检索到整段
但答案在角落] --> B1[召回率高
精确度低] end subgraph 分块过小 A2[问题:句子被切断
失去上下文] --> B2[精确度高
可读性差] end subgraph 最佳实践 A3[根据文档类型
动态调整] --> B3[平衡召回
和精确度] end

5.3 LLM 幻觉的坑

大模型最大的问题:一本正经地胡说八道

缓解策略

  1. 强制引用:Prompt 中要求必须基于检索结果回答
  2. 置信度过滤:检索相关度太低时,直接回复"未找到相关信息"
  3. 事实核查:关键信息二次检索验证
  4. 用户反馈:让用户标记错误答案,持续优化

5.4 知识图谱的坑

实体抽取看起来简单,实际上:

问题示例后果
实体歧义"苹果"是公司还是水果?图谱混乱
指代消解"它"、"该系统"指什么?关系错误
抽取过度把所有名词都当实体图谱太乱
抽取不足只抽专有名词图谱太稀疏

建议:先做好 RAG,图谱锦上添花。不要本末倒置。

六、性能优化

6.1 检索性能优化

flowchart TB subgraph 优化手段 A[原始检索] --> B{瓶颈分析} B -->|Embedding 慢| C[模型量化 / GPU 加速] B -->|向量检索慢| D[HNSW 索引 / 量化压缩] B -->|网络延迟| E[本地缓存热点 Query] B -->|LLM 慢| F[流式输出 / 模型蒸馏] end

6.2 缓存策略

flowchart LR subgraph 多级缓存 Q[Query] --> L1[L1: Query 缓存
完全匹配] L1 -->|Miss| L2[L2: 语义缓存
相似 Query] L2 -->|Miss| L3[L3: 结果缓存
检索结果] L3 -->|Miss| DB[实际检索] end

七、部署架构

7.1 开发环境

flowchart LR subgraph 本地开发 DEV[开发机] --> CHROMA[Chroma
嵌入式] DEV --> MYSQL[MySQL
Docker] DEV --> API[DeepSeek API] end

适合:PoC 验证、功能开发、个人使用

7.2 生产环境

flowchart TB subgraph 生产部署 LB[负载均衡] --> API1[API Server 1] LB --> API2[API Server 2] API1 & API2 --> QDRANT[Qdrant 集群] API1 & API2 --> MYSQL_M[MySQL 主] MYSQL_M --> MYSQL_S[MySQL 从] API1 & API2 --> REDIS[Redis 缓存] API1 & API2 --> LLM_GW[LLM 网关] LLM_GW --> DS[DeepSeek] LLM_GW --> QW[Qwen] LLM_GW --> GM[Gemini] end

资源估算(10 万文档规模):

组件配置数量
API Server4C8G2
Qdrant4C16G1
MySQL4C8G1主1从
Redis2C4G1

八、什么时候该放弃自建?

写了这么多,最后说点实在的。

8.1 自建 vs 买现成的

quadrantChart title "自建 vs 现成方案决策" x-axis "定制需求低" --> "定制需求高" y-axis "预算少" --> "预算多" quadrant-1 "自建" quadrant-2 "商业方案" quadrant-3 "开源方案" quadrant-4 "自建" "FastGPT": [0.3, 0.2] "Dify": [0.4, 0.3] "RAGFlow": [0.5, 0.3] "企业微信知识库": [0.2, 0.6] "Azure AI Search": [0.3, 0.8] "自建方案": [0.8, 0.5]

8.2 放弃自建的信号

当你遇到以下情况,考虑用现成方案:

信号说明
没有专职维护人员知识库需要持续运营
文档量 < 1 万杀鸡用牛刀
老板要下周上线自建至少要 1-2 个月
团队没有 ML 背景调优 RAG 需要经验
预算充足花钱买时间

8.3 推荐的现成方案

方案特点适合场景
Dify开源、可视化、功能全快速搭建
FastGPT国产、知识库专精中文场景
RAGFlow深度文档解析复杂文档
Coze字节出品、简单易用轻量需求

九、总结

回顾一下这趟"从入门到放弃"之旅:

journey title 自建知识库心路历程 section 入门 看到 RAG 教程: 5: 开心 决定自己搭一个: 5: 兴奋 section 实践 选型纠结: 3: 焦虑 第一个 Demo 跑通: 5: 开心 发现检索效果不行: 2: 崩溃 调了一周分块策略: 3: 疲惫 section 深入 搞定混合检索: 4: 欣慰 加上知识图谱: 4: 骄傲 被 LLM 幻觉坑: 2: 绝望 section 放弃? 重新评估需求: 3: 理性 决定继续/放弃: 4: 释然

核心收获

  1. RAG 不是银弹:检索质量决定生成质量,垃圾进垃圾出
  2. 分块是玄学:没有最优,只有最适合
  3. 图谱是锦上添花:先把 RAG 做好
  4. 成本要算清楚:自建的隐性成本(人力、维护)往往被低估

最后,无论你是选择自建还是放弃,希望这篇文章能帮你少走一些弯路。

毕竟,放弃也是一种智慧


评论区
暂无评论
avatar