将 Anthropic Messages API 格式的请求转换为 OpenAI Chat Completions 格式,转发给 Kimi API,使 Claude Code CLI 能够使用 Kimi 大模型。
Claude Code CLI Kimi API
│ │
│ POST /v1/messages │
│ (Anthropic 格式) │
▼ │
┌──────────┐ ┌───────────┐ ┌─────────────┐
│ server │──▶│ converter │──▶│ client │──▶ POST /chat/completions
│ │◀──│ │◀──│ │◀── (OpenAI 格式)
└──────────┘ └───────────┘ └─────────────┘
- server — HTTP 服务端,接收 Anthropic 格式请求,路由
/v1/messages和/v1/models - converter — 双向格式转换引擎,支持请求转换、响应转换、SSE 流式转换状态机
- client — Kimi API 的 HTTP 客户端,支持流式和非流式调用
- config — 纯环境变量配置,零配置文件
零外部依赖,仅使用 Go 标准库。
go build -o cc-proxy ./cmd/proxy/export MOONSHOT_API_KEY=sk-xxxxx
./cc-proxy代理默认监听 http://localhost:3699,启动日志:
cc-proxy listening on :3699
Kimi API: https://api.moonshot.cn/v1
Model map: map[kimi-k2.7-code:kimi-k2.7-code kimi-k3:kimi-k3]
在 ~/.claude/settings.json 中修改 env 部分:
{
"env": {
"ANTHROPIC_BASE_URL": "http://localhost:3699",
"ANTHROPIC_AUTH_TOKEN": "any-value-not-empty",
"ANTHROPIC_MODEL": "kimi-k3",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k3",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "kimi-k3",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "kimi-k2.7-code",
"CLAUDE_CODE_SUBAGENT_MODEL": "kimi-k2.7-code"
}
}说明:
ANTHROPIC_AUTH_TOKEN填任意非空值即可(代理不校验),ANTHROPIC_MODEL等模型名会被 MODEL_MAP 映射到对应的 Kimi 模型。
| Anthropic 字段 | OpenAI 字段 | 转换方式 |
|---|---|---|
model |
model |
查 MODEL_MAP 映射,未匹配则原样透传 |
system (string/array) |
messages[0] (role: system) |
提取文本,插入消息数组最前面 |
messages[].content (string) |
messages[].content (string) |
直接映射 |
messages[].content (blocks) |
多个 OpenAI 消息 | 按 block type 拆分(见下方) |
max_tokens |
max_tokens |
直接映射 |
temperature |
temperature |
直接映射 |
top_p |
top_p |
直接映射 |
top_k |
— | OpenAI 不支持,丢弃 |
stop_sequences |
stop |
直接映射 |
stream |
stream |
直接映射 |
tools[] |
tools[] |
包装 {type:"function", function:{...}} |
Content Block 转换规则:
| Anthropic Block | OpenAI 表示 |
|---|---|
{type:"text", text:"..."} |
合并到 content 字符串 |
{type:"tool_use", id, name, input} |
role:"assistant" + tool_calls[{id, function:{name, arguments}}] |
{type:"tool_result", tool_use_id, content} |
role:"tool" + tool_call_id + content |
{type:"thinking", thinking:"..."} |
包裹为 [思考]...[\/思考] 合并到 content |
| OpenAI 字段 | Anthropic 字段 |
|---|---|
choices[0].message.content |
content: [{type:"text", text:"..."}] |
choices[0].message.tool_calls[] |
content: [{type:"tool_use", id, name, input}] |
choices[0].message.reasoning_content |
content: [{type:"thinking", thinking:"..."}] |
choices[0].finish_reason |
stop_reason(映射规则见下方) |
usage |
usage(字段名直接对应) |
finish_reason → stop_reason 映射:
| OpenAI | Anthropic |
|---|---|
stop |
end_turn |
tool_calls |
tool_use |
length |
max_tokens |
content_filter |
end_turn |
| 未知值 | 原样透传 |
OpenAI 的 SSE 流式事件被 StreamConverter 状态机逐 chunk 转换为 Anthropic 的 SSE 事件序列:
message_start → content_block_start(text) → content_block_delta(text_delta)*
→ content_block_stop → ... (轮换 thinking/tool_use block)
→ message_delta(含 stop_reason)→ message_stop
支持的 delta 类型:
content→text_deltareasoning_content→thinking_deltatool_calls[]→input_json_delta(累积strings.Builder拼接 JSON 片段)
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
MOONSHOT_API_KEY |
是 | — | Kimi API 密钥,启动时校验 |
PROXY_PORT |
否 | 3699 |
代理监听端口(1-65535,无效值回退默认) |
KIMI_BASE_URL |
否 | https://api.moonshot.cn/v1 |
Kimi API 地址(末尾 / 自动去除) |
MODEL_MAP |
否 | kimi-k3:kimi-k3,kimi-k2.7-code:kimi-k2.7-code |
模型映射,格式 src:dst,...(逗号分隔多个映射对,冒号分隔源与目标) |
MODEL_MAP 示例:
MODEL_MAP=kimi-k3:kimi-k3,kimi-k2.7-code:kimi-k2.7-code,claude-sonnet-4-20250514:kimi-k3
未匹配的模型名会原样透传给 Kimi API,不做修改。
Anthropic Messages API 兼容端点,支持 stream: true/false。
非流式请求示例:
curl -s http://localhost:3699/v1/messages \
-H 'Content-Type: application/json' \
-d '{
"model": "kimi-k3",
"max_tokens": 100,
"messages": [{"role": "user", "content": "你好"}]
}' | python3 -m json.tool流式请求示例:
curl -sN http://localhost:3699/v1/messages \
-H 'Content-Type: application/json' \
-d '{
"model": "kimi-k3",
"max_tokens": 100,
"stream": true,
"messages": [{"role": "user", "content": "你好"}]
}'返回 MODEL_MAP 中所有目标模型的去重列表:
{
"data": [
{"id": "kimi-k3", "type": "model"},
{"id": "kimi-k2.7-code", "type": "model"}
],
"has_more": false
}| 场景 | HTTP 状态码 | 响应格式 |
|---|---|---|
MOONSHOT_API_KEY 未设置 |
启动时 fatal | Config error: MOONSHOT_API_KEY environment variable is required |
| 请求体 JSON 解析失败 | 400 | Anthropic 格式 {"type":"error","error":{"type":"api_error","message":"..."}} |
非 POST 方法访问 /v1/messages |
405 | 同上 |
| Kimi API 返回错误 | 透传 Kimi 状态码 | 转换为 Anthropic 格式 error 响应 |
| 流式传输中断 | — | 记录日志,结束 SSE 流 |
| 未匹配路由 | 404 | Anthropic 格式 error 响应 |
cc-proxy/
├── cmd/proxy/main.go # 入口点:config.Load() → server.New() → srv.Start()
├── internal/
│ ├── config/config.go # 环境变量解析与校验
│ ├── converter/
│ │ ├── types.go # Anthropic + OpenAI 双向数据类型(26 个 struct)
│ │ ├── request.go # Anthropic → OpenAI 请求转换
│ │ ├── response.go # OpenAI → Anthropic 响应转换 + /v1/models
│ │ ├── stream.go # SSE 流式状态机(StreamConverter)
│ │ └── converter_test.go # 12 个单元测试
│ ├── client/client.go # Kimi API HTTP 客户端(流式 + 非流式)
│ └── server/server.go # HTTP 服务端(路由、处理器、错误响应)
├── scripts/smoke.sh # 端到端冒烟测试
├── go.mod
├── go.sum
└── README.md
# 运行所有单元测试(12 个用例,覆盖请求/响应/内容解析)
go test ./... -v
# 静态检查
go vet ./...
# 端到端冒烟测试(需要有效 MOONSHOT_API_KEY)
export MOONSHOT_API_KEY=sk-xxxxx
bash scripts/smoke.sh冒烟测试会:
- 编译二进制
- 启动代理
- 验证
/v1/models返回模型列表 - 发送非流式
/v1/messages请求并验证 Anthropic 格式响应 - 发送流式请求并验证收到 SSE 事件
- 自动清理
- 零外部依赖 — 仅使用 Go 标准库(
net/http,encoding/json,bufio等) - Go 1.26+ — 使用模块模式
cc-proxy - 多态 JSON 处理 —
AnthropicMessage.Content和AnthropicRequest.System字段可能是 string 或 array,使用json.RawMessage+ 两级尝试反序列化 - 流式安全 —
StreamConverter内建状态机追踪 content block 生命周期,client.ChatCompletionStream通过 buffered channel(容量 100)和 context 取消防止 goroutine 泄漏 - 模型名映射 —
kimi-k3支持 128K 上下文,kimi-k2.7-code为代码场景优化