hermes使用OpenCode Go订阅模型慢?本地curl_cffi代理一招搞定
适用场景:Hermes(或其他 Python OpenAI SDK 客户端)调用 OpenCode Go 订阅的模型(deepseek-v4-flash、grok-4.5、kimi-k3 等)响应极慢(120 秒+),而 DeepSeek 官方 API、Claude CLI 却很快。
一、问题现象
在 Hermes 中配置 OpenCode Go 订阅后,调用模型:
- 每次 API 调用固定120~130 秒
- 同一台服务器上:
- DeepSeek 官方 API:约 2 秒 ✅
- Claude CLI 调 OpenCode:很快 ✅
curl直接请求 OpenCode API:TTFB 0.4 秒 ✅- Python httpx 请求:20~200 秒 ❌
结论:模型本身不慢,是"客户端身份"的问题。
二、排查证据(为什么是 Cloudflare 的锅)
| 测试 | TTFB |
|---|---|
| curl(libcurl TLS 指纹) | 0.2~0.4s |
| Python httpx / requests(OpenSSL 指纹) | 1.5~200s(随频率累积) |
| curl_cffi 模拟 Chrome 指纹 | 1.3~1.7s |
响应头确认:
server: cloudflare cf-ray: a27b8672ce4f75b2-SEAopencode.ai 在 Cloudflare 后面。Cloudflare 对 Python/OpenSSL 的 TLS 指纹执行了延迟处理(类似 bot 检测),而 curl / 浏览器指纹直接放行。这就是为什么:
- DeepSeek 官方 API(不走 Cloudflare)快
- Claude CLI(原生二进制,TLS 栈不同)快
- Hermes(Python httpx)慢
三、解决方案:本地 curl_cffi 代理
思路:让 Hermes 的请求先到本地代理,代理用curl_cffi 模拟 Chrome 浏览器 TLS 指纹转发到 opencode.ai,绕过 Cloudflare 的指纹识别。
Hermes ──> 127.0.0.1:18080 (curl_cffi 代理, Chrome 指纹) ──> opencode.ai只影响 opencode-go 这一个 provider,其他 provider(DeepSeek、mimo 等)完全直连不受影响。
效果
| 状态 | 延迟 |
|---|---|
| 修复前(直连) | 120~130s |
| 修复后(走代理) | 2~7s |
四、实施步骤
1. 安装 curl_cffi
假设 Hermes 装在~/hermes-agent,使用 venv:
cd~/hermes-agent venv/bin/python-mpipinstallcurl_cffi验证:
venv/bin/python-c"import curl_cffi; print(curl_cffi.__version__)"2. 创建代理脚本
sudomkdir-p/opt/opencode-proxysudochown$USER:$USER/opt/opencode-proxy创建/opt/opencode-proxy/proxy.py:
#!/usr/bin/env python3"""opencode.ai 本地代理:curl_cffi 模拟 Chrome TLS 指纹转发请求"""importhttp.serverimportsocketserverimportsysfromcurl_cffiimportrequests# 注意:不要带 /v1,Hermes 请求路径自带 /v1UPSTREAM="https://opencode.ai/zen/go"IMPERSONATE="chrome"# 模拟 Chrome 浏览器 TLS 指纹LISTEN_HOST="127.0.0.1"LISTEN_PORT=18080classHandler(http.server.BaseHTTPRequestHandler):# HTTP/1.0:连接结束以 EOF 表示 SSE 流结束protocol_version="HTTP/1.0"deflog_message(self,fmt,*args):sys.stderr.write("[proxy] %s\n"%(fmt%args))def_forward(self):length=int(self.headers.get("Content-Length",0)or0)body=self.rfile.read(length)iflengthelseb""headers={"Content-Type":self.headers.get("Content-Type","application/json"),"Authorization":self.headers.get("Authorization",""),"Accept":"application/json",}url=UPSTREAM+self.pathtry:response=requests.post(url,content=body,headers=headers,impersonate=IMPERSONATE,stream=True,timeout=300,)exceptExceptionasexc:self.send_response(502)self.send_header("Content-Type","text/plain; charset=utf-8")self.end_headers()self.wfile.write(("proxy error: %s\n"%exc).encode())returnself.send_response(response.status_code)self.send_header("Content-Type",response.headers.get("Content-Type","text/event-stream"),)self.send_header("Cache-Control","no-cache")self.end_headers()try:# 关键:按原始字节流转发,保留 SSE 空行(事件分隔符)# 不要用 iter_lines(),会吞掉空行导致 SDK 解析失败forchunkinresponse.iter_content(chunk_size=8192):ifchunk:self.wfile.write(chunk)self.wfile.flush()except(BrokenPipeError,ConnectionResetError):passexceptException:passdo_POST=_forward do_GET=_forwardclassThreadingServer(socketserver.ThreadingMixIn,http.server.HTTPServer):daemon_threads=Trueallow_reuse_address=Trueif__name__=="__main__":server=ThreadingServer((LISTEN_HOST,LISTEN_PORT),Handler)print(f"[proxy] listening on{LISTEN_HOST}:{LISTEN_PORT}->{UPSTREAM}",flush=True)try:server.serve_forever()exceptKeyboardInterrupt:pass3. 先手动启动测试
cd~/hermes-agent venv/bin/python /opt/opencode-proxy/proxy.py另开终端测试(把KEY换成你的 key):
curl-sN-o/dev/null-w"HTTP: %{http_code} | TTFB: %{time_starttransfer}s | 总: %{time_total}s\n"\--max-time60-XPOST http://127.0.0.1:18080/v1/chat/completions\-H"Content-Type: application/json"\-H"Authorization: Bearer 你的key"\-d'{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"max_tokens":5}'预期:HTTP: 200,总耗时几秒。
4. 配置 systemd 开机自启
创建/etc/systemd/system/opencode-proxy.service:
[Unit] Description=OpenCode curl_cffi TLS proxy After=network-online.target Wants=network-online.target [Service] Type=simple User=你的用户名 ExecStart=/home/你的用户名/hermes-agent/venv/bin/python /opt/opencode-proxy/proxy.py Restart=always RestartSec=3 Environment=PYTHONUNBUFFERED=1 [Install] WantedBy=multi-user.target启动:
sudosystemctl daemon-reloadsudosystemctlenable--nowopencode-proxysudosystemctl status opencode-proxy# 确认 active (running)5. 修改 Hermes 配置
把 opencode-go 的 base_url 指向本地代理:
# 先确认 opencode-go 在 custom_providers 里的索引(从 0 开始)hermes config get custom_providers|grep-n"name: opencode-go"# 假设索引是 2,把 base_url 改成本地代理hermes configsetcustom_providers.2.base_url"http://127.0.0.1:18080/v1"验证:
hermes config get custom_providers.2.base_url# 应输出 http://127.0.0.1:18080/v16. 重启 Hermes Gateway
systemctl--userrestart hermes-gateway五、验证是否生效
grep"API call"~/.hermes/logs/agent.log|tail-5修复前:latency=126.8s
修复后:latency=2.4s
也可以直接聊天测试。CLI 首次启动要加载技能/记忆,几十秒是正常的,以日志里的latency=为准。
六、常见问题
1. 返回 404
路径重复了。代理脚本里UPSTREAM = "https://opencode.ai/zen/go"不能带 /v1(请求路径自带 /v1),否则变成/v1/v1/chat/completions。
2. 报 EmptyStreamError / chunks=0
代理用了iter_lines()或过滤了空行。SSE 事件用空行分隔,必须用iter_content()按原始字节转发。
3. 端口被占用
ss-tlnp|grep18080# 看谁占着pkill-f"opencode-proxy/proxy.py"sudosystemctl restart opencode-proxy4. 改完还是慢
检查三件事:
sudosystemctl is-active opencode-proxy# 代理在跑吗hermes config get custom_providers.2.base_url# 指向 127.0.0.1:18080 吗grep"API call"~/.hermes/logs/agent.log|tail-1# 日志里 latency 多少七、注意事项
- API Key 安全:本方案中 key 只存于 Hermes 配置,代理透传不改写。发布教程时务必用
***打码。 - 只影响 opencode-go:其他 provider(DeepSeek 官方、mimo 等)的 base_url 没改,完全直连。
- 代理挂了会怎样:Hermes 会快速报连接错误(比 120 秒卡死好发现得多),systemd 会自动拉起。
- 合规提醒:这只是改变 HTTP 客户端 TLS 特征,不涉及任何破解/绕过鉴权,请遵守 OpenCode 服务条款。