posts/claude-code-model-switch-hooks.md
Claude Code 换模型,省下的 token 可能不够重建缓存
PreModelSwitch 出现在更新日志里的那一刻,/model 就不再只是一个选择菜单。
以前切模型很像换挡。任务简单,切个便宜模型。代码难啃,换回 Opus。上下文快满了,索性开个新会话。谁切的、为什么切、冷缓存花了多少 token,通常散在聊天记录、账单和某个人的记忆里。
现在 Claude Code 给这个动作前后各装了一道门。
2.1.251 的官方更新日志 加入了 PreModelSwitch 和 PostModelSwitch 两个 hook。切换前可以阻断、确认或补充说明,切换后可以接住事件继续处理。恢复会话时,SessionStart 还会拿到会话陈旧程度与预计缓存重建成本。
同一版里,/cost 开始展示单会话 prompt cache 的命中率、miss、重新写入的 token 和 warm/cold 状态。通过 Claude apps gateway 且设置了消费上限的开发者,也能在 /usage 和状态栏看到 spend limit。
好家伙,几条看起来各管一摊的更新,拼起来刚好是一条链。
模型切换之前做决策,切换之后留证据,缓存指标告诉你这次换挡到底花了多少钱,预算条再决定这台车还能不能继续跑。
我一直觉得,Agent 工具真正开始进入团队,不是看它又会写哪种语言,而是看这些不起眼的运行时动作能不能被管理。模型切换就是其中一个。它以前太像个人偏好,现在终于有机会变成工程策略。
最容易漏算的,不是模型单价
很多朋友可能不知道,prompt cache 不是一个跨模型通用的压缩包。
Claude Code 的缓存文档 讲得很直白。系统会缓存已经处理过的 system prompt、工具定义和对话前缀,后续请求可以读取这些内容,不用每轮从头算。缓存读取的价格大约是标准输入价的十分之一,所以长会话一旦热起来,速度和费用都会舒服很多。
但模型一换,原来的缓存不能直接当作新模型的缓存继续用。新模型要重新处理前缀,下一轮可能更慢,也会产生 cache creation token。
这就出现了一个挺荒诞的场景。
团队为了省钱,把一段看似简单的收尾任务从贵模型切到便宜模型。模型单价确实降了,结果前面塞着仓库说明、工具定义、十几轮调试记录和一大块代码上下文。便宜模型先把这堆东西重新吃一遍,刚省下来的钱,转头交给缓存重建。

省了个寂寞。
坦率讲,不能据此得出「长会话绝对不要换模型」。有些任务边界足够清楚,切换后剩下的工作又足够长,重建一次完全划算。问题在于,过去这个决定经常只看每百万 token 的价目表,很少把当前上下文体积、缓存冷热、后续还剩几轮和延迟一起算。
2.1.251 把这笔暗账摆到了台面上。/cost 能看 hit ratio、miss、re-cached token 和 warm/cold,状态栏脚本还能收到 prompt_cache 对象。模型路由至少不用再靠「感觉这轮应该便宜点」拍脑袋。
我会怎么理解这个变化呢。
模型选择从一个静态配置,变成了带迁移成本的运行时决策。像数据库换主库,不能只比较两台机器的单价,还得算数据搬迁、缓存预热和切流风险。模型没那么重,但道理差不多。
所以一个靠谱的路由判断,至少要同时看任务风险、目标模型价格、当前上下文大小、预计 re-cache token、剩余任务长度和可接受延迟。这里任何一个值缺席,「自动选最便宜模型」都可能只是漂亮的账面优化。
两个 hook,把个人手滑变成团队策略
Claude Code 的 hook 文档 把 hook 定义为固定生命周期事件触发的处理器。事件到了就运行,不需要等模型临场想起来。处理器可以是本地命令、HTTP endpoint、MCP tool,也可以是 prompt hook 或 agent hook。
这个区别很关键。
如果团队规定,涉及生产配置、身份权限、支付逻辑和安全审计的会话不能降到某些模型,最不稳的做法是往 CLAUDE.md 里写一句「请记住不要切」。模型可能理解,也可能在长会话里把这条埋进压缩摘要。确定性的安全门禁,不该寄希望于一次语言模型判断。
PreModelSwitch 更适合站在门口。
它可以检查目标模型是不是项目白名单里的成员,当前任务是否触及受保护目录,或者这次切换是否需要人工确认。普通文档整理可以放行,准备改 Terraform、数据库 migration 或鉴权代码时,策略可以更严。具体条件当然要按团队风险模型定,不是把所有人锁死在最贵模型上。
PostModelSwitch 则适合负责留痕。
从哪个模型切到哪个模型,谁触发的,属于哪个项目和会话,为什么切,策略结果是什么,这些信息可以送进团队已有的日志或审计系统。日志里别塞 prompt 正文、密钥和客户数据,不然安全功能转眼变成新的泄漏口。这个坑很常见,观测系统什么都想收,到头来自己成了数据沼泽。
厉害了,模型路由终于可以像部署、权限变更和密钥轮换一样,拥有自己的事件边界。
但这里有一条很容易被忽略的线。
官方文档 里既有 command hook,也有会调用模型做判断的 prompt hook 和能读代码库的 agent hook。后两种适合处理需要语义判断的情况,代价是判断本身又回到了模型。要做硬白名单、硬预算或合规拒绝,优先用确定性处理器,或者把决策交给团队控制的服务端策略系统。
别拿一个 Agent 去问另一个 Agent,这次换模型合不合规,然后把它的「应该没问题」当防火墙。这个画面挺赛博,安全性就不怎么赛博了。
真正值钱的是把四层控制接起来
两个 hook 本身不复杂。有点子牛逼的地方,是它们正好能把过去断开的几层控制接起来。

