1. 为什么你的 Agent 越跑越贵:从一次真实账单说起
如果你正在用 Claude Code 或者自己搭的 Agent 跑长任务,大概率遇到过这种情况:前几轮响应很快,跑到二三十轮之后,每一轮都开始变慢,账单也肉眼可见地涨。你打开日志一看,模型输出并没有变多,工具调用也没暴增,但 token 消耗就是下不来。
问题往往不在模型,而在上下文结构。每一轮请求,系统都要把那一大段根本没变的内容重新处理一遍:系统提示词没变、工具定义没变、项目规则没变、常驻背景资料没变。但只要你是按普通请求发出去的,模型就得把这些内容从头再走一遍预处理。这就是所谓的“上下文税”——Agent 每走一步,都在为旧上下文重新付费。
Prompt Cache 要解决的就是这件事。它不是把提示词文本简单存起来,而是把模型已经算过的前缀状态保留下来。前缀不变就直接复用,只把这轮新增的动态尾部继续往后算。听起来偏底层,但落到工程上会立刻清楚:它把上下文第一次真正拆成了两类资源——应该尽量稳定的,和天然会增长的。
这篇教程面向用 Claude Code 等工具做上下文管理的开发者,会交付可复制的缓存命中配置、Agent 纪律检查清单,以及验证缓存命中率和上下文裁剪效果的具体动作。核心检索词就三个:Prompt Cache、Agent 架构纪律、上下文管理。适合谁?适合已经能跑通 Agent、但发现长任务成本失控、响应变慢、模型开始抓不住重点的人。如果你还在纠结怎么发第一条请求,这篇可以先收藏,等跑起来再回来看。
先说结论:Prompt Cache 看起来像一个省钱功能,最后一定会落到架构纪律上。因为缓存不是靠“意思差不多”命中的,它依赖前缀稳定。顺序变了、工具集合变了、模型切了,通常就失效。这会倒逼你把系统往更稳定、更克制的结构上收。下面从问题场景开始,一步步拆到可复制的配置和排障。
2. TaoToken 前置准备:把 Base URL、Key、Model ID 三件套配齐
在讲缓存配置之前,得先把请求通道搭好。不管你用 Claude Code、Cline 还是自己写脚本调 API,都需要三样东西:Base URL、API Key、Model ID。这三件套缺一个,请求就发不出去,后面所有缓存优化都无从谈起。
TaoToken 的 API 入口是 https://taotoken.net/api,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。注意 API 地址后面不加任何参数,保持干净。你需要先去控制台生成一个 API Key,然后把它写进环境变量或配置文件。
为什么强调这一步?因为很多人缓存配了半天没生效,最后发现是请求根本没走对通道,或者 Key 权限不对导致请求被拒。先把通道打通,再谈缓存命中,顺序不能反。
具体操作:登录后进入控制台,找到 API Keys 页面,创建一个新 Key。建议按用途分 Key,比如一个给 Claude Code 用,一个给脚本测试用,方便后面排查问题时隔离变量。创建完复制出来,只显示一次,丢了就重建。
然后配置环境变量。以 macOS/Linux 为例,在~/.zshrc或~/.bashrc里加:
export TAOTOKEN_API_KEY="sk-你的key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows 用 PowerShell:
$env:TAOTOKEN_API_KEY="sk-你的key" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api"Model ID 这块要注意,不同工具填法不一样。Claude Code 里通常填claude-sonnet-4-5这类模型标识,具体以你控制台里可用的模型列表为准。Cline 或 Continue 这类插件,Model ID 填在模型选择框里,Base URL 填https://taotoken.net/api,Key 填刚才生成的。
如果你用的是 Claude Code,还需要确认它读的是哪个配置文件。Claude Code 的配置一般在~/.claude/settings.json或项目级的.claude/settings.json。这个文件后面讲缓存配置时会重点用到,先记住路径。
配完之后别急着跑长任务,先用一条最简单的请求验证通道。可以用 curl:
curl https://taotoken.net/api/v1/messages \ -H "x-api-key: $TAOTOKEN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 64, "messages": [{"role": "user", "content": "说一句你好"}] }'返回里有content字段就说明通道通了。这一步过了,再往下配缓存才有意义。如果这里就报 401,先回去检查 Key 有没有复制全、有没有多余空格。通道验证是后面所有步骤的地基,别跳过。
3. 可复制的缓存命中配置:settings.json 与请求体怎么写
这一节是全文最核心的部分,直接给可复制的配置片段。缓存命中率上不去,九成问题出在前缀不稳定。而前缀稳不稳定,取决于你怎么组织请求体里的内容顺序。
先讲原理再给配置。一个 Agent 请求大致拆成两部分:静态前缀和动态尾部。静态前缀包括系统指令、工具定义、项目规则、长期不变的背景资料;动态尾部包括用户消息、工具输出、终端观察结果、这轮新增的上下文。真正贵的是前面那一大段静态前缀,Prompt Cache 要复用的也是它。
所以配置的核心思路只有一条:把最稳定的内容放最前面,把会变的内容放后面,并且给稳定部分打上缓存标记。
以 Claude Code 的settings.json为例,路径是~/.claude/settings.json,可复制片段如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的key", "ANTHROPIC_MODEL": "claude-sonnet-4-5" }, "permissions": { "allow": ["Read", "Write", "Bash"] }, "context": { "maxTokens": 200000, "compactThreshold": 0.8 } }这里ANTHROPIC_BASE_URL填https://taotoken.net/api,ANTHROPIC_API_KEY填你的 Key,ANTHROPIC_MODEL填 Model ID。三件套齐了。compactThreshold是压缩触发阈值,0.8 表示上下文用到 80% 时触发压缩,这个值后面讲压缩时会再提。
如果你是自己写脚本调 API,请求体里的缓存标记要这样写。以 Anthropic 风格的 messages 接口为例:
{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "system": [ { "type": "text", "text": "你是一个严谨的编码助手,遵守以下项目规则……", "cache_control": {"type": "ephemeral"} } ], "tools": [ { "name": "read_file", "description": "读取指定路径的文件内容", "input_schema": { "type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"] } } ], "messages": [ {"role": "user", "content": "帮我看看 src/index.ts 的结构"} ] }关键在system数组里那个cache_control: {"type": "ephemeral"}。它告诉服务端:这段前缀可以缓存,后续请求如果前缀一致就直接复用。工具定义放在tools里,顺序要固定,别今天加一个明天删一个,否则前缀就飘了。
如果你用 Cline 或 Continue 这类插件,配置在插件的设置界面里。Base URL 填https://taotoken.net/api,API Key 填你的 Key,Model ID 填claude-sonnet-4-5。插件通常会自动处理缓存标记,但你要保证的是:项目规则文件(比如.clinerules)内容稳定,别每次会话都改。
再强调一遍顺序纪律:系统提示词 → 工具定义 → 项目规则 → 长期背景 → 用户消息 → 工具输出。前面几层越稳定,缓存命中率越高。很多人把运行时状态塞回系统提示词,想让模型“记住现在到哪个阶段了”,结果最适合缓存的部分变成了最不稳定的部分,缓存永远热不起来。正确做法是把状态放到后面的用户消息或外部文件里,前缀保持不动。
配置写完,先别跑长任务。用一条带缓存标记的请求测一下,看返回里有没有cache_creation_input_tokens和cache_read_input_tokens字段。第一次请求应该是 creation 有值、read 为 0;第二次发同样的前缀,read 应该有值。这一步验证过了,再往下走。
4. 验证缓存命中与上下文裁剪:看哪几个指标、怎么读
配置写完不等于生效,必须验证。这一节给具体的验证动作和指标读法。很多人配了缓存但命中一直不高,往往不是统计问题,而是结构问题。学会看指标,才能定位到是哪一层在拖后腿。
先看三个核心字段:cache_creation_input_tokens、cache_read_input_tokens、input_tokens。第一个是这轮新建缓存消耗的 token,第二个是这轮命中缓存复用的 token,第三个是没走缓存的普通输入 token。理想状态下,长会话跑到后面,cache_read_input_tokens应该占大头,cache_creation_input_tokens只在缓存失效后重建时出现。
验证步骤一:发两次相同前缀的请求。第一次请求,观察返回:
{ "usage": { "input_tokens": 120, "cache_creation_input_tokens": 9600, "cache_read_input_tokens": 0 } }这说明 9600 token 的静态前缀被写入了缓存。第二次发同样前缀、只改用户消息的请求:
{ "usage": { "input_tokens": 130, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 9600 } }cache_read_input_tokens变成 9600,说明前缀命中了。如果第二次cache_creation_input_tokens还是 9600,说明前缀没稳住,回去检查系统提示词或工具定义是不是变了。
验证步骤二:看上下文裁剪效果。Claude Code 里有几个命令可以直接用。/context list查看窗口有多满、每个注入文件的大小;/context detail深入看每个工具 schema、每个 Skills 条目的 token 占用;/usage tokens在每次回复后附加使用量明细;/status包含会话 token 数和估算费用。
跑一个长任务,每隔十轮记录一次/context detail的输出。你会看到固定开销(系统提示词 + 工具 schema)基本不变,动态开销(对话历史 + 工具输出)持续增长。当动态开销逼近窗口上限时,压缩应该被触发。如果压缩没触发,检查compactThreshold是不是设太高了。
验证步骤三:测压缩前后的 token 变化。手动触发一次压缩(Claude Code 里是/compact),对比压缩前后的/status输出。正常情况下,压缩后总 token 应该明显下降,但关键信息(架构决策、已修改文件、未完成事项)要保留。如果压缩后模型开始“失忆”,说明压缩指令没写清楚,需要在 Compact Instructions 里明确哪些信息绝对不能丢。
一个实测下来比较稳的节奏:每 20 到 30 轮做一次/context detail检查,动态开销超过窗口 60% 就考虑压缩,超过 80% 必须压缩。缓存命中率低于 50% 时,优先查前缀稳定性,而不是急着换模型。
这里给一个排查用的对照表,方便你快速定位:
| 现象 | 可能原因 | 检查动作 |
|---|---|---|
| cache_read 一直为 0 | 前缀不稳定或没打缓存标记 | 检查 system 里有没有 cache_control |
| cache_creation 每轮都有 | 工具定义或系统提示词在变 | 对比两轮请求的 tools 顺序 |
| 命中率突然掉 | 模型切换或 TTL 过期 | 确认 Model ID 没变,检查会话空闲时长 |
| 压缩后质量下降 | 压缩指令没保留关键信息 | 补充 Compact Instructions |
验证这一步别偷懒。缓存和压缩都是“配了不一定生效”的东西,只有看指标才能确认。指标正常了,再回到长任务里跑,成本曲线会明显平缓。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
这一节对照真实报错,给排查路径。这些错误我在不同工具里都碰到过,按出现频率排序。
401 Unauthorized。最常见,也最好查。原因通常是 Key 不对、Key 过期、或者 Base URL 填错。先确认ANTHROPIC_API_KEY有没有多余空格,再确认ANTHROPIC_BASE_URL是https://taotoken.net/api而不是别的。如果 Key 是从控制台复制的,注意别把前后引号也复制进去。还有一种情况是 Key 权限不够,去控制台确认这个 Key 有没有对应模型的调用权限。
local proxy failed。这个报错通常出现在你本地配了代理层,但代理层没起来或者端口不对。排查顺序:先确认代理进程在跑,再确认端口和配置里写的一致,最后确认代理转发目标是不是https://taotoken.net/api。如果你没主动配代理,检查环境变量里有没有残留的HTTP_PROXY或HTTPS_PROXY,有的话先清掉再试。
reading choices 相关报错。这类错误一般出现在响应解析阶段,说明返回结构和你代码里预期的结构对不上。常见原因是 Model ID 填错,导致返回的是错误结构而不是正常的 choices 数组。检查ANTHROPIC_MODEL或请求体里的model字段,确认它和控制台里可用的模型标识完全一致。另外确认请求头里content-type是application/json,少了这个也可能导致解析异常。
OAuth 相关报错。如果你用的是 Claude Code 的 OAuth 登录流程,但同时又配了 API Key,两者可能冲突。Claude Code 优先走 OAuth 时,会忽略ANTHROPIC_API_KEY。解决办法是明确走 Key 模式:在settings.json里确保ANTHROPIC_API_KEY有值,并且没有残留的 OAuth token 文件。OAuth token 一般在~/.claude/下的某个凭证文件里,具体文件名以你版本为准,清掉后重启。
缓存不生效但没报错。这种最隐蔽。请求能通,返回正常,但cache_read_input_tokens一直是 0。排查顺序:先看 system 里有没有cache_control标记;再看工具定义顺序是不是每轮都在变;再看系统提示词里有没有动态内容(比如时间戳、随机 ID)。时间戳是缓存杀手,如果系统提示词里带了当前时间,每轮前缀都不一样,缓存永远命中不了。正确做法是把时间放到用户消息里,或者用工具按需获取。
压缩后请求失败。偶尔出现在压缩触发的边界上,压缩后的摘要加上近期消息超过了窗口限制。检查compactThreshold是不是设得太高,留的余量不够。调到 0.7 到 0.8 之间通常更稳。另外确认压缩摘要本身没有异常膨胀,如果摘要比原文还长,说明压缩指令有问题。
排查时记住一个原则:先看结构,再看模型。前缀稳不稳、工具漂不漂、历史该不该压缩,这三件事查完,再考虑是不是模型能力问题。顺序反了,容易越补越重。
如果你在排查过程中需要重新生成 Key 或查看文档,可以去 API Keys 页面和接入文档。验证模型行为是否正常,可以用模型对话页面直接测。长期跑编码或 Agent 任务,Coding Plan 会更合适。这几个入口按你的场景选,别只盯着首页。
6. 把缓存纪律变成 Agent 架构纪律:一份可执行的检查清单
走到这里,你应该已经能把缓存配起来、把指标看出来、把常见错排掉了。但 Prompt Cache 真正值钱的地方,不是省了多少钱,而是它逼着你回答一个更根本的问题:一个要连续运行几十分钟甚至几小时的 Agent,到底该怎么组织自己的上下文?
答案就是架构纪律。缓存命中率只是症状,上下文分层才是病根。下面这份检查清单,可以直接贴到你的项目 README 里,每次改 Agent 结构时过一遍。
第一,常驻前缀收短收稳。系统提示词、工具定义、长期项目规则,能不在运行时改的就别改。前缀越飘,缓存越难热。第二,状态变化往动态尾部放。当前阶段、任务进度、临时提醒,留在消息层或外部文件,别塞回系统提示词。第三,工具别堆太满。工具多不等于能力强,职责重叠、返回冗长、描述漂移的工具,只会让前缀越来越重。第四,能按需取的别预先全塞。文件路径、查询结果、外部存储,先保留引用,需要时再取。
第五,长任务离不开压缩。历史越长噪声越多,不主动压缩,主线程会变成日志堆场。压缩的目标是保留影响后续行动的部分:已确定的架构决策、未解决的关键问题、必须保留的约束、最近操作的关键对象。第六,重调查任务交给子 Agent。子 Agent 值钱的地方不只是并行,而是它把大量探索过程隔离在自己的窗口里,只回传摘要。主 Agent 只保留高信号结论。
第七,看缓存指标别只盯总 token。cache_creation_input_tokens、cache_read_input_tokens、input_tokens三个一起看,命中率低先查结构。第八,缓存不热时先看结构再看模型。前缀稳不稳、工具漂不漂、历史该不该压缩,这三件事查完再考虑换模型。
把这八条落到文件层面,就是一张分层表:系统提示词放工具列表和 Safety,最稳定,缓存收益最高;引导文件放构建命令和项目契约,控制在 2 到 3K token;Skills 只在系统提示词放元数据,内容按需加载;记忆层写磁盘,语义搜索取回;对话历史适时压缩,修剪旧工具结果;子 Agent 完全隔离,只回传摘要。
这套分层一旦清楚,很多取舍就有依据了。你会发现,Prompt Cache 不是让你去抠每一分钱,而是让你把上下文当成一套供应链来设计。哪些信息该常驻,哪些该按需,哪些该压缩,哪些该隔离,这四个问题回答清楚了,Agent 的架构纪律也就立起来了。
最后给一个可以直接用的动作:今天就去把你的系统提示词和工具定义固定下来,打上缓存标记,跑一个 30 轮的任务,记录每 10 轮的cache_read_input_tokens。如果这个数字在涨,说明你的前缀稳住了;如果一直是 0,回去查时间戳和工具顺序。这一个动作做完,你对 Prompt Cache 的理解会比读十篇原理文章都实在。