从零搭一个rag系统要做什么

接入与开发

给开发者梳理从零搭一个 RAG 系统的落地步骤:文档进料、切片入库、检索拼装、上线后的超时并发与评测清单。

很多人第一次搭 RAG,卡的不是原理不懂,而是动手时不知道先做哪一步:资料怎么洗、切多大、向量存哪、检索怎么拼进提示词、上线后超时和并发怎么办。这篇按工程顺序给你一条能照着走的路径,每一步都给出判断标准和验收动作,并标出哪些环节容易返工。

第一步:先把范围和数据进料定死

很多人一上手就急着选向量库、调 embedding,结果做到一半发现答非所问。问题往往不在技术栈,而在于开始前没人把「这套系统到底要回答什么」写清楚。范围和数据进料这两件事定不死,后面每一轮调优都是在猜。

回答边界
明确哪些问题该进这套系统、哪些直接拒答,以及拒答时统一说什么话
数据进料
原始文档进库前的清洗与格式统一,决定后续检索的上限
切片粒度
一份文档按什么单位切成小段,直接影响召回的相关性
最小验收集
一批带标准出处的人工标注问题,用来判断系统是否真的可用

回答边界先写成一句话规则

  • 能答的:只覆盖你实际喂进去的文档范围,比如内部制度、产品手册、常见故障处理
  • 该拒答的:询问未收录主题、要求预测未来、涉及个人隐私或超范围承诺
  • 拒答话术:统一为「这部分资料里没有,建议联系 XX」,别让模型自由发挥编一个
  • 边界写完后回头核对:每条边界都能对应到「有文档」或「没文档」两种状态

边界不清最典型的后果,是模型把「文档里没写」当成「可以推理」,输出看着通顺但和事实对不上。所以边界不是写完就锁死,每次新增一类文档,都要重新判断它是否扩大了能答的范围,以及原来那条拒答话术还成不成立。什么情况下不适用:如果你的系统只是做文档检索、不生成答案,那拒答话术可以简化,但「哪些能查、哪些查不到」的界定仍然要写。

进料清洗:脏数据进了库,后面全白搭

  1. 去页眉页脚与水印把每页重复出现的公司名、页码、密级标记删掉,否则这些词会大量出现在切片里,干扰检索权重
  2. 还原多栏排版双栏 PDF 直接复制文本常串行,先确认阅读顺序是否正常,串了就按栏切分再合并
  3. 扫描件先做识别图片型 PDF 必须先过 OCR,识别后抽查几页错字率,错得多的那批宁可人工校一遍也别直接入库
  4. 统一格式再入库表格、清单、纯文本段落分开处理,别指望一套解析逻辑通吃,格式混乱会让切片质量断崖式下跌
内容形态切片单位理由
条款、制度、规范按条目切一条一个完整意思,拆开就丢前提
说明、教程、叙述按自然段落切段落本身就是作者划好的语义单元
代码、配置、脚本按函数或配置块切半截代码检索出来没法用
表格、参数清单整表或按行切行列关系断了,数字就对不上

切片粒度没有万能值,固定字数一刀切是新手最常见坑。条款类拆成半条,回答就会漏掉限定条件;说明类切太碎,上下文接不上;代码切到函数中间,模型看到的就是语法错误的片段。判断标准很简单:随机抽几段切片单独读一遍,如果你自己都看不出它在说什么,那检索出来也没用。不适用的情况是,如果你只想做关键词级别的全文检索,粒度可以粗一些,但只要涉及语义匹配就绕不开这件事。

给自己定一个最小验收动作

  1. 准备一批真实问题数量不用多,先把日常被问得最多的那些整理出来,覆盖不同文档来源,别全挑同一份文件里的
  2. 人工标注出处每条问题写下答案应该出自哪份文档、大概哪一段,这一步是后面判断检索对不对的唯一依据
  3. 标出该拒答的样本故意放几条资料里没有的问题,看系统会不会硬答,这类样本比能答对的更能暴露问题
  4. 跑通后逐条比对看检索命中的文档是否和标注一致,不一致的先查切片和清洗,别急着动模型参数

最后提醒一句顺序:先把边界和进料这两件事做完,再去碰检索和生成环节。顺序反了,你会在错误的数据上反复优化一个本来就不该问的问题。想对照不同中转站在文档解析和调用稳定性上的表现,可以到 中转站导航(/zhongzhuan) 看当时的实测记录,但记住那只是参考,你自己的进料质量才是决定性的。

第二步:入库和检索跑通一条链路

