← Back
linguo2625469

linguo2625469/workbuddy2api-panel

把腾讯WorkBuddy账号变成 OpenAI 兼容 API 的多账号网关,同时自动完成任务中心全部任务,附 Web 管理面板(账号池可视化 / 积分任务 / 配置热更新)。基于 Sliverkiss/workbuddy2api 的增强分支

View on GitHub ↗
admin-panelcodebuddygatewaygolangopenai-apireverse-proxyworkbuddy
Stars
1.6K
Forks
465
Watchers
1.6K
Open issues
34
Contributors
16
Language
Go
License
MIT License
Default branch
main
Created Sep 12, 2026Updated Oct 1, 2026

Star growth

Today—
This week—
This month—

Star history will appear here once this repo has been tracked for a couple of days.

README

WorkBuddy2API

WorkBuddy2API Panel

把腾讯 CodeBuddy 账号变成 OpenAI 兼容 API 的多账号网关 · 附 Web 管理面板
Web 面板 · OAuth 浏览器登录 · 账号池轮转 · 熔断与冷却 · 会话粘性 · 定时签到 / 活跃 / 旅行 / 保活 · 成长任务一键完成(17/18) · 流式 / 非流式

Go API Deploy Transport


本项目是 Sliverkiss/workbuddy2api 的增强分支(fork)。 上游仓库现已删除;本项目已同步至上游删库前的最后一次更新(ea8b1e5),此后由本分支独立维护演进。 在上游基础上重构了可视化运维层;差异概览见 与上游的差异,上游设计的精巧之处(账号池调度、错误分类、提示词体系)原样保留,详见下文。

⚠️ 本项目仅限自用账号(签到 / 保活 / 个人工具接入),不支持同时也是禁止批量小号分发额度、二次打包或收费售卖。 详见 使用声明(必读)。

使用声明(必读)

本项目(含上游 Sliverkiss/workbuddy2api,下同)的开发初衷只有一个:方便个人管理自己的 CodeBuddy 账号——自动签到、保活、给自己的本地工具提供一个 OpenAI 兼容入口。它是免费、开源、按「原样」提供的个人自用工具。

近期我们发现有人将本项目用于以下行为:

  • 批量注册小号 / 收购账号,对外提供付费 API、共享池、代充等业务;
  • 二次加壳、捆绑卡密(授权码)售卖,或以「公益服」「低价中转」等名义变相收费分发。

我们对上述行为表达最强烈的反对,并声明如下:

  1. 一切商用 / 售卖行为与本项目及作者无关。 本项目不授权、不支持、不参与任何面向公众的 API 售卖、账号池出租、卡密收费分发;行为人由此产生的一切后果(包括但不限于账号封禁、条款违约与法律风险)由其自行承担,与作者和贡献者无任何关系。
  2. 批量注册与转售接口配额违反目标平台服务条款。 CodeBuddy / 腾讯系服务条款禁止批量注册账号及商业转售接口。上游仓库已删除、停止公开维护——我们无法断定具体原因,但此类滥用行为正在毁掉所有正常使用者的环境,请勿再消耗社区的善意。
  3. 请勿购买任何「收费版」「卡密版」「公益中转版」。 本项目永远免费开源。任何加壳、加密、捆绑收费的「版本」都是他人篡改的产物,与本项目无关;且此类分发无法审计,存在被植入后门、回传并窃取你 CodeBuddy 凭证的风险(auths/ 中保存的是明文 accessToken / refreshToken)。你付钱买到的不是本项目,而是把自己账号交给陌生人的机会。
  4. 关于开源协议的诚实说明。 本项目基于 MIT 协议开源,协议允许自由使用与修改源码——这是开源的本意,我们不会收回;但 MIT 赋予的是代码层面的自由,不赋予以本项目名义宣传、售卖、捆绑分发,或要求作者提供支持与背书的权利。作者不为任何第三方分发版本提供支持、更新承诺或安全保证。
  5. 作者保留止损的权利。 若滥用行为持续,作者可能随时停止维护、关闭或删除仓库,且不另行通知。上游的今天可能就是本项目的明天,望自重。

如果你的用途是管理自己的账号,欢迎正常使用、反馈问题与提交 PR。

项目简介

WorkBuddy2API 是一个自托管的 OpenAI 兼容反向代理网关,将腾讯 CodeBuddy(copilot.tencent.com)账号包装为统一的 /v1/chat/completions 服务。

  • 官方不提供 OpenAI 形态的开放 API,本项目通过 OAuth 设备授权(面板「添加账号」或 login.sh)获取账号凭证,在网关侧做 token 自动刷新、账号池调度与流量治理;
  • 面向 个人多账号 场景:多账号共享、单号故障自动换号、冷却 / 熔断防止雪崩、会话粘性保证多轮上下文不跳号;
  • 对客户端只暴露 OpenAI 兼容接口,现有 SDK / 前端 / 工具 零改造接入。

⚠️ 合规须知:本项目是非官方网关,使用 CodeBuddy 账号作为上游,仅限本人授权账号、本机 / 私有环境测试。详细边界见安全与合规。

核心能力

能力 说明
🔑 OAuth 一键登录 login.sh 设备授权流程,自动落盘凭证并重启容器加载新账号
🔄 多账号池 最早到期优先 + 成本分层 + 加权随机选号,Top-5 候选 + 防惊群
🛡️ 熔断与冷却 429 软冷却 600s 起指数退避(封顶 soft_rate_max)、404 固定 60s 短冷却、402 硬冷却至次日 04:00、连续失败熔断、在途租约限流
🧲 会话粘性 同一会话(conversation_id)尽量绑定同一账号,TTL 滚动续期,失败自动解绑,可镜像 Redis 防重启丢失
⏰ 定时任务 签到(09/21 点,末尾自动跑连登管家:兑换已解锁档位 + 抽完抽奖次数)+ 活跃上报(10 点,点亮连登 / 解锁领养 + streak 自检)+ 猫猫旅行(09/21 点,独立排程)+ token 保活(22 点),四类独立开关
⚡ 流式 + 非流式 出站强制 stream:true;SSE 帧按规范白名单重建;非流式由本地聚合为单响应
🧠 推理模型兼容 DeepSeek 思维链注入(thinking.type=enabled + 默认档)、reasoning_content 多轮回填、effort 档位自动降级
💬 系统提示词体系 网关自有提示词替换客户端 system(默认 custom),从源头消灭 system 来源的内容误报;passthrough 遇拦截自动降级重试
🗑️ 指纹脱敏 出站请求体黑名单指纹字段清洗(可关闭),与提示词体系两层叠加
📊 可观测 每请求一行表格日志(TTFB / token 速率 / uid);/healthz 带 service 身份标识可接负载均衡 / 宿主探活
💾 状态持久化 池状态本地原子落盘 + Upstash Redis 异步镜像(可选),重启择新恢复
🖥️ Web 管理面板 内嵌单页面板(明暗主题),账号运维 / 模型档位查询 / 在线改配置(热生效)/ 运行日志 / 积分任务,见 Web 管理面板

🎯 成长任务一键完成(17/18)

官方「成长计划」的 18 个成长任务中,17 个可在面板上一键纯 API 完成——无需安装官方客户端、无需人工交互,点一下「一键完成」即自动推进进度、等待异步计分落定并自动领奖。剩余任务展示操作指引。

任务覆盖与奖励

