1. 项目概述Dify2OpenAI Gateway 是什么如果你正在使用 Dify 来构建和部署基于大语言模型的应用同时又希望你的应用能无缝接入那些只认 OpenAI API 标准的第三方工具、客户端或框架那么你很可能正需要一个“翻译官”。Dify2OpenAI Gateway 就是这个角色。它是一个轻量级的网关服务核心功能是将 Dify 应用提供的原生 API实时、准确地“翻译”成标准的 OpenAI API 格式。简单来说它在你部署的 Dify 应用和外部调用者之间架起了一座标准化的桥梁。调用者比如一个支持 ChatGPT 的聊天客户端、一个自动化脚本或者另一个需要调用 LLM 的服务完全不需要知道背后是 Dify它只需要按照 OpenAI 官方文档的格式发送请求Dify2OpenAI 就会在内部将这个请求“转译”成 Dify 能理解的格式调用你的 Dify 应用再将 Dify 的响应“转译”回 OpenAI 的格式返回给调用者。这极大地扩展了 Dify 应用的兼容性和应用场景让你辛苦在 Dify 上构建的智能体、工作流或知识库问答能轻松融入更广阔的生态。这个项目特别适合两类开发者一是已经在生产环境使用 Dify但苦于其 API 与主流生态不兼容的团队二是希望利用 Dify 强大的可视化编排和知识库能力快速构建应用原型同时又需要以标准化接口对外提供服务的个人或小团队。它用 Node.js 编写部署简单几乎零配置就能跑起来是打通 Dify 与外部世界的关键拼图。2. 核心设计思路与架构解析2.1 为什么需要这样一个网关Dify 本身提供了功能强大的 API但其设计更偏向于服务其自身的平台生态。而 OpenAI 的 API 格式尤其是/v1/chat/completions这个端点已经成为事实上的行业标准被无数开源项目、商业软件和开发框架所支持。这种不匹配导致了几个痛点生态锁死你无法直接使用诸如OpenAI SDK、LangChain、LlamaIndex等主流工具链来调用 Dify 应用必须为 Dify 单独编写适配代码。客户端兼容性差许多优秀的开源聊天界面如ChatGPT-Next-Web、浏览器插件或移动端 App其后端接口通常只预设了 OpenAI 格式。迁移成本高如果你的业务未来需要考虑多云或多模型部署一个统一的 API 接口标准能大幅降低切换底层服务商时的重构工作量。Dify2OpenAI 的诞生就是为了消除这些痛点。它采用了“单一入口动态路由”的设计哲学。所有请求都统一发送到/v1/chat/completions然后网关根据请求中携带的元信息智能地将其分发到 Dify 对应的三个核心端点对话型应用 (/chat-messages)、文本补全型应用 (/completion-messages) 和工作流应用 (/workflows/run)。2.2 统一鉴权模型简洁与安全的平衡早期版本可能支持多种配置组合但这会给使用者带来困惑也增加了网关的解析复杂度。当前版本收敛为一种极其简洁且安全的统一鉴权模型这也是我认为设计上最精妙的地方。核心规则只有两条Authorization头只传递你的 Dify API 基础地址。格式为Bearer DIFY_API_URL。例如Bearer https://api.dify.ai/v1。这个地址告诉了网关你的 Dify 服务在哪里。model参数在这里打包传递应用类型和密钥。格式为dify|BOT_TYPE|API_KEY。例如dify|Chat|app-abc123def456。这种设计将“服务地址”和“应用凭证”分离带来了几个好处安全性API Key 不再出现在容易被日志记录或代理服务器看到的 URL 或普通 Header 中而是放在请求体里。虽然 HTTPS 下整体是加密的但这仍是一种良好的安全实践。清晰性调用者一眼就能看出当前请求的是哪种类型的 Dify 应用Chat, Completion, Workflow。灵活性网关本身无需配置任何 Dify 相关的密钥或地址做到了完全无状态。所有配置都来自请求本身这使得网关可以作为一个公共服务被多人复用或者轻松进行水平扩展。2.3 核心架构与数据流项目虽然代码量不大但模块划分清晰职责明确。我们来看一下它的核心处理流程请求接收与解析(app.js)Express 服务器接收到发送到/v1/chat/completions的 POST 请求。首先它会从Authorization头提取DIFY_API_URL并从model参数中解析出BOT_TYPE和API_KEY。同时它会校验必要的参数是否存在格式是否正确。请求体转换与增强这是网关的核心“翻译”逻辑。网关需要将 OpenAI 格式的请求体转换为对应 Dify 端点期待的格式。例如将 OpenAI 的messages数组可能包含system,user,assistant角色映射为 Dify 的query最后一条用户消息和conversation_id用于多轮对话。处理stream参数决定是使用 Dify 的流式 (streaming) 还是非流式 (blocking) 响应模式。收集并处理文件。这里的设计非常周到它同时支持了两种主流的多模态文件传入方式一是 OpenAI 风格的messages[].content[].image_url二是 Dify 原生的顶层files字符串数组。网关会统一处理这些文件引用将其转换为 Dify API 能接受的格式。传递自定义变量。通过请求体中的variable对象可以将任意键值对传入 Dify 应用的“输入参数”中这对于驱动工作流或配置化应用行为至关重要。路由与处理器分发根据解析出的BOT_TYPE请求会被路由到botType/目录下对应的处理器chatHandler.js,completionHandler.js,workflowHandler.js。每个处理器都封装了与特定 Dify 端点通信的细节。代理请求与响应转换处理器使用提取到的DIFY_API_URL和API_KEY构造出真正的 Dify API 请求并发出。收到 Dify 的响应后处理器再负责将 Dify 的响应格式无论是流式的 Server-Sent Events 还是阻塞式的 JSON转换回 OpenAI 的格式。事件与元数据透传一个高级特性是x_dify扩展字段。Dify 在流式响应中会返回丰富的事件如workflow_started、node_finished等这些对于调试和构建复杂交互界面非常有用。网关会尽力保留这些原始事件并通过扩展字段或响应体的特定方式透传给调用者做到了“翻译”而不“丢失信息”。日志记录(config/logger.js)整个过程的关键步骤和错误都会被结构化的日志系统记录便于运维和问题排查。日志系统区分了开发和生产环境并支持按日期轮转。这个架构确保了网关的轻量、高效和可维护性。每个环节都职责单一修改或扩展一个功能比如支持新的 Dify 端点通常只需要改动对应的处理器模块。3. 详细部署与配置指南3.1 本地开发环境部署对于想要二次开发或深度定制的用户本地部署是最佳起点。项目基于 Node.js因此你需要先确保本地环境已安装 Node.js建议版本 16和 npm。第一步获取代码与安装依赖# 克隆项目仓库到本地 git clone https://github.com/onenov/Dify2OpenAI.git # 进入项目目录 cd Dify2OpenAI # 安装所有必要的Node.js依赖包 npm install这个过程会下载 Express、axios、winston 等核心依赖。如果遇到网络问题可以考虑配置 npm 镜像源。第二步选择启动方式项目提供了两种运行方式开发模式使用npm run dev。这个命令背后是nodemon它会监控项目文件的变化并自动重启服务非常适合边改代码边测试。你会在控制台看到详细的请求日志。生产模式使用npm start。这会直接运行app.js日志输出更为简洁。启动后默认服务会运行在http://localhost:3099。你可以立即使用curl命令或 Postman 进行测试。实操心得开发环境配置在本地开发时我强烈建议使用npm run dev。它的热重载功能能节省大量手动重启的时间。nodemon.json配置文件里已经设置好了监控的目录和忽略的文件如node_modules通常无需修改。如果你在 Windows 下遇到路径问题可以检查一下nodemon.json中的路径分隔符。3.2 使用 PM2 进行生产环境进程管理对于任何需要 7x24 小时运行的后端服务使用进程管理工具是必须的。PM2 是 Node.js 生态中最流行的选择它提供了守护进程、日志管理、集群模式等强大功能。项目已经贴心地准备好了ecosystem.config.cjs配置文件。部署步骤全局安装 PM2如果尚未安装npm install -g pm2使用 PM2 启动网关pm2 start ecosystem.config.cjs这个命令会读取配置文件将应用命名为dify2openai并在后台运行。PM2 常用运维命令启动后管理起来非常方便# 查看所有由PM2管理的应用状态 pm2 list # 查看指定应用的实时日志类似 tail -f pm2 logs dify2openai # 重启应用比如在更新代码后 pm2 restart dify2openai # 停止应用 pm2 stop dify2openai # 从PM2列表中删除应用停止并删除记录 pm2 delete dify2openai # 监控应用的资源占用CPU/内存 pm2 monit项目还封装了等价的 npm 脚本例如npm run pm2:logs对应pm2 logs dify2openai方便记忆。注意事项生产环境日志默认的生产环境日志配置只会在控制台输出error级别以上的日志。所有日志会同时写入logs/目录下的文件并按日期轮转。你需要确保运行进程的用户对该目录有写权限。定期检查日志文件大小虽然配置了自动清理保留14天但在高流量下仍需留意磁盘空间。3.3 一键部署到 VercelServerless 方案对于想快速体验或构建轻量级、无服务器架构的用户项目支持一键部署到 Vercel。这是一个非常酷的特性让你在几分钟内就能获得一个全球可用的网关端点。部署流程点击项目 README 中的 “Deploy with Vercel” 按钮。这会跳转到 Vercel 的导入项目页面。你需要使用 GitHub 账号登录。按照 Vercel 的指引授权并导入onenov/Dify2OpenAI这个仓库。通常不需要修改任何环境变量配置直接点击 “Deploy” 即可。Vercel 会自动完成构建和部署。完成后你会获得一个类似于https://your-project-name.vercel.app的公共 URL。你的网关服务就在这个地址上运行起来了。重要限制与考量Vercel 的无服务器函数有10 秒的超时限制。这意味着流式响应如果 Dify 应用生成一个很长的流式响应并且传输时间超过10秒连接会被 Vercel 强行中断。阻塞式响应如果 Dify 应用处理一个复杂请求的时间超过10秒请求会失败。因此Vercel 部署方案仅适用于响应较快的场景例如简单的问答、意图分类等。对于需要长时间运行的工作流或复杂文本生成你应该选择使用 PM2 部署在自己的服务器上或者选择支持更长超时时间的 Serverless 平台如 Google Cloud Run、AWS Lambda with provisioned concurrency。实操心得Vercel 环境变量项目设计为从每次请求中动态读取配置因此部署到 Vercel 时确实无需预配置环境变量。这简化了部署流程。但请注意你的 Dify API Key 和地址会随着每次请求体发送务必确保你的前端或调用客户端与 Vercel 端点之间的通信使用 HTTPS并且考虑在客户端实现密钥轮换等安全策略。4. 核心接口使用详解与示例理解并正确构造请求是使用网关的关键。下面我将拆解每个参数并通过丰富示例展示如何调用不同类型的 Dify 应用。4.1 请求参数全解构一个标准的 OpenAI 格式请求发送到网关后其中关键字段会被映射和处理Authorization: Bearer DIFY_API_URL作用告诉网关你的 Dify 服务的基础地址。示例Bearer https://api.dify.ai/v1。如果你使用的是自托管的 Dify请替换为你的服务器地址如Bearer http://your-dify-server:5001/v1。model: “dify|BOT_TYPE|API_KEY”作用这是网关的“路由指令”。它定义了要访问的 Dify 应用类型和身份凭证。BOT_TYPE必填决定路由方向。Chat路由到 Dify 的/chat-messages端点。适用于所有对话型应用包括基于普通 LLM 的对话和基于工作流编排的对话应用。Completion路由到 Dify 的/completion-messages端点。适用于文本补全型应用。Workflow路由到 Dify 的/workflows/run端点。适用于纯工作流应用非对话界面。API_KEY必填你的 Dify 应用密钥。在 Dify 应用界面的“访问 API”或“发布”部分可以找到。stream: boolean作用控制是否使用流式传输。设置为true时网关会返回 Server-Sent Events (SSE) 流数据会分块实时返回。设置为false或省略时会等待 Dify 处理完毕一次性返回完整结果。选择建议对于需要实时显示生成过程的聊天场景用true。对于后端程序化调用且不关心中间过程用false更简单。messages: Array作用对话历史。格式完全遵循 OpenAI 标准。映射规则网关会将整个messages数组传递给 Dify。Dify 通常会取最后一条role为user的消息作为本次请求的query。之前的消息历史则用于构建对话上下文。system角色的消息也会被正确传递。user: string(可选)作用终端用户标识。用于 Dify 平台进行用量统计和审计。variable: object(可选)作用这是连接 OpenAI 格式与 Dify 强大功能的关键通道。这个对象里的所有键值对都会被原封不动地传递到 Dify 应用的inputs中。使用场景假设你在 Dify 工作流的开始节点定义了一个名为input_topic的字符串类型变量。你可以在请求中这样传值variable”: {“input_topic”: “人工智能”}。工作流中的后续节点就可以使用{{inputs.input_topic}}来引用这个值。命名建议虽然可以任意命名但遵循 Dify 惯例如input_*,file_*可以提高可读性。files: Arraystring(可选)作用传递文件列表。这是网关提供的非常便利的特性。格式支持两种形式。一是公开可访问的 URL如https://example.com/image.jpg二是 Base64 编码的 Data URL如data:image/png;base64,iVBORw0KGgo...。类型推断网关会根据 URL 的文件扩展名或 Data URL 的 MIME 类型自动推断文件类型image, document, audio 等并转换为 Dify 能识别的格式。4.2 调用示例从简单到复杂下面我们通过几个具体场景看看如何组合使用这些参数。示例一基础对话调用流式这是最常见的场景调用一个普通的 Dify 聊天应用。curl -X POST http://localhost:3099/v1/chat/completions \ -H “Content-Type: application/json” \ -H “Authorization: Bearer https://api.dify.ai/v1” \ -d ‘{ “model”: “dify|Chat|app-your-chat-app-id”, “stream”: true, “messages”: [ {“role”: “system”, “content”: “你是一个专业的科技文章翻译助手。”}, {“role”: “user”, “content”: “将这句话翻译成英文深度学习正在改变世界。”} ], “user”: “user-123” }’关键点BOT_TYPE是Chatstream设为true以便在命令行中看到逐字输出的效果。示例二调用工作流并传递变量假设你有一个用于内容审核的 Dify 工作流它需要一个input_text变量作为待审核文本。curl -X POST http://your-gateway.com/v1/chat/completions \ -H “Authorization: Bearer https://your-dify.com/v1” \ -H “Content-Type: application/json” \ -d ‘{ “model”: “dify|Workflow|app-your-workflow-id”, “stream”: false, “variable”: { “input_text”: “这是一段需要审核的用户评论可能包含不当言论。”, “check_level”: “strict” }, “user”: “audit-system” }’关键点BOT_TYPE是Workflow。注意工作流调用通常不需要messages而是通过variable传递输入。stream设为false等待最终审核结果。示例三多模态请求上传图片分析你想让 Dify 应用分析一张图片。这里展示两种上传方式。方式A使用顶层files数组curl -X POST http://localhost:3099/v1/chat/completions \ -H “Authorization: Bearer https://api.dify.ai/v1” \ -H “Content-Type: application/json” \ -d ‘{ “model”: “dify|Chat|app-vision-app-id”, “stream”: true, “query”: “描述这张图片里的内容。”, “files”: [ “https://example.com/scenery.jpg” ], “user”: “test-user” }’方式B使用 OpenAI 兼容的image_url格式curl -X POST http://localhost:3099/v1/chat/completions \ -H “Authorization: Bearer https://api.dify.ai/v1” \ -H “Content-Type: application/json” \ -d ‘{ “model”: “dify|Chat|app-vision-app-id”, “stream”: true, “messages”: [ { “role”: “user”, “content”: [ {“type”: “text”, “text”: “描述这张图片里的内容。”}, { “type”: “image_url”, “image_url”: {“url”: “https://example.com/scenery.jpg”} } ] } ] }’关键点两种方式网关都支持并会统一处理。方式A更简洁方式B与 OpenAI 官方格式完全一致兼容性最好。如果你的客户端库如 OpenAI SDK原生支持多模态消息用方式B会更方便。实操心得conversation_id的使用对于对话型应用 (Chat)如果你想进行多轮对话需要在后续请求中传递 Dify 返回的conversation_id。网关会将它映射到 OpenAI 响应中的同名字段。你可以在第二轮请求的variable或请求体中回传这个conversation_idDify 就能关联上下文。不过更常见的做法是由调用方你的客户端应用自己维护一个conversation_id并在每次请求时通过variable传入这样更灵活可控。5. 高级特性、问题排查与优化实践5.1 深入理解x_dify扩展与事件流当你使用流式模式 (stream: true) 调用网关时返回的数据流不仅仅是文本内容。网关会尽力保留 Dify 后端返回的原始事件并通过一个名为x_dify的扩展字段或在响应体特定位置透传出来。这对于构建具有丰富交互界面的应用至关重要。典型的事件流片段可能如下所示data: {“id”:”chatcmpl-xxx”, “object”:”chat.completion.chunk”, “choices”:[{“delta”:{“content”:”思考”}}], “x_dify”:{“event”:”agent_thought”, “data”:{“thought”:”用户需要我翻译这句话…”}}} data: {“id”:”chatcmpl-xxx”, “object”:”chat.completion.chunk”, “choices”:[{“delta”:{“content”:”Deep”}}]} data: {“id”:”chatcmpl-xxx”, “object”:”chat.completion.chunk”, “choices”:[{“delta”:{“content”:” learning”}}], “x_dify”:{“event”:”workflow_node_finished”, “data”:{“node_id”:”node-123”, “outputs”:{…}}}}你能从中获得什么Agent 思考过程如果应用使用了 Agent 模式你可以看到agent_thought事件了解模型在“想”什么这可以用于在 UI 上展示一个“正在思考”的动画或提示。工作流执行详情对于工作流编排的应用你可以收到workflow_node_started、node_finished、workflow_finished等事件。这允许你实时展示工作流的执行进度或者在某些节点完成后触发前端特定操作。引文与参考如果应用连接了知识库你可能会收到包含引用来源 (document) 的事件。错误与重试node_retry等事件可以帮助你了解工作流执行中遇到的临时问题。如何利用这些事件在你的前端你需要解析 SSE 流。除了提取标准的choices[0].delta.content进行文本拼接显示外还要检查每个数据块中是否包含x_dify字段。根据其中的event类型更新你的 UI 状态。例如收到agent_thought时可以在消息旁显示一个“思考中”的图标收到workflow_node_finished时可以在一个流程图组件上高亮完成节点。5.2 常见问题排查清单在实际集成和使用中你可能会遇到一些问题。下面是一个快速排查指南问题现象可能原因排查步骤与解决方案返回 401 Unauthorized1.Authorization头格式错误。2.DIFY_API_URL不正确或不可访问。3.API_KEY无效或已过期。1. 检查Authorization头是否为Bearer URL格式且 URL 以http://或https://开头。2. 直接在浏览器或 curl 中访问DIFY_API_URL/chat-messages(需带 API Key) 测试 Dify 服务是否正常。3. 登录 Dify 控制台确认应用的 API Key 是否正确并检查其是否有访问权限。返回 404 Not Found1. 网关服务本身未成功启动。2. 请求路径错误。1. 检查网关进程是否运行 (pm2 list或查看端口3099)。2. 确认请求地址为http://网关地址:3099/v1/chat/completions。返回 400 Bad Request1.model参数格式错误。2. 请求体 JSON 格式错误。3. 缺少必要参数。1. 检查model值是否为dify|Chat|app-xxx格式竖线是英文的。2. 使用 JSON 校验工具检查请求体。3. 对于Chat/Completion类型确保有messages或query对于Workflow确保variable包含工作流所需的输入。流式响应中断或不完整1. 网络超时尤其是 Vercel 10秒限制。2. Dify 应用响应缓慢或出错。3. 客户端 SSE 解析逻辑有 bug。1. 对于长文本生成考虑使用非流式模式或部署在无超时限制的环境。2. 查看网关日志 (pm2 logs) 和 Dify 应用日志确认后端是否返回了错误。3. 使用简单的 curl 命令测试流式排除客户端问题。variable传入后 Dify 工作流未接收1. 变量名与 Dify 工作流开始节点定义的输入变量名不匹配。2. 变量值类型不匹配如期望字符串却传了数字。1. 仔细核对 Dify 工作流画布中开始节点的输入变量名称确保完全一致区分大小写。2. 在 Dify 工作流调试界面手动输入测试确认工作流本身逻辑正确。文件上传失败或类型识别错误1. 文件 URL 不可公开访问。2. Data URL 格式不正确。3. 文件类型不被 Dify 支持。1. 确保文件 URL 是有效的并且网关服务器能够访问考虑网络策略。2. 检查 Data URL 的格式是否为data:[MIME类型];base64,[数据]。3. 查阅 Dify 文档确认其支持的文件类型如图片、txt、pdf等。5.3 性能优化与安全实践性能优化建议连接池与超时设置网关使用axios与 Dify 后端通信。在生产环境中可以考虑配置axios的连接池以复用 HTTP 连接减少 TCP 握手开销。同时合理设置timeout避免慢请求拖垮网关。// 可以在创建 axios instance 时配置 const axiosInstance axios.create({ baseURL: difyApiUrl, timeout: 30000, // 30秒超时 maxRedirects: 5, httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), });启用响应压缩如果传输的文本内容很大可以在 Express 应用中启用compression中间件对响应进行 Gzip 压缩减少网络传输量。监控与告警利用 PM2 的监控功能或集成如PM2Keymetrics、Prometheus等工具监控网关的 CPU、内存、请求延迟和错误率。设置告警阈值。安全实践网关本身的无状态化如前所述网关不存储任何 Dify 的 API Key这是最大的安全优势。确保你的网关服务器本身有防火墙保护仅对必要的客户端 IP 开放端口。Dify API Key 管理不要在客户端如网页前端硬编码或直接传输 Dify API Key。应该由你的后端服务器保管或者使用临时的、权限受限的令牌。网关的设计允许你将鉴权逻辑放在你自己的后端由后端服务器向网关发起请求携带 API Key再转发结果给前端。请求验证与限流在网关前方可以部署一层 API 网关如 Nginx, Kong, APISIX或使用 Express 中间件对请求进行频率限制、IP 黑白名单过滤防止滥用。日志脱敏检查config/logger.js的配置确保日志中不会记录完整的请求体和响应体以免泄露敏感的API_KEY或用户对话内容。可以配置winston在格式化日志时过滤掉特定字段。扩展性思考当前网关是一个单点服务。如果流量很大你可以水平扩展由于网关无状态可以轻松启动多个实例前面用 Nginx 做负载均衡。容器化部署将项目 Docker 化可以更一致地在不同环境开发、测试、生产中部署和扩展。支持更多模型协议虽然项目名为 Dify2OpenAI但其架构可以扩展以支持其他模型的 API 协议如 Anthropic Claude, Google Gemini。可以抽象出一个通用的“适配器”接口未来增加新的BOT_TYPE如claude、gemini并路由到对应的处理器。这个项目提供了一个坚实而优雅的起点它不仅解决了 Dify 与 OpenAI 生态的兼容问题其模块化设计和清晰的接口也为我们根据自身业务需求进行定制和扩展留下了充足的空间。