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-SEA
opencode.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 -m pip install curl_cffi
验证:
venv/bin/python -c "import curl_cffi; print(curl_cffi.__version__)"
2. 创建代理脚本
sudo mkdir -p /opt/opencode-proxy
sudo chown $USER:$USER /opt/opencode-proxy
创建 /opt/opencode-proxy/proxy.py:
#!/usr/bin/env python3
"""opencode.ai 本地代理:curl_cffi 模拟 Chrome TLS 指纹转发请求"""
import http.server
import socketserver
import sys
from curl_cffi import requests
# 注意:不要带 /v1,Hermes 请求路径自带 /v1
UPSTREAM = "https://opencode.ai/zen/go"
IMPERSONATE = "chrome" # 模拟 Chrome 浏览器 TLS 指纹
LISTEN_HOST = "127.0.0.1"
LISTEN_PORT = 18080
class Handler(http.server.BaseHTTPRequestHandler):
# HTTP/1.0:连接结束以 EOF 表示 SSE 流结束
protocol_version = "HTTP/1.0"
def log_message(self, fmt, *args):
sys.stderr.write("[proxy] %s\n" % (fmt % args))
def _forward(self):
length = int(self.headers.get("Content-Length", 0) or 0)
body = self.rfile.read(length) if length else b""
headers = {
"Content-Type": self.headers.get("Content-Type", "application/json"),
"Authorization": self.headers.get("Authorization", ""),
"Accept": "application/json",
}
url = UPSTREAM + self.path
try:
response = requests.post(
url,
content=body,
headers=headers,
impersonate=IMPERSONATE,
stream=True,
timeout=300,
)
except Exception as exc:
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())
return
self.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 解析失败
for chunk in response.iter_content(chunk_size=8192):
if chunk:
self.wfile.write(chunk)
self.wfile.flush()
except (BrokenPipeError, ConnectionResetError):
pass
except Exception:
pass
do_POST = _forward
do_GET = _forward
class ThreadingServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
daemon_threads = True
allow_reuse_address = True
if __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()
except KeyboardInterrupt:
pass
3. 先手动启动测试
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-time 60 -X POST 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
启动:
sudo systemctl daemon-reload
sudo systemctl enable --now opencode-proxy
sudo systemctl 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 config set custom_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/v1
6. 重启 Hermes Gateway
systemctl --user restart 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 | grep 18080 # 看谁占着
pkill -f "opencode-proxy/proxy.py"
sudo systemctl restart opencode-proxy
4. 改完还是慢
检查三件事:
sudo systemctl 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 服务条款。
更多推荐

所有评论(0)