ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于腾讯云向量数据库的企业级RAG知识库落地实战

基于腾讯云向量数据库的企业级RAG知识库落地实战

1. 为什么企业都在聊 RAG 知识库,它到底解决了什么痛点

这两年跟不少做企业数字化的朋友聊天,话题绕来绕去最后总会落到同一个地方:公司内部文档太多,找东西太费劲。产品手册、售后工单、合同模板、会议纪要、技术方案,散落在网盘、Wiki、邮件附件、聊天记录里,新人上手靠问,老人离职带走经验,跨部门协作全靠“你发我一份”。更麻烦的是,通用大模型虽然能聊天,但你问它“我们公司差旅报销标准是多少”,它要么一本正经地胡说,要么告诉你“我无法获取内部信息”。

RAG(Retrieval-Augmented Generation,检索增强生成)就是冲着这个场景来的。它的核心思路不复杂:先把企业内部的文档切碎、转成向量存起来,用户提问时先去向量库里捞出最相关的几段原文,再把这几段原文和问题一起塞给大模型,让大模型基于真实资料来回答。这样一来,答案有出处、可追溯,还能随时更新知识库而不需要重新训练模型。

我这次要分享的,是一套基于腾讯云向量数据库搭建企业知识库的完整落地过程。选腾讯云向量数据库的原因很直接:企业环境里,自建 Milvus 或 Chroma 虽然灵活,但运维成本、权限管理、数据备份、横向扩容这些事全得自己扛;而腾讯云向量数据库提供托管服务,支持标量过滤、混合检索、多副本容灾,对于没有专职向量数据库运维团队的公司来说,能省掉大量脏活累活。整套方案用 Python 串联,从文档解析、切片、向量化、入库,到检索、重排、生成回答,每一步我都会给出可复现的代码和参数说明。

这篇文章适合谁看?如果你是后端开发、算法工程师、技术负责人,正在评估企业知识库方案,或者已经动手但卡在切片策略、检索召回率、大模型幻觉这些坑里,那接下来的内容应该能帮你少走不少弯路。即使你之前只写过一点 Python,跟着步骤也能把最小可用版本跑起来。

2. 整体架构设计与技术选型背后的取舍

2.1 从“文档进、答案出”拆解 RAG 的完整链路

很多人第一次做 RAG,容易把它想成“向量库 + 大模型”两个盒子,实际落地时会发现中间环节远比想象中多。我把整条链路拆成六个阶段,每个阶段都有明确的输入输出和失败模式。

第一阶段是文档接入。企业文档格式极其杂乱,PDF、Word、Excel、PPT、Markdown、HTML、扫描件都有。PDF 里还分文本型和图片型,图片型必须走 OCR。这一步的目标是把所有格式统一转成纯文本,同时保留标题层级、表格结构、页码等元数据,后面切片和溯源都要用。

第二阶段是文本切片。切片不是简单按字数切,那样会把一个完整语义单元拦腰截断。常见策略有按固定长度重叠切、按标题层级递归切、按语义相似度切。企业知识库里,我通常用“标题层级 + 固定长度重叠”的混合策略,既保留文档结构,又控制单块长度。

第三阶段是向量化。把每个文本块通过 Embedding 模型转成高维向量。这里要选模型:中文场景下,腾讯云提供的文本嵌入模型、BGE 系列、M3E 系列都是常见选择。维度越高表达能力越强,但存储和检索成本也越高。企业知识库一般 768 维或 1024 维就够用。

第四阶段是向量入库。把向量和原始文本、元数据一起写入腾讯云向量数据库。这里要设计集合结构、索引类型、副本数、分片数。索引类型直接影响检索速度和召回率,后面会详细讲。

第五阶段是检索与重排。用户提问先向量化,然后在向量库里做相似度搜索,通常召回 Top 20 到 Top 50,再用重排模型精排到 Top 3 到 Top 5。重排这一步对最终答案质量影响极大,很多团队省掉它,结果就是“检索到了但没排到前面”。

