在 VS Code 里免费使用 opencode ZEN 模型节点(deepseek-v4-flash-free)完整指南

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

适用版本:VS Code 1.9x+ / Copilot Chat;opencode 1.14.x;系统:Linux(Ubuntu/Debian 系)
更新时间:2026-08-16

零、前提

首先要会使用copilot的Custom Endpoint或oaicopilot插件配置,二者选其一即可。该文章以Custom Endpoint为例进行讲解。关于Custom Endpoint的配置,可以参考我另一篇文章VS Code 通过 Custom Endpoint 接入第三方模型

一、背景与痛点

opencode 提供了免费的 ZEN 节点,端点地址为:

https://opencode.ai/zen/v1

其中包含一些免费模型(如 deepseek-v4-flash-free),官方在 opencode 客户端里可以直接选用。但当我们想把免费模型接入 VS Code 的 Copilot Chat(通过"自定义端点/自带模型"功能)时,会遇到一个非常典型的报错:

Sorry, your request failed. Please try again.
Client Request Id: xxxx
Reason: Rate limit exceeded
{"type":"FreeUsageLimitError","message":"Error from provider (Console): Rate limit exceeded. Please try again later."}

配置里 API Key 写 public、URL 写 https://opencode.ai/zen/v1 都是对的,但请求就是被 429 拦截。为什么 opencode 客户端能用、VS Code 不能用?答案在请求头里。

二、根因:服务端靠请求头识别客户端

通过抓包/实测(curl 直连对比)可以得到以下结论:

