外部知识库集成:连接结构化与非结构化知识
用户查询 │ ▼ 查询路由 / 意图识别 │ ├─ 结构化数据通道 ──► NL2SQL 引擎 ──► 关系型数据库 │ └─ 非结构化数据通道 ──► 检索器 ──► 向量数据库 / 搜索引擎 │ ▼ 结果融合 & 重排序 │ ▼ 上下文增强提示词 → 大模型 → 最终回复
本教程将分别拆解结构化与非结构化知识库的集成方法,并展示如何将两者融合。
## 非结构化知识库集成
### 文档解析与分块策略
将 Word、PDF、Markdown 等文档转换为纯文本是第一步。常用工具有 PyMuPDF、Unstructured、LangChain 的 Document Loader。
分块(Chunking)直接影响检索质量。基本原则:
- **保持语义独立**:尽量在自然段落或标题边界处切断。
- **注意长度限制**:每个 chunk 的 token 数应小于 embedding 模型上限,并留出上下文窗口余量。
- **引入重叠**:相邻 chunk 之间保留 10%~20% 的重叠文本,避免关键信息被切断。
示例(Python 伪代码):
```python
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""]
)
chunks = splitter.split_documents(documents)
向量化与存储
将每个文本块通过 Embedding 模型(如 text-embedding-3-small、BGE、Jina 等)转换为高维向量,存入向量数据库(如 Milvus、Pinecone、Weaviate、Chroma)。
关键考量:
- 选择与领域匹配的 Embedding 模型:通用模型可能对垂直领域术语不敏感,有时需进行微调。
- 元数据过滤:存储向量时同时保存文档来源、日期、章节等元数据,便于后续精确筛选。
检索与重排序
基础检索通常采用近似最近邻(ANN)搜索。为了提升准确率,常引入两阶段排序:
- 粗排(向量召回):快速获取 Top-K 候选块。
- 精排(Reranker):使用基于 Transformer 的交叉编码器(如 Cohere Rerank、bge-reranker)对候选块重新打分,选出最相关的 Top-N。
示例(使用 LangChain 集成 Reranker):
from langchain.retrievers import ContextualCompressionRetriever
from langchain_community.document_compressors import CohereRerank
compressor = CohereRerank(model="rerank-multilingual-v2.0", top_n=3)
retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
compression_retriever = ContextualCompressionRetriever(
base_retriever=retriever, document_compressor=compressor
)
relevant_docs = compression_retriever.get_relevant_documents(query)
结构化知识库集成
结构化知识的查询难点在于:用户使用自然语言提问,而数据库需要 SQL 或特定查询语法。因此需要 NL2SQL(自然语言转 SQL)或者 Text-to-API 能力。
NL2SQL 基本流程
- 获取 Schema 上下文:将表名、字段名、字段描述、样例数据拼接进提示词。
- 生成 SQL:大模型根据用户问题和 Schema 写出查询语句。
- 安全执行与容错:对生成的 SQL 进行语法检查、权限控制,执行并捕获异常,可重试修正。
- 结果解释:将查询结果连同原始问题交给模型,生成自然语言答案。
示例提示词模板:
你是一个将自然语言转换为 SQL 查询的助手。
数据库为 SQLite,包含以下表格:
- products(id, name, category, price, stock)
- orders(id, product_id, quantity, order_date)
用户问题:上个月销量最高的商品是哪些?
请生成 SQL 查询语句。
Text-to-API 的扩展
很多结构化知识通过 REST API 或 GraphQL 提供。可以训练或提示大模型生成正确的 API 调用参数。关键是将 API 文档(端点、参数说明、示例)缩减后写入上下文,让模型学会在什么场景调用哪个接口。
统一融合:多源检索与混合推理
真正的“连接结构化与非结构化知识”意味着在一次查询中同时利用两者。例如用户问:“对比文档中提到的 A 产品环保标准与数据库中实际检测数据”。
查询路由与并行检索
设计一个意图分类器(可以是一个小型语言模型或基于规则的调度器),将用户问题分配给不同通道:
- 若涉及数据统计、对比趋势 → 走 NL2SQL 通道。
- 若需要解释概念、查找政策 → 走文档搜索通道。
- 若两者皆有 → 同时触发,最后融合结果。
结果融合与上下文编排
从不同通路获得的结果格式不同:数据库返回表格,文档检索返回文本块。需要将它们整合为结构化的提示词:
[结构化数据]
上个月产品销售排行:
- 产品A: 1200 件
- 产品B: 980 件
[相关文档片段]
文档1:产品A 已升级环保包装,符合欧盟标准...
文档2:本月质检报告显示产品A 重金属含量低于阈值...
请基于以上信息回答:产品A 是否满足出口欧盟的条件?
使用 LangChain 或 LlamaIndex 构建融合链
这些框架提供了工具编排和 Agent 机制,可定义“数据源工具”并让模型自主决定调用顺序。例如 LangChain 的 create_sql_query_chain 与 create_retrieval_chain 结合 Agent 使用。
生产级部署注意事项
权限与数据安全
不同用户对数据库表的访问权限不同。NL2SQL 查询必须纳入权限上下文,避免注入攻击或越权访问。解决方案:
- SQL 语句使用参数绑定,禁止拼接用户输入。
- 在生成 SQL 前注入用户可访问表的范围约束。
- 对文档检索也需实现基于元数据的访问控制列表(ACL)。
索引同步与更新
知识库会持续更新。需要设计管道以监听数据变化并增量更新向量索引。可以使用消息队列(Kafka、RabbitMQ)或定时扫描 CDC 日志。
缓存与成本控制
对于高频相似问题,可对最终答案或中间检索结果做语义缓存(如 GPTCache),降低重复计算和 API 费用。
评测体系
不能仅凭主观感受判断集成效果。建议构建包含“问题-预期答案-依据来源”的测试集,自动化评估:
- 答案准确性(有据可查 vs. 幻觉)
- 检索召回率和精确率
- SQL 语法正确率及执行结果匹配度
实践案例:快速原型搭建
这里给出一个最小可运行思路(以 Python + LangChain + Chroma + SQLite 为例):
- 准备非结构化知识:将产品手册 Markdown 文件放入文件夹,用
DirectoryLoader加载。 - 建立向量检索引擎:使用
OpenAIEmbeddings+Chroma。 - 准备结构化数据:创建一个 SQLite 数据库
business.db,导入 products 表。 - 构建融合代理:
from langchain.agents import initialize_agent, Tool from langchain_experimental.sql import SQLDatabaseChain db = SQLDatabase.from_uri("sqlite:///business.db") sql_tool = Tool( name="business_db", func=SQLDatabaseChain.from_llm(llm, db).run, description="查询商品库存和销售数据。输入需是完整自然语言问题" ) retriever_tool = Tool( name="document_search", func=retriever.get_relevant_documents, description="搜索产品手册、政策文档。输入需是关键词或句子" ) agent = initialize_agent( tools=[sql_tool, retriever_tool], llm=llm, agent="zero-shot-react-description", verbose=True )