Aekor

Aekor
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. Blog
  3. 正文

ZCode Start Plan 容器化 —— 实现原理与配置说明

2026-09-30 29348点热度 81人点赞 0条评论

本文说明 zcode-docker/ 的完整工作原理、所需配置、每个组件存在的理由,以及绕过(更准确地说:复现)ZCode 桌面客户端风控链路的技术细节。

一、快速开始:跑起来需要什么配置

1.1 前置条件

项要求
Docker20.10+(Compose V2),Apple Silicon 或 x86 均可
出站网络容器需能访问 zcode.z.ai(API)与 o.alicdn.com(阿里云 captcha SDK)
一个已登录 Start Plan 的 ZCode 桌面用于提取 JWT(一次性)

1.2 必需配置(共 2 项)

① ZLIGHT_JWT —— Start Plan 的鉴权令牌(必需)

来自本机已登录 ZCode 的凭据文件:

Bash

python3 - << 'EOF'
import json, os
cfg = json.load(open(os.path.expanduser("~/.zcode/v2/config.json")))
jwt = cfg["provider"]["builtin:zai-start-plan"]["options"]["apiKey"]
print(jwt)   # 一串 eyJ... 开头的 JWT
EOF
  • 这就是桌面客户端访问 Start Plan 用的 zcodejwttoken(桌面把它同时放在 Authorization: Bearer 和 x-api-key 两个头里)。
  • JWT 内含 user_id/sub(账号绑定),没有失效时间字段,长期有效直至重新登录轮换。
  • 重新登录桌面后 JWT 会轮换,需要更新 .env 并重启 client 容器。

② ZLIGHT_DEVICE_MID —— 设备身份(必需,建议保持与本机一致)

Bash

python3 -c "import json, os; print(json.load(open(os.path.expanduser('~/.zcode/v2/telemetry-state.json')))['deviceMid'])"
  • 桌面客户端用它作为 X-Device-Mid 计费头和 ARMS RUM 遥测的用户标识。
  • 风控按 (账号, deviceMid, 出口 IP) 累积历史信誉;因此从本机提取原值填入,保持设备身份连续。
  • 实测(2026-09-30):deviceMid 不可随意。同一流程、同一 IP、同一新鲜 param:
    • 随机 deviceMid → 3012 拒绝(服务端按账号-设备历史绑定校验);
    • 且这次异常尝试会临时连坐——紧接着的真实 deviceMid 请求也被 3012,约 4 分钟后自行恢复。
    • 结论:deviceMid 参与风控强校验,请勿用它做实验。

可选配置

变量默认说明
ZLIGHT_MODELGLM-5.3-Flash兜底模型;实际以 billing 授权动态读取为准
ZLIGHT_API_KEY空(开放)设置后本代理要求调用方携带匹配的 x-api-key
ORACLE_PORT / PORT8790 / 8791两个服务的外部端口

1.3 生成配置并启动

Bash

cd zcode-docker

# 生成 .env(compose 自动读取)
python3 - << 'EOF'
import json, os
cfg = json.load(open(os.path.expanduser("~/.zcode/v2/config.json")))
mid = json.load(open(os.path.expanduser("~/.zcode/v2/telemetry-state.json")))["deviceMid"]
jwt = cfg['provider']['builtin:zai-start-plan']['options']['apiKey']
open('.env', 'w').write(f"ZLIGHT_JWT={jwt}\nZLIGHT_DEVICE_MID={mid}\n")
EOF

docker compose up -d --build
docker compose ps              # 两个服务应显示 healthy

首次构建约 3–5 分钟(node:22 镜像 + Electron Linux 版下载);启动后 oracle 首次铸参约 4–8 秒。

1.4 验证与调用

Bash

# 授权模型列表(动态读取 billing,当前应为 GLM-5.3-Flash 一项)
curl http://127.0.0.1:8791/v1/models

# 标准非流式调用(claude-* 模型名自动映射)
curl -X POST http://127.0.0.1:8791/v1/messages \
  -H 'content-type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{"model":"claude-sonnet-4-5","max_tokens":1024,"messages":[{"role":"user","content":"你好"}]}'

# 流式(标准 Anthropic SSE 原样透传):请求体加 "stream": true

# 接入 Claude Code:
ANTHROPIC_BASE_URL=http://127.0.0.1:8791 ANTHROPIC_API_KEY=anything claude

二、背景:为什么不能“直接调 API”

