☰
RAG的坑不在检索,在对齐:pgvector实战排查指南
2026/10/18 21:27:04 网站建设 项目流程

做 RAG 项目做到中期,最容易产生一种错觉:问题都出在“检不出来”。于是今天调 embedding 模型,明天换 pgvector 索引参数,后天又把 chunk 大小从 512 改成 256,折腾一圈,效果还是时好时坏。我跟不少团队聊过,也看了很多开源 issue,最后发现一个被低估的事实——RAG 的坑不在检索,而在“对齐”。检索只是最后一公里,真正决定上限的,是前面的数据切片、向量化、元数据设计、查询改写和重排,它们有没有跟用户的真实问题对齐。

这篇文章就围绕 pgvector 实战展开,标题叫“RAG 的坑不在检索,在对齐”,想聊的其实是一整套排查思路和落地细节:为什么检索结果不理想时,先别急着怪向量数据库;pgvector 在中间到底扮演什么角色;以及从切块到建索引,从查询 SQL 到分数校准,每个环节的“对齐”该怎么具体做。内容偏实战,适合已经跑通 demo、正在优化线上效果的开发者,也适合准备从 ES 切到 pgvector、想提前避坑的团队。

1. RAG 真正的瓶颈:为什么说“坑不在检索,在对齐”

1.1 检索只是最后一公里,前面的数据管线决定上限

先讲一个我自己的项目经历。当时做企业知识库问答,场景不复杂:把几十份产品文档和运维手册切成片段,存进 PostgreSQL 加 pgvector 扩展,用户提问后走向量检索,TopK 取 5 条拼进 prompt 让大模型回答。

上线第一天,demo 效果挺惊艳。但一到真实问法就露馅了。比如文档里写的是“节点出现 CPU 飙高”,用户问的是“服务器卡死了怎么办”,向量相似度根本拉不近这两个表达的距离。当时我第一反应是 embedding 模型不够强,换了更大参数的模型,结果提升有限。后来把文档片段调出来一看,发现问题出在原文切块:一段操作步骤被拦腰截断,前半段讲“如何查看监控指标”,后半段讲“如何重启服务”,语义被切散了,向量表示自然飘忽不定。

这个案例让我意识到,RAG 是一个流水线,不是单点工具。检索只是在向量空间里做 KNN,它能做的只是在已有片段里找最像的。如果片段本身就不是一个完整的语义单元,如果 metadata 没有记录章节路径和文档来源,如果用户的问法跟库里的表达方式存在明显 gap,那无论索引多快、召回多准,结果都不可能好。用一句行话讲:检索负责“把已有的东西找出来”,而对齐负责“让库里的东西恰好是用户需要的东西”。

所以,遇到效果差,别第一个怀疑 pgvector。先回去查数据管线:切块是否保留了语义边界,embedding 是否跟领域术语对齐,metadata 是否足以支撑过滤和重排。这些才是决定 RAG 天花板的环节。

1.2 “对齐”到底指什么:四个层面同时对齐

“对齐”这个词在 RAG 语境里听上去很玄,拆开看其实是四件很具体的事。

第一是切片单元与语义边界的对齐。文档里的一个完整段落、一个表格、一段代码示例,都应该尽量作为一个独立单元进入向量库。固定长度硬切是最省事、也最容易出问题的方式,尤其对表格和代码不友好。后面我会详细讲。

第二是问题表达与库内表达的对齐。用户不会按照文档原话提问,这是 RAG 最残酷的现实。比如文档写“该接口返回 HTTP 200”,用户问“请求成功长什么样”,字面上毫无交集。解决思路有两个方向:一个是切块时保留尽量丰富的表达(比如把同义词、缩写、别名写进原文),另一个是查询时做改写(query rewrite),把口语化问题改写成更接近文档术语的表述。

第三是向量维度与内容粒度的对齐。一个 1536 维的 embedding 向量,有能力表达一段话的核心语义,但不擅长表达“第 3 章 2.1 节”这类位置信息。所以位置信息不要指望靠向量体现,要靠 metadata 字段存起来,在 SQL 里用 filter 精确过滤。很多团队把章节号硬塞进文本里去向量化,效果反而更差,因为模型的注意力被无关字符分散了。

第四是召回排序与用户预期的对齐。向量相似度高不一定代表答案正确,尤其是 FAQ 类问题和长文档场景,一条高相似片段可能只是“沾边”,并不是真正能回答问题的段落。所以 TopK 不能拍脑袋定,rerank 也不能省。这个我也会在检索部分展开。

