Files
writing-agent/docs/briefs/token-cost-report-2026-10-06.md
chenmoranzhiandCursor 7185f25dec 补上费用核对与缓存审计材料,方便后续按结论继续压创作成本。
交接说明自包含,不依赖聊天记录也能接着做优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 23:49:51 +08:00

11 KiB
Raw Permalink Blame History

创作对话 Token 花销报告与优化建议

状态:体验核对结论。数据来自本机 ~/.writing-agent/stats(2026-10-02~10-06),对照 docs/briefs/creation-round-cost.md 旧基线与当前 src/dictate/round.ts 实现。
记录:2026-10-06。
诉求:对话不要继续走「自由 Agent 工具循环」,花销过大。

1. 结论(先看这个)

  1. 主战场仍是创作落盘,不是游玩。 本窗口 dictate_agent 约占总 prompt 的 99%+;游玩 Worker 几乎可忽略。
  2. 已经不是旧版「读甲写甲再读乙」自由 Agent,而是 select → 程序注入 → fill 工具循环(可 retry)→ 偶发 lean reply。但 fill 仍是 agent 形态(toolChoice: required + 多工具 + 最多 4 次重试),这是当前贵的原因。
  3. 一句用户话平均约 3.3 次模型请求,中位 3、P90 5、最多 6。其中 60% 的轮次至少 1 次 fill-retry,约 29% 要 2 次及以上重试。
  4. 相对花销(未缓存=1、缓存=0.25、输出=4):fill 53%、fill-retry 28%、select 15%、reply 4%。去 Agent 的收益应打在 fill 一次写完、消灭重试、砍历史,而不是再抠游玩。
  5. 首填缓存几乎失效:fill 首次命中率约 1%,retry 才爬到约 70%。大前缀每一轮至少全价付一遍。

2. 本窗口花销账

统计范围:本地 jsonl,2026-10-02、10-03、10-04、10-06(10-05 无落盘)。共 298 条用量记录。

2.1 按 caller

路径 请求 prompt 其中缓存 命中率 completion
dictate_agent 279 636.7 万 202.6 万 31.8% 57.9 万
worker:* 13 2.5 万 ~0.1 万 — 0.3 万
main_agent 6 0.06 万 — — —

创作 : 游玩(prompt)≈ 250 : 1(本窗口游玩样本很少;方向与旧基线「创作远大于游玩」一致)。

2.2 按日(仅创作)

日 请求 prompt(百万) 缓存(百万) completion(千)
10-02 37 0.30 0.04 117
10-03 86 2.25 0.76 187
10-04 23 0.31 0.07 45
10-06 133 3.50 1.15 229

2.3 创作按 step(关键)

step 请求 prompt 合计 单次均 prompt 缓存命中 completion
fill-retry 89 284.1 万 31.9k 70.1% 16.4 万
fill 82 242.5 万 29.6k 1.1% 33.7 万
select 85 89.6 万 10.5k 0.9% 5.0 万
reply 23 20.5 万 8.9k 0% 2.7 万

要点:

  • retry 请求数 多于 首次 fill(89 > 82)→ 重试是常态,不是边角。
  • 首次 fill 几乎不吃缓存;钱主要花在「每一轮第一枪大前缀」。
  • select 已不带产物正文(实现正确),但仍带全量更早对话,均约 10k prompt。
  • lean reply 只覆盖约 28% 有 fill 的轮次;其余多在 fill 同请求里 reply_user(设计意图),或失败后走 prose 回退。

2.4 按用户轮次(turnId)

指标 值
轮次数 85
每轮模型调用 avg / p50 / p90 / max 3.28 / 3 / 5 / 6
每轮 prompt 合计 avg / p50 / p90 / max 7.5 万 / 6.0 万 / 14.9 万 / 25.9 万
无 retry 34(40%)
≥1 次 retry 51(60%)
≥2 次 retry 25(29%)

2.5 上下文块(segmentChars,字符,非 token)

