摘要:Claude Code Haha 原版绑定 Anthropic API,对国内开发者极不友好。本文手把手教你通过 LiteLLM 代理,将通义千问、DeepSeek、MiniMax 等国产大模型接入 cc-haha,实现真正的"模型自由"。从协议转换原理到 .env 配置,从单模型到多模型热切换,全文提供可直接复制的配置模板和 Python 自动化脚本。
一、为什么国产模型接入是个难题
Claude Code 基于 Anthropic Messages API 协议通信。这套协议与 OpenAI 的 Chat Completions API 有本质区别:
- 请求格式不同:Anthropic 使用 messages + system 分离,OpenAI 使用统一的 messages 列表
- 流式响应不同:Anthropic 是 Server-Sent Events with event: content_block_delta,OpenAI 是 SSE with data: {…}
- 专有参数不同:Anthropic 有 thinking、cache_control 等 OpenAI 不认识的参数
国内大模型厂商(阿里云百炼、DeepSeek、MiniMax)几乎全部采用 OpenAI 兼容接口。这意味着:不做协议转换,cc-haha 根本无法调用国产模型。
通义千问 API
LiteLLM Proxy
Anthropic API
cc-haha CLI
通义千问 API
LiteLLM Proxy
Anthropic API
cc-haha CLI
#mermaid-svg-cXZQGzg2dyQJSm1d{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cXZQGzg2dyQJSm1d .error-icon{fill:#552222;}#mermaid-svg-cXZQGzg2dyQJSm1d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cXZQGzg2dyQJSm1d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cXZQGzg2dyQJSm1d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cXZQGzg2dyQJSm1d .marker.cross{stroke:#333333;}#mermaid-svg-cXZQGzg2dyQJSm1d svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cXZQGzg2dyQJSm1d p{margin:0;}#mermaid-svg-cXZQGzg2dyQJSm1d .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cXZQGzg2dyQJSm1d text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-cXZQGzg2dyQJSm1d .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-cXZQGzg2dyQJSm1d .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-cXZQGzg2dyQJSm1d #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-cXZQGzg2dyQJSm1d .sequenceNumber{fill:white;}#mermaid-svg-cXZQGzg2dyQJSm1d #sequencenumber{fill:#333;}#mermaid-svg-cXZQGzg2dyQJSm1d #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-cXZQGzg2dyQJSm1d .messageText{fill:#333;stroke:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cXZQGzg2dyQJSm1d .labelText,#mermaid-svg-cXZQGzg2dyQJSm1d .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .loopText,#mermaid-svg-cXZQGzg2dyQJSm1d .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-cXZQGzg2dyQJSm1d .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-cXZQGzg2dyQJSm1d .noteText,#mermaid-svg-cXZQGzg2dyQJSm1d .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-cXZQGzg2dyQJSm1d .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cXZQGzg2dyQJSm1d .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cXZQGzg2dyQJSm1d .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cXZQGzg2dyQJSm1d .actorPopupMenu{position:absolute;}#mermaid-svg-cXZQGzg2dyQJSm1d .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-cXZQGzg2dyQJSm1d .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cXZQGzg2dyQJSm1d .actor-man circle,#mermaid-svg-cXZQGzg2dyQJSm1d line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-cXZQGzg2dyQJSm1d :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
原生支持
Anthropic 协议
直接调用
不兼容 ❌
通过 LiteLLM 代理 ✅
POST /v1/messages
Anthropic 格式 ✅
POST /v1/messages
Anthropic 格式 → 400 Error
POST /v1/messages
Anthropic 格式
协议转换
丢弃专有参数
POST /compatible-mode/v1/chat/completions
OpenAI 格式
SSE 流式响应
SSE 流式响应
Anthropic 格式
二、LiteLLM:一把钥匙开所有锁
LiteLLM 是一个开源的 LLM 代理网关,核心能力是统一接入 100+ 模型,并支持接收 Anthropic 协议请求。它的工作原理是"中间人代理":
#mermaid-svg-FjYKROiievjNhUGp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FjYKROiievjNhUGp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FjYKROiievjNhUGp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FjYKROiievjNhUGp .error-icon{fill:#552222;}#mermaid-svg-FjYKROiievjNhUGp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FjYKROiievjNhUGp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FjYKROiievjNhUGp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FjYKROiievjNhUGp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FjYKROiievjNhUGp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FjYKROiievjNhUGp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FjYKROiievjNhUGp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FjYKROiievjNhUGp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FjYKROiievjNhUGp .marker.cross{stroke:#333333;}#mermaid-svg-FjYKROiievjNhUGp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FjYKROiievjNhUGp p{margin:0;}#mermaid-svg-FjYKROiievjNhUGp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FjYKROiievjNhUGp .cluster-label text{fill:#333;}#mermaid-svg-FjYKROiievjNhUGp .cluster-label span{color:#333;}#mermaid-svg-FjYKROiievjNhUGp .cluster-label span p{background-color:transparent;}#mermaid-svg-FjYKROiievjNhUGp .label text,#mermaid-svg-FjYKROiievjNhUGp span{fill:#333;color:#333;}#mermaid-svg-FjYKROiievjNhUGp .node rect,#mermaid-svg-FjYKROiievjNhUGp .node circle,#mermaid-svg-FjYKROiievjNhUGp .node ellipse,#mermaid-svg-FjYKROiievjNhUGp .node polygon,#mermaid-svg-FjYKROiievjNhUGp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FjYKROiievjNhUGp .rough-node .label text,#mermaid-svg-FjYKROiievjNhUGp .node .label text,#mermaid-svg-FjYKROiievjNhUGp .image-shape .label,#mermaid-svg-FjYKROiievjNhUGp .icon-shape .label{text-anchor:middle;}#mermaid-svg-FjYKROiievjNhUGp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FjYKROiievjNhUGp .rough-node .label,#mermaid-svg-FjYKROiievjNhUGp .node .label,#mermaid-svg-FjYKROiievjNhUGp .image-shape .label,#mermaid-svg-FjYKROiievjNhUGp .icon-shape .label{text-align:center;}#mermaid-svg-FjYKROiievjNhUGp .node.clickable{cursor:pointer;}#mermaid-svg-FjYKROiievjNhUGp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FjYKROiievjNhUGp .arrowheadPath{fill:#333333;}#mermaid-svg-FjYKROiievjNhUGp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FjYKROiievjNhUGp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FjYKROiievjNhUGp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FjYKROiievjNhUGp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FjYKROiievjNhUGp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FjYKROiievjNhUGp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FjYKROiievjNhUGp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FjYKROiievjNhUGp .cluster text{fill:#333;}#mermaid-svg-FjYKROiievjNhUGp .cluster span{color:#333;}#mermaid-svg-FjYKROiievjNhUGp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FjYKROiievjNhUGp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FjYKROiievjNhUGp rect.text{fill:none;stroke-width:0;}#mermaid-svg-FjYKROiievjNhUGp .icon-shape,#mermaid-svg-FjYKROiievjNhUGp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FjYKROiievjNhUGp .icon-shape p,#mermaid-svg-FjYKROiievjNhUGp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FjYKROiievjNhUGp .icon-shape .label rect,#mermaid-svg-FjYKROiievjNhUGp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FjYKROiievjNhUGp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FjYKROiievjNhUGp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FjYKROiievjNhUGp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Anthropic 协议
OpenAI 协议
OpenAI 协议
Anthropic 协议
OpenAI 协议
Anthropic 协议
cc-haha
LiteLLM Proxy
通义千问
DeepSeek
MiniMax
Ollama 本地
OpenRouter
协议转换层
drop_params: true
格式映射
错误转换
2.1 安装 LiteLLM
# 推荐使用独立 Python 环境
python3 -m venv ~/.venvs/litellm
source ~/.venvs/litellm/bin/activate
# 安装带 proxy 功能的完整版
pip install 'litellm[proxy]'
# 验证安装
litellm –version
2.2 配置文件:litellm_config.yaml
这是整个接入流程的核心。下面提供一份生产就绪的多模型配置:
# ============================================================
# LiteLLM 配置文件:支持通义千问 + DeepSeek + MiniMax + Ollama
# ============================================================
model_list:
# —- 通义千问(阿里云百炼 / DashScope)—-
– model_name: qwen–max
litellm_params:
model: openai/qwen–max
api_key: os.environ/DASHSCOPE_API_KEY
api_base: https://dashscope.aliyuncs.com/compatible–mode/v1
– model_name: qwen–plus
litellm_params:
model: openai/qwen–plus
api_key: os.environ/DASHSCOPE_API_KEY
api_base: https://dashscope.aliyuncs.com/compatible–mode/v1
– model_name: qwen–turbo
litellm_params:
model: openai/qwen–turbo
api_key: os.environ/DASHSCOPE_API_KEY
api_base: https://dashscope.aliyuncs.com/compatible–mode/v1
# —- DeepSeek —-
– model_name: deepseek–chat
litellm_params:
model: deepseek/deepseek–chat
api_key: os.environ/DEEPSEEK_API_KEY
api_base: https://api.deepseek.com
– model_name: deepseek–coder
litellm_params:
model: deepseek/deepseek–coder
api_key: os.environ/DEEPSEEK_API_KEY
api_base: https://api.deepseek.com
# —- MiniMax(直连 Anthropic 兼容接口)—-
– model_name: minimax–m2.7
litellm_params:
model: anthropic/MiniMax–M2.7
api_key: os.environ/MINIMAX_API_KEY
api_base: https://api.minimaxi.com/anthropic
# —- 本地 Ollama(可选)—-
– model_name: llama3
litellm_params:
model: ollama/llama3
api_base: http://localhost:11434
# —- 关键全局设置 —-
litellm_settings:
# 丢弃 Anthropic 专有参数(thinking、cache_control 等)
drop_params: true
# 部分国产模型只支持 /v1/chat/completions
# 此选项强制 LiteLLM 使用该端点而非 /v1/messages
use_chat_completions_url_for_anthropic_messages: true
# 请求超时(毫秒)
request_timeout: 120
# 失败重试
num_retries: 2
关键参数说明:
- drop_params: true:必须开启。cc-haha 会发送 thinking 等 Anthropic 专有参数,如果不丢弃,国产模型会直接报错。
- use_chat_completions_url_for_anthropic_messages: true:强制使用 OpenAI 的 chat completions 端点,解决部分国产模型不支持 Anthropic /v1/messages 的问题。
2.3 启动代理
# 设置环境变量(从各平台控制台获取)
export DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx
export DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx
export MINIMAX_API_KEY=xxxxxxxxxxxxxxxx
# 启动代理(默认监听 4000 端口)
litellm –config litellm_config.yaml –port 4000
# 验证代理是否正常工作
curl http://localhost:4000/v1/models
三、cc-haha 端配置:.env 文件详解
3.1 基础配置模板
# ============================================================
# cc-haha 环境变量配置
# 指向本地 LiteLLM 代理
# ============================================================
# 认证令牌(使用 LiteLLM 代理时可填任意值,除非设置了 master_key)
ANTHROPIC_AUTH_TOKEN=sk-anything
# Base URL:LiteLLM 代理地址
# ⚠️ 注意:不要带 /v1 后缀!Anthropic SDK 会自动拼接
ANTHROPIC_BASE_URL=http://localhost:4000
# 默认模型(对应 litellm_config.yaml 中的 model_name)
ANTHROPIC_MODEL=qwen-max
# 三档模型映射(cc-haha 内部使用 sonnet/haiku/opus 区分任务复杂度)
ANTHROPIC_DEFAULT_SONNET_MODEL=qwen-plus
ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen-turbo
ANTHROPIC_DEFAULT_OPUS_MODEL=qwen-max
# API 超时(毫秒)
API_TIMEOUT_MS=3000000
# 禁用遥测和非必要网络请求(建议始终开启)
DISABLE_TELEMETRY=1
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
3.2 ⚠️ 最常见的配置错误
#mermaid-svg-dX79ke4RA1rH0PUV{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dX79ke4RA1rH0PUV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dX79ke4RA1rH0PUV .error-icon{fill:#552222;}#mermaid-svg-dX79ke4RA1rH0PUV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dX79ke4RA1rH0PUV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dX79ke4RA1rH0PUV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dX79ke4RA1rH0PUV .marker.cross{stroke:#333333;}#mermaid-svg-dX79ke4RA1rH0PUV svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dX79ke4RA1rH0PUV p{margin:0;}#mermaid-svg-dX79ke4RA1rH0PUV .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-dX79ke4RA1rH0PUV .cluster-label text{fill:#333;}#mermaid-svg-dX79ke4RA1rH0PUV .cluster-label span{color:#333;}#mermaid-svg-dX79ke4RA1rH0PUV .cluster-label span p{background-color:transparent;}#mermaid-svg-dX79ke4RA1rH0PUV .label text,#mermaid-svg-dX79ke4RA1rH0PUV span{fill:#333;color:#333;}#mermaid-svg-dX79ke4RA1rH0PUV .node rect,#mermaid-svg-dX79ke4RA1rH0PUV .node circle,#mermaid-svg-dX79ke4RA1rH0PUV .node ellipse,#mermaid-svg-dX79ke4RA1rH0PUV .node polygon,#mermaid-svg-dX79ke4RA1rH0PUV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dX79ke4RA1rH0PUV .rough-node .label text,#mermaid-svg-dX79ke4RA1rH0PUV .node .label text,#mermaid-svg-dX79ke4RA1rH0PUV .image-shape .label,#mermaid-svg-dX79ke4RA1rH0PUV .icon-shape .label{text-anchor:middle;}#mermaid-svg-dX79ke4RA1rH0PUV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dX79ke4RA1rH0PUV .rough-node .label,#mermaid-svg-dX79ke4RA1rH0PUV .node .label,#mermaid-svg-dX79ke4RA1rH0PUV .image-shape .label,#mermaid-svg-dX79ke4RA1rH0PUV .icon-shape .label{text-align:center;}#mermaid-svg-dX79ke4RA1rH0PUV .node.clickable{cursor:pointer;}#mermaid-svg-dX79ke4RA1rH0PUV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dX79ke4RA1rH0PUV .arrowheadPath{fill:#333333;}#mermaid-svg-dX79ke4RA1rH0PUV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dX79ke4RA1rH0PUV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dX79ke4RA1rH0PUV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dX79ke4RA1rH0PUV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dX79ke4RA1rH0PUV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dX79ke4RA1rH0PUV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dX79ke4RA1rH0PUV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dX79ke4RA1rH0PUV .cluster text{fill:#333;}#mermaid-svg-dX79ke4RA1rH0PUV .cluster span{color:#333;}#mermaid-svg-dX79ke4RA1rH0PUV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dX79ke4RA1rH0PUV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dX79ke4RA1rH0PUV rect.text{fill:none;stroke-width:0;}#mermaid-svg-dX79ke4RA1rH0PUV .icon-shape,#mermaid-svg-dX79ke4RA1rH0PUV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dX79ke4RA1rH0PUV .icon-shape p,#mermaid-svg-dX79ke4RA1rH0PUV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dX79ke4RA1rH0PUV .icon-shape .label rect,#mermaid-svg-dX79ke4RA1rH0PUV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dX79ke4RA1rH0PUV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dX79ke4RA1rH0PUV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dX79ke4RA1rH0PUV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
配置错误
Base URL 带 /v1 后缀
忘记 drop_params
模型名不匹配
API Key 未设置
错误: http://localhost:4000/v1
正确: http://localhost:4000
最终请求: /v1/v1/messages → 404
Anthropic 发送 thinking 参数
国产模型返回 400: unknown parameter
.env 写 qwen-max-2024
litellm 配置 qwen-max
LiteLLM 找不到模型 → 404
| Base URL 带 /v1 | 404 Not Found | 去掉 /v1,只写 http://localhost:4000 |
| 未设置 drop_params | 400 unknown parameter: thinking | 在 litellm_config.yaml 中添加 drop_params: true |
| 模型名不匹配 | 404 model not found | 确保 .env 中的模型名与 litellm_config.yaml 的 model_name 完全一致 |
| API Key 无效 | 401 Unauthorized | 检查环境变量是否正确注入,或直接在 litellm_config.yaml 中硬编码测试 |
四、多模型热切换:一套配置,自由切换
在实际开发中,不同任务适合不同模型。下面提供一套Python 自动化切换工具。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
cc-haha 模型管理器
支持一键切换通义千问 / DeepSeek / MiniMax
"""
import os
import sys
import argparse
from pathlib import Path
from dataclasses import dataclass
from typing import Optional
@dataclass
class ModelProfile:
"""模型配置档案"""
name: str
display_name: str
model_id: str
sonnet: str
haiku: str
opus: str
description: str
provider: str
# 预定义模型档案库
PROFILES: dict[str, ModelProfile] = {
# 通义千问系列
"qwen-max": ModelProfile(
name="qwen-max",
display_name="通义千问-Max",
model_id="qwen-max",
sonnet="qwen-max",
haiku="qwen-turbo",
opus="qwen-max",
description="阿里云最强模型,适合复杂代码重构和架构设计",
provider="阿里云百炼"
),
"qwen-plus": ModelProfile(
name="qwen-plus",
display_name="通义千问-Plus",
model_id="qwen-plus",
sonnet="qwen-plus",
haiku="qwen-turbo",
opus="qwen-plus",
description="均衡型模型,日常编码和 Debug 首选",
provider="阿里云百炼"
),
"qwen-turbo": ModelProfile(
name="qwen-turbo",
display_name="通义千问-Turbo",
model_id="qwen-turbo",
sonnet="qwen-turbo",
haiku="qwen-turbo",
opus="qwen-turbo",
description="极速响应,适合简单问答和文档生成",
provider="阿里云百炼"
),
# DeepSeek 系列
"deepseek-chat": ModelProfile(
name="deepseek-chat",
display_name="DeepSeek-V3",
model_id="deepseek-chat",
sonnet="deepseek-chat",
haiku="deepseek-chat",
opus="deepseek-chat",
description="数学/逻辑推理强,长上下文表现出色",
provider="DeepSeek"
),
"deepseek-coder": ModelProfile(
name="deepseek-coder",
display_name="DeepSeek-Coder",
model_id="deepseek-coder",
sonnet="deepseek-coder",
haiku="deepseek-coder",
opus="deepseek-coder",
description="专为代码优化,代码补全和审查能力强",
provider="DeepSeek"
),
# MiniMax 系列
"minimax-m2.7": ModelProfile(
name="minimax-m2.7",
display_name="MiniMax-M2.7",
model_id="minimax-m2.7",
sonnet="minimax-m2.7",
haiku="MiniMax-M2.7-highspeed",
opus="minimax-m2.7",
description="国内直连 Anthropic 协议,无需 LiteLLM 转换",
provider="MiniMax"
),
}
def read_env_file(path: Path) –> list[str]:
"""读取 .env 文件为行列表"""
if not path.exists():
print(f"❌ 文件不存在: {path}")
sys.exit(1)
return path.read_text(encoding="utf-8").splitlines()
def write_env_file(path: Path, lines: list[str]) –> None:
"""写回 .env 文件"""
path.write_text("\\n".join(lines) + "\\n", encoding="utf-8")
def switch_model(env_path: Path, profile_key: str) –> None:
"""切换模型配置"""
if profile_key not in PROFILES:
print(f"❌ 未知模型: {profile_key}")
print(f"可用选项: {', '.join(PROFILES.keys())}")
sys.exit(1)
profile = PROFILES[profile_key]
lines = read_env_file(env_path)
# 需要替换的配置项
replacements = {
"ANTHROPIC_MODEL": profile.model_id,
"ANTHROPIC_DEFAULT_SONNET_MODEL": profile.sonnet,
"ANTHROPIC_DEFAULT_HAIKU_MODEL": profile.haiku,
"ANTHROPIC_DEFAULT_OPUS_MODEL": profile.opus,
}
new_lines = []
modified = set()
for line in lines:
stripped = line.strip()
# 保留注释和空行
if not stripped or stripped.startswith("#"):
new_lines.append(line)
continue
# 尝试匹配替换
for key, value in replacements.items():
if stripped.startswith(f"{key}="):
# 保留行内注释
comment = ""
if " #" in stripped:
comment = stripped[stripped.index(" #"):]
new_lines.append(f"{key}={value}{comment}")
modified.add(key)
break
else:
new_lines.append(line)
# 追加遗漏的键
for key, value in replacements.items():
if key not in modified:
new_lines.append(f"{key}={value}")
write_env_file(env_path, new_lines)
print(f"✅ 已切换至: {profile.display_name}")
print(f" 提供商: {profile.provider}")
print(f" 描述: {profile.description}")
print(f" 模型映射: Sonnet={profile.sonnet}, Haiku={profile.haiku}, Opus={profile.opus}")
def list_models() –> None:
"""列出所有可用模型"""
print("📋 可用模型列表\\n")
print(f"{'键名':<20} {'显示名':<18} {'提供商':<12} {'描述'}")
print("-" * 80)
for key, profile in PROFILES.items():
print(f"{key:<20} {profile.display_name:<18} {profile.provider:<12} {profile.description}")
def current_model(env_path: Path) –> Optional[str]:
"""读取当前配置的模型"""
lines = read_env_file(env_path)
for line in lines:
if line.strip().startswith("ANTHROPIC_MODEL=") and not line.strip().startswith("#"):
return line.strip().split("=", 1)[1]
return None
def main() –> None:
parser = argparse.ArgumentParser(
description="cc-haha 模型切换工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
# 列出所有模型
python model_manager.py –list
# 切换到通义千问 Max
python model_manager.py –model qwen-max
# 切换到 DeepSeek
python model_manager.py –model deepseek-chat –env ~/cc-haha/.env
# 查看当前模型
python model_manager.py –current –env ~/cc-haha/.env
"""
)
parser.add_argument("–model", "-m", choices=list(PROFILES.keys()), help="目标模型")
parser.add_argument("–env", "-e", type=Path, default=Path(".env"), help=".env 文件路径")
parser.add_argument("–list", "-l", action="store_true", help="列出所有模型")
parser.add_argument("–current", "-c", action="store_true", help="查看当前模型")
args = parser.parse_args()
if args.list:
list_models()
elif args.current:
model = current_model(args.env)
print(f"当前模型: {model or '未配置'}")
elif args.model:
switch_model(args.env, args.model)
else:
parser.print_help()
if __name__ == "__main__":
main()
4.1 使用示例
# 查看所有可用模型
python model_manager.py –list
# 日常开发用 qwen-plus(均衡)
python model_manager.py –model qwen-plus
# 复杂任务用 qwen-max(最强)
python model_manager.py –model qwen-max
# 快速问答用 qwen-turbo(最快)
python model_manager.py –model qwen-turbo
# 逻辑推理任务用 DeepSeek
python model_manager.py –model deepseek-chat
五、模型选择决策树
#mermaid-svg-peqbFABSMHAsl57H{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-peqbFABSMHAsl57H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-peqbFABSMHAsl57H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-peqbFABSMHAsl57H .error-icon{fill:#552222;}#mermaid-svg-peqbFABSMHAsl57H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-peqbFABSMHAsl57H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-peqbFABSMHAsl57H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-peqbFABSMHAsl57H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-peqbFABSMHAsl57H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-peqbFABSMHAsl57H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-peqbFABSMHAsl57H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-peqbFABSMHAsl57H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-peqbFABSMHAsl57H .marker.cross{stroke:#333333;}#mermaid-svg-peqbFABSMHAsl57H svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-peqbFABSMHAsl57H p{margin:0;}#mermaid-svg-peqbFABSMHAsl57H .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-peqbFABSMHAsl57H .cluster-label text{fill:#333;}#mermaid-svg-peqbFABSMHAsl57H .cluster-label span{color:#333;}#mermaid-svg-peqbFABSMHAsl57H .cluster-label span p{background-color:transparent;}#mermaid-svg-peqbFABSMHAsl57H .label text,#mermaid-svg-peqbFABSMHAsl57H span{fill:#333;color:#333;}#mermaid-svg-peqbFABSMHAsl57H .node rect,#mermaid-svg-peqbFABSMHAsl57H .node circle,#mermaid-svg-peqbFABSMHAsl57H .node ellipse,#mermaid-svg-peqbFABSMHAsl57H .node polygon,#mermaid-svg-peqbFABSMHAsl57H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-peqbFABSMHAsl57H .rough-node .label text,#mermaid-svg-peqbFABSMHAsl57H .node .label text,#mermaid-svg-peqbFABSMHAsl57H .image-shape .label,#mermaid-svg-peqbFABSMHAsl57H .icon-shape .label{text-anchor:middle;}#mermaid-svg-peqbFABSMHAsl57H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-peqbFABSMHAsl57H .rough-node .label,#mermaid-svg-peqbFABSMHAsl57H .node .label,#mermaid-svg-peqbFABSMHAsl57H .image-shape .label,#mermaid-svg-peqbFABSMHAsl57H .icon-shape .label{text-align:center;}#mermaid-svg-peqbFABSMHAsl57H .node.clickable{cursor:pointer;}#mermaid-svg-peqbFABSMHAsl57H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-peqbFABSMHAsl57H .arrowheadPath{fill:#333333;}#mermaid-svg-peqbFABSMHAsl57H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-peqbFABSMHAsl57H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-peqbFABSMHAsl57H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-peqbFABSMHAsl57H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-peqbFABSMHAsl57H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-peqbFABSMHAsl57H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-peqbFABSMHAsl57H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-peqbFABSMHAsl57H .cluster text{fill:#333;}#mermaid-svg-peqbFABSMHAsl57H .cluster span{color:#333;}#mermaid-svg-peqbFABSMHAsl57H div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-peqbFABSMHAsl57H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-peqbFABSMHAsl57H rect.text{fill:none;stroke-width:0;}#mermaid-svg-peqbFABSMHAsl57H .icon-shape,#mermaid-svg-peqbFABSMHAsl57H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-peqbFABSMHAsl57H .icon-shape p,#mermaid-svg-peqbFABSMHAsl57H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-peqbFABSMHAsl57H .icon-shape .label rect,#mermaid-svg-peqbFABSMHAsl57H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-peqbFABSMHAsl57H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-peqbFABSMHAsl57H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-peqbFABSMHAsl57H :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
高架构设计/大规模重构
中日常编码/Debug
低快速问答/文档
是
是
否
任务类型
复杂度?
qwen-max / deepseek-chat
qwen-plus / deepseek-coder
qwen-turbo
是否需要 Anthropic 协议?
MiniMax-M2.7直连免转换
预算敏感?
qwen-turbo cheapest
qwen-max best quality
| 大型项目重构 | qwen-max | 上下文理解强,能协调多文件修改 |
| 日常 Bug 修复 | qwen-plus | 性价比最优,响应速度适中 |
| 算法/数学题 | deepseek-chat | 逻辑推理和数学能力突出 |
| 代码审查 | deepseek-coder | 专为代码优化训练 |
| 快速问答 | qwen-turbo | 延迟最低,成本低 |
| 国内直连免代理 | MiniMax-M2.7 | 原生 Anthropic 兼容接口 |
六、生产环境进阶配置
6.1 多 LiteLLM 实例负载均衡
对于团队使用场景,可以部署多个 LiteLLM 实例,通过 Nginx 做负载均衡:
upstream litellm {
server 127.0.0.1:4000;
server 127.0.0.1:4001;
server 127.0.0.1:4002;
}
server {
listen 8080;
location / {
proxy_pass http://litellm;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
6.2 请求日志与审计
LiteLLM 支持详细的请求日志,便于排查问题和审计:
litellm_settings:
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
# 或自定义 webhook
success_callback: ["custom_handler"]
6.3 速率限制与配额管理
model_list:
– model_name: qwen–max
litellm_params:
model: openai/qwen–max
api_key: os.environ/DASHSCOPE_API_KEY
api_base: https://dashscope.aliyuncs.com/compatible–mode/v1
# 每分钟最多 10 次请求
rpm: 10
# 每分钟最多 10000 tokens
tpm: 10000
七、常见问题深度解答
Q1: LiteLLM 代理报错 /v1/responses 找不到?
原因:部分国产模型只支持 /v1/chat/completions,不支持 Anthropic 的 /v1/messages。
解决:在 litellm_config.yaml 中设置:
litellm_settings:
use_chat_completions_url_for_anthropic_messages: true
Q2: 模型输出乱码或截断?
原因:编码问题或 max_tokens 设置过小。
解决:检查 .env 中的 API_TIMEOUT_MS 是否足够大(建议 3000000),并在 LiteLLM 中设置合理的 max_tokens。
Q3: 如何同时配置多个模型并动态切换?
解决:在 litellm_config.yaml 中配置多个 model_name,然后通过修改 .env 中的 ANTHROPIC_MODEL 切换。不需要重启 LiteLLM,只需要重启 cc-haha。
Q4: 本地 Ollama 模型效果不好?
原因:cc-haha 大量使用工具调用(tool_use),对小模型要求很高。
建议:使用 70B 以上的模型(如 Llama 3 70B、Qwen 72B),小模型(7B/13B)几乎无法正确处理工具调用。
总结
通过 LiteLLM 代理,cc-haha 彻底打破了"只能用 Claude"的枷锁。本文提供了:
最佳实践:建议每个开发者维护一份自己的 litellm_config.yaml,按使用频率排列模型顺序。日常开发用 qwen-plus 或 deepseek-chat,复杂任务切 qwen-max,简单问答用 qwen-turbo。
参考资料
本文配置经过实际验证,可直接复制使用。如遇问题,建议先检查 ANTHROPIC_BASE_URL 是否带 /v1 后缀——这是 90% 配置问题的根源。





