☰
腾讯云向量数据库搭建RAG知识库:架构设计与实操调优
2026/10/27 6:32:28 网站建设 项目流程

1. 为什么企业都在折腾 RAG 知识库

这两年做大模型应用落地,绕不开的一个词就是 RAG。全称 Retrieval-Augmented Generation,检索增强生成。说白了就是给大模型外挂一个知识库,让它回答问题的时候先去知识库里查资料,再基于查到的内容组织答案,而不是全靠模型自己脑子里记的那点东西。

我接触过不少企业内部的问答项目,从最早的纯 Prompt 硬塞上下文,到后来用向量检索做召回,再到现在结合知识图谱做混合检索,踩过的坑真不少。最直观的感受是:企业知识库的核心矛盾从来不是模型不够聪明,而是模型不知道你企业内部的那些事。你问它公司报销流程是什么,它要么瞎编,要么说“我无法获取内部信息”。RAG 就是来解决这个问题的。

那为什么偏偏要选腾讯云向量数据库?我自己对比过几套方案。自建 Milvus 或者 Chroma 当然可以,但企业场景下要考虑运维成本、权限管理、数据安全、弹性扩缩容这些事。腾讯云向量数据库在这方面省心不少,尤其是它跟腾讯云其他产品(比如 COS 对象存储、CVM 云服务器)的打通比较顺,对于已经在用腾讯云的企业来说,迁移和集成成本低。当然,如果你只是个人学习或者小规模测试,本地跑 Chroma 也完全够用,这个后面我会说怎么选。

这篇文章我打算把整个搭建过程拆开讲清楚:从整体架构怎么设计,到文档怎么切分、向量怎么生成、检索怎么调优,再到实际部署时遇到的坑。适合谁看?我觉得有三类人:一是想给公司搭内部知识库的后端或全栈工程师;二是做 AI 应用产品、需要理解 RAG 链路的同学;三是自己折腾项目、想搞明白向量数据库到底怎么用的开发者。不管你是刚入门还是已经做过一版想优化,应该都能找到点有用的东西。

2. 整体架构设计与技术选型思路

2.1 RAG 系统的核心链路拆解

先把 RAG 的完整链路捋一遍,不然后面讲细节容易迷。一个标准的 RAG 系统分两大阶段:离线索引阶段和在线检索生成阶段。

离线索引阶段做的事是:把企业内部的文档(PDF、Word、Markdown、网页、数据库记录等)读进来,做清洗和切分,然后通过 Embedding 模型把每个文本块转成向量,最后存进向量数据库。这个过程是一次性的或者定期增量更新的。

在线检索阶段是:用户提一个问题,系统把问题也转成向量,去向量数据库里找最相似的 Top-K 个文本块,把这些文本块作为上下文拼进 Prompt,一起发给大模型,让大模型基于这些上下文生成答案。

听起来简单对吧?但每个环节都有讲究。比如文档切分切多大?切太小语义不完整,切太大检索精度下降。Embedding 模型选哪个?中文场景和英文场景差别很大。检索的时候要不要做重排序?Top-K 取多少合适?这些问题后面会一个个展开。

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

我先把选型逻辑说清楚,这样你在自己项目里做决策时有个参照。

自建向量库的方案常见的有 Milvus、Qdrant、Weaviate、Chroma。Chroma 最轻量,适合本地开发和原型验证,但生产环境的高可用和扩展性一般。Milvus 功能强大,但部署和运维复杂度高,光是依赖的组件就一堆(etcd、MinIO、Pulsar 等),小团队维护起来吃力。

腾讯云向量数据库的优势在于:开箱即用,无需运维。你创建实例后直接拿连接地址和密钥就能用,底层的分片、副本、索引优化都是自动的。它支持标准的向量检索接口,也兼容一些常见的 SDK。另外它在标量过滤方面做得不错,比如你可以按文档来源、时间范围做过滤后再做向量检索,这在企业场景里很实用——比如只想搜某个部门的知识。

成本方面,腾讯云向量数据库按实例规格和存储量计费,小规模起步的配置一个月几百块,比专门养一个人维护 Milvus 划算。当然如果你的数据量特别大(上亿条向量),或者有特殊需求,自建可能更灵活。这个取舍你自己判断。

2.3 技术栈组合与依赖清单

我这套方案用的技术栈如下,你可以根据实际情况替换:

组件选型说明
向量数据库腾讯云向量数据库存储和检索向量
Embedding 模型腾讯云混元 Embedding 或 BGE-M3中文效果好,支持长文本
大模型混元 / DeepSeek / 本地部署模型生成最终答案
后端框架Python + FastAPI提供 API 接口
文档解析PyPDF2、python-docx、BeautifulSoup处理不同格式文档
文本切分LangChain TextSplitter 或自写控制切分粒度
对象存储腾讯云 COS存原始文档

Python 版本建议 3.9 以上,3.10 或 3.11 更稳。依赖库主要是tcvectordb(腾讯云向量数据库 SDK)、langchain、fastapi、uvicorn、sentence-transformers(如果本地跑 Embedding)。安装的时候注意,tcvectordb的版本要和你的实例版本匹配,这个在官方文档里有对照表,别装错了。

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

3.1 文档切分:RAG 效果的第一道分水岭

我见过太多 RAG 项目效果差,根子就出在文档切分上。切分策略直接决定了检索能不能命中正确的信息。

先说切分粒度。一般推荐chunk size 在 300 到 800 个 token 之间,overlap(重叠部分)取 chunk size 的 10% 到 20%。为什么要有 overlap?因为一句话可能被切断,重叠能保证语义连续性。比如你切到“公司报销需要提供”,下一块从“发票原件和审批单”开始,如果没有重叠,检索“报销需要什么材料”可能两块都召回不全。

但这不是死规定。技术文档、法律合同这种结构化强的内容,可以按章节或条款切,保持语义完整。FAQ 类的内容,一问一答作为一个 chunk 最合适。会议纪要这种,按发言人或议题切。

我自己的经验是:先按文档的自然结构切,再用固定长度兜底。比如 Markdown 按标题层级切,PDF 按段落切,切完如果某块超过 1000 token 再强制分割。这样比无脑按字数切效果好很多。

还有一个细节:切分的时候要不要保留标题信息?要。比如一个 chunk 来自“第三章 报销流程”下面的内容,你可以在 chunk 前面加上“第三章 报销流程:”作为前缀。这样检索时标题信息也参与向量化,命中率会提升。

3.2 Embedding 模型选择与向量维度权衡

Embedding 模型是把文本转成向量的关键。选型主要看三点:语言支持、维度、性能。

中文场景我推荐 BGE-M3 或者腾讯云混元的 Embedding 接口。BGE-M3 支持多语言,维度 1024,在中文语义相似度任务上表现很好,而且可以本地部署,不依赖外部 API。混元 Embedding 的好处是跟腾讯云生态集成方便,不用自己维护模型服务。

维度是不是越高越好?不是。维度高意味着存储成本高、检索慢。768 到 1024 维在大多数场景下够用了。腾讯云向量数据库支持多种索引类型,维度不同索引参数也要调整,这个后面讲。

还有一个容易忽略的点:Embedding 模型必须和检索时用的模型一致。你不能索引的时候用 BGE,检索的时候用混元,那向量空间都不一样,检索结果全是乱的。这个坑我见新人踩过不止一次。

3.3 腾讯云向量数据库的集合设计与索引配置

在腾讯云向量数据库里,数据存在“集合”(Collection)中,类似关系数据库的表。创建集合时要定义字段和索引。

字段分三类:主键字段(primary key)、向量字段(vector)、标量字段(scalar)。主键就是唯一 ID,向量字段存 Embedding 结果,标量字段存元数据,比如文档来源、章节、时间等。

索引配置是性能的关键。腾讯云向量数据库支持 FLAT、HNSW、IVF 等索引类型。FLAT 是暴力检索,精度最高但速度慢,适合数据量小(几万条以内)的场景。HNSW 是图索引,检索快、精度高,是大多数场景的首选。IVF 是倒排索引,适合超大规模数据,但需要训练,精度略低。

HNSW 的关键参数是M和efConstruction。M控制每个节点的连接数,越大精度越高但内存占用越大,一般取 16 到 64。efConstruction是建索引时的搜索范围,越大索引质量越好但建索引越慢,一般取 100 到 200。检索时还有个ef参数,越大召回率越高但越慢,通常取 50 到 200。

我的建议是:先用默认参数跑通,再根据实际效果调。别一上来就调参,没有基准你都不知道调的方向对不对。

4. 完整实操流程与核心代码实现

4.1 环境准备与 SDK 安装

先把环境搭起来。Python 安装就不多说了,官网下载安装包,注意勾选“Add to PATH”。装完验证一下:

python --version pip --version

然后装依赖。我习惯用虚拟环境,避免污染全局:

python -m venv rag_env source rag_env/bin/activate # Windows 用 rag_env\Scripts\activate pip install tcvectordb langchain fastapi uvicorn python-docx PyPDF2 beautifulsoup4