step n rules history products injected tail 均 prompt tok
fill 82 ~2.1k ~9.3k ~23.0k ~5.6k ~5.0k 29.6k
fill-retry 89 ~2.1k ~8.7k ~21.9k ~6.3k ~4.9k 31.9k
select 85 ~0.3k ~9.0k 0 0 ~4.9k 10.5k
reply 23 ~0.5k 0 ~6.7k(仅本轮) 0 ~6.3k 8.9k

近期 fill 样本(10-06):products 已到 4.3~5.0 万字,history 2.6~3.2 万字,单次 prompt 5.5~6.9 万 token,且 cached=0。

2.6 模型

模型 请求 prompt(百万) 命中率
google/gemini-3.8-flash 171 4.13 29.6%
google/gemini-3.7-flash 102 2.17 36.4%
openai/gpt-6.1-sol 6 0.07 16.1%

金额粗算(按 Flash 量级:未缓存输入 $0.15/M、缓存 $0.0375/M、输出 $0.60/M,仅作数量级):本窗口创作约 $1.1,约 $0.013/用户轮。体感「贵」主要来自 轮次×大前缀×重试,以及若换成更贵模型会线性放大;不是输出写爆。

2.7 与旧基线对照

旧账(creation-round-cost.md,约 9/20 起、创作 416 次 / prompt 1198 万):自由工具循环、先 read_module 再 insert。

旧自由 Agent 本窗口(已 planned)
形状 读/写交织、工具定义常驻 select + 注入 + fill 循环
同轮请求倍数 常「每节点×2」再加回复 中位 3(已封顶感更好)
产物进选题 会进(旧整包) 已去掉
填写大头 全产物 + 全对话 仍是全产物 + 近乎全对话
首填缓存 依赖粘滞 仍≈0(select/fill 前缀形状不同)

程序已省下「读模块那一轮」和选题侧产物;未省下「大前缀×(1+重试)」。用户觉得「还是 Agent、还是贵」,与数据一致。


3. 当前链路(体验视角)

用户一句
  → select(小上下文,强制 select_nodes)     ≈1 次,~10k prompt
  → 程序注入本轮节点写法 / 库正文(确定性,不计模型)
  → fill(大上下文,强制工具:insert/delete/变量/映射 + reply_user)
       └─ 未过则 append 工具结果,再 fill-retry(最多约 4)
  → 若填完却未 reply_user → lean reply(只带本轮产物)  ≈9k

与「不要 Agent」的落差:

已做到 仍像 Agent
选题与填写拆开 fill 仍多工具 + required
规格由程序注入,不再 read_module 校验失败靠模型再跑一整轮大前缀
回复可瘦身 变量/映射/删除仍走 tool loop
session_id 粘滞已接 select→fill 系统提示与正文布局不同,首填缓存打不穿

4. 优化建议(按性价比)

P0 — 对话填写去 Agent(最贴合诉求)

目标形状:一句用户话 = 2 次模型请求(澄清时 1 次),禁止 fill 工具循环。

  1. select:保持现状(或改为纯 JSON structured output,去掉 tools 税)。只出 module_id[] / 澄清题。
  2. fill:改为 一次 structured complete:{ products: [...], reply, declares?, deletes? },程序本地切开落盘与校验。
  3. 失败只重写失败 tag:上下文 = 规则摘要 + 该 tag 注入规格 + 相关产物白名单 + 错误信息;不要再塞全黑板 + 全历史重跑。
  4. 变量/映射:优先由程序从 structured 字段落盘,或极窄工具面;不要与长正文 insert 共用大循环。

预期(相对本窗口相对花销):

  • 去掉 fill-retry 常态 → 相对花销约 -25%~35%
  • 首填仍大,但每轮从 3.3 枪降到 ~2 枪 → 再 -20%~30%(视澄清/失败率)
  • 合计数量级:创作输入花销降到现在的一半左右(前提:一次交多份成功率够高)

