English | 简体中文
一个 Go 单二进制:在你的 claude、codex 或任何编码智能体 CLI 和上游 API 之间,按项目目录硬性限额,本地运行、无后台守护、无任何遥测。
不到一年,编码智能体 CLI 从"每轮按一下回车"变成了通宵跑的自动循环,可计费模型并没有跟上。一份不小心留下的 .env 就能把 $187 的请求路由到错的 Anthropic 账号(这条 476↑ 的 PSA 帖子 就是这个仓库的起点)。通宵的 agentic loop 把 Max 套餐烧穿。6 月 15 日的 --print 计费变更,把跑了好几个月的脚本从"订阅"一夜之间挪进了"按 credit 计费"。
AgentFuse 就是那条 PSA 想要的总闸:一个轻量本地代理,读取项目根目录的 .fuse.toml,累计花费一旦触顶就直接 fail-closed——下一个请求根本不会离开你的机器。无后台守护、无云端、无遥测,用 strings 30 秒就能审计完整二进制。
fuse run <cmd> 把你的智能体 CLI 作为子进程 exec,并把它的 base URL 改写到一个随机的 127.0.0.1 端口——也就是 AgentFuse 代理。每个请求都要过四道 fail-closed 闸:账号守卫(对照 ~/.fuse/accounts.toml 做指纹匹配)、tiktoken 请求前预估、对照 cap_usd 检查 ledger,最后 ALLOW → 转发到配置的 provider 或 DENY → HTTP 402。每个响应的 usage 会按 (project, day) 写进本地 bbolt ledger。除了你指定的那次上游调用,没有任何东西离开这台机器。
# 1. 安装(需要 Go 1.24+)
go install github.com/SuperMarioYL/agentfuse/cmd/fuse@latest
# 2. 在项目下写一份默认 $5 上限的配置
cd ~/code/myproj && fuse init
# 3. 用代理跑你的智能体 CLI
fuse run claude触发上限时的标准输出示例
$ fuse run claude
agentfuse: proxy on 127.0.0.1:54021 → api.anthropic.com (account=personal, cap=$5.00 project)
agentfuse: forwarding child stdio …
[claude 会话正常输出一段时间 …]
agentfuse: budget exceeded for project /Users/you/code/myproj ($5.02 / $5.00) — raise with: fuse cap +5
claude: API request denied (HTTP 402): budget exceeded. exiting.
$ fuse status
project: /Users/you/code/myproj
account: personal
cap: $5.00 (project window)
today: $0.4317 (12 req, 145020 in / 9802 out tokens)
project: $5.0231 (318 req)
remaining: $0.0000 ✗ over cap — next request will be denied
↑ 终端实录,由 CI 用 vhs 渲染 docs/demo.tape(每次打 release tag 时自动重新生成)。覆盖:fuse init → fuse run → 跑飞的循环 → 触顶返回 HTTP 402 → fuse cap +5 → 继续。
v0.1 只支持 Anthropic + OpenAI。v0.2 把同一把熔断器扩展到 5 个 provider 家族——同一个二进制、同一个 fuse run、同一份 .fuse.toml——并通过 tiktoken-go 在本地重新分词,把 v0.1 那个"流式响应不带 usage"的漏洞堵上。
| Provider | .fuse.toml 里的 provider |
Wire 格式 | usage 来源 |
|---|---|---|---|
| Anthropic | "anthropic"(默认值) |
Messages API + SSE usage 事件 |
上游 usage 区块 |
| OpenAI | "openai" |
Chat Completions + include_usage |
上游 usage 区块 |
| Google Gemini | "gemini" |
:generateContent / :stream… |
usageMetadata → tiktoken 兜底 |
| DeepSeek | "deepseek" |
OpenAI-compat 形态 | 最终 SSE usage(即使没开 include_usage 也有) |
| OpenAI-compat | "openai_compat" + upstream_url |
任何 OpenAI 形态上游 | 有就用上游 usage,没有就 tiktoken 兜底 |
例:把项目挂到 Groq 的 Llama-3.1,$5 上限。
# .fuse.toml
cap_usd = 5.0
provider = "openai_compat"
upstream_url = "https://api.groq.com/openai/v1"
account = "personal"fuse run aider --model llama-3.1-70b
# fuse: proxy on 127.0.0.1:51234 — provider=openai_compat upstream=api.groq.com cap=$5.00
# … 正常用 … 触顶时的行为和 v0.1 一模一样三份开箱即用的示例配置放在 examples/:.fuse.toml.gemini、.fuse.toml.deepseek、.fuse.toml.openai-compat。
assets/prices.toml 在编译期通过 //go:embed 打进二进制(2026-05 快照)。如果你想覆盖某个 (provider, model) 的价格,不用重新编译,直接写一份 ~/.fuse/prices.toml:
# ~/.fuse/prices.toml —— 按 (provider, model) 维度覆盖,用户键永远赢。
[openai_compat."llama-3.1-70b"]
input_usd_per_1k = 0.00079 # 你协商到的价
output_usd_per_1k = 0.00099二进制永远不从网络拉价格——这是刻意为之、不可妥协的设计。价格过期是用户的问题;一个会偷偷联网的熔断器是信任的问题。
项目根目录放一份 .fuse.toml。AgentFuse 会从 cwd 一级一级往上找最近的那一份。
| 键 | 类型 | 默认值 | 含义 |
|---|---|---|---|
cap_usd |
float |
必填 | USD 硬上限。请求前的预估也会计入这条线,单次特别大的请求绝不会"漏"过去。 |
window |
string |
"project" |
"project"(从 fuse init 起累计)或 "daily"(UTC 零点滚动)。 |
account |
string |
"" |
~/.fuse/accounts.toml 中的命名账号。设置后,AgentFuse 会拒绝指纹对不上的 inbound key,并把正确的那把 key 注入子进程,把误读到的 .env key 直接盖掉。 |
provider |
string |
"anthropic" |
取值之一:"anthropic" / "openai" / "gemini" / "deepseek" / "openai_compat"。v0.2 新增。 |
upstream_url |
string |
对应 provider 默认 | 覆盖上游主机。provider = "openai_compat" 必填;其它 provider 可选(例如把 Gemini 指到 Vertex AI)。 |
账号本身放在 ~/.fuse/accounts.toml:
[accounts.personal]
provider = "anthropic"
api_key = "sk-ant-..."
[accounts.work]
provider = "anthropic"
api_key = "sk-ant-..."| 命令 | 作用 |
|---|---|
fuse init |
在当前目录写一份 .fuse.toml(参数:--cap、--account)。 |
fuse run <cmd> |
启动本地代理,把子进程 CLI 指向 127.0.0.1:<rand>,exec <cmd>,跟随子进程退出。 |
fuse cap +N / =N / -N |
原子修改上限(任何会让上限 ≤ 0 的变更都会被拒)。 |
fuse status |
打印当前账号、上限、今日花费、项目累计花费、剩余预算。 |
fuse prices |
只读:打印解析后的价格表(内置快照 + ~/.fuse/prices.toml 覆盖)、快照日期、tiktoken 与上游 usage 的误差。--check <provider/model> 会标出命中兜底价的模型。不联网。 |
┌─────────────────┐ ┌───────────────────┐
│ fuse run claude│ ─── exec ──────► │ claude (child) │
└─────────────────┘ │ ANTHROPIC_BASE │
│ → 127.0.0.1:PORT │
└─────────┬─────────┘
│
┌──────────────▼──────────────┐
│ AgentFuse proxy │
│ 1. 账号守卫 │
│ 2. 请求前预估 │
│ 3. 对照 ledger 检查上限 │
│ 4. ALLOW → 转发 │
│ DENY → HTTP 402 │
│ 5. 解析 `usage`,写入 │
│ ~/.fuse/ledger.db │
└──────────────┬──────────────┘
│
api.anthropic.com
api.openai.com
全部本地:单二进制、无守护进程、ledger 是 ~/.fuse/ledger.db(bbolt KV,(project, day) → {tokens_in, tokens_out, usd, requests})。代理跟随子进程一起退出,binary 除了你配置的 provider 不会和任何外网通信。
- m1 — 拦截 & 记账。 本地代理透明转发
claude/codex流量,解析usage,按 cwd 写 token + USD ledger。fuse status给出真实数字。 - m2 — 硬上限。
.fuse.toml解析 +cap_usd强制执行。请求前预估杜绝单次"漏掉"。触顶返回 HTTP 402。fuse cap ±N原子修改。 - m3 — 启动器模式。
~/.fuse/accounts.toml命名账号、指纹匹配、OpenAI provider 对齐。误读到的.envkey 再也不会把流量带到错的账号上。 - m4 — 扩宽楔形(v0.2)。 新增 Gemini、DeepSeek、OpenAI-compat 三个 handler;用 tiktoken-go 给"流式无 usage"的上游兜底;价格表外置
assets/prices.toml,可通过~/.fuse/prices.toml覆盖。 - v0.3 — fail-closed 正确性修复。 修掉了流式 Anthropic / OpenAI 响应被计为 $0 的缺陷(SSE 响应体从未被解析,上限因此永不触发);部分 usage 时按 in/out 分别用 tiktoken 兜底;新增原子
Reserve/CommitDelta,并发请求不再越过上限;关键计费改走可被用户覆盖的价格表。新增 tiktoken 精度测量 harness 与只读的fuse prices诊断命令。
依然不做:Web UI、多人 / SSO、跨主机花费聚合、细粒度 per-tool 限额、Slack / 邮件告警、Windows 原生(仅 WSL2),以及——绝不做的——任何远端价格拉取或遥测调用。
MIT,详见 LICENSE。Issue 和 PR 都欢迎,地址:github.com/SuperMarioYL/agentfuse。当下最有价值的贡献:拿一个真实的通宵循环跑一遍上面的 quickstart,把熔断触发(或者没触发)的细节开个 issue 告诉我们。
MIT © 2026 SuperMarioYL