如果你要本地跑 Embedding 模型,再装:

pip install sentence-transformers torch

这里注意,tcvectordb的安装有时候会因为网络问题失败,可以换国内镜像源:

pip install tcvectordb -i https://pypi.tuna.tsinghua.edu.cn/simple

装完之后写个测试脚本验证能不能连上腾讯云向量数据库。你需要先在腾讯云控制台创建实例,拿到访问地址和密钥。

import tcvectordb from tcvectordb.model.collection import Collection from tcvectordb.model.document import Document client = tcvectordb.VectorDBClient( url="你的实例访问地址", username="root", key="你的密钥" ) # 列出所有数据库,验证连接 db_list = client.list_databases() print(db_list)

能打印出数据库列表就说明连接正常。如果报连接超时,检查一下实例的网络配置,是不是开了外网访问,或者你的服务器 IP 有没有加到白名单里。

4.2 文档解析与清洗的实操细节

文档解析这块,不同格式处理方式不一样。我写一个通用的解析函数框架:

import os from PyPDF2 import PdfReader from docx import Document as DocxDocument from bs4 import BeautifulSoup def parse_document(file_path): ext = os.path.splitext(file_path)[1].lower() if ext == '.pdf': reader = PdfReader(file_path) text = '\n'.join([page.extract_text() for page in reader.pages]) elif ext == '.docx': doc = DocxDocument(file_path) text = '\n'.join([para.text for para in doc.paragraphs]) elif ext in ['.html', '.htm']: with open(file_path, 'r', encoding='utf-8') as f: soup = BeautifulSoup(f.read(), 'html.parser') text = soup.get_text(separator='\n') elif ext in ['.md', '.txt']: with open(file_path, 'r', encoding='utf-8') as f: text = f.read() else: raise ValueError(f"不支持的文件格式: {ext}") # 基础清洗:去掉多余空行和空格 lines = [line.strip() for line in text.split('\n')] lines = [line for line in lines if line] return '\n'.join(lines)

PDF 解析是个大坑。很多 PDF 是扫描件或者排版复杂,extract_text()出来的文本顺序是乱的。如果遇到这种情况,要么用 OCR 方案(比如 PaddleOCR),要么让业务方提供原始文档。别指望一个库能搞定所有 PDF。

清洗的时候注意保留有意义的标点,去掉页眉页脚、页码这些噪音。我一般会写正则把“第 X 页 共 Y 页”这种模式去掉。

4.3 文本切分与向量化入库

切分我用 LangChain 的 RecursiveCharacterTextSplitter,它对中文支持还行:

from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=80, separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""] ) chunks = splitter.split_text(text)

注意separators的顺序,优先按段落切,再按句子切,最后才按字符切。中文的句号、问号这些要加进去,不然切出来的块可能断在句子中间。

向量化我用 BGE-M3 本地模型举例:

from sentence_transformers import SentenceTransformer model = SentenceTransformer('BAAI/bge-m3') def get_embedding(text): return model.encode(text, normalize_embeddings=True).tolist()

normalize_embeddings=True很重要,归一化后余弦相似度计算更快更准。

入库的时候批量插入,别一条条插,太慢。腾讯云向量数据库支持批量写入:

from tcvectordb.model.document import Document def insert_chunks(collection, chunks, source): documents = [] for i, chunk in enumerate(chunks): vec = get_embedding(chunk) doc = Document( id=f"{source}_{i}", vector=vec, text=chunk, source=source ) documents.append(doc) # 批量插入,每批 100 条 batch_size = 100 for i in range(0, len(documents), batch_size): batch = documents[i:i+batch_size] collection.insert(batch) print(f"已插入 {i+len(batch)} 条")

这里有个细节:id要保证唯一,我用“文件名_序号”的格式。如果同一个文档更新了,要先删掉旧的再插新的,不然会有重复。

4.4 检索与生成:把召回结果喂给大模型

检索的时候,先把用户问题向量化,然后去向量库查 Top-K:

def search(collection, query, top_k=5): query_vec = get_embedding(query) results = collection.search( vectors=[query_vec], limit=top_k, output_fields=["text", "source"] ) return results

Top-K 取多少?我一般取 5 到 10。太少可能漏掉关键信息,太多会引入噪音,而且大模型上下文长度有限。如果检索结果里有明显不相关的,可以加个相似度阈值过滤,比如只保留相似度大于 0.7 的。

然后拼 Prompt 发给大模型:

def generate_answer(query, contexts): context_text = '\n\n'.join([c['text'] for c in contexts]) prompt = f"""基于以下参考资料回答问题。如果资料中没有相关信息,请如实说不知道,不要编造。 参考资料: {context_text} 问题:{query} 答案:""" # 调用大模型接口 answer = call_llm(prompt) return answer

