在 Dify 工作流中落地 RAG 效果评估体系
你改了一版提示词,怎么证明没改坏?——在 Dify 工作流中落地 RAG 效果评估体系
本文拆解一个 Dify RAG 工作流中每个环节该看什么指标、怎么看、看到问题怎么修。适合有 Dify 使用经验但尚未建立系统化评估意识的 AI 应用开发者。
一、从一个真实场景说起
你用 Dify 搭了一个客服问答助手。第一周效果惊艳,老板很满意。
第二周,有用户反馈"问了个问题,答得驴唇不对马嘴"。你打开 Dify,改了改提示词,加了几条规则。测了刚才那个问题——好了。上线。
第三周,另一个用户投诉"以前能答对的问题,现在答错了"。
按下葫芦浮起了瓢。
问题的本质是:你没有一套量化的自动化验证体系。每次改动后,你只人工测了 2-3 个问题就上线了,完全不知道这次修改对其他 100 个场景的影响。
核心结论先抛出来:先建测试体系,再谈应用迭代。没有自动化测试兜底的提示词修改,就是赌博。
那这套测试体系怎么建?首先你需要搞清楚一个问题——你的 RAG 工作流出了错,到底该怪谁?
二、在 Dify 工作流中,到底该评估什么?
2.1 先看清你的工作流长什么样
一个典型的 Dify RAG 工作流(以客服 FAQ 为例)通常长这样:
用户提问
↓
[节点1] 提问改写(结合上下文消除指代、补全信息)
↓
[节点2] 意图识别(判断是闲聊/行政/财务/...)
↓
[节点3] 知识库检索召回(混合检索 + Rerank)
↓
[节点4] LLM 整理生成答复
↓
输出给用户
关键认知:RAG 的评估不是给整个工作流打一个总分,而是把每个节点拆开来,分别打分。 就像微服务的链路追踪一样,哪个环节出了问题,就定位到哪个环节去修。
如果最终回答错了:
- 可能是节点 1 改写时丢掉了关键信息(比如把"高意向"丢了)
- 可能是节点 2 意图识别错了,路由到了错误的知识库
- 可能是节点 3 检索没找到正确文档,或者找回来一堆垃圾
- 可能是节点 4 大模型看到了正确文档但自己瞎编
不拆开看,你永远不知道该修哪里。
2.2 RAGAS 四大指标:谁负责评估谁?
RAGAS(Retrieval Augmented Generation Assessment)提供了四个核心指标。它们不是一锅炖的,而是精确对应到工作流中的检索阶段和生成阶段:
┌─────────────────────────────────┐
│ 评估检索阶段 │
│ (你的知识库搜得好不好) │
│ │
│ ┌───────────────────────────┐ │
│ │ 上下文召回率 │ │
│ │ Context Recall │ │
│ │ → 该找到的,找全了吗? │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ 上下文精准度 │ │
│ │ Context Precision │ │
│ │ → 找回来的,有多少是有用的? │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 评估生成阶段 │
│ (你的大模型答得好不好) │
│ │
│ ┌───────────────────────────┐ │
│ │ 答案忠实度 │ │
│ │ Faithfulness │ │
│ │ → 模型有没有瞎编幻觉? │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ 答案相关性 │ │
│ │ Answer Relevance │ │
│ │ → 模型有没有答非所问? │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
这张图就是你在生产环境中的"仪表盘"。 四个指标,两大类,分别诊断两个不同的病灶。下面逐个拆解。
三、检索阶段的两个指标:你的知识库搜得好不好?
3.1 上下文召回率(Context Recall)——该来的全来了吗?
一句话定义:回答这个问题需要的知识片段,你的检索系统捞全了吗?
举例:用户问"查询张三本月跟进的高意向客户明细"。正确回答需要两条信息——跟进人+时间的过滤逻辑,以及意向等级的过滤逻辑。如果知识库只召回了前者,漏掉了后者,召回率就是 50%。
怎么算:
$$\text{Context Recall} = \frac{\text{被召回的相关片段数}}{\text{回答问题所需的全部相关片段数}}$$
生产环境中怎么看这个指标:
| 召回率区间 | 说明 | 行动 |
|---|---|---|
| > 90% | 知识库覆盖良好,检索配置合理 | 维持现状,关注长尾 |
| 70%-90% | 有一定比例的必要信息没被找到 | 检查是否缺少同义词覆盖、是否需要 Query 扩写 |
| < 70% | 严重漏检 | 优先排查:分段策略是否合理?是否需要补充知识库文档?混合检索是否开启? |
召回率低了怎么修?
- 开启混合检索:纯向量检索在遇到生僻人名、编号等专有名词时容易漏,关键词检索靠字面匹配可以兜底。在 Dify 中,知识库设置 → 检索设置 → 选择"混合检索"即可。
- 提问改写/扩写:在工作流的第一个 LLM 节点中,把用户口语化的短问题改写成包含更多关键词的完整问句。比如把"上周北京1分的数据"改写成"查询北京第一分公司上周的拜访数据"——改写后的表述更完整,检索命中的概率更高。
- 知识库分段策略调整:如果一个回答需要的信息被分散在两个段落中,考虑合并为一个完整片段,或者使用 Dify 的"父子分段"模式。
3.2 上下文精准度(Context Precision)——找回来的有多少是废话?
一句话定义:检索系统送给大模型的文档片段中,有多大比例是真正有用的?
举例:知识库返回了 10 个片段,但只有 2 个跟问题相关,其他 8 个都是噪音。精准度就是 20%。
怎么算:
$$\text{Context Precision} = \frac{\text{召回片段中相关的数量}}{\text{召回片段总数}}$$
生产环境中怎么看这个指标:
| 精准度区间 | 说明 | 行动 |
|---|---|---|
| > 80% | 送给大模型的都是干货 | 继续保持 |
| 50%-80% | 有不少噪音混进去了 | 检查 Rerank 配置,考虑提高阈值或减少 Top-K |
| < 50% | 一多半是垃圾 | 紧急排查:知识库文档是否混杂?元数据标签是否缺失?Rerank 是否开启? |
精准度低了怎么修?
- 接入 Rerank 重排序模型:这是目前 RAG 系统的标配。向量检索是"粗排",速度快但不够精。Rerank 模型(如 BGE-Reranker)会把用户的问题和每个召回片段拼在一起细细品味,重新打分排序,把不相关的踢掉。在 Dify 中,知识库设置 → 检索设置 → 开启 Rerank 模型即可。
- 提高 Rerank 阈值:Dify 中 Rerank 有一个 Score 阈值设置。生产环境建议从 0.3 起步调试,逐步提高到 0.5-0.7,观察精准度和召回率的平衡点。阈值设太高会误杀相关文档(召回率下降),设太低等于没过滤。
- 元数据过滤:在知识库中给文档打标签(如部门、业务类型),检索时先用元数据硬过滤,缩小搜索范围后再做语义匹配。
3.3 召回率和精准度的跷跷板
这两个指标天然存在张力。 你把网撒大(召回多一些),召回率上去了,但精准度可能下来;你把网收紧(过滤严格),精准度上去了,但可能漏掉关键信息。
这就是为什么 Dify 的检索配置里有 Top-K(召回数量)和 Score 阈值两个参数——它们就是这架跷跷板的调节杆。
生产上的实战经验:
- 对于 FAQ 客服场景:宁可精准度高一点,因为用户期望得到直接答案,塞一堆无关信息反而让大模型困惑。建议 Top-K 设小(3-5),Rerank 阈值设高。
- 对于复杂分析/报告场景:宁可召回率高一点,因为回答需要综合多条信息。Top-K 可以设大一些(8-15),Rerank 阈值适当放低。
四、生成阶段的两个指标:你的大模型答得好不好?
检索做得再好,大模型拿到了正确的知识片段,还是可能搞砸。生成阶段有两种典型的"搞砸"方式。
4.1 答案忠实度(Faithfulness)——模型有没有瞎编?
一句话定义:大模型的回答内容,是不是都能在召回的知识片段中找到依据?还是它自己脑补了一些东西?
RAGAS 怎么算这个分(它的底层逻辑非常巧妙):
分两步走,用一个"裁判大模型"来自动打分——
Step 1(拆句子):把大模型生成的回答,拆成一条条独立的"原子陈述"。
Step 2(逐一验证):拿每条陈述去检索到的上下文中比对——这句话能从上下文中推导出来吗?能 = 1,不能 = 0。
$$\text{Faithfulness} = \frac{\text{能从上下文推导出的陈述数}}{\text{总陈述数}}$$
一个生产中的真实案例:
用户提问:这款蓝牙耳机续航多久?支持降噪吗?
检索到的知识库内容:
"这款蓝牙耳机单次充电可使用 6 小时,配合充电仓总续航达 24 小时。"
大模型回答:
"这款耳机总续航 24 小时,同时支持最新的主动降噪(ANC)技术。"
裁判模型拆句验证:
- 陈述 1:"总续航 24 小时" → 上下文有提到 ✅
- 陈述 2:"支持主动降噪 ANC 技术" → 上下文压根没提降噪 ❌
忠实度 = 1/2 = 0.5
知识库里没有降噪相关的内容,但模型自己编了一个。这就是经典的幻觉(Hallucination)。
生产环境中怎么看这个指标:
| 忠实度区间 | 说明 | 行动 |
|---|---|---|
| > 90% | 模型很"听话",严格依据上下文 | 维持现状 |
| 70%-90% | 偶尔脑补 | 检查提示词是否明确要求"仅根据上下文回答";考虑降低模型 temperature |
| < 70% | 大面积幻觉 | 紧急排查:模型是否过于"发散"?提示词是否缺少约束?知识库覆盖是否不足导致模型被迫脑补? |
忠实度低了怎么修?
- 提示词中加硬约束:在 Dify 的 LLM 节点 System Prompt 中明确写上"你必须且只能根据提供的上下文回答,如果上下文中没有相关信息,请直接回答'抱歉,我没有找到相关信息'"。
- temperature 设为 0:在 Dify 的 LLM 节点配置中,把温度调到 0(贪心解码),消除模型的随机发散。
- 减少模型决策空间:让模型只做"选择"和"填充",不做"推理"和"创造"。比如在 Text2SQL 场景中,给模型一个 SQL 模板,让它只负责填入 WHERE 条件的值,而不是从零开始写 SQL。
一个重要的认知纠偏:
忠实度低 ≠ 回答错误。模型可能脑补了一个恰好正确的答案(比如上面的降噪例子,也许这款耳机确实支持降噪),但它"超纲"了——回答的依据不在检索到的上下文中。
反过来,忠实度高 ≠ 回答正确。如果检索阶段就送来了一堆错误的文档,模型忠实地复述了错误内容,忠实度是 100%,但答案是错的。
这就是为什么必须检索指标和生成指标一起看,不能只盯一个。
4.2 答案相关性(Answer Relevance)——有没有答非所问?
一句话定义:大模型的回答,是不是在直接回答用户的问题?还是说了一堆正确但无关的废话?
RAGAS 怎么算这个分(这个思路非常反直觉):
它用了一个"逆向检验法"——
Step 1(反推问题):把大模型生成的回答丢给裁判模型,让它根据这个回答反向生成 N 个可能的问题。
Step 2(对比相似度):计算这 N 个反推问题和用户原始问题的向量余弦相似度。
$$\text{Answer Relevance} = \text{Avg}(\text{cos_sim}(q_{\text{original}}, q_{\text{generated_i}}))$$
为什么这招有效? 如果模型的回答确实是在回答原问题,那么从答案反推出的问题,应该跟原问题非常像。如果答非所问,反推出来的问题一定跟原问题风马牛不相及。
举个例子:
用户问:"病假怎么扣工资?"
模型回答:"请假需要提前在 OA 系统提交审批,选择假期类型..."
裁判模型反推的问题:"请假审批流程是什么?"
与原问题的相似度 → 很低(一个问钱,一个问流程)
→ 相关性得分低
模型说的内容可能完全正确(请假确实要在 OA 提交),也完全忠实于上下文(知识库里确实有这段流程说明),但它没有回答用户的问题。根源在于检索阶段送来了流程文档而不是薪资文档(精准度的问题),导致模型只能"就着手头的材料瞎说"。
生产环境中怎么看这个指标:
| 相关性区间 | 说明 | 行动 |
|---|---|---|
| > 85% | 问什么答什么 | 维持现状 |
| 60%-85% | 有跑题倾向 | 先排查检索精准度(可能送错了材料),再检查提示词是否引导模型聚焦问题 |
| < 60% | 严重答非所问 | 联合排查检索和生成:是知识库里根本没有对应内容,还是模型理解力不足? |
五、四个指标联合诊断:一张生产环境的诊断速查表
在生产环境中,单看某一个指标没有意义,需要交叉诊断。以下是常见的"症状-病因-治疗"对照:
| 症状(指标表现) | 病因定位 | 治疗方案 |
|---|---|---|
| 召回率低 + 精准度高 | 检索太保守,有用的文档被过滤掉了 | 降低 Rerank 阈值、增大 Top-K、开启混合检索 |
| 召回率高 + 精准度低 | 检索太宽泛,垃圾信息太多 | 提高 Rerank 阈值、减小 Top-K、增加元数据过滤 |
| 召回率低 + 精准度低 | 知识库本身有问题 | 重新审视文档内容、分段策略、是否缺少关键文档 |
| 忠实度低 | 大模型在瞎编 | 加提示词约束、降 temperature、缩减模型决策范围 |
| 相关性低 + 忠实度高 | 模型忠实地复述了无关内容 | 根源在检索——送错了材料,先修检索的精准度 |
| 相关性低 + 忠实度低 | 检索送错材料 + 模型还自己加戏 | 检索和生成都要修,优先修检索 |
| 四项全高 | 系统运行健康 | 持续监控,关注边界/长尾 case |
诊断的优先级顺序(重要):先看检索,再看生成。
因为如果检索送来的材料就是错的,后面的生成指标就失去了参考意义。你不能指望大模型把一堆垃圾素材变成正确答案。
在 Dify 工作流中的排查路径:
最终回答有问题
↓
先查 [节点3 检索] 的日志 → 看召回了什么文档 → 是不是该来的没来?不该来的来了?
│
├── 是检索的问题 → 调 Rerank / Top-K / 分段策略 / 混合检索
│
└── 检索没问题(正确文档都在)
↓
再查 [节点4 LLM 生成] → 模型是不是忠实?是不是切题?
│
├── 模型幻觉 → 改提示词约束 / 降 temperature
│
└── 答非所问 → 检查提示词是否引导模型聚焦用户问题
六、评估怎么跑起来?生产落地的三步走
6.1 第一步:准备 Ground Truth 测试集
所有评估的前提是:你手里得有一套"标准答案"。
Ground Truth 测试集长这样(以 FAQ 客服为例):
| 用户提问 | 期望召回的文档ID | 期望答案要点 |
|---|---|---|
| 病假怎么扣工资 | doc_salary_005 | 按最低工资 80% 发放 |
| 年假怎么申请 | doc_leave_002 | OA 提交 → 主管审批 → HR 备案 |
| 菏泽1分的拜访量 | doc_visit_template_003 | sql_id: visit_count_by_dept |
没有历史数据怎么办(冷启动)?
RAGAS 提供了一个"合成测试集生成"功能:把你的知识库文档喂给大模型,让它扮演各种类型的用户出题——简单提取题、跨文档推理题、条件变换题——一次性生成几百道测试 QA 对。
但要注意:合成数据只能用于冷启动保底。真正有价值的测试数据来自线上真实用户的提问。上线后要持续收集真实 Bad Case,清洗后补充到测试集中。
6.2 第二步:搭建自动化评估流水线
有了测试集,评估脚本的核心逻辑如下:
# 伪代码:每日自动化评估流水线
results = []
for test_case in test_dataset:
# 1. 调用 Dify API,获取实际输出
prediction = dify_api.run(test_case["user_input"])
# 2. 检索阶段评估
# 路由/召回准确率:预测的文档ID 是否命中期望的文档ID
recall_hit = test_case["expected_doc_id"] in prediction["retrieved_doc_ids"]
# 精准度:召回文档中有多少是相关的
relevant_count = count_relevant(prediction["retrieved_docs"], test_case)
precision = relevant_count / len(prediction["retrieved_docs"])
# 3. 生成阶段评估(调用裁判大模型)
faithfulness = ragas.evaluate_faithfulness(
answer=prediction["answer"],
contexts=prediction["retrieved_docs"]
)
relevance = ragas.evaluate_relevance(
question=test_case["user_input"],
answer=prediction["answer"]
)
results.append({...})
# 4. 输出成绩单
print(f"召回率: {avg_recall:.1%}")
print(f"精准度: {avg_precision:.1%}")
print(f"忠实度: {avg_faithfulness:.1%}")
print(f"相关性: {avg_relevance:.1%}")
# 5. 阈值拦截:低于基线则阻断发布
assert avg_recall > 0.90, "召回率低于 90%,禁止上线!"
assert avg_precision > 0.75, "精准度低于 75%,禁止上线!"
关键配置建议(让裁判大模型打分更稳定):
| 配置项 | 建议 | 原因 |
|---|---|---|
| 裁判模型 temperature | 设为 0 | 消除随机性,同样的评测每次结果一致 |
| Few-shot 示例 | 在评测 Prompt 中提供 3-5 个打分样例 | 给裁判一个"评分标准" |
| 裁判模型选择 | 用比被测模型高一个代差的模型 | 避免"自我偏好偏差"——同款模型评自己的输出会成绩虚高 |
| 交叉验证 | 出题用 A 厂模型,答题用 B 厂模型,裁判用 C 厂模型 | 彻底避免偏差 |
6.3 第三步:线上线下双循环飞轮
评估体系不是"建完就完"的一次性工程,而是一个持续旋转的飞轮:
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 离线评估 │ │ 线上监控 │ │
│ │ (防御阵地) │ │ (进化引擎) │ │
│ │ │ │ │ │
│ │ 每次改动后 │ │ 收集用户 │ │
│ │ 跑全量测试集 │ ←──── │ 点赞/点踩 │ │
│ │ 确保不退化 │ │ 真实 Bad Case │ │
│ │ │ │ │ │
│ └──────┬───────┘ └──────────────┘ │
│ │ │
│ │ 清洗后的真实数据 │
│ │ 补充到测试集中 │
│ ↓ │
│ ┌──────────────┐ │
│ │ 测试集越来越 │ │
│ │ 贴合真实业务 │ │
│ └──────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
- 离线评估:每次修改提示词、调整 Rerank 参数、更新知识库文档后,跑一遍自动化测试集。分数不降才允许上线。这就是你"证明没改坏"的底气。
- 线上监控:用户的点踩 = 一个天然的负向标签。每天从线上日志中筛出 Bad Case,人工审核后补充 Ground Truth,加入到离线测试集中。
- 飞轮效应:冷启动时测试集可能只有合成的 200 条"不够真实"的数据。但随着线上运行,每周都有真实 Bad Case 回流。一个月后,你的测试集就从"玩具级"进化成了"生产级"。
七、回到开头那个问题
你改了一版提示词,怎么证明没改坏?
现在你的回答应该是:
- 我有一个包含 500 道真实业务问题的测试集,每道题都有标准答案。
- 我每次改动后,自动跑一遍全量测试,拿到四个指标的成绩单。
- 召回率 > 90%,精准度 > 75%,忠实度 > 85%,相关性 > 80%——全部达标,上线。
- 任何一项不达标,回滚,定位是检索的问题还是生成的问题,针对性修复。
- 线上持续收集 Bad Case,每周回流到测试集中,评估体系越来越强。
不再赌博,用数据说话。
附录:快速参考卡片
A. 四大指标速查
| 指标 | 评估对象 | 一句话 | 公式核心 |
|---|---|---|---|
| Context Recall | 检索 | 该来的全来了吗 | 召回相关数 / 应召回总数 |
| Context Precision | 检索 | 来的都有用吗 | 召回相关数 / 实际召回总数 |
| Faithfulness | 生成 | 有没有瞎编 | 可验证陈述数 / 总陈述数 |
| Answer Relevance | 生成 | 有没有跑题 | 逆向问题与原问题的平均相似度 |
B. 在 Dify 中对应的调优手段
| 指标异常 | Dify 中调什么 |
|---|---|
| 召回率低 | 知识库 → 检索设置 → 开启混合检索;增加 Top-K;工作流中加提问改写节点 |
| 精准度低 | 知识库 → 检索设置 → 开启/调高 Rerank 阈值;减少 Top-K;给文档打元数据标签 |
| 忠实度低 | LLM 节点 → 加 System Prompt 约束"只根据上下文回答";temperature 设 0 |
| 相关性低 | 先修检索精准度;LLM 节点 → 提示词引导聚焦用户问题 |
C. 一天时间快速落地的优先级
如果你只有一天时间,优先做路由/召回准确率的评估:
- 实现成本最低:
预测文档ID == 期望文档ID ? 1 : 0,不需要调大模型 - 准备门槛最低:Ground Truth 只需
(用户提问, 正确文档ID)两列 - 投入产出比最高:路由错了后面全白做,这是整条链路的咽喉
浙公网安备 33010602011771号