更新时间:2026-07-01。依据是当前机器上 ~/.comate/extensions/baidu.baidu-cc-2.1.158-rc.0/ 的实地探查(ducc shell、claude-go 二进制 strings、resources/settings.json~/.baidu-cc/user.json)。版本升级后细节可能变,但分层思想不会。

Ducc 是什么,不是什么?

Ducc(baidu-cc)不是 Claude Code 的分叉,也不是自己实现的 agent runtime,而是官方 Claude Code CLI 的二次分发容器。它把官方 claude.exe 完整打包进扩展目录,运行时不 patch 一行 CLI 代码,只做三件事:合成配置、注入签名、拉起官方 CLI。

它要解决的核心问题很清楚:让 Claude Code 在不知道自己被代理的前提下,把请求发到百度内部 oneapi 网关,并携带足够信息让网关完成鉴权、模型路由和审计。

一次 ducc 启动的四层调用链

先看整条链路的形状,后面再逐层拆开:

每一层的职责边界都很薄,这也是它能在 Claude Code 版本频繁更新时保持稳定的关键。

第一层:ducc 只是个 shell 包装

入口脚本 ~/.comate/baidu-cc/bin/ducc 是软链,指向扩展 resources 里的同名 shell(native-binary/bin/ducc)。整个脚本只做三件事:

  • http_proxy / https_proxy 等代理变量,避免走本机全局代理导致签名请求被拦。
  • 导出 DISABLE_BAIDU_CLAUDE_UPDATE=1BAIDU_CC_PLATFORM=AIIDE-terminal,前者关掉官方 auto-update,后者让上游区分调用平台。
  • sed 就地把 resources/settings.jsonno-baidu-settings.json 里的占位符 ~/.baidu-cc/baidu-cc 替换成 resources 的真实绝对路径,然后 exec 同目录的 claude-go

⚠️ 这里的 sed -i ''改扩展目录里的 settings.json 本身,所以扩展升级后占位符会被重置为下一次启动时再替换,这个副作用是有意为之,不是 bug。

第二层:claude-go 才是 Ducc 主体

claude-go 是一个 6.3 MB 的 Go 编译产物,负责真正的桥接工作。从二进制 strings 能确认它至少做四件事。

合并两份配置

Ducc 有两份配置文件,分工非常清晰:

文件角色谁在维护
resources/settings.json扩展默认模板(含 ANTHROPIC_BASE_URL、hooks、permissions、statusLine)扩展安装/升级时写入
~/.baidu-cc/user.json用户覆盖层(含 ANTHROPIC_AUTH_TOKEN、模型别名、登录态)Ducc 登录流程写入

claude-go 启动时读 user.json 的 env 段,merge 进 settings.json 的 env 段,日志里能看到 Synced N env configs from user.json to <settings.json>。这套分层的好处是:扩展升级会覆盖模板,但 token 存在用户目录不会丢。

⚠️ 这不是内存 merge,是持久化写入claude-go 会把 user.json 的 env 项真的写回扩展目录的 resources/settings.json 里。这带来一个反直觉后果:你在 user.json 里加过的任何 env 项(比如 ANTHROPIC_BASE_URL),即使之后从 user.json 里删掉,settings.json 里的那条不会被自动清除。想彻底回滚,必须两份文件都还原(详见文末回滚清单)。
如果 settings.json 损坏,claude-go 会从 https://oneapi-comate.baidu-int.comhttp://baidu-cc-client.bj.bcebos.com 远端拉一份回来(Restored settings.json from remote download),这是 Ducc 自愈能力。

计算 ANTHROPIC_CUSTOM_HEADERS

这是整个桥接机制里最关键的一步。claude-go 会拼一段 JSON 塞进 ANTHROPIC_CUSTOM_HEADERS,格式大致是:

comate_custom_header:{
  "agentId": "ducc:user:<username>",
  "username": "...",
  "repo": "...",
  "source": "ducc",
  "os": "darwin",
  "x-source-auth-version": "v1",
  "x-source-auth-timestamp": "2026-07-01T12:50Z",
  "x-source-auth-signature": "<HMAC/RSA 签名>"
}

x-source-auth-signature 是拿本地密钥对 timestamp 做的签名,让 oneapi 网关能验证请求确实来自合法 Ducc 客户端,而不是有人抓到 token 后自己伪造。

⚠️ 签名字段每次启动重算,光有 ANTHROPIC_AUTH_TOKEN 也无法绕过网关鉴权,这是 token 泄露的兜底防线。

有两种场景会跳过这段头:命令行显式加 --disable-model-proxy,或者使用组织级模型(日志:Using org model - skipping ANTHROPIC_CUSTOM_HEADERS)。

拉起官方 claude.exe

最后 claude-go exec resources/claude-code/bin/claude.exe(215 MB,官方 Claude Code CLI 的独立打包版本),并通过 --settings <path> 把合并好的 settings.json 传进去。所有 env 变量随子进程继承。

第三层:官方 Claude Code 完全无感

对官方 CLI 而言,这就是一次「读到了几个 env 变量」的普通启动:

环境变量作用
ANTHROPIC_BASE_URL请求发到 https://oneapi-comate.baidu-int.com 而不是 Anthropic 官方
ANTHROPIC_AUTH_TOKEN当作 API key 放进 Authorization 头
ANTHROPIC_CUSTOM_HEADERS每次 HTTP 请求都带上百度自定义头(含签名)
ANTHROPIC_MODEL / ANTHROPIC_DEFAULT_OPUS_MODEL请求 body 里的 model id,如 Claude Opus 4.7[1m]
--settings <path>复用同一份 settings.json 里的 hooks / permissions / statusLine

所以官方 CLI 什么都不用改,oneapi-comate 网关承担了「校验签名 → 换算模型 id → 转发到真正上游」的工作。这也是 Ducc 敢宣传「兼容 Claude Code 全部开放能力」的根本前提——它压根没碰这些能力的实现。

为什么这套设计能稳?

Ducc 的精妙之处在于每一层只做自己的事

  • ducc shell 只做环境清洗和路径修复,改也是改配置模板不是改代码。
  • claude-go 只做配置合成和签名注入,不实现任何 agent 逻辑。
  • 官方 claude.exe 只做 Claude Code 的本职工作。
  • oneapi-comate 网关只做鉴权和路由,模型上游可以随时换。

这带来两个直接后果:Claude Code 版本更新时 Ducc 只需要换 claude.exe,不用改任何桥接逻辑;上游模型切换时(比如某天换成百度自研)只需要改网关端映射,客户端零感知。

这套「不改内核,只改入口 + 配置 + 网关」的思路和 Codex 低侵入扩展架构思想 里提炼的「统一入口 + 生命周期事件 + 外挂控制面」是同一套心智模型的两个变种。

想动 baseUrl / authKey 的时候看这里

三份文件的实际分工

虽然只有两份配置文件,但因为 sync 机制的存在,实操时要同时想着三个位置:

位置作用手改风险
~/.baidu-cc/user.jsonenv用户覆盖层,改这里最安全扩展升级不会丢;claude-go 会 sync 到 settings.json
resources/settings.jsonenv模板+运行态混合,同时被扩展升级和 sync 覆盖直接改会被下次 sync 覆盖;扩展升级也会重置
Ducc 进程环境变量上述两份合并后的最终生效值只读,看 env | grep ANTHROPIC_ 就知道实际读到什么

正确改法

想持久生效、不被扩展升级冲掉:只改 user.json 的 env 段,让 claude-go 每次启动帮你 sync 到 settings.json。

⚠️ 但要注意:换掉 ANTHROPIC_BASE_URL 后,ANTHROPIC_CUSTOM_HEADERS 里的 x-source-auth-signature 依然会被注入。如果目标网关不校验百度签名(比如原生 Anthropic API 或自建代理),多带这段头一般无害;如果目标网关严格校验且签名对不上,反而会失败。想彻底关掉签名注入,加 --disable-model-proxy 启动。

彻底回滚清单(踩过的坑)

只从 user.json 里删掉一条 env 项不够,因为它已经被 sync 到 settings.json 落盘了。必须两份都还原:

# 1. 还原 user.json
cp ~/.baidu-cc/backups/user.json.bak-XXXX ~/.baidu-cc/user.json
# 2. 还原扩展 settings.json(关键,最容易漏)
cp ~/.baidu-cc/backups/settings.json.bak-XXXX \
   ~/.comate/extensions/baidu.baidu-cc-*/resources/settings.json
# 3. 新开 ducc 窗口验证
env | grep -E "ANTHROPIC_BASE_URL|ANTHROPIC_AUTH_TOKEN"

所以改之前一定先备份两份文件

TS=$(date +%Y%m%d-%H%M%S)
mkdir -p ~/.baidu-cc/backups
cp ~/.baidu-cc/user.json ~/.baidu-cc/backups/user.json.bak-$TS
cp ~/.comate/extensions/baidu.baidu-cc-*/resources/settings.json \
   ~/.baidu-cc/backups/settings.json.bak-$TS

一次真实的踩坑复盘

clawguard-llm.baidu-int.com 这类 UUAP/零信任网关不能直接当 Anthropic API 用。表现是根路径 302uuap.baidu.com/login/v1/messages 无论带不带 token 都是 403(对比 oneapi 无 token 是 401,差别就能看出鉴权方式不同)。这类值往往属于其它协议(OpenAI 兼容 / 内部 SDK 专用),换域名前先用 curl 探测协议和鉴权方式:

# 401 = Anthropic 协议、只是缺 token(可以试)
# 403 + Location 到 uuap = 走 SSO cookie,CLI 场景不能用
# 302 到 login = 同上
curl -sSI -m 8 -X POST https://<target>/v1/messages

相关笔记