配置 Webhook 后为什么 Flow 会莫名其妙失败?

·踩坑

问题描述

想象一个日常场景: 你考前做了很多模拟题,模拟考每次都满分。信心满满去参加正式考试,结果正式考试却不及格。

你会不会很崩溃?"明明模拟考都成功了,正式考试怎么就失败了?"

更糟糕的是,试卷上还写着"因技术故障导致考试失败",但你完全不知道这个故障具体是什么。


在公司实习期间,我负责测试 Documind Workflow 系统,发现了类似的问题

某次测试时,我按照测试计划在系统里配置了一个"Send Webhook"(发送通知)功能,用来在数据处理完成后通知外部系统。配置时,我点击"Test API"测试按钮,测试成功了,系统提示"API 测试通过"。

于是我勾选"Send Webhook",保存配置,开始上传文件运行 Workflow。

结果:Flow(工作流)在"数据提取完成"后,莫名其妙失败了。


Bug 现象

测试场景

  1. 进入 Workflow 配置页,找到"Actions After Data Processing"(数据处理后的操作)
  2. 勾选"Send Webhook",选择一个已配置的 Send API(通知接口)
  3. 点击"Test API"按钮,系统返回"测试成功"
  4. 保存配置,回到文件上传页面
  5. 上传文件运行 Workflow

系统的反应

测试时

  • Test API 成功
  • 系统提示"API 调用成功"
  • 看起来一切正常

正式运行时

  • Flow 正常运行到"数据提取完成"
  • 然后突然失败,状态变成"failed"
  • 找不到具体的失败原因(系统没有记录 webhook 失败的详细日志)
  • Flow 卡在失败状态,无法继续

取消 Send Webhook 后

  • 再次上传文件运行
  • Flow 正常完成,没有失败

这说明:问题确实是由 Send Webhook 引起的


为什么会这样?

一句话解释:测试环境和正式运行环境的数据 ID 不匹配。

类比说明

就像你模拟考时用的是"练习卷 ID",正式考试时用的是"真实试卷 ID"。但通知系统里,只认"真实试卷 ID",不接受"练习卷 ID"。

当你用"练习卷 ID"去通知外部系统时,外部系统说:"找不到这个试卷 ID",然后直接报错,导致整个考试失败。

技术层面的原因

更详细的解释

  1. 测试环境

    • Test API 时,系统构造了一个模拟的通知数据
    • 数据中的"document_id"(文档 ID)只是为了测试而生成的临时 ID
    • 外部系统收到通知后,不会去数据库里验证这个 ID 是否真实存在
    • 所以测试成功了
  2. 正式运行环境

    • Flow 运行完成后,系统准备发送真实的通知
    • 通知数据中包含了一个"document_id",但这个 ID 其实是"提取结果 ID"(extraction_result_id),不是"真实文档 ID"(processed_document.id)
    • 系统要把这条通知记录保存到数据库(webhook_deliveries 表)
    • 数据库检查:这个"document_id"在"processed_documents"表里是否存在?
    • 结果:不存在!因为这是个"提取结果 ID",不是"真实文档 ID"
    • 数据库报错:"外键冲突,找不到这个文档 ID"
    • 关键问题:这个数据库报错没有被隔离,直接传播到整个 Workflow
    • Workflow 收到报错后,认为"出现了严重故障",直接失败

为什么这么严重?

这个 Bug 的严重性在于:通知失败导致整个 Flow 失败

正常设计的预期

如果通知发送失败,应该:

  • 记录一条"通知失败"的日志
  • Flow 继续运行,不受影响
  • 用户可以查看失败原因,修复通知配置
  • 重新发送通知,或者忽略失败继续处理

实际发生的情况

  • 通知发送失败,数据库报错
  • 报错没有被隔离,直接传播到整个 Workflow
  • Workflow 收到报错后,认为"出现了严重故障"
  • 整个 Flow 直接失败,无法继续
  • 用户找不到失败原因(因为失败日志没有正确记录)

类比说明

就像考试结束后,系统要发一条通知告诉家长"考试结束了"。

如果通知发送失败(比如家长手机号码错误),正常情况下:

  • 系统记录一条"通知发送失败"的日志
  • 考试成绩正常记录,不影响考试本身
  • 家长后来可以从系统里查看成绩

