补上费用核对与缓存审计材料,方便后续按结论继续压创作成本。
交接说明自包含,不依赖聊天记录也能接着做优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
|||||||
|
# 提示缓存核对:记账是否可信、首填为何仍≈0
|
||||||
|
|
||||||
|
> 状态:核对结论。承接 `docs/briefs/token-cost-report-2026-10-06.md` §1.5 / §4.P2。
|
||||||
|
> 记录:2026-10-07。
|
||||||
|
> 疑问:若 OpenRouter 上 DeepSeek 缓存能挂住,是否说明「首填几乎不吃缓存」是本地记账错了,或粘滞/TTL 才是主因?
|
||||||
|
|
||||||
|
## 1. 结论(先看这个)
|
||||||
|
|
||||||
|
1. **缓存记账可信。** 我们只是把 OpenRouter 的 `usage.prompt_tokens_details.cached_tokens`(及 DeepSeek 的 `prompt_cache_hit_tokens`)原样写入 `~/.writing-agent/stats`;不是自估、不是漏字段。
|
||||||
|
2. **「首填≈0、retry 高」是上游真值,不是漏记。** 同轮对照里,绝大多数是 fill 几乎无缓存、fill-retry 立刻上到八成上下。若解析坏了,两边应同假。
|
||||||
|
3. **DeepSeek 探测成功,不能推翻 Gemini 首填账。** 花销报告窗口主模型是 `google/gemini-3.*-flash`;DeepSeek 只证明「同 baseUrl + 同解析链路」在另一模型上能读到非零缓存。
|
||||||
|
4. **粘滞/`session_id` 不是当前主账缺口。** 同会话内 fill→retry 已能吃到缓存,说明粘到了同一上游且前缀可复用;贵在 **每一轮第一枪大前缀仍全价**,以及 **重试常态**。
|
||||||
|
5. **优化优先级不变:** 仍以 P0(fill 去工具循环、消灭 retry)为主;P2(跨 select/fill 稳定前缀)对 Gemini 有意义,但不是「先修记账」或「再抠 TTL」。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 记账链路(我们记了什么)
|
||||||
|
|
||||||
|
| 层 | 行为 |
|
||||||
|
|---|---|
|
||||||
|
| OpenRouter 响应 | `prompt_tokens` / `prompt_tokens_details.cached_tokens` / 可选 `cache_write_tokens` |
|
||||||
|
| `parseUsage`(`src/llm/client.ts`) | 读 `cached_tokens`;DeepSeek 另认 `prompt_cache_hit_tokens` |
|
||||||
|
| stats jsonl | `cachedTokens` / `cacheWriteTokens` 等落盘 |
|
||||||
|
| 报告命中率 | `cached / prompt`(按请求平均或合计) |
|
||||||
|
|
||||||
|
单元测试覆盖 OpenAI details、DeepSeek hit/miss、cache write。探测脚本与生产走同一 `complete` → `parseUsage`。
|
||||||
|
|
||||||
|
**原始对照(2026-10-07,Gemini 探测):** 直接 `fetch` OpenRouter,两次固定大前缀请求的 `usage` 均为:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"prompt_tokens_details": {
|
||||||
|
"cached_tokens": 0,
|
||||||
|
"cache_write_tokens": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
落盘若记成 0,与上游一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 生产窗口再切一刀(缓存行为)
|
||||||
|
|
||||||
|
范围:`~/.writing-agent/stats` 中 2026-10-03、10-04、10-06、10-07 的 `dictate_agent`(与花销报告同一量级窗口,略含 10-07)。
|
||||||
|
|
||||||
|
| step | 请求 | 有缓存占比 | 均命中率 | 中位命中 |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| select | 79 | 1.3% | ~0.8% | 0% |
|
||||||
|
| **fill** | 79 | **1.3%** | **~0.5%** | **0%** |
|
||||||
|
| **fill-retry** | 83 | **84.3%** | **~71.9%** | **~85%** |
|
||||||
|
| reply | 30 | 0% | 0% | 0% |
|
||||||
|
|
||||||
|
同轮 `fill` + 至少一次 `fill-retry` 的配对约 **45**:
|
||||||
|
|
||||||
|
| 模式 | 对数 |
|
||||||
|
|---|---:|
|
||||||
|
| 首填≈0 且 retry 命中 >40% | **34** |
|
||||||
|
| 两者都≈0 | 11 |
|
||||||
|
| 首填命中 >20% | **0** |
|
||||||
|
|
||||||
|
样本:同轮 `fill` `cached=0`、随后 `fill-retry` `prompt≈90k / cached≈82k`(命中约 91%)。这只能解释为:**上游在 retry 上报了高缓存,我们照记。**
|
||||||
|
|
||||||
|
模型侧:窗口几乎全是 `google/gemini-3.7-flash` / `gemini-3.8-flash`;与花销报告一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 探测:DeepSeek vs Gemini(同 OpenRouter)
|
||||||
|
|
||||||
|
核对方式:固定大前缀连打 OpenRouter,读 `usage.prompt_tokens_details.cached_tokens`(脚本已删,结论仍有效)。
|
||||||
|
|
||||||
|
### 4.1 `deepseek-v4-pro`
|
||||||
|
|
||||||
|
| 调用 | prompt | cached | 命中 |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| A#1 | 10602 | 7650 | 72.2% |
|
||||||
|
| A#2~A#4 | 10602 | 9743 | 91.9% |
|
||||||
|
| 等 60s 后 B#1~B#2 | 10602 | 9743 | 91.9% |
|
||||||
|
|
||||||
|
→ 隐式缓存可用;短间隔爬升;**约 1 分钟间隔未掉光**。说明「同 baseUrl + 粘滞 + 记账」这条链对 DeepSeek 成立。
|
||||||
|
|
||||||
|
### 4.2 `google/gemini-3.8-flash`
|
||||||
|
|
||||||
|
| 调用 | prompt | cached | 命中 |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| A#1~A#3(间隔 5s) | ~12.7k~15k | **0** | **0%** |
|
||||||
|
|
||||||
|
原始 `usage` 同上,`cached_tokens=0` 且 `cache_write_tokens=0`。
|
||||||
|
|
||||||
|
→ **不是我们没记上**,是这次探测形态下上游就报 0。与生产里「仅当 fill 刚跑过、retry 几乎同前缀」才高频命中,并不矛盾:retry 复用的是**刚发过的整包 fill 前缀**;探测的「新会话 + 纯文本 system」未复现那种命中。
|
||||||
|
|
||||||
|
OpenRouter 文档对 Gemini 同时提到隐式缓存与 `cache_control` 显式断点;本仓库**未打** `cache_control`。生产 retry 仍能命中,说明至少部分 Gemini 路由在「极近、极同前缀」下会报 `cached_tokens>0`;**不能**因此假设跨 select→fill、或跨轮首填也会自动吃到折扣。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 对花销报告结论的修正与保留
|
||||||
|
|
||||||
|
| 命题 | 判定 |
|
||||||
|
|---|---|
|
||||||
|
| 首填缓存几乎失效 | **保留**(记账复核后仍成立) |
|
||||||
|
| 贵主要因为 fill 大前缀 ×(1+retry) | **保留** |
|
||||||
|
| 怀疑「其实有缓存但没记」 | **否定** |
|
||||||
|
| 怀疑「DeepSeek 能缓存 ⇒ 当前账单不该贵」 | **否定**(账单模型是 Gemini;且 DeepSeek≠本窗口主路径) |
|
||||||
|
| 怀疑「再抠 TTL / session 就能救首填」 | **弱化**:同会话 retry 已证明粘滞可用;缺口在 **select≠fill 前缀** 与 **每轮第一枪**,不是「完全没粘住」 |
|
||||||
|
| P2 共享稳定前缀 | **仍值得做**(对 Gemini 跨步命中),但收益次于 P0 去循环 |
|
||||||
|
| 换 DeepSeek 指望隐式缓存 | **可作实验**,质量/工具/价格需另验;不能单靠本次探测定案 |
|
||||||
|
|
||||||
|
相对花销直觉(未缓存=1、缓存≈0.25):retry 已打折,**未打折的是几乎每次的首填大包**。消灭 retry 或把首填变小,都比「怀疑仪表」更贴账单。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 建议(本核对后的落地顺序)
|
||||||
|
|
||||||
|
与 `token-cost-report-2026-10-06.md` §5 对齐,本文件只钉缓存相关验收:
|
||||||
|
|
||||||
|
| 步 | 动作 | 验收 |
|
||||||
|
|---|---|---|
|
||||||
|
| A | **不改记账**;需要时用原始 `usage` 或 `/generation` 抽查 | 抽查与 jsonl `cachedTokens` 一致 |
|
||||||
|
| B | P0:fill 改为一次 structured,去掉写工具循环 | 每轮调用 p50→2;fill-retry 占比 <10%(主收益) |
|
||||||
|
| C | P2:select/fill 共用不变字节前缀,易变块置尾 | 同会话第 2 轮起 **fill 首次** `cached/prompt` 明显上升(Gemini 目标仍可订 ≥40%,以实测为准) |
|
||||||
|
| D | 若坚持 Gemini 显式缓存 | 单独评估 OpenRouter `cache_control`;先小流量探测,再决定是否进主路径 |
|
||||||
|
| E | 可选:用 DeepSeek 等隐式缓存友好模型做同书对照周 | 比的是 **有效单价 × 质量**,不是单看 hit% |
|
||||||
|
|
||||||
|
**明确不做:** 为「修缓存」去动 `parseUsage` 估命中;用 `provider.order` 换 `session_id`;把探测脚本的 DeepSeek 命中率写进创作账单当成已省钱。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 一句话答复原疑问
|
||||||
|
|
||||||
|
> 缓存记录是正确的。DeepSeek 能挂缓存,只说明链路和仪表没坏;当前创作窗口里 Gemini **首填几乎不打折是上游如实返回**,retry 高命中反而证明记对了。钱仍主要花在「每轮第一枪大前缀 + 填写真的在重试」,优先去 Agent 循环,而不是先怀疑统计或死磕 TTL。
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
# 创作对话 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. 当前链路(体验视角)
|
||||||
|
|
||||||
|
```text
|
||||||
|
用户一句
|
||||||
|
→ 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 太大」两件事。
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
这份文档是自包含的。接收方看不到代码仓库、聊天记录和作品正文。请只根据本文做事。
|
||||||
|
|
||||||
|
请你做两件事,都要写成我们可以逐条核对的文字:
|
||||||
|
|
||||||
|
1. 列出业界在「长上下文 Agent、提示缓存、工具循环」上的常见优化方案。每条写清:做法、它节省的是请求次数、未缓存输入、缓存折扣价还是输出,以及它依赖什么前提(例如上游必须支持前缀缓存、模型必须能一次返回多个工具)。
|
||||||
|
2. 针对下面描述的这个程序,给出你建议采用的组合方案。说明每一步落在哪一次模型请求、上下文里留哪些块、块的顺序、相位机的哪一段执行。不要假装读过仓库。标出你不确定、需要我们核对的点。
|
||||||
|
|
||||||
|
我们会用仓库和现有规则检查你的方案。与本文第三节冲突的做法请单独标出,不要藏进推荐里。
|
||||||
|
|
||||||
|
|
||||||
|
一、这是什么程序
|
||||||
|
|
||||||
|
这是一个写作代理,分两段使用。
|
||||||
|
|
||||||
|
创作期:用户用自然语言描述想要的作品,不手动点选节点。模型决定这一轮要落实哪些「能力」,把结果写成黑板上的产物。产物是规格(舞台、规则、变量、正文如何呈现、开场),不是游玩回合的正文。聊天只做确认和追问。清空对话后产物还在,产物才是创作期的持久记忆。
|
||||||
|
|
||||||
|
游玩期:另一套 Worker 按已经写好的规格往下演。一次用户输入通常对应一次生成,不是创作那种多步工具循环。
|
||||||
|
|
||||||
|
主要渠道是 OpenRouter 上的 Gemini 与 OpenAI 兼容模型。也有直连的单一上游做对照。费用近似为:
|
||||||
|
|
||||||
|
请求次数 ×(未缓存输入 × 全价 + 缓存输入 × 折扣价 + 输出 × 输出价)
|
||||||
|
|
||||||
|
缓存命中不是免费的,只是更便宜。输出大约是 prompt 的 3% 到 9%,单价更高,但当前主账仍是输入。最近约三天(2026-09-28 10:35 到 2026-10-01 10:35,东八区)创作落盘 416 次请求、prompt 1199 万 token,游玩 29 次、prompt 80 万。创作大约是游玩的 14 倍。优化主战场是创作。
|
||||||
|
|
||||||
|
|
||||||
|
二、会有哪些上下文
|
||||||
|
|
||||||
|
下面这些块都会进模型,但不是每一次请求全有。
|
||||||
|
|
||||||
|
创作落盘,一句用户话开始时拼一次,工具循环中途只在末尾追加:
|
||||||
|
|
||||||
|
- 系统提示:身份、写入规则、推进原则。尚无本局方案时附整份能力短目录(何时用、何时不用、档位)。已有方案时改为方案进度:每个能力一行,已落或未落、档位、理由、缺口。不含产物正文。样本里系统提示 6695 字,其中方案进度 4392 字。
|
||||||
|
- 一条用户消息:全部已有产物,按相对顺序,然后全量对话。样本 25 份产物共 60518 字(约 69%),全量对话 19975 字(约 23%)。没有一份产物独大,最大的正文组成 8879 字,其次变量规则 6005、实现机制 4569,其余多为两三千字。会话中段产物已经约 5.6 万字。
|
||||||
|
- 工具定义:每次都带,约 4917 字,一轮内不变。工具有读取能力、读取库条目、准备开场、写入产物、删除、声明变量、声明映射、清空对话。
|
||||||
|
- 预设:包在系统侧的文风或格式片段。当前选用预设启用正文约 362 字,可忽略。换长预设时它变成稳定前缀,应放在易变内容之前。
|
||||||
|
- 工具结果:循环后半才出现。读取能力的返回体是该能力的任务、原则、产物规格、评分和库目录,一份约 5000 字。库条目正文同理,目录里只有摘要。这些全文一开始不在上下文里。
|
||||||
|
|
||||||
|
游玩一次生成:
|
||||||
|
|
||||||
|
- 预设与该 Worker 的系统说明(相对稳定)。
|
||||||
|
- 槽位人设。
|
||||||
|
- 静态规格投影(舞台、规则、叙事、正文组成等,开局后很少改)。
|
||||||
|
- 对话历史(每轮变长,按投影裁剪)。
|
||||||
|
- 变量当前值与本轮用户输入(每轮都变)。
|
||||||
|
|
||||||
|
总管工具循环(决定何时调用哪个 Worker)另有一套 message 历史。最近三天的账几乎不在这条上,优化时不要把它和创作落盘混成同一次请求。
|
||||||
|
|
||||||
|
能力的完整写法存放在各自的提示文件里,不在系统目录里。不把所有能力的完整提示放进第一次请求。完整写法只在已经确定本轮要写哪几项之后进入上下文。
|
||||||
|
|
||||||
|
|
||||||
|
三、相位机之内,程序可以动哪里
|
||||||
|
|
||||||
|
运行分两层。
|
||||||
|
|
||||||
|
业务阶段:创作(设计规格)然后游玩。这是作品生命周期,不是每一次模型调用。
|
||||||
|
|
||||||
|
运行相位:idle、running、waiting_user、done、error。相位机是纯函数:给定会话和事件,决定谁在场、哪些工具可用、何时必须等用户、产物何时算数。它不决定「下一步叫哪个能力」。下一步由 running 里的模型工具循环决定。
|
||||||
|
|
||||||
|
一句用户话是硬事件:相位进入 running,这一轮的模型调用次数从零计。创作落盘在这次 running 里跑工具循环,上限大约三十多步。循环开始时拼好开头,之后只追加助手工具调用和工具结果。模型不再欠请求、或写出给用户的回复之后,相位回到 waiting_user,等待下一句。
|
||||||
|
|
||||||
|
因此:
|
||||||
|
|
||||||
|
- 减少「读一份、写一份、再读下一份」必须发生在同一次 running 里,也就是两次用户话之间。
|
||||||
|
- 不能把相位机改成按固定工序自动跑完全部节点。用户不手选节点,方案也不是执行顺序。
|
||||||
|
- Worker 执行时总管暂停。游玩的那一次生成是 Worker 自己的请求,上下文按该 Worker 的投影拼,不复用创作工具循环的消息历史。
|
||||||
|
- 程序可以在两次模型请求之间插入确定性步骤:注入选中能力的当前提示、核对版本哈希、逐条校验、只重试失败的那一条。这些不是相位变化。
|
||||||
|
|
||||||
|
|
||||||
|
四、缓存优化
|
||||||
|
|
||||||
|
目标是稳定前缀留在同一上游的提示缓存里。不追求两次请求的整包字节相同。Agent 每一轮消息都会变。
|
||||||
|
|
||||||
|
已经接上:OpenRouter 请求体带本局 session_id(最长 256 字)。直连其它渠道不带,避免未知字段被拒绝。有 session_id 后,粘滞从该会话第一次成功请求就开始,不必先看到缓存命中,也不再因为第一条用户消息被重写就换上游。不要发送 provider.order,它会关掉粘滞。
|
||||||
|
|
||||||
|
接上之前的测量(不要把「再做一遍粘滞」当成主体):
|
||||||
|
|
||||||
|
- 创作工具循环间隔约 7 到 15 秒。Gemini 上同一次循环内缓存从约 3800 token 按大约 4000 一块爬到 7600、11500、15000,整段 prompt 约 50% 到 68% 命中。每个 burst 的第一下经常是 0。中途偶尔整段掉回 0,是换了上游。
|
||||||
|
- 游玩在 OpenRouter 上 28 次、prompt 1.8 万到 3.4 万,间隔约 5 到 22 分钟,缓存为 0。直连单一上游时,间隔 8 到 30 分钟仍常见命中 80% 到 91%。有一轮从约 1.1 万掉到 6656 并停住,说明前缀中段改过,只保住开头。
|
||||||
|
- Gemini 隐式缓存大约 4000 token 一块记账。前缀差几百 token,账单上可以差一整块。
|
||||||
|
|
||||||
|
session_id 只保证落到同一上游。命中到第几个 token,取决于前缀从哪一字开始和上一次不同。
|
||||||
|
|
||||||
|
|
||||||
|
五、上下文编排顺序
|
||||||
|
|
||||||
|
原则:越少改的越靠前,每轮都变的放在最后。一处改动只作废它后面的缓存。
|
||||||
|
|
||||||
|
游玩已按此排列:人设与静态规格、对话历史、变量与本轮输入。排序本身在直连上游上有效;OpenRouter 上曾经是 0,是因为当时没有粘住上游,不是因为顺序反了。
|
||||||
|
|
||||||
|
创作当前顺序是:系统提示(含会随落盘改写的方案进度)、然后一条用户消息里先全部产物再全量对话、工具定义在消息之外、工具结果接在末尾。产物任何一份被覆盖,如果它不在整包的末尾,它后面的产物和对话都会离开缓存。工具定义一轮内稳定,适合留在前缀可缓存的区域。方案进度放在系统提示里,方案一改,系统提示之后全部重计。
|
||||||
|
|
||||||
|
选择与填写若拆成两次请求,稳定的能力目录应在选择请求的前部,方案进度、上一问和本轮用户输入在末尾。填写请求里,少改的产物在前,本轮新注入的节点提示和最近对话在后。节点提示每轮都会换,不能放在全部产物之前,否则一换节点,后面整包产物都按未缓存计。
|
||||||
|
|
||||||
|
|
||||||
|
六、上下文发送哪些
|
||||||
|
|
||||||
|
用同一本书量过,不发请求。当前一次创作请求的 messages 约 88218 字,末次实际 prompt 约 46897 token(大约 2 字 1 token)。
|
||||||
|
|
||||||
|
- 选择若只带方案进度加用户最后一句:约 4522 字。再加助手上一问仍然很小。产物正文不要放进选择。只看用户这句原文不够,否则回答「第二个」或修改已有地图时会对错节点,已写过的能力会被再选。
|
||||||
|
- 填写若仍带全部产物,对话只留最近约两轮(样本 1770 字):约 6.2 万字。砍掉全量对话大约只省两成。
|
||||||
|
- 再注入一份节点提示约 5000 字。一次注入两三份,是在产物之上加一两成,而且只发送一次。
|
||||||
|
|
||||||
|
所以「少发对话」省得有限。「少发哪些产物」才碰到大头,但填写时不带相关产物就会和已有设定冲突。哪些产物是这份能力的材料、哪些只是其它规格,本文没有一份可执行的白名单。常见的相关性裁剪、摘要、检索如果要用,必须说明误裁后怎样发现。
|
||||||
|
|
||||||
|
模型在看见该能力当前版本的任务、原则、规格、评分之前,写出来的正文不算数。程序用这些段落的哈希做版本。同一轮生成里先调用读取、同时把正文交上去,不算看见过写法。不要求先读的只有 narrative、variable-context、context-order、worker-spec、refine 和标记为自动的程序步骤。库条目正文同样要先看见再写进产物,默认 1 到 2 条。
|
||||||
|
|
||||||
|
|
||||||
|
七、程序上的优化,仍在同一次 running 里
|
||||||
|
|
||||||
|
现在的倍数来自工具循环,不是来自相位来回切换。
|
||||||
|
|
||||||
|
同一项内容能力至少两次模型请求:第一次凭短说明选定并读取,第二次看见写法之后写入。提示要求一轮通常写这句已经说清的 2 到 3 个能力。模型常一次只返回一个工具,于是变成读甲、写甲、读乙、写乙,最后再单独回用户一句。驱动程序其实会在同一步执行该次响应里的全部工具,日志里偶发一次写两份。
|
||||||
|
|
||||||
|
讨论过、尚未定案的程序形状,供你对照业界做法,不是已经同意的设计:
|
||||||
|
|
||||||
|
- 同一次 running 里两次模型请求。第一次只输出能力 id 和库条目 id。程序注入对应提示并记下版本。第二次一次交回多份产物和一段给用户的回复。某一条校验失败只重写那一条。
|
||||||
|
- 若模型一次只能调用一个工具,用一次结构化输出携带多条产物,程序在本地切开。这只去掉写入侧的重复前缀。规格说明仍须在这次生成之前就在上下文里。
|
||||||
|
- 写开场前有一次程序准备:编译信息可见范围、游玩执行方式、上下文排序和运行配置。这一步不应再变成一次让模型自由发挥的长上下文请求。
|
||||||
|
|
||||||
|
必须仍然成立的行为:用户不手选节点;方案不是执行顺序;没说清的「必须」和「有必要」本轮不写,先问 2 到 3 个选项;同轮多份产物只给一次综合回复,不把完整 JSON 贴进回复;变量和映射不走普通写入。
|
||||||
|
|
||||||
|
|
||||||
|
八、请你列出的常见方案,以及针对我们的组合
|
||||||
|
|
||||||
|
常见方案请覆盖至少这四类,每类不要只写一句口号。我们没给仓库,所以请把业界做法写全,包括你最后不推荐的。我们会删掉不适合的。
|
||||||
|
|
||||||
|
1. 缓存:粘滞路由、前缀稳定性、缓存块粒度、显式缓存断点、把变化赶出前缀。session_id 已经有了,请写它解决不了的部分。
|
||||||
|
2. 编排顺序:稳定段、历史、本轮输入的常见排序,以及中段一改、后段全失效时业界怎么处理。
|
||||||
|
3. 发送哪些:全文产物、摘要、按能力取材料、检索、滑动窗口、把目录和正文拆开。请写误删材料的后果。
|
||||||
|
4. 程序:一次多工具、结构化输出一次交多份、选择与填写拆请求、把规格预注入从而取消读取轮、限制工具循环次数。请写哪种依赖模型能力,哪种只依赖程序。
|
||||||
|
|
||||||
|
然后给出你建议我们采用的组合:一句用户话几次请求、每次上下文块的顺序、预计相对现在少发送几次大前缀、产物那约 6 万字还在不在填写请求里。故意不做的项写原因。和第三节、第六节冲突的地方单独列出,不要写进推荐步骤。
|
||||||
Reference in New Issue
Block a user