Dify 企业级实验(12):外部系统集成——第三方系统如何通过 Dify API 双向编排?
1. 业务场景
先讲一个我们实际遇到的场景。
项目交付给客户,不是给一个网页,而是给一套可编程的 API + 一套能主动干活的工作流。
客户 OA 里点「智能问答」按钮,调用 Dify 工作流返回答案;另一边,Dify 工作流要主动查 OA 的审批数据。双向打通,Dify 才能真正嵌进客户现有系统——被调时是能力中台,主动时是执行者。我们第一次接这类需求时,第一反应也是「集成嘛,给个链接让客户自己调」。真正动手才发现——只给链接等于没交付:外部系统调不进来、Dify 也调不出去,两头不通,集成就是半吊子。
这不是个例。任何「把 Dify 嵌进客户现有系统」的交付都是这个模式:外部系统调 Dify(Service API)+ Dify 调外部系统(HTTP/工具),两头都要通。
2. 场景痛点
这个流程的痛点,在交付方和客户对接人身上体现得最直接:
- 交付物是网页:客户系统嵌不进去,用户在两个系统之间来回切,体验割裂。
- 单向集成:只会被调、不会主动调,OA 里的数据进不来,工作流成了孤岛。
- 鉴权混乱:一个 Key 通吃所有能力,泄露即全量风险;token 过期不刷新,链路说断就断。
- 外部不可达无兜底:OA 系统挂了,整个工作流跟着 failed,主流程被拖垮。
本质上,集成不是「给个链接」,是「双向可编程」——被调方要有干净的 API,主动方要有降级的能力。
3. 方案:为什么是Service API + HTTP 双向集成
Dify 的 Service API + HTTP 节点(headers 模板 + fail-branch),正好实现双向集成。选它的理由:
- 被调方开箱即用:工作流发布后创建 API Key,外部系统直接调 Service API,每个外部系统独立 Key;
- 主动方动态鉴权:http 节点 headers 支持模板语法,动态带 token,模拟登录→带 token 调用成标准链路;
- 降级兜底:error_strategy: fail-branch + cd_fallback,外部不可达不拖垮主流程。
这篇文章我们就用它搭一套「OA 双向集成」:知识问答(被调方)+ 主动查询(主动方)。
4. 整体架构
两个应用:被调方(知识问答)+ 主动方(主动查询)。
链路很清晰:被调方——OA 调 Service API 拿 answer/sources;主动方——模拟登录取 token → 带 token 调 OA → 成功输出 / 失败降级。两头都留好接口与降级,是双向集成的关键设计。
5. 模块设计
5.1 被调方:Service API 暴露
工作流发布后创建 API Key,给 OA 开发者的调用文档(交付物之一):
POST /v1/workflows/run
Authorization: Bearer app-xxxxxxxx # 每个外部系统独立 Key
{"inputs": {"question": "如何申请退货?"}, "response_mode": "blocking", "user": "oa-1001"}
# 响应 data.outputs: {"answer": "...", "sources": "..."}LLM 节点 system 提示词要求「基于知识回答问题,引用来源以编号形式标注,知识库没有的内容直接说明未找到,不要编造」。
5.2 主动方:登录取 token
cd_token 模拟登录(生产换真实登录接口),输出 token 供 http 节点引用:
def main(token: str) -> dict:
return {"token": token or "mock-oa-token-abc123"}5.3 调 OA:headers 模板引用
http 节点 headers 支持模板语法,动态带 token(实测要点):
url: "http://172.19.0.50:8123/echo?q={{#start.query#}}"
method: GET
headers: "Authorization: Bearer {{#cd_token.token#}}"
timeout: {connect: 10, read: 60, write: 20}
error_strategy: fail-branch5.4 响应解析与降级
cd_parse 解析回显的查询词与 Authorization 头;查询词为空或解析失败 → success=false → cd_fallback 降级提示:
# cd_parse(节选)
data = json.loads(body or "{}")
q = data.get("args", {}).get("q", "")
ok = "true" if q else "false"
detail = "OA 接口返回:查询词「" + str(q) + "」,Authorization=" + str(data.get("headers", {}).get("Authorization", "无"))
return {"success": ok, "data": detail, "raw": str(body or "")[:300]}# cd_fallback
return {"message": "OA 系统暂时不可用,请稍后重试。查询词「" + str(query or "") + "」已记录,恢复后自动补查。"}6. 运行验证
| 输入 | 预期 | 实测 |
|---|---|---|
| OA 调 Dify(带 Key,问题=如何申请退货) | 返回 answer + sources(《售后政策》第 2 章等) | 与预期一致,Service API 返回 data.outputs |
| Dify 调 OA(query=审批单 A2024) | 回显查询词与 Authorization 头,走 end_ok | 与预期一致,输出 OA 回显明细 |
| Key 无效 | 401,错误码明确(文档含错误码表) | 与预期一致 |
| 查询词为空 | cd_parse 判定失败 → 降级提示 | 与预期一致,提示「OA 系统暂时不可用…」 |
| OA 不可达 | 工作流不整体失败,走降级分支 | 与预期一致(error_strategy: fail-branch) |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| API Key 权限过大 | 一个 Key 通吃所有能力,泄露即全量风险 | 每个外部系统独立 Key + 独立授权(102/103 Key 管理实测) |
| 外部 API token 过期未刷新 | 调用突然 401,链路中断 | 统一「登录取 token → 带 token 调用」链路,token 存 KV 供刷新(102-10 实测) |
| http 节点不会自动带鉴权头 | OA 接口返回未授权 | headers 用模板引用动态注入:Authorization: Bearer {{#cd_token.token#}}(实测) |
| 外部系统不可达无降级 | 节点报错,工作流整体失败 | error_strategy: fail-branch + cd_fallback 降级提示(102-17 实测) |
| 无接口文档 | 外部开发对接全靠猜,反复返工 | 交付「请求/响应/错误码」调用文档(实验文档设计约束) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤,含 OA 对接文档样例):DIFY-104-12:外部系统集成——第三方系统通过Dify API双向编排.md
- 源码一(被调方):dify104_12_01_知识问答.yml
- 源码二(主动方):dify104_12_02_主动查询.yml
- 源码目录:dify-104/dsl
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 企业级实验(01):多应用编排——如何让多个 Dify 应用协同完成一条业务链?
- Dify 企业级实验(02):跨应用状态传递——多轮对话的状态如何跨应用不丢?
- Dify 企业级实验(03):事件驱动流水线——Webhook 与定时触发如何组成异步处理链?
- Dify 企业级实验(04):性能优化实战——长流程从 60 秒到秒回有哪些手段?
- Dify 企业级实验(05):Token 成本控制——AI 应用省钱改造怎么做?
- Dify 企业级实验(06):可观测性体系——日志埋点与监控告警如何落地?
- Dify 企业级实验(07):安全与合规——全链路脱敏与权限分级怎么做?
- Dify 企业级实验(08):人机协同审批流——机器预审与人工确认如何配合?
- Dify 企业级实验(09):复杂业务状态机——订单状态流转与非法跳转防护?
- Dify 企业级实验(10):知识库持续更新闭环——数据飞轮怎么转起来?
- Dify 企业级实验(11):企业 API 工具化——如何把客户系统封装成 Dify 工具?
- Dify 企业级实验(12):外部系统集成——第三方系统如何通过 Dify API 双向编排?