但在这个 Bug 里:

  • 通知发送失败后,整个考试成绩直接作废
  • 考试记录被标记为"失败"
  • 学生和家长都不知道为什么考试失败
  • 完全找不到失败原因

如何复现这个问题?

如果你也遇到类似情况,可以按以下步骤验证:

  1. 进入 Workflow 配置页
  2. 在"Actions After Data Processing"中勾选"Send Webhook"
  3. 选择一个已配置的 Send API
  4. 点击"Test API",确认测试成功
  5. 保存配置
  6. 上传文件运行 Workflow
  7. 观察 Flow 是否在"数据提取完成"后失败
  8. 取消"Send Webhook"勾选,再次运行,观察是否正常

临时解决办法

最快的方法:暂时不勾选"Send Webhook"。

操作步骤

  1. 进入 Workflow 配置页
  2. 找到"Actions After Data Processing"
  3. 取消勾选"Send Webhook"
  4. 保存配置
  5. 重新上传文件运行
  6. Flow 应能正常完成

注意:这只是临时 workaround,意味着暂时无法使用 Webhook 通知功能。


技术细节(给开发者参考)

如果你是开发者,想了解更深层原因:

数据 ID 混淆的根源

  • 系统通过 WorkflowRunService().external_poll_document_id() 设置 runtime webhook context 中的 document_id
  • external_poll_document_id() 返回的是 ExtractionResult.id(提取结果 ID),不是 ProcessedDocument.id(真实文档 ID)
  • Webhook delivery 保存时写入 document_id = context.get("document_id")
  • 数据库外键检查:workflow_webhook_deliveries.document_id 必须指向 processed_documents.id
  • 结果:外键冲突,数据库报错

异常传播的根源

  • send_step_notifications() 没有 safe wrapper(安全包装器)
  • send_run_completion_notifications_safe() 有保护(失败时只记录错误,不传播)
  • 结果:step notification(阶段通知)的异常直接冒泡,导致 Workflow failed

代码位置

前端

  • Webhook 配置:CompletionActionsFooter.tsx, DataProcessingConfigPanel.tsx

后端

  • Webhook 发送:webhook_sender.py:280, webhook_sender.py:814
  • Document ID 设置:run_service.py:2371
  • Delivery 表:workflow_webhook_delivery.py:24
  • Notification 发送:runtime_service.py:1996, runtime_service.py:2023
  • Temporal Workflow:document_workflow.py:417

给新手开发者的建议

设计通知系统时

  1. 测试环境和正式环境要保持一致

    • Test API 用的数据 ID 应该和正式运行时一致
    • 或者 Test API 时明确说明"这是测试数据,不会写入数据库"
  2. 通知失败不应影响主流程

    • 通知是"附属功能",失败不应导致主 Flow 失败
    • 用 safe wrapper 包装通知发送逻辑
    • 异常时只记录错误日志,不传播到 Workflow
  3. 明确区分不同类型的 ID

    • extraction_result_iddocument_id 是两个不同的概念
    • Webhook delivery 的外键应该用真实 document_id,或者允许 FK 为空
    • 不要把临时 ID 写入需要真实 ID 的字段
  4. 失败日志要清晰

    • 用户找不到失败原因,是因为错误没有正确记录
    • 即使通知失败,也要记录"通知失败原因:xxx"
    • 不要让用户面对"莫名失败"的 Flow

总结

这个 Bug 的本质是:测试环境和正式运行环境的数据 ID 不匹配,且异常没有被隔离

  • Test API 成功,用的是临时 ID
  • 正式运行时,用的是"提取结果 ID",但数据库期待"真实文档 ID"
  • 数据库外键冲突,报错没有被隔离
  • 报错传播到 Workflow,导致整个 Flow 失败
  • 用户找不到失败原因

解决方法:暂时不勾选 Send Webhook。

教训

  1. 通知失败不应影响主流程
  2. 测试环境和正式环境要保持一致
  3. 失败日志要清晰,不要让用户面对"莫名失败"

发现日期:2026-07-06
背景:公司实习期间负责测试 Documind Workflow 系统
项目开发者:公司正式员工
问题分类:数据 ID 混淆 + 异常传播未隔离
严重程度:High(影响所有使用 Send Webhook 的 Workflow)