Loading... # 使用 NGINX 作为 AI 代理实现指南 # 一、概述 ## 1. 背景 ### A. AI 时代的新挑战 人工智能(AI)技术的快速发展创造了一个复杂的生态环境。组织开始将多个大型语言模型(LLM)提供商结合起来,并管理具有不同 API 规范的多个模型端点,以构建 AI 驱动的应用程序。这种多模型并存的环境催生了 AI Gateway 和 AI Proxy 的兴起。 ### B. AI Proxy 的定义 AI Proxy 是 AI Gateway 的简化实现,是位于应用程序与 AI 模型之间的专用基础设施组件,用于编排和保护 AI 请求的流向。 ## 2. 核心功能 ### A. 流量控制 流量控制是 AI Proxy 的核心功能,它实现了身份验证与授权机制以及速率限制,有助于防止模型滥用,并确保资源的公平分配。 ### B. 模型转换 模型转换通过提供对不同 AI 模型的统一 API 入口来实现跨 AI 模型和提供商的无缝集成,从而将复杂性从客户端应用抽象掉。 ### C. 宕机切换 宕机切换系统提供高可用性,确保请求的可靠性,能够优雅地处理模型不可用或速率受限的情况。 ### D. 流量可观测性 提供全面的监控和日志记录,以维持 AI 流程中的运营可见性。对请求与响应负载进行流量审计,对于合规性验证、故障排查与模型性能分析至关重要。 # 二、技术方案 ## 1. 系统架构 使用 NGINX 结合 NJS(NGINX JavaScript)脚本实现 AI Proxy,可以提供完整的 AI 流量治理能力。 ```mermaid graph TB Client[客户端应用] -->|OpenAI API 格式| NGINX[NGINX AI Proxy] NGINX -->|NJS 转换| Anthropic[Anthropic API] NGINX -->|直接透传| OpenAI[OpenAI API] subgraph NGINX Router[NJS 路由脚本] Transform[模型转换] RBAC[访问控制] Failover[宕机切换] Logger[Token 日志] Router --> Transform Router --> RBAC Router --> Failover Router --> Logger end style NGINX fill:#e1f5fe style Client fill:#f3e5f5 style OpenAI fill:#c8e6c9 style Anthropic fill:#fff9c4 ```  ## 2. 工作原理 ### A. 请求流程 1. 客户端发送统一格式的 API 请求到 NGINX 2. NJS 脚本验证用户身份和访问权限 3. 根据请求的模型类型进行路由决策 4. 如果目标模型是 Anthropic,将请求转换为 Anthropic 格式 5. 如果目标模型是 OpenAI,直接透传请求 6. 发起子请求到后端模型提供商 7. 接收响应并进行必要格式转换 8. 记录 Token 使用情况 9. 返回统一格式的响应给客户端 ```mermaid sequenceDiagram participant C as 客户端 participant N as NGINX participant JS as NJS 脚本 participant A as Anthropic API participant O as OpenAI API C->>N: POST /v1/chat/completions N->>JS: 路由处理 JS->>JS: 验证用户权限 JS->>JS: 检查模型类型 alt Anthropic 模型 JS->>JS: 转换请求格式 JS->>A: 子请求 A-->>JS: 响应 JS->>JS: 转换响应格式 else OpenAI 模型 JS->>O: 直接透传 O-->>JS: 响应 end JS->>JS: 记录 Token 使用 JS-->>C: 返回响应 ```  # 三、核心实现 ## 1. 模型路由与转换 ### A. 请求格式转换 OpenAI Chat Completions API 和 Anthropic Messages API 使用不同的请求格式。需要实现转换函数: ```javascript function transformAnthropicRequest(requestBody) { let maxTokens = requestBody.max_completion_tokens || requestBody.max_tokens || 512; const anthropicRequest = { model: requestBody.model, max_tokens: maxTokens, stream: requestBody.stream || false, temperature: requestBody.temperature || 1.0, top_p: requestBody.top_p }; if (anthropicRequest.temperature > 1.0) { anthropicRequest.temperature = requestBody.temperature / 2.0; } if (requestBody.stop) { anthropicRequest.stop_sequences = Array.isArray(requestBody.stop) ? requestBody.stop : [requestBody.stop]; } const systemMessages = []; const messages = []; for (let i = 0; i < requestBody.messages.length; i++) { const msg = requestBody.messages[i]; if (msg.role === "system") { systemMessages.push({text: msg.content, type: "text"}); } else { messages.push({role: msg.role, content: msg.content}); } } if (systemMessages.length > 0) { anthropicRequest.system = systemMessages; } anthropicRequest.messages = messages; return anthropicRequest; } ``` ### B. 响应格式转换 ```javascript function transformAnthropicResponse(anthropicResponse) { const response = JSON.parse(anthropicResponse); if (response.error) { return { error: { type: response.error.type, message: response.error.message, code: response.error.code } }; } const openaiResponse = { id: response.id, object: "chat.completion", model: response.model, choices: [], usage: { prompt_tokens: response.usage.input_tokens, completion_tokens: response.usage.output_tokens, total_tokens: response.usage.input_tokens + response.usage.output_tokens } }; for (let i = 0; i < response.content.length; i++) { const content = response.content[i]; openaiResponse.choices.push({ index: i, finish_reason: response.stop_reason, message: { role: response.role, content: content.text } }); } return openaiResponse; } ``` ## 2. NGINX 配置 ### A. 基础配置 ```nginx js_import /etc/njs/aiproxy.js; resolver 8.8.8.8; upstream openai { zone openai 64k; server api.openai.com:443 resolve; } upstream anthropic { zone anthropic 64k; server api.anthropic.com:443 resolve; } server { listen 4242; default_type application/json; location /v1/chat/completions { set $aiproxy_user $http_x_user; js_content aiproxy.route; } location /openai { internal; rewrite ^ /v1/chat/completions break; proxy_pass_request_headers off; proxy_set_header Host "api.openai.com"; proxy_set_header Content-Type "application/json"; proxy_set_header Authorization 'Bearer ${OPENAI_API_KEY}'; proxy_method POST; proxy_pass https://openai; proxy_ssl_verify on; proxy_ssl_server_name on; } location /anthropic { internal; rewrite ^ /v1/messages break; proxy_pass_request_headers off; proxy_set_header Host "api.anthropic.com"; proxy_set_header Content-Type "application/json"; proxy_set_header anthropic-version "2023-06-01"; proxy_set_header x-api-key 'Bearer ${ANTHROPIC_API_KEY}'; proxy_method POST; proxy_pass https://anthropic; proxy_ssl_verify on; proxy_ssl_server_name on; } } ``` # 四、高级功能 ## 1. 访问控制 ### A. RBAC 配置 通过 JSON 文件定义用户和模型访问权限: ```json { "users": { "user-a": { "models": [ {"name": "gpt-5"}, {"name": "claude-sonnet-4-20250514"} ] }, "user-b": { "models": [ {"name": "gpt-5"} ] } }, "models": { "gpt-5": { "provider": "openai", "location": "/openai" }, "claude-sonnet-4-20250514": { "provider": "anthropic", "location": "/anthropic" } } } ``` ### B. 权限验证流程 1. 从请求头 X-User 中提取用户标识 2. 查询用户是否存在于配置中 3. 验证用户是否有权访问请求的模型 4. 如果验证失败,返回相应的 HTTP 错误代码 ```mermaid graph TD A[接收请求] --> B{用户是否存在} B -->|否| C[返回 403] B -->|是| D{模型是否可访问} D -->|否| E[返回 404] D -->|是| F[继续处理请求] style C fill:#ffcdd2 style E fill:#ffcdd2 style F fill:#c8e6c9 ```  ## 2. 宕机切换 ### A. 配置扩展 在用户模型配置中添加 failover 字段: ```json { "users": { "user-a": { "models": [ { "name": "gpt-5", "failover": "claude-sonnet-4-20250514" } ] } } } ``` ### B. 切换逻辑 1. 尝试调用主模型 2. 如果主模型返回非 200 状态码 3. 检查是否配置了 failover 模型 4. 使用 failover 模型重新发起请求 5. 返回 failover 模型的响应 ```mermaid graph TD A[请求主模型] --> B{状态码是否为 200} B -->|是| C[返回响应] B -->|否| D{是否有 Failover 配置} D -->|否| E[返回错误] D -->|是| F[请求 Failover 模型] F --> G{状态码是否为 200} G -->|是| H[返回 Failover 响应] G -->|否| E style C fill:#c8e6c9 style H fill:#fff9c4 style E fill:#ffcdd2 ```  ## 3. Token 使用日志 ### A. 提取 Token 信息 从响应中提取 Token 使用情况并设置到 NGINX 变量: ```javascript if (serviceReply.status === 200) { try { const parsedResponse = JSON.parse(responseBody); if (parsedResponse.usage) { r.variables.ai_proxy_response_prompt_tokens = parsedResponse.usage.prompt_tokens || ""; r.variables.ai_proxy_response_completion_tokens = parsedResponse.usage.completion_tokens || ""; r.variables.ai_proxy_response_total_tokens = parsedResponse.usage.total_tokens || ""; } } catch (e) { r.log(`Warning: Failed to parse response body for token extraction: ${e.toString()}`); } } ``` ### B. 日志格式配置 在 NGINX 主配置中定义自定义日志格式: ```nginx log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' 'prompt_tokens=$ai_proxy_response_prompt_tokens ' 'completion_tokens=$ai_proxy_response_completion_tokens ' 'total_tokens=$ai_proxy_response_total_tokens'; access_log /var/log/nginx/access.log main; ``` # 五、部署方案 ## 1. Docker 容器部署 ### A. 前置条件 - OpenAI API Key - Anthropic API Key - Docker 环境 ### B. 部署步骤 1. 拉取 NGINX Docker 镜像 ```bash docker pull nginx:1.29.1 ``` 2. 导出 API Key 到环境变量 ```bash export OPENAI_API_KEY=<API_KEY> export ANTHROPIC_API_KEY=<API_KEY> ``` 3. 创建 Docker Volume 用于密钥存储 ```bash docker volume create nginx-keys ``` 4. 启动容器 ```bash docker run -it --rm -p 4242:4242 \ -v $(pwd)/config:/etc/nginx \ -v $(pwd)/njs:/etc/njs \ -v $(pwd)/templates:/etc/nginx-ai-proxy/templates \ -v nginx-keys:/etc/nginx-ai-proxy/keys \ -e NGINX_ENVSUBST_TEMPLATE_DIR=/etc/nginx-ai-proxy/templates \ -e NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx-ai-proxy/keys \ -e OPENAI_API_KEY \ -e ANTHROPIC_API_KEY \ --name nginx-ai-proxy \ nginx:1.29.1 ``` ## 2. Kubernetes 集群部署 可以使用类似的架构在 Kubernetes 集群中部署,使用 ConfigMap 管理 NJS 脚本和配置文件,使用 Secret 管理 API 密钥。 # 六、测试验证 ## 1. 基本功能测试 ### A. User A 访问 OpenAI 模型 ```bash curl -s -X POST http://localhost:4242/v1/chat/completions \ -H 'Content-Type: application/json' \ -H 'X-User: user-a' \ -d '{"model":"gpt-5","messages":[{"role":"user","content":"Hello"}]}' ``` ### B. User A 访问 Anthropic 模型 ```bash curl -s -X POST http://localhost:4242/v1/chat/completions \ -H 'Content-Type: application/json' \ -H 'X-User: user-a' \ -d '{"model":"claude-sonnet-4-20250514","messages":[{"role":"user","content":"Hello"}]}' ``` ### C. User B 访问 OpenAI 模型 ```bash curl -s -X POST http://localhost:4242/v1/chat/completions \ -H 'Content-Type: application/json' \ -H 'X-User: user-b' \ -d '{"model":"gpt-5","messages":[{"role":"user","content":"Hello"}]}' ``` ### D. User B 访问 Anthropic 模型(应该失败) ```bash curl -s -X POST http://localhost:4242/v1/chat/completions \ -H 'Content-Type: application/json' \ -H 'X-User: user-b' \ -d '{"model":"claude-sonnet-4-20250514","messages":[{"role":"user","content":"Hello"}]}' ``` ## 2. 宕机切换测试 使用无效的 OpenAI API Key 启动容器,验证 User A 的请求是否自动切换到 Anthropic: ```bash docker run -it --rm -p 4242:4242 \ -v $(pwd)/config:/etc/nginx \ -v $(pwd)/njs:/etc/njs \ -v $(pwd)/templates:/etc/nginx-ai-proxy/templates \ -v nginx-keys:/etc/nginx-ai-proxy/keys \ -e NGINX_ENVSUBST_TEMPLATE_DIR=/etc/nginx-ai-proxy/templates \ -e NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx-ai-proxy/keys \ -e OPENAI_API_KEY=bad \ -e ANTHROPIC_API_KEY \ --name nginx-ai-proxy \ nginx:1.29.1 ``` # 七、局限性与改进方向 ## 1. 当前方案的局限性 ### A. 缺乏专用 AI 安全防护 NGINX 作为 AI Proxy 的主要限制是缺乏专用的 AI 安全防护措施。防范针对 LLM 的特定威胁(如 prompt 注入或数据外泄攻击)通常需要专业的 AI 安全解决方案,无法仅通过 NJS 来实现最佳效果。 ### B. 功能扩展性 虽然 NJS 提供了强大的扩展能力,但对于复杂的 AI 流量治理场景,可能需要更专业的 AI Gateway 产品。 ## 2. 改进方向 ### A. 增强安全功能 - 集成专业的 AI 安全解决方案 - 实现 prompt 注入检测 - 添加敏感数据过滤 ### B. 扩展可观测性 - 集成 Prometheus 指标 - 添加分布式追踪 - 实现实时监控仪表板 ### C. 优化性能 - 实现连接池管理 - 添加响应缓存 - 优化请求批处理 # 八、总结 使用 NGINX 结合 NJS 脚本可以实现功能完善的 AI Proxy,提供模型路由与转换、访问控制、宕机切换和 Token 使用日志等核心功能。这种方案的优势在于利用 NGINX 的高性能和稳定性,为构建可控、可观测的 AI 应用入口提供了实践参考。 对于需要更高级 AI 安全防护和流量治理能力的场景,可以考虑结合专业的 AI Gateway 产品,形成更完整的 AI 流量治理解决方案。 *** ## 参考资料 1. [使用 NGINX 作为 AI Proxy - 微信公众号](https://mp.weixin.qq.com/s/_ObrMUibObrEyNU4Ef6BPg) 最后修改:2026 年 02 月 03 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