交接说明自包含,不依赖聊天记录也能接着做优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
创作对话 Token 花销报告与优化建议
状态:体验核对结论。数据来自本机
~/.writing-agent/stats(2026-10-02~10-06),对照docs/briefs/creation-round-cost.md旧基线与当前src/dictate/round.ts实现。
记录:2026-10-06。
诉求:对话不要继续走「自由 Agent 工具循环」,花销过大。
1. 结论(先看这个)
- 主战场仍是创作落盘,不是游玩。 本窗口
dictate_agent约占总 prompt 的 99%+;游玩 Worker 几乎可忽略。 - 已经不是旧版「读甲写甲再读乙」自由 Agent,而是
select → 程序注入 → fill 工具循环(可 retry)→ 偶发 lean reply。但 fill 仍是 agent 形态(toolChoice: required+ 多工具 + 最多 4 次重试),这是当前贵的原因。 - 一句用户话平均约 3.3 次模型请求,中位 3、P90 5、最多 6。其中 60% 的轮次至少 1 次 fill-retry,约 29% 要 2 次及以上重试。
- 相对花销(未缓存=1、缓存=0.25、输出=4):fill 53%、fill-retry 28%、select 15%、reply 4%。去 Agent 的收益应打在 fill 一次写完、消灭重试、砍历史,而不是再抠游玩。
- 首填缓存几乎失效: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 工具循环。
- select:保持现状(或改为纯 JSON structured output,去掉 tools 税)。只出
module_id[]/ 澄清题。 - fill:改为 一次 structured complete:
{ products: [...], reply, declares?, deletes? },程序本地切开落盘与校验。 - 失败只重写失败 tag:上下文 = 规则摘要 + 该 tag 注入规格 + 相关产物白名单 + 错误信息;不要再塞全黑板 + 全历史重跑。
- 变量/映射:优先由程序从 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 已说明:误裁会导致冲突设定。
建议分两档:
- 安全档:按本轮
module_id维护「必带 tag」白名单(依赖边写在 catalog);其余只带 标题+一行摘要+hash。 - 激进档:检索/摘要。必须配套:写入前冲突扫描、失败则自动补全文重试(否则质量回退会被当成「省钱事故」)。
预期:做好可再砍 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 太大」两件事。