Dify 韧性验证实验(06):全链路追踪——一次请求如何在 Dify 中被完整追踪?
1. 业务场景
先讲一个我们实际遇到的场景。
一家做客服工单 SaaS 的公司,应用是跨应用链路:门户对话 → 工单应用 → 通知。用户打电话来说「刚才建单失败了」——运维要回答三个问题:卡在哪个节点、哪一圈、什么原因?没有 trace,只能全链路猜:翻日志、试复现、问开发。更麻烦的是指标造假:看板显示「全部成功」,实际通知已经失败——用户都投诉了,指标还全绿。
我们第一次接这类需求时,第一反应是「Dify 不是自带 node-executions 吗,还要我们自己建可观测性?」。真正动手才发现——平台的执行记录是「有」,能不能回答客户的问题、指标真不真,是另一回事。我们把 trace、日志、指标拆成三层面逐项建,建完再拿「用户说刚才卡住了」的真实场景演练一遍,能定位才算数。
这不是个例。任何「多应用协作、要上线要接客户」的系统都是这个模式:可观测性不是锦上添花——「客户说刚才卡住了」时,你能不能只用日志回答「卡在哪、为什么、影响多大」?
2. 场景痛点
这套链路的痛点,在跨应用协作时体现得最直接:
- 出了事查不到:用户说「刚才建单失败了」——没有 trace,只能靠猜,定位一个节点问题要几小时。
- 跨应用断链:门户调工单(Workflow as Tool),trace 串不起来——问题到底出在哪个应用里,说不清,两边互相推。
- 指标造假:看板显示成功、实际失败——指标和真实行为不一致,比没有指标更危险,因为它让你误以为系统没问题。
- 降级静默:降级分支不输出原因,排查时不知道哪一环降级了——「已降级」三个字都看不到。
本质上,没有 trace 的结论是猜测,有 trace 的结论是证据——可观测性是验收体系的元模块,没有它其它五个模块的验收都跑不起来。
3. 方案:为什么是可观测性建设
Dify 工作流里建设可观测性最系统的方法,就是三层面建设:trace 完整、日志结构化、指标真实。
选它的理由:
- trace_id 贯穿:每个关键节点输出可查,跨应用调用(Workflow as Tool)也能串联——「卡在哪」有据可查;
- 结构化日志统一落 KV:关键事件(任务开始/结束/工具调用/降级/错误)有记录,降级分支输出明确降级信息(不静默)——「为什么」说得清;
- 指标真实可核对:看板统计与 KV 实际记录核对,成功/失败计数与真实行为一致(不造假)——「影响多大」有数。
这篇文章我们就用它搭「门户对话 + 工单流程 + 数据看板」三个应用,把 trace/日志/指标三层可观测性建起来。
4. 整体架构
链路很清晰:门户对话 →(Workflow as Tool)工单应用 → 通知 → 统一日志 → 数据看板。关键设计是 trace_id 贯穿全链路、每圈记录四元组(圈数/思考摘要/工具调用/结果),降级分支必须留痕——出了事能顺着日志一路查到根因。
5. 模块设计
5.1 可观测性建设三层面(本实验核心)
- Trace 完整:每个关键节点输出可查——多出口 end 有明确输出变量名;node-executions 全链路可追
- 日志结构化:关键事件(任务开始/结束/工具调用/降级/错误)有记录;降级分支输出明确降级信息(不静默)
- 指标真实:成功/失败计数与真实行为一致(不造假)
5.2 降级留痕(不静默)
降级分支输出「已降级 + 原因」——D102-P1-01 教训:
def main(reason: str, trace_id: str) -> dict:
return {"status": "degraded",
"reason": reason, # 如实记录,如 notify_failed
"trace_id": trace_id,
"message": "(已降级)通知通道异常,工单已站内记录"}5.3 日志写入(KV 结构化)
def main(trace_id: str, query: str, status: str, app: str) -> dict:
# 写 KV dify105_06_logs:{trace_id, query, status, app, ts}
log_entry = {"trace_id": trace_id, "query": query,
"status": status, "app": app}
return {"log_ok": True, "entry": log_entry}5.4 run_id 取证
workflow blocking 用 data.id(succeeded/partial
一致,实测统一);chatflow 取证路径不同(chat-messages 无 run_id,需
console 触发——先确认 mode)。
5.5 数据看板(指标真实不造假)
定时读统一日志 → 指标统计(总数/成功/降级/按应用分布),指标与 KV 实际记录一致:
def main(logs: str) -> dict:
# 解析 KV 日志 → 统计
return {"total": n, "success": s, "degraded": d,
"success_rate": f"{s / n * 100:.0f}%",
"by_app": {"portal": p, "ticket": t}}6. 运行验证
| 用例 | 输入要点 | 预期 | 结果 |
|---|---|---|---|
| 门户 trace + 日志 | 跑门户对话 | KV dify105_06_logs 记录 portal 2 条(trace_id/query/status=success) | 通过(实测) |
| 工单 trace + 日志 | 触发工单(含一次通知失败) | ticket 2 条(1 success + 1 degraded with reason=notify_failed)——降级如实留痕 | 通过(实测) |
| 指标真实 | 看板统计 | 输出「近 4 条日志,成功 3(75%),降级 1,portal=2; ticket=2」——与 KV 实际记录完全一致,无造假 | 通过(实测) |
| 看板触发 | trigger-schedule 定时(演示 weekly) | console draft run 手动触发验证通过 | 通过(实测) |
| 排障演练 | 「刚才卡住了」场景 | 仅凭可观测性定位根因(卡在哪节点/什么原因/影响范围) | 通过(实测) |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| run_id 取值 | data.id vs workflow_run_id 两态不一致取值错 | blocking 统一用 data.id(succeeded/partial 一致,实测) |
| chatflow 取证限制 | chat-messages 无 run_id | 用 console 触发取证(实测,104/103) |
| 指标造假 | 显示成功实际失败 = P1 | 指标与 KV 实际记录核对,10 次任务注入 2 次失败应如实反映(实验文档设计约束) |
| 节点 title 英文 | 排查时用户按中文 UI 定位 | 节点 title 用中文(用户要求,102) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-105-06:全链路追踪.md
- 源码(可直接导入,可观测加固三应用):
- 源码一(门户对话-可观测加固):dify105_06_01_门户对话-可观测加固.yml
- 源码二(工单流程-可观测加固):dify105_06_02_工单流程-可观测加固.yml
- 源码三(数据看板):dify105_06_03_数据看板.yml
- 全部实验文档目录:dify-105/experiments
- 全部源码目录:dify-105/dsl
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 韧性验证实验(01):故障注入与降级链——如何用故障注入验证 AI 应用的降级链?
- Dify 韧性验证实验(02):契约与消费一致性——多应用协作时契约变了如何第一时间发现?
- Dify 韧性验证实验(03):多轮记忆边界——对话记忆在哪些场景会失效?
- Dify 韧性验证实验(04):安全对抗——如何给 AI 应用做安全对抗测试?
- Dify 韧性验证实验(05):并发与可靠性——高并发下 AI 应用如何保证可靠?
- Dify 韧性验证实验(06):全链路追踪——一次请求如何在 Dify 中被完整追踪?
- Dify 韧性验证实验(07):六模块综合验收——AI 应用上线前如何做六维度验收?
- Dify 韧性验证实验(08):上线后回归——应用上线后如何持续回归验证?