第六阶段是生成回答。把精排后的原文片段和用户问题组装成 Prompt,交给大模型生成答案。Prompt 里要明确要求“只基于给定资料回答,资料中没有就说不知道”,并附上引用来源。

2.2 为什么选腾讯云向量数据库而不是自建

自建向量库的方案我试过不少,Milvus、Qdrant、Weaviate、Chroma 都跑过。小规模验证阶段自建确实快,但一到企业生产环境,问题就来了:数据量上千万条后,索引重建慢、内存吃紧、查询延迟抖动;多租户隔离要自己写;备份恢复要自己搭;监控告警要自己配。团队如果没有专职的基础设施工程师,这些事会拖慢整个项目进度。

腾讯云向量数据库是托管服务,几个关键能力对企业场景很实用。第一是弹性扩容,数据量涨了直接调分片和副本,不用停机迁移。第二是标量过滤,企业知识库经常要按部门、密级、时间范围过滤,比如“只检索研发部 2024 年之后的文档”,这个能力是刚需。第三是混合检索,支持向量相似度和关键词检索结合,对专有名词、产品型号这类精确匹配场景提升明显。第四是多副本容灾,数据可靠性有保障,不用自己写同步逻辑。

成本方面,托管服务看起来单价高,但算上人力运维、故障处理、扩容改造的隐性成本,中等规模以上企业用托管通常更划算。当然,如果数据量很小、预算极紧、团队又有运维能力,自建也不是不行,这个后面在常见问题里会再展开。

2.3 大模型选型的三个现实约束

生成环节用哪个大模型,不能只看榜单分数。企业场景有三个硬约束:上下文长度、调用成本、数据合规。

上下文长度决定你一次能塞多少参考资料。主流模型现在动辄 32K、128K,但实际用的时候,塞太多反而会稀释关键信息,导致模型“抓不住重点”。我的经验是,精排后保留 3 到 5 个片段,总长度控制在 4K 到 8K token 之间,效果最稳。

调用成本要算清楚。如果知识库每天几千次查询,每次输入几千 token,一个月下来费用不小。可以在检索层做缓存,相似问题直接命中缓存答案,减少大模型调用。

数据合规方面,涉及敏感信息的场景,优先考虑私有化部署的模型,或者使用云厂商提供的企业级 API,确保数据不被用于训练。这一点在选型阶段就要和法务、安全团队对齐,不要等上线了再返工。

3. 核心细节解析与实操要点

3.1 文档解析:别让脏数据毁掉整个知识库

文档解析是 RAG 里最容易被低估的环节。我见过太多项目,向量库建得漂漂亮亮,但检索效果差,最后追查发现是 PDF 解析出来一堆乱码、页眉页脚混进正文、表格被拆得七零八落。

PDF 解析推荐用pdfplumber或PyMuPDF,前者对表格支持好,后者速度快。Word 用python-docx,PPT 用python-pptx,HTML 用BeautifulSoup。扫描件必须走 OCR,腾讯云 OCR 或 PaddleOCR 都可以,中文场景下 PaddleOCR 的准确率我实测比较稳。

解析时要保留元数据:文件名、页码、标题层级、创建时间、所属部门。这些元数据后面既用于过滤,也用于答案溯源。用户看到答案时能点开原文,信任度会高很多。

注意:页眉页脚、水印、广告位这些噪声一定要在解析阶段去掉。我通常用规则过滤:连续出现在多页相同位置的短文本,大概率是页眉页脚。

3.2 切片策略:长度、重叠与语义完整性的平衡

切片参数没有万能值,但有一套推导逻辑。假设你的 Embedding 模型最大输入是 512 token,那单块长度就不能超过这个值,否则会被截断。中文里 1 个 token 大约对应 1.5 到 2 个汉字,所以 512 token 大约是 700 到 1000 字。

我通常设单块 500 字,重叠 100 字。重叠的目的是防止关键信息刚好落在切割边界上,被切成两半后两边都不完整。重叠比例一般取 10% 到 20%。

按标题层级切是更优的策略。先用正则或解析库识别出“第一章”“1.1 节”这类标题,把文档切成树状结构,再对每个叶子节点内的长文本做固定长度切分。这样每个块都带有完整的章节路径,检索时语义更清晰。

def split_by_heading(text, max_len=500, overlap=100): # 简化示例:按标题行切分,再对超长段落做重叠切分 import re sections = re.split(r'\n(?=#{1,3}\s)', text) chunks = [] for sec in sections: if len(sec) <= max_len: chunks.append(sec) else: start = 0 while start < len(sec): chunks.append(sec[start:start + max_len]) start += max_len - overlap return chunks

3.3 向量化模型选择与批量处理技巧

Embedding 模型选型要看三个指标:中文语义相似度、推理速度、维度。BGE-large-zh 在中文检索任务上表现很好,维度 1024;如果追求速度,BGE-small-zh 维度 512,效果略降但快很多。腾讯云也提供文本嵌入 API,省去自己部署模型的麻烦。

批量处理能大幅提升入库效率。假设你有 10 万个文本块,单条调用 Embedding API 可能要几个小时,批量调用(每批 16 到 64 条)能压缩到几十分钟。但批量太大会触发 API 限流,要根据服务商文档调整。

import requests def get_embeddings(texts, batch_size=32): all_vectors = [] for i in range(0, len(texts), batch_size): batch = texts[i:i + batch_size] # 这里替换为实际 Embedding API 调用 vectors = call_embedding_api(batch) all_vectors.extend(vectors) return all_vectors

提示:向量化后的结果建议本地缓存一份。如果入库失败或需要重建索引,不用重新调用 Embedding API,省时省钱。

3.4 腾讯云向量数据库集合设计与索引参数

在腾讯云向量数据库里,先创建集合(Collection),定义向量字段维度和标量字段。标量字段包括doc_id、chunk_id、department、create_time、text等。text字段存原始文本,检索后直接取用,不用回表查数据库。

索引类型常用 HNSW 和 IVF。HNSW 检索速度快、召回率高,但内存占用大;IVF 内存友好,适合超大规模数据,但需要训练且召回率略低。企业知识库数据量在百万级以内,我优先选 HNSW。

关键参数M控制每个节点的邻居数,越大召回率越高但内存和构建时间增加,通常取 16 到 32。efConstruction控制构建时的搜索范围,取 100 到 200。查询时的ef参数控制搜索范围,取 50 到 200,越大越准但越慢。

参数推荐值影响
M16-32越大召回越高,内存越大
efConstruction100-200越大索引质量越高,构建越慢
ef (查询)50-200越大召回越高,延迟越大
副本数2-3越高可用性越好,成本越高
分片数按数据量影响并行查询能力

4. 实操过程与核心环节实现

4.1 环境准备与依赖安装

先准备 Python 环境,建议 3.9 以上。创建虚拟环境,安装依赖:

python -m venv rag_env source rag_env/bin/activate # Windows 用 rag_env\Scripts\activate pip install pdfplumber python-docx python-pptx beautifulsoup4 pip install requests numpy tqdm pip install tcvectordb # 腾讯云向量数据库 SDK

腾讯云向量数据库 SDK 的安装包名以官方文档为准,这里用tcvectordb示意。安装完成后,在控制台创建实例,拿到访问地址和密钥。

4.2 文档解析与切片完整代码

下面是一段可复用的解析与切片代码,覆盖 PDF 和 Word:

import pdfplumber from docx import Document def parse_pdf(path): text = [] with pdfplumber.open(path) as pdf: for i, page in enumerate(pdf.pages): content = page.extract_text() or "" text.append({"page": i + 1, "content": content}) return text def parse_docx(path): doc = Document(path) return [{"page": 1, "content": p.text} for p in doc.paragraphs if p.text.strip()] def clean_text(text): import re text = re.sub(r'\s+', ' ', text) text = re.sub(r'第\s*\d+\s*页', '', text) return text.strip() def chunk_text(text, max_len=500, overlap=100): chunks = [] start = 0 while start < len(text): chunks.append(text[start:start + max_len]) start += max_len - overlap return chunks

