<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>YinFeng&apos;s Blog</title><description>吟风的个人博客，记录 AI、Agent、学习与思考。</description><link>https://www.windchant.online</link><item><title>Claude Code的缓存命中，为什么能帮你省这么多钱？</title><link>https://www.windchant.online/blog/20260412---claude-code/post</link><guid isPermaLink="true">https://www.windchant.online/blog/20260412---claude-code/post</guid><description>当你打开中转站去查找你的对话记录时，会明显察觉到除了输入输出token，还会有一个缓存读取token，而且这个缓存读取token的大小经常会是远远大于你的输入输出token。 很显然，这个缓存命中机制能帮你省下一大笔钱，那么它是怎么做到的呢？ 这篇文章，我会将Claude Code这套缓存命中机制讲清楚。 先说结论：C</description><pubDate>Sun, 12 Apr 2026 19:35:56 GMT</pubDate><content:encoded>&lt;p&gt;import WordPressHtml from &apos;@/components/blog/WordPressHtml.astro&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;WordPressHtml html={String.raw`当你打开中转站去查找你的对话记录时，会明显察觉到除了输入输出token，还会有一个缓存读取token，而且这个缓存读取token的大小经常会是远远大于你的输入输出token。&lt;/p&gt;
&lt;p&gt;很显然，这个缓存命中机制能帮你省下一大笔钱，那么它是怎么做到的呢？&lt;/p&gt;
&lt;p&gt;这篇文章，我会将Claude Code这套缓存命中机制讲清楚。&lt;/p&gt;
&lt;p&gt;Claude Code这里说的缓存命中，并不是传统意义上的&quot;问题一样，直接返回旧答案&quot;。&lt;/p&gt;
&lt;p&gt;它主要命中的是输入侧的 prompt cache。&lt;/p&gt;
&lt;p&gt;从源码里的usage统计就能看出来，Claude Code关心的是：&lt;/p&gt;
&lt;p&gt;而结论中的&quot;前缀&quot;通常包括：&lt;/p&gt;
&lt;p&gt;也就是说：发给模型的请求里，前面的一大段内容，在序列化之后要尽量完全一致。&lt;/p&gt;
&lt;p&gt;所以，这里的&quot;稳定&quot;其实非常严格：&lt;/p&gt;
&lt;p&gt;在源码中，有一个特别关键的常量：SYSTEM_PROMPT_DYNAMIC_BOUNDARY&lt;/p&gt;
&lt;p&gt;它的作用非常直接：将system prompt分成静态和动态两部分&lt;/p&gt;
&lt;p&gt;本质上就是在做：system prompt的前缀分层缓存。&lt;/p&gt;
&lt;p&gt;Claude Code的工具协议非常的厚，还带prompt，权限，UI渲染，语义标记等大量信息（这块后续也会更新）。但问题就来了：这些工具定义本身，也是会进入模型上下文的。&lt;/p&gt;
&lt;p&gt;这就意味着，一旦 tool schema 有变更，那么缓存就可能失效。&lt;/p&gt;
&lt;p&gt;Claude Code对此做了一个非常牛逼的处理：&lt;/p&gt;
&lt;p&gt;它不会每次都重新构造tool schema，而是将schema分成两层：&lt;/p&gt;
&lt;p&gt;per-request overlay里放的是可能每轮变化的字段，比如：&lt;/p&gt;
&lt;p&gt;Claude Code这里的思路是：不将所有消息都缓存，而是通过一些marker去控制：&lt;/p&gt;
&lt;p&gt;而且值得注意的是，注释中明确写到：每个请求只放一个message-level cache marker。&lt;/p&gt;
&lt;p&gt;这是为了让缓存边界更加稳定，避免多个marker让局部生命周期更复杂。&lt;/p&gt;
&lt;p&gt;通常情况，这个marker会放在最后一条message，意思是：&lt;/p&gt;
&lt;p&gt;它通常会设置成对应的 tool_use_id,作用是告诉服务端：&lt;/p&gt;
&lt;p&gt;这个旧工具结果已经在缓存前缀里了，后面可以通过这个引用识别并复用。&lt;/p&gt;
&lt;p&gt;这一步非常重要，因为在和Claude Code的对话里会有大量工具结果，而这些结果通常会很长，如果每一轮都把它们完整再塞一遍，成本会迅速膨胀。&lt;/p&gt;
&lt;p&gt;有了cache_reference，系统就可以更稳定地把工具结果纳入缓存前缀，而不是每次都当新内容重复读。&lt;/p&gt;
&lt;p&gt;它的作用是：告诉服务端，哪些旧的 cache_reference不值得保留，可以删掉。&lt;/p&gt;
&lt;p&gt;这意味着Claude Code的上下文治理，不是&quot;整个缓存失效重新算&quot;，而是：&lt;/p&gt;
&lt;p&gt;具体做法是：&lt;/p&gt;
&lt;p&gt;最直接的指标就是 usage：&lt;/p&gt;
&lt;p&gt;换句话说，改代码通常只是尾部发生变化，但是大面积的前缀并不会被修改。&lt;/p&gt;
&lt;p&gt;实用的原则大概以下几条。&lt;/p&gt;
&lt;p&gt;1. 尽量在同一个会话里持续推进&lt;/p&gt;
&lt;p&gt;不要刚做两步就重开一个全新会话。同一会话的公共前缀越稳定，越容易持续命中。&lt;/p&gt;
&lt;p&gt;2.不要频繁切模型&lt;/p&gt;
&lt;p&gt;模型一变，基本就不是同一条 cache key 了。&lt;/p&gt;
&lt;p&gt;3.不要频繁切影响请求形态的模式&lt;/p&gt;
&lt;p&gt;例如 fast mode、某些 agentic mode、beta 开关。这些状态抖动很容易改变前缀协议形态。&lt;/p&gt;
&lt;p&gt;4.MCP 和工具环境尽量一开始就定好&lt;/p&gt;
&lt;p&gt;不要中途频繁连上/断开 MCP server，不然工具集合和提示信息可能会变化。&lt;/p&gt;
&lt;p&gt;5.让变化尽量发生在“尾部”&lt;/p&gt;
&lt;p&gt;正常改代码没问题。真正要避免的是不断回头重写很早期的上下文条件。&lt;/p&gt;
&lt;p&gt;6.如果要 fork，就尽量让它们共享同一个稳定父上下文&lt;/p&gt;
&lt;p&gt;不要人为给每个 fork child 注入风格不同的大前缀。&lt;/p&gt;
&lt;p&gt;Claude Code之所以强，不只是它会调模型，而是它已经把“如何让模型在很多轮里持续高效工作”做成了一套系统工程。&lt;/p&gt;
&lt;p&gt;而缓存命中，正是这套工程的核心之一。`} /&gt;&lt;/p&gt;</content:encoded><h:img src="/_astro/hero.CMlunf1Z.jpg"/><enclosure url="/_astro/hero.CMlunf1Z.jpg"/></item><item><title>Learn-Claude-Code s01 - s04 学习记录</title><link>https://www.windchant.online/blog/20260329---learn-claude-code-s01-s04/post</link><guid isPermaLink="true">https://www.windchant.online/blog/20260329---learn-claude-code-s01-s04/post</guid><description>最近在看 learn-claude-code 的前四章，最大的感受是：一个 Agent 不是突然“变聪明”的，而是被一层一层搭出来的。 这四章刚好对应四个非常基础、但非常关键的能力： s01 ：让模型形成执行闭环 s02 ：让工具能力可扩展、可约束 s03 ：让多步任务有显式计划 s04 ：让复杂子任务在独立上下文里完</description><pubDate>Sun, 29 Mar 2026 22:52:54 GMT</pubDate><content:encoded>&lt;p&gt;import WordPressHtml from &apos;@/components/blog/WordPressHtml.astro&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;WordPressHtml html={String.raw`最近在看 learn-claude-code 的前四章，最大的感受是：一个 Agent 不是突然“变聪明”的，而是被一层一层搭出来的。&lt;/p&gt;
&lt;p&gt;这四章刚好对应四个非常基础、但非常关键的能力：&lt;/p&gt;
&lt;p&gt;Agent 的成长，不是单纯靠更强模型，而是靠更清晰的系统结构。&lt;/p&gt;
&lt;p&gt;模型本身只会生成内容，它并不能天然读文件、跑命令、看测试结果。真正让它开始“做事”的，是这样一个循环：&lt;/p&gt;
&lt;p&gt;没有这个 loop，模型再聪明也只是会说，不算真正能做。&lt;/p&gt;
&lt;p&gt;最重要的变化不是“工具变多了”，而是：&lt;/p&gt;
&lt;p&gt;所以，工具不仅是能力接口，也是约束边界。&lt;/p&gt;
&lt;p&gt;好的 Agent，不只是会调用工具，而是知道每种能力应该从哪个入口进入，并且入口本身是可控的。&lt;/p&gt;
&lt;p&gt;它的解决方法不是在prompt上做优化，而是直接加一个 todo 工具，把计划写成结构化状态。&lt;/p&gt;
&lt;p&gt;于是任务不再只是脑子里想的几步，而是明确写成：&lt;/p&gt;
&lt;p&gt;再加上“几轮不更新 todo 就提醒一次”，整个系统就开始具备一种持续推进任务的能力。&lt;/p&gt;
&lt;p&gt;很多人看到这里会先想到“多智能体协作”，但我觉得这章更重要的关键词其实是：上下文隔离。&lt;/p&gt;
&lt;p&gt;父 Agent 工作久了之后，messages 会越来越长。读文件、跑命令、分析结果，这些信息都会不断堆积，最后主上下文会越来越乱，重新开一个对话窗口的代价也很高。&lt;/p&gt;
&lt;p&gt;s04 的做法是：&lt;/p&gt;
&lt;p&gt;Subagent 不只是为了分工，更是为了把噪音挡在主上下文之外。&lt;/p&gt;
&lt;p&gt;复杂过程留在局部，主线只保留高价值信息，这才是它最实用的地方。&lt;/p&gt;
&lt;p&gt;所以我最后给自己的总结是：&lt;/p&gt;
&lt;p&gt;Loop 解决执行问题，Tool 解决边界问题，Todo 解决进度问题，Subagent 解决复杂度上升时的上下文问题。`} /&gt;&lt;/p&gt;</content:encoded><h:img src="/_astro/hero.6CV6xTKg.jpg"/><enclosure url="/_astro/hero.6CV6xTKg.jpg"/></item><item><title>大多数人不是不会学 AI，而是脑子里缺了这张 Agent 地图</title><link>https://www.windchant.online/blog/20260324---ai-agent/post</link><guid isPermaLink="true">https://www.windchant.online/blog/20260324---ai-agent/post</guid><description>从 LLM、token、prompt 到 RAG、tool calling、ReAct、memory、orchestration，为初学者画一张从模型到 Agent 系统的概念地图。</description><pubDate>Tue, 24 Mar 2026 14:03:25 GMT</pubDate><content:encoded>&lt;p&gt;import WordPressHtml from &apos;@/components/blog/WordPressHtml.astro&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;WordPressHtml html={String.raw`当我们聊 Agent 时，我们到底在说什么？&lt;/p&gt;
&lt;p&gt;用户输入
-&gt;
prompt 组装
-&gt;
模型开始 inference
-&gt;
生成输出 token
-&gt;
返回结果给用户或程序&lt;/p&gt;
&lt;p&gt;用户问题
-&gt;
LLM 判断是否需要工具
-&gt;
输出 tool call
-&gt;
程序执行工具
-&gt;
得到 tool_result
-&gt;
把 tool_result 塞回 context
-&gt;
LLM 继续判断
-&gt;
最终回答 / 继续调用工具&lt;/p&gt;
&lt;p&gt;用户问题
-&gt;
LLM 判断当前缺什么信息
-&gt;
决定一个动作
-&gt;
调用工具 / 检索资料 / 访问文件
-&gt;
观察返回结果
-&gt;
继续判断下一步
-&gt;
直到任务完成或停止&lt;/p&gt;
&lt;p&gt;用户问题
-&gt;
检索器提取关键词 / 向量特征
-&gt;
从知识库找相关片段
-&gt;
把片段拼进 prompt
-&gt;
LLM 基于资料生成答案&lt;/p&gt;
&lt;p&gt;用户任务
-&gt;
LLM 判断需要读取或修改文件
-&gt;
调用 read / write / edit / list 等文件工具
-&gt;
程序访问工作区文件系统
-&gt;
结果或文件变化回写 context
-&gt;
LLM 继续判断下一步&lt;/p&gt;
&lt;p&gt;聊天接口
只有 prompt + LLM + answer
-&gt;
工具增强应用
prompt + LLM + tool calling + ReAct
-&gt;
知识增强应用
prompt + LLM + ReAct + RAG + FileSystem Access + memory
-&gt;
Agent 应用
prompt + tools + ReAct + RAG + FileSystem Access + state + planning + orchestration + eval&lt;/p&gt;
&lt;p&gt;`} /&gt;&lt;/p&gt;</content:encoded><h:img src="/_astro/hero.6CV6xTKg.jpg"/><enclosure url="/_astro/hero.6CV6xTKg.jpg"/></item></channel></rss>