与相位机约束不冲突:仍在同一次 running 内、用户不手选节点、方案不是执行序。

P1 — 砍 history(实现便宜、收益稳)

现状:select/fill 的「更早对话」均值约 9k 字,会话后期到 3 万字+,占填写真份额。

建议:

  • select:方案进度 + 最近 2~4 轮 + 末句(旧 brief 已论证「只留末句会误选」,不能只留一句)。
  • fill:同样只留最近对话;产物冲突靠产物正文,不靠早期闲聊。
  • 清空对话能力保持;长记忆另走总结产物,不靠无限 chat。

预期:fill/select 各再 -10%~20% prompt;会话越长收益越大。

P2 — 稳首填缓存前缀

session_id 只保证同上游;select 与 fill 的 system + 前部布局不一致 → 首填命中≈0。

建议(不改业务语义):

  • 抽出 跨 select/fill 不变的规则前缀(身份、写入规则骨架),两边共用同一字节前缀。
  • 易变块(方案进度、checklist、注入、末句)固定在末尾(现已部分如此)。
  • 工具定义:若 fill 改 structured,则工具 JSON 不再每轮跟着大请求。

预期:在粘滞有效时,同一会话后续轮次的「规则+稳定壳」可从全价变折扣;不降低发送体积,但降低未缓存账单。对 OpenRouter+Gemini 块粒度(约 4k)敏感,前缀差几百 token 可能整块失效。

P3 — 产物相关性(大头,风险高)

products 占 fill 字符大头(样本约一半以上)。旧 brief 已说明:误裁会导致冲突设定。

建议分两档:

  1. 安全档:按本轮 module_id 维护「必带 tag」白名单(依赖边写在 catalog);其余只带 标题+一行摘要+hash。
  2. 激进档:检索/摘要。必须配套:写入前冲突扫描、失败则自动补全文重试(否则质量回退会被当成「省钱事故」)。

预期:做好可再砍 fill 30%~50%;做不好会表现为反复 rewrite,把 P0 收益吃掉。

P4 — 游玩与总管

本窗口不必优先。游玩保持「一次生成、非创作 tool loop」。OpenRouter 上游玩缓存曾为 0,靠 session_id 已具备前提;前缀顺序现已正确。总管 main_agent 可忽略。

明确不做 / 慎做

项 原因
改回用户手选节点以省一轮 与产品「自然语言推进」冲突
相位机按固定工序跑完全部节点 与「方案不是执行序」冲突
选题带全产物 已证明浪费;会毁掉 select 的小请求优势
用 provider.order 代替 session_id 会关掉粘滞
只砍输出 输出约是 prompt 的 3%~9%,不是主账

5. 建议落地顺序(可验收)

步 改动 验收指标(同书同对话长度)
A fill 改为一次 structured,去掉 write tool loop 每轮模型调用 p50→2;fill-retry 调用占比 <10%
B select/fill history 窗口 fill segmentChars.history 中位下降 ≥50%
C 共享稳定 system 前缀 同会话第 2 轮起 fill 首次 cached/prompt ≥40%(Gemini/OpenRouter)
D 产物白名单/摘要 fill 均 prompt 再降 ≥25%,且 insert 拒绝率不升高

体验验收(主观):

  • 用户感知仍是「说一句 → 落几块 → 一段综合回复」,无「工具来回思考很久」的 Agent 感。
  • 澄清轮仍可 1 次请求结束(仅选题提问)。
  • 失败时只看到「某几块重写」,而不是整轮长时间空转。

6. 和「不要 Agent」的直接答复

  • 创作对话:应该停止以 Agent tool loop 作为填写主路径。 选题可以极窄工具或 JSON;填写应程序注入规格 + 一次结构化交卷 + 本地校验。
  • 游玩:已经不是创作那种 Agent;继续单次 Worker 生成即可。
  • 总管 Agent:不是当前账单;可维持。

若只做一件事:做 P0(fill 去循环)。它同时回应「体感像 Agent」和「token 太大」两件事。