这四个层面是层层递进的关系:前两层做不好,后两层调参等于白调。pgvector 能帮我们解决的是“存”和“查”,但救不了前两层的结构性缺陷。

1.3 为什么 pgvector 容易让人误以为“装上就完事”

pgvector 有一个很大的优势:它是 PostgreSQL 的扩展,意味着你可以把结构化字段(价格、日期、状态)和向量字段放在同一张表里,一条 SQL 同时做过滤和相似度检索,不用像 ES 那样非得搞两套系统。这个能力太方便了,但也埋了一个隐患——太方便,容易让人忽略数据建模。

我见过不少项目,建表时随便加一个 vector 列,把整篇文档丢进去,embedding 之后就不管了。没有 metadata,没有切块策略,没有索引参数调优。等检索效果差了,就开始骂向量检索不行,甚至把责任推给 pgvector。实际上 pgvector 只是执行者,它忠实地完成了“在向量空间里找最近邻”这个任务,但你的向量空间本身是乱的,再快的检索也只能在垃圾堆里翻东西。

另一个容易踩的误区和 PostgreSQL 本身有关。pgvector 的检索性能高度依赖索引类型和参数,如果表里已经塞了几十万行、又没建索引,那每次查询都是全表扫描计算余弦距离,响应时间直接到秒级。很多人为了图省事,在数据量很小的时候没建索引,等数据涨上去才开始着急。不是说 pgvector 不能撑大并发,而是说它和所有数据库一样,需要对索引、分页、参数做功课。

所以我给团队的建议一直是:把 pgvector 当成一个“严肃的向量数据库”来对待,而不是一个临时玩具。数据建模和索引设计要认真做,这部分我会在接下来的章节里具体展开。

2. pgvector 部署与基础配置:先把地基打牢

2.1 安装与版本选择:PostgreSQL 版本要对齐

pgvector 的安装本身不复杂,但有个前提容易被忽略:PostgreSQL 版本必须跟 pgvector 的发布版本兼容,而且不同 PostgreSQL 大版本之间,扩展的编译产物不通用。

我在生产环境用过 PostgreSQL 14 和 15,pgvector 分别装过 0.5.x 和 0.6.x。整体体验差异不大,但 0.6.0 之后 HNSW 索引的构建速度有改善,而且支持了更多的 distance 类型选项。如果是从零开始的新项目,建议直接用 PostgreSQL 15 或 16 + pgvector 0.7.x 的搭配,省的后面走迁移的弯路。

在 Linux 上安装很简单,源码编译或者用 apt 都行。我习惯用源码编译,因为这样能确保 pg_config 路径对应的版本是对的:

git clone --branch v0.7.0 https://github.com/pgvector/pgvector.git cd pgvector make make install

装完以后在目标数据库里执行一句:

CREATE EXTENSION IF NOT EXISTS vector;

Windows 上也能装,一般是从 GitHub Releases 下载对应 PostgreSQL 版本的 zip,解压后把 .dll 和 .sql 文件复制到 PostgreSQL 的 share 和 lib 目录。这里最容易踩的坑是:zip 包里的版本必须和你的 PostgreSQL 版本完全一致,比如 14.24.2 就得找 pg14 的包,混着用会直接报“could not access file vector”之类的错误。

还有一点算是我个人习惯:在生产环境,尽量把 pgvector 单独装在一个专门的 schema 里,避免跟业务表混在一起。这样后续如果需要监控扩展版本、做权限隔离,会省很多事。做法是:

CREATE SCHEMA IF NOT EXISTS vectors; CREATE EXTENSION IF NOT EXISTS vector SCHEMA vectors;

这样建表时用vectors.vector(1536)来声明列类型就行。

2.2 开启扩展与关键配置参数

pgvector 装完之后,最容易被忽视的是 PostgreSQL 自身的参数调优。很多人以为向量检索慢是 pgvector 的问题,其实一半以上的情况是 PostgreSQL 的 shared_buffers、work_mem、maintenance_work_mem 没调好。

我自己常用的几个关键配置:

shared_buffers = 4GB # 视机器内存而定,建议占物理内存 1/4 左右 maintenance_work_mem = 1GB # HNSW 索引构建阶段会用,太小会频繁刷盘 work_mem = 64MB # 排序、哈希等操作的临时内存 max_parallel_workers_per_gather = 4

