<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>第 21 章 AI Agent 运行时 on Go 语言原本</title><link>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/</link><description>Recent content in 第 21 章 AI Agent 运行时 on Go 语言原本</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/index.xml" rel="self" type="application/rss+xml"/><item><title>21.1 Agent 控制循环</title><link>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/loop/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/loop/</guid><description>&lt;h1 id="211-agent-控制循环"&gt;21.1 Agent 控制循环&lt;/h1&gt;
&lt;p&gt;第 20 章讲的是 Go 服务一个模型,请求进、token 流出。可当下的系统往往更进一步:让模型不只回答，
还能&lt;strong&gt;调用工具、连成多步、自主推进&lt;/strong&gt;,直到把一个任务办完。这就是 &lt;strong&gt;AI Agent&lt;/strong&gt;。Agent 听起来很
玄,本书的立场一以贯之:把模型的智能搁在一边，只看运行时那一面。这样看，一个 Agent 不过是一个
&lt;strong&gt;控制循环&lt;/strong&gt;,而控制循环正是计算机科学最古老、最朴素的结构之一。这一章就用这个视角，
把 Agent 还原成一个 Go 程序员熟悉的并发问题。&lt;/p&gt;
&lt;h2 id="2111-把-agent-还原成一个控制循环"&gt;21.1.1 把 Agent 还原成一个控制循环&lt;/h2&gt;
&lt;p&gt;剥掉所有花哨的说法，一个 Agent 的核心是一个循环:&lt;/p&gt;

&lt;script src="https://golang.design/under-the-hood/mermaid.min.js"&gt;&lt;/script&gt;
&lt;script src="https://golang.design/under-the-hood/mermaid-init.js"&gt;&lt;/script&gt;