请求头组合结果
裸请求(只带 Authorization: Bearer public❌ 429 限流
x-opencode-client / x-opencode-project 等头,没有 UA❌ 429 限流
x-opencode-* 头 + User-Agent: opencode/...✅ 200 正常

也就是说:ZEN 服务端会把请求头中的 User-Agent 和 x-opencode-* 头当作"客户端指纹",只有看起来像 opencode 客户端的请求才进入免费通道,否则全部丢进共享限流池返回 429。

而 VS Code 的"自定义端点"(Custom Endpoint)在处理 requestHeaders 时,会过滤掉 User-Agent 等保留头。也就是说,不管你如何在 chatLanguageModels.json 里写 "User-Agent": "opencode/...",这个头根本不会被发出去(源码里 _reservedHeaders 包含 user-agent_sanitizeCustomHeaders 会直接跳过)。

补充:曾经用 API Key(sk-...)测试也是 429,和认证方式无关,纯粹是"客户端指纹"缺失。

三、解决思路:本地小代理补头转发

既然 VS Code 不让我们改 User-Agent,那就让 VS Code 把请求发给本地代理,由代理负责补上所有 opencode 头,再转发到 ZEN 上游。

整体架构:

请求 /v1/chat/completions

补 User-Agent + x-opencode-* 头

200 响应

原样回传

VS Code Copilot Chat
chatLanguageModels.json

本地代理 zen-proxy.py
127.0.0.1:8788

opencode.ai/zen/v1
ZEN 免费节点

代理只监听 127.0.0.1,仅本机可访问,不会暴露到局域网。

四、实施步骤

第 1 步:编写本地代理脚本

新建 /home/你的用户名/zen-proxy.py,内容如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
zen-proxy.py - ZEN (opencode.ai) 免费模型本地代理

背景:
  VS Code 的 customendpoint 会把 requestHeaders 中的 User-Agent 当作保留头过滤掉,
  而 ZEN 服务端 (opencode.ai/zen/v1) 必须看到 opencode 客户端的 User-Agent 和
  x-opencode-* 头才放行免费额度, 否则返回 429 FreeUsageLimitError。

方案:
  在本地监听一个端口, VS Code 的模型 URL 指向本代理;
  代理收到请求后, 补上 opencode 客户端头(每次请求生成新的 session/request id),
  再原样转发到 https://opencode.ai/zen/v1。

用法:
  python3 zen-proxy.py [--port 8788]
"""
import argparse
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.request import Request, urlopen, HTTPError

UPSTREAM = "https://opencode.ai/zen/v1"
USER_AGENT = "opencode/1.14.28 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.13"

# 需要透传但由代理统一生成的头(防止 VS Code 的固定值被服务端拿去限流)
DYN_HEADERS = {
    "User-Agent": USER_AGENT,
    "x-opencode-client": "cli",
    "x-opencode-project": "global",
}


class ZenProxyHandler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def _handle(self):
        length = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(length) if length > 0 else b""

        # 上游路径: VS Code 会请求 /v1/chat/completions, 剥掉 /v1 前缀
        path = self.path
        if path.startswith("/v1/"):
            path = path[len("/v1"):]
        upstream_url = UPSTREAM + path

        req = Request(upstream_url, data=body, method=self.command)
        # 透传客户端头(保留 Authorization: Bearer public 等)
        for k, v in self.headers.items():
            if k.lower() in ("host", "connection", "content-length",
                             "transfer-encoding", "user-agent",
                             "x-opencode-client", "x-opencode-project",
                             "x-opencode-session", "x-opencode-request"):
                continue
            req.add_header(k, v)

        # 生成 opencode 客户端头: 每次请求唯一 id, 避免共享限流池按 id 计数
        ts = int(time.time() * 1000)
        for k, v in DYN_HEADERS.items():
            req.add_header(k, v)
        req.add_header("x-opencode-session", f"ses_vscode_{ts}")
        req.add_header("x-opencode-request", f"msg_vscode_{ts}")

        try:
            resp = urlopen(req, timeout=300)
            status = resp.status
            resp_headers = dict(resp.headers.items())
            data = resp.read()
        except HTTPError as e:
            status = e.code
            resp_headers = dict(e.headers.items())
            data = e.read()
        except Exception as e:
            status = 502
            resp_headers = {}
            data = str(e).encode("utf-8", errors="replace")

        self.send_response(status)
        for k, v in resp_headers.items():
            if k.lower() in ("transfer-encoding", "connection", "content-encoding"):
                continue
            self.send_header(k, v)
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()
        self.wfile.write(data)

    do_GET = do_POST = do_OPTIONS = do_PUT = do_DELETE = _handle

    def log_message(self, fmt, *args):
        pass  # 静默


def main():
    ap = argparse.ArgumentParser(description="ZEN 免费模型本地代理")
    ap.add_argument("--port", type=int, default=8788)
    args = ap.parse_args()

    server = ThreadingHTTPServer(("127.0.0.1", args.port), ZenProxyHandler)
    print(f"[zen-proxy] listening on http://127.0.0.1:{args.port} -> {UPSTREAM}")
    print("[zen-proxy] Ctrl+C 停止。")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n[zen-proxy] 已停止")


if __name__ == "__main__":
    main()

说明:x-opencode-session / x-opencode-request 每次都生成唯一值,避免多个请求共用同一个 id 被服务端限流池计数。

第 2 步:启动代理并自测

python3 /home/你的用户名/zen-proxy.py --port 8788

另开一个终端自测:

curl -s --max-time 60 "http://127.0.0.1:8788/v1/chat/completions" \
  -H "Authorization: Bearer public" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash-free","messages":[{"role":"user","content":"say ok"}],"max_tokens":20}'

返回 200 且带 choices 字段即成功:

{"id":"router-xxx","object":"chat.completion","model":"deepseek-v4-flash-free",
 "choices":[{"index":0,"finish_reason":"stop","message":{"role":"assistant","content":"ok"}}], ...}

第 3 步:配置 VS Code 自定义端点

编辑 VS Code 用户配置文件 chatLanguageModels.json

打开方式:Ctrl+Shift+P → 输入 Manage Language Models → 添加 Custom Endpoint;或直接编辑
~/.config/Code/User/chatLanguageModels.json

在数组里加入:

{
    "name": "ZEN",
    "vendor": "customendpoint",
    "apiKey": "public",
    "apiType": "chat-completions",
    "models": [
        {
            "id": "deepseek-v4-flash-free",
            "name": "zen/deepseek-v4-flash-free",
            "url": "http://127.0.0.1:8788/v1",
            "toolCalling": true,
            "vision": true,
            "maxInputTokens": 200000,
            "maxOutputTokens": 4096,
            "thinking": true,
            "supportsReasoningEffort": ["low", "medium", "high", "max"],
            "reasoningEffortFormat": "chat-completions"
        }
    ],
    "settings": {
        "deepseek-v4-flash-free": {
            "reasoningEffort": "max"
        }
    }
}

字段含义:

字段说明
apiKeypublicZEN 免费节点的公共令牌,无需注册
urlhttp://127.0.0.1:8788/v1指向本地代理(注意不要写 https://opencode.ai
apiTypechat-completions走 Chat Completions 协议
thinkingtrue声明模型支持推理
supportsReasoningEffort["low","medium","high","max"]在模型选择器里显示 Thinking Effort 四级
reasoningEffortFormatchat-completions以顶层 reasoning_effort 字段发送
settings(provider 级)reasoningEffort: "max"默认推理努力,新会话/重开不重置

第 4 步:重载 VS Code 并验证

  1. Ctrl+Shift+PDeveloper: Reload Window
  2. 在 Chat 输入框的模型选择器里选 zen/deepseek-v4-flash-free
  3. 模型旁的 > 箭头打开 Thinking Effort 子菜单,可切换 Low/Medium/High/Max
  4. 随便发一条消息,能正常回复即成功

验证底层是否真的带了 reasoning_effort:临时在代理里加一行日志(转发前打印请求体),应能看到:

{"model": "deepseek-v4-flash-free", "reasoning_effort": "max", ...}

五、(可选)配置开机自启

用 systemd 用户服务托管代理:

mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/zen-proxy.service << 'EOF'
[Unit]
Description=ZEN free model proxy (opencode.ai)
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/你的用户名/zen-proxy.py --port 8788
Restart=on-failure
RestartSec=3

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable --now zen-proxy

说明:

  • enable登录桌面即自动启动,无需手动操作;
  • 如果希望"开机未登录也运行"(如远程 SSH 使用),需额外执行 sudo loginctl enable-linger 你的用户名,一般场景不需要;
  • 常用管理命令:systemctl --user status zen-proxysystemctl --user restart zen-proxysystemctl --user stop zen-proxy

六、常见问题 FAQ

Q1:为什么直接配 https://opencode.ai/zen/v1 会被 429?

服务端按请求头识别客户端,裸请求 / 非 opencode UA 的请求全部丢进共享限流池。必须通过代理补 User-Agent: opencode/...x-opencode-* 头。

Q2:为什么不用 requestHeaders 在 VS Code 里直接加 User-Agent?

VS Code 的 Custom Endpoint 会把 User-Agent 等保留头从 requestHeaders 中过滤掉,配置了也不会发出去,这是源码层面的安全限制。

Q3:apiKeypublic 安全吗?

公开节点就是这样设计的,无私有密钥;即便用私有 sk- 密钥,不满足头指纹条件同样 429,所以没有意义。

Q4:免费额度用完了怎么办?

FreeUsageLimitError 是全局共享限流,等一段时间(几分钟到几小时)会自动恢复;代理每次生成新的 session/request id 就是为了尽量避开按 id 的计数。

Q5:模型列表里还有其他免费模型吗?

可以请求 http://127.0.0.1:8788/v1/models 查看当前节点支持的所有模型(包含付费与免费),免费模型一般以 -free 结尾。

七、小结

一句话总结整个方案:

VS Code 无法自定义 User-Agent,而 ZEN 免费节点只认"opencode 客户端指纹"——通过一个 30 行的本地 Python 代理补头转发即可绕过 429,把 deepseek-v4-flash-free 免费模型接入 Copilot Chat,还支持 Thinking Effort(Low/Medium/High/Max)四级推理。

本方案实测:请求全部 200,推理档位 max 在底层请求中确认生效,代理 systemd 托管开机自启,无日志文件产生。

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值