Azure OpenAI代理部署指南:无缝兼容OpenAI API工具
1. 项目概述与核心价值如果你正在使用或打算使用微软Azure OpenAI服务但手头有一大堆只认OpenAI官方API格式的工具和项目比如各种开源的ChatGPT Web UI那你肯定遇到过这个头疼的问题这些工具配置项里通常只有一个OPENAI_API_KEY和OPENAI_BASE_URL而Azure OpenAI的API格式和鉴权方式跟OpenAI官方完全是两码事。直接填Azure的密钥和端点根本行不通你需要一个“翻译官”。scalaone/azure-openai-proxy这个项目就是专门解决这个痛点的。它是一个轻量级的代理服务部署在你自己的环境里接收标准的OpenAI API请求然后无缝地转换成Azure OpenAI API的格式并转发再把Azure的响应转换回OpenAI的格式返回给你的应用。这样一来任何兼容OpenAI API的应用程序无需任何修改就能直接对接Azure OpenAI服务。这个项目的核心价值在于“兼容性”和“可控性”。它让你摆脱了被工具绑定的烦恼你可以自由选择像ChatGPT-Next-Web、chatbot-ui这些优秀的开源前端后端则稳定地使用企业级的Azure OpenAI服务。所有数据流量都经过你自己的代理服务器安全可控部署也极其灵活无论是用Docker一键拉起还是在Azure上直接部署为Web应用都非常方便。对于开发者、企业IT或者任何想搭建私有化AI对话服务的团队来说这是一个非常实用的“桥梁”工具。2. 核心原理与架构拆解2.1 为什么需要这个代理—— API格式差异详解要理解这个代理的价值得先搞清楚OpenAI官方API和Azure OpenAI API到底有哪些不同。这不仅仅是换个URL那么简单而是协议层面的差异。首先端点路径不同。OpenAI官方的聊天补全接口路径是/v1/chat/completions。而Azure OpenAI的对应接口路径长得像这样/openai/deployments/{deployment-name}/chat/completions?api-version2024-02-01。你需要把模型部署名deployment name直接嵌入到URL路径里。其次也是最大的不同鉴权方式。OpenAI使用简单的Bearer Token即在HTTP请求头的Authorization字段里放Bearer sk-xxx。Azure OpenAI则使用API Key认证但它的Key是放在请求头的api-key字段里而不是Authorization字段。同时Azure的API Key是与特定的Azure资源比如“中国东部2”区域的某个OpenAI服务绑定的。最后请求/响应体。虽然核心的JSON结构如messages,temperature高度相似但一些字段名和细节仍有差异。例如OpenAI请求体中的model参数如gpt-3.5-turbo在Azure端是不需要的因为模型信息已经体现在URL的部署名里了。代理需要妥善处理这些字段的映射和增删。这个代理的工作流程就像一个尽职的协议转换器监听在某个端口默认3000启动一个HTTP服务器。接收接收来自客户端如ChatGPT-Next-Web的、符合OpenAI格式的POST请求路径为/v1/chat/completions。解析与转换从请求头Authorization中提取出经过特殊编码的Azure配置信息资源ID、部署名、API密钥、API版本。根据提取的部署名重写请求URL将其转换为Azure的端点格式。将Authorization: Bearer ...请求头替换为api-key: ...。对请求体进行清洗移除或转换Azure不支持的字段如model并可能添加必要的字段。转发与回传将转换后的请求转发给真正的Azure OpenAI服务端点收到响应后再反向转换回OpenAI的格式返回给最初的客户端。整个过程对客户端是透明的客户端以为自己一直在和OpenAI官方服务器通信。2.2 项目技术栈与设计亮点这个项目基于Node.js环境采用Next.js 13框架构建。选择Next.js一方面是因为其作为全栈框架能非常方便地处理API路由项目核心就是API路由另一方面也便于未来扩展一个简单的管理或监控界面。它的设计非常“Unix哲学”做好一件事并且做得足够轻巧。整个代理的核心逻辑集中在一个API路由处理函数中没有复杂的依赖。查看其package.json可以发现生产依赖非常精简主要就是处理HTTP请求的库。这种极简设计带来了几个好处部署简单镜像体积小启动速度快。资源消耗低作为代理它本身不进行复杂的计算只是做协议转换和转发因此对服务器资源要求极低甚至可以在低配的云函数或容器实例上稳定运行。易于理解和定制代码结构清晰如果你有特殊需求比如需要添加请求日志、修改负载均衡策略可以很容易地基于源码进行二次开发。注意虽然项目基于Next.js但它在运行时是以一个独立的Node.js服务器形式运行的通过npm start启动server.js而不是通常的Next.js开发服务器。这意味着它更适合作为后端服务部署而不是一个需要服务端渲染的前端应用。3. 详细部署与配置指南3.1 前置条件获取Azure OpenAI资源信息在部署代理之前你必须先准备好Azure OpenAI服务。如果你还没有需要在Azure门户中创建。这里假设你已经有了一个可用的Azure OpenAI资源。你需要收集以下四个关键信息它们将用于配置代理AZURE_RESOURCE_ID: 这不是门户上直接显示的名字。它通常是你的Azure OpenAI服务的终结点Endpoint的主机名部分。例如如果你的终结点是https://my-openai-resource.openai.azure.com/那么资源ID就是my-openai-resource.openai.azure.com。在某些上下文中也可能指你的Azure订阅中该资源的唯一资源ID但在此代理的语境下通常指终结点主机名。AZURE_MODEL_DEPLOYMENT: 你在Azure OpenAI Studio中为模型如GPT-3.5-Turbo创建的部署名称。例如你可能创建了一个名为my-gpt35-turbo-deployment的部署。AZURE_API_KEY: 在你的Azure OpenAI资源下于“密钥和终结点”页面找到的两个密钥之一。AZURE_API_VERSION(可选): Azure OpenAI API的版本号例如2024-02-01。如果不指定代理会使用一个默认值。一个常见的误解是试图将Azure门户上的“资源名称”直接当作AZURE_RESOURCE_ID。请务必使用完整的终结点主机名。3.2 方案一Azure一键部署最省心对于已经深度使用Azure生态的用户项目提供了最便捷的部署方式——通过Azure Resource Manager (ARM) 模板一键部署。点击项目README中的“Deploy to Azure”按钮你会被引导至Azure门户的自定义部署界面。这个模板会帮你创建以下资源一个App Service Plan应用服务计划默认为B1 tier基本版。这是Azure Web App的运行容器B1 tier提供共享的CPU和内存适合轻量级应用每月有固定的免费额度具体时长需参考Azure最新政策超出后会产生少量费用。一个Web AppWeb应用这就是运行我们代理代码的容器。模板会直接从Docker Hub拉取scalaone/azure-openai-proxy镜像并运行。部署时需要你填写几个参数Resource Prefix资源前缀这会用于生成所有资源的名称如Web App名称。如果提示名称已被占用换一个独特的即可。Region区域强烈建议选择与你的Azure OpenAI资源相同的区域。例如你的OpenAI资源在“East US”那么代理也部署在“East US”。这能最大程度降低网络延迟提升响应速度。SKU服务等级保持默认的B1即可除非你预期有非常高的并发请求。部署完成后在Azure门户中找到创建好的Web App其默认URL格式为https://你指定的前缀.azurewebsites.net。这个URL就是你的代理服务地址。你可以将其理解为你的私有化api.openai.com。实操心得部署后建议立即进入Web App的“配置”页面添加一个应用设置WEBSITES_PORT3000。虽然Docker镜像内暴露的是3000端口但明确指定端口可以避免Azure App Service内部路由可能出现的偶发问题。此外在“诊断和解决问题”中可以查看启动日志确认容器是否成功拉取并运行。3.3 方案二Docker部署最灵活如果你有自己的服务器云服务器、NAS、本地开发机或者不想依赖Azure App ServiceDocker部署是最灵活通用的选择。它让你对代理拥有完全的控制权。基础运行命令docker run -d -p 3000:3000 --name azure-openai-proxy scalaone/azure-openai-proxy-d: 后台运行。-p 3000:3000: 将宿主机的3000端口映射到容器的3000端口。--name: 给容器起个名字方便管理。这行命令会从Docker Hub拉取最新的镜像并运行。代理服务将在你服务器的http://服务器IP:3000上可用。生产环境进阶配置 对于正式使用我们通常需要更稳定的配置。推荐使用docker-compose.yml来管理version: 3.8 services: azure-openai-proxy: image: scalaone/azure-openai-proxy:latest # 可以指定特定版本如 :v1.0.0以提高稳定性 container_name: azure-openai-proxy restart: unless-stopped # 确保容器意外退出时自动重启 ports: - 3000:3000 # 可选设置内存和CPU限制防止代理本身占用过多资源 # deploy: # resources: # limits: # cpus: 0.5 # memory: 256M # reservations: # cpus: 0.1 # memory: 128M使用docker-compose up -d启动服务。restart: unless-stopped策略能保证服务器重启后代理自动恢复非常适合生产环境。注意事项如果你的服务器有防火墙如云服务商的安全组、iptables务必放行3000端口的入站流量。否则外部将无法访问你的代理服务。3.4 方案三本地开发与测试对于开发者而言从源码本地运行是调试和定制的必经之路。步骤也非常清晰环境准备确保系统已安装Node.js版本18或20项目推荐20和npm。获取代码git clone https://github.com/scalaone/azure-openai-proxy.git安装依赖进入项目目录运行npm install。启动服务运行npm start。开发模式下你也可以使用npm run dev来启动支持热重载的开发服务器方便调试。本地服务默认运行在http://localhost:3000。接下来你需要验证它是否工作正常。4. 连接测试与客户端配置实战4.1 测试代理服务是否就绪部署或启动代理后第一件事就是验证它能否正常工作。项目提供了一个非常直观的cURL测试脚本。你需要将脚本中的占位符替换成你自己的Azure信息。假设你的信息如下AZURE_RESOURCE_ID:mycompany-openai.openai.azure.comAZURE_MODEL_DEPLOYMENT:gpt-35-turbo-deployAZURE_API_KEY:abcdef123456...AZURE_API_VERSION:2024-02-01(可选)那么你需要构造一个特殊的Authorization头格式为AZURE_RESOURCE_ID:AZURE_MODEL_DEPLOYMENT:AZURE_API_KEY:AZURE_API_VERSION如果省略API版本格式为AZURE_RESOURCE_ID:AZURE_MODEL_DEPLOYMENT:AZURE_API_KEY在终端中执行以下命令替换为你自己的信息curl -X POST http://localhost:3000/v1/chat/completions \ -H Authorization: mycompany-openai.openai.azure.com:gpt-35-turbo-deploy:abcdef123456...:2024-02-01 \ -H Content-Type: application/json; charsetutf-8 \ -d ${ messages: [ { role: system, content: You are a helpful assistant. }, { role: user, content: What is the capital of France? } ], temperature: 0.7, model: gpt-3.5-turbo, # 注意这个model字段会被代理忽略或转换但按OpenAI格式仍需提供 stream: false }如何解读响应成功你会收到一个JSON格式的响应包含id,choices,usage等字段结构与OpenAI官方API返回的完全一致。这证明代理工作正常且成功从Azure OpenAI获取了答案。失败常见的错误响应包括401 Unauthorized: 通常是AZURE_API_KEY错误或者Authorization头格式不正确比如漏了冒号。404 Not Found: 通常是AZURE_RESOURCE_ID终结点或AZURE_MODEL_DEPLOYMENT部署名拼写错误或者该部署在你指定的资源下不存在。400 Bad Request: 可能是请求体JSON格式错误或者包含了Azure不支持的参数。4.2 配置主流ChatGPT客户端代理测试通过后就可以配置各种客户端了。所有兼容OpenAI API的客户端配置思路都是一样的将Base URL或API Endpoint指向你的代理地址将API Key字段填入那个特殊的、包含所有Azure信息的字符串。下面以几个热门项目为例1. ChatGPT-Next-Web这是目前非常流行的自部署Web UI。如果你使用Docker运行它环境变量配置如下# docker-compose.yml 片段 services: chatgpt-next-web: image: yidadaa/chatgpt-next-web environment: - OPENAI_API_KEYmycompany-openai.openai.azure.com:gpt-35-turbo-deploy:abcdef123456... # 关键在这里 - BASE_URLhttp://你的代理IP或域名:3000 # 指向azure-openai-proxy - CODE你的访问密码可选在它的Web界面设置里API Key和API Endpoint通常会自动从环境变量读取无需再填。2. Chatbot-UI / ChatGPT-Web 等配置方式大同小异。在客户端的设置页面找到类似“API Base URL”和“API Key”的选项。API Base URL: 填入http://你的代理IP或域名:3000(注意是http还是https取决于你的代理是否配置了SSL)。API Key: 填入那个长长的字符串mycompany-openai.openai.azure.com:gpt-35-turbo-deploy:abcdef123456...。3. 通过环境变量配置许多客户端支持通过环境变量注入配置。在启动客户端容器时可以这样设置docker run -d \ -e OPENAI_API_KEYmycompany-openai.openai.azure.com:gpt-35-turbo-deploy:abcdef123456... \ -e OPENAI_API_BASEhttp://host.docker.internal:3000 \ # 如果客户端容器和代理容器在同一台主机上可以用这个特殊域名 -p 3001:3000 \ your-chat-client-image重要提示那个包含Azure信息的API Key字符串是明文密钥请务必妥善保管。不要在代码仓库中直接提交建议使用环境变量、密钥管理服务或Docker Secrets来传递。4.3 多模型支持与高级配置默认情况下代理的Authorization头格式只指定了一个模型部署。但如果你在Azure上部署了多个模型例如一个GPT-3.5-Turbo用于日常对话一个GPT-4用于复杂分析并且希望客户端能动态选择该怎么办项目README的FAQ中给出了解决方案使用扩展的密钥格式。其核心思想是在Authorization头中通过管道符|和逗号,来映射多个模型。格式如下AZURE_RESOURCE_ID:openai-model-name|azure-deployment-name,openai-model-name2|azure-deployment-name2,...:AZURE_API_KEY:AZURE_API_VERSION举例说明 假设你在Azure上有三个部署部署my-gpt35对应 OpenAI 的gpt-3.5-turbo模型。部署my-gpt4对应 OpenAI 的gpt-4模型。部署my-gpt4-32k对应 OpenAI 的gpt-4-32k模型。那么你的OPENAI_API_KEY即Authorization头内容应该配置为mycompany-openai.openai.azure.com:gpt-3.5-turbo|my-gpt35,gpt-4|my-gpt4,gpt-4-32k|my-gpt4-32k:abcdef123456...:2024-02-01工作原理 当客户端发送请求并在请求体的model字段中指定了gpt-4时代理会解析这个扩展密钥。它发现映射关系中有gpt-4|my-gpt4于是就会将请求转发到Azure的/openai/deployments/my-gpt4/chat/completions端点。这样客户端就能像调用原生OpenAI API一样通过改变model参数来切换使用后端不同的Azure部署。这个功能非常强大它使得那些支持模型切换的客户端如ChatGPT-Next-Web的模型下拉菜单能够完全兼容Azure的多模型环境。5. 常见问题排查与性能调优5.1 部署与连接问题排查表在实际操作中你可能会遇到各种问题。下面是一个快速排查清单问题现象可能原因排查步骤与解决方案部署失败(Azure)资源名称冲突、配额不足、模板参数错误。1. 检查并更换一个全局唯一的“资源前缀”。2. 检查Azure订阅是否有足够的资源配额特别是某些区域对OpenAI服务有申请限制。3. 仔细核对部署参数确保区域选择正确。容器启动后立即退出(Docker)端口冲突、镜像损坏、环境变量问题。1. 运行docker logs azure-openai-proxy查看容器日志。2. 检查宿主机3000端口是否已被其他程序占用netstat -tuln | grep :3000。3. 尝试删除旧镜像重新拉取docker pull scalaone/azure-openai-proxy:latest。cURL测试返回401/403Azure API密钥错误、密钥格式错误、资源ID或部署名错误。1.逐字符核对Authorization头确保格式是资源ID:部署名:密钥:API版本冒号数量正确。2. 登录Azure门户确认API密钥是否有效部署是否处于“成功”状态。3. 尝试在Azure OpenAI Studio的“游乐场”中直接用相同密钥和部署名测试以排除Azure服务本身的问题。cURL测试返回404终结点URL错误、部署名拼写错误。1. 确认AZURE_RESOURCE_ID是你的终结点主机名如xxx.openai.azure.com而不是资源名称。2. 在Azure OpenAI Studio中精确复制部署名称注意大小写。客户端连接代理超时网络不通、代理服务未运行、防火墙阻止。1. 在运行客户端的机器上用telnet 代理IP 3000或curl -v http://代理IP:3000测试网络连通性。2. 在代理服务器上运行docker ps确认容器状态为“Up”。3. 检查代理服务器的防火墙/安全组规则确保3000端口对客户端IP开放。客户端收到“模型不支持”错误客户端请求的model字段与代理密钥中定义的映射不匹配。1. 检查客户端配置的model参数值如gpt-4。2. 核对你在OPENAI_API_KEY中为这个模型名定义的映射关系gpt-4|你的部署名是否正确。流式响应(stream)不工作部分早期版本代理或客户端对stream支持不完善。1. 确保你使用的是项目的最新版本。2. 在测试cURL命令中将stream: false改为true观察是否能收到持续的SSE数据流。3. 检查客户端是否配置了正确的流式响应处理方式。5.2 性能、安全与监控建议当代理服务稳定运行后可以考虑一些进阶优化。1. 性能考量代理位置这是最重要的因素。务必让代理服务器在物理位置上尽量靠近你的Azure OpenAI资源区域以最小化网络延迟。同区域最佳。资源规格对于轻量级个人使用Azure B1实例或1核1G的云服务器足够。如果面临高并发如团队使用需要考虑提升代理服务器的CPU和网络带宽。代理本身消耗很小瓶颈通常在于网络I/O。连接池与超时项目本身比较简单未实现复杂的HTTP连接池。在极高并发下可以考虑在代理前放置一个Nginx反向代理利用Nginx的连接池和负载均衡能力。同时可以在代理代码中或通过Nginx配置适当调整上游Azure和下游客户端的超时时间。2. 安全加固使用HTTPS在公网暴露服务时务必启用HTTPS。对于Azure Web App它自动提供了.azurewebsites.net域名的HTTPS。对于自部署Docker可以使用Nginx或Caddy作为反向代理来配置SSL证书如Let‘s Encrypt免费证书。访问控制不要将代理服务无限制地暴露在公网。可以通过以下方式加固IP白名单在代理服务器防火墙或前置的Nginx中只允许你或你团队使用的客户端IP地址访问3000端口。添加认证层修改代理源码在转发请求前增加一层简单的HTTP Basic认证或API Key认证。这样即使代理地址泄露没有额外密钥也无法使用。使用私有网络将代理和客户端部署在同一个虚拟私有云VPC内不分配公网IP通过内网通信这是最安全的方式。3. 基础监控日志Docker容器的标准输出日志包含了所有访问和错误信息。使用docker logs -f azure-openai-proxy可以实时查看。对于生产环境建议将日志收集到ELK、Loki等集中式日志系统中。健康检查可以编写一个简单的定时任务cron job定期向代理的/v1/chat/completions发送一个简单的测试请求例如询问“你好”根据响应状态码和内容判断服务是否健康。资源监控监控代理服务器的CPU、内存和网络流量。如果流量异常增高可能是客户端配置错误导致的重试风暴或者是遭到了不必要的访问。5.3 自定义开发与扩展思路这个代理项目代码结构清晰为二次开发提供了很好的基础。以下是一些可能的扩展方向多租户支持当前代理通过请求头中的密钥来识别一个Azure后端。你可以修改它使其支持从数据库或配置文件中根据客户端提供的不同API Key甚至是一个简单的令牌来映射到不同的Azure资源和部署。这样一个代理实例就能为多个独立用户或项目服务。负载均衡与故障转移如果你有多个相同模型的Azure部署例如跨不同区域用于灾备可以修改代理使其在转发请求时按照一定策略轮询、随机选择后端端点并在某个端点失败时自动切换到其他端点。请求审计与限流在代理层添加中间件记录所有请求的元数据如客户端IP、请求模型、Token消耗量并可以实施基于IP或API Key的速率限制防止滥用。响应缓存对于一些常见的、重复的、对实时性要求不高的查询可以在代理层实现简单的响应缓存减少对Azure API的调用节省成本和延迟。统一错误处理与日志将Azure返回的错误信息通常格式与OpenAI不同更友好地转换回OpenAI的格式并增加更详细的请求/响应日志便于调试。进行自定义开发时建议Fork原项目仓库在理解现有代码逻辑主要关注app/api/目录下的路由处理文件的基础上进行修改。修改完成后构建自己的Docker镜像并部署测试。我个人在将一个内部工具从直接调用OpenAI迁移到Azure时就使用了这个代理方案。最大的体会是它极大地降低了迁移成本。我们不需要修改任何前端应用代码只需要重新配置一下环境变量。在调试初期通过查看代理容器的日志能非常清晰地看到请求是如何被转换和转发的对于排查鉴权、模型映射这类问题帮助巨大。对于需要同时使用多个AI模型后端的团队利用其多模型映射功能可以在客户端实现灵活的后端切换策略比如将简单的日常问答路由到成本更低的模型将复杂的分析任务路由到能力更强的模型这一切对前端应用都是透明的。