:企业系统对接工具——如何用插件对接企业 ERP/CRM?)
Dify 插件开发实验04企业系统对接工具——如何用插件对接企业 ERP/CRMDify 实验系列 · 插件开发 04/12 | 实验编号DIFY-106-04基于 Dify 1.16.1 实测2026-081. 业务场景先讲一个我们实际遇到的场景。客服工单 SaaS 的客服处理工单时经常要查企业内部的 ERP 和 CRM用户问「我的订单 DTF-ORD-001 发货了没」客服要去 ERP 查订单状态、金额、物流用户问「我这个客户的等级是什么」客服要去 CRM 查客户等级、联系人和历史工单数。这些查询散落在客服的日常工作里每次都要切系统、手动查、再回来答复。我们第一次接这类需求时第一反应也是「写个 HTTP 请求调接口不就行了」。真正动手才发现——企业系统对接的难从来不在「调通」而在「怎么调得稳、换得动」真实系统在内网不可直连联调连个环境都没有每家客户的网关地址还不一样写死一个就换不了客户mock 联调时返回的字段和真实系统对不上上线才发现解析全错。这不是个例。任何「客服/业务系统要对接企业 ERP、CRM、内部系统」的场景都是这个模式订单查询、客户查询、库存查询、发票查询——真实环境里这些系统往往不可直连、地址各不相同、接口契约还随时可能变。2. 场景痛点这个流程的痛点在对账查询时体现得最直接系统不可直连企业 ERP/CRM 在内网外部应用不能直接访问联调时根本没有真实环境可用。每个客户环境地址各异这家客户的网关是http://10.0.1.5那家是https://gw.xxx.com——地址写死在代码里换一家客户就要改代码重新打包。契约不一致mock 联调时返回的字段和真实系统不一样下游解析直接错位上线才发现。错误码一团乱上游返回 404、500、超时工具层不映射就直接抛给下游业务无法区分「没有这个订单」和「系统挂了」。本质上企业系统对接的难点不在「调通一个接口」而在「契约与环境的可管理性」——地址要能配置、返回要能对齐、错误要能分层。3. 方案为什么是工具插件选工具插件我们实际对比过mock/真实双模式本地 uvicorn mock 出与真实 API 返回结构一致的契约开发期不依赖真实系统base_url 配置化服务地址放 credentials客户环境改凭证即可插件代码零改动——mock↔真实切换只改一个配置契约表 错误码映射落地订单/客户各 4 个返回字段、四类错误码统一下游消费有据可依。这篇文章我们就用它搭一个对接企业系统的工具插件order_query按订单号查 ERP 订单customer_query按客户 ID 查 CRM 客户跑通契约设计、mock 联调与配置化切换的全流程。4. 整体架构客服对话/工单流程order_query / customer_query工具插件credentialsbase_urlmock↔真实切换点 api_keymock 服务本地 uvicorn返回契约结构GET /api/orders/{id} → 返回order_id、status、amount、trackingGET /api/customers/{id} → 返回customer_id、level、contact、tickets超时/重试requests timeout10错误码映射404→not_found、500→upstream_error、超时→upstream_error开始order_id customer_idorder_querycustomer_query汇总输出结束链路很清晰入口收单号/客户号 → 工具查 ERP/CRM → 错误码映射 → 汇总输出。关键设计是「契约先行」——返回字段和错误码在动手写代码前先定成契约表mock 与真实系统都按契约实现切换才不出错。5. 模块设计5.1 多工具插件结构provider yaml 的 tools 列表credentials_for_provider:api_key:type:secret-inputrequired:truebase_url:type:text-inputdefault:http://host.docker.internal:8003# 宿主机 mock 服务地址required:falsetools:-tools/order_query.yaml-tools/customer_query.yaml一个 provider 声明多个工具各自tools/*.yaml*.py公共逻辑放tools/common.py官方 dify_extractor 有 helpers.py 先例打包验证通过。5.2 公共请求模块tools/common.pydefrequest_json(base_url:str,api_key:str,path:str):GET 请求返回 (成功数据, 错误 JSON 字符串)。成功时错误为 None。try:resprequests.get(f{base_url}{path},headers{X-API-Key:api_key},timeout10)exceptrequests.exceptions.RequestExceptionase:returnNone,err(_ERR_UPSTREAM,fenterprise system unreachable:{type(e).__name__})ifresp.status_code401:returnNone,err(_ERR_AUTH,authentication failed, check api_key)ifresp.status_code404:returnNone,err(_ERR_NOT_FOUND,fresource not found:{path})ifresp.status_code!200:returnNone,err(_ERR_UPSTREAM,fenterprise system returned HTTP{resp.status_code})try:returnresp.json(),NoneexceptValueError:returnNone,err(_ERR_UPSTREAM,invalid JSON response from enterprise system)两个工具复用同一request_json参数校验ORD-/CUS- 正则各自在工具代码内做字段缺失用防御性 get 兜底。5.3 base_url 配置化切换零改代码base_url放 credentials 而非写死在代码客户环境把凭证里的地址改成真实 ERP/CRM 网关即可插件代码零改动。mock↔真实切换实测base_url 改错误地址 → upstream_error改回 → 恢复。5.4 插件网络路径与 http 节点不同实测结论工作流 http 节点走 ssrf_proxysquid 172.16.0.0/12 白名单插件内 requests 直连出口不经代理——daemon 容器访问宿主机用host.docker.internal内网地址访问无 SSRF 限制需在插件代码层自行控制生产环境靠企业网络策略收口。6. 运行验证输入预期结果mock 服务 curl 两端点订单/客户契约结构正确通过order_query正常订单订单详情结构正确通过customer_query正常客户客户信息结构正确通过不存在的订单/客户not_found通过错误 api_keyauth_failed通过mock 服务停止upstream_errortimeout10 生效通过workflow 集成双工具串联订单错误不影响客户查询通过切换 base_url错误地址 → upstream_error改回 → 恢复通过配置化生效零改代码7. 实战坑坑现象修复契约不一致mock 与真实返回字段不同 → 下游解析错契约表落地订单/客户各 4 字段 字段缺失兜底防御性 get实测插件出口网络误以为受限以为与 http 节点一样走 ssrf_proxy 白名单实测插件 requests 直连不经 ssrf_proxy无白名单限制——访问宿主机用 host.docker.internal生产靠企业网络策略控制实测修正预期超时无兜底上游卡死拖垮流程requests timeout10超时归 upstream_error与 02 四 code 统一实测base_url 写死切换环境要改代码重新打包credentials 配置化mock↔真实切换零改代码实测多工具公共逻辑重复每个工具文件重复凭证/请求/错误代码tools/common.py 收敛 request_json/err实测打包验证通过8. 实验文档及源码获取实验文档完整操作步骤DIFY-106-04企业系统对接工具.md源码可直接导入dify106_04_验证应用.yml插件包签名安装包控制台上传用dify106_04_enterprise_tool.signed.difypkg全部源码目录dify-106/dsl |插件包目录dify-106/plugins文章聚焦核心配置与采坑点实验的完整分步操作节点搭建/参数表/调试指引见实验文档原文。下一篇Dify 插件开发实验05有状态与幂等——插件如何安全地保持状态和处理重复调用 你在这个实验的场景里踩过什么坑欢迎评论区分享你的实战经验。