你照着教程跑通了一个 RAG demo,一问真实资料就答非所问;或者第一版能跑,数据一多就超时、限流、成本失控。这篇不讲原理,只讲怎么把一个能演示的 RAG 变成能天天用的应用:先定验收标准,再一步步把链路搭起来,最后补上日志和评测,让你知道它什么时候坏了、坏在哪。
你大概已经照着某篇教程跑通了一个 RAG demo,上传几页文档,问一句问题,模型答得还不错。然后你把真实资料丢进去,它开始答非所问;再然后用户多了一点,接口开始超时、限流,账单也不太对劲。这不是你哪里写错了,是 demo 和生产之间本来就隔着一段工程活。
这篇不讲向量是什么、注意力是什么,那些教程里都有。这里只讲怎么把一个能演示的 RAG 变成能天天用的 RAG:先定验收标准,再把进料、检索、生成、日志、评测这几段串起来,每一段都说清楚怎么做、怎么验证、什么情况下不适用。读完你应该能照着排出自己的开发顺序。
先定验收标准,再动手写代码
大部分 RAG 项目失败不是败在技术,是败在一开始没说清楚「要它答什么」。动手之前先写一句话:这个应用只回答哪一类问题。范围越窄,第一版越容易做对。比如「只回答公司内部产品手册里的功能问题」,就比「回答公司所有知识」好做十倍。同时要明确哪些问题是允许答不上来的,这句话会直接决定你后面提示词怎么写。
然后准备一份固定的评测集,20 到 50 条就够。每条包含一个真实问题和期望答案,这份东西是你项目里唯一的事实来源。以后每次改动——换切分方式、换嵌入模型、改提示词——都拿它跑一遍,看的是整体变好还是只有个别样例变好。没有它,你每次改完只能靠感觉说「好像准了一点」。
- 准备 20 到 50 条真实问题和期望答案,覆盖常见问法和几个边角问法
- 把不可接受的错误分三类:答非所问、编造事实、该答却答不上来
- 三类错误的处理策略完全不同,答非所问多半是检索的锅,编造多半是提示词的锅,答不上来可能只是资料没入库
- 第一版允许一部分问题答不上来,但绝不允许编造,这条底线要写进验收标准
进料环节:文档怎么清洗和切分
进料是最容易被低估的一段,也是最常见的答非所问来源。第一步是分流:纯文本、结构清晰的 PDF、扫描件、图片,处理路径不一样。扫描件该先走 OCR 就走 OCR,别指望后面的模型能读懂一张糊图。分流这一步不做,后面所有环节都在为垃圾数据打工。
切分优先按结构切,标题、段落、列表项,而不是按固定字数硬切。硬切会把一句话劈成两半,两半分别入库,检索时哪半都召不全。切完每块要保留它的标题路径作为上下文,比如「第三章 > 安装 > 环境要求」,这一行信息对检索和生成都有用。每个块还要带上来源、页码、更新时间这些元数据,后面做引用和过滤都靠它。
清洗别跳过。页眉页脚、目录页、重复出现的免责声明、乱码段落,这些混进向量库之后,用户问什么它都能召回一堆噪音。一个实用的判断标准:随便抽十个块读一遍,如果有三个以上你读着都觉得没用,就回去重新清洗。
| 文档类型 | 处理重点 | 容易踩的坑 |
|---|---|---|
| 纯文本 / Markdown | 按标题层级切,保留层级路径 | 把代码块和正文混在一起切 |
| 结构化 PDF | 先解析版式再切,保留页码 | 页眉页脚重复混入每个块 |
| 扫描件 / 图片 | 先 OCR 再走文本流程 | OCR 错误没校对直接入库 |
| 表格 / 数据表 | 整表保留或按行切并带表头 | 按字符硬切,表头丢了 |
向量化与入库:怎么选、怎么省
嵌入模型怎么选,先按中文效果和可获取性筛一轮,别凭某篇博客的结论就定下来。具体型号和实测分数到站内的模型资料库核对当时的数字,站里那份是持续更新的。选完先在本地拿你的评测集跑一遍,看召回质量,比看榜单分数靠谱得多。
入库前先做一次全量试跑,量出这批文档要花多少时间和多少钱。这个数字不用写死在代码里,但你自己心里要有数,它决定你要不要分批、要不要先上缓存、要不要砍掉一些低价值文档。入库脚本要能重跑:重复写入怎么办、增量更新怎么判断、旧版本怎么删除,这三件事一开始就设计好,否则数据一多你会花大量时间手工修库。
- 嵌入模型先在本地拿评测集试召回,别只看榜单
- 向量库先选你能本地跑起来、运维成本低的,数据量没到瓶颈前不用纠结选型
- 入库脚本必须支持重跑,重复写入要幂等
- 删除旧版本的能力要一开始就有,否则库里会长期存在过时内容
检索与拼装:让模型拿到对的上下文
先做纯向量召回,跑通再加关键词检索做混合。一步到位做混合检索,出问题时你分不清是向量那路错了还是关键词那路错了。纯向量跑通之后,用评测集看哪些问题明明库里有一模一样的关键词却没召回,这些就是需要关键词补齐的漏网问题。
控制喂给模型的上下文条数。条数越多不一定越准,反而增加超时风险和成本,还会把噪音一起塞进去。用评测集试出你的拐点:加到某条数之后准确率不再涨,那就停在那里。提示词里明确要求引用来源,并要求资料不足时直接说没有,不要自己编。
排查时要分层。把「检索到了什么」和「模型怎么答」分成两段看:先打印召回片段,如果召回的片段里根本没有答案,那就是检索的问题,改提示词没用;如果片段里有答案但模型答错,那才是生成段的问题。
| 现象 | 先看哪一段 | 典型原因 |
|---|---|---|
| 答非所问 | 检索 | 召回片段本身不相关,切分或嵌入有问题 |
| 编造细节 | 生成 | 提示词没要求引用来源,或上下文里确实没有 |
| 该答答不上来 | 进料 | 资料根本没入库,或切分时答案被劈开 |
| 回答越来越慢 | 检索 + 生成 | 上下文条数太多,或上游在限流 |
超时、并发与降级:生产环境的三条线
生产环境和 demo 最大的差别是:请求会失败,而且失败会传染。先把一次请求拆成若干段计时:向量化、检索、模型生成分别设超时。超了就走降级,而不是无限等,因为一个卡住的请求会占着资源把后面的请求一起拖垮。
并发按上游限额设上限,加排队。别让所有请求同时打出去,触发限流之后要退避重试而不是立刻重试,立刻重试只会让你更快地被限得更死。重试必须有次数上限和幂等判断,否则一次上游抖动会被重试放大成账单事故。
- 分段计时给向量化、检索、生成分别打时间戳,日志里能看到每一段花了多久
- 设超时每段设独立超时,超时抛出的错误要能被上层捕获,而不是让请求挂住
- 排队限流请求先进队列,按上游限额并发放行,队列满了要快速失败并告诉用户稍后再试
- 退避重试限流和超时错误用指数退避重试,设次数上限,超过就降级
- 验证降级手动把上游切掉或调慢,看系统是优雅降级还是直接雪崩
降级方案至少准备两套:少召回几条直接答,或换更快的模型先给一个粗略答案。哪套更合适取决于你的场景——客服问答场景里粗糙但快的答案通常比等半分钟更让人接受,而合同审查场景里宁可让它说「暂时无法回答」。这个选择没有标准答案,但必须提前定下来。
日志怎么记:让一次请求可复盘
日志的目标只有一个:任何一次用户投诉,你都能在几分钟内复盘这次请求发生了什么。要做到这一点,每次请求固定记这几个字段就够了:请求 id、耗时分段、召回命中数、用的模型、token 数、是否命中降级。字段固定下来,后面做统计和告警才有基础。
召回到的文本片段要单独存一份,不要只存最终回答。出问题时先看检索结果对不对,再看模型回答,这个顺序能帮你节省大量时间。存片段还有个好处:你可以定期抽出来看,发现哪些低质量文档一直在被召回。
- 请求 id:串起一次请求的每一段日志
- 耗时分段:向量化、检索、生成各花了多久
- 召回命中数:这次召回了多少条,命中几条
- 模型与 token 数:用于成本和性能分析
- 降级标记:这次是不是走了降级路径
成本按天汇总并设阈值告警。成本突然上涨通常不是用户变多了,而是重试失控或者召回条数被某次改动调大了。到站内的价格与用量相关页面核对当时的单价,再乘上你自己的调用量,就能算出每天大概的消耗,不用自己维护一张单价表。
评测闭环:改完到底有没有变好
评测要分成两层:检索效果和回答质量。检索没召回到的东西,再好的模型也答不出来,所以如果回答质量下降,先回头查检索是不是退化了。两层分开评,你才知道该改哪一段。
小改动就用固定评测集跑一遍,看的是整体稳定变好还是只有个别样例变好。如果只有一两条变好、另外几条变差,那这次改动多半不值得上线。评测集覆盖不到的边角问题,只能靠线上抽真实问题定期人工看,这一步省不掉。
每次改动记一行:改了什么、评测结果、是否上线。这不是形式主义,等你两个月后回头看「为什么当时换了提示词」的时候,这一行能救你半天。
一个 RAG 应用能不能长期用,不取决于它答对过多少题,取决于它答错的时候你能不能快速知道错在哪。下一步搭完之后,先别急着扩功能。到站内的中转站可信度检测和价格追踪页面看一眼你正在用的渠道当时的稳定性与报价,再回来看你的超时和降级参数设得合不合理。→
常见问题
搭一个 RAG 应用最少需要哪些组件?
四块:文档处理(清洗加切分)、嵌入模型、向量库、一个大模型接口。再加一个评测集和一份日志。前四块决定能不能跑,后两块决定你敢不敢让它上线。先用本地能跑起来的方案,别一上来就堆服务。
为什么我的 RAG 回答总是答非所问?
多数情况不是模型的问题,是检索没召回对的片段。先打印出每次实际召回的文本,人工看一眼对不对。如果召回就是错的,去查切分粒度和文档清洗;如果召回对了但答错,再去看提示词和上下文长度。
检索几段资料比较合适?
没有固定值,取决于你的文档粒度和问题类型。做法是固定评测集,从少到多逐档加,看回答质量和响应耗时在哪一档开始变差,取拐点前那一档。别默认越多越好,多出来的片段既加成本又容易干扰模型。
RAG 应用上线后最该盯什么?
盯三件事:响应耗时是否稳定、降级是否被触发、成本是否异常上涨。这三项一旦波动,通常不是模型变差,而是上游限流、检索召回变多或者重试失控。把这三项做成每天的固定检查项,比事后排查省事得多。
一定要做评测集吗?
如果只是自己玩的 demo,可以不做。只要打算给别人用,就至少要准备几十条真实问题和期望答案。没有它,你每次改切分、换模型、调提示词都只能凭感觉,改坏了也不知道是哪次改坏的。