最里面是任务层。当前工作是在改 README,还是准备动生产权限。任务风险决定了可接受的模型和 effort level。模型配置文档 提醒,同一个 effort 名称在不同模型上不代表完全相同的底层预算,支持范围也不同。光记住 low、medium、high、xhigh、max 五个词,不等于有了统一量尺。
再往外是客户端策略层。PreModelSwitch 负责在动作发生前给出允许、拒绝或确认,PostModelSwitch 负责补上结果和理由。项目级 hook 可以跟仓库走,用户级 hook 留在个人机器,配置文档 还提供 managed settings,让组织限制只允许受管 hook,并收紧 HTTP hook 可以访问的地址。
第三层是成本与缓存。prompt_cache 告诉你这次切换有没有把一个热会话打冷,/cost 告诉你重新写入了多少 token。对启用了 gateway spend limit 的环境,rate_limits.spend_limit 又给出预算位置。注意,这个预算条不是所有 Claude 订阅用户都会有,官方限定了适用条件,别把一条企业网关能力写成全民福利。
最外面才是服务端控制。模型 allowlist、身份权限、请求预算、账单告警和 OpenTelemetry 审计,应该放在用户不能随手绕开的控制面。成本管理文档 也把 usage、上下文、缓存、模型选择和团队限额放在一起谈,方向很清楚,省 token 不是单点小技巧,而是一套系统行为。
怎么说呢,本地 hook 像门口保安,反应快,也离开发者最近。网关策略像门禁系统,决定哪张卡真的能开门。财务限额像总闸,到了线就停。三者谁也替代不了谁。
如果只装 PreModelSwitch,然后把网关权限开得四通八达,安全感大概率来自那条绿色日志,不是来自真实边界。
别急着复制一段 JSON
这次更新还有一个很现实的细节。
官方 changelog 已经公开了 PreModelSwitch 和 PostModelSwitch 能做什么,但相关事件的完整字段与配置示例未必会和二进制发布同时出现在所有文档镜像里。现在最危险的内容创作动作,就是看见两个新名字,顺手编一段看起来很像真的 settings JSON,让大家复制粘贴。
棒棒的,文章交付了,读者的 CI 也交代了。
更稳的落地顺序,是先升级并确认 claude --version,再从 /hooks 检查当前客户端实际识别到的事件、来源和处理器详情。拿一个非生产仓库测试允许、确认、拒绝和异常退出,确认日志里没有 prompt、token、密钥与客户数据。再把项目级策略提交进仓库,让同事 review。
缓存这块也别只看一轮。先记录切换前后的 warm/cold、cache miss、re-cached token、响应延迟和后续轮数,再判断切换是否划算。一次长上下文重建很贵,但如果后面还有几十轮,成本可能很快摊薄。反过来,只剩一个收尾 prompt 时,切模型往往没有账面上那么香。
说真的,这些步骤没有「让 Agent 自动选择最优模型」听着性感。它们更像给一辆快车装仪表盘、刹车和行车记录仪。
可生产系统大多就是靠这些不性感的东西活下来。
Claude Code 2.1.251 不是突然发明了模型路由,也没有替团队完成成本治理。它做了一件更朴素的事,把模型切换从菜单里的瞬间动作,变成了前后都能接住的事件,又把缓存与预算的暗账亮了出来。
回到开头那条 /model。
以后再按下去,选中的不只是一张模型名片。后面跟着的,应该还有策略、成本和一条查得到的航迹。