任务 奖励 一键完成方式
first_buddy +300c +8e 解锁上报 → 同意协议 → 领养第一只 Buddy
create_canvas +300c +5e 设计画布创建事件组(Ardot 遥测)
chat_5 +100c 对话活跃上报 ×5(自动补足差额)
Model_chat_GLM5.2 +100c +5e glm-5.2 真实对话一次(发一条短消息)
RichMeow_Chat +100c +5e +UR Buddy 桌面端对话事件链(6 事件,含成功回执)
Buddy_App +100c +5e Buddy 应用「发现→进入→授权」事件链
Buddy_App_QQ +50c +5e 企鹅教师助手进入事件链(与上一条共用)
automation_1 +100c +5e 定时任务创建成功事件
Library_read +100c +5e 资料库阅读点击(web 域上报)
template_5 +100c +5e 模板使用事件组 ×5
playbook_prompt +100c +5e 灵感案例「做同款」发送事件
expert_5 +100c +5e 真实专家召唤+使用链 ×5(专家市场拉真实专家 → 真实对话 → 使用事件)
Expert_team_use_3 +100c +5e 专家团召唤+使用链 ×3
Hp_Appearance +100c +5e 主题设置 + 皮肤生效事件
Expert_lighthouse +100c +5e 轻量云专家召唤+使用链(真实对话 requestId,可免费领一个月轻量服务器)
skill_1 +100c +5e 真实对话 + 技能加载事件(skill_info)

全新账号一键全做完 ≈ +1950 credits +78 能量,其中仅数个任务涉及真实对话(Model_chat_GLM5.2 一条、expert_5/Expert_team_use_3/skill_1 各数条 fast-model 短对话),其余全部为行为事件上报,零对话消耗。

不可自动的 1 个

任务 原因
Expert_Philanthropy 需真实捐款(服务端领奖时校验捐赠回执,已实测无法绕过)

实现原理(简述)

任务计分走 /v2/report 行为上报,但不同任务认不同客户端指纹:CLI 指纹(www.codebuddy.cn)、桌面指纹(copilot.tencent.com + WorkBuddy/5.5.6 UA + workbuddy-desktop 事件族)、web 指纹(www.workbuddy.cn + x-client-platform: web)。网关为每类任务构造对应指纹的判据事件链(internal/upstream/desktop.go);专家类任务额外要求真实专家 id 与真实对话回执(internal/upstream/streak.go 之外的 expert 序列)。上报 200 ≠ 计分——面板在执行后轮询任务进度,达标即自动调用 Web 域领奖接口。

⚠️ 行为事件按天幂等:重复点「一键完成」不会重复扣资源,已达标的任务自动跳过。

🧭 任务中心(面板新视图)

「任务中心」视图把散落的任务能力收拢成一处:

  • 全账号任务扫描:一键拉取每个账号的成长任务(未完成且可自动化的 19 项,含小程序口径的「校园日」与「小程序首对话」)+ 开学季待办,列表一目了然
  • 执行队列:把待办按账号排队执行——账号内串行(与单任务/一键完成共用互斥锁),账号间可选并发(1-3);执行进度实时更新到每个条目
  • 开学季独立状态卡:每账号 5 任务(分享/桌面/对话×3/专家/学生认证)的状态矩阵 + 剩余抽奖次数,一键触发全账号闭环
  • 日志分频道:运行日志按「任务 / 对话 / 系统」三个频道筛选——对话流量再大,任务结果也不会被冲掉;日志条目带频道徽标与时间
🎒 开学季活动(5/5 全自动)——活动期至 2026-09-24,已结束;逆向成果存档,点击展开

官方「AI 好 Buddy,开学有好礼」小程序活动的 5 个任务全部纯 API 自动完成(挂签到排程末尾,幂等):

任务 奖励(每日) 判据(已逆向)
分享活动 +100c +1抽奖 share-complete 直调即点亮
桌面端体验(单次) +100c +1抽奖 viewed 激活 + 真实 chat + 桌面六事件链
和 AI 对话 3 次 +50c +1抽奖 viewed 后 3 条 chat_request_send 埋点(无需真实会话)
召唤开学季专家 +50c +1抽奖 viewed 后 mp 事件链(召唤×3 + 对话)
学生认证 +100c 需微信学生真实认证,不做

抽奖次数自动全部抽完。期间逆向成果(cf-connect 加密通道、mp 云对话全链路)记录在 data/desktop-task-protocol.md §8。

同一活动在成长任务中心还有两条小程序口径任务(X-Client-Platform: miniprogram 专属下发,默认列表不可见,各 +100c+5e):

任务 判据(已逆向)
school_season 校园日 mini chat_request_send + activityId=school_open_day_2026(无 activityId 不点亮;accept/claim 均要求 mp 头)
Sequential_Tasks_1 小程序首对话 mini chat_request_send(无 activityId,服务端按 source=mini_program 指纹关联)

任务中心扫描自动合并 mp 口径待办;accept 带登记回读验证(上游存在 200+OK 但未落账的形态,未生效自动重试一次)。

连登兑换与抽奖(自动)

成长中心连登档位(连续登录 7/14/28 天)兑换后发放积分 / 能量 / 补签卡 / 抽奖次数,抽奖次数只能从兑换获得。网关把它挂在每日签到排程末尾自动跑闭环(见定时任务):档位解锁当天自动兑换、有抽奖次数自动抽完,全程无需人工盯。

🆚 与上游的差异

本分支相对 上游 master 的增量(均已在真实多账号环境验证):

新增

能力 说明
Web 管理面板 internal/panel,前端 go:embed 单文件进二进制,零外部依赖。账号池可视化(健康色条 / 积分量条 / 冷却倒计时)、积分到期分布、单号运维、批量任务、日志查看、明暗主题
请求指标与脱敏日志 面板展示完成成功率 / HTTP 成功率 / 平均耗时 / 最近请求,响应带 X-Request-Id;JSONL 只归档请求元数据,不写提示词、响应正文或凭证。请求记录表带调用来源(客户端 IP / User-Agent,按 logging.request_client_info 可关),支持按 IP / UA / 模型 / 账号 / 请求 ID 与结果筛选
浏览器内 OAuth 添加账号 面板「添加账号」按钮完成设备授权 → 凭证落盘 → 热加载进池(免重启),替代命令行 login.sh 流程
在线配置编辑(热生效) 面板直接改 config.json:API 密钥 / soft_rate / 脱敏开关 / 池参数 / 任务排程立即生效;装配期字段(listen 等)保存后提示需重启。写入采用深合并 + 原子替换,保留未知键
积分任务体系 任务列表 / 接受 / 领取接口 + 面板弹窗;「一键完成」覆盖 17 个任务(对话 / 领养 / 桌面行为链 / 模板 / 灵感案例 / 画布 / 专家召唤 / 技能尝鲜 / 主题 / 资料库 / 夜猫子等),推进进度、等待异步计分落定后自动领奖,纯 API 零客户端依赖
首启自动生成配置 目录下无 config.json 时自动生成推荐配置(含 crypto/rand 随机 api_key),双击即开
粘性会话内容回退 客户端不发 conversation_id 时,用 system + 首条 user 哈希派生会话键(d- 前缀),通用 OpenAI 客户端也能享受粘性
余额后台刷新 schedule.balance_refresh_minutes(默认 5)周期查余额并更新池,冷却账号余额恢复自动解冻
模型能力透出 /v1/models 附带 supported_efforts / default_effort / 积分倍率 / 输入输出上限等上游真实字段
安全加固 常量时间密钥比较(internal/httpauth)、CSP 与安全响应头、UID 白名单防路径穿越、前端属性转义修复
领养前置修复 上游 travelAdopt 缺 report 前置导致领养恒失败于 first_buddy task not completed yet;本分支修正后实测 +300 到账(3/3 账号)

同步上游

第一轮(fork 基线 53ee3a1 → 9a87758,34 个提交):四类任务独立排程、pool 文件拆分、12153 连续计数才禁用、429 code=6004 模型级限流收窄、11101 不罚号、请求体 413、DeepSeek 思维链、reasoning_content 回填、Codex 指纹脱敏、系统提示词体系、出站 UA 可配等。

第二轮(9a87758 → ea8b1e5,2026-09-14,只吸收底层):