解析完一个文档后,把每个 chunk 和元数据组装成字典,准备向量化。

4.3 向量化与批量入库

向量化后调用腾讯云向量数据库 SDK 写入。注意批量写入时每批不要太大,建议 100 到 500 条,避免单次请求超时。

from tcvectordb import VectorClient client = VectorClient(url="你的实例地址", username="root", key="你的密钥") database = client.create_database("enterprise_kb") collection = database.create_collection("docs", dimension=1024) def insert_chunks(chunks, vectors): data = [] for chunk, vec in zip(chunks, vectors): data.append({ "id": chunk["chunk_id"], "vector": vec, "text": chunk["text"], "doc_id": chunk["doc_id"], "department": chunk["department"], "create_time": chunk["create_time"] }) collection.insert(data)

写入完成后,在控制台确认文档数量,并检查索引构建状态。索引构建是异步的,数据量大时需要等几分钟。

4.4 检索、重排与生成回答

检索时先对用户问题向量化,然后调用相似度搜索,带上标量过滤条件:

def search(query, top_k=20, department=None): query_vec = get_embeddings([query])[0] filter_expr = f'department == "{department}"' if department else None results = collection.search( vectors=[query_vec], limit=top_k, filter=filter_expr, params={"ef": 100} ) return results

召回 Top 20 后,用重排模型精排。重排模型可以用 BGE-reranker,也可以用腾讯云提供的重排 API。重排后取 Top 5 组装 Prompt:

prompt_template = """你是一个企业知识库助手。请只根据以下资料回答问题,资料中没有的信息不要编造。 资料: {context} 问题:{question} 回答:"""

把 Prompt 发给大模型,拿到答案后连同引用来源一起返回给用户。引用来源包括文档名、页码、原文片段,用户可点击查看。

5. 常见问题与排查技巧实录

5.1 检索召回率低,答案答非所问

这是最常见的问题。排查顺序:先看切片是否合理,如果块太大,关键信息被稀释;如果块太小,语义不完整。再看 Embedding 模型是否适合中文,有些英文模型在中文上表现很差。然后看索引参数,ef太小会导致召回不足。最后看是否缺少重排,Top 20 里其实有正确片段,但没排到前面。

我的经验是,先加重排,再调切片,最后换 Embedding 模型。重排的投入产出比最高。

5.2 大模型仍然产生幻觉

即使给了资料,大模型有时还是会“自由发挥”。解决办法有三个:Prompt 里明确要求“只基于资料回答”,并给出“不知道”的示例;降低 temperature 到 0.1 以下;在答案后强制附上引用片段,让用户能核对。

如果某个问题反复出现幻觉,检查检索结果里是否真的包含答案。很多时候不是模型的问题,而是检索根本没召回正确内容。

5.3 入库速度慢、API 限流

批量大小和并发数要平衡。批量太小速度慢,批量太大触发限流。建议先小批量测试,观察 API 返回的限流错误,再逐步调大。同时加指数退避重试:

import time def retry_call(func, max_retries=5): for i in range(max_retries): try: return func() except Exception as e: if i == max_retries - 1: raise time.sleep(2 ** i)

5.4 常见问题速查表

问题可能原因解决方向
检索不到相关内容切片过大/过小、Embedding 不匹配调整切片、换中文模型
答案答非所问缺少重排、Prompt 不清晰加重排、优化 Prompt
模型编造信息temperature 高、无引用约束降温度、强制引用
入库慢批量小、无限流重试调大批量、加退避重试
查询延迟高ef 过大、副本少调小 ef、加副本
专有名词检索差纯向量检索对精确匹配弱启用混合检索

提示:企业知识库上线后,一定要建反馈机制。用户对答案点“有用/没用”,这些数据是后续优化的金矿。定期把“没用”的问题捞出来分析,往往能发现切片或检索策略的系统性问题。