maintenance_work_mem尤其重要。HNSW 索引构建阶段需要构建多层图结构,内存不够就会疯狂写临时文件,导致CREATE INDEX跑几分钟甚至几十分钟。我在一台 8G 内存的机器上试过,把 maintenance_work_mem 从默认 64MB 调到 1GB,索引构建时间直接缩短了 5 倍以上。

然后就是向量列本身的参数。pgvector 支持三种距离类型:L2、内积(negative inner product)、余弦距离。如果你用 OpenAI 的 text-embedding-ada-002(1536 维),默认建议用余弦距离;如果用 Cohere 或者 BGE 系列,同样看模型文档里的推荐。这里有个细节:pgvector 的余弦距离在底层其实是归一化后的 L2 距离,所以用余弦距离作为索引类型时,它存储和计算的方式跟 L2 是相通的。

创建索引时,我推荐 HNSW 优先:

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

这里的m和ef_construction直接影响索引质量和构建速度。m 越大,图连接越密,召回理论上更好,但内存占用也更高;ef_construction 越大,构建时探索的候选越多,索引质量越高。后面检索时还会用hnsw.ef_search控制探索范围。

2.3 表结构与字段设计:metadata 的正确姿势

很多人建 pgvector 表时只想着“文档内容 + 向量”,但实际业务里,查询几乎从不会是纯向量检索。最常见的是带条件的检索:只查某本书、某个分类、某个时间段内的内容。这时候 metadata 字段的价值就出来了。

我常用的建表语句大概长这样:

CREATE TABLE documents ( id BIGSERIAL PRIMARY KEY, title TEXT, doc_source TEXT, -- 来源,比如 "运维手册" / "产品文档" chapter_path TEXT, -- 章节路径,比如 "第3章/2.1 错误码" chunk_text TEXT, -- 切片后的原始文本 token_count INT, -- 为了控制 prompt 长度 embedding vectors.vector(1536), created_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

注意chapter_path这种字段,看起来可以塞进向量文本里,但我建议单独存。因为向量模型对于“第3章/2.1 错误码”这类结构信息的编码效果很差,它会把注意力分散到这些字符上,反而稀释了真正的内容语义。用单独的字段存,之后在 SQL 里精确过滤,效果又好又快。

另外,建议给doc_source和chapter_path建普通 B-tree 索引。这样当查询指定来源时,PostgreSQL 会先用常规索引过滤,再在过滤后的子集上做向量 KNN,性能提升非常明显。我在一个 80 万行的表里实测,无过滤条件的向量检索平均耗时在 150ms 左右,加上doc_source过滤后,如果某个来源只占 10 万行,响应能压到 50ms 以内。

3. 向量化与切块:对齐的第一道分水岭

3.1 切块策略:固定长度切块为什么容易翻车

切块是 RAG 里最容易被低估的环节。很多教程直接告诉你“按 512 个 token 切,重叠 50”,但你真按这个做了,大概率会在实际场景里踩坑。

原因很简单:语义单元天然不是等长的。文档里的一个完整段落可能是 200 token,一个表格可能跨好几个 chunk,一个函数定义可能正好卡在边界上被切成两半。固定长度切块等于无视语义边界,把一句完整的话、一段完整逻辑强制劈开,导致每一块单独看都是“残缺的”。

我在一个法律文书项目里就深有体会。原文条款经常是“甲方应于 X 日内……,但若乙方……,则……”,一个条件句横跨 300 多 token,固定 256 切的话,前半块是“甲方应于 X 日内”,后半块是“则……”,两块单独看都说不清楚完整义务关系。用户问“乙方违约了怎么办”,只能召回后半块,但后半块缺了前提,模型回答就飘了。

后来我改成按段落和标题层级切:先用正则把 Markdown / HTML 标题拆出来,再按一级、二级标题把文档拆成区块,每个区块内部如果是长段落,再按句子边界做二次切分。这样每一块基本保持了一个完整语义单元,查询效果提升是肉眼可见的。

当然,完全不用固定长度也不现实。我的策略是“固定长度兜底 + 语义边界优先”:优先按标题和段落切,但每个 chunk 长度控制在 300~800 token 之间,如果某个段落特别长,再按句子边界切开,同时用重叠窗口保留上下文。

3.2 重叠窗口与元数据标记:不让上下文断层

切块之后,文本边界上往往会丢失上下文。比如第 1 块结尾提到“这个错误码”,第 2 块开头没有解释“这个”指代什么,模型拿到第 2 块就懵了。解决办法是老生常谈的重叠(overlap),但重叠也有讲究。

我给团队定的规则是:重叠窗口不要机械地固定 50 个 token,而是尽量选择“上一段的末尾一句话”作为重叠内容。具体做法是先按段落切,如果段落太短,就把相邻段落合并;如果段落太长,按句子边界切成子块,并在每个子块前面拼接上一子块的最后一句话。这么做的好处是,重叠内容本身携带明确的语义边界,而不是一个半截句子。

还要配合 metadata 标记。每个 chunk 在入库时都记录它属于哪个文档、哪个章节、在文档中的顺序号。这样做有两个作用:一是重排时可以按文档内顺序还原上下文;二是检索时可以通过doc_source过滤,避免跨文档混合召回。

我曾经遇到一个诡异现象:用户问“如何配置 SSL”,系统召回的第一条是 A 产品手册里的 SSL 章节,第二条是 B 产品手册里的 SSL 配置,两条说法还有冲突。模型把两条都吃了进去,答案自然自相矛盾。后来在查询 SQL 里加上doc_source = 'A产品手册'的过滤条件,类似问题就消失了。

3.3 Embedding 模型选型与维度对齐

embedding 模型的选择,直接决定了“对齐”的精度上限。现在开源模型很多,从 BGE、M3E、GTE 到商业 API 都有,选型时除了看公开评测分数,更重要的是看你的领域文本是否在模型的训练语料里有覆盖。

比如通用代码和英文文档场景,OpenAI 的 text-embedding-ada-002 表现稳定;中文知识库场景,BGE-large-zh 或者 M3E 通常比通用英文模型好;如果你的文本里有大量专业术语(比如专利、法律、医疗),建议用领域微调过的模型,或者干脆自己基于 BGE 做二次微调。

这里要提醒一个细节:维度对齐。pgvector 的向量维度是建表时写死的,比如vectors.vector(1536)。如果你中途换了 embedding 模型,从 1536 维换成 1024 维,表结构就废了,要么重建表,要么加一列新向量。所以在项目初期就要确定模型,别想着“先随便用一个,后面再换”。后面换模型的代价远比你想象的大,因为不仅数据要全部重新向量化,索引也要全部重建。

另外,必须注意 embedding 模型的文本长度上限。ada-002 支持最多 8191 个 token,但很多开源模型上限只有 512 或 1024。如果你的 chunk 超过模型上限,会出现截断,向量表示的质量会大打折扣。我在切块时,都会把 chunk_text 的 token 数控制在模型上限的 70% 以内,留出余量。

4. 索引选择与检索 SQL:参数背后的“对齐经济学”

4.1 HNSW 与 IVFFlat:别再凭感觉选索引

pgvector 支持两种主要的向量索引:IVFFlat 和 HNSW。很多教程会说“数据量大用 IVFFlat,数据量小用 HNSW”,但实际上这个说法过于粗糙,我自己的选型逻辑是这样的。

IVFFlat 的思路是把向量空间划分成若干个聚类桶,查询时只搜索最近的几个桶。它的参数是lists,表示聚类数量。如果 lists 太小,每个桶里的向量太多,检索速度慢;如果 lists 太大,每个桶太小,容易漏召回。而且 IVFFlat 有一个致命弱点:数据分布变化时需要重建索引,否则聚类中心和实际数据不匹配,召回质量下降。这意味着你每次批量导入新数据后,最好都重新训练一次索引。

HNSW 的思路则是构建一个多层图结构,查询时从顶层开始沿图逼近最近邻。它的优势是对数据动态变化的容忍度更高,插入新数据不需要重建整个图,而且召回质量通常比 IVFFlat 稳定。代价是内存占用更高,构建时间更长。

我的建议是:除非你的表是“一次性灌入,之后很少更新”,否则直接上 HNSW。尤其生产环境会有增量更新,IVFFlat 的“数据分布变了要重建”这个特性会让你非常难受。HNSW 构建慢一点,但省心。

HNSW 有两个参数需要关注:m和ef_construction。m控制每个节点的最大连接数,默认 16,调大到 32 可以提高召回,但内存和构建时间也跟着涨;ef_construction控制构建时探索的候选数量,默认 64,调大到 100 以上可以进一步提升索引质量。对于千万级以下的数据量,m=16、ef_construction=64 通常够用,不用追求极端参数。

4.2 检索 SQL 与分数归一化:TopK 不是越大越好

基本的查询 SQL 很简单:

SELECT id, chunk_text, 1 - (embedding <=> $1) AS similarity FROM documents WHERE doc_source = '产品手册' ORDER BY embedding <=> $1 LIMIT 5;

其中<=>是余弦距离运算符,$1是传入的查询向量。很多人写的查询到这里就结束了,但实际项目中还有几个优化点要注意。

第一个是分数归一化。向量相似度在不同模型、不同文本长度上的分布差异很大。同一个查询,可能在 ada-002 下最高的相似度是 0.82,在某个开源模型下最高只有 0.71。如果你把原始相似度直接作为“置信度”来过滤,阈值很难定。我一般会在拿到检索结果后做 min-max 归一化:把 TopK 条目的分数映射到 0~1 区间,再设定一个最低阈值,比如 0.3。这样能过滤掉一批“相对接近但绝对不相关”的结果。

第二个是 TopK 的选择。你可能会以为 TopK 越大越好,取 10 条、20 条喂给大模型,信息多总能答对。但实际不是这样:TopK 太大,无关片段会污染大模型的注意力,反而把正确答案带偏。另外,你要考虑 prompt 的 token 预算,比如 4K 上下文的模型,你塞进去 20 个 500 token 的 chunk,光上下文就 1 万 token 了,根本放不下。

我自己的经验是:先粗召回(比如 TopK=20),再做重排,最后保留 top 3~5 条作为上下文。粗召回阶段保证不漏,精排阶段保证不错。单纯用 pgvector 的相似度排序直接取前 5 条,很多时候并不理想。

第三个是注意 SQL 里的LIMIT和ef_search参数。HNSW 索引有一个查询参数hnsw.ef_search,表示搜索时探索的候选数量,默认 40,调大到 100 可以提高召回,但响应时间也会增加。如果你发现某些边缘查询召回不准,可以在会话级别临时调高:

SET hnsw.ef_search = 100;

不过别把这个设置放进全局配置里,否则所有查询都变慢。更合理的做法是,对复杂的查询单独开一个连接设置,或者用插件来处理。

4.3 混合检索与 rerank:多路信号一起对齐

纯向量检索的瓶颈在于:embedding 模型擅长语义相似,但不擅长精确匹配。比如用户问“错误码 404 是什么”,如果你的文档里写的是“HTTP 404 Not Found”,向量相似度可能并不高,因为模型把“404”这个词的语义和“状态码”的语义混在一起了。但如果用全文检索,精确匹配“404”反而很简单。

所以我现在做检索都是“混合检索”:一路是 pgvector 的向量召回,另一路是 PostgreSQL 自带的全文检索(tsvector / tsquery),两路结果合并再做重排。PostgreSQL 本身就有全文检索能力,不用额外装 ES,这也是我选 pgvector 的重要原因。

实现上,可以给文章表加一个 tsvector 列,用触发器维护:

ALTER TABLE documents ADD COLUMN search_vec tsvector GENERATED ALWAYS AS (to_tsvector('simple', chunk_text)) STORED; CREATE INDEX search_vec_idx ON documents USING GIN (search_vec);

查询时用websearch_to_tsquery解析用户输入,和向量检索一起跑:

SELECT id, chunk_text, ts_rank(search_vec, query) AS text_score FROM documents, websearch_to_tsquery('404 not found') AS query WHERE search_vec @@ query ORDER BY text_score DESC LIMIT 10;

然后把向量召回和全文召回的结果合并,去重后交给 reranker 排序。reranker 我一般用一个交叉编码器(cross-encoder),比如 BGE-reranker-base,它会把每一对“问题-片段”做一个联合编码,直接输出相关性分数。这个过程比向量召回慢很多,但因为只对合并后的 20~30 条候选做精排,总体延迟还是可控的。

加入 rerank 之后,你会发现一个明显的改变:以前那些“字面没有匹配、语义也不太上道”的片段被压下去了,真正能回答问题的片段被提到了前面。这一步做的是“召回后的对齐”,相当于把向量检索初选的粗粒度结果,再用模型细粒度拉回正确轨道。

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

5.1 检索效果差的排查路线图

很多同学一遇到“检索不到”就开始怀疑 pgvector,但排查步骤其实有固定的路线。我整理了一个自己常用的排查顺序,能省不少时间。

现象优先怀疑排查方法
相似度普遍偏低(<0.5)embedding 模型或文本语言不匹配换领域更匹配的模型;检查文本是否被截断
召回的片段语义不相关切块破坏了语义边界检查 chunk_text 是否完整;改成按段落/标题切
字面匹配有效但向量召回不到向量模型不擅长精确匹配加全文检索;用 hybrid 检索合并
带过滤条件时召回为空metadata 字段类型不匹配检查 SQL 条件;确认 metadata 值是否一致
响应时间突然变慢索引未生效或参数过小EXPLAIN 查看索引;调大 hnsw.ef_search
相关片段排不到前面缺少 rerank用交叉编码器做精排

这个表看起来简单,但我每次用它都能定位根因。特别第一条,很多中文项目用了默认的英文 embedding 模型,相似度低到离谱,并不是 pgvector 的问题,而是模型和语言不匹配。

5.2 pgvector 特有坑:维度、索引与内存

pgvector 有一些报错和异常,光看错误信息很难定位。最常见的是维度不匹配:

expected 1536 dimensions, not 1024

这种错误一般发生在更换 embedding 模型之后。解决办法是重新建表或加新列,而不是试图强行转换旧数据。所以前面才强调,项目启动时就要把模型固定下来。

另一个容易踩的是索引失效。HNSW 索引在 PostgreSQL 里是增量的,但如果你大批量导入数据(比如一次灌 10 万条),可能会出现索引构建滞后,查询时走全表扫描。怎么确认?用EXPLAIN ANALYZE看执行计划:

EXPLAIN ANALYZE SELECT * FROM documents ORDER BY embedding <=> $1 LIMIT 5;

如果看到Seq Scan而不是Index Scan using ... hnsw,说明索引没有命中。常见原因包括:数据量太小(pgvector 优化器可能觉得全表扫更快)、过滤条件太多导致优化器选择别的路径、或者索引确实没建成功。强制走索引可以用SET enable_seqscan = off;验证,但生产环境不建议长期关闭。

还有内存问题。HNSW 索引会把整张图放进内存,所以数据量大时,shared_buffers 要足够。如果机器内存吃紧,可以先从 IVFFlat 过渡,但后续生产建议加内存。我在一个 500 万行、1536 维的表上做过测试,HNSW 索引的内存占用大约在 5GB~8GB 之间,这个量级需要心里有数。

5.3 对齐调试方法论:从一条 Query 反向定位断点

最后分享一个很实用的调试方法:拿到一条效果差的 query,不要急着改全局参数,而是从这条 query 反向走一遍流水线,看在哪一步断了。

我的做法是三步走。

第一步,把用户 query 原样丢给 embedding 模型,计算 query 向量,然后直接在 pgvector 里做一次不带过滤的 TopK=10 检索,把召回结果的文本逐条看一遍。如果召回结果里根本没有跟用户问题相关的片段,说明问题出在“库里没有能对齐的内容”,可能是切块碎了,也可能文档本身缺这块知识。

第二步,如果召回结果里有一条相关,但排在第 5 名开外,说明“候选有但排序不对”,这时需要调的是索引参数、ef_search,或者加 rerank,而不是怀疑检索。先看看相似度分数,如果第一名 0.80、第五名 0.79,分数差异很小,说明模型区分度不够,换模型或加 rerank 会有帮助。

第三步,如果召回结果里有相关片段,且排在前面,但大模型回答还是不对,那问题就不在检索,而在这几块片段的内容是否足以支撑答案。可能是片段缺了上下文、被截断了,也可能是 metadata 没有附带章节信息,模型不知道这句是哪份文档的结论。这种情况要去调切块和 prompt 模板,让片段更完整地呈现给大模型。

这套调试方法最适合多人协作的项目。团队里有人负责数据、有人负责模型、有人负责工程,一旦效果差,大家容易互相甩锅。用这个流程,可以快速定位“锅”到底在谁头上,谁也不用背锅,谁也不能甩锅。

6. 写在最后的实操体会

踩过这么多坑之后,我自己对 RAG 项目有一个很深的体会:真正决定项目成败的,往往不是某个单一技术的选型,而是整个流水线上每一环是否做到“对齐”。pgvector 是一个可靠的底座,它让我能用一套 PostgreSQL 同时搞定结构化数据和向量检索,省掉了多余的中间件,但代价是我必须更认真地对待数据建模和查询设计。

如果让我给刚起步的团队一个建议,那就是:先别急着把效果差的锅推到检索上。把切块代码打开看看,把召回结果的文本逐条读一遍,把用户问题和小片段放在一起对比一下,你多半会发现,问题出在那个“你以为没问题”的环节。真把对齐这件事做好,你的 RAG 效果会远远超出预期。这也是我把“对齐”而不是“检索”写进标题的原因——我希望后来的人能少走一点我曾经走过的弯路。

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

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

立即咨询