上游改动 吸收内容
净化增强 tool_calls.arguments 盲区修复(content=null 的工具调用轮此前完全漏净化)、裸 11128 反探测改写、桌面版身份句(逗号形态)漏网修复、反馈句整句改写
出站头族 UA 对齐官方三段式 WorkBuddy/<ver> WorkBuddy/<ver> CLI/<ver>(默认 5.5.4/2.137.1,可配);X-IDE-* 用量归属四头 + X-Agent-Purpose(client_name 配 WorkBuddy 即对齐官方桌面端);X-Device-Token 设备风控头(auth 每号 / config / 文件三源);X-IDE-Version 补齐
并发修复 客户端 IP 改按请求参数传递(消除共享字段竞态);billing 单段 UA 形态
签到幂等 IsAlreadyCheckin 识别"今天已签到"(code=10001/14001),调度日志不再把重复签到当失败
粘性按模型判活 会话绑定的账号被 6004 模型级限额后,换模型请求自动解绑重分配(治"限额后换不动号");/healthz 探活计入模型豁免形态(治"全号被单模型限流探活误报 503")
report 增强 ReportChatActivity 支持独立 requestID(同会话多轮上报各条可区分)

未吸收(明确不做):脚本体系(task_runner/school 脚本—我们已有更完整的纯 API 实现)、governance/CI workflow、成本账本选号(依赖 usage.credit 观测,收益待验证)。

上游仓库此后已删除,上述第二轮(ea8b1e5)即其删库前的最后一次更新,本分支已完整吸收。此后本仓库与上游不再有同步关系,演进以本仓库为准。

架构总览

flowchart LR
    Client["客户端 / SDK\nOpenAI 兼容请求"] --> H

    subgraph GWI["WorkBuddy2API 网关 :7863"]
        H["HTTP Handler\n鉴权 · 请求体上限 · 提示词改写 · 轮转"] --> P
        H --> S
        P["账号池\n最早到期优先 · 成本分层 · 熔断 · 冷却 · 租约"] --> U
        S["会话粘性路由"] -.绑定镜像.-> REDIS
        T["定时调度\n签到 09/21 · 旅行 09/21 · 活跃 10 · 保活 22"] --> P
        U["上游 Client\nChatHTTP 流式 · 短 RPC"]
    end

    P -. "读凭证 (0600)" .-> AUTH[("auths/*.json")]
    P -. "状态镜像" .-> REDIS[("Upstash Redis\n可选")]
    U -->|"chat/completions (SSE)"| CB["CodeBuddy\ncopilot.tencent.com"]
    U -->|"billing / auth / growth"| CB
Loading

上游请求在出站前经历统一的改写管线(internal/upstream/payload.go):强制 stream:true、developer 角色归一、tool_choice 归一、image_url 字符串兼容为 OpenAI 对象形态、DeepSeek 思维链注入、reasoning_effort 档位降级、reasoning_content 回填、指纹脱敏。

快速开始

环境要求

  • Docker + Docker Compose(服务端部署方式,镜像内已含低权限用户与全部工具脚本)——或
  • Windows / macOS / Linux 直接跑单文件二进制(无需 Docker,见下方「Windows 单文件运行」)
  • 一个或多个已注册的 CodeBuddy 账号,用于 OAuth 登录
  • 宿主机 Go ≥ 1.22(仅从源码构建时需要)

方式〇:GHCR 镜像(免克隆免构建)

CI 会自动构建多架构镜像(amd64 / arm64)并发布到 GHCR,git clone 之外的部署路径:

# 1. 准备配置与数据目录
mkdir -p auths data && cp config.example.json config.json
#    建议编辑 config.json 设置 api_key(或留空由程序自动生成随机密钥)

# 2. 拉取并运行
docker run -d --name workbuddy2api \
  -p 7863:7863 -e TZ=Asia/Shanghai \
  -v ./auths:/app/auths -v ./data:/app/data -v ./config.json:/app/config.json \
  ghcr.io/linguo2625469/workbuddy2api-panel:latest

# 3. 健康检查(无可用账号时返回 503)
curl -s http://localhost:7863/healthz

首次发布后须将包设为公开:GitHub 仓库页 → Packages → workbuddy2api-panel → Package settings → Change visibility → Public,否则拉取需要 docker login ghcr.io。

镜像 tag 规则:main 分支推送 latest / main / sha-xxxxxx;打 v* tag 额外发布 1.2.3 / 1.2 / 1 语义化版本;PR 仅构建验证、不推送。

方式一:Docker Compose(推荐服务器部署)

# 1. 克隆
git clone https://github.com/linguo2625469/workbuddy2api-panel.git
cd workbuddy2api-panel

# 2. 准备配置(compose 挂载此文件,缺失会导致容器启动失败)
cp config.example.json config.json
#    建议编辑 config.json 设置 api_key(或留空由程序自动生成随机密钥)

# 3. 启动(首次会构建镜像,约 1-2 分钟)
docker compose up -d --build

# 4. 健康检查(无可用账号时返回 503)
curl -s http://localhost:7863/healthz
# {"healthy":0,"total":0,"service":"workbuddy2api"}

启动后打开 http://localhost:7863/panel/,用面板「添加账号」完成登录(见下节)。

常用运维命令:

docker compose logs -f          # 跟踪日志
docker compose restart          # 重启
docker compose down             # 停止并移除容器(数据在 ./auths 与 ./data,不受影响)

方式二:Windows 单文件运行(无需 Docker)

# 1) 下载 Release 中的 wb2api.exe,或从源码构建
go build -trimpath -ldflags="-s -w" -o wb2api.exe ./cmd/server

# 2) 直接运行:首次启动自动生成 config.json(含随机 api_key,日志打印一次)
.\wb2api.exe -config config.json

# 3) 浏览器打开面板添加账号
#    http://127.0.0.1:7863/panel/

exe 为单文件自包含(前端资源已 embed 进二进制),拷到任意 Windows 机器即可运行,只需保证 auths/(凭证)与 data/(状态)目录可写。

方式三:源码运行(开发调试)

go build ./...
go vet ./...
go test ./...                      # 完整测试套件
go run ./cmd/server -config config.json

构建全部二进制:

CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o wb2api ./cmd/server
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o signin_bin ./cmd/signin
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o login ./cmd/login
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o credit ./cmd/credit

添加账号(登录)

方式 A:Web 面板(推荐,各平台通用,免命令行)

打开 http://127.0.0.1:7863/panel/,点右上角「添加账号」:面板展示授权链接 → 浏览器完成登录 → 自动检测并落盘凭证 → 热加载进池(无需重启),顺带完成首次签到。

方式 B:命令行脚本(仅 Linux / macOS,依赖 bash + python3)

./login.sh
# 按提示在浏览器打开授权链接 → 回到终端确认 → 凭证落盘 auths/workbuddy-<uid>.json

login.sh 内置授权 URL 获取 + 浏览器登录 + token 轮询 + 首次签到 + 凭证落盘 + 容器重启,全程无 PKCE(state 由服务端签发)。账号池在容器启动时用 auths/ 目录自动对齐,新增凭证文件即自动发现。

Windows 用户请用方式 A(或 WSL);login.sh 需要 python3。

验证

# 模型列表
curl -s http://localhost:7863/v1/models -H "Authorization: Bearer your-api-key"

# 账号状态(汇总 + 每账号详情,disabled 账号透出 disabled_reason)
curl -s http://localhost:7863/status -H "Authorization: Bearer your-api-key"

# 流式聊天
curl -sN http://localhost:7863/v1/chat/completions \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"stream":true}'

# 非流式聊天(本地聚合)
curl -s http://localhost:7863/v1/chat/completions \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"stream":false}'

配置说明

config.example.json 是配置项最完整的参考:每个字段、默认值与结构都能在其中找到,示例值一律是 test_key 之类占位符,不含任何真实密钥。下表为字段含义速查。

字段速查

