<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Prompt Cache on Codexer</title><link>https://codexer.com/tags/prompt-cache/</link><description>Recent content in Prompt Cache on Codexer</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 23 Aug 2026 09:00:00 +0800</lastBuildDate><atom:link href="https://codexer.com/tags/prompt-cache/index.xml" rel="self" type="application/rss+xml"/><item><title>一个 web_search 开关，账单涨了四五倍：复盘 Codex 在 Bedrock 上的提示词缓存事故</title><link>https://codexer.com/posts/2026-08-23-codex-bedrock-prompt-cache-cost/</link><pubDate>Sun, 23 Aug 2026 09:00:00 +0800</pubDate><guid>https://codexer.com/posts/2026-08-23-codex-bedrock-prompt-cache-cost/</guid><description>&lt;p&gt;设想这样一个场景：你把自己常用的 Codex CLI 挂到了 AWS Bedrock 上，用 GPT-5.6 Sol 跑日常的 agentic 编码任务。一切看起来正常，命令照跑，代码照写，模型响应也很快。直到你打开 Cost Explorer，发现这周的账单比预期多出好几百美元。你翻来覆去检查自己的用量，请求数没有暴涨，代码量也没变多，可钱就是凭空多出来了。&lt;/p&gt;
&lt;p&gt;这不是虚构。2026 年 8 月，一位开发者在 openai/codex 仓库提交了 issue #37674，记录的就是这样一起真实发生的事故。账单里多出来的那一大块，全是一个叫「cache-write」的东西。&lt;/p&gt;
&lt;h2 id="账本里凭空多出的-85"&gt;账本里凭空多出的 85%&lt;/h2&gt;
&lt;p&gt;issue 作者贴出的数据很直接：在 8 月 5 日到 8 日这四天里，他通过 Bedrock 跑了 3656 次请求，累计产生了 1.719 亿个 cache-write token。按照 Bedrock 的费率估算，光缓存写入这一项就烧掉了约 1182 美元，而同期总成本约 1386 美元。也就是说，85% 的钱都花在了「写缓存」上，而「读缓存」的 token 是零。&lt;/p&gt;
&lt;p&gt;这里需要先解释一个概念。所谓提示词缓存（prompt caching），指的是模型服务商把请求里重复出现的那段「前缀」缓存下来，比如系统提示词、工具定义、项目约定这些每次都一样的部分。下一次请求时，模型直接读缓存，不用重新计算，成本能降到正常输入的十分之一甚至更低。对应地，缓存有「写」和「读」两个动作：写缓存按正常 token 甚至略高的价格计费，读缓存则便宜得多。&lt;/p&gt;
&lt;p&gt;一个健康的 agentic 编码会话，应该是「写一次、读很多次」。第一次请求把稳定的前缀写进缓存，之后每一轮对话只处理新增的那一小段尾巴。可 issue 里的数据恰好反了过来：读是零，写却占了 85%。这意味着每一次请求，模型都在把整段前缀重新写一遍，从来没从缓存里读到过任何东西。&lt;/p&gt;
&lt;p&gt;作者还给了个更直观的观察：本地一次 76 个请求的 Codex 会话里，cache_write_input_tokens 高达 670 万，cached_input_tokens 是零，平均每个请求要写约 8.8 万个 token。而 CloudWatch 里没有任何客户端错误。也就是说，这不是报错，而是模型在「正常地」反复重写同一段内容，钱就是这么无声无息流走的。&lt;/p&gt;</description></item><item><title>我们搞定了 Prompt Cache，流水线反而更慢了：Codex App-Server 的四个反直觉真相</title><link>https://codexer.com/posts/2026-08-06-codex-app-server-performance-trap/</link><pubDate>Thu, 06 Aug 2026 09:00:00 +0800</pubDate><guid>https://codexer.com/posts/2026-08-06-codex-app-server-performance-trap/</guid><description>&lt;p&gt;你跟同事聊 Codex 的性能优化时，有没有听过这句话？&lt;/p&gt;
&lt;p&gt;「你每次都 spawn 一个新的 &lt;code&gt;codex exec&lt;/code&gt; 干嘛？直接跑个 app-server 复用线程啊，光 prompt cache 就值回票价了。」&lt;/p&gt;
&lt;p&gt;听起来太对了，对到没人会去质疑。开一个常驻服务进程，预热几条线程，所有请求走同一个会话，cache 命中率拉满，token 消耗直接腰斩，对不对？&lt;/p&gt;
&lt;p&gt;我们团队也是这样想的。然后我们测了四次。&lt;/p&gt;
&lt;p&gt;结果是：prompt cache 最终打到了 86% 的命中率，但流水线比最朴素的基线&lt;strong&gt;更慢&lt;/strong&gt;，原始 token 消耗反而&lt;strong&gt;多了 39%&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这篇文章讲的就是为什么，以及 app-server 到底是为谁设计的。&lt;/p&gt;
&lt;h2 id="背景两种运行模式"&gt;背景：两种运行模式&lt;/h2&gt;
&lt;p&gt;Codex CLI 有两种运行模式，很多人分不清它们的适用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;codex exec&lt;/code&gt;&lt;/strong&gt;：每次调用启动一个独立进程，执行完就退出。没有状态，没有记忆，每次都是「从头来过」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;codex app-server --stdio&lt;/code&gt;&lt;/strong&gt;：启动一个常驻服务进程，通过 JSON-RPC 协议维持长连接。可以创建线程、复用上下文、支持中断和恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从表面上看，app-server 显然是更「高级」的选择。进程复用省启动时间，线程上下文省 token，还有一套完整的会话管理协议（&lt;code&gt;thread/list&lt;/code&gt;、&lt;code&gt;thread/resume&lt;/code&gt;、&lt;code&gt;thread/fork&lt;/code&gt;、&lt;code&gt;turn/interrupt&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;但表面正确不等于实际正确。&lt;/p&gt;
&lt;h2 id="实验设计公平对比"&gt;实验设计：公平对比&lt;/h2&gt;
&lt;p&gt;我们的实验设计刻意保持了公平性。不搞连接池，不搞常驻守护进程。app-server 为每个 formation 私有启动、跑完所有 turn 后立即销毁，生命周期跟 &lt;code&gt;codex exec&lt;/code&gt; 子进程一模一样。&lt;/p&gt;
&lt;p&gt;这样对比的只有一件事：&lt;strong&gt;复用同一个线程到底能不能省钱省时间？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在正式测量之前，我们先写了一个探针程序去直连真实的 app-server，验证了八个关键行为，顺手挖出了三个文档里没写但会坑死你的细节：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cwd&lt;/code&gt; 和 &lt;code&gt;sandboxPolicy&lt;/code&gt; 是粘性参数&lt;/strong&gt;。如果你不每个 turn 显式传一遍，turn 3 会悄悄继承 turn 2 的写权限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;token 用量上报在 &lt;code&gt;thread/tokenUsage/updated&lt;/code&gt; 事件上&lt;/strong&gt;，不在 &lt;code&gt;turn/completed&lt;/code&gt;。等 turn 结束再去读，永远晚一步。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空线程不会被物化&lt;/strong&gt;。一个从未跑过任何 turn 的线程，你无法恢复它。线程池预热是个陷阱。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后探针跑了一个简单的两轮对话，turn 2 报告了 &lt;strong&gt;15,104 个缓存的输入 token&lt;/strong&gt;。Cache 管用！赶紧上线！&lt;/p&gt;</description></item></channel></rss>