单二进制 GLM API 透明转发代理。将 OpenAI / Anthropic 协议的请求按路径前缀路由到 Z.AI(智谱)GLM 上游,支持流式响应、客户端鉴权与模型校验。
- 多协议端点:通过 URL 路径前缀区分协议,同一代理同时服务 OpenAI 与 Anthropic 格式请求
- 透明转发:原样转发请求体与响应体,完整支持 SSE 流式输出
- 客户端鉴权:静态 API key 白名单,恒定时间比对防止时序攻击
- 模型校验:精确匹配优先,支持前缀通配符(如
glm-*、glm-z1-*) - 结构化日志:支持 JSON 与人类可读两种格式,通过
RUST_LOG控制级别 - 优雅关闭:捕获 SIGTERM 与 Ctrl-C,确保连接正常结束
- 零配置启动:开箱即用的示例配置,三个环境变量即可运行
客户端请求
│
▼
┌─────────────────────────────────────────────────┐
│ transport(axum) │
│ ├─ 提取路径前缀(openai / anthropic) │
│ ├─ 提取 API key(Authorization / x-api-key) │
│ ├─ 解析请求体中的 model 字段 │
│ └─ 过滤逐跳头(hop-by-hop headers) │
├─────────────────────────────────────────────────┤
│ application(ProxyService) │
│ └─ 鉴权 → 模型校验 + 端点选择 → 密钥映射 │
├─────────────────────────────────────────────────┤
│ security + routing │
│ ├─ ConfigKeyValidator:恒定时间 key 比对 │
│ └─ GlmRoutingEngine:精确匹配 + 通配符路由 │
└─────────────────────────────────────────────────┘
│
▼
GLM 上游(按前缀选择 base_url)
客户端请求 URL 的第一段路径为协议前缀,代理剥离后转发到对应上游:
| 前缀 | 上游端点 | 示例 |
|---|---|---|
openai |
https://open.bigmodel.cn/api/coding/paas/v4 |
/openai/v1/chat/completions → .../paas/v4/v1/chat/completions |
anthropic |
https://api.z.ai/api/anthropic |
/anthropic/v1/messages → .../anthropic/v1/messages |
端点在配置文件 [[glm.endpoints]] 中定义,可自由扩展。
git clone <repo-url> && cd ai-proxy
cargo build --release复制示例配置并按需修改(文件名以 local. 开头会被 .gitignore 自动忽略):
cp configs/proxy.toml configs/local.proxy.toml配置文件结构:
# configs/local.proxy.toml
[client_keys]
keys = [
{ key = "sk-your-client-key", client_id = "alice" },
]
[glm]
api_key_env = "GLM_API_KEY" # GLM API key 对应的环境变量名
model_patterns = ["glm-*"] # 允许的模型名规则
[[glm.endpoints]]
prefix = "openai" # URL 路径前缀
base_url = "https://open.bigmodel.cn/api/coding/paas/v4"
[[glm.endpoints]]
prefix = "anthropic"
base_url = "https://api.z.ai/api/anthropic"
[server]
body_limit_bytes = 33554432 # 请求体大小限制(默认 32 MB)export GLM_API_KEY=<your_glm_api_key>./target/release/ai-proxy默认监听 0.0.0.0:8080,可通过环境变量覆盖:
AI_PROXY_ADDR=127.0.0.1:9000 ./target/release/ai-proxy# 健康检查
curl http://localhost:8080/health
# → ok
# OpenAI 格式请求
curl http://localhost:8080/openai/v1/chat/completions \
-H "Authorization: Bearer sk-your-client-key" \
-H "Content-Type: application/json" \
-d '{"model":"glm-4-flash","messages":[{"role":"user","content":"你好"}]}'
# Anthropic 格式请求
curl http://localhost:8080/anthropic/v1/messages \
-H "Authorization: Bearer sk-your-client-key" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"glm-4-flash","max_tokens":256,"messages":[{"role":"user","content":"你好"}]}'| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
client_keys.keys |
数组 | 是 | — | 客户端 API key 白名单 |
client_keys.keys[].key |
字符串 | 是 | — | 客户端 API key |
client_keys.keys[].client_id |
字符串 | 是 | — | 日志追踪用的客户端标识 |
glm.endpoints |
数组 | 是 | — | 协议端点列表 |
glm.endpoints[].prefix |
字符串 | 是 | — | URL 路径前缀(不含斜杠) |
glm.endpoints[].base_url |
字符串 | 是 | — | 对应上游 base URL |
glm.api_key_env |
字符串 | 是 | — | 持有 GLM API key 的环境变量名 |
glm.model_patterns |
数组 | 是 | — | 模型匹配规则 |
glm.timeout_secs |
整数 | 否 | 300 | 上游请求总超时(秒) |
glm.connect_timeout_secs |
整数 | 否 | 10 | 上游连接超时(秒) |
server.body_limit_bytes |
整数 | 否 | 33554432 | 最大请求体大小(字节) |
server.cors_allowed_origins |
数组 | 否 | — | CORS 允许的来源列表(未设置时允许所有来源) |
model_patterns 支持两种格式,按声明顺序评估:
- 精确匹配:
glm-4-flash— 仅匹配该模型名 - 前缀通配符:
glm-*— 匹配所有以glm-开头的模型名
精确匹配优先于通配符。建议将高频精确名放在前面,通配符放在末尾兜底:
model_patterns = ["glm-4-flash", "glm-z1-air", "glm-z1-*", "glm-*"]| 变量 | 默认值 | 说明 |
|---|---|---|
GLM_API_KEY |
—(必须) | GLM 上游 API key |
AI_PROXY_CONFIG |
configs/local.proxy.toml(如存在),否则 configs/proxy.toml |
配置文件路径,设置后忽略自动查找 |
AI_PROXY_ADDR |
0.0.0.0:8080 |
监听地址与端口 |
RUST_LOG |
info |
日志级别过滤(如 info,ai_proxy=debug) |
LOG_FORMAT |
人类可读 | 设为 json 启用结构化 JSON 日志 |
src/
├── main.rs 入口:加载配置、组装应用、启动服务器、优雅关闭
├── lib.rs 库入口:导出所有公共模块
├── domain.rs 强类型 ID(ModelId、ClientKeyId、RequestId)与请求上下文
├── config.rs TOML 配置解析与一致性验证
├── security.rs 客户端 key 校验(KeyValidator trait + ConfigKeyValidator)
├── routing.rs 模型路由(RoutingEngine trait + GlmRoutingEngine)
├── error.rs 统一错误类型与 HTTP 状态码映射
├── observability.rs 链路追踪初始化(tracing-subscriber)
├── application.rs 代理业务编排(ProxyService)
└── transport.rs HTTP 入口层(axum Router、proxy_handler、逐跳头过滤)
domain (零外部依赖,仅 uuid 例外)
↓
config (无外部依赖)
↓
error (依赖 domain)
security (依赖 config、domain)
routing (依赖 config、domain)
↓
application (依赖 security、routing、error、domain)
↓
transport (依赖 application、domain、error、routing、security)
↓
main (依赖全部,组装并启动)
规则:domain 零外部依赖;禁止下层模块反向依赖上层。
- 客户端发送请求到
http://<addr>/<prefix>/<path> transport层提取路径前缀、API key、请求体中的model字段application层调用security校验客户端 keyrouting层校验模型名是否匹配model_patterns,并按前缀选择上游端点security层提供 GLM 上游 API keytransport层过滤逐跳头、注入上游鉴权头、转发请求- 上游响应(含 SSE 流)原样回传客户端
- 记录请求日志(request_id、client_id、model、状态码、耗时)
- Rust 2024 edition(rustup 最新 stable)
- 环境变量
GLM_API_KEY(集成测试需要,单元测试不需要)
# 运行所有测试
cargo test
# 仅单元测试
cargo test --lib
# 仅集成测试
cargo test --test integration
# Clippy 检查(.cargo/config.toml 已开启 -D warnings)
cargo clippy
# 开发模式运行(自动加载 configs/local.proxy.toml,不存在则加载 configs/proxy.toml)
cargo run
# 发布构建(thin LTO + strip symbols)
cargo build --release
# 运行端到端演示(需要真实 GLM API key)
GLM_API_KEY=<key> cargo run --example proxy_demo- 单元测试(32 个):各模块
#[cfg(test)]内联测试,覆盖配置解析、key 校验、模型路由、错误映射 - 集成测试(11 个):
tests/integration.rs使用 wiremock 模拟上游,覆盖健康检查、鉴权失败、模型不存在、请求转发、响应透传、上游错误透传等完整链路
- 模块顶部
//!和公开 API///注释使用中文 - 库边界错误使用
thiserrorenum;业务逻辑使用anyhow::Result - 日志使用
tracing宏,禁止println! - 不在日志或错误信息中暴露明文 API key
- 领域类型使用 newtype 模式(
Deref<Target=str>+Display+Hash)
- GLM API key 仅通过环境变量注入,禁止写入配置文件或代码
- 客户端 key 校验使用恒定时间字节比较,防止时序攻击
- 本地配置文件(
configs/local.*)、生产配置文件(configs/production.*)已加入.gitignore - 请求头中的
Authorization和x-api-key不会转发到上游,由代理统一注入上游鉴权头
FROM rust:1.85 AS builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/ai-proxy /usr/local/bin/
COPY configs/proxy.toml /etc/ai-proxy/proxy.toml
EXPOSE 8080
CMD ["ai-proxy"]docker build -t ai-proxy .
docker run -e GLM_API_KEY=<key> -p 8080:8080 ai-proxyGLM_API_KEY=<key> AI_PROXY_CONFIG=/etc/ai-proxy/proxy.toml AI_PROXY_ADDR=0.0.0.0:8080 ./ai-proxy配合 systemd 实现进程管理与自动重启。
MIT