Prompt 里一定要加“不知道就说不知道”这句,不然大模型很容易在检索不到的时候硬编。这个我在实际项目里验证过,加了这句之后幻觉率明显下降。

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

5.1 检索不准的排查思路

检索不准是最常见的问题。排查顺序我一般是这样:

第一步,看切分。把检索到的 chunk 打印出来,看看内容是不是完整的。如果 chunk 断在句子中间,或者一个 chunk 里混了好几个不相关的主题,那就是切分有问题。调整 chunk_size 和 separators。

第二步,看 Embedding。拿几个典型问题,手动算一下问题和正确文档块的相似度。如果相似度很低,可能是 Embedding 模型不适合你的领域。试试换个模型,或者在领域数据上微调一下。

第三步,看索引参数。如果用的是 HNSW,把ef调大试试,比如从 50 调到 200。如果召回率明显提升,说明之前索引精度不够。

第四步,看查询本身。用户的问题可能太短或者有歧义。可以试试查询改写,让大模型先把用户问题扩展成几个相关查询,分别检索再合并结果。

5.2 大模型幻觉与上下文长度限制

幻觉问题在 RAG 里依然存在,只是比纯生成好很多。除了 Prompt 里加约束,还可以做引用溯源:让大模型在答案里标注每句话来自哪个文档块,这样用户能自己判断可信度。

上下文长度限制是另一个坑。如果你检索了 10 个 chunk,每个 500 token,那就是 5000 token,加上问题和系统 Prompt,可能就超了。解决办法:一是控制 Top-K,二是对 chunk 做压缩,只保留跟问题最相关的句子,三是用支持更长上下文的模型。

5.3 增量更新与数据一致性

企业知识库不是一次建完就不管了,文档会更新、会新增。增量更新要注意:更新文档时,先根据 source 字段删掉旧的所有 chunk,再插入新的。不然新旧内容混在一起,检索结果会乱。

腾讯云向量数据库支持按条件删除:

collection.delete(filter="source == '旧文档.pdf'")

删除是异步的,删完等几秒再插,避免冲突。

5.4 性能优化与成本控制

性能方面,几个关键点:批量操作、索引预热、连接复用。批量插入比单条快几十倍。索引建好后先跑一批查询预热,让缓存生效。SDK 的 client 要复用,别每次请求都新建连接。

成本方面,Embedding 计算是大头。如果文档量大,建议用本地模型批量跑,比调 API 便宜。向量数据库的存储成本跟维度、数据量成正比,定期清理不再使用的集合能省不少。

常见问题速查表:

问题现象可能原因解决方向
检索结果不相关切分粒度不当调整 chunk_size 和 overlap
相似度普遍偏低Embedding 模型不匹配换模型或微调
检索速度慢索引参数不合理调大 ef 或换索引类型
大模型答非所问Prompt 设计问题优化 Prompt,加约束
插入报错ID 重复或维度不符检查 ID 唯一性和向量维度
连接超时网络或白名单配置检查实例网络设置

6. 进阶方向:从 RAG 到知识图谱增强

纯向量检索有个天然缺陷:它只能找到语义相似的文本块,但没法处理多跳推理。比如你问“A 项目的负责人所在部门的报销标准是什么”,这需要先找到 A 项目的负责人,再找他的部门,再找报销标准,向量检索很难一次搞定。

这时候可以引入知识图谱,把实体和关系抽出来,做 Graph RAG。腾讯云向量数据库本身不存图结构,但你可以把图的三元组也向量化存进去,检索时同时查文本块和三元组,合并结果。或者用 Neo4j 这类图数据库配合使用。

另一个方向是混合检索:向量检索 + 关键词检索(BM25)结合,用 RRF(Reciprocal Rank Fusion)算法融合排序。这样既能抓住语义相似,又能保证关键词命中,实际效果比单一检索好不少。

我自己在实际项目里的体会是:没有银弹,RAG 的效果是调出来的,不是搭出来的。同样的架构,参数不同、切分策略不同,效果可能差一倍。多准备一些测试问题,建个评估集,每次调整都跑一遍,用数据说话,比凭感觉调靠谱得多。

最后分享一个小技巧:如果你的知识库文档里有大量表格,别直接转成文本,表格的结构信息会丢失。可以把表格转成 Markdown 格式再向量化,或者单独存一份结构化数据,检索到表格时直接返回原表。这个细节处理好了,问答体验会提升一个档次。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询