字段 默认 说明
listen :7863 HTTP 监听地址
api_key 空 网关鉴权密钥;空 = 不鉴权直接放行(公网必须设置)
auth_dir ./auths 账号凭证目录
state_file ./data/state.json 账号池状态持久化文件
panel.package_detail_limit 5 积分构成页单账号默认展示的最早到期包数;其余未用完包与已用完包聚合折叠
logging.request_archive_enabled true 请求元数据 JSONL 归档开关;不记录提示词、响应正文或 Authorization
logging.request_retention_days 7 请求归档保留天数;超期文件在启动和周期清理时删除
logging.request_archive_max_mb 100 请求归档总容量上限(MiB);超限优先删除最旧文件
logging.request_client_info true 请求日志是否记录调用来源(客户端 IP + User-Agent):写入 JSONL 归档、stdout 流水行与面板「运行日志」。IP 取 X-Forwarded-For 首段 / X-Real-IP,无代理头时回落 TCP 对端;UA 截断 200 字节。关闭后来源字段留空(IP 属个人信息,共享部署可关)。热生效
cooldown.soft_rate 600s 软限流(429 / 限流文案)冷却基数;同一账号连续触发按 2 倍指数退避
cooldown.soft_rate_max 2h 软冷却指数退避封顶
schedule.checkin_hours [9, 21] 每日本地时区整点签到 + 余额查询解冻。空数组 / null = 未配置回落默认(不是禁用)
schedule.travel_hours [9, 21] 每日本地时区整点推进猫猫旅行状态机(领养 / 派出 / 领奖)
schedule.activity_hours [10] 每日本地时区整点对话活跃上报(点亮连登 + 解锁 first_buddy)
schedule.keepalive_hours [22] 每日本地时区整点刷新 token 保活
schedule.blackcat_hours [23] 每日本地时区整点夜猫子补足(23:00–08:00 计数窗口)
schedule.checkin_enabled true 签到总开关;false 真正关闭
schedule.travel_enabled true 猫猫旅行总开关(独立于签到)
schedule.activity_enabled true 活跃上报总开关
schedule.keepalive_enabled true token 保活总开关
schedule.blackcat_enabled true 夜猫子总开关
upstream.timeout_seconds 120 短 RPC(刷新 / 签到 / 余额 / 模型列表)总时长上限
upstream.header_timeout_seconds 回落 timeout_seconds 聊天首字节前(响应头)上限
upstream.idle_timeout_seconds 300 聊天流中空闲上限(活跃续命,静默断流)
upstream.user_agent 空 出站 User-Agent 覆盖(空 = 现状 CLI/2.63.2 CodeBuddy/2.63.2)。官网「使用端」列按出站 UA 服务端归因;官方 WorkBuddy 桌面 UA 为 WorkBuddy/<version>,需要时可配
features.sanitize_blacklist_fingerprints true 出站请求体黑名单指纹脱敏
prompt.mode custom 系统提示词模式:custom = 网关用自有提示词替换客户端 system;append = 开头连续 system/developer 块后插网关提示词(既有消息逐字不动);passthrough = 透传客户端原始 system(降级重试仍切中性提示词)
prompt.file 空 提示词文件路径;空 = 内置默认(约 2KB);路径非空但不可读 → 启动报错
upstash.url / upstash.token 空 空 = 纯内存模式(Noop 降级,功能照常)
pool.max_in_flight 3 单账号最大在途请求数(0 = 不限)
pool.max_in_flight_global 2 global 域单账号在途上限(国际版 WAF 风控更紧,压低并发)
pool.degrade_threshold 5 连败降权阈值:未知错误(ErrClient/传输层)连败 N 次临时出池
pool.degrade_cooldown / pool.degrade_cooldown_max 10m / 2h 连败降权时长与上限钳制
pool.cost_explore_interval 30m costTier 条件探索窗口:免费层垄断且存在未知号时,每窗口把一个真实请求搭车改道给未知号(零新增上游请求;成功即毕业,失败走既有错误策略)。0 = 关停
pool.credit_floor 100 积分保底:账号余额低于该值时,对实测收费模型(tier 2,账本 6h 内有效观测)不再参与选号——防止收费模型把余额打穿、连免费模型都 402 冷却到次日签到(最坏约 11.5 小时不可用)。tier 0(实测免费)/ tier 1(无观测)不受限:保底保的是「留余额给免费模型用」,且 tier 1 若拦会让账本过期 / 重启清零的触底号死锁在「学不回来」。含会话粘性路径(粘性号触底则解绑换号)。全池触底且全 tier 2 时选号返回空(网关回 503),不放行。签到回血越过 floor 即刻自动恢复。0 = 关闭
pool.breaker_threshold 3 连续失败触发熔断阈值
pool.breaker_cooldown 30m 熔断基础退避时长
pool.breaker_cooldown_max 6h 熔断指数退避封顶
pool.idle_weight_per_hour 0.5 闲置补偿:每小时未使用 +0.5 权重
pool.idle_weight_max 5.0 闲置补偿权重封顶
pool.prefer_expiring true 最早到期优先:窗口内存在快过期积分时,按最近到期时间升序选择账号(同时间剩余积分多者优先)
pool.expiring_soon 168h 快过期路由窗口:仅此窗口内的批次参与最早到期优先;留空或 0 关闭
session_sticky.enabled true 会话粘性路由开关
session_sticky.ttl 30m 会话绑定 TTL(滚动续期)
session_sticky.gc_interval 5m 过期绑定 GC 周期

上游超时语义(三段各归其位)

字段 作用对象 默认 行为
timeout_seconds 短 RPC(token 刷新 / 签到 / 余额 / 模型列表) 120 总时长硬上限,到期报错走换号 / 熔断
header_timeout_seconds 聊天 SSE 首字节前 120 由 Transport.ResponseHeaderTimeout 约束;超时 = 换号重发
idle_timeout_seconds 聊天 SSE 流中空闲 300 活跃吐数据续命不掐;静默超时才断流释放租约

聊天流(stream true / false 均同)没有总时长上限:聊天使用 Timeout=0 的专用 client,长思考 / 长输出不会被掐断。

环境变量覆盖

加载顺序:JSON 文件 → WB2A_* 环境变量(变量非空才覆盖):

WB2A_LISTEN · WB2A_API_KEY · WB2A_AUTH_DIR · WB2A_STATE_FILE · WB2A_MAX_BODY_MB · WB2A_SOFT_RATE(duration) · WB2A_SOFT_RATE_MAX(duration) · WB2A_TIMEOUT_SECONDS · WB2A_HEADER_TIMEOUT_SECONDS · WB2A_IDLE_TIMEOUT_SECONDS · WB2A_USER_AGENT · WB2A_SANITIZE_FINGERPRINTS(bool) · WB2A_PROMPT_MODE · WB2A_PROMPT_FILE

核心行为语义

系统提示词体系

客户端(Claude Code / Codex 等 CLI)会在 system prompt 注入固定模板句,上游内容审核按逐字精确匹配误杀合法流量(HTTP 400 + 审核文案)。网关提供两层防护,互不替代:

  1. 提示词体系(解决 system / developer 来源的误报):由 prompt.mode 控制
  2. 指纹脱敏(兜底 用户 / assistant 消息里的指纹串):由 features.sanitize_blacklist_fingerprints 控制
模式 语义
custom(默认) 出站前用网关自有提示词替换客户端 system / developer 消息(删除全部 system / developer,头部插入单条 system);user / assistant / tool 消息逐字不动
append 开头连续 system / developer 块之后插入一条网关自有 system,既有消息(含客户端项目规范/工具约定)逐字不动——两者并用;降级期退化为 replace(带指纹原文重试只会确定性再撞 400)
passthrough 透传客户端原始 system,不做改写

内置默认提示词约 2KB(internal/prompt/defaultprompt.md,嵌入二进制)。prompt.file 指向自定义提示词文件(自定义人格 / 人设)即整体替换内置默认;留空 = 内置默认,路径非空但不可读 → 启动报错(fail fast,不会静默回落到内置默认)。