ZCode Start Plan 的模型端点:

[https://zcode.z.ai/api/v1/zcode-plan/anthropic/v1/messages](https://zcode.z.ai/api/v1/zcode-plan/anthropic/v1/messages)

走 Anthropic Messages 协议,但前面叠了三层风控,每一层都是实测确认的:

第 1 层:JWT 鉴权

  • Authorization: Bearer <JWT> + x-api-key: <同一 JWT> 两个头并存(缺 x-api-key 会被部分路径拒绝)。
  • 失败表现:HTTP 401。

第 2 层:captcha 票据闸(3007)—— 3.14.4 起对模型请求默认关闭

  • 2026-09-30 实测更新:ZCode 3.14.4 发布后,服务端 client/configs 的 captcha 配置新增 skip_model_request: true。captcha 机制本身保留(enabled 仍 true、sceneId/prefix/region 原样、客户端 3.14.4 内 CaptchaRequestRetry/preflight 代码原样),但模型请求被服务端策略明确豁免。裸请求(无 param)不再返回 3007,而是直接进入第 3 层行为评分。本项目的 client 会动态读取该标志:跳过时完全不铸参;若服务端重新要求,自动恢复铸参兜底(oracle 容器待命)。
  • 每一个 /messages 请求都必须携带一个未消费的阿里云 captcha param:
    • 请求头 X-Aliyun-Captcha-Verify-Param(280B base64:{certifyId, sceneId, isSign, securityToken})
    • 请求头 X-Aliyun-Captcha-Verify-Region: cn
  • param 只能由真实浏览器引擎里运行的阿里云 SDK(无痕验证)产出,Node 无法直接生成。
  • param 一次性:被任何请求消费后即失效;过期/伪造环境产出的 param 同样无效。
  • 失败表现:HTTP 400 {"code":3007,"msg":"captcha verify failed"}。
  • 阿里侧对同一设备状态重复铸参有去重:F008 错误,需约 30–60 秒冷却。

第 3 层:行为/信誉风控(3012)

param 有效只是入场券,请求本身还要过服务端行为评分,实测确认的必要条件:

  1. 连接预热 —— 同一 keep-alive TLS 连接上必须先完成“应用特征”请求(GET /api/v1/agent/configs + GET /api/v1/zcode-plan/billing/balance),冷连接第一发就 POST messages = 工具特征 → 3012。
  2. payload 指纹 —— 请求体必须携带 ZCode 官方 system 提示词("You are ZCode, an interactive coding agent...",v3.14.3 共 3 个 block 约 6.7K 字符);极简 body 即使 param 正确也 → 3012。
  3. 环境一致性 —— 请求头声明的平台应与铸参环境一致。
  4. 失败表现:HTTP 405 {"code":3012,"msg":"request has been blocked due to unusual activity"}。

结论:无法用纯 curl/Node“伪造”通过;必须复现客户端的三件套——浏览器铸参、连接行为、合法 payload。这就是双容器架构的由来。

三、实现原理

3.1 总体架构

Plaintext

调用方(Claude Code / Anthropic SDK / curl)
       │
       │ POST /v1/messages(标准 Claude 格式)
       ▼
┌─────────────── client 容器 :8791(纯 Node)────────────────┐
│ 1. POST oracle:8790/mint        → 领取一个未消费 param      │
│ 2. 预热:同连接 GET configs + GET billing(建立连接信誉)   │
│ 3. 注入 ZCode system 指纹块(zcode-system.json,调用方      │
│    自带 system 保留在后)                                  │
│ 4. POST zcode.z.ai /v1/messages:                          │
│      authorization + x-api-key(同 JWT)                   │
│      X-Aliyun-Captcha-Verify-Param / -Region: cn            │
│      完整引擎头(UA/x-platform/x-device-mid/...)           │
│ 5. 上游 Anthropic SSE ── stream? 原样透传 : 聚合为 JSON    │
└────────────────────────────────────────────────────────────┘
       ▲ POST /mint
┌──────┴──────── oracle 容器 :8790(Electron 41 + xvfb)─────┐
│ Xvfb 虚拟显示 → Electron 无头窗口                          │
│   → 加载 page.html(o.alicdn.com 的 AliyunCaptcha.js)     │
│   → initAliyunCaptcha(官方场景配置)                        │
│   → instance.startTracelessVerification()(无痕验证)      │
│   → success 回调直接产出 280B param                        │
│ F008 冷却:销毁页面→重载新实例→最多重试 4 次×45s            │
└────────────────────────────────────────────────────────────┘

3.2 oracle 容器:param 是怎么铸出来的

真实桌面的做法(从桌面日志与渲染器 bundle 逆向得到): 每次模型请求前,渲染器里的闭源控制器跑一次“无痕验证”——阿里云 SDK 在页面里静默收集设备/行为信号,向阿里风控换取一个已签名的 certifyId,打包成 param。

oracle 容器复现了这个流程的最小必要集:

  1. 为什么必须 Electron 而不是纯 Node:SDK 采集 canvas/WebGL/字体/时序等浏览器环境信号,Node 环境直接抛 Captcha requires browser environment。
  2. 为什么可以脱离真实桌面:实测证明 param 的有效性绑定的是“浏览器环境信号的合理性”而非“桌面进程身份”——一个干净的无头 Electron(Linux 容器内、原生 UA、不伪装)铸出的 param 与桌面铸的等效。早期“新环境 param 被拒”的结论实为冷连接所致(当时误归因于设备信誉)。
  3. page.html 与桌面渲染器完全同构:同一 SDK 源(o.alicdn.com)、同一场景配置(sceneId: 11xygtvd / prefix: no8xfe / region: cn)、同一元素 ID(#zcode-aliyun-captcha-element / #zcode-aliyun-captcha-button)、navigator.webdriver = false。
  4. main.mjs 的铸参循环:每次 /mint 重载页面(销毁旧 SDK 实例、避免复用已消费的 certify 状态)→ 轮询 window.__param(success 回调产出即注入)→ 60 秒超时;遇 F008 等 45 秒重试,最多 4 次。

3.3 client 容器:一次 /v1/messages 的完整生命周期

Plaintext

POST /v1/messages {model, max_tokens, messages, system?, stream?}
  ├── mapModel()          GET billing 授权(5 分钟缓存)→ claude-* 映射到已授权模型
  ├── serialized()       并发闸(默认 1;F008 冷却下并发只会互相挤兑)
  ├── mintParam()         oracle /mint(首次 ~4s;冷却期最长 ~2.5 分钟)
  ├── 预热                同一 keep-alive 连接 GET configs → GET billing(两条 200)
  ├── 组包                body.system = [ZCODE_SYSTEM, ...调用方system]
  │                       metadata.user_id = JSON{device_id, account_uuid:"", session_id}
  ├── POST                引擎头 + 双鉴权头 + param/region 头 → 上游
  └── 响应                stream=true  → SSE 字节流原样透传(含 thinking 块)
                          stream=false → 聚合 message_start/content_block_*/message_delta
                                         为标准 Messages JSON

模型映射规则(实测:Start Plan 仅授权 GLM-5.3-Flash 一个模型):

  • /v1/models 返回 billing entitlements 里 model:* capability 对应的 show_name(动态、5 分钟缓存,不会列出无授权的 GLM-5.3 / GLM-5-Turbo)。
  • 显式 glm-5.3-flash(任意大小写)→ 原样使用;其余一切名字(含 claude-*)→ 首个授权模型。
  • 身份字段:metadata.user_id 是 JSON 字符串 {device_id, account_uuid:"", session_id},account_uuid固定空串(与桌面行为一致,源码 anthropic-request-metadata.ts 证实);x-session-id 用裸 UUID(引擎会把内部 sess_ 前缀剥掉再上网,runner-attribution.ts)。

3.4 为什么这样能过风控(实验依据)

以下均为逐因子替换实验的结论(当天风控策略下):

实验结果
完整引擎头、无 param3007
+ 已消费 param3007
+ 未消费 param、冷连接、极简 body3012
+ 桌面同款会话 ID / Node24 TLS / x-api-key(单独补)仍 3012
+ 完整 95KB 真实 body、新会话 ID200
+ 真实 body 但极简 messages200(body envelope 是关键)
+ 仅注入 ZCode system 提示词200(指纹就是 system)
容器内:oracle 铸参 + 预热 + system 注入200(端到端)

配额归属核验:调用前后 billing/balance 的 used_units 增量落在 zcode-v3-start-plan-trust-* 计划——即 Start Plan 配额本身。

四、文件清单

文件作用
Dockerfilenode:22-bookworm + xvfb + Electron 运行库(libnss3/libgtk-3/字体等)
entrypoint.sh按 ROLE 分流:oracle → Xvfb + electron;client → node
compose.yaml双服务 + 健康检查 + 环境注入
oracle/page.html与桌面渲染器同构的铸参页(SDK + 场景配置 + 无痕验证)
oracle/main.mjs铸参 HTTP 服务(POST /mint、GET /health),F008 重试
client/server.mjs标准 Claude API 代理(/v1/messages、/v1/models、/ask、/health)
client/zcode-system.jsonZCode v3.14.3 官方 system 提示词(风控指纹,从真实请求提取)
.envZLIGHT_JWT、ZLIGHT_DEVICE_MID(不入库,已 gitignore)

五、运维

  • 健康检查:GET :8790/health(oracle 窗口状态)、GET :8791/health(client 配置);compose 每 30 秒自动探测,异常自动重启。
  • 日志:docker compose logs -f oracle|client。client 每次请求打一行 param minted (280B) → done;oracle 打铸参耗时与 F008 重试。
  • 扩容吞吐:param 一次性 + F008 冷却 → 单实例 ≈ 1 请求/30–60 秒。横向扩 oracle(docker compose up -d --scale oracle=3,client 侧轮询多 oracle)可线性提升;注意所有实例共享账号配额与风控信誉,扩容不改变单请求 30–60 秒的串行延迟。

故障排查速查

症状原因处置
client 启动即退(ZLIGHT_JWT 未设置).env 缺失/未加载检查 .env 与 compose environment
oracle 反复重启Electron 缺 --no-sandbox(root)确认 entrypoint 参数完整
大量 401JWT 过期(桌面重新登录轮换)重新提取 JWT 更新 .env
上游 3007param 已消费/过期检查 oracle 与 client 时钟、缩短铸造到使用间隔
上游 3012风控升级或预热不足确认 system 注入与预热未被改动;观察是否当日策略变化
铸参超时F008 连续冷却正常重试机制内;持续失败则重启 oracle 容器

六、限制与提醒

  • 吞吐:单实例约 1 请求 / 30–60 秒;不适合高并发场景。
  • 风控漂移:风控策略会随时间升级(本方案验证日就发生过一次),system 指纹 / 预热路径 / 双鉴权头都是与当日策略对齐的产物,未来可能需要再校准。
  • 出口 IP:全部验证在同一出口 IP 完成;跨 IP 部署未验证,账号-IP 历史关联可能参与评分。
  • 配额:消耗的就是 Start Plan 计划本身的 token 余额;该方案自动化的是自己账号的配额,但请控制节奏——高频工具特征可能触发账号级风控升级。

七、实测与三层分析

专项三层分析与容器适配记录:

1. 客户端二进制层面

3.14.4 的 app.asar 里验证码链路原样保留——preflight、AliyunCaptcha SDK、param 请求头、重试器一个没少。所以不是客户端删了功能。

2. 服务端配置层面(真正的变化)

GET /api/v1/client/configs 返回的 captcha 配置里新增了标志:

JSON

"captcha": {
  "enabled": true,
  "sceneId": "11xygtvd",
  "prefix": "no8xfe",
  "region": "cn",
  "skip_model_request": true   // ← 新增
}
  • 机制本身还在(enabled 仍 true),但模型请求被服务端策略明确豁免。
  • 3.14.4 客户端认这个标志就跳过 preflight;旧客户端不认识所以继续铸参。
  • 这是典型的服务端灰度开关——随时可以翻回来。

3. 实测确认

  • 完整合法 payload(system 指纹 + 预热 + 真实 deviceMid)不带 param 裸调 → 200。
  • 同时验证了行为风控层依然存在:极简 body(无 system 注入)+ 无 param → 3012。
  • 所以预热和指纹注入依然必要,只是 param 这一层免了。

4. 容器自动适配方案

client 升级为动态读取该标志(2 分钟缓存):

  • skip_model_request: true(当前) → 完全不铸参,端到端从 30–60 秒降到 ~5 秒。
  • 服务端若翻回 false → 自动恢复铸参,oracle 容器继续待命。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026-09-30

Aekor

这个人很懒,什么都没留下

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

使用AI教程

  • API报错解决方案
  • API 基础知识
  • API Key 获取
  • 最新接入教程

分类

  • Blog
  • TradingAgents-CN
  • 使用教程

COPYRIGHT © 2026 Aekor. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang