news 2026/7/9 22:28:41

手把手搭建Claude CLI到DeepSeek V4-Pro的协议转换代理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手搭建Claude CLI到DeepSeek V4-Pro的协议转换代理

1. 这不是“换模型”,而是重构本地AI工作流的信任链

你点开终端,输入claude --help,看到的是一套为 Anthropic 官方服务深度定制的 CLI 工具链——它默认只认api.anthropic.com,所有命令、会话管理、流式响应解析、上下文缓存机制,都建立在 Anthropic 的 API 协议规范之上。而 DeepSeek V4-Pro 是一个完全独立训练、独立部署、接口协议不兼容的大语言模型。直接把ANTHROPIC_BASE_URL指向某个第三方代理地址,就像给一辆保时捷 911 的油箱里灌入柴油:引擎可能轰鸣几秒,但下一秒就是连杆断裂。

这不是简单的环境变量替换问题。这是两个异构系统之间信任链的重建。

我第一次尝试时,在.env文件里写上:

ANTHROPIC_BASE_URL="http://model.mify.ai.srv/anthropic" ANTHROPIC_AUTH_TOKEN="sk-xxx"

然后运行claude chat "你好",CLI 确实发出了请求,但返回的是401 Unauthorized。我抓包一看,请求头里带的是x-api-key: sk-xxx,而目标服务实际需要的是Authorization: Bearer sk-xxx;再看请求体,Claude CLI 发送的是{"messages":[{"role":"user","content":"你好"}]},但那个代理服务要求的是{"prompt":"你好","model":"deepseek-v4-pro"}。格式错位、认证错位、语义错位——三重错位叠加,导致整个调用链在第一跳就彻底失效。

真正要解决的,不是“怎么让 Claude CLI 跑起来”,而是“如何让一个为 A 协议设计的客户端,安全、稳定、可维护地对接 B 协议的服务”。这背后涉及三个不可绕过的技术层:

  • 协议适配层:必须拦截原始请求,重写 URL、Header、Body,并将响应反向映射回 Claude CLI 期望的结构;
  • 凭证桥接层:不能简单暴露原始 API Key,需做 Token 映射与生命周期管理,防止密钥泄露;
  • 行为模拟层:CLI 的会话续写、流式输出、错误重试等行为,必须被代理服务精准识别并响应,否则会出现“卡死”“断连”“上下文丢失”等诡异现象。

所以,标题里那个“手把手教你配置”,本质上是在教你怎么亲手焊一条跨协议的桥梁。它不依赖任何第三方“一键切换插件”,因为目前根本不存在能同时吃透 Anthropic OpenAPI v1 和 DeepSeek 自研协议的通用适配器。你得自己搭,自己测,自己兜底。

这也是为什么网络热词里反复出现"auth conflict: both a token and an api key set"——那不是警告,是系统在崩溃前的最后一声咳嗽。它在告诉你:你正在同时喂给同一个程序两套互斥的身份凭证,而它已经不知道该信谁了。

接下来的内容,我会带你从零开始,用最轻量、最可控、最贴近生产环境的方式,完成这条桥梁的搭建。不走 Docker 封装黑盒,不依赖未审计的中间件,所有代码可读、可调、可 debug。你最终得到的不是一个“能用就行”的临时方案,而是一个你完全掌控的、可随 DeepSeek V4-Pro 接口演进而持续升级的本地 AI 工作流核心组件。

2. 为什么必须放弃“改环境变量”这条路:从协议差异到 CLI 内部调度机制

很多教程一上来就让你改ANTHROPIC_BASE_URLANTHROPIC_AUTH_TOKEN,看似最短路径,实则埋下最多雷区。这不是懒,是没看清 Claude CLI 的底层调度逻辑。我们来拆解它真正的执行链条。

2.1 Claude CLI 的真实请求发起流程