内容拦截误报与降级重试

passthrough 模式请求被上游内容策略拦截(HTTP 400 + blocked by security policy / unapproved channel / illegal api invocation 文案)时,判定为 system 指纹误报:同请求内换 Degraded 中性提示词重试一次;第二次仍被拦(用户内容本身触发审核)→ 走既有错误路径返回客户端,并如实报给调用方。

  • 触发降级后持续到次日 00:00 CST(Asia/Shanghai)重置;降级期内 passthrough 请求直达中性提示词,不再先撞 400
  • 降级状态是进程内存态,重启清零
  • 内容问题非账号问题:ErrContentBlocked 不罚账号(无冷却 / 熔断 / 计错),由网关降级重试消化

错误分类与账号处置

上游错误由 Classify 统一分类(判定优先级:余额耗尽 → session 失效 → 限流文案 → 状态码兜底),账号处置如下:

分类 触发条件 账号处置 恢复
余额不足 HTTP 402 / body 含余额关键词 硬冷却到次日 04:00(本地时区) 签到(09/21 点)余额恢复自动解冻
频控 HTTP 429 / 限流文案(不限状态码) 软冷却 soft_rate(600s 起,连续触发指数退避,封顶 soft_rate_max)。code 6004(模型级)带「将在 … 重置」时冷却到上游重置墙钟并豁免切模型(见常见问题) 到期自动恢复 / 成功清零退避
Session 失效 body 含 Offline user session not found / 12153 连续 3 次才永久禁用(一次 12153 多为临时抖动:网络 / 闪断 / refresh 竞态);刷新成功 / 任意成功 / 手工复活清计数 人工重新登录(login.sh)或 ReviveDisabled 复活
上游 404 HTTP 404 软冷却固定 60s(不随 soft_rate、不单独退避) 到期自动恢复
服务端错误 HTTP ≥500 喂连续失败计数,达阈值熔断 熔断到期 / 成功清零
请求体解析失败 HTTP 400 + Unmarshal chat params failed / code 11101 不罚账号,但仍轮转(客户端畸形 JSON,换号照样 400) 即时
内容拦截 HTTP 400 + 审核文案 不罚账号,passthrough 模式走降级重试 即时
客户端错误 其余 4xx / 业务 code≠0 不处罚,换号重试 即时

请求体解析失败(11101)与内容拦截一样不罚账号:问题在请求内容而非账号健康。网关不做请求体截断与预拦截,11101 均为客户端发来的畸形 JSON。

熔断器:所有冷却入口与 5xx 共用唯一连续失败计数器 fails;累计达 breaker_threshold(默认 3)触发熔断,退避 breaker_cooldown × 2^retryCount,封顶 6h;成功清零。

软冷却指数退避(与熔断器并存的第二条升级线):软限流的冷却时长本身也按连续次数退避——同一账号连续触发软冷却时 soft_rate × 2^(连续次数-1),封顶 soft_rate_max。计数 soft_streak 独立于熔断器的 fails,只在成功或签到解冻时清零,随 state.json 持久化。

选号策略

  1. 过滤:禁用 / 冷却 / 熔断 / 在途占满账号不参与

  2. 按模型实测成本分层,只保留当前最优层

  3. 默认开启最早到期优先:

    • 在模型成本最优层内,筛选 expiring_soon 窗口内仍有积分的账号。
    • 按最近到期时间升序排序;同到期时间按该批次剩余积分降序。
    • 已过期、零余额、无有效到期时间的账号不进入优先集。
  4. 优先集为空时退回普通加权随机:

    weight = credits 比例 ×10 + idleWeight

    • credits 比例 = 该号积分 / 候选集最大积分
    • idleWeight = min(闲置小时 × idle_weight_per_hour, idle_weight_max),从未使用给满分
  5. 防惊群:跳过 100ms 内刚被选中的账号;全冷却时从非禁用、非余额耗尽的软冷却 / 熔断账号中选最早到期者顶班

会话粘性

同一会话尽量复用同一账号,多轮对话不跳号:

  • 会话键提取顺序:metadata.conversation_id → metadata.conversationId → metadata.user_id → 顶层 conversation_id → 顶层 conversationId(snake_case 优先于 camelCase)
  • TTL 滚动续期(默认 30m),GC 周期 5m;绑定可镜像到 Redis(7 天 TTL)防重启丢失
  • 请求失败自动解绑;成功后绑定跟随最终成功账号

定时任务

五类任务各自独立排程、各有开关,互不影响。容器时区由 TZ 控制(compose 默认 Asia/Shanghai)。

任务 开关(默认 true) 时刻(默认) 行为
签到 schedule.checkin_enabled checkin_hours [9, 21] 整点 签到 + 余额查询;余额恢复则解冻冷却账号。末尾追加连登管家(见下)
活跃上报 schedule.activity_enabled activity_hours [10] 整点 对话活跃上报(chat_request_send 事件,必须含 userId);点亮连登 + 解锁 first_buddy;每号每天 1 次
猫猫旅行 schedule.travel_enabled travel_hours [9, 21] 整点 独立排程:无猫领养 / idle 派出 / arrived 领奖
保活 schedule.keepalive_enabled keepalive_hours [22] 整点 全账号刷新 token;session 失效连续 3 次才自动禁用
夜猫子 schedule.blackcat_enabled blackcat_hours [23] 整点 先查任务进度再决定:black_cat 未达标才在 23:00–08:00 计数窗口内补足 glm-5.2 短对话(每天 1 次累计 3 天,漏跑次日窗口自动补)

连登管家(签到排程末尾自动执行)

成长中心的连登档位(连续登录 7/14/28 天)兑换后发放积分 / 能量 / 补签卡 / 抽奖次数,抽奖次数只能从兑换获得。管家在每日签到后自动跑一遍闭环(幂等,未解锁静默跳过):

  1. 查连登档位状态 → 已解锁(非 locked / 非 claimed)的档位自动兑换
  2. 查抽奖次数 → 有次数自动全部抽完,奖品记日志(streak-bonus <uid>: 🎲 …)

无需配置,跟随签到排程;到天数那天自动完成「兑换 → 抽奖」,无需人工盯。

关闭定时任务:用 schedule.*_enabled: false 显式关闭(四个都设 false 则调度器不空转,直接阻塞等待退出信号)。注意两点语义:

  • 空数组与 null 表示「未配置 → 回落默认」,不是「禁用」;真正关闭请用 *_enabled: false
  • 禁用不会擦除小时配置:*_hours 原样保留,改回 true 即恢复原时点;小时值必须是 0-23,非法值启动即报错
  • 关签到会把「余额恢复即解冻」一起关掉,被硬冷却的账号只能等次日 04:00 自然到期

活跃上报(独立排程)

对池内每个可用账号在 activity_hours(默认 [10] 整点)发送一条对话活跃上报(事件 chat_request_send,body 为数组,事件必须含 userId):

  • 一条上报同时点亮 growth 连登 + 解锁 first_buddy 任务(领养前置)
  • 每号每天 1 次即可(单时点):日活跃奖励按天去重,重复上报无额外收益
  • conversationId 由网关生成(wb2api-<ms>),无需真实会话
  • 限速:账号间间隔 800ms(与旅行同口径)
  • streak 自检:上报成功后回读连登天数(只读 oracle),日志每号一行可 grep:activity <uid>: streak days=N。days=0 记 warn(report OK but streak.days=0 (silent drop?),对应上游「200 但静默丢弃」);回读失败记 warn 但不影响主流程(上报按天幂等,不重试,只观测)
  • 手动诊断 / 补跑用 python3 scripts/probe_active.py(只读探测;写操作默认 dry-run,需 --yes)

猫猫旅行(独立排程)

对池内每个可用账号在 travel_hours(默认 [9, 21] 整点)单趟推进一次,每趟只做一个动作,不轮询不等待。默认两趟闭环:9 点领昨日到站奖励并派出,21 点领当日到站奖励(daily_limit_reached 自动挡住二次派出)。

