深入专题:RAG、Eval 与线上治理¶
RAG Demo 很容易:
生产难点是:
答错时要知道是资料没进库、切块错误、检索没召回、排序不好、模型没遵循证据,还是权限过滤错了。
把 RAG 拆成两条流水线¶
入库流水线¶
查询流水线¶
只有看到两条链,才能定位故障。
入库不是“读取 PDF”¶
文档可能包含:
- 页眉页脚;
- 表格;
- 多栏排版;
- 扫描图片;
- 章节层级;
- 版本号和生效日期;
- 重复附件;
- 已废止规则。
如果抽取阶段把表格列顺序打乱,后面的 Embedding 再好也无法恢复事实。
因此每个 Chunk 至少需要:
{
"chunk_id": "doc-17:v3:section-4:chunk-2",
"document_id": "doc-17",
"document_version": "v3",
"title": "设备离线处理规范",
"section_path": ["运维规范", "离线告警"],
"effective_at": "2026-01-01",
"tenant_id": "T-2",
"access_level": "internal",
"source_uri": "...",
"content": "..."
}
元数据不是装饰,它参与权限、过滤、引用、更新和删除。
Chunk 大小没有万能答案¶
过小:
- 语义不完整;
- 条件和结论被拆开;
- 表格标题与数据分离;
- 检索到片段但无法回答。
过大:
- 一个 Chunk 包含多个主题;
- 相似度被稀释;
- 上下文成本高;
- Top K 中冗余严重。
应通过具体问题实验:
记录:
- 正确证据是否进入 Top K;
- 每次上下文 Token;
- 答案正确率;
- 延迟和成本。
“500 字一块”只能是起点,不是结论。
为什么纯向量检索会漏¶
向量检索擅长语义相似:
但对精确词可能不稳定:
- 设备型号
GW-XJ-210; - 告警码
E042; - 订单号;
- 人名或缩写;
- 法规条款号。
关键词检索对精确词更强。因此常用混合检索:
融合可使用 Reciprocal Rank Fusion 等方法,再用 Cross Encoder 或其他重排模型评估问题与 Chunk 的相关性。
Metadata Filter 是安全步骤¶
错误流程:
敏感信息已经进入模型上下文,边界已经失守。
正确流程:
权限过滤应由检索层或数据库执行,不能由模型自觉完成。
文档更新和删除为什么麻烦¶
文档 v2 发布后:
- v1 Chunk 是否还在索引中?
- 搜索是否同时命中旧版和新版?
- 原文删除时向量是否同步删除?
- 缓存答案是否仍引用旧内容?
- 已运行任务能否重放当时版本?
建议使用稳定文档 ID 和版本:
不要在生产索引里边删边写,导致用户看到半套资料。
生成答案必须区分证据和推断¶
建议输出结构:
{
"answer": "...",
"claims": [
{
"text": "离线超过 90 秒触发告警",
"citations": ["chunk-21"]
}
],
"uncertainties": ["文档未说明弱网环境是否延长阈值"],
"used_sources": ["doc-17:v3"]
}
引用不是只显示“来源 1”。系统需要验证:
- 引用的 Chunk 确实存在;
- 用户有权访问;
- 引用内容支持对应主张;
- 版本仍有效;
- 模型没有编造 chunk_id。
RAG 的四类评测¶
不要把所有问题压成一个“答案得分”。
| 评测对象 | 问题 | 典型指标 |
|---|---|---|
| 检索 | 正确证据是否被召回 | Recall@K、MRR、人工相关性 |
| 忠实度 | 答案是否由证据支持 | Groundedness / Faithfulness |
| 正确性 | 与已知正确答案是否一致 | Exact/规则/语义评分 |
| 有用性 | 是否真正解决用户问题 | 人工评分、任务完成率 |
一个关键诊断矩阵¶
| 正确证据召回 | 最终答案正确 | 可能问题 |
|---|---|---|
| 否 | 否 | 入库、切块、查询或检索问题 |
| 是 | 否 | 上下文组装或生成问题 |
| 否 | 是 | 模型可能靠参数知识碰巧答对,仍不可靠 |
| 是 | 是 | 成功样本,但仍检查权限和引用 |
只看最终答案会掩盖“模型碰巧知道”的问题。
Eval 数据集怎样建立¶
第一批不必有 1000 条。先为每个关键能力人工挑 5~10 个高价值样本:
- 正常高频问题;
- 容易混淆的相似问题;
- 必须拒答的问题;
- 没有资料的问题;
- 跨租户权限问题;
- 精确型号或告警码;
- 多跳问题;
- 文档互相冲突;
- Prompt Injection 文档;
- 历史线上失败案例。
每条数据最好包括:
{
"case_id": "rag-018",
"question": "E042 如何处理?",
"expected_document_ids": ["manual-v3"],
"expected_facts": ["检查网关供电", "确认通信线"],
"forbidden_facts": ["恢复出厂设置"],
"expected_behavior": "answer",
"tags": ["exact-code", "maintenance"]
}
数据集是一项产品资产,不是随手收集的聊天记录。
先用确定性评测¶
能够用代码判断的,不要先交给另一个模型:
- JSON Schema 是否合法;
- 必填字段是否存在;
- 工具名称是否正确;
- 参数是否精确;
- 是否调用禁用工具;
- 引用 ID 是否存在;
- Token、延迟、费用是否超阈值;
- 是否命中预期文档;
- 是否泄露测试中的敏感标记。
这些结果稳定、便宜、容易定位。
LLM-as-Judge 的价值和限制¶
它适合评估:
- 表达是否完整;
- 解释是否有帮助;
- 答案与参考答案语义是否一致;
- 答案是否由上下文支持。
但 Judge 也会:
- 受提示词和模型版本影响;
- 对长答案有偏好;
- 对某些语言或表达有偏差;
- 被待评答案里的指令干扰;
- 与人工专家意见不一致。
使用方法:
- 先定义清晰评分 Rubric;
- 使用结构化评分和理由;
- 用人工标注集校准 Judge;
- 定期抽查;
- 重要指标不只依赖一个 Judge;
- 版本升级后重新验证一致性。
Agent 不只评最终答案¶
Agent 可能给出正确答案,却走了危险或昂贵路径:
因此需要评估轨迹:
- 是否选择正确工具;
- 参数是否正确;
- 调用顺序是否合理;
- 是否有冗余调用;
- 是否触碰高风险工具;
- 是否在正确节点请求人工批准;
- 总步骤、延迟和成本;
- 最终任务是否完成。
回归测试要比较版本矩阵¶
一次发布可能同时变化:
如果全部一起改,结果变差时很难归因。
每次运行记录:
{
"model": "provider/model-version",
"prompt_version": "diagnosis-v7",
"toolset_version": "tools-v4",
"retriever_version": "hybrid-v3",
"index_version": "kb-2026-07-20",
"workflow_version": "wf-v5"
}
先单变量实验,再做整体候选版本回归。
发布门禁不是“平均分更高”¶
假设新版本:
- 平均正确率从 86% 升到 88%;
- 高风险操作误调用从 0% 升到 1%。
不能因为平均分变高就发布。
门禁可分层:
不同失败的代价不同。
线上观测要记录完整因果链¶
一次运行至少能关联:
trace_id
run_id / task_id
user / tenant(脱敏后)
模型与版本
Prompt / Tool / Workflow / Index 版本
每一步输入输出摘要
工具参数与结果状态
检索文档 ID 和分数
Token、费用、首 Token 和总延迟
重试、错误、人工审批
用户反馈与最终状态
不要默认把完整 Prompt、用户原文和工具结果全部写日志。应:
- 敏感字段脱敏;
- 大对象只存引用;
- 设置保留期限;
- 按权限查看 Trace;
- 区分调试环境和生产环境。
OpenTelemetry 的 Trace、Metric、Log 可以形成统一关联:
成本要按“完成一个任务”计算¶
单次模型调用便宜,不代表 Agent 任务便宜。
应记录:
- 单任务模型轮数;
- 输入/输出 Token;
- 缓存命中;
- 每类工具调用次数;
- 成功任务成本;
- 失败任务成本;
- 不同租户或场景的预算。
降低成本的顺序通常是:
- 减少无意义步骤;
- 减少无用上下文;
- 确定性逻辑不调用模型;
- 简单任务路由到便宜模型;
- 缓存稳定结果;
- 最后才是单纯压缩答案字数。
Prompt Injection 的生产防线¶
RAG 文档、网页、邮件和 Tool 返回都属于不可信输入。
分层防御:
没有一种 Prompt 能彻底解决注入。即使模型被诱导,也应因为工具权限不足而无法造成严重后果。
完整实验:优化“设备规范问答”¶
基线¶
建立 60 条数据集¶
- 20 条语义问题;
- 10 条精确告警码;
- 10 条多步骤问题;
- 10 条无答案/应拒答;
- 5 条跨租户;
- 5 条注入攻击。
先测检索¶
发现:
说明不是生成问题,而是纯向量搜索不擅长精确代码。
改成混合检索¶
全文检索与向量召回融合,重排后:
再测生成¶
正确证据已召回,但一些答案把“建议”说成“必须”。于是:
- 调整回答结构;
- 要求区分规范原文、解释和不确定性;
- 对关键事实做引用校验。
发布判断¶
这才是一轮可解释的 RAG 改进,不是“感觉回答变好了”。
开源方案的务实组合¶
个人学习和中小型项目可以从:
开始。优点是组件少,能真正理解数据、检索、状态和观测。
当数据量、团队协作或搜索能力明确超出边界,再评估专用搜索引擎、向量数据库和托管观测平台。不要为了“像大平台”提前引入十几个组件。
权威延伸资料¶
- pgvector 官方项目:向量索引、精确/近似搜索及与 PostgreSQL 全文检索组成混合搜索;
- RAG 评测教程:分别评测检索相关性、答案忠实度、相关性和正确性;
- OpenTelemetry Signals:理解 Trace、Metric、Log 的不同观察角度;
- OWASP Prompt Injection Prevention:对不可信文档和 Agent 工具的分层防护。
自测¶
- 为什么 RAG 必须拆成入库链和查询链?
- Chunk 越小或越大分别会造成什么问题?
- 为什么设备型号、告警码更适合加入关键词检索?
- Metadata Filter 为什么属于安全控制,而不只是搜索优化?
- 检索到了正确证据但答案错了,应优先排查哪一段?
- 哪些 Eval 应优先使用确定性代码?
- LLM-as-Judge 为什么需要人工校准?
- 为什么 Agent 最终答案正确仍可能判为失败?
- 新版本平均正确率提高,为什么仍可能不能发布?
- Prompt Injection 为什么无法只靠 Prompt 防御?
参考答案要点
- 入库决定知识是否正确进入索引,查询决定是否正确找回并生成;不拆开就无法定位失败。
- 太小会失去上下文和语义完整性;太大会混合主题、稀释相似度并增加 Token。
- 这些是精确符号,语义向量可能不稳定,全文或关键词匹配更直接。
- 必须在不可信内容进入模型前排除无权数据,否则即使最终不展示也已越过数据边界。
- 上下文组装、提示要求、模型生成和引用绑定,而不是继续盲目调检索参数。
- Schema、字段、工具、参数、引用存在性、权限、成本和延迟等可精确判断的指标。
- Judge 同样是概率模型,存在版本、表达和长度偏差,必须和专家标注对比。
- 它可能走了危险、违规、昂贵或冗余的工具轨迹。
- 安全红线、核心场景、延迟或成本可能退化,平均值会掩盖高代价失败。
- 注入利用自然语言中指令与数据边界模糊;必须用权限、工具限制、审批和监控控制后果。
完成标准¶
- 能画出 RAG 入库和查询两条完整流水线
- 能用失败矩阵区分检索问题与生成问题
- 能设计包含拒答、权限和注入样本的 Eval 集
- 能区分确定性指标与 LLM-as-Judge
- 能为候选版本设计安全、质量、延迟和成本门禁
- 完成 Agent Eval 实验
回到主线总结:从 Demo 到生产系统