6. 企业级落地时容易忽略的工程细节

6.1 权限隔离与多租户设计

企业知识库往往不是全公司共享,研发文档、财务制度、人事资料各有密级。在腾讯云向量数据库里,可以用标量字段department和security_level做过滤,检索时根据用户身份动态拼接过滤条件。用户属于哪个部门、什么级别,从企业 SSO 或权限系统获取,不要在前端传,防止越权。

多租户场景下,可以为每个租户建独立集合,也可以共用集合靠tenant_id过滤。独立集合隔离性好但管理成本高,共用集合管理简单但要严防过滤条件写漏。我通常建议中小规模用共用集合加严格过滤,大规模再拆集合。

6.2 知识库更新与增量入库

企业文档不是静态的,每天都有新文件、修订版、作废通知。全量重建索引成本高,必须支持增量。做法是给每个文档算一个内容哈希,入库前先查哈希是否已存在,存在则跳过,不存在则插入。文档更新时,先按doc_id删除旧块,再插入新块。

作废文档要软删除,标记status=deleted,检索时过滤掉。不要物理删除,万一误操作还能恢复。

6.3 监控指标与效果评估

上线不是终点。要监控几个核心指标:检索命中率(Top 5 里是否包含正确答案)、答案采纳率(用户是否满意)、平均响应延迟、大模型调用成本。检索命中率需要人工标注一批测试问题,定期跑评估。

我一般会准备 100 到 200 条真实用户问题作为评估集,每次调整切片或模型后跑一遍,看命中率变化。没有评估集的优化都是盲调。

6.4 成本控制的几个实用手段

Embedding 和向量存储的成本相对可控,大模型调用才是大头。几个省钱技巧:对高频问题做答案缓存,相似问题直接返回缓存;检索层做粗排,减少送入大模型的内容量;非高峰时段批量处理文档更新;根据问题复杂度动态选择模型,简单问题用轻量模型。

还有一个容易被忽略的点:向量维度不是越高越好。1024 维和 768 维在实际检索效果上差距往往很小,但存储和计算成本差不少。上线前做个对比测试,选性价比最高的维度。

7. 从 RAG 到知识工程的延伸思考

7.1 RAG 知识库与结构化知识库的边界

RAG 擅长处理非结构化文本,但企业里还有大量结构化数据,比如订单表、客户表、设备台账。这些数据用 RAG 处理效果不好,因为向量检索对精确数值查询很弱。正确做法是结构化数据走 Text2SQL 或 API 查询,非结构化文档走 RAG,两者通过路由层统一入口。用户问“上个月华东区销售额”,路由到 SQL;问“退货政策怎么规定的”,路由到 RAG。

7.2 知识图谱与 RAG 的结合场景

纯 RAG 的局限在于它只能检索相似文本,无法做多跳推理。比如“A 产品的负责人是谁,他负责的另一个产品有什么特性”,这需要跨文档关联。知识图谱(KG)能把实体和关系显式建模,和 RAG 结合后,可以先在图上做多跳查询,再把结果作为上下文给大模型。这个方向目前还在演进,落地成本较高,适合知识密度大、关联复杂的场景,比如医疗、法律、金融。

7.3 后续可扩展的方向

最小可用版本跑通后,可以逐步加这些能力:多模态检索,支持图片和表格;对话式检索,支持多轮追问;主动学习,根据用户反馈自动优化切片和检索策略;Agent 化,让知识库能调用外部工具完成复杂任务。每一步都建议先小范围验证,再全量推广,不要一次性堆太多功能。

我个人在实际操作中的体会是,RAG 企业知识库的效果,七分靠数据治理,三分靠模型和检索。文档解析干净、切片合理、元数据完整,比换更贵的模型管用得多。很多团队一上来就纠结用哪个大模型,其实先把文档管道做扎实,效果自然就上来了。另外,上线初期不要追求大而全,选一个部门、一类文档先跑通,拿到真实反馈再扩展,比闷头建三个月再上线靠谱得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进