探测结果 动作
无猫(buddy 为 null) 先同意协议(幂等),再尝试领养;过门槛则 +300 积分并获得猫
state=idle 且今日未派出 派出 location_id=4(古镇客栈;4 个地点收益 / 时长区间相同,无最优解)
state=arrived 领取到站奖励(带 record_id)
state=traveling / 今日已达上限 / 未知状态 跳过
  • 领养门槛未达标时上游返回 HTTP 400,每账号每自然日只尝试一次(跨日重试,记录仅存内存);门槛可用活跃上报解除
  • 限速:账号间间隔 800ms
  • 每自然日 1 次派出:按 CST(Asia/Shanghai)自然日重置,与容器 TZ 无关
  • 失败隔离:单账号失败只跳过该账号当趟;401 不强刷(token 刷新交保活时点)

API 端点

余额后台刷新(schedule.balance_refresh_enabled,缺省开启):每 balance_refresh_minutes(缺省 5)分钟并发查询全部账号余额并更新池内积分——两次签到时点之间 credits 保持新鲜,余额恢复的冷却账号也会自动解冻(语义同签到,但不做签到不刷 token)。面板「立即刷新」按钮也是全量刷余额;5 秒自动轮询只读内存,不打上游。

🖥️ Web 管理面板

内嵌式管理面板(internal/panel,前端 go:embed 单文件打进二进制,无外部构建依赖),服务启动后访问:

http://127.0.0.1:7863/panel/

鉴权与 API 同口径:api_key 非空时面板要求输入一次密钥(浏览器 localStorage 记住);为空则直接可用。 界面支持明暗主题切换(首次跟随系统偏好,点击按钮两态翻转并记住选择),左侧导航分四个视图:

视图 功能
账号池 统计条(总数/可用/冷却/禁用/可用积分合计/粘性会话)+ 账号表:状态标签(可用/限流冷却/积分冷却/熔断/已禁用)、积分量条、成功失败计数、在途、单号操作(签到/余额/任务/解冻/禁用/移除);批量「全部签到」「旅行巡检」「活跃上报」「全部保活」
添加账号(顶部按钮) 浏览器内完成 OAuth 设备授权(显示授权链接 + 自动轮询),登录后凭证落盘并热加载进池,免重启
积分任务(账号行内「任务」按钮) 展示全部任务(进度 / 奖励分数与能量 / 状态);「全部接受」批量报名;「一键完成」覆盖 17 个任务(推进进度 + 异步计分等待 + 自动领奖,幂等可重复点);其余任务展示操作指引
模型与档位 实时查询上游:每模型的积分倍率、默认思考档、支持的档位(含「off(可关)」)、上下文长度与最大输出;若存在探测数据,最大输出列显示实测上限与钳制告警(见「探测模型真实输出上限」)。列表支持按条件查询:关键词(ID / 名称 / 描述 / 厂商,空格分词 AND)、域(CN / Global)、能力(工具 / 视觉 / 思考 / 默认)、思考档位、价格(折扣 / 限时免费 / 打折),以及按倍率、上下文、最大输出、ID 排序
用量 指标卡(请求数 / 总 token / prompt / completion / 失败 / 平均延迟)+ Token 时序图(渐变柱、均值线、峰值标注)+ 用量明细(按账号 / 模型 / 域切换)+ 积分扣除历史。时间范围支持 今天 / 近 24 小时 / 近 3 天 / 近 7 天 / 近 30 天 / 全部历史 / 自定义区间(精确到分钟),卡片、表格与图表全部按同一窗口统计
配置 在线编辑 config.json:API 密钥、定时任务(四类任务时点与开关、余额刷新间隔)、账号池与流量治理参数、上游超时与 UA、提示词模式、脱敏/粘性开关、日志来源记录开关
运行日志 请求记录表(时间 / 结果 / 模型 / 账号 / 来源 IP / User-Agent / 耗时 / Token / 积分 / 请求 ID),支持关键词与结果筛选、读取条数,以及 今天 / 近 24 小时 / 近 7 天 / 自定义区间等时间范围查询(由归档侧按时间取,不是筛已拉取的条目)+ 最近 500 行服务日志(按任务 / 对话 / 系统分频道,可开关自动滚动)

配置热生效:保存配置后,api_key、cooldown.soft_rate、features.sanitize_blacklist_fingerprints、 logging.request_client_info、pool.*(熔断/在途/权重)、schedule.*(时点/开关/余额刷新间隔)立即生效,无需重启; 涉及进程装配期依赖的字段(listen、auth_dir、state_file、upstream.*、upstash.*、session_sticky.ttl) 保存后会提示"需重启进程生效"。配置写入采用「深合并且原子替换」:只更新面板表单覆盖的键, 用户手写的未知键与其余字段原样保留。

顶部「刷新」按钮 = 向上游全量查询真实余额并回写(5 秒自动轮询只读内存,不打上游)。

