在为中文企业文档构建检索增强生成(RAG)系统时,后端工程师往往会遭遇标准英文技术栈的“水土不服”:中文文本缺乏天然的空格分词边界,合同、制度与技术规范包含大量专业术语及条款编号,而通用多语言 Embedding 模型在面对中文时容易出现字词切分碎片化(Token 膨胀与语义失真),显著降低检索召回率。
百川智能(Baichuan AI)基于海量中文语料训练了系列基础模型,其中 Baichuan4 与 Baichuan3-Turbo 在中文推理和知识总结方面表现出色,而 Baichuan-Text-Embedding(1024 维向量输出)专门针对中文稠密检索进行了深度优化。百川 API 在 https://api.baichuan-ai.com/v1 提供了兼容 OpenAI 规范的 REST 接口,后端工程师可以使用 LangChain 与 LlamaIndex 的原生社区组件或 OpenAI 兼容适配器无缝完成集成。
本文针对中文企业知识库场景,梳理完整的架构选型、文档分块策略以及生产级 Python 实现。
1. 准备工作与 API 接口规范
在开始搭建管道前,需在百川智能开发者平台创建 API Key,并在后端服务环境变量中完成配置:
export BAICHUAN_API_KEY="your-baichuan-api-key"
安装核心框架及向量数据库依赖:
pip install langchain-community langchain-core langchain-openai \
llama-index-core llama-index-llms-openai-like llama-index-embeddings-openai \
chromadb pydantic
百川智能核心服务端点与参数规范:
- Base URL:
https://api.baichuan-ai.com/v1 - 对话接口:
POST /v1/chat/completions(模型可选Baichuan4、Baichuan4-Air、Baichuan3-Turbo) - 向量化接口:
POST /v1/embeddings(模型固定为Baichuan-Text-Embedding)
2. 中文企业文档的分块策略与 Token 限制
常见的英文分块逻辑习惯按空格与单词切分,这会直接割裂中文从句、财务数字与法规条款。中文文档切分必须优先识别段落换行与中文全角标点符号(。、!、?、;)。
特别需要注意:Baichuan-Text-Embedding 模型的单次输入长度上限为 512 个 Token。为避免因文本超长导致接口返回 400 Bad Request,在分块时建议将单块字符数控制在 350 至 400 字之间,重叠长度保留 40 至 50 字,以确保语义连贯且不超出向量化限制。
from langchain_text_splitters import RecursiveCharacterTextSplitter
def create_chinese_document_splitter(chunk_size: int = 400, chunk_overlap: int = 50):
"""
针对中文企业合同、规章制度和技术规范优化的递归文本切分器
"""
return RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=[
"\n\n", # 优先段落切分
"\n", # 换行切分
"。", # 句号
"!", # 感叹号
"?", # 问号
";", # 分号(常见于合同与政策分项条款)
" ", # 空格
"" # 单字兜底
]
)
3. LangChain 集成方案(LCEL + 稠密检索)
在 LangChain 中,既可以使用 langchain_community 中的 ChatBaichuan 和 BaichuanTextEmbeddings,也可以使用 langchain_openai 配合百川的 openai_api_base。在生产级服务中,推荐采用兼容适配模式,以便充分利用流式传输(Streaming)、异步调用及 LangChain 表达式语言(LCEL)。
完整 LangChain RAG 流程实现
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.documents import Document
# 1. 初始化百川大模型与向量化接口
BAICHUAN_BASE_URL = "https://api.baichuan-ai.com/v1"
api_key = os.environ.get("BAICHUAN_API_KEY")
embeddings = OpenAIEmbeddings(
model="Baichuan-Text-Embedding",
openai_api_base=BAICHUAN_BASE_URL,
openai_api_key=api_key,
check_embedding_ctx_length=False # 禁用针对 OpenAI 模型的固定长度预检
)
llm = ChatOpenAI(
model="Baichuan4",
openai_api_base=BAICHUAN_BASE_URL,
openai_api_key=api_key,
temperature=0.1, # 规避幻觉,保持制度回答严谨
streaming=True
)
# 2. 模拟中文企业文档录入
raw_docs = [
Document(
page_content="第十三条 采购付款管理规范:对于超过人民币50万元的设备采购合同,供应商必须提供全额增值税专用发票后,财务部方可安排第二笔30%的进度款支付。项目初验合格前,累计付款比例不得超过合同总额的80%。",
metadata={"source": "采购管理制度2024.pdf", "department": "Finance"}
),
Document(
page_content="第二十四条 差旅报销标准:研发部门员工赴一类城市(北京、上海、广州、深圳)出差,住宿费上限为每人每天550元,餐饮补贴为每人每天120元,按实际出差自然天数核算。",
metadata={"source": "员工差旅规范v3.pdf", "department": "HR"}
)
]
splitter = create_chinese_document_splitter(chunk_size=400, chunk_overlap=40)
chunked_docs = splitter.split_documents(raw_docs)
# 3. 构建向量索引库
vectorstore = Chroma.from_documents(
documents=chunked_docs,
embedding=embeddings,
collection_name="enterprise_knowledge"
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
# 4. 设计符合企业合规要求的中文提示词模板
system_prompt = (
"你是一个严谨的企业知识库助手。请严格根据以下检索到的参考文档回答用户的问题。\n"
"如果参考文档中没有提供明确信息,请直接回答'依据现有内部制度未查到相关说明',严禁虚构或主观臆测。\n\n"
"【参考文档】:\n{context}"
)
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{question}")
])
def format_docs(docs):
return "\n\n".join(f"[{doc.metadata.get('source', '未知')}] {doc.page_content}" for doc in docs)
# 5. 编排 LCEL 调用链
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 6. 查询执行
if __name__ == "__main__":
query = "50万元以上的设备采购合同,第二笔进度款支付的前提条件是什么?"
response = rag_chain.invoke(query)
print("生成回答:\n", response)
4. LlamaIndex 集成方案(OpenAILike 与自定义分块)
在 LlamaIndex(v0.10+)体系中,由于核心模块拆分,且默认 OpenAI 类会验证官方模型名单,连接百川 API 推荐使用 llama-index-llms-openai-like 中的 OpenAILike,向量化则使用配置了 api_base 的 OpenAIEmbedding。
完整 LlamaIndex RAG 流程实现
import os
from llama_index.core import Settings, VectorStoreIndex, Document
from llama_index.core.node_parser import SentenceSplitter
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.openai import OpenAIEmbedding
# 1. 全局配置百川模型组件
BAICHUAN_BASE_URL = "https://api.baichuan-ai.com/v1"
api_key = os.environ.get("BAICHUAN_API_KEY")
Settings.llm = OpenAILike(
model="Baichuan4",
api_base=BAICHUAN_BASE_URL,
api_key=api_key,
is_chat_model=True,
temperature=0.1
)
Settings.embed_model = OpenAIEmbedding(
model="Baichuan-Text-Embedding",
api_base=BAICHUAN_BASE_URL,
api_key=api_key
)
# 2. 配置中文句式切分节点解析器
Settings.node_parser = SentenceSplitter(
chunk_size=400,
chunk_overlap=40,
separator="。"
)
# 3. 载入企业制度文档并构建索引
documents = [
Document(
text="第七章 核心数据安全防护要求:涉及生产数据库导出操作必须提前48小时提交工单,经信息安全负责人与业务分管领导双重审批后方可在堡垒机中执行。导出文件须启用国密SM4算法加密并在3天内自动销毁。",
metadata={"category": "SecOps", "author": "Security Committee"}
)
]
index = VectorStoreIndex.from_documents(documents)
# 4. 构建检索查询引擎
query_engine = index.as_query_engine(
similarity_top_k=2,
system_prompt=(
"你代表企业信息化与安全管理委员会回答技术咨询。"
"请严格根据检索到的规范作答,列出具体审核流程及技术加密要求。"
)
)
if __name__ == "__main__":
response = query_engine.query("导出生产环境数据库需要满足哪些审批与安全条件?")
print("查询结果:\n", str(response))
5. 企业级后端工程落地实践
在企业生产环境中交付基于百川 API 的 RAG 知识库,还需关注以下关键架构细节:
1. 混合检索(向量 + BM25)提升准确度
向量语义检索能够理解近义表达,但在面对精准的条款编号(如“第十五条第三款”)、专有缩写或特定项目编码时容易泛化。后端应通过 LangChain 的 EnsembleRetriever 或 LlamaIndex 的混合检索,将 Baichuan-Text-Embedding 的稠密检索与基于 BM25 / Elasticsearch 的稀疏检索按权重融合。
2. 向量化前置长度防御
由于 Baichuan-Text-Embedding 严格限制 512 Token,在处理包含 OCR 噪点、超长无标点代码或数据表格的非结构化文档时,需在切片后增加 Token 长度前置拦截校验逻辑,超长文本自动执行二级硬切分,防止批量入库事务中途崩溃。
3. API 速率限制与重试机制
企业知识库初期全量建库涉及高并发 API 调用。百川开放平台对请求频率(RPM/TPM)设有限制。后端批量处理程序应当配合 tenacity 实现指数退避重试,或采用异步并发令牌桶(Token Bucket)进行平滑流量整形。
4. 严控模型幻觉与元数据溯源
在内部制度或合规问答场景中,回答错误往往比回答不知道危害更大。提示词中需明确约定拒答规则,并通过返回文档的 metadata(包含文件名、页码、所属部门)构建来源引用链,让员工可以直接核验制度原文。