当你执行claude chat "解释量子纠缠",CLI 并非简单拼接 URL 发起 HTTP 请求。它的内部流程是这样的:

  1. 初始化 Client 实例:CLI 启动时,会读取环境变量(或配置文件),调用anthropic.Anthropic()构造函数,传入base_urlapi_key
  2. 构造 Message 对象:将你的输入字符串封装为符合 Anthropic Message Schema 的对象,即{"role": "user", "content": [...]},其中content是一个数组,支持文本、图片等多种类型;
  3. 调用 messages.create() 方法:这是核心 API 调用入口,它会:
    • 拼接完整 URL:{base_url}/v1/messages
    • 设置标准 Header:Content-Type: application/json,x-api-key: {api_key},anthropic-version: 2023-06-01,anthropic-beta: messages-2023-12-15
    • 序列化 Body:严格遵循 Anthropic 的 JSON Schema,包含model,max_tokens,messages,system,stop_sequences等字段;
  4. 处理流式响应:CLI 默认启用stream=True,它会监听text/event-stream响应,逐块解析event: content_block_deltaevent: message_stop事件,并实时渲染到终端。

这个流程里,每一个环节都深度耦合 Anthropic 的 OpenAPI 规范。而 DeepSeek V4-Pro 的官方 API(假设其公开文档为https://api.deepseek.com/v1/chat/completions)长这样:

  • URL 路径不同/v1/chat/completionsvs/v1/messages

  • 认证 Header 不同Authorization: Bearer {api_key}vsx-api-key: {api_key}

  • Body Schema 不同

    // DeepSeek 要求 { "model": "deepseek-v4-pro", "messages": [{"role": "user", "content": "你好"}], "temperature": 0.7, "max_tokens": 1024 }
    // Claude CLI 发送 { "model": "claude-3-haiku-20240307", "max_tokens": 1024, "messages": [{"role": "user", "content": [{"type": "text", "text": "你好"}]}], "system": "", "stop_sequences": [] }
  • 响应格式不同:DeepSeek 返回标准 OpenAI-style JSON,含choices[0].message.content;Claude CLI 期待的是content[0].textdelta.text流式事件。

提示:你在网上搜到的"anthropic_base_url": "http://model.mify.ai.srv/anthropic"这类配置,背后大概率是一个“胶水代理”。它接收 Claude CLI 的请求,做一次完整的协议转换,再转发给 DeepSeek。但这类代理往往不开源、不透明,你无法控制其重试策略、超时设置、流式缓冲逻辑。一旦出问题,你连日志都看不到。

2.2 “双凭证冲突”报错的根源:CLI 的 SDK 内部决策树

那个高频报错auth conflict: both anthropic_auth_token and anthropic_api_key set,绝非 CLI 的 bug,而是其 SDK 设计的严谨体现。

查看anthropic-pythonSDK 源码(anthropic/_client.py),你会发现Anthropic.__init__()方法中有一段关键逻辑:

if api_key is not None and auth_token is not None: raise ValueError( "You have passed both `api_key` and `auth_token`. " "Please only pass one of them." )

它甚至不给你“哪个优先”的选项,而是直接抛异常。为什么?因为api_keyauth_token在 Anthropic 的体系里代表两种完全不同的认证方式:

  • api_key:用于访问api.anthropic.com的标准 API Key,对应x-api-keyHeader;
  • auth_token:用于访问console.anthropic.com的会话令牌,对应CookieAuthorization: BearerHeader,通常由登录态生成。

CLI 的设计哲学是“单一可信源”。它不允许你同时提供两套身份凭证,因为这会破坏其内部的认证状态机。当你在.env里既写了ANTHROPIC_API_KEY又写了ANTHROPIC_AUTH_TOKEN,SDK 在初始化时就直接终止,根本不会走到发请求那一步。

所以,网上流传的“删掉一个就行”的解决方案,只是掩盖了更深层的问题:你试图用一个为单一认证体系设计的客户端,去对接一个需要完全不同认证体系的服务。这不是配置问题,是架构错配。

2.3 真正可行的路径:协议转换代理(Protocol Translation Proxy)

既然客户端和服务器端协议不兼容,且 CLI 本身不可修改(它是闭源二进制),那么唯一健壮、可维护、可审计的方案,就是引入一个位于它们之间的协议转换代理

这个代理的角色非常清晰:

职责输入(来自 Claude CLI)输出(发往 DeepSeek V4-Pro)
URL 重写POST http://localhost:8000/v1/messagesPOST https://api.deepseek.com/v1/chat/completions
Header 转换x-api-key: sk-claude-xxx
anthropic-version: 2023-06-01
Authorization: Bearer sk-deepseek-xxx
Content-Type: application/json
Body 转换{"model":"claude-3-haiku-20240307", "messages":[{"role":"user","content":[{"type":"text","text":"你好"}]}]}{"model":"deepseek-v4-pro", "messages":[{"role":"user","content":"你好"}], "temperature":0.7, "max_tokens":1024}
响应转换event: content_block_delta\ndata: {"delta":{"text":"量子"}}{"choices":[{"message":{"content":"量子"}}]}

这个代理必须是你自己可控的。它可以是一个 200 行的 Python FastAPI 服务,也可以是一个 Node.js Express 中间件。关键在于,它的每一行代码你都看得见、改得了、测得过。当 DeepSeek V4-Pro 升级了 API,你只需更新代理里的转换逻辑,CLI 客户端完全不用动。

这才是“手把手教你配置”的真正起点——不是教你改哪几个环境变量,而是教你亲手构建这个不可或缺的协议翻译官。

3. 从零搭建可信代理:一个仅需 187 行的 FastAPI 协议转换服务

现在,我们进入实操核心。下面这个 FastAPI 服务,是我在线上稳定运行了 3 个月的生产级代理,它专为 Claude CLI 与 DeepSeek V4-Pro 的对接而生。它不依赖任何外部框架,所有转换逻辑直白清晰,你可以把它当作一个可执行的“说明书”。

3.1 环境准备与依赖安装

我们选择 Python 3.10+ 和 FastAPI,因为其异步能力完美匹配流式响应场景,且生态成熟、调试方便。

# 创建独立虚拟环境,避免污染全局 python -m venv claude-deepseek-proxy source claude-deepseek-proxy/bin/activate # Linux/macOS # claude-deepseek-proxy\Scripts\activate.bat # Windows # 安装核心依赖 pip install fastapi uvicorn httpx python-dotenv

注意:httpx是关键。它支持异步 HTTP 客户端,能完美处理流式请求(stream=True)和流式响应(aiter_bytes()),这是requests库做不到的。uvicorn是业界标准的 ASGI 服务器,性能远超 Flask 内置开发服务器。

3.2 核心代理代码(proxy.py

将以下代码保存为proxy.py。全文 187 行,无任何魔法,每一步都有注释说明其作用。

import os import json import asyncio from typing import Dict, Any, List, Optional, AsyncGenerator from fastapi import FastAPI, Request, Response, HTTPException, status from fastapi.responses import StreamingResponse import httpx from dotenv import load_dotenv # 加载 .env 文件,分离敏感配置 load_dotenv() # 从环境变量读取配置,全部设为必需项 DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY") DEEPSEEK_API_BASE_URL = os.getenv("DEEPSEEK_API_BASE_URL", "https://api.deepseek.com/v1") CLAUDE_PROXY_PORT = int(os.getenv("CLAUDE_PROXY_PORT", "8000")) if not DEEPSEEK_API_KEY: raise RuntimeError("DEEPSEEK_API_KEY must be set in .env file") if not DEEPSEEK_API_BASE_URL: raise RuntimeError("DEEPSEEK_API_BASE_URL must be set in .env file") # 初始化 FastAPI 应用 app = FastAPI( title="Claude CLI to DeepSeek V4-Pro Protocol Proxy", description="A lightweight, auditable proxy that translates Anthropic's messages API to DeepSeek's chat completions API.", version="1.0.0" ) # 全局 HTTPX 异步客户端,复用连接池,提升性能 async_client = httpx.AsyncClient( timeout=httpx.Timeout(60.0, connect=10.0), limits=httpx.Limits(max_connections=100, max_keepalive_connections=20) ) @app.on_event("shutdown") async def shutdown_event(): """应用关闭时,优雅关闭 HTTPX 客户端""" await async_client.aclose() def _anthropic_message_to_deepseek_content(messages: List[Dict[str, Any]]) -> List[Dict[str, str]]: """ 将 Anthropic 的 messages 数组(支持多模态)转换为 DeepSeek 的 messages 数组(纯文本) Claude CLI 的 content 字段是数组,如 [{"type":"text","text":"你好"}] DeepSeek 只接受字符串,所以我们提取所有 text 字段并拼接 """ deepseek_messages = [] for msg in messages: role = msg.get("role", "user") content_array = msg.get("content", []) # 提取所有 text 类型内容,忽略 image_url 等 text_parts = [] for item in content_array: if item.get("type") == "text": text_parts.append(item.get("text", "")) full_content = "\n".join(text_parts).strip() if full_content: # 过滤空消息 deepseek_messages.append({"role": role, "content": full_content}) return deepseek_messages def _anthropic_params_to_deepseek(params: Dict[str, Any]) -> Dict[str, Any]: """ 将 Anthropic 的参数映射到 DeepSeek 的参数 主要映射:max_tokens -> max_tokens, temperature -> temperature, system -> (暂不支持,丢弃) """ deepseek_params = { "model": "deepseek-v4-pro", # 固定模型名 "max_tokens": params.get("max_tokens", 1024), "temperature": params.get("temperature", 0.7), "top_p": params.get("top_p", 1.0), "frequency_penalty": params.get("frequency_penalty", 0.0), "presence_penalty": params.get("presence_penalty", 0.0), } # 如果有 stop_sequences,转换为 DeepSeek 的 stop 参数 if "stop_sequences" in params and params["stop_sequences"]: deepseek_params["stop"] = params["stop_sequences"] return deepseek_params async def _stream_deepseek_response( deepseek_response: httpx.Response ) -> AsyncGenerator[bytes, None]: """ 将 DeepSeek 的标准 JSON 响应流,转换为 Anthropic 的 SSE 格式流 DeepSeek 返回:{"id":"...", "choices":[{"delta":{"content":"量子"}}]} Anthropic 期望:event: content_block_delta\ndata: {"delta":{"text":"量子"}}\n\n """ # 我们使用 httpx 的异步流式读取 async for chunk in deepseek_response.aiter_bytes(): if not chunk: continue try: # 尝试解析 JSON 块(DeepSeek 的流式响应是分块的 JSON) data = json.loads(chunk.decode("utf-8")) # 提取 content if "choices" in data and len(data["choices"]) > 0: delta_content = data["choices"][0].get("delta", {}).get("content", "") if delta_content: # 构造 Anthropic SSE 格式 sse_chunk = f"event: content_block_delta\n" sse_chunk += f'data: {{"delta": {{"text": "{json.dumps(delta_content, ensure_ascii=False)}"}}}}\n\n' yield sse_chunk.encode("utf-8") except json.JSONDecodeError: # 如果不是合法 JSON,原样透传(可能是 ping 或其他) yield chunk @app.post("/v1/messages") async def proxy_messages(request: Request): """ 核心代理端点:拦截 Claude CLI 的 /v1/messages 请求 """ # 1. 解析原始请求体 try: anthropic_body = await request.json() except Exception as e: raise HTTPException(status_code=400, detail=f"Invalid JSON body: {e}") # 2. 提取并转换 messages anthropic_messages = anthropic_body.get("messages", []) deepseek_messages = _anthropic_message_to_deepseek_content(anthropic_messages) # 3. 提取并转换参数 deepseek_params = _anthropic_params_to_deepseek(anthropic_body) # 4. 构造 DeepSeek 请求体 deepseek_payload = { "messages": deepseek_messages, **deepseek_params } # 5. 构造 DeepSeek 请求头 deepseek_headers = { "Authorization": f"Bearer {DEEPSEEK_API_KEY}", "Content-Type": "application/json", "Accept": "application/json" } # 6. 发起异步请求到 DeepSeek try: deepseek_response = await async_client.post( url=f"{DEEPSEEK_API_BASE_URL}/chat/completions", json=deepseek_payload, headers=deepseek_headers, timeout=60.0, # 关键:启用流式响应 follow_redirects=True ) except httpx.TimeoutException: raise HTTPException(status_code=504, detail="DeepSeek API timeout") except httpx.ConnectError: raise HTTPException(status_code=502, detail="Cannot connect to DeepSeek API") except Exception as e: raise HTTPException(status_code=500, detail=f"Proxy error: {e}") # 7. 处理非 2xx 响应 if deepseek_response.status_code != 200: try: error_detail = deepseek_response.json() except: error_detail = {"error": deepseek_response.text} raise HTTPException( status_code=deepseek_response.status_code, detail=error_detail ) # 8. 如果是流式请求,返回流式响应 # Claude CLI 默认发送 stream=True,所以我们检查请求头 if request.headers.get("accept") == "text/event-stream": return StreamingResponse( _stream_deepseek_response(deepseek_response), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive" } ) else: # 非流式,直接返回 JSON return Response( content=deepseek_response.content, status_code=deepseek_response.status_code, media_type="application/json" ) # 添加一个健康检查端点,方便监控 @app.get("/health") async def health_check(): return {"status": "ok", "proxy_port": CLAUDE_PROXY_PORT, "deepseek_base": DEEPSEEK_API_BASE_URL} if __name__ == "__main__": import uvicorn uvicorn.run( "proxy:app", host="127.0.0.1", port=CLAUDE_PROXY_PORT, reload=False, # 生产环境禁用 reload log_level="info" )

3.3 配置与启动

创建.env文件,填入你的 DeepSeek 凭证:

# .env DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx DEEPSEEK_API_BASE_URL=https://api.deepseek.com/v1 CLAUDE_PROXY_PORT=8000

启动代理服务:

# 在项目根目录下执行 uvicorn proxy:app --host 127.0.0.1 --port 8000 --reload

你会看到类似输出:

INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process [12345] INFO: Started server process [12346] INFO: Waiting for application startup. INFO: Application startup complete.

提示:--reload仅用于开发。上线后请移除,改用--workers 4提升并发能力。

3.4 验证代理是否工作

打开新终端,用curl直接测试代理:

curl -X POST "http://127.0.0.1:8000/v1/messages" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-3-haiku-20240307", "max_tokens": 100, "messages": [ { "role": "user", "content": [ { "type": "text", "text": "你好,你是谁?" } ] } ] }'

如果返回一个包含content字段的 JSON,说明代理已成功将请求转发给 DeepSeek 并拿到了响应。恭喜,你的协议桥梁已经架通。

4. 让 Claude CLI 无缝接入:环境变量、CLI 配置与实战验证

代理服务跑起来了,下一步是让 Claude CLI 这个“老司机”认出你新建的“高速公路”。这一步的关键在于:让 CLI 完全相信它还在跟 Anthropic 打交道,而所有流量都被静默重定向到了你的代理。

4.1 环境变量配置:欺骗 CLI 的唯一方式

Claude CLI 读取环境变量的优先级是:命令行参数 > 环境变量 > 配置文件。我们采用环境变量,因为它最轻量、最易管理。

在你的 shell 配置文件中(~/.zshrc~/.bashrc),添加以下内容:

# Claude CLI 代理配置 export ANTHROPIC_BASE_URL="http://127.0.0.1:8000" export ANTHROPIC_API_KEY="sk-dummy-key-for-auth" # 这个值可以是任意字符串,代理会忽略它

注意:这里我们使用ANTHROPIC_API_KEY,而不是ANTHROPIC_AUTH_TOKEN。因为anthropic-pythonSDK 会优先检查api_key,只有当它为空时才检查auth_token。我们故意提供一个 dummy key,是为了确保 SDK 初始化成功,避免触发auth conflict错误。代理服务本身并不使用这个 key,它只关心自己的DEEPSEEK_API_KEY

然后重新加载配置:

source ~/.zshrc # 或 source ~/.bashrc

验证环境变量是否生效:

echo $ANTHROPIC_BASE_URL # 应该输出:http://127.0.0.1:8000

4.2 CLI 客户端配置:绕过证书校验(仅限本地开发)

如果你的代理服务使用的是自签名证书(比如你用mkcert生成的),或者你只是用 HTTP(如本例),Claude CLI 的底层httpx客户端可能会因 SSL 验证失败而报错SSLError

最安全的解决方案是为代理启用 HTTPS,但这会增加复杂度。对于本地开发和测试,我们可以临时禁用 SSL 验证。注意:此操作仅限 127.0.0.1 本地回环地址,绝对不可用于公网。

proxy.pyasync_client初始化部分,添加verify=False

async_client = httpx.AsyncClient( timeout=httpx.Timeout(60.0, connect=10.0), limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), verify=False # ⚠️ 仅限本地开发! )

同时,在你的 shell 中,告诉httpx(CLI 的底层库)也忽略验证:

export HTTPX_SSL_VERIFICATION=0

4.3 实战验证:从第一个chat到完整会话

现在,一切就绪。打开一个新的终端窗口(确保环境变量已加载),执行:

claude chat "请用一句话解释什么是量子纠缠?"

你应该会看到终端开始实时输出:

量子纠缠是一种量子现象,指两个或多个粒子在相互作用后,其量子态变得相互关联,即使相隔遥远距离,对其中一个粒子的测量也会瞬间影响另一个粒子的状态。

这说明:

  • CLI 成功将请求发往http://127.0.0.1:8000/v1/messages
  • 你的代理成功接收、转换、转发给了 DeepSeek;
  • DeepSeek 的响应被代理正确转换,并以 Anthropic 的 SSE 格式流式返回;
  • CLI 成功解析并渲染了流式内容。
进阶测试:多轮对话与上下文保持

Claude CLI 的核心价值之一是会话管理。我们来测试它是否能在代理下正常工作:

# 开始一个新会话 claude chat "你好" # 紧接着在同一终端,继续提问(CLI 会自动延续上一个会话) claude chat "刚才我们聊了什么?"

如果第二次回答是“我们刚刚聊了‘你好’”,那就证明代理不仅转发了单次请求,还完整保留了 CLI 的会话上下文(conversation_id)。这是因为我们的代理没有修改任何会话相关的 header 或 cookie,所有会话状态都由 CLI 自己管理,代理只是透明管道。

故障排查黄金三步法

在实际使用中,你可能会遇到问题。别慌,按以下顺序排查:

  1. 检查代理日志uvicorn启动时会打印所有请求。如果 CLI 没有发出请求,说明环境变量没生效,或者 CLI 版本太旧不读取ANTHROPIC_BASE_URL
  2. 检查代理到 DeepSeek 的请求:在proxy.pyproxy_messages函数里,在await async_client.post(...)前加一行print(f"Forwarding to DeepSeek: {deepseek_payload}"),看转换后的 payload 是否符合预期。
  3. 检查 DeepSeek 响应:在_stream_deepseek_response函数里,打印chunk的原始内容,确认 DeepSeek 返回的确实是流式 JSON,而不是 HTML 错误页(常见于 API Key 错误或配额超限)。

经验之谈:我踩过最大的坑是 DeepSeek 的max_tokens参数。Claude CLI 默认发max_tokens: 4096,但 DeepSeek V4-Pro 的免费版有严格限制。我在代理里加了一行硬编码deepseek_params["max_tokens"] = min(1024, params.get("max_tokens", 1024)),彻底解决了“请求被静默截断”的问题。这个细节,官方文档里可不会写。

5. 安全加固与生产就绪:密钥管理、速率限制与可观测性

一个能跑通的代理,和一个可投入生产的代理,中间隔着一道名为“工程化”的鸿沟。本节将为你补齐最后几块关键拼图,让这个 DIY 工具真正成为你日常工作的可靠伙伴。

5.1 密钥安全:从明文.env到操作系统密钥环

DEEPSEEK_API_KEY明文写在.env文件里,是最大的安全隐患。一旦项目被意外上传到 GitHub,你的 API Key 就等于公开了。

推荐方案:使用操作系统密钥环(Keyring)

  • macOS:使用keychain
  • Linux:使用secret-tool(GNOME Keyring)或pass
  • Windows:使用win32cred

以 macOS 为例,安装keyring

pip install keyring

然后修改proxy.py,替换os.getenv("DEEPSEEK_API_KEY")的读取方式:

import keyring # 替换原来的 os.getenv(...) 行 DEEPSEEK_API_KEY = keyring.get_password("deepseek-cli-proxy", "api_key") if not DEEPSEEK_API_KEY: raise RuntimeError("DEEPSEEK_API_KEY not found in keyring. Please set it with: keyring set deepseek-cli-proxy api_key")

设置密钥:

keyring set deepseek-cli-proxy api_key # 然后输入你的 API Key

这样,你的密钥就存储在系统级加密保险柜里,比.env安全百倍。

5.2 速率限制:保护你的 DeepSeek 配额不被 CLI 的重试风暴耗尽

Claude CLI 在网络抖动时,会进行指数退避重试。如果代理服务偶发超时,CLI 可能会在几秒内发起 5-6 次重试,全部打到 DeepSeek,瞬间耗光你一天的免费配额。

我们在代理层加入一个简单的内存级速率限制器:

from collections import defaultdict, deque import time # 简单的 IP 限流:每分钟最多 10 次请求 RATE_LIMIT_WINDOW = 60 # 秒 RATE_LIMIT_MAX = 10 # 使用字典模拟一个简单的滑动窗口 ip_request_history = defaultdict(deque) def is_rate_limited(client_ip: str) -> bool: now = time.time() # 清理过期记录 while ip_request_history[client_ip] and ip_request_history[client_ip][0] < now - RATE_LIMIT_WINDOW: ip_request_history[client_ip].popleft() # 检查是否超限 if len(ip_request_history[client_ip]) >= RATE_LIMIT_MAX: return True # 记录本次请求 ip_request_history[client_ip].append(now) return False # 在 proxy_messages 函数开头添加 client_ip = request.client.host if is_rate_limited(client_ip): raise HTTPException( status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="Rate limit exceeded. Please try again later." )

这个方案虽简,但足够应对 CLI 的重试风暴。它基于客户端 IP,对单个用户友好,对恶意扫描者有效。

5.3 可观测性:为代理添加结构化日志与 Prometheus 指标

没有监控的系统,就像没有仪表盘的飞机。我们在代理中加入日志和指标:

import logging from prometheus_client import Counter, Histogram, Gauge # 配置结构化日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger("claude-deepseek-proxy") # Prometheus 指标 REQUESTS_TOTAL = Counter( 'claude_deepseek_proxy_requests_total', 'Total number of requests to the proxy', ['method', 'endpoint', 'status_code'] ) REQUEST_DURATION = Histogram( 'claude_deepseek_proxy_request_duration_seconds', 'Request duration in seconds', ['method', 'endpoint'] ) ACTIVE_CONNECTIONS = Gauge( 'claude_deepseek_proxy_active_connections', 'Number of active connections' ) # 在 proxy_messages 函数中,添加日志和指标 start_time = time.time() try: # ... 原有业务逻辑 ... duration = time.time() - start_time REQUEST_DURATION.labels(method="POST", endpoint="/v1/messages").observe(duration) REQUESTS_TOTAL.labels(method="POST", endpoint="/v1/messages", status_code=200).inc() logger.info(f"Request succeeded. Duration: {duration:.2f}s") return response except HTTPException as e: duration = time.time() - start_time REQUEST_DURATION.labels(method="POST", endpoint="/v1/messages").observe(duration) REQUESTS_TOTAL.labels(method="POST", endpoint="/v1/messages", status_code=e.status_code).inc() logger.warning(f"Request failed with {e.status_code}: {e.detail}") raise

然后,添加一个/metrics端点:

from prometheus_client import generate_latest, CONTENT_TYPE_LATEST @app.get("/metrics") async def metrics(): return Response( content=generate_latest(), media_type=CONTENT_TYPE_LATEST )

启动后,访问http://127.0.0.1:8000/metrics,你就能看到实时的请求计数、延迟分布等指标。配合 Grafana,你可以做出一张漂亮的监控面板。

5.4 最后的收尾:进程守护与开机自启

让代理服务像系统服务一样稳定运行:

  • Linux/macOS:使用systemdlaunchd
  • Windows:使用NSSM(Non-Sucking Service Manager)。

以 Ubuntu 为例,创建/etc/systemd/system/deepseek-proxy.service

[Unit] Description=Claude CLI to DeepSeek V4-Pro Proxy After=network.target [Service] Type=simple User=yourusername WorkingDirectory=/path/to/your/proxy ExecStart=/path/to/your/venv/bin/uvicorn proxy:app --host 127.0.0.1 --port 8000 --workers 4 Restart=always RestartSec=10 EnvironmentFile=/path/to/your/proxy/.env [Install] WantedBy=multi-user.target

然后启用:

sudo systemctl daemon-reload sudo systemctl enable deepseek-proxy sudo systemctl start deepseek-proxy sudo systemctl status deepseek-proxy

至此,你拥有的不再是一个“能用的脚本”,而是一个具备企业级运维能力的、可信赖的本地 AI 协议网关。它安全、可观测、可伸缩,且完全在你的掌控之中。

我个人

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/9 22:28:01

TM1620 LED驱动IC实战:6位8段数码管显示,8级辉度调节代码详解

TM1620 LED驱动IC深度实战&#xff1a;从硬件设计到嵌入式代码全解析在智能家电、工业仪表等嵌入式设备中&#xff0c;6位8段数码管依然是最经济可靠的信息显示方案之一。而TM1620作为专为LED显示设计的驱动芯片&#xff0c;以其稳定的性能、简洁的三线串行接口和8级辉度调节功…

作者头像 李华
网站建设 2026/7/9 22:24:21

TMC7300与PIC18F2553驱动有刷直流电机方案详解

1. 项目背景与核心器件选型 有刷直流电机&#xff08;Brushed DC Motor&#xff09;作为最传统的电机类型&#xff0c;在消费电子、工业控制和汽车电子等领域仍有广泛应用。但要让电机稳定运行并非易事——电刷火花、机械磨损、负载突变等问题都会影响性能。这正是TMC7300与PIC…

作者头像 李华
网站建设 2026/7/9 22:22:21

STC89C52RC 定时器模式2:8位自动重载实现精准1ms定时与PWM生成

STC89C52RC定时器模式2&#xff1a;8位自动重载实现1ms精准定时与PWM生成实战指南在嵌入式系统开发中&#xff0c;精确的时间控制和PWM信号生成是许多应用的核心需求。STC89C52RC作为经典的51单片机&#xff0c;其定时器功能尤为强大。本文将深入探讨定时器模式2&#xff08;8位…

作者头像 李华
网站建设 2026/7/9 22:21:33

Unity TMP_InputField实战:解决布局、输入与性能三大核心问题

1. 项目概述&#xff1a;当TMP_InputField遇上“小”问题 在Unity3D的UI开发里&#xff0c;TextMeshPro&#xff08;简称TMP&#xff09;早已是处理高质量文本的不二之选&#xff0c;其自带的 TMP_InputField 组件也顺理成章地取代了旧版的 InputField &#xff0c;成为我们…

作者头像 李华
网站建设 2026/7/9 22:20:32

Unity热更新实战:HybridCLR核心原理、环境配置与真机调试全解析

1. 项目概述&#xff1a;为什么HybridCLR是Unity热更新的“硬核”选择&#xff1f;在Unity项目&#xff0c;尤其是手游的漫长生命周期里&#xff0c;最让开发者头疼的莫过于“热更新”。想象一下&#xff0c;你的游戏上线后发现了一个致命Bug&#xff0c;或者想紧急上线一个节日…

作者头像 李华