LiteLLM 实现 Claude Code 与 Azure OpenAI 无缝对接
1. 为什么非得用 LiteLLM 做这层“翻译官”?——直击 Claude Code 对接 Azure OpenAI 的真实堵点
你装好了 Claude Code,打开设置,想把后端模型换成自己 Azure 账户里已部署的gpt-4o或gpt-35-turbo,结果卡在“API Endpoint”那一栏,反复试了十几种格式:https://your-resource.openai.azure.com/、https://your-resource.openai.azure.com/openai/deployments/your-deployment/chat/completions?api-version=2024-06-01、甚至加了/v1后缀……全报错:“Invalid URL”、“Connection refused”、“Unauthorized”。不是 401 就是 404,再或者直接弹窗提示“Failed to fetch models”。
这不是你配置错了,而是根本性协议错位。
Claude Code(注意,不是 Anthropic 的 Claude,而是那个基于 VS Code 衍生、主打本地 AI 编程助手的开源项目)原生只认一种 API 格式:标准 OpenAI v1 接口规范。它发请求时,Header 里写的是Authorization: Bearer sk-xxx,Body 里是{ "model": "gpt-4", "messages": [...] },期待返回的 JSON 结构里必须有choices[0].message.content字段。而 Azure OpenAI 的接口,虽然也自称“兼容 OpenAI”,但骨子里是另一套逻辑:它要求 Header 里带api-key,URL 路径里必须硬编码 deployment name 和 api-version,返回体里choices[0].message.content是有的,但外层还裹着一层id、created、object等字段,且object值是chat.completion而非chat.completion(注意大小写差异,某些旧版 SDK 会校验这个)。更致命的是,Azure 的 endpoint 不接受model字段作为请求体参数——它只认 URL 路径里的 deployment name。
这就形成了一个经典“鸡生蛋还是蛋生鸡”的困境:Claude Code 说“我只吃标准 OpenAI 饭”,Azure 说“我只做 Azure 特供餐,不换盘子”。双方都“没错”,但就是无法坐上同一张饭桌。
LiteLLM 就是那个端着托盘、现场改菜、还能把两套餐具无缝切换的资深服务员。它不修改任何一方代码,只在中间加一层轻量级代理服务。你告诉 LiteLLM:“我后端有 Azure 的gpt-4o,部署名是my-gpt4o,key 是xxx,endpoint 是https://xxx.openai.azure.com/”,LiteLLM 就立刻给自己注册一个虚拟模型azure/gpt-4o。然后,当 Claude Code 发来一个标准 OpenAI 请求(POST /v1/chat/completions,model: "gpt-4o")时,LiteLLM 接住它,瞬间完成三步操作:
- 把
model: "gpt-4o"替换成 Azure 实际需要的 deployment namemy-gpt4o; - 把
Authorization: Bearer sk-xxx拆解,转成api-key: xxx放进 Header; - 把 URL 重写为
https://xxx.openai.azure.com/openai/deployments/my-gpt4o/chat/completions?api-version=2024-06-01,并确保返回体结构完全符合 OpenAI v1 规范。
整个过程对 Claude Code 透明,它以为自己真连上了 OpenAI 官方服务器;对 Azure 也透明,它收到的是一个完全合规的、带正确 deployment 和 key 的请求。LiteLLM 不是网关,不是反向代理,它是一个语义翻译器 + 协议适配器。这才是“丝滑对接”的底层逻辑——不是强行拉郎配,而是让双方在各自熟悉的语言体系里,自然对话。
提示:很多教程直接让你用 Nginx 做反向代理,这是典型误区。Nginx 只能转发 URL 和 Header,无法动态解析、改写请求体(Body)里的
model字段,也无法根据model名称智能路由到不同 Azure deployment。它只能做“管道工”,而 LiteLLM 是“翻译官+调度员”。
2. 从零启动 LiteLLM 代理服务:避开 WSL、Docker、环境变量的三重陷阱
现在明确目标:在你本机(Windows/macOS/Linux)跑起一个 LiteLLM 服务,监听http://localhost:4000,让它成为 Claude Code 和 Azure 之间的唯一桥梁。很多人卡在这一步,不是因为命令不会敲,而是掉进了三个隐蔽极深的坑。
2.1 陷阱一:WSL 用户的 localhost “幻觉”
如果你用的是 Windows + WSL2(绝大多数开发者现状),执行litellm --model azure/gpt-4o --api-key "xxx" --api-base "https://xxx.openai.azure.com/"后,终端显示LiteLLM running on http://0.0.0.0:4000,你兴冲冲打开 Claude Code,填入http://localhost:4000/v1,点击测试——失败。错误日志里赫然写着Connection refused。
原因?WSL2 的网络栈是 NAT 模式,localhost在 WSL 里指向 WSL 自身,而在 Windows 主机里指向 Windows 自身。你在 WSL 里启动的 LiteLLM 监听的是 WSL 的0.0.0.0:4000,但 Windows 上的 Claude Code 根本访问不到 WSL 的localhost:4000。这不是防火墙问题,是网络命名空间隔离。
破局方案:强制绑定到 Windows 可达地址。不要用默认的0.0.0.0,改用 WSL 的 host IP。在 WSL 终端里执行:
# 获取 Windows 主机在 WSL 网络中的 IP(通常是 172.x.x.1) cat /etc/resolv.conf | grep nameserver | awk '{print $2}' # 假设输出是 172.28.16.1,则启动命令为: litellm --host 172.28.16.1 --port 4000 --model azure/gpt-4o --api-key "xxx" --api-base "https://xxx.openai.azure.com/"然后在 Claude Code 里填http://172.28.16.1:4000/v1。实测下来,比折腾.wslconfig或netsh portproxy稳定十倍。
2.2 陷阱二:Docker 镜像的“版本幻影”
看到官方文档推荐docker run -p 4000:4000 ...,你立刻拉取最新镜像ghcr.io/berriai/litellm:latest,运行后发现 Azure 模型列表为空,或调用时报Model not found。查日志,LiteLLM 启动时只加载了openai、anthropic等基础模型,azureprovider 根本没注册。
根源在于:LiteLLM 的 Docker 镜像分base和full两个版本。latest标签默认指向base,它只包含最精简依赖,不包含 Azure、Google Vertex、AWS Bedrock 等云厂商的专用 SDK。azureprovider 需要azure-identity和azure-mgmt-authorization等包,base镜像里没有。
破局方案:明确指定full镜像:
docker run -d \ --name litellm-proxy \ -p 4000:4000 \ -e AZURE_API_KEY="xxx" \ -e AZURE_API_BASE="https://xxx.openai.azure.com/" \ -e AZURE_API_VERSION="2024-06-01" \ ghcr.io/berriai/litellm:full \ --model azure/gpt-4o注意:环境变量名必须全大写,且AZURE_API_VERSION必须与你 Azure portal 里部署模型时选择的版本严格一致(新版推荐2024-06-01,旧版可能是2023-05-15),否则 Azure 会返回Unsupported api-version。
2.3 陷阱三:环境变量的“作用域迷雾”
你按文档在终端里执行export AZURE_API_KEY="xxx",再运行litellm --model azure/gpt-4o,一切正常。但当你把命令写进start.sh脚本,用./start.sh运行,又报Missing API Key。或者,你用 VS Code 的终端运行成功,但用 Windows Terminal 就失败。
这是因为 Shell 环境变量只在当前 Shell 进程及其子进程中有效。./start.sh是新进程,不继承父进程的export;不同终端是独立进程,互不共享环境变量。
破局方案:用.env文件实现跨平台、跨终端统一管理。在 LiteLLM 启动目录下创建.env文件:
AZURE_API_KEY=your_actual_azure_api_key_here AZURE_API_BASE=https://your-resource-name.openai.azure.com/ AZURE_API_VERSION=2024-06-01然后启动命令简化为:
litellm --model azure/gpt-4o --dotenv .envLiteLLM 会自动读取.env并注入所有变量。这个文件可以 gitignore,安全又方便,无论你用 PowerShell、CMD、Bash 还是 VS Code 终端,只要在该目录下运行,就绝对生效。
3. Claude Code 的终极配置:Endpoint、Key、Model 三者如何精准咬合?
LiteLLM 服务跑起来了,curl http://localhost:4000/v1/models能返回gpt-4o列表,说明代理通了。但 Claude Code 里填什么?很多人在这里反复试错,填错一个字符就前功尽弃。我们拆解它的三个核心配置项,每个都对应一个精确的物理含义。
3.1 Endpoint:不是 URL,而是“服务入口地址”
在 Claude Code 设置里,API Endpoint这一栏,你填的不是 Azure 的地址,也不是 LiteLLM 的完整 URL,而是一个相对路径前缀。官方文档写https://api.openai.com/v1,这只是个示例。对你而言,它必须是http://<你的-litellm-host>:<端口>/v1。
- ✅ 正确示例(WSL 用户):
http://172.28.16.1:4000/v1 - ✅ 正确示例(macOS 本地):
http://localhost:4000/v1 - ❌ 错误示例:
http://localhost:4000(缺/v1,Claude Code 会自动拼.../v1/chat/completions,但 LiteLLM 默认只监听/v1下的路径,根路径 404) - ❌ 错误示例:
https://your-resource.openai.azure.com/openai/deployments/...(这是 Azure 原生地址,绕过 LiteLLM,必然失败)
关键点:Claude Code 的Endpoint是它发起请求的“基地”,所有请求都会以这个地址为根,拼上/v1/chat/completions、/v1/models等后缀。LiteLLM 的/v1路由必须与之严格匹配。
3.2 API Key:一个被严重误解的“占位符”
API Key这一栏,在 Claude Code 里看似要填真实密钥,但在 LiteLLM 代理模式下,它只是一个“通行令牌”,可以是任意字符串。LiteLLM 默认开启--no-secure模式(即不校验传入的 key),它只关心你请求里model字段的值,然后去自己配置的后端列表里找对应模型。
所以,你可以填sk-123、mykey、甚至留空(如果 LiteLLM 启动时加了--no-secure参数)。但为了符合 Claude Code 的 UI 逻辑和未来扩展性(比如你以后想加多模型轮询),强烈建议填一个有意义的、与你 Azure 模型强关联的字符串,例如azure-gpt4o-prod。这样,当你在 LiteLLM 日志里看到model=azure-gpt4o-prod,就能立刻知道流量走向。
注意:这个 key绝不等于你 Azure 的
AZURE_API_KEY。后者是 LiteLLM 用来调用 Azure 的密钥,存在.env里,对 Claude Code 完全不可见。前者只是 Claude Code 发给 LiteLLM 的一个“标签”,用于 LiteLLM 内部路由决策(如果你配置了多个后端模型)。
3.3 Model Name:Claude Code 的“下单菜单”,LiteLLM 的“路由指令”
Model Name是整个链路里最精妙的一环。Claude Code 会把这个值,原封不动地塞进请求体的model字段,发送给 LiteLLM。LiteLLM 收到后,会拿着这个字符串,去自己的模型注册表里查找。
假设你在启动 LiteLLM 时用了:
litellm --model azure/gpt-4o --model azure/gpt-35-turbo那么 LiteLLM 内部就注册了两个虚拟模型:azure/gpt-4o和azure/gpt-35-turbo。此时,Claude Code 的Model Name必须填azure/gpt-4o,才能命中 Azure 的gpt-4o。填gpt-4o不行,填azure-gpt4o也不行,必须是启动命令里--model后面写的完整字符串。
这个设计带来巨大灵活性:你可以把 Azure 的gpt-4o映射为my-prod-model,把另一个测试用的gpt-35-turbo映射为my-dev-model,然后在 Claude Code 里只需切换Model Name,就完成了生产/开发环境的秒级切换,无需重启任何服务。
4. 实战排障:从日志第一行开始,定位 90% 的连接失败
即使你严格按照上述步骤配置,第一次点击“Test Connection”时,大概率还是会看到红色错误提示。别慌,LiteLLM 的日志就是你的 X 光机。我们按日志出现的顺序,逐行解读,告诉你每一行意味着什么,以及下一步该做什么。
4.1 日志起点:Starting proxy server...之后的三秒黄金窗口
LiteLLM 启动后,控制台会快速滚动几行初始化日志,然后停顿约 2-3 秒。这 3 秒,是它在后台尝试连接所有配置的后端模型(Azure、OpenAI 等)进行健康检查。如果这里卡住超过 5 秒,或者直接报错,问题一定出在 LiteLLM 到 Azure 的链路上。
典型错误日志:
ERROR:litellm:Azure Exception: Authentication failed. Please check your API key and endpoint.这表示 LiteLLM 用你.env里的AZURE_API_KEY和AZURE_API_BASE去连 Azure,被拒了。99% 的原因是AZURE_API_BASE格式错误。请严格核对:
- 必须以
https://开头; - 必须是
https://<your-resource-name>.openai.azure.com/,结尾必须有/; - 不能有多余的路径,如
/openai或/v1; AZURE_API_KEY必须是 Azure portal 里“Keys and Endpoint”页签下的KEY 1或KEY 2的完整值,复制时别漏了最后几个字符。
验证方法:不用碰 Claude Code,直接用 curl 测试 LiteLLM 的健康检查:
curl -X GET "http://localhost:4000/v1/models" \ -H "Authorization: Bearer sk-123" \ -H "Content-Type: application/json"如果返回{"object":"list","data":[...]},说明 LiteLLM 自身健康;如果返回{"error":{"message":"Authentication failed..."}},问题就在.env配置。
4.2 请求发起:Received request for model: azure/gpt-4o—— 成功的信号灯
当你在 Claude Code 里点击测试,LiteLLM 控制台会立刻打印一行:
INFO:litellm:Received request for model: azure/gpt-4o恭喜,Claude Code 的请求已经成功抵达 LiteLLM!这行日志证明:
- Endpoint 配置正确(网络可达);
- Model Name 配置正确(LiteLLM 找到了注册的模型);
- LiteLLM 的路由逻辑工作正常。
接下来,如果一切顺利,你会看到INFO:litellm:Azure call completed successfully,然后是完整的响应体。但如果卡在这里,或者紧接着报错,问题就出在 LiteLLM 到 Azure 的“最后一公里”。
典型错误日志:
ERROR:litellm:Azure Exception: The request was rejected because the provided API version is invalid.这说明AZURE_API_VERSION不匹配。登录 Azure portal,打开你的 OpenAI 资源 -> “Deployments”,点开gpt-4o部署详情,看右上角的“API version”字段,把它原样复制到.env里,重启 LiteLLM。
4.3 响应返回:Response from litellm proxy: {"object":"list","data":[...]}—— 最后的临门一脚
LiteLLM 成功拿到 Azure 的响应后,会把它转换成标准 OpenAI 格式,再发回给 Claude Code。这时,日志会显示Response from litellm proxy: ...。如果这个响应体里data数组为空,或者model字段是azure/gpt-4o而不是gpt-4o,说明 LiteLLM 的响应体转换出了问题。
根本原因:LiteLLM 的--model参数写法。如果你启动时写的是--model azure/gpt-4o,它会把 Azure 返回的model: "gpt-4o"原样透传。但 Claude Code 期望的model字段值,应该是它自己在 UI 里填的那个Model Name(即azure/gpt-4o),而不是 Azure 的原始值。这会导致 Claude Code 内部模型识别失败。
解决方案:强制覆盖响应体 model 字段。启动 LiteLLM 时,加上--set-model-name参数:
litellm --model azure/gpt-4o --set-model-name gpt-4o --dotenv .env这样,LiteLLM 在返回给 Claude Code 之前,会把响应体里的"model": "gpt-4o"强制改成"model": "gpt-4o"(看起来一样,但这是为了确保与 Claude Code 的内部模型注册表一致)。实测下来,这是解决“Test Connection 通过但实际调用无响应”的终极钥匙。
5. 进阶实战:一个 LiteLLM 实例,同时驱动 Claude Code 和 Cursor
LiteLLM 的价值远不止于“救活一个 Claude Code”。当你把它跑起来,它就是一个可编程的 AI 模型路由中枢。我们可以用它,让同一个 Azure 账户,同时服务多个本地 AI 工具,且互不干扰。
5.1 场景还原:Claude Code 用gpt-4o,Cursor 用gpt-35-turbo
假设你有两个需求:
- 在 Claude Code 里,专注写代码,用最强的
gpt-4o; - 在 Cursor 里,做日常聊天、文档总结,用更便宜的
gpt-35-turbo。
你不想开两个 LiteLLM 实例,也不想每次切换工具就改配置。目标:一个 LiteLLM,一个端口,靠Model Name自动分流。
配置步骤:
在
.env里,定义两个 Azure deployment 的完整信息:AZURE_API_KEY=your_main_key AZURE_API_BASE=https://your-resource.openai.azure.com/ AZURE_API_VERSION=2024-06-01 # 为 gpt-35-turbo 单独定义一个 deployment name AZURE_DEPLOYMENT_NAME_GPT35=gpt35turbo-prod # 为 gpt-4o 单独定义一个 deployment name AZURE_DEPLOYMENT_NAME_GPT4O=gpt4o-prod启动 LiteLLM,注册两个模型,并指定各自的 deployment:
litellm \ --model azure/gpt-4o --azure-deployment-name gpt4o-prod \ --model azure/gpt-35-turbo --azure-deployment-name gpt35turbo-prod \ --dotenv .env \ --set-model-name gpt-4o --set-model-name gpt-35-turbo注意:
--azure-deployment-name参数必须紧跟在对应的--model后面,LiteLLM 会按顺序将它们一一绑定。在 Claude Code 设置里,
Model Name填azure/gpt-4o;在 Cursor 的设置里(Settings > AI > Provider > Custom OpenAI),Model填azure/gpt-35-turbo。
原理揭秘:LiteLLM 的模型注册表是一个字典,键是--model的值(azure/gpt-4o),值是包含azure-deployment-name、api-key等完整配置的对象。当请求进来,它先查键,再用键对应的值去调用 Azure。整个过程毫秒级,你感觉不到任何延迟。
5.2 安全加固:用 Master Key 实现“单点登录,多端授权”
上面的方案有个隐患:Claude Code 和 Cursor 的API Key都是明文填在 UI 里的,万一被截图泄露,攻击者就能用你的 LiteLLM 实例调用 Azure,产生费用。LiteLLM 提供了--master-key机制,完美解决。
操作流程:
- 启动 LiteLLM 时,加上
--master-key "sk-my-super-secret-master-key"; - 在 Claude Code 的
API Key栏,不再填任意字符串,而是填这个 master key:sk-my-super-secret-master-key; - 在 Cursor 里,同样填这个 master key。
效果:LiteLLM 会校验每一个请求的AuthorizationHeader。只有持有这个 master key 的请求,才会被放行。它就像一把总钥匙,打开了 LiteLLM 这扇大门,至于进门后你想调用哪个模型(azure/gpt-4o还是azure/gpt-35-turbo),则由Model Name决定。这样,你只需要保护好这一把 master key,就守住了整个 Azure 账户的安全。
提示:master key 可以用
openssl rand -hex 32生成一个 64 位随机字符串,存进.env文件,启动时用--master-key $(cat .env | grep MASTER_KEY | cut -d'=' -f2)读取,彻底避免硬编码。
5.3 效能监控:用 LiteLLM Metrics 看清每一次调用的成本
你可能关心:gpt-4o一次调用花了多少钱?今天总共调用了多少次?哪些模型最耗 token?LiteLLM 内置了 Prometheus metrics,只需加一个参数就能开启。
启动命令追加--metrics:
litellm --model azure/gpt-4o --metrics --dotenv .env然后,访问http://localhost:4000/metrics,你会看到类似这样的文本:
# HELP litellm_total_requests_total Total number of requests # TYPE litellm_total_requests_total counter litellm_total_requests_total{model="azure/gpt-4o"} 127 # HELP litellm_request_cost_total Total cost of requests (USD) # TYPE litellm_request_cost_total counter litellm_request_cost_total{model="azure/gpt-4o"} 0.0234这些指标可以直接接入 Grafana,画出实时调用曲线、成本热力图。对于个人开发者,一个简单的curl http://localhost:4000/metrics | grep cost就能随时掌握今日开销。这才是真正“丝滑”背后的理性支撑——不盲目,不浪费,每一分算力都花在刀刃上。