&lt;pre class="mermaid"&gt;flowchart TD
 start[&amp;#34;收到任务&amp;#34;] --&amp;gt; ctx[&amp;#34;把对话历史 &amp;#43; 可用工具&amp;lt;br/&amp;gt;发给模型&amp;#34;]
 ctx --&amp;gt; model[&amp;#34;模型回应&amp;#34;]
 model --&amp;gt; branch{&amp;#34;要调用工具吗？&amp;#34;}
 branch --&amp;gt;|&amp;#34;否：给出最终答案&amp;#34;| done[&amp;#34;返回结果&amp;#34;]
 branch --&amp;gt;|&amp;#34;是：请求调用某工具&amp;#34;| tool[&amp;#34;执行工具&amp;#34;]
 tool --&amp;gt; append[&amp;#34;把工具结果&amp;lt;br/&amp;gt;追加进对话历史&amp;#34;]
 append --&amp;gt; ctx&lt;/pre&gt;
&lt;p&gt;把这张图读一遍:把上下文（对话历史加可用工具的描述)发给模型;模型要么给出最终答案（循环结束），
要么请求调用某个工具;若是后者，就执行那个工具，把结果追加回上下文，再发给模型,如此往复。
所谓「自主」「智能体」，机制上就是&lt;strong&gt;这个带工具调用的循环&lt;/strong&gt;。模型负责在每一步决定「下一步做什么」,
而 Agent 运行时负责忠实地执行这个循环:维护上下文、调度工具、把结果喂回去。&lt;/p&gt;
&lt;p&gt;这个认识是解魅的，也是解放的。它告诉我们:写一个 Agent 框架，&lt;strong&gt;不需要任何机器学习&lt;/strong&gt;,需要的是
把一个循环、一组工具、一份不断增长的上下文，组织得正确、并发、可靠,而这些全是本书前面讲过的
东西。&lt;/p&gt;
&lt;h2 id="2112-它是一台状态机"&gt;21.1.2 它是一台状态机&lt;/h2&gt;
&lt;p&gt;把这个循环看得再细一点，它其实是一台&lt;strong&gt;状态机&lt;/strong&gt;。每一轮，Agent 处在某个明确的状态里:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;思考中&lt;/strong&gt;:等模型返回（一次第 20 章意义上的推理请求，往往是流式的)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;待执行工具&lt;/strong&gt;:模型已决定调用某工具，正准备或正在执行它。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;整合中&lt;/strong&gt;:工具有了结果，正把它追加进上下文。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完成 / 失败&lt;/strong&gt;:循环终止。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把这些状态&lt;strong&gt;显式&lt;/strong&gt;地写出来，而不是让它们隐式地散落在控制流里，在工程上有实打实的好处。其一，
&lt;strong&gt;可观测&lt;/strong&gt;:任一时刻都能报告「这个 Agent 卡在哪一步」,对调试和监控至关重要。其二，&lt;strong&gt;可持久化与
可恢复&lt;/strong&gt;:一个长跑的 Agent 任务可能要数分钟、调几十个工具，把状态显式化，就能把它存盘、在崩溃后
从断点续跑，而不是从头再来。其三，&lt;strong&gt;可控&lt;/strong&gt;:超时、重试、人工介入，都更容易挂在显式的状态转移上。
Go 没有花哨的状态机框架，但一个带 &lt;code&gt;state&lt;/code&gt; 字段的结构体加一个 &lt;code&gt;for&lt;/code&gt; 加一个 &lt;code&gt;switch&lt;/code&gt;,
朴素得正好:&lt;/p&gt;</description></item><item><title>21.2 工具调用与 MCP</title><link>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/mcp/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/mcp/</guid><description>&lt;h1 id="212-工具调用与-mcp"&gt;21.2 工具调用与 MCP&lt;/h1&gt;
&lt;p&gt;&lt;a href=".././loop"&gt;21.1&lt;/a&gt; 把 Agent 还原成了一个控制循环，而循环里最关键的那个动作是「执行工具」。
这一节就专讲工具:一个工具在机制上是什么，模型怎么决定调用它，运行时怎么把调用分发到真正的代码，
以及当工具的数量与来源爆炸式增长时，&lt;strong&gt;Model Context Protocol&lt;/strong&gt;(MCP)如何用一套协议把这件事
标准化。看清这一层，会发现工具调用本质上就是一次结构化的远程过程调用,而 RPC 正是 Go 的老本行。&lt;/p&gt;
&lt;h2 id="2121-工具是什么一个带-schema-的函数"&gt;21.2.1 工具是什么：一个带 schema 的函数&lt;/h2&gt;
&lt;p&gt;剥开「工具」这个词，它不过是&lt;strong&gt;一个带类型描述的函数&lt;/strong&gt;,由三部分组成:一个名字、一段给模型看的
自然语言描述、一份参数的结构化 schema(通常是 JSON Schema)。模型在每一步看到的，就是这样一组
工具的名字、描述与参数格式;它据此决定要不要调、调哪个、传什么参数，然后输出一个&lt;strong&gt;结构化的调用
请求&lt;/strong&gt;:工具名加一组 JSON 参数。运行时接到这个请求，查到对应的函数，反序列化参数，执行，
再把结果序列化回去。&lt;/p&gt;
&lt;p&gt;把这个流程念一遍，它的形状就是 RPC:&lt;strong&gt;名字 + 序列化参数 → 执行 → 序列化结果&lt;/strong&gt;。在 Go 里，
工具最自然地落成一个接口:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Tool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 分发用的键&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 给模型看的说明&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RawMessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 参数的 JSON Schema&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RawMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RawMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 分发就是一次 map 查找加一次反序列化&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RawMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;unknown tool: %s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ctx 一路透传，为 21.3 的取消埋好线&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;注意 &lt;code&gt;Invoke&lt;/code&gt; 的签名带着 &lt;code&gt;context.Context&lt;/code&gt;,这不是摆设:工具可能要发 HTTP、查数据库、读文件，
都是可能阻塞的 I/O,把 ctx 透传进去，21.3 讲的超时与取消才能一路抵达最底层。一个工具调用框架的
运行时核心，基本就是这个接口加这次 map 分发,又一次，没有一行机器学习。&lt;/p&gt;</description></item><item><title>21.3 流式、背压与取消</title><link>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/stream/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/zh-cn/part6hetero/ch21agent/stream/</guid><description>&lt;h1 id="213-流式背压与取消"&gt;21.3 流式、背压与取消&lt;/h1&gt;
&lt;p&gt;&lt;a href=".././loop"&gt;21.1&lt;/a&gt; 立起了 Agent 的控制循环，&lt;a href=".././mcp"&gt;21.2&lt;/a&gt; 讲清了循环里的工具调用。这最后一节，
把第 7 章的 context 与第 10、11 章的并发原语请上台做全书的谢幕演出,因为一个生产级的 Agent，
真正的难处不在循环的骨架，而在贯穿整条链路的三件事:流式、背压、取消。它们彼此缠绕，
而把它们统一起来的，是一个 Go 程序员再熟悉不过的形状,一棵由 context 绑定的 goroutine 树。&lt;/p&gt;
&lt;h2 id="2131-整条链路都是流式的"&gt;21.3.1 整条链路都是流式的&lt;/h2&gt;
&lt;p&gt;先看清数据在 Agent 里怎么流。一个请求从客户端进来，落到 Agent 的控制循环;循环每一步向模型发起
一次推理，而那次推理是 &lt;a href="../../ch20inference/serving"&gt;20.3&lt;/a&gt; 讲的&lt;strong&gt;流式 token 流&lt;/strong&gt;;循环还会调用
工具，工具自身也可能慢、可能流式返回。于是&lt;strong&gt;流式不是某一层的特性，而是贯穿整条链路的常态&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这带来一个体验上的要求:用户不该盯着空白等 Agent 默默跑完几十步。模型在想什么、正调用哪个工具、
工具回了什么，都应当&lt;strong&gt;边发生边呈现&lt;/strong&gt;。这意味着 Agent 不只是消费下层的流，它还要&lt;strong&gt;再生产&lt;/strong&gt;一条
自己的事件流向上吐:把「模型正在输出的 token」「开始调用工具 X」「工具 X 返回了」这些事件，
实时地流给客户端。一条流进、一条流出，中间是控制循环,这又是第 10 章「goroutine 经通道把事件
逐个传出」的句式，只是这次传的是 Agent 的执行事件。&lt;/p&gt;
&lt;h2 id="2132-取消必须穿透每一层"&gt;21.3.2 取消必须穿透每一层&lt;/h2&gt;
&lt;p&gt;流式之外，最硬的需求是&lt;strong&gt;取消&lt;/strong&gt;。用户随时可能点「停止」、可能关掉连接;一个上层任务失败，
正在并行跑的兄弟任务就该立刻停手。一次取消，必须能穿透 Agent 那一整摞调用层,而这正是第 7 章
&lt;code&gt;context&lt;/code&gt; 被设计出来要做的事。&lt;/p&gt;
&lt;p&gt;把一个 ctx 从最顶端（客户端请求）一路透传下去:进入控制循环，传给每一次 &lt;code&gt;model.Infer&lt;/code&gt;,
传给每一次 &lt;code&gt;tool.Invoke&lt;/code&gt;,再传进工具内部的每一次 HTTP、每一次数据库查询。这样，
顶端一个 &lt;code&gt;cancel()&lt;/code&gt;,&lt;code&gt;ctx.Done()&lt;/code&gt; 的信号就会同时抵达每一层:模型停止生成（释放 20.1 那块
每请求的 KV cache),在飞的工具 I/O 当即中止。整摞调用栈像多米诺一样干净地倒下，没有泄漏的
goroutine、没有继续空转烧钱的推理。&lt;/p&gt;</description></item></channel></rss>