bug: 长 prompt 场景缓存命中率低(XD 网关路线缺缓存断点),且长输入一次性粘贴缺少思考拆解
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 问题描述 / What happened
长 prompt(大工具集 + 长 system 前缀)会话中存在两个相互叠加的效率问题:
1. **prompt 缓存命中率低**:部分路线下缓存持续 miss,每轮请求都全量重读长前缀,延迟与费用明显偏高。期望行为:稳定前缀(tools + system)在会话内应持续命中缓存。
2. **长输入一次性粘贴缺少思考与拆解**:用户把整份需求文档 / 多任务混合的大段文本一次性粘贴进输入框时,原文全量进入单条 user 消息,模型容易囫囵吞枣、遗漏子任务。期望行为:长输入场景下模型应先重述理解、拆解为有序子任务,再逐项执行并核对遗漏。
## 环境 / Environment
- Cindy 版本或 commit / version or commit: main @ 9d41cb29(代码走读结论,与运行版本无关)
- 平台与版本 / platform & OS version: 全平台
- 安装方式 / install method: 源码
## 复现步骤 / Steps to reproduce
**缓存问题**
1. 使用 Codex agent,模型走 XD 网关路线(`responses-anthropic-bridge` 翻译)。
2. 发送请求使入站 body 不带 `prompt_cache_key` 字段。
3. 观察上游收到的 Anthropic 请求:没有任何 `cache_control` 断点,缓存必然 miss。
4. 即使带了断点的路线,断点 TTL 恒为默认 5 分钟;回合间隔超过 5 分钟(长工具执行、用户阅读思考)后整个长前缀全量重写。
**长输入问题**
1. 在输入框一次性粘贴超过折叠阈值(24 行或 4000 字符)的长篇需求文本并发送。
2. 输入框折叠只是 UI 层(`pastePipeline.ts` 的 PastedTextChip),agent 收到全量原文、无任何附加指引。
3. 观察模型行为:是否拆解子任务全凭模型自觉,长输入下常见遗漏。
## 根因分析
### 缓存:断点门控与 TTL 都在桥接层
- `packages/responses-anthropic-bridge/src/translate-request.ts` 的断点门控原为 `options.promptCaching !== false && (Boolean(raw.prompt_cache_key) || options.automaticPromptCaching === true)`。XD 网关路线 `promptCaching=true`、`automaticPromptCaching=false`(`codex-proxy-host.ts`),是否放断点完全取决于 Codex app-server 是否恰好带 `prompt_cache_key`——而该字段桥接后即被丢弃,纯属被借来当开关,行为不可控。
- 全仓没有任何代码使用 `ttl: '1h'`(`types.ts` 的 `CacheControl` 已声明该字段但无人使用)。
- 已排除 prompt 组装层:`packages/maker-core` 的 system 各段均为会话启动一次性快照,无每轮变动的易变内容注入前缀,工具列表会话内顺序稳定。
### 长输入:全链路无任何长输入特殊处理
- 链路:输入框 → `makerChatStore` → IPC → `agent-input-coordinator` → `makerSendTransaction` → 各 agent。送出前的加工只有交接前缀、手机渠道说明、IM 渠道说明等 wire 层 note,**没有**针对长输入的阈值判定、摘要或拆解指引。
- system prompt 中也没有「先理解拆解再执行」的段落(且按仓库规则 §3.1/§4,条件化内容不应进 system 段)。
- plan 模式与 goal 模式均为纯手动触发,无按输入特征自动触发的先例。
## 优化方案
### 缓存(修复已就绪,稍后提 PR)
1. 断点门控只看 provider 级能力开关(`promptCaching === true`),不再依赖入站 `prompt_cache_key`——host 本来就按 provider 判断了上游是否支持缓存。
2. 稳定前缀(tools + system,长 prompt 的主体、逐轮逐字节不变)使用 `ttl: '1h'`,移动的对话尾部保持 5m 默认,兼顾命中寿命与写放大成本。
影响面:仅 Codex 通道经 `responses-anthropic-bridge` 的路线(XD 网关、官方 Anthropic 上游);用户自定义 provider 维持 `promptCaching=false` 不变;订阅直连方向(`anthropic-responses-bridge` 丢弃 `cache_control` 仅靠 `prompt_cache_key`)是另一条链路,不在本次范围内。
### 长输入拆解(实现中,稍后提 PR)
采用 wire 消息 note 方案(与手机客户端说明 `mobileClientPromptNote` 同层同语义):
- 在 `makerSendTransaction.ts`(send 路径)与 `register.ts`(steer 路径)按长度阈值(与输入框折叠口径一致:24 行 / 4000 字符)判定,向 wire payload 前置一段逐字节稳定的固定提示:先重述理解、拆解为有序子任务清单、歧义影响执行方向时先澄清、完成后对照清单确认无遗漏。
- 只进喂给 agent 的内容,不进落库/显示的用户消息原话;不进 system 段,零缓存率影响,绕开 §4 门禁;三 agent(claude-code / codex / pi)天然一致。
曾评估的备选:Ghost `screenUserMessage` rewrite(有 16000 字符上限且串行 LLM 调用增加延迟)、自动进 plan 模式(语义偏移、三 agent 行为差异大)、旁路 LLM 预拆解(额外延迟与费用)。wire note 成本最低、最贴合现有架构,更强的约束可后续叠加。
## 日志与截图 / Logs & screenshots
无(代码走读结论)。可用 `UsageTracker.getCacheStats()` 的 session 级缓存命中桶(`packages/maker-core/src/agents/shared/usage-tracker.ts`)观测修复前后的命中率变化。
Contributor guide
Research direction
Start with packages/responses-anthropic-bridge/src/translate-request.ts and codex-proxy-host.ts to trace prompt-cache breakpoint gating and TTL handling. Then read makerSendTransaction.ts and register.ts for long-input wire notes, using the 24-line/4000-character threshold described in the issue. Check packages/maker-core/src/agents/shared/usage-tracker.ts and confirm cache behavior and agent handling of long inputs match the stated expectations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ai, backend-api-design
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 42/100