一句话理解本文:把大模型当成一个"博学但容易记错话"的同事,知识库就是帮它查资料、理关系的"外挂硬盘 + 关系图谱"。 本文从零开始,用类比 + 步骤 + 代码 + 表格,带你搭出一套标准的企业级知识库。
目录
- 引言:为什么需要知识库?
- 1. RAG 知识库原理与核心技术点
- 1.1 整体架构:Indexing → Retrieval → Generation
- 1.2 文档解析:把 PDF 变成"干净的文本"
- 1.3 切片:把书拆成"知识点卡片"
- 1.4 Embedding:让文字变成"坐标点"
- 1.5 向量数据库:给上亿个坐标点建索引
- 1.6 混合检索与重排序:先海选,再精挑
- 1.7 上下文组装:把最有用片段喂给大模型
- 2. 知识图谱构建流程
- 2.1 知识图谱是什么:用"关系"组织知识
- 2.2 本体设计:先画好"骨架"
- 2.3 实体识别与关系抽取:LLM 辅助抽取三元组
- 2.4 图数据库选型:Neo4j / NebulaGraph / JanusGraph
- 3. GraphRAG:知识图谱 + LLM 的融合
- 3.1 为什么需要 GraphRAG
- 3.2 微软 GraphRAG 原理:建图 + 社区摘要 + 双通道查询
- 3.3 其他结合模式:ToG、Vector + Graph 混合
- 4. 从零搭建实现步骤清单(RAG + KG 混合架构)
- 5. 关键注意点:数据质量、更新维护、评估、成本、幻觉
- 6. 真实实现案例(2024-2026)
- 7. 常见问题速查表(FAQ)
- 结语
引言:为什么需要知识库?
大语言模型(LLM)有一个"天生缺陷":它学到的知识只停留在训练数据截止的那一刻,而且回答时容易"一本正经地胡说八道"(幻觉)。
打个比方:LLM 像一个记忆力超强、但从不查资料的天才实习生。你问他"公司报销流程是什么",他会凭印象编一个——因为他的"印象"里根本没有你公司的制度文件。
知识库要解决的就是这两件事:
| 知识过时 | 只会训练截止前的知识 | 从最新文档中检索实时信息 |
| 幻觉 | 编造不存在的"事实" | 答案必须基于检索到的原文片段 |
| 私有数据 | 不知道你的内部资料 | 只从你的私有文档中回答 |
| 复杂关系 | 难以做多跳推理 | 知识图谱提供关系路径 |
而知识图谱(Knowledge Graph)进一步解决"关系复杂、需要多跳推理"的问题:比如"A 公司的 CEO 曾在 B 公司任职,B 公司被 C 集团收购,问 C 集团的实际控制人是谁"——这类问题靠"关键词匹配"是答不出来的,要靠"图上走两步"。
1. RAG 知识库原理与核心技术点
1.1 整体架构:Indexing → Retrieval → Generation
RAG(Retrieval-Augmented Generation,检索增强生成)的核心链路只有三步:
数据入库(Indexing) -> 检索(Retrieval) -> 生成(Generation)
[你的文档] –解析–> [切片] –向量化–> [向量数据库]
↑
用户提问 ———————————–┘
↓
[检索到 Top-K 片段] –组装–> [Prompt] –> [LLM] –> 带引用的答案
通俗类比:这就像你去图书馆查资料写论文。
下面逐个拆解核心环节,每个环节都有一个"关键易错点"。
1.2 文档解析:把 PDF 变成"干净的文本"
原理:把非结构化数据(PDF、Word、HTML、扫描件)转换成 LLM 能理解的纯文本或结构化格式。
通俗类比:文档解析就像"拆快递"。快递箱里可能塞了泡沫、说明书、赠品,你要的是里面的商品(正文文本)。解析做得好不好,直接决定后面所有环节的质量。
主流工具:
| Unstructured.io | 行业标准,按元素类型(标题/列表/表格)分区输出 | 通用文档,大多数场景首选 |
| Marker / Mathpix | 精准提取复杂数学公式(LaTeX) | 论文、技术文档、理科教材 |
| LLM-as-a-Parser(GPT-4o 等多模态模型直接"看"PDF) | 准确率极高,直接输出 Markdown | 扫描件、排版复杂的 PDF;成本较高 |
示例(使用 Unstructured 解析 PDF):
from unstructured.partition.pdf import partition_pdf
elements = partition_pdf(
filename="docs/产品手册.pdf",
strategy="hi_res", # 高分辨率策略,表格/图片识别更准
)
for el in elements:
print(el.category, "|", el.text[:100])
# 输出示例:
# Title | 第一章 产品概述
# Table | 型号 | 功率 | 价格 …
⚠️ 易错点 1:扫描版 PDF 必须先 OCR,否则解析出来全是乱码或空白。Unstructured 的 strategy="ocr_only" 或 hi_res 可处理,但不要默认跳过这一步。
⚠️ 易错点 2:表格是解析重灾区。Word 转 PDF 的表格、带合并单元格的表格,解析后常常行列错乱。解析后必须抽样人工检查——"解析错误的 PDF 是 RAG 失败的首要原因",没有之一。
1.3 切片:把书拆成"知识点卡片"
原理:大模型的上下文窗口有限(几千到几十万 token),你不能把整本书塞进去,要把长文档切成语义完整的小片段,检索时按片段召回。
通俗类比:切片就像把一本厚书拆成一张张"知识点卡片",每张卡片讲清一个小主题。切得太粗,检索浪费 token;切得太碎,语义被拦腰截断。
切片方案演进:
| 固定大小切片 | 每 500 token 一刀 | 实现简单 | 极易切断语义,比如把"小明是"和"程序员"分到两段 |
| 结构切片 | 按 Markdown 标题、代码函数切 | 语义完整 | 依赖文档结构,无结构的文档没用 |
| 语义切片 | 用 Embedding 算句子相似度,在语义断层处切 | 边界最合理 | 计算成本高 |
| Late Chunking(Cohere) | 先对全文做 Embedding,再按边界平均池化 | 保留跨边界上下文信息 | 需要特定模型配合 |
实际推荐:先用结构切片(MarkdownHeaderTextSplitter 或按段落),再叠加大小上限(如 5121024 token,重叠 50100 token)。重叠(overlap)是为了防止问题恰好落在切口上。
from langchain.text_splitter import MarkdownHeaderTextSplitter
splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[("#", "标题1"), ("##", "标题2")],
)
chunks = splitter.split_text(markdown_doc)
for c in chunks[:3]:
print(c.metadata, "|", c.page_content[:50])
⚠️ 易错点:不要对表格、JSON、代码做纯字符切片。这类结构化内容最好整块保留(比如一个表格作为一片),否则检索到的片段"缺头少尾",模型根本读不懂。
1.4 Embedding:让文字变成"坐标点"
原理:把文本映射到一个高维向量空间(比如 1024 维),让语义相近的文本在空间里距离更近。
通俗类比:想象一个巨大的"语义地图"。"苹果"和"水果"离得近,"苹果"和"汽车"离得远。Embedding 模型负责把每句话在地图上"定位"。
2026 年开源模型已全面反超闭源 API——Qwen3-Embedding-8B、Harrier-OSS-v1、NV-Embed-v2 等在 MTEB v2 排行榜上均超越 OpenAI / Cohere 等商业 API。自托管优先选开源;闭源 API 只在你不想运维 GPU、且对长上下文/多模态有特定需求时考虑。
2026 最新开源 Embedding 模型:
| Qwen3-Embedding-8B | 8B | 多语言第一梯队(119 语言),指令感知,支持 MRL 截断 | 32K | Apache 2.0 | 自托管首选,中文/多语言 RAG 最强开源选择 |
| Qwen3-Embedding-4B / 0.6B | 4B / 0.6B | 同系列小尺寸,性价比与边缘部署 | 32K | Apache 2.0 | 4B 是生产部署甜点,0.6B 适合 CPU/边缘 |
| Harrier-OSS-v1(微软) | 27B | 2026 年 3 月发布,MMTEB v2 榜首(~74.3) | 32K | MIT | 有推理基础设施、追求极致质量 |
| NV-Embed-v2(NVIDIA) | 7B | 英文检索专项最强(~72.3) | 32K | CC-BY-NC | 英文场景、NVIDIA/vLLM 服务 |
| BGE-M3(智源) | 568M | dense + sparse + 多向量三合一,100+ 语言 | 8K | MIT | 仍是生产环境最常用,混合检索免多模型 |
| Jina v5-text-small | ~0.6B | MTEB v2 英文 71.7,跨语言对齐好 | 32K | CC-BY-NC | 内部工具/研究;商用走 API |
| gte-Qwen3-8B(阿里) | 8B | 通用多语言,Apache 2.0 | 32K | Apache 2.0 | 需要宽松商用许可的替代 |
| KaLM-Embedding-Gemma3-12B(腾讯) | 12B | MMTEB 官方榜首(72.32) | 32K | 腾讯社区许可 | 中文/多语言高质量场景 |
| Nomic Embed Text V2(MoE) | ~475M | 首个开源 MoE Embedding,dense+sparse 一体 | 512 | Apache 2.0 | 混合检索、低资源 |
2026 最新闭源 API:
| Gemini Embedding 2(Google) | 首个原生多模态(文本+图+音视频+PDF 统一向量),MTEB ~68.3 | 8K | ~$0.008(batch 更低) |
| Voyage-3.1 / voyage-4-large | 代码/法律/金融领域检索最强,长上下文之王 | 128K | ~$0.06–0.5 |
| Cohere Embed v4 | 多模态、100+ 语言、Matryoshka 维度可调 | 128K | ~$0.10–0.12 |
| OpenAI text-embedding-3-large/small | 仍是 2024 年 1 月的版本,未更新,靠生态吃老本 | 8K | $0.13 / $0.02 |
关键趋势:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-m3")
texts = ["苹果是一种水果", "苹果发布了新手机", "今天天气很好"]
embeddings = model.encode(texts, normalize_embeddings=True)
print(embeddings.shape) # (3, 1024)
⚠️ 易错点 1:查询语句和文档要用同一个 Embedding 模型,否则"地图"都不一样,检索必然失效。
⚠️ 易错点 2:Embedding 模型升级后要全量重灌向量库。混用新旧模型产生的向量(新旧"地图")无法互相比较,这是线上事故高发点。
⚠️ 易错点 3:中文场景请优先选择多语言模型(如 Qwen3-Embedding、BGE-M3、KaLM-Embedding),纯英文专项模型(如 NV-Embed-v2)对中文语义的理解会大打折扣。
⚠️ 易错点 4:MRL 维度截断必须全局统一。Qwen3-Embedding 等支持把 4096 维截到任意维度(如 1024/512/256),但一旦选定截断维度,入库与查询必须一致,中途改维度要全量重建向量库。
1.5 向量数据库:给上亿个坐标点建索引
原理:向量数据库解决"在几亿个高维向量里快速找到离查询点最近的 Top-K 个"。核心是近似最近邻(ANN)索引(如 HNSW、IVF),牺牲少量精度换取毫秒级响应。
通俗类比:Embedding 模型只是把书的内容"翻译成坐标",向量数据库才是真正的"书架"——它把上亿个坐标点组织成可以秒查的索引结构。
选型对比:
| Pinecone | 全托管 SaaS,自动扩缩容 | 快速原型、不想运维的初创团队 |
| Milvus / Zilliz | 高性能,十亿级向量,支持向量+标量混合检索 | 海量数据、高并发生产环境 |
| Weaviate | 开源,原生多模态,内置 LLM 模块 | 需要本地部署、模块化要求高 |
| Chroma / FAISS | 轻量级、嵌入式 | 本地测试、小规模应用 |
# 以 Milvus 为例:建 Collection 并写入
from pymilvus import MilvusClient
client = MilvusClient("milvus_demo.db")
client.create_collection(
collection_name="kb_docs",
dimension=1024, # 与 bge-m3 输出维度一致
metric_type="COSINE", # 余弦相似度
)
client.insert(
collection_name="kb_docs",
data=[
{"id": 1, "vector": embeddings[0].tolist(), "text": "苹果是一种水果", "source": "常识.md"},
{"id": 2, "vector": embeddings[1].tolist(), "text": "苹果发布了新手机", "source": "新闻.md"},
],
)
⚠️ 易错点:向量维度必须与 Embedding 模型输出维度一致(BGE-M3 是 1024 维,换模型就要重建 Collection)。另外,生产环境不要只存向量,要把原文、来源、元数据(标题、页号)一起存进去,否则检索结果无法追溯、无法展示引用。
1.6 混合检索与重排序:先海选,再精挑
问题:纯向量检索(语义匹配)对精确关键词很弱。比如用户搜"iPhone 15 Pro Max 256G 深蓝",向量检索可能把"蓝色手机"都召回,却漏掉这个精确型号。
混合检索(Hybrid Search):向量检索(语义) + 关键词检索(BM25/TF-IDF,精确匹配)双路召回,再合并去重。
重排序(Reranking):把召回的上百个候选,交给一个更精细的 Cross-Encoder 模型重新打分,只把最相关的 Top-N(如 5 个)传给 LLM。
通俗类比:混合检索是"先海选"——语义搜索和关键词搜索各招一批人;重排序是"终面"——让资深面试官(Reranker)逐个精挑,只留最优秀的几个人进 final round(喂给 LLM)。
目前提升 RAG 准确率性价比最高的手段,就是加一个 Reranker。
| Cohere Rerank | 商业 API,效果好、接入快 |
| BGE Reranker | 开源,中文效果好,可本地部署 |
| Voyage Reranker | 与 voyage embedding 配合最佳 |
from FlagEmbedding import FlagReranker
reranker = FlagReranker("BAAI/bge-reranker-v2-m3")
query = "苹果公司的创始人是谁?"
candidates = ["苹果是一种水果。", "乔布斯是苹果公司的联合创始人之一。", "苹果发布了新手机。"]
scores = reranker.compute_score([[query, c] for c in candidates])
# 输出: [-7.8, 8.5, -2.1] -> 第2条最相关
⚠️ 易错点 1:重排序后仍然要控制送进 LLM 的 token 量。Top-5 每条 1000 token,就是 5000 token,超出预算会拖慢响应、提高成本。
⚠️ 易错点 2:BM25 的 k1、b 参数、混合比例(如向量 0.7 : 关键词 0.3)需要在真实数据上调参,不要拍脑袋。可以用验证集做一次小实验确定比例。
1.7 上下文组装:把最有用片段喂给大模型
检索到片段后,不能简单拼接,要做"组装"加工,常见技术:
| Prompt 模板组装 | 按"系统指令 + 检索片段 + 问题"结构化拼接,要求"只能基于片段回答" | 给大模型定规矩:不许瞎编,引用要标出处 |
| Prompt Compression(如 LLMLingua) | 压缩检索上下文,剔除冗余 token | 把 5000 token 的"废话+干货"压缩到 2000 token 的"纯干货" |
| CRAG(Corrective RAG) | 先评估检索结果相关性,不相关就触发 Web 搜索补充 | 检索"掉链子"时自动换一条路走 |
| 引文标注 | 让 LLM 在答案后标注 [来源: 文档名-页号] | 用户能回溯验证,提升可信度 |
prompt = f"""你是一名严谨的知识库助手。请只依据下面提供的资料回答问题;
如果资料中没有相关信息,请明确回答"资料中未找到"。回答末尾请标注来源。
【资料】
{context} # 已拼接、去重、压缩后的 Top-5 片段
【问题】
{question}
【回答】
"""
⚠️ 易错点 1:拼接上下文时要去重、按相关性降序,把最相关的放前面——LLM 对 Prompt 前部的注意力更强。 ⚠️ 易错点 2:一定要在 Prompt 里写明"找不到就说找不到",否则模型会强行用上下文"编"一个答案,幻觉又回来了。
2. 知识图谱构建流程
2.1 知识图谱是什么:用"关系"组织知识
原理:知识图谱用图结构存储知识——节点(Node)表示实体,边(Edge)表示关系,即 (实体A) –关系–> (实体B) 的三元组结构。
┌─────────┐ 工作于 ┌─────────┐
│ 乔布斯 │ ────────> │苹果公司 │
└─────────┘ └─────────┘
│ │
创立于│ │发布
▼ ▼
┌─────────┐ ┌─────────┐
│ 1976年 │ │ iPhone │
└─────────┘ └─────────┘
通俗类比:如果说向量库是"按内容相似度排书的图书馆",知识图谱就是"画满人物关系网的白板"——它不关心两句话像不像,只关心"谁和谁是什么关系"。
知识图谱擅长什么:
- ✅ 多跳推理:"乔布斯创立了苹果 → 苹果发布了 iPhone → iPhone 的供应商是谁?"(沿关系走两步)
- ✅ 精确关系查询:"列出所有在 2024 年融资超过 1 亿的 A 轮公司"
- ✅ 全局总结:"这套软件架构的演变趋势是什么?"
- ❌ 不擅长:长篇模糊语义的相似匹配(这是向量库的强项)
2.2 本体设计:先画好"骨架"
原理:本体(Ontology)就是图谱的 schema(模式)——规定有哪些实体类型(如:人物、公司、药物、疾病)和关系类型(如:工作于、治疗、竞争、收购)。
通俗类比:盖楼前先画结构图——哪里是承重墙、哪里是房间、哪里是走廊。没有本体直接往里堆数据,图谱很快就会变成"一锅乱麻"。
设计原则:
示例本体(企业人才图谱):
实体类型:
– Person: {name, title, email}
– Company: {name, industry, founded_year}
– Position: {name}
关系类型:
– Person -[工作于]-> Company
– Person -[担任]-> Position
– Person -[毕业于]-> School
– Company -[收购]-> Company
⚠️ 易错点:先想清楚"图谱用来回答什么问题",再设计本体。为建模而建模、把本体设计得又大又全,会导致后续抽取成本爆炸、图谱利用率极低。20 个实体类型往往比 200 个更实用。
2.3 实体识别与关系抽取:LLM 辅助抽取三元组
原理:从非结构化文本中提取 (Subject, Predicate, Object) 三元组。例如从句子"乔布斯于 1976 年创立了苹果公司"提取:
(乔布斯, 创立, 苹果公司)
(乔布斯, 工作于, 苹果公司)
(苹果公司, 成立年份, 1976)
传统方法:SpaCy、Stanford CoreNLP 训练领域 NER 模型——需要标注数据、训练成本高、换领域要重训。
2024-2026 主流:LLM-based Extraction(Few-shot + Schema-guided):
from langchain.llms import OpenAI
from langchain.chains import create_extraction_chain
schema = {
"properties": [
{"name": "subject", "type": "string", "description": "主体实体"},
{"name": "relation", "type": "string", "description": "关系,取值限定为: 工作于/创立/收购/毕业于"},
{"name": "object", "type": "string", "description": "客体实体"},
],
"required": ["subject", "relation", "object"],
}
chain = create_extraction_chain(llm, schema)
res = chain.run("乔布斯于1976年创立了苹果公司。")
# 输出: [{"subject": "乔布斯", "relation": "创立", "object": "苹果公司"}]
⚠️ 易错点 1:LLM 抽取非常消耗 Token(大量输入 + 结构化输出),是 RAG 体系的"成本大头"。建议:① 优先用 Batch API 批量处理;② 对高频抽取任务微调一个小模型(如 Llama-3.1-8B),成本能降一个数量级。
⚠️ 易错点 2:LLM 抽取结果必须做校验。用正则/规则过滤明显错误,用置信度阈值丢弃低质量三元组,否则"垃圾三元组进图谱",推理结果全是错的。
2.4 图数据库选型:Neo4j / NebulaGraph / JanusGraph
原理:图数据库专门为"节点 + 关系"的高效遍历设计,提供图查询语言(Cypher / nGQL / Gremlin)。
通俗类比:图数据库是"专为查关系网优化的搜索引擎"——查询"A 的朋友的朋友里有哪些是医生",它毫秒级返回;而传统关系型数据库做同样的多表 JOIN 会慢到崩溃。
选型对比:
| Neo4j | 最流行,Cypher 查询语言,生态完善(APOC 插件) | 社区大、工具多、上手快、文档全 | 开源版(Community)不支持高可用集群 | 中小企业、团队学习成本低、快速交付 |
| NebulaGraph | 国产开源,分布式架构,兼容 OpenCypher | 性能强、支持超大规模图谱、云原生 | 学习曲线陡、生态不如 Neo4j | 百万亿级边、高并发生产环境 |
| JanusGraph | 完全开源,支持 HBase/Cassandra 等多后端 | 灵活性高、支持海量数据 | 架构复杂、需自维护存储与索引、延迟较高 | 深度定制、已有大数据组件栈 |
Neo4j Cypher 示例:
// 建节点
CREATE (p:Person {name: "乔布斯"})
CREATE (c:Company {name: "苹果公司"})
// 建关系
MATCH (p:Person {name: "乔布斯"}), (c:Company {name: "苹果公司"})
CREATE (p)-[:创立 {year: 1976}]->(c)
// 多跳查询:乔布斯创立的公司发布了哪些产品?
MATCH (p:Person {name: "乔布斯"})-[:创立]->(c:Company)-[:发布]->(prod:Product)
RETURN prod.name
⚠️ 易错点:不要用图数据库存大段文本原文。图数据库擅长关系遍历,不擅长全文检索。正确姿势是**"向量库存原文、图库存关系"**:文本检索走向量库,关系推理走图库,两者互补(详见第 4 部分架构)。
3. GraphRAG:知识图谱 + LLM 的融合
3.1 为什么需要 GraphRAG
传统 RAG 的短板:
| 全局性问题 | "这套系统架构的整体演进趋势是什么?" | ❌ 检索到的只是局部片段,拼不出全局 |
| 多跳推理 | "X 公司的股东与 Y 公司有何间接关联?" | ❌ 单轮检索无法沿关系链走两步 |
| 关系聚合 | "哪些员工同时参与了 A 和 B 两个项目?" | ❌ 向量检索难以做精确的关系交集 |
GraphRAG(图检索增强生成)把"知识图谱的关系推理能力"注入 RAG 流程,专门补上这三块短板。
3.2 微软 GraphRAG 原理:建图 + 社区摘要 + 双通道查询
微软 2024 年开源了 microsoft/graphrag,是目前 GraphRAG 事实上的标准实现。
构建阶段(Indexing):
查询阶段(Query):
- Global Search(全局搜索):针对"总结整个数据集"类问题,通过 Map-Reduce 方式把多个社区摘要汇总、压缩,再交给 LLM 生成全局性答案;
- Local Search(局部搜索):针对"某个具体实体"的问题,从该实体节点出发,沿图谱关系做多跳检索,把相关邻居节点和原文片段一起交给 LLM。
┌─────────────────────────────────┐
│ 微软 GraphRAG 流程 │
└─────────────────────────────────┘
构建: 文档切片 -> LLM抽取实体关系 -> 图数据库 -> 社区检测 -> 社区摘要
│
查询: Global Search: 汇总多个社区摘要 (Map-Reduce) ──────┘
Local Search: 实体节点出发 -> 多跳检索邻居 + 原文
# 安装与初始化(microsoft/graphrag)
pip install graphrag
# 初始化项目(会生成 settings.yaml 等配置文件)
graphrag init –root ./ragproject
# 索引构建(构建阶段:解析 -> 切片 -> LLM 建图 -> 社区摘要)
graphrag index –root ./ragproject
# 查询(全局 / 局部)
graphrag query –root ./ragproject –method global –query "这份文档的核心主题是什么?"
graphrag query –root ./ragproject –method local –query "乔布斯创立了哪些公司?"
⚠️ 易错点:GraphRAG 的索引构建非常昂贵(每个片段都要 LLM 抽三元组 + 生成社区摘要),构建时间以"小时"计是常态。先在小数据集上跑通,确认效果后再全量构建;不要一上来就对 100 万文档跑索引。
3.3 其他结合模式:ToG、Vector + Graph 混合
除微软 GraphRAG 外,还有几种主流结合模式:
| ToG(Think-on-Graph) | LLM 在推理过程中每一步都到图上执行搜索,像人一样沿着关系路径边想边走 | 需要逐步推理的复杂问答 |
| Vector + Graph 混合 | 先用向量检索找到起始节点,再沿图遍历发现关联节点 | 大多数生产落地场景,性价比最高 |
| Graph 作为验证层 | 向量检索出候选答案后,用图谱验证实体关系是否成立 | 提高答案可信度、过滤幻觉 |
Vector + Graph 混合检索伪代码:
def hybrid_retrieve(question):
# 1. 向量检索:找到相关文本片段及其中的实体
vec_hits = vector_store.search(question, top_k=20)
# 2. 从片段中识别实体(或用 LLM 抽取)
seed_entities = extract_entities(question) + extract_entities_from(vec_hits)
# 3. 图遍历:从种子实体出发,多跳扩展关联节点
graph_hits = neo4j.query(
"MATCH (e) WHERE e.name IN $entities "
"MATCH (e)-[r*1..2]-(neighbor) RETURN neighbor, r",
entities=seed_entities,
)
# 4. 合并两类结果 -> 组装 Prompt -> LLM
return assemble_prompt(question, vec_hits, graph_hits)
落地建议:生产项目优先选 Vector + Graph 混合——它不需要像微软 GraphRAG 那样构建昂贵的社区摘要,改造现有 RAG 系统的成本最低。
4. 从零搭建实现步骤清单(RAG + KG 混合架构)
下面是一份"照做即可"的 10 步清单,架构同时包含向量库(RAG)与图库(KG),是标准的企业知识库形态。
┌─────────────────────────────────────────────────────────────┐
│ 混合知识库架构总览 │
│ │
│ PDF/MD/HTML ──解析──> 切片 │
│ ├──> 向量化 -> 向量库(Milvus) │
│ │ ▲ │
│ │ └── 混合检索(向量+BM25) │
│ ├──> LLM抽取三元组 -> 图库(Neo4j) │
│ │ ▲ │
│ │ └── 图遍历(多跳) │
│ └──> 原文存储 -> 引用溯源 │
│ │
│ 用户提问 -> 混合检索 + 重排序 -> Prompt组装 -> LLM -> 答案│
└─────────────────────────────────────────────────────────────┘
步骤 1:明确问题域与数据盘点(最重要的一步)
- 列出知识库要回答的 10~20 个典型问题(如"报销流程""某型号产品参数""离职员工交接");
- 盘点数据源:哪些是 PDF、哪些是 Word/HTML、哪些需要 OCR;
- 判断每类问题该走向量检索(语义)还是图谱推理(关系)——这一步决定后面所有架构选型。
步骤 2:环境准备
- Python 3.10+,安装 langchain、unstructured、sentence-transformers、pymilvus、neo4j;
- 准备一个 Embedding 模型(推荐 BGE-M3)、一个 Reranker(BGE-Reranker)、一个 LLM API(GPT-4o / Claude / DeepSeek 均可)。
步骤 3:文档解析与清洗
- 用 Unstructured/Marker 解析全部文档;
- 清洗:去页眉页脚、去重复空白、修正 OCR 错字;
- 抽样 10% 人工检查解析质量,重点看表格和公式。
步骤 4:切片与向量化
- 结构切片(Markdown 标题/段落)为主,设置 chunk_size=5121024,overlap=50100;
- 用 Embedding 模型批量向量化,保存原始文本、来源、页号等元数据。
步骤 5:写入向量库
- 建 Collection(维度与模型一致,metric=COSINE);
- 批量写入;建立主键索引(用文档 ID 方便后续更新删除)。
步骤 6:知识图谱抽取与入库
- 基于第 2.2 节定义的本体,用 LLM + Schema-guided Prompt 抽取三元组;
- 实体消歧(同名实体合并、别名映射,如"苹果公司"= "Apple Inc.");
- 写入 Neo4j,建立唯一约束与索引: CREATE CONSTRAINT unique_person IF NOT EXISTS FOR (p:Person) REQUIRE p.name IS UNIQUE;
CREATE INDEX company_name_idx IF NOT EXISTS FOR (c:Company) ON (c.name);
步骤 7:构建混合检索器
- 向量检索(语义)+ BM25 关键词检索(精确)双路召回,按比例融合(如 0.7:0.3);
- 图检索:从问题中提取种子实体,做 1~2 跳图遍历;
- 三者结果合并去重。
步骤 8:接入重排序
- 用 Reranker 对候选重新打分,只保留 Top-5 交给 LLM;
- 在验证集上对比"有无 Reranker"的准确率差异,量化收益。
步骤 9:Prompt 组装与生成
- 结构化模板:系统指令(只依据资料回答 + 找不到就说找不到)+ 检索片段(带来源)+ 问题;
- 接入 LLM 生成答案,强制输出引用标注。
步骤 10:评估与上线
- 用 Ragas / DeepEval / LangSmith 评估(指标见第 5.3 节);
- 构建 CI 式回归集:每次改切片策略/换模型,先跑一遍评估集,分数不降再上线;
- 上线后接监控:记录检索命中率、无引用回答占比、用户反馈。
⚠️ 易错点(贯穿全程):
- 不要跳过步骤 1——很多项目失败不是技术问题,是"没想清楚要回答什么问题";
- 不要一次性全量处理——先用 20~50 份文档跑通全流程,再横向扩展;
- 每一步都要留"可回滚":向量库用 version 字段区分 Embedding 模型版本,图谱保留构建脚本与原始抽取结果,方便重建。
5. 关键注意点:数据质量、更新维护、评估、成本、幻觉
5.1 数据质量(Garbage In, Garbage Out)
核心原则:解析错误的 PDF 是 RAG 失败的首要原因。
| 扫描件未 OCR | 检索全是乱码,答案全错 | 解析流程强制 OCR + 抽样检查 |
| 表格行列错乱 | 检索到残缺表格,模型读不懂 | 表格整体切片,专表专用解析器 |
| 过期/矛盾文档 | 新旧版本答案互相打架 | 文档元数据带版本号 + 生效日期 |
| 重复文档 | 检索结果冗余,浪费 token | 入库前去重(hash + 语义相似度) |
⚠️ 预警:"数据治理"花的时间应该占整个项目 60% 以上。数据没治理好之前,模型换得再强都是白搭。
5.2 更新维护
- 向量库:支持 Upsert(更新插入)——文档更新时按文档 ID 覆盖旧向量,删除时按 ID 删除;
- 知识图谱:
- 实体消歧(Entity Resolution)非常难。新数据入库时,要判定 "Apple Inc." 和"苹果公司"是否指向同一节点——建议维护别名表(Alias Table),先查表再建节点;
- 关系时效性:关系要带时间属性(如"任职于 2019-2023"),避免把历史关系当现状;
- 增量抽取:只对新增文档做抽取,不要每次全量重建图谱。
5.3 评估指标
评估分两层——检索层和生成层,推荐工具:Ragas、DeepEval、LangSmith。
| 检索层 | Hit Rate(命中率) | 正确答案是否被召回 | 鱼是不是捞进网里了 |
| 检索层 | MRR(平均倒数排名) | 正确答案排得多靠前 | 鱼是不是捞在网口附近 |
| 检索层 | NDCG | 排序质量整体评估 | 鱼的大小是否按价值排好 |
| 生成层 | Faithfulness(忠实度) | 答案是否忠于检索资料 | 有没有脱离资料编瞎话 |
| 生成层 | Answer Relevance(答案相关性) | 答案是否切题 | 答非所问没有 |
# Ragas 评估示例
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
result = evaluate(
dataset=test_dataset, # 包含 question / answer / contexts / ground_truth
metrics=[faithfulness, answer_relevancy, context_precision],
)
print(result)
⚠️ 预警:评估集必须真实——用线上真实用户问题构建,不要用"感觉像"的样例。评估集要持续扩充,每次业务变化都往里加新问题。
5.4 成本控制
| LLM 抽取三元组(大头) | Batch API 批量处理;针对抽取微调小模型(Llama-3.1-8B 级别);只抽"能回答问题"的实体 |
| Embedding 调用 | 本地部署开源模型(BGE 系列),一次向量化长期复用 |
| 每次问答的 token | Prompt 压缩(LLMLingua)、限制 Top-K、限制片段长度 |
| 索引构建 | 增量构建,不要频繁全量重建 |
⚠️ 预警:先估算再动手。100 万文档全量抽取三元组,按 LLM API 单价,成本可能在数千到数万美元量级——先小规模试跑,量出单文档成本,再乘以总量做预算。
5.5 幻觉控制
| 强制引用 | Prompt 要求"每个结论必须标注来源",无来源不输出 |
| 找不到就承认 | 明确指令:资料中没有的,回答"未找到",禁止编造 |
| 图谱验证 | 用知识图谱校验答案中的实体关系是否真实存在 |
| 答案溯源 UI | 前端展示引用来源(文档名 + 页号 + 高亮片段),用户可自行验证 |
| 低置信度拦截 | 检索相关性低于阈值时,直接提示"资料不足",不硬答 |
6. 真实实现案例(2024-2026)
案例 1:微软开源 GraphRAG(2024)
- 场景:分析庞大的内部技术文档与开源社区数据,回答"架构演进趋势"等全局性问题;
- 实现:利用 LLM 自动构建知识图谱索引(抽取实体关系 + 社区检测 + 社区摘要),查询时 Global/Local 双通道;
- 效果:回答全局性问题时准确率远超传统 RAG;验证了"关系型知识"必须用图结构承载;
- 启示:如果你的核心需求是"总结整库""梳理全局脉络",GraphRAG 是首选。
案例 2:某头部电商供应链知识库(2025 典型实践)
- 需求:客服与运营要查询数百万 SKU 的复杂属性及关联关系,如"适合敏感肌且不含尼泊金酯的防晒";
- 架构:
- 知识图谱(NebulaGraph):存储"商品 – 属性 – 品牌 – 供应商"关系,支撑高并发图查询;
- RAG(向量库):存储商品详情页的长文本描述;
- ToG(Think-on-Graph):用户提问后,模型先在图谱中过滤出"敏感肌适用"和"不含尼泊金酯"的实体子集,再检索具体产品详情;
- 效果:把"多条件属性组合筛选"这类传统 RAG 难以处理的问题,变成可精确求解的图查询;
- 启示:强结构化、多条件组合的业务(电商、医疗、供应链),图 + 向量的混合架构几乎是必选项。
案例 3:法律合同审查助手(2024-2025)
- 技术栈:LangChain + Neo4j + Claude 3.5;
- 流程:
- 上传合同 PDF,解析、切片;
- LLM 抽取关键实体与条款:甲方、乙方、金额、违约责任、生效日期,构建合同子图;
- 用户提问:"如果甲方违约,乙方有哪些救济途径?"
- 系统沿图谱找到"违约责任"节点,关联对应条款原文,喂给 LLM 生成带引用答案;
- 效果:从"全文找条款"变为"图谱定位 + 条款直引",响应速度和准确性都大幅提升;
- 启示:专业文档(合同、法律、病历)最适合"抽取结构化子图 + 原文溯源"的组合——图谱负责定位,向量库负责供原文。
案例共性总结:
核心需求是"相似文本召回" -> 纯 RAG(向量库)就够
核心需求是"关系推理/全局总结" -> 引入知识图谱(GraphRAG / Vector+Graph)
数据强结构化、属性组合筛选 -> 图数据库做主力,向量库做补充
7. 常见问题速查表(FAQ)
| 1 | 什么时候只需要 RAG,不需要知识图谱? | 需求是"找相似文档/回答片段级问题"时,纯 RAG 足够;出现"多跳推理、全局总结、属性组合筛选"需求再加图谱 |
| 2 | 切片多大合适? | 经验值 5121024 token,overlap 50100;以"语义完整"为先,表格/代码/JSON 整块保留 |
| 3 | Embedding 模型怎么选? | 中文首选 BGE-M3(多语言多粒度);英文长文档 jina-v2;代码 voyage-code-2;查询与文档必须同一模型 |
| 4 | 向量数据库选哪个? | 原型用 Chroma/FAISS;生产海量高并发用 Milvus;不想运维用 Pinecone |
| 5 | 重排序真的有用吗? | 是。提升 RAG 准确率性价比最高的单点改造,强烈建议必做 |
| 6 | 知识图谱构建好贵,怎么省钱? | 只抽"能回答问题"的实体;用 Batch API;对高频抽取微调 8B 小模型;增量构建不重建 |
| 7 | 实体消歧怎么做? | 维护别名表("苹果公司"="Apple Inc.");建唯一约束 + 先查后建;关系带时间戳 |
| 8 | 怎么评估我的知识库好不好? | 检索层看 Hit Rate / MRR;生成层看 Faithfulness / Answer Relevance;用 Ragas/DeepEval 跑真实问题集 |
| 9 | 如何防止模型编答案? | 强制引用标注;"找不到就说找不到";低置信度拦截;图谱验证;前端展示来源高亮 |
| 10 | 微软 GraphRAG 和普通 RAG 什么关系? | GraphRAG 是 RAG 的进阶版:在向量检索基础上增加"图构建 + 社区摘要",擅长全局性问题;成本更高,按需选用 |
| 11 | 文档更新了怎么办? | 向量库按文档 ID Upsert/删除;图谱只对增量文档抽取;新旧版本用元数据区分 |
| 12 | 中文文档解析乱码怎么办? | 扫描件先 OCR(hi_res/ocr_only);表格用专用解析策略;解析后必须抽样人工检查 |
| 13 | 知识库检索不到答案怎么办? | 先查数据是否入库;再查切片是否切碎语义;然后查混合检索比例;最后查 Reranker 是否过滤掉了正确答案 |
| 14 | 向量维度怎么定? | 由 Embedding 模型决定(BGE-M3=1024),改模型必须重建 Collection |
| 15 | 要不要用 GraphRAG 的 Global Search? | 只有"总结全库、梳理全局"类需求才用;日常问答用 Local Search 或 Vector+Graph 混合,成本低得多 |
结语
知识库,不再是"切片 + 向量化"的单点技术,而是一套组合拳:
最后送你一句口诀:
先问"要回答什么问题",再选"向量、图谱还是混合";数据不好不建库,评估不过不上线。
如果本文对你有帮助,欢迎收藏、转发。有任何搭建过程中的问题,欢迎在评论区交流。
网硕互联帮助中心



评论前必须登录!
注册