薅满火山引擎每天数百万免费 Tokens:我写了一个自动轮换代理

前言

火山引擎方舟平台对每个模型每天提供 50 万 tokens 的免费额度,而且平台上有大量免费模型可用------不只是豆包系列,还有 DeepSeek、GLM 等第三方模型。配置的模型越多,每天能白嫖的额度就越多。

但问题来了:

  • 手动切换模型太麻烦
  • 用着用着某个模型额度就没了,请求直接报错
  • 本地用 Codex、Cursor 这些工具时,没法动态切换模型
  • 模型那么多,谁记得住哪个用完了哪个没用完?

于是我写了 VolcProxy,一个 OpenAI 兼容的代理服务,自动在多个免费模型之间轮换,额度用完无感切换,把免费 tokens 吃干抹净。

项目已开源:github.com/talkcozy/vo...


火山引擎免费模型(部分)

以下是我常用的几个最新模型,实际平台上还有更多免费模型可以配置:

模型 每日免费额度 特点
doubao-seed-2-0-code-preview-260215 50万 编程专用,代码生成质量高
doubao-seed-2-0-pro-260215 50万 综合能力强
doubao-seed-2-0-mini-260215 50万 轻量快速
doubao-seed-2-0-lite-260215 50万 轻量版
doubao-seed-1-8-251228 50万 上一代主力模型
deepseek-v3-2-251201 50万 DeepSeek V3
glm-4-7-251222 50万 智谱 GLM-4

光这 7 个就有 350 万 tokens/天,实际可配置的远不止这些。你可以在模型列表中查看所有支持免费额度的模型,加到配置里就能自动轮换。

这些模型都支持 OpenAI 兼容的 /v1/chat/completions 接口,鉴权方式也很简单,Bearer Token 即可。


设计思路

核心需求就三个:

  1. 透明代理:对外暴露 OpenAI 兼容接口,客户端无需关心后面用的是哪个模型
  2. 自动轮换:按优先级选模型,额度用完自动切下一个
  3. 用量追踪:实时统计每个模型的 token 消耗,提供 Web 面板查看

架构很简单:

scss 复制代码
Codex / Cursor / aider / 任意 OpenAI 兼容客户端
                    ↓
            VolcProxy (:9088)
          ┌─────────┼─────────┐
          │         │         │
     模型选择器  额度管理器  Web面板(:9089)
          │         │
          └────┬────┘
               ↓
        火山引擎 API

核心实现

项目用 Go 写,单二进制部署,无外部依赖(SQLite 用的纯 Go 实现)。

模型选择器

最核心的逻辑其实很简单------遍历优先级列表,找到第一个没用完额度的模型:

go 复制代码
func (s *Selector) Select(ctx context.Context) (string, *apikey.Key, error) {
    key, err := s.keyPool.Get()
    if err != nil {
        return "", nil, err
    }
    for _, m := range s.models {
        if !s.quotaMgr.IsExhausted(m) {
            return m, key, nil
        }
    }
    return "", nil, errors.New("all models exhausted for today")
}

调用失败时自动跳到下一个模型重试:

go 复制代码
func (s *Selector) SelectNext(ctx context.Context, failedModel string) (string, *apikey.Key, error) {
    // 从失败模型的下一个开始找
    // 找不到就 wrap around 从头找
    // 全部用完返回错误
}

额度追踪

火山引擎的 API 响应里自带 usage 字段,每次请求都会返回本次消耗的 token 数:

json 复制代码
{
  "usage": {
    "prompt_tokens": 100,
    "completion_tokens": 200,
    "total_tokens": 300
  }
}

我们只需要从响应里提取这个字段,累加到本地计数器就行。流式请求需要设置 stream_options.include_usage = true,代理会自动注入这个参数:

go 复制代码
if isStream {
    so, _ := reqMap["stream_options"].(map[string]interface{})
    if so == nil {
        so = make(map[string]interface{})
    }
    so["include_usage"] = true
    reqMap["stream_options"] = so
}

计数器持久化到 SQLite,服务重启不丢失。每天 0 点(Asia/Shanghai)自动重置。

失败自动切换

不只是额度用完才切换。如果火山引擎返回了 429、quota exceeded 等错误,代理会立即标记该模型为已满,然后重试下一个模型:

go 复制代码
func isQuotaError(errStr string) bool {
    lower := strings.ToLower(errStr)
    return strings.Contains(lower, "quota") ||
        strings.Contains(lower, "rate limit") ||
        strings.Contains(lower, "429") ||
        strings.Contains(lower, "insufficient") ||
        strings.Contains(lower, "exceeded")
}

这样即使本地计数不准(比如有其他地方也在用同一个 API Key),也能正确处理。

流式转发

流式响应是 SSE 格式,代理逐行读取上游响应,原样转发给客户端:

go 复制代码
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
    line := scanner.Text()
    fmt.Fprintf(w, "%s\n", line)
    if line == "" {
        flusher.Flush()  // 每个 chunk 结束时 flush
    }
}

最后一个 chunk 里提取 usage 信息用于计数。


使用方式

最简单:直接当 OpenAI 代理

bash 复制代码
# 构建
make build

# 配置(复制模板,填入你的火山引擎 API Key)
cp config.example.yaml config.yaml

# 启动
./bin/volcproxy start

然后设置环境变量:

bash 复制代码
export OPENAI_BASE_URL=http://localhost:9088/v1
export OPENAI_API_KEY=sk-any  # 随便填,代理不校验

Codex、aider、Cursor 等工具直接就能用了。

进阶:对接 New API,让 Claude Code 也能用

Claude Code 用的是 Anthropic 格式,不能直接连 OpenAI 兼容接口。但如果你有 New API,可以把 VolcProxy 作为一个渠道接入:

css 复制代码
Claude Code → New API (Anthropic→OpenAI 格式转换) → VolcProxy → 火山引擎

在 New API 后台添加渠道:

字段
类型 OpenAI
Base URL http://<volcproxy地址>:9088
密钥 sk-any
模型映射 claude-sonnet-4-20250514doubao-seed-2-0-code-preview-260215

这样 Claude Code 请求 claude-sonnet-4-20250514 时,实际走的是火山引擎的免费模型。

Web 管理面板

启动后访问 http://localhost:9089,可以实时查看:

  • 每个模型的用量和剩余额度
  • 进度条直观展示(绿色→黄色→红色)
  • API Key 管理
  • 最近 7 天使用趋势

服务器部署

Go 的好处就是交叉编译一把梭:

bash 复制代码
# 编译 Linux 版本
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/volcproxy-linux ./cmd/volcproxy

# 上传
scp bin/volcproxy-linux your-server:~/volcproxy/volcproxy

# 启动
ssh your-server "~/volcproxy/manage.sh start"

项目自带 manage.sh 管理脚本,支持 start/stop/restart/status/log。

用 crontab 设置开机自启:

bash 复制代码
@reboot cd ~/volcproxy && nohup ./volcproxy start > volcproxy.log 2>&1 &

一些细节

为什么不用 CGO?

最初用的 go-sqlite3(CGO),交叉编译很麻烦。后来换成了 modernc.org/sqlite(纯 Go 实现),CGO_ENABLED=0 直接编译出静态二进制,扔到任何 Linux 机器上就能跑。

为什么不直接调管控面 API 查用量?

我研究过火山引擎的管控面 API(GetUsage),确实能查到历史用量。但它是按 Endpoint 维度聚合的,而我们是通过模型名直接调用,维度对不上。而且有几分钟延迟,不适合做实时额度判断。

最终方案是从每次响应的 usage 字段实时累计,最准确也最简单。

额度缓冲

每个模型 50 万 tokens 的限额,我没有设任何缓冲。原因是:即使本地计数到了 50 万,实际可能还没用完(因为火山引擎的计费可能有细微差异)。真正用完时火山引擎会返回错误,代理检测到后立即切换,不会丢请求。


项目结构

bash 复制代码
volcproxy/
├── cmd/volcproxy/main.go        # CLI 入口
├── internal/
│   ├── config/config.go         # 配置管理
│   ├── proxy/handler.go         # 代理处理器
│   ├── model/
│   │   ├── selector.go          # 模型选择器
│   │   └── client.go            # 火山引擎 API 客户端
│   ├── quota/manager.go         # 额度追踪
│   ├── apikey/pool.go           # API Key 池
│   └── web/                     # Web 管理面板
├── pkg/db/sqlite.go             # SQLite 封装
├── config.example.yaml          # 配置模板
├── manage.sh                    # 管理脚本
└── Makefile

总共不到 2000 行 Go 代码,没有任何花哨的框架,标准库 net/http 搞定一切。


总结

特性 说明
每日免费额度 数百万 tokens(取决于配置的模型数量)
接口兼容 OpenAI /v1/chat/completions
模型轮换 按优先级自动切换,失败自动重试
额度追踪 实时统计,SQLite 持久化
管理面板 Web UI,10 秒自动刷新
部署方式 单二进制,零依赖

如果你也在用火山引擎的免费额度,或者想给 Codex/Cursor 接一个免费的模型后端,可以试试这个项目。

GitHub:github.com/talkcozy/vo...

欢迎 Star 和 PR。

相关推荐
lpfasd1231 小时前
2026年第17周科技社区趋势周报
人工智能·科技
IT_陈寒1 小时前
SpringBoot配置加载顺序把我坑惨了
前端·人工智能·后端
集和诚JHCTECH1 小时前
BRAV-7120加持,让有毒有害气体无处遁形
大数据·人工智能·嵌入式硬件
机械X人2 小时前
Encoder-Decoder PLM
人工智能·深度学习
小锋java12342 小时前
天天说的 Agent,到底是啥???
人工智能
阿里云大数据AI技术2 小时前
MaxFrame 视频帧智能分析:从视频到语义向量的端到端分布式处理
人工智能·python
大模型任我行2 小时前
谷歌:大模型规划最优性超越传统算法
人工智能·语言模型·自然语言处理·论文笔记
两万五千个小时2 小时前
为什么你的 Agent 读了文件,却好像什么都没读到?
人工智能·程序员·架构
淘矿人2 小时前
从0到1:用Claude启动你的第一个项目
开发语言·人工智能·git·python·github·php·pygame