成本与用量
读完这页你能做到四件事:
- 看懂
/cost和会话摘要里每个数字的口径,知道哪个是真实总花费 - 判断你的缓存命中率是正常还是异常(两族协议的正常上限根本不一样)
- 用四种手段把成本压下来,从改一行配置到换模型
- 给会话设花费上限,超了自动停
为什么这页值得读
成本是 sid-code 少数有硬数据支撑的方向。缓存命中率不是玄学—— 它直接决定你同一个任务花八分钱还是三毛钱。而这个数就摆在 /cost 里,随时能看。
快速上手
会话里打 /cost:
会话时长: 24s
总费用: $0.0529
API 耗时: 21s
工具耗时: 22ms
Token 用量(汇总):
输入: 101287
输出: 210
缓存读取: 71680
缓存命中率: 70.8% 省钱: $0.0589
(openai 协议为隐式缓存,60-70% 属正常上限;90%+ 需 anthropic 族显式缓存)上面是真实数据:来自一次「改一个写错的
add函数」的四轮会话 (glm-5.2 / openai 协议),四次请求的 usage 取自落盘轨迹。
退出时不用问也会给一份摘要(同一次会话的真实输出):
Session ID: 20260727-184318-a08fce0c
会话时长: 0.4 分钟
LLM: 4 次请求, 25585 tokens, 平均 5.3s, $0.0529
工具: 3 次调用 (3成功/0失败)
交互: 1 次提示, 4 轮循环
缓存: 命中 71% (71.7k/101.3k)摘要里有两处口径与 /cost 不同,别直接横向对比:
LLM:行的 tokens 是「各模型末次输入之和 + 输出累计」(stock,避免历史被重复计数), 不是/cost那条累加口径的输入。所以同一次会话里25585 tokens和101.3k并存是正常的。LLM:行的金额只含 LLM 请求,不含辅助调用;含影子调用的完整总花费看/cost的总费用与其中辅助调用。
有压缩或自定义指标时,摘要还会追加 压缩: / 工具明细: / ── Harness 指标 ── 行。
先看两个数:$0.0529(花了多少)和 命中 71%(缓存效率)。 第二个数低的时候,第一个数会翻好几倍。
每个数字的口径
这几个数容易看混,逐个说清:
| 字段 | 含义 | 注意 |
|---|---|---|
总费用 | 主对话 + 辅助调用的真实总花费 | 这是你实际要付的钱 |
其中辅助调用 | 标题生成 / 记忆抽取 / 风险分类 / 摘要等影子调用 | 只在 >0 时显示。这部分容易被忽略,但它真的花钱 |
输入 | 全会话累计输入 token(累加口径,不是最后一次请求的长度) | 多轮会话里这个数会远大于上下文窗口,正常。两族含义不同:anthropic 族是「未命中余量」,与 缓存读取/缓存创建 互斥;openai 族含命中部分,输入 已经包含 缓存读取 |
输出 | 全会话累计输出 token | — |
缓存读取 | 命中缓存的 token 数 | 这部分单价便宜得多。与 缓存创建 一样只在非 0 时显示 |
缓存创建 | 主动写入缓存的 token 数 | 只有 anthropic 族会返回;openai 族协议不下发该字段(代码不强制归零) |
缓存命中率 | 缓存命中 / 归一化后的完整 prompt | 分母是 cacheHit + cacheWrite + uncached,不是表格里的 输入 字段(anthropic 族下两者不等)。判断标准见下一节,别拿一个数字套两族协议 |
省钱 | 因为命中缓存而少付的金额 | 注意它可能比总费用还大——省下的比花掉的多是常态 |
多模型会话还会按模型分列,input 用的是同一套累计口径, 所以「分模型之和」和「上方汇总」是能对上的。
缓存命中率:两族协议的正常值不同
这是最容易误判的一点。同样 65% 的命中率,在一族是正常上限,在另一族是明显偏低。
| 协议族 | 缓存机制 | 正常命中率 | 判断 |
|---|---|---|---|
anthropic | 客户端显式写入(cache_control) | 90%+ | 低于 80% 说明有问题,值得查 |
openai(含各家兼容网关) | 服务端隐式缓存 | 60–70% | 到 70% 就已经是上限,别再折腾 |
区别在于:anthropic 族由客户端主动标记「这段缓存起来」,能精确控制缓存边界; openai 族靠服务端自己判断前缀是否重复,客户端插不上手,所以 缓存创建 恒为 0, 结构性上限就在 60–70%。
sid-code 会替你判断并在 /cost 里标注,就是那行括号里的提示—— 看到 70% 配这行提示,说明你已经到顶了,不用再优化。
提示的触发条件要说清楚:本次会话里有 openai 协议的缓存命中、且 openai 协议部分没有缓存写入时才出现 (判断写在 packages/cli/src/command/builtins.ts 的 /cost 实现里)。 它和命中率同源,都按全会话里每个模型实际走的协议来算,与你当前选的是哪个模型无关。 所以中途 /model 切过模型,或者子代理用了别的协议族,提示照样会出现。
如果会话混用了 openai 和 anthropic 两族,提示会换成「本会话混用多个协议族」那一版: 这时上面的命中率是两族加权的结果,60–70% 和 90%+ 哪个标准都不能直接套。 表里 90%+ / 60–70% 是实测区间,不是代码里的阈值判定。
上面这两个区间不是估的。Prompt Cache:两族协议的分叉 交出了跨数百个会话、数亿输入 token 的实测账本(含逐模型命中率与复现命令), 并解释了两族协议为什么必须写成两种形状(openai 族的 缓存创建 恒为 0 就是那里推出来的)。
不要拿 100% 当目标
实测一个单轮会话可以打出 缓存: 命中 100% (24.6k/24.6k)——因为输入全部是重复前缀。 这个数很好看但没意义,真实多轮任务的命中率天然会被新增内容拉低。 有参考价值的是同类任务的命中率趋势,不是单次绝对值。
怎么把命中率提上去
缓存是按前缀命中的,前缀一变整段就废。所以核心原则只有一条: 让稳定的东西待在前面,让变化的东西待在后面。
| 做法 | 为什么有效 |
|---|---|
| 同一个任务留在同一个会话里做 | 换会话等于前缀重建,命中率归零 |
别频繁 /clear | 同上。想换话题但保留上下文用 /compact |
项目约定写进 CLAUDE.md 而不是每次口述 | 它作为稳定前缀被缓存;口述的内容夹在对话中间,破坏后续前缀 |
中途别反复 /model 切换 | 换模型等于换缓存空间,前面攒的全不算 |
| 长任务里别插无关的临时问题 | 插进去的内容会让后面每一轮的前缀都变 |
跨会话:/cache
/cost 只看当前这次会话。想知道"这周缓存命中率是不是在掉""哪个模型最近老断缓存", 用 /cache——它读的是跨会话的用量账本,不是内存里的当前会话。
数据存在 ~/.sid-code/usage-ledger.jsonl(每会话一行汇总,按 sessionId 做 upsert: 每轮结束增量落盘、覆盖同会话旧行,不是 append-only, packages/core/src/telemetry/usage-ledger.ts)。所以即使会话关了、机器重启了,历史还在。
默认输出:长期命中率与省钱趋势
/cache无参显示长期统计:总命中率(百分比 + 绝对 token 数)、累计省钱(USD)、累计成本
- 会话数、按模型分列的用量(按命中 token 降序)、各周期命中率的趋势 sparkline (
▁▃▅▆▇█字符图,一眼看出在涨还是在掉;至少有两个周期才显示)。
五个参数
| 参数 | 作用 |
|---|---|
--period day|week|month | 聚合粒度,默认 day。week 用 ISO 周键、month 用年月键 |
--model <name> | 按模型名过滤(精确、前缀或反向前缀匹配) |
--breaks | 显示本会话最近 20 条缓存中断记录 + 健康度建议(packages/core/src/api/cache-detection.ts 的 getCacheHealthAdvice())。记录只在进程内存里(最多 50 条),进程重启即丢;跨会话历史用 --history |
--history | 跨会话缓存中断遥测历史聚合,从 ~/.sid-code/cache-breaks.jsonl 读,按归因类型计数 |
--prune <N> | 滚动裁剪账本,只保留最近 N 行(账本太大时用) |
--breaks:本会话的缓存退化诊断
缓存命中率从 90% 掉到 70%,不是 /cost 的单次快照能看出来的——你得知道它为什么掉了。 --breaks 给的是当前进程内的最近 20 条中断记录(内存环形缓冲最多 50 条), 用来即时解释「刚才那几轮为什么掉」。进程重启即丢;要看跨会话的长期记录用 --history。
检测机制(packages/core/src/api/cache-detection.ts 的 CacheBreakDetector):每轮请求前快照缓存关键状态 (system prompt hash / 工具 schema hash / 模型 / cache control / beta headers / 消息数 / 工具顺序),响应后比较 cache_read_tokens 变化。下降 > 5% 且绝对值 > 2000 tokens 才算一次中断(cache-detection.ts 的 DROP_PERCENT_THRESHOLD / DROP_TOKENS_THRESHOLD)——避免正常波动报假警。
10 类归因:模型变化(model)、system prompt 变化(system_prompt)、工具增删改 (tools)、工具顺序变化(tool_order)、缓存策略变化(cache_policy)、beta headers 变化 (beta_headers)、消息数量骤减即 compact 导致(compact)、TTL 过期(ttl_expiry)、 本地前缀断裂(prefix_break)、服务端波动(server_fluctuation)。
--history 把这些中断落盘到 ~/.sid-code/cache-breaks.jsonl(append-only),长期聚合, 告诉你"最近哪种归因最频繁"。子代理的缓存中断独立计——MultiSourceCacheDetector 按 agentId 维护独立基线(cache-detection.ts 的 MultiSourceCacheDetector),不会把子代理的正常波动算进主会话。
实测这些归因跑出来长什么样、以及为什么"本地前缀 hash 变没变"是最关键的那个判据, 见 Prompt Cache 的归因分布一节——绝大多数真实中断都是 本地不可控的服务端波动,这个分布本身就是结论:本地能优化的前缀断裂几乎为零, 所以这里该做的是监测而不是排查。
compact 会触发一次中断,但会被抑制
/compact 会让消息数量骤减,正常应该报一次中断。但 notifyCompaction() / notifyCacheDeletion() 会跳过紧接的那次检测(假阳性抑制),所以 compact 后看到命中率 下降是正常的,不会在 --breaks 里报成异常。
单会话深度分析:/insights
/cost 给数字,/insights 给结构化报告——这次会话到底发生了什么、哪里可能有问题。
/insights # 分析当前会话
/insights latest # 分析最近一个会话
/insights 20260728-004217-cc55cf0d # 分析指定会话别名 /analyze。纯本地执行,不调模型(packages/cli/src/command/commands/insights/insights.ts), 复用 packages/core/src/trace/digest.ts 的 renderHuman() 渲染。下面是与 轨迹采集与可观测 里 trace-digest 同源的核心结构节选 (实际报告还会渲染成本结构、Provider 健康、端到端耗时等,且各块按会话数据条件出现):
━━━ session <id> [<exitStatus>] ━━━
模型/API 次数/步骤/耗时/成本/token
用户意图: 1. <prompt>
L0 事实层 (N) — 机器可验证,带出处,不含判断:
[高] <kind>: <detail> ⊢ 出处: <file> @<lineRef> = <rawValue>
L1 假设层 (N) — 待验证,先消解证伪条件再采信:
[中] <kind>: <detail> ⚖ 证伪条件: <condition>
工具序列 (N 次调用): · <tool> / ✗ <tool> / ○ <tool>(孤儿调用)
思维链要点: 💭 <thought>
子代理执行: <N> 个(<M> 成功 / <K> 失败) 执行模式: 串行|并行|混合|单个没有检出异常时,L0/L1 两块会合并成一行 ✓ 未检出异常信号; 工具序列 / 思维链要点 / 子代理执行 同样只在有数据时出现, 子代理失败之外还可能追加 / N 未知。
它最有价值的设计是 L0/L1 分层:L0 事实层带出处(messages.json 的哪一行 = 什么值), L1 假设层带证伪条件。它不直接给你"结论是 X",而是给可验证的事实 + 待验证的假设 + 推翻假设需要看哪里。比如 exit_status = error 是事实,但假设层同时给了证伪路径—— 查 messages.json 的 attribution 会看到实际是正常 end_turn,说明 error 状态不等价于异常终止。
/cost、/cache、/insights 各管什么
| 命令 | 回答什么 | 数据源 | 范围 |
|---|---|---|---|
/cost | 这次花了多少钱、缓存命中多少 | 内存当前会话 | 单会话 |
/cache | 缓存命中率长期趋势、是否在退化 | usage-ledger.jsonl + cache-breaks.jsonl | 全部历史会话 |
/insights | 这次发生了什么、哪里可能有问题 | trajectories/sessions/<id>/ 轨迹文件 | 单会话深度 |
三者不重复、不替代:/cost 看当下花了多少、/cache 看长期缓存在不在退化、 /insights 看单次会话的决策链与异常信号。时间花在哪、最近是不是变慢了,用 /perf, 见性能与延迟。
设花费上限
单次会话给个硬顶,超了自动停。交互模式和 -p 都生效:
sid-code --max-budget-usd 0.50配置里同时有 quota.costLimit 时,取两者中更严的那个。上限是只许收紧的语义, 所以团队默认配置的 100 不会盖掉你传的 0.5,你也没法用参数把团队上限放宽。 0 表示「这一侧不限」,不参与比较。
四级预警,按累计花费占上限的比例触发。括号里的百分比是实测时的实际比例, 触发阈值是 50% / 80% / 95% / 100%(packages/core/src/llm/quota.ts):
$0.0011 / 0.002 → [info] 成本已达配额 55%($0.0011 / $0.0020)
$0.0017 / 0.002 → [warning] ⚠ 成本已达配额 85%($0.0017 / $0.0020),请注意控制用量
$0.0019 / 0.002 → [critical] ⚠ 成本已达配额 95%($0.0019 / $0.0020),即将超限!
$0.0025 / 0.002 → [exceeded] 成本已超出配额($0.0025 / $0.0020),自动停止到 exceeded 就真的终止本轮(消息文本本身不带 emoji,TUI 渲染时两端加 ── … ──)。
每级只报一次(级别升级才报),不会刷屏。计算基数是含辅助调用的总花费, 所以影子调用烧的钱也受这个上限约束,不会绕过去。
上限不生效时先查这个
--max-budget-usd 与 quota.costLimit 取更严的。所以「设了更小的值却没拦住」 只剩一种可能:参数根本没传进去(拼错了、写在了别的子命令后面)。 想放宽上限时,参数是做不到的,要改配置里的 quota.costLimit。
团队级的持续管控(每分钟请求数、token 速率、按规则降级)不在这页, 见配额与成本控制。
把成本降下来的四招
按性价比排序,第一招几乎零成本:
一、给子代理配便宜模型
主对话用强模型,子代理干粗活用便宜的。子代理通常做的是搜索、读文件、跑命令这类 不需要强推理的事,用同一个贵模型是浪费。配置见子代理。
二、保住缓存命中率
见上一节。一次会话做完一件事,比来回开新会话省得多。
三、控制上下文体积
上下文越长,每一轮的输入 token 越多。/context 看当前占用, 长任务到七八成的时候 /compact 一次。细节见上下文与压缩。
四、按任务难度换模型
简单任务不需要最贵的档位。/model 随时切,不用重启。 还有 /effort 调推理档位——低档位输出的 reasoning token 少,也省钱。
Fast Mode:/fast(预留开关)
/fast 切换的是 fastMode 偏好——"优先用更快的输出端点/服务档位"。 如实说明:当前网关未提供对等 fast 能力,开启暂无实际加速效果。 这是预留开关,等网关支持后无需改命令即可生效 (packages/cli/src/command/commands/fast/fast.ts,透传到 packages/core/src/llm/fallback.ts 的 fastMode 字段,fallback 层标注为「预留,暂未启用」)。
/fast 显示当前开关态 + 能力说明
/fast on 开启(仅当前会话)
/fast off 关闭
/fast on -p 开启并持久化到 settings.json(别名 --persist / save)无参时的真实输出:
Fast Mode: off
注: 当前网关未提供对等 fast 能力,开启暂无实际加速效果(此为预留开关,网关支持后自动生效)。和 /effort 的区别:/effort 控制推理深度(已生效,支持的模型上低档位省 reasoning token), /fast 控制输出端点速度(预留,当前无实际效果)。两者正交——/effort 现在就能用, /fast 等网关就绪后才真正生效。无效参数会提示 用法: /fast [on|off] [-p]。
/effort 有一个前提:模型本身要支持 reasoning 能力,否则命令会拒绝切换并提示; DeepSeek 这类只认 high 的模型会把 low/medium 钳到 high,低档位不一定真省。
为什么不删掉这个预留命令
打了会明确告知"当前无实际加速",比让命令消失、等网关支持后再突然出现更清楚。
常见问题
省钱金额比总费用还大,是不是算错了
没算错。省钱 是「如果这些 token 全按未缓存单价付,会多付多少」。 缓存命中比例高的时候,省下的确实可以超过实际花掉的。
总费用和我在网关看到的账单对不上
先看 其中辅助调用 那行。标题生成、记忆抽取、风险分类这些影子调用也算钱, /cost 的总费用是把它们算进去的。如果差得多,用 /trace 看这次会话的完整调用清单。
--max-budget-usd 设了但没生效
它和 quota.costLimit 取更严的那个,两种模式都生效。所以「设小了却没拦住」 基本只剩参数没传进去这一种可能。反过来,「想用参数把上限调大」是做不到的, 更宽的那个永远不会胜出,要放宽就改配置:
grep -A3 '"quota"' ~/.sid-code/settings.json输入 token 数比模型的上下文窗口还大
正常。输入 是全会话累加,不是单次请求的长度。 四轮会话每轮输入 25k,累加就是 100k,但每一轮实际都在窗口内。 想看单次占用去 /context。
想看跨会话的成本趋势
/cache 看跨会话命中率与成本趋势(--period week 按周聚合),/trace --health 看 provider 维度的成功率与延迟。 团队维度的采集见轨迹采集与可观测。
这些数字能证明"AI 提效了"吗
不能,它们只回答"花了多少"。省下的 token 与省下的工程时间不是一回事—— 成本降了,验证侧的负担可能同时在涨,而这一侧本页一个字段都没有覆盖。 为什么会这样、该换成什么指标去看,见 AI 提效的熵账本。