这一步的目标不是把系统做漂亮,而是让「文档进去、问题出来、结果能看见」这条链路完整跑通一次。判断标准很简单:给一条查询,能把召回的原文片段打印在终端里,你能逐条判断「这条相关、那条不相关」。做不到这一点,后面调什么都是在猜。

  1. 先定向量库别急着比性能,先看三件事:能不能本地一条命令起起来、有没有现成的写入和删除接口、换个库迁移成本高不高。本地起不来就得先解决网络和账号问题,会拖慢整条链路。具体功能差异到 /stacks/vector-dbs 翻目录对照,按你的部署环境筛。
  2. 把嵌入模型固定下来嵌入这一环只关心「召不召得回」。先用一个能本地跑、你团队能维护的嵌入模型,把文档切块后写进库,跑几十条查询看召回片段在不在前面。别在这一步同时换切块策略和嵌入模型,否则出了问题分不清是谁的锅。
  3. 只跑纯向量召回第一版就用纯向量相似度检索,不做关键词、不做重排、不做多路融合。链路越短越容易定位问题。等纯向量召回的相关性稳定了,再评估要不要叠关键词召回做混合。
  4. 把结果打印出来人工看写一个最小脚本,输入问题,输出前几条召回片段的原文和来源。逐条标注相关与不相关,记下不相关的原因是切块太碎、还是问法跟原文差太远。这一步花十分钟,比调一堆参数更有用。
环节决定什么怎么验证
向量库数据怎么存、怎么增删改本地能起、能插能删、能按 id 取回
嵌入模型召回的片段相不相关人工看前几条召回是否命中
生成模型回答的表达和结构好不好拿同一批召回片段,换模型对比输出
检索策略先纯向量还是加混合纯向量稳定后再叠加,逐项验证
  • 向量库选型优先看本地可起、接口齐全、迁移成本,功能对比去 /stacks/vector-dbs 翻目录
  • 嵌入模型决定召回,生成模型决定表达,分开评估、分开换
  • 第一版只跑纯向量召回,混合检索等链路稳定后再加
  • 召回结果必须能打印原文和来源,人工看一眼是最直接的判断
  • 每次只改一个变量,改完重新跑同一批查询做对比

第三步:把检索结果拼进提示词并约束输出

检索做完,最后一步就是把这些片段塞进提示词交给模型。这一步看着简单,坑却最多:顺序乱、来源丢、片段贪多,都会让模型答得又飘又贵。下面按能直接照抄的方式讲一遍。

  1. 固定拼装顺序按「系统约束 → 检索片段 → 用户问题」三段拼。系统约束放最前,写清角色和回答边界;中间是检索片段,每段前面带一个来源标识,比如方括号里写文档名或链接;用户问题放最后,紧挨着让模型回答的位置。顺序固定下来,排查问题时才容易定位是约束没写清还是片段没找对。
  2. 给片段加分隔和来源片段之间用统一的分隔符隔开,别让两段内容粘成一坨。每段开头标来源,结尾可以标一句这条片段在说什么。这样模型引用时有东西可指,你回查时也能按来源直接翻原文。
  3. 约束输出行为在系统约束里明确两句话:只依据给定片段回答;片段里找不到就直说找不到,不要编。再要求它在答案里带上来源标记,比如用了哪条片段就标哪条。这个约束不是万能的,遇到模型上下文被撑满或者片段本身互相矛盾时,它还是会答偏,所以别把它当成质量保证。
片段档位适用问题类型给几条
少事实型、单点查询按站内 指南中心(/guides) 里当前的推荐档位取偏小值
中解释型、需要两三条材料对照取中间档位
多对比型、跨文档汇总取偏大值,但仍要设上限
不适用问题本身没有对应片段不硬塞,直接走「答不出来」这条路
  • 片段数量别贪多,塞太多既费 token 又稀释重点,模型反而抓不住主线
  • 来源标识要统一格式,别一半写文件名一半写链接,回查时会乱
  • 用户问题放最后,别夹在片段中间,容易被前面的材料带跑
  • 每次改动提示词都记一笔版本,不然出了偏差没法回溯
  • 改完别凭感觉说变好了,用那 20 条问题重跑一遍再对比

常见问题

没有标注数据,怎么判断 RAG 搭得好不好?

先攒一份人为挑的真实问题清单,几十条就够,每条人工写出「应该引用哪份文档」。跑完看两件事:该召回的有没有召回、回答里引用的来源对不对。不用追求指标漂亮,能稳定发现退化就够用。

切片大小到底设多少?

没有通用答案,取决于你的文档形态。做法是先按段落或条目切,然后抽十条问题看召回效果:召回片段太碎、上下文不够就调大,片段里塞了太多无关内容就调小。定完记得写进配置并记录版本,别随手改。

检索和生成哪个更值得先优化?

优先看检索。生成模型再强,如果召回的片段本身不相关,回答也只会一本正经地跑偏。先把召回的前几条人工看一遍,确认相关再回头调提示词和模型,返工成本低很多。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。