我用 Go 两百行写了一个 LLM 透明代理,现在所有大模型共用同一个 API 入口
一个不到 15MB 的 Go 二进制,让你把 OpenAI、Anthropic、Azure、Ollama 全部塞进一个
localhost:18080里。
为什么需要这个东西?
事情很简单:我的机器上跑着好几个 LLM API 的客户端——OpenCode、Claude Code、自己写的脚本、前端调试工具。每个都要配置不同的 base_url、api_key、model,而且这几个值散落在 ~/.config/opencode.json、环境变量、.env 文件、脚本硬编码里……
改一个后端地址,六七个地方都要动。
更烦的是,有些工具只支持 OpenAI 协议,有些只支持 Anthropic。你的后端是 Anthropic 但客户端只认 OpenAI?对不起,调不通。
LLM API Proxy 就是来解决这个问题的——一个端口,双协议,所有后端统一入口。
一句话说清楚它是干嘛的
你所有的 API 客户端 → localhost:18080 → 任意后端(OpenAI/Anthropic/Ollama/Azure)
再直白点:你在客户端把 base_url 全部改成 http://localhost:18080/v1,配一个统一的 API Key,然后后端随便换、随便加,客户端一行代码不用改。
当然,他还有一个作用,就是吧所有Agent的请求都记录到你的数据库中,方便你研究AI的思考过程。🤭
架构就是个透明代理
curl / OpenCode / Claude Code
│
▼
┌─────────────────────┐
│ LLM API Proxy │
│ │
│ /v1/chat/completions──→ OpenAI 后端 (mimo / DeepSeek / Ollama)
│ /v1/messages ──→ Anthropic 后端 (Claude)
│ /v1/models ──→ 透传
│ /v1/embeddings ──→ 透传
│ │
│ ✅ 全量日志 → MongoDB
└─────────────────────┘
不解析请求、不改模型参数、不做协议魔法转换。透明转发,就跟 nginx 反代一样,只是它同时支持两种 LLM 协议。
核心设计原则
1. 按协议路由,不做翻译
这是跟其他 LLM 代理最大的区别。常见的网关类项目喜欢做 OpenAI ↔ Anthropic 协议互译——把 /v1/messages 的 Anthropic 请求翻译成 /v1/chat/completions 再发给 OpenAI 后端。
我们这个不行。protocol: "anthropic" 的后端就是原样透传——Anthropic 格式进去,Anthropic 格式出来,零损耗。
为什么?第一,协议翻译必然丢字段(Anthropic 的 system 是顶层字段、stop_reason 语义不同、thinking 块没对应物)。第二,翻译本身是额外开销,你代理在中间做 JSON 重写,延迟直接上去。
简单说:后端是什么协议就用什么协议直通,不装聪明。
2. 多后端动态切换
配置文件里可以挂任意多个后端,每个带上协议标签:
backends:
- name: "mimo"
base_url: "https://token-plan-cn.xiaomimimo.com/v1"
protocol: "openai"
default: true
- name: "claude"
base_url: "https://api.anthropic.com"
protocol: "anthropic"
api_key: "sk-ant-xxxx"
- name: "ollama"
base_url: "http://localhost:11434/v1"
protocol: "openai"
运行时通过 X-Backend 请求头切换,不用重启服务:
# 切到 Ollama
curl http://localhost:18080/v1/chat/completions \
-H "X-Backend: ollama" \
-d '{"model":"llama3","messages":[{"role":"user","content":"hi"}]}'
3. MongoDB 可选的日志系统
每次 API 调用自动记录到 MongoDB:请求体、响应体、耗时、状态码、后端名、客户端 IP。Authorization 自动脱敏。
关键设计:MongoDB 挂了不影响代理服务。启动时连不上就降级,日志写入变成 no-op,服务照常跑。你不需要为了跑个代理还要维护一个数据库实例。
// MongoDB 查询——按后端统计调用量
db.api_logs.aggregate([{$group: {_id: "$backend_name", count: {$sum: 1}}}])
// 查看慢请求
db.api_logs.find({duration_ms: {$gte: 10000}}).sort({timestamp: -1})
技术实现
Go 标准库 net/http/httputil.ReverseProxy,没用什么花哨框架。核心文件三个:
llm-proxy/
├── main.go # 入口 + 路由注册
├── internal/
│ ├── config/config.go # YAML 配置加载 + 协议默认值
│ ├── handler/proxy.go # 反向代理核心 + 日志捕获
│ ├── handler/anthropic.go # Anthropic 端点处理
│ └── store/mongo.go # MongoDB 存储层
编译出来一个 15MB 左右的二进制,无运行时依赖,直接丢服务器上 ./llm-proxy 就跑。
几个实现细节:
流式 SSE ——
captureResponseWriter包装了http.ResponseWriter,非流式完整捕获 body,流式从data:行中拼接 content 字段存库路径不重复拼接 ——
base_url里的/v1不会跟客户端请求的/v1拼成/v1/v1,反向代理的 Director 里做了 TrimPrefixHost 头重写 ——
req.Host在 Director 中显式设为后端 Host,避免 Go net/http 默认行为把 Host 设成目标 URL 而非请求原始 Host,导致 WAF 403Anthropic 鉴权 —— 直透传模式用
x-api-key,不走Authorization: Bearernil-safe 存储 ——
MongoStore.InsertLog()零接收器直接 return,主程序就算没连上 MongoDB 也不 panic
Claude Code 集成
很多人用 Claude Code 做开发。配置方式就一行:
export ANTHROPIC_BASE_URL="http://localhost:18080/v1"
export ANTHROPIC_API_KEY="sk-proxy-demo-key-2024"
然后 claude 命令的所有请求都走代理,后端可以随时切换到任何兼容服务。
跟其他方案的区别
LLM API Proxy 的定位很清晰:极简、透明、可靠。不提供 Web 管理界面,不走数据库依赖,不在中间做协议转换。就是一台反向代理,做得非常纯粹。
快速上手
# 1. 克隆
git clone https://gitee.com/yang_davip/llm-proxy.git
cd llm-proxy
# 2. 编辑配置
vim config.yaml # 填入你的后端地址和 API Key
# 3. 编译启动
go build -o llm-proxy . && ./llm-proxy
# 4. 测试
curl http://localhost:18080/health
curl -X POST http://localhost:18080/v1/chat/completions \
-H "Authorization: Bearer sk-proxy-demo-key-2024" \
-d '{"model":"mimo-v2.5-pro","messages":[{"role":"user","content":"hi"}]}'
启动后把你要用的所有工具的 base_url 全部指向 http://localhost:18080/v1,后端随便换。
后续
项目在 Gitee 上开源:yang_davip/llm-proxy
TODO 列表里还有几个想做的:
请求重试 + 限流熔断
/metrics端点(Prometheus 格式)请求级缓存(相同 prompt 直接返回缓存结果)
Docker 一键部署
欢迎 Issue 和 PR。
写于 2026 年 6 月的一个下午。
- 感谢你赐予我前进的力量