面板后端接口挂在 /panel/api/*(同一 Bearer 鉴权),可脚本化调用;账号运维操作均落到池既有入口(Revive/Disable/Remove 等),与 /status 观测口径一致。

安全响应头:面板页面与全部 /panel/api/* 响应统一带 Content-Security-Policy(default-src 'none',脚本仅同源,frame-ancestors 'none' 禁嵌套)、X-Content-Type-Options: nosniff、X-Frame-Options: DENY、Referrer-Policy: no-referrer 等;前端脚本独立为同源 app.js,不含内联脚本与内联事件处理器。

鉴权实现:internal/httpauth 统一 server 与 panel 的 Bearer 校验,使用 SHA-256 摘要 + subtle.ConstantTimeCompare 常量时间比较(避免逐字节比较泄露密钥信息);上游返回的 uid 经白名单校验([A-Za-z0-9_-],长度 ≤64)后才用于拼凭证文件名,防止路径穿越。

⚠️ 公网部署提示:服务自身只提供明文 HTTP,请务必置于 HTTPS 反向代理之后(Nginx/Caddy 等)并配置访问限流;仅本机或私有网络使用时可直接运行。

🔌 API 端点

端点 鉴权 说明
POST /v1/chat/completions Bearer(api_key 非空时) OpenAI 兼容补全;流式/非流式;请求体上限 8 MiB
GET /v1/models Bearer(api_key 非空时) 模型列表(纯动态拉取,缓存 1h;失败返回空列表 + 5min 负缓存);每模型带 context_length/max_output_tokens(四级查找链:上游目录 → 内置知识表 → model.json 缓存 → models.dev)、reasoning_supported_efforts/reasoning_default_effort 思考档位及描述/标签/倍率等全字段(上游有返回时)
GET /status Bearer(api_key 非空时) 账号状态汇总 + 每账号详情(积分/冷却/熔断/在途/粘性)
GET /healthz 无 健康检查:有 healthy 且未占满账号返回 200,否则 503;响应带身份标识(见下)

鉴权规则:仅当 api_key 非空才校验 Authorization: Bearer <api_key>;api_key 为空时上述端点直接放行;/healthz 恒无鉴权。

/healthz 响应示例(200 / 503 同结构,仅状态码与计数变化):

{"healthy": 2, "total": 3, "service": "workbuddy2api"}

响应同时带 X-Service: workbuddy2api 头。这两个身份标识用于区分本网关与同端口上可能残留的其他服务——对方即使返回 2xx 也不会带该字段 / 头,宿主探测据此避免"假成功"。

宿主健康探测指引:强校验(推荐)用 /status + api_key——只有持有正确 api_key 的本网关返回 200,其他服务返回 401 / 404;弱校验(不适合持 key 的负载均衡器)用 /healthz + service 字段判据(/healthz 恒无鉴权,service == "workbuddy2api" 才算命中本网关)。容器自带 HEALTHCHECK 用的就是弱校验(仅进程内自检,够用)。

流式行为细节

  • 出站请求强制 stream:true;SSE 帧按 OpenAI 规范白名单重建(reasoning_content 保留、工具调用按 index 合并、未知字段剥离)
  • 保证恰好一个 data: [DONE](上游漏发时兜底补写);空流先写一帧 error 再补 [DONE]
  • 非流式请求由本地聚合完整 SSE 流为单 chat.completion 响应(含 reasoning_content / tool_calls)

上游端点

上游接口均为 CodeBuddy 官方 CLI / 插件使用的非公开 / 逆向接口,未见公开 API 文档;路径及 Host 以代码内常量为准(见文末出处表)。两类 base:

  • copilot.tencent.com:聊天补全(SSE)、token 刷新、OAuth、模型列表、growth 域(旅行 / streak)
  • www.codebuddy.cn:每日签到、余额查询、活跃上报
相对路径(绝对路径见出处表) 方法 用途
chat/completions POST 聊天补全(SSE)
console/enterprises/personal/models GET 动态模型列表
plugin/auth/token/refresh POST token 刷新
billing/meter/daily-checkin POST 每日签到
billing/meter/get-user-resource POST 余额查询
report POST 对话活跃上报(chat_request_send 事件数组,必须含 userId;点亮连登 / 解锁领养)
plugin/auth/state?platform=CLI POST OAuth 取授权 URL
plugin/auth/token?state= GET OAuth 轮询取 token
plugin/login/account?state= GET OAuth 取账号信息
activity/growth/buddy/agreement POST 猫猫旅行:同意协议(幂等)
activity/growth/buddy/first POST 猫猫旅行:首次领养
activity/growth/buddy/info GET 猫猫旅行:查询猫档案
activity/growth/buddy/travel/status GET 猫猫旅行:旅行状态
activity/growth/buddy/travel/depart POST 猫猫旅行:派出
activity/growth/buddy/travel/claim POST 猫猫旅行:领奖
activity/growth/streak GET 连登天数 + 兑换档位状态(活跃自检 / 连登管家)
activity/growth/redeem POST 连登档位兑换({tier, client_token};未解锁 403)
activity/growth/lottery/summary GET 抽奖次数查询
activity/growth/lottery/draw POST 抽奖一次({client_token},消耗 1 次)
activity/growth/tasks GET 任务列表(含 reward_credit/reward_energy/progress)
activity/growth/tasks/accept POST 接受任务({"task_codes":[...]})
activity/growth/tasks/<task_code>/claim POST 领取任务奖励(任务码在路径、无 body;Web 域 www.workbuddy.cn,非 CLI 域——这是领奖能成功的关键)

出站请求统一携带 CLI/2.63.2 CodeBuddy/2.63.2 UA(可被 upstream.user_agent 覆盖);聊天请求带账号头(X-User-Id 等),永不携带 X-Refresh-Token(该头只出现在 token 刷新请求)。领奖请求额外带 x-client-platform: web 与 workbuddy.cn 的 Origin/Referer。

请求级日志

每个 /v1/chat/completions 请求结束时输出一行表格日志(stdout):

| #001 | 18:31:31 | deepseek-v4 | stream | 200 | uid=0851ce35 | TTFB=801ms | tok=60 | 23.5tok/s | total=2.6s |
字段 说明
#001 进程级请求序号
18:31:31 结束时刻
deepseek-v4 模型名(超 11 字符截断)
stream / sync 请求模式
200 状态码
uid=0851ce35 账号 UID 前 8 位
TTFB 流式首帧耗时(非流式为 -)
tok / tok/s / total 输出 token 数 / 速率 / 总时长

敏感度:日志不含任何 token 明文(详见安全与合规),无落盘日志文件。

部署运维

Docker 镜像

多阶段镜像(golang:1.23-alpine 构建 → alpine:3.20 运行)一次编译全部四个二进制并随镜像分发:

  • wb2api(主服务)、signin_bin、login、credit + 脚本(login.sh / signin.sh / credit.sh / scripts/probe_active.py)
  • 以 app 用户(uid 10001)运行,app/auths 与 app/data 预建
  • 镜像内默认落 config.example.json 作为空配置(不含密钥),生产用挂载卷覆盖 /app/config.json
  • 内置 HEALTHCHECK(wget /healthz,30s 间隔)

账号 / 数据通过 docker-compose.yml 卷挂载持久化:./auths、./data、./config.json。

工具脚本

脚本 用途
./login.sh OAuth 登录 → 落盘 auth → 重启容器
./signin.sh [auths_dir] 批量签到(过期先刷新)
./credit.sh / ./credit.sh -json 积分日报(美化 / 原始 JSON)
python3 scripts/probe_active.py 活跃上报手动诊断 / 补跑(probe=只读 / report=单号上报 / unlock=单号领猫 / ALL=全池;写操作默认 dry-run,需 --yes)
python3 scripts/probe_max_tokens.py 探测各模型真实输出上限(区分静默钳制与模型主动收尾),--panel-out 结果可直接进面板展示(见下节)

二进制不在 git 中:脚本首次使用自动 go build 对应 cmd/*(Docker 镜像内已预编译)。

探测模型真实输出上限

上游 /v3/config 里的 max_output_tokens 是声称值,普遍虚高:实测 16 个 CN 模型中 8 个被 静默钳制(请求 max_tokens 更大也不报错,输出到真实上限即截断),最狠的声称 1M 实际 32K。 「模型与档位」视图因此支持在最大输出列叠加实测标注:

  • 🔴 32K ⚠ 钳制 12× —— 实测被截断于 32K,声称值的 1/12(finish=length 判据,可信)
  • 🟢 48K ✓ / 64K ↑ —— 实测与声称一致 / 实际比声称更大
  • ⚪ ≥40K / ? —— 满额未触顶(下界)/ 模型主动收尾未测出

实测值不写死在代码里——它来自探测工具写入的数据文件,上游调整后重跑一次即自动刷新:

# 在网关所在机器上(探测会真实消耗积分;单模型预算默认 600s,并行 4)
python3 scripts/probe_max_tokens.py   --base http://127.0.0.1:7863/v1 --key sk-xxx   --panel-out data/output_probes.json

# 断点续测 / 只测指定模型 / 预览计划
... --resume
... --models cn:glm-5.2 --panel-out data/output_probes.json
... --dry-run

文件落在 state 文件同目录(默认 data/output_probes.json,data/ 已被 gitignore),面板 GET /panel/api/model_probes 只读透传,写入后下次查询即生效,无需重启网关;未探测的 模型不受影响。探测判据(两种停止的区分 / 提示词量级匹配 / 并行与时间预算)的设计细节 见脚本头部注释。

账号管理

  • 多账号复制 auths/workbuddy-<uid>.json 即可,池启动时自动对齐目录
  • Session 失效账号被禁用(disabled_reason 透出在 /status)后,可用 ./login.sh 重新登录覆盖凭证;已持久化 disabled=true 的账号可在源码侧调用 Pool.ReviveDisabled(uid) 复活(state.json 中清除 disabled 标志)
  • 备份 = auths/(凭证)+ data/state.json(池状态:积分 / 冷却 / 计数);配置 Upstash 后状态另镜像至 Redis(7 天 TTL)

安全与合规

1. 凭据管理(auths)

  • 位置:./auths(auth_dir 可配),文件名 workbuddy-<uid>.json
  • 内容:明文 accessToken / refreshToken + 账号元信息(account.uid / enterpriseId / nickname)
  • 权限:容器内以 app 用户(uid 10001)运行;token 刷新由 SaveAtomic 以 0600 原子写回(tmp + rename);login.sh 首次落盘遵循登录 umask,建议手动 chmod 600 auths/*.json
  • 切勿提交 git:.gitignore 已排除 auths/、data/、backups/、config.json、*.key、*.pem、*.env、docs/ 及除 README 外的全部 *.md 工作文档

2. 网络暴露与日志敏感度

  • 默认监听 :7863,compose 暴露 0.0.0.0:7863,无内置 TLS;公网部署必须设置 api_key,建议前置反代 / 内网
  • 请求日志字段:序号 / 模型 / 模式 / 状态码 / uid 前 8 位 / TTFB / token 数——不含 accessToken / refreshToken / api_key 明文(不读取 Authorization 头)
  • 日志写 stdout / stderr(容器内进入 docker logs),代码无任何落盘日志文件

3. 发布来源与合规边界

  • 无预编译 release:仓库无 Release / tag,产物 = 源码自构建(Dockerfile 多阶段在本地构建时完成)
  • 登录 / 签到 / 积分工具:./login.sh / ./signin.sh / ./credit.sh
  • 无产物校验和:go.sum 仅约束 Go 模块依赖;Docker 镜像由本地 docker compose build 生成,未引用第三方镜像
  • 上游 CodeBuddy 属腾讯系商业产品,本项目是其非官方 OpenAI 兼容网关;使用其账号做 API 网关涉及目标平台服务条款与账号风险,作者不对账号封禁、条款违约或使用结果负责

4. 授权使用边界

  • 仅限本人授权账号、本机 / 私有环境测试
  • 不得共享、转售、违规分发,或用于违反目标平台条款的用途;严禁批量注册账号做 API 业务、二次加壳或捆绑卡密售卖——详见顶部使用声明(必读)
  • 遵守 CodeBuddy 平台服务条款与所在地法律
  • 妥善保管 auths/(明文凭证)与网关端口

常见问题

429 code=6004(模型级限流)的冷却语义?

上游 429 + code 6004 是该模型的使用量超限(msg 通常带「将在 YYYY-MM-DD HH:MM:SS UTC+8 重置」),不是账号整体被限流。网关的处理:

  • 冷却到上游重置时间:msg 带「将在 … 重置」时,账号冷却 until 精确等于该墙钟(按 UTC+8 解释),并封顶 soft_rate_max(默认 2h)
  • 切模型立即可用:冷却由 6004 触发时会记录触发模型;同一账号改用其他模型请求时视为可用。同模型或未记录模型的冷却回到现状
  • 退回指数退避:6004 无「将在 … 重置」文案,或非 6004 的普通软限流 → 仍是 soft_rate(600s 起,连续触发指数退避,封顶 soft_rate_max)

多图会话请求体超限怎么办?

网关不再设请求体上限(server.max_body_mb 已移除,对齐上游):任意大小的请求体都会完整读入并转发上游,超限类问题由上游自然返回错误——其响应信息量更大(能看到上游的真实策略),网关不再以 413 提前拦截。

  • 多图/长上下文会话(历史图片每轮以 base64 重发,编码再膨胀约 37%)不会再撞网关侧 413
  • 若上游真的返回 413/超限错误,网关按既有错误分类链路如实透传(不打码、不罚号——超限是请求侧问题)
  • 客户端中途断流导致的半截 body 在读入阶段即报 400 invalid_request,不会把截断 JSON 喂给上游(issue #41 语义保留在读错误路径)

Docker 部署登录后报「写入 auths/…json.tmp 失败: permission denied」?

容器以 app 用户(uid 10001)运行,而宿主机挂载的 ./auths、./data 目录属主不是它——写凭证 tmp 文件被拒。三种解法任选(前两种均无需 root 容器):

# 方案 1(推荐,非 root):让容器以你自己的 uid 运行——挂载目录本来就是你建的
PUID=$(id -u) PGID=$(id -g) docker compose up -d --force-recreate
# 或写进 .env 文件长期生效(.env 已被 .gitignore 忽略):
#   echo "PUID=1000" > .env && echo "PGID=1000" >> .env

# 方案 2:把挂载目录属主交给容器默认用户(需要 sudo)
sudo chown -R 10001:10001 ./auths ./data ./config.json

# 方案 3:compose 设 user: "0:0" 以 root 运行(NAS/群晖不便 chown 时用)

报错信息里自带这条指引;compose 的 user 已参数化为 ${PUID:-10001}:${PGID:-10001}。

账号被 Disable 后如何恢复?

  • 用 ./login.sh 重新登录覆盖凭证,重启后自动回池;
  • 或源码侧调用 Pool.ReviveDisabled(uid) 清除 disabled 状态(state.json 同步刷新)。

系统提示词被内容策略误杀怎么办?

默认 prompt.mode=custom 已用网关自有提示词替换客户端 system,从源头消除大部分误报;用户 / assistant 消息中的指纹串由 features.sanitize_blacklist_fingerprints 清洗,两层叠加。passthrough 模式下首遇拦截会自动换 Degraded 中性提示词同请求重试一次。

如何让官网「使用端」列显示为 WorkBuddy?

官网「使用端」列按出站请求 UA 服务端归因。配置 upstream.user_agent: "WorkBuddy/2.x.x"(或环境变量 WB2A_USER_AGENT)即可改写全部出站请求的 UA;默认保持 CLI/2.63.2 CodeBuddy/2.63.2 现状(指纹净化考虑,可配而非改死)。

关键断言 ↔ 代码出处

断言 出处
prompt.mode 默认 custom cmd/server/config.go:148
请求体无网关侧上限(max_body_mb 已移除) internal/server/handler.go chatCompletions 读 body 段
出站强制 stream:true internal/upstream/payload.go:28
DeepSeek 思维链注入(thinking.type=enabled) internal/upstream/thinking.go:110
默认 reasoning_effort 档位 = high internal/upstream/thinking.go:32
reasoning_content 多轮回填(assistant 消息) internal/upstream/thinking.go:54
Degraded 中性提示词常量 internal/prompt/prompt.go:25
降级触发与次日 00:00 CST 重置 internal/server/degrade.go:30(Trigger)、:46(nextMidnightCST)
6004 模型级限流 code 与重置时间解析 internal/upstream/client.go:127、internal/upstream/client.go:147
11101 / Unmarshal 失败不罚号 internal/upstream/client.go:114-115;处理分支 internal/server/handler.go:489
出站 UA 覆盖(空 = 现状 CLI/2.63.2 CodeBuddy/2.63.2) cmd/server/config.go:73;接线 cmd/server/main.go:96
session-dead 连续阈值 3 才禁用 internal/pool/pool.go:249-253(sessionDeadThreshold)
ReviveDisabled 人工复活 internal/pool/pool.go:951
disabled 账号透出 disabled_reason internal/pool/pool.go:1162-1165
硬冷却至次日 04:00 internal/pool/pool.go:882(CooldownUntilTomorrow4AM)
软冷却退避封顶 2h internal/pool/pool.go:247(defaultSoftRateMax)
Top-5 候选短名单 internal/pool/pool.go:584
activity_hours 默认 [10] cmd/server/config.go:135
活跃自检回读 streak internal/scheduler/scheduler.go:227(checkActivityStreak)
streak 端点 activity/growth/streak internal/upstream/travel.go:24(常量)、:139(GrowthStreak)
Redis 粘性镜像 7 天 TTL internal/redisstore/redisstore.go:21
静态模型表含 deepseek-v4-flash 等 internal/server/handler.go:146

免责声明

本项目仅供学习和研究使用。使用者需遵守 CodeBuddy 服务条款,自行承担使用风险(包括账号封禁、条款违约等)。作者不对任何因使用本项目产生的直接或间接损失负责。

License

本项目采用 MIT License 开源协议。

  • 允许任意使用、复制、修改、合并、发布、分发、再授权及销售
  • 再分发(源码或二进制形式)时,请保留原仓库的 MIT 版权声明与许可声明(如在 NOTICE 或 README 中注明原始出处 https://github.com/Sliverkiss/workbuddy2api)
  • 本项目不授予任何上游(CodeBuddy / 腾讯)接口或服务的权利;使用者仍需自行遵守上游服务条款