<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ai on DK Blog</title><link>/tags/ai/</link><description>Recent content in Ai on DK Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Thu, 25 Jun 2026 20:20:00 +0800</lastBuildDate><atom:link href="/tags/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Codex 的 Main Agent 和 Subagent 是怎么通信的</title><link>/dkblog/037-codex-main-agent-subagent-communication/</link><pubDate>Thu, 25 Jun 2026 19:20:00 +0800</pubDate><guid>/dkblog/037-codex-main-agent-subagent-communication/</guid><description>&lt;img src="/dkblog/assets/ai/codex-main-agent-subagent-flow.svg" alt="Featured image of post Codex 的 Main Agent 和 Subagent 是怎么通信的" /&gt;&lt;p&gt;很多人第一次看 Codex 的多 agent 机制时，会下意识把 subagent 理解成 “main agent 调了一个内部函数”。这个理解不对。&lt;/p&gt;
&lt;p&gt;在 Codex 里，subagent 更接近于：主线程之外又开了一个独立聊天线程。它有自己的 thread id、上下文、工具调用过程、执行状态和 mailbox。main agent 和 subagent 不共享完整上下文，它们靠 Codex runtime 转发内部消息协作。&lt;/p&gt;
&lt;p&gt;这篇文章不讲 Rust 语法，只讲一条闭环：&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" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt;5&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;main agent 怎么派任务
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; runtime 怎么投递消息
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; subagent 怎么独立执行
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 结果怎么回到 main agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; main agent 怎么继续往下走
&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;/p&gt;
&lt;p&gt;&lt;img alt="Codex main agent 与 subagent 通信总览图" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="/dkblog/assets/ai/codex-main-agent-subagent-flow.svg"&gt;&lt;/p&gt;
&lt;h2 id="先记住一个模型"&gt;&lt;a href="#%e5%85%88%e8%ae%b0%e4%bd%8f%e4%b8%80%e4%b8%aa%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;先记住一个模型
&lt;/h2&gt;&lt;p&gt;可以先把 Codex 的 subagent 理解成下面这个结构：&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" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;main agent 所在主聊天线程
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 另外开了一个独立聊天线程
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 这个独立线程就是 subagent
&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;所以 subagent 不是 main agent 内部的一个函数。它有自己的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;thread id&lt;/li&gt;
&lt;li&gt;上下文&lt;/li&gt;
&lt;li&gt;工具调用过程&lt;/li&gt;
&lt;li&gt;执行状态&lt;/li&gt;
&lt;li&gt;mailbox&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这件事决定了 main agent 和 subagent 的关系不是同步函数调用，而是多个独立线程之间通过内部信件协作。&lt;/p&gt;
&lt;h2 id="先看通信闭环"&gt;&lt;a href="#%e5%85%88%e7%9c%8b%e9%80%9a%e4%bf%a1%e9%97%ad%e7%8e%af" class="header-anchor"&gt;&lt;/a&gt;先看通信闭环
&lt;/h2&gt;&lt;p&gt;main agent 能控制 subagent 的方式，不是直接读写它的内存，而是调用一组协作工具：&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" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt;6&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;spawn_agent 创建子 agent，并给第一条任务
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;send_message 给已有 agent 发消息，但不强制它马上跑
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;followup_task 给已有 agent 发后续任务，并在空闲时触发它继续跑
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wait_agent 等待有 agent 消息或状态变化
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;interrupt_agent 打断某个 agent 当前执行
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;list_agents 查看当前有哪些 agent
&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;这些工具本质上都是 function call。模型输出 function call，Codex runtime 收到后执行内部逻辑。&lt;/p&gt;
&lt;p&gt;把完整流程压成一条主链路，大致就是：&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" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-5"&gt;&lt;a class="lnlinks" href="#hl-3-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-6"&gt;&lt;a class="lnlinks" href="#hl-3-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-7"&gt;&lt;a class="lnlinks" href="#hl-3-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-8"&gt;&lt;a class="lnlinks" href="#hl-3-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-9"&gt;&lt;a class="lnlinks" href="#hl-3-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-10"&gt;&lt;a class="lnlinks" href="#hl-3-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-11"&gt;&lt;a class="lnlinks" href="#hl-3-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-12"&gt;&lt;a class="lnlinks" href="#hl-3-12"&gt;12&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户提问
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; main agent 决定拆任务
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; main agent 调 spawn_agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; runtime 创建 child thread
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 第一条任务进入 child thread
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; child agent 独立执行
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; main agent 调 wait_agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; child agent 调 send_message(target=root)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; root mailbox 收到结果
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; wait_agent 被唤醒
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; main agent 下一轮读到这条消息
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; main agent 汇总后回答用户
&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;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;subagent&lt;/code&gt; 是真的独立 thread，不是内部函数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;send_message&lt;/code&gt; 和 &lt;code&gt;followup_task&lt;/code&gt; 都是在投递内部消息，只是后者会顺带触发目标 agent 开始下一轮执行。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wait_agent&lt;/code&gt; 等的是 mailbox 活动，不是同步拿子函数返回值。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如 main agent 创建 subagent 时，可能只是发出这样一个调用：&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" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt;4&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;spawn_agent({
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; task_name: &amp;#34;research&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; message: &amp;#34;你去分析 app-server 里的 subagent 通信逻辑&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;但 runtime 真正做的是：&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" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-2"&gt;&lt;a class="lnlinks" href="#hl-5-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-3"&gt;&lt;a class="lnlinks" href="#hl-5-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-4"&gt;&lt;a class="lnlinks" href="#hl-5-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-5"&gt;&lt;a class="lnlinks" href="#hl-5-5"&gt;5&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;创建 child thread
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;记录 parent_thread_id
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;记录 task_name
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;投递第一条任务
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;启动 child agent 独立执行
&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;而 subagent 回结果时，走的也不是“返回值”，而是再发一封内部信：&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" id="hl-6-1"&gt;&lt;a class="lnlinks" href="#hl-6-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-2"&gt;&lt;a class="lnlinks" href="#hl-6-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-3"&gt;&lt;a class="lnlinks" href="#hl-6-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-4"&gt;&lt;a class="lnlinks" href="#hl-6-4"&gt;4&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;send_message({
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; target: &amp;#34;root&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; message: &amp;#34;我查完了。结论是：Codex 通过 InterAgentCommunication 和 mailbox 通信。&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;所以从运行时视角看，Codex 的多 agent 更像：&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" id="hl-7-1"&gt;&lt;a class="lnlinks" href="#hl-7-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-7-2"&gt;&lt;a class="lnlinks" href="#hl-7-2"&gt;2&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;多个独立聊天线程
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 通过 runtime 维护的内部邮箱互相发消息
&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;h2 id="wait_agent-为什么经常被误解"&gt;&lt;a href="#wait_agent-%e4%b8%ba%e4%bb%80%e4%b9%88%e7%bb%8f%e5%b8%b8%e8%a2%ab%e8%af%af%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;wait_agent 为什么经常被误解
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;wait_agent&lt;/code&gt; 最容易被误解成“同步等待 subagent 返回值”，其实它更像：&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" id="hl-8-1"&gt;&lt;a class="lnlinks" href="#hl-8-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;main agent 暂停一下，等 mailbox 有没有新消息
&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;/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" id="hl-9-1"&gt;&lt;a class="lnlinks" href="#hl-9-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-9-2"&gt;&lt;a class="lnlinks" href="#hl-9-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-9-3"&gt;&lt;a class="lnlinks" href="#hl-9-3"&gt;3&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wait_agent({
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; timeout_ms: 30000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;/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" id="hl-10-1"&gt;&lt;a class="lnlinks" href="#hl-10-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-10-2"&gt;&lt;a class="lnlinks" href="#hl-10-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-10-3"&gt;&lt;a class="lnlinks" href="#hl-10-3"&gt;3&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;最多等 30 秒
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如果收到 agent 消息或相关活动，就返回
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如果 30 秒没有，就返回 timed_out
&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;/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" id="hl-11-1"&gt;&lt;a class="lnlinks" href="#hl-11-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-11-2"&gt;&lt;a class="lnlinks" href="#hl-11-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-11-3"&gt;&lt;a class="lnlinks" href="#hl-11-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-11-4"&gt;&lt;a class="lnlinks" href="#hl-11-4"&gt;4&lt;/a&gt;
&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-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Wait timed out.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;timed_out&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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&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;/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" id="hl-12-1"&gt;&lt;a class="lnlinks" href="#hl-12-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-12-2"&gt;&lt;a class="lnlinks" href="#hl-12-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-12-3"&gt;&lt;a class="lnlinks" href="#hl-12-3"&gt;3&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wait_agent 超时
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;!=
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;subagent 被杀掉
&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;子 agent 可能还在继续跑。runtime 不会自动替 main agent 做决策，它不会自动：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kill 子 agent&lt;/li&gt;
&lt;li&gt;interrupt 子 agent&lt;/li&gt;
&lt;li&gt;判定子 agent 失败&lt;/li&gt;
&lt;li&gt;自动重派任务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它只是把 &lt;code&gt;timed_out=true&lt;/code&gt; 交还给 main agent。接下来 main agent 自己决定下一步：&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" id="hl-13-1"&gt;&lt;a class="lnlinks" href="#hl-13-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-2"&gt;&lt;a class="lnlinks" href="#hl-13-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-3"&gt;&lt;a class="lnlinks" href="#hl-13-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-4"&gt;&lt;a class="lnlinks" href="#hl-13-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-5"&gt;&lt;a class="lnlinks" href="#hl-13-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-6"&gt;&lt;a class="lnlinks" href="#hl-13-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-7"&gt;&lt;a class="lnlinks" href="#hl-13-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-8"&gt;&lt;a class="lnlinks" href="#hl-13-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-9"&gt;&lt;a class="lnlinks" href="#hl-13-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-10"&gt;&lt;a class="lnlinks" href="#hl-13-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-11"&gt;&lt;a class="lnlinks" href="#hl-13-11"&gt;11&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;继续等:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; wait_agent({ timeout_ms: 30000 })
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;打断:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; interrupt_agent({ target: &amp;#34;research&amp;#34; })
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;催促:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; followup_task({ target: &amp;#34;research&amp;#34;, message: &amp;#34;收敛一下，直接给结论&amp;#34; })
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;先回答用户:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;所以 main agent 在这套机制里，本质上更像调度者。&lt;/p&gt;
&lt;h2 id="这条闭环在源码里怎么落地"&gt;&lt;a href="#%e8%bf%99%e6%9d%a1%e9%97%ad%e7%8e%af%e5%9c%a8%e6%ba%90%e7%a0%81%e9%87%8c%e6%80%8e%e4%b9%88%e8%90%bd%e5%9c%b0" class="header-anchor"&gt;&lt;/a&gt;这条闭环在源码里怎么落地
&lt;/h2&gt;&lt;p&gt;如果只看一张源码总览图，可以先记住这条链：&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" id="hl-14-1"&gt;&lt;a class="lnlinks" href="#hl-14-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-14-2"&gt;&lt;a class="lnlinks" href="#hl-14-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-14-3"&gt;&lt;a class="lnlinks" href="#hl-14-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-14-4"&gt;&lt;a class="lnlinks" href="#hl-14-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-14-5"&gt;&lt;a class="lnlinks" href="#hl-14-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-14-6"&gt;&lt;a class="lnlinks" href="#hl-14-6"&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-14-7"&gt;&lt;a class="lnlinks" href="#hl-14-7"&gt;7&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;模型看到工具定义
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 模型发起 function call
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; tool handler 接住 function call
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; AgentControl 负责创建 / 发消息 / 等待 / 中断
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; session handlers 和 InputQueue 负责 mailbox
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; turn loop 把 mailbox 消息放进模型上下文
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; app-server / TUI 把事件展示给客户端
&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;按这个链路往下看，源码可以分成 6 个位置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一层：工具暴露给模型&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件：&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" id="hl-15-1"&gt;&lt;a class="lnlinks" href="#hl-15-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-15-2"&gt;&lt;a class="lnlinks" href="#hl-15-2"&gt;2&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/tools/spec_plan.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/tools/handlers/multi_agents_spec.rs
&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spec_plan.rs&lt;/code&gt; 决定当前 turn 要不要把多 agent 工具暴露给模型&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multi_agents_spec.rs&lt;/code&gt; 定义这些 function call 的参数 schema&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，main agent 能不能调用 &lt;code&gt;spawn_agent&lt;/code&gt;、&lt;code&gt;send_message&lt;/code&gt;、&lt;code&gt;wait_agent&lt;/code&gt;，第一步先看这里。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二层：main agent 的控制入口&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件：&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" id="hl-16-1"&gt;&lt;a class="lnlinks" href="#hl-16-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-2"&gt;&lt;a class="lnlinks" href="#hl-16-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-3"&gt;&lt;a class="lnlinks" href="#hl-16-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-4"&gt;&lt;a class="lnlinks" href="#hl-16-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-5"&gt;&lt;a class="lnlinks" href="#hl-16-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-6"&gt;&lt;a class="lnlinks" href="#hl-16-6"&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-7"&gt;&lt;a class="lnlinks" href="#hl-16-7"&gt;7&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/wait.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/interrupt_agent.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/agent/control.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/agent/control/spawn.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/agent/registry.rs
&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;这层可以理解成 “main agent 的控制台”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spawn.rs&lt;/code&gt; 负责接住 &lt;code&gt;spawn_agent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;message_tool.rs&lt;/code&gt; 负责接住 &lt;code&gt;send_message&lt;/code&gt; 和 &lt;code&gt;followup_task&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wait.rs&lt;/code&gt; 负责 &lt;code&gt;wait_agent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;interrupt_agent.rs&lt;/code&gt; 负责打断入口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AgentControl&lt;/code&gt; 是统一控制中心&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spawn_agent_internal(...)&lt;/code&gt; 真正创建 child thread&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AgentRegistry&lt;/code&gt; 记住有哪些 agent、各自的 thread id 和 path&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以“main agent 怎么找到 subagent、怎么把任务发过去”，主要都在这层。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三层：内部消息的数据结构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件：&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" id="hl-17-1"&gt;&lt;a class="lnlinks" href="#hl-17-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/protocol/src/protocol.rs
&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;/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" id="hl-18-1"&gt;&lt;a class="lnlinks" href="#hl-18-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;InterAgentCommunication
&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;author&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;recipient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trigger_turn&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如：&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" id="hl-19-1"&gt;&lt;a class="lnlinks" href="#hl-19-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-19-2"&gt;&lt;a class="lnlinks" href="#hl-19-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-19-3"&gt;&lt;a class="lnlinks" href="#hl-19-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-19-4"&gt;&lt;a class="lnlinks" href="#hl-19-4"&gt;4&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;from: root
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;to: research
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;content: 继续查一下 thread_history.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;trigger_turn: true
&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;strong&gt;第四层：消息如何进入 mailbox&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件：&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" id="hl-20-1"&gt;&lt;a class="lnlinks" href="#hl-20-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-20-2"&gt;&lt;a class="lnlinks" href="#hl-20-2"&gt;2&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/session/handlers.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/session/input_queue.rs
&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;这里是真正的 mailbox 落点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;handlers.rs&lt;/code&gt; 里的 &lt;code&gt;inter_agent_communication(...)&lt;/code&gt; 收到 &lt;code&gt;Op::InterAgentCommunication&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;input_queue.rs&lt;/code&gt; 里的 &lt;code&gt;enqueue_mailbox_communication(...)&lt;/code&gt; 把消息放进 mailbox&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;trigger_turn=true&lt;/code&gt;，runtime 会尝试启动目标 agent 的新一轮 turn&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以 &lt;code&gt;send_message&lt;/code&gt; 和 &lt;code&gt;followup_task&lt;/code&gt; 的本质区别，就落在 &lt;code&gt;trigger_turn&lt;/code&gt; 上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第五层：mailbox 怎么进入模型上下文&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件：&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" id="hl-21-1"&gt;&lt;a class="lnlinks" href="#hl-21-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-21-2"&gt;&lt;a class="lnlinks" href="#hl-21-2"&gt;2&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/session/turn.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/session/mod.rs
&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;turn.rs&lt;/code&gt; 在每轮请求模型前，从 &lt;code&gt;InputQueue&lt;/code&gt; 取 pending input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mod.rs&lt;/code&gt; 里的 &lt;code&gt;record_inter_agent_communication(...)&lt;/code&gt; 把消息写进历史和 rollout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，agent 间消息不只是内存里短暂经过，它还会：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进入下一轮模型上下文&lt;/li&gt;
&lt;li&gt;写入会话历史&lt;/li&gt;
&lt;li&gt;被客户端看到&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第六层：状态和展示&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件：&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" id="hl-22-1"&gt;&lt;a class="lnlinks" href="#hl-22-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-2"&gt;&lt;a class="lnlinks" href="#hl-22-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-3"&gt;&lt;a class="lnlinks" href="#hl-22-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-4"&gt;&lt;a class="lnlinks" href="#hl-22-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-5"&gt;&lt;a class="lnlinks" href="#hl-22-5"&gt;5&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/core/src/agent/status.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/app-server-protocol/src/protocol/thread_history.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/tui/src/app/loaded_threads.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/tui/src/app/agent_navigation.rs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-rs/tui/src/app/thread_routing.rs
&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;status.rs&lt;/code&gt; 把事件映射成 &lt;code&gt;Running&lt;/code&gt;、&lt;code&gt;Completed&lt;/code&gt;、&lt;code&gt;Errored&lt;/code&gt; 等状态&lt;/li&gt;
&lt;li&gt;&lt;code&gt;thread_history.rs&lt;/code&gt; 把协作事件转成客户端能展示的 &lt;code&gt;ThreadItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;TUI 相关文件负责找出主线程下有哪些 subagent，以及怎么在界面里切换和缓存它们&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以你在 app-server 或 TUI 里看到的“创建了哪个 agent”“谁给谁发了消息”“wait 有没有超时”，最终都依赖这层映射和展示。&lt;/p&gt;
&lt;h2 id="最短源码定位表"&gt;&lt;a href="#%e6%9c%80%e7%9f%ad%e6%ba%90%e7%a0%81%e5%ae%9a%e4%bd%8d%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;最短源码定位表
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;你想看什么&lt;/th&gt;
					&lt;th&gt;看哪个文件&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;工具怎么暴露给模型&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/tools/spec_plan.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;function call 参数怎么定义&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/tools/handlers/multi_agents_spec.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;spawn_agent&lt;/code&gt; 入口&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;创建 child thread&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/agent/control/spawn.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;main 如何控制 agent&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/agent/control.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;agent 列表和 metadata&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/agent/registry.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;send_message&lt;/code&gt; / &lt;code&gt;followup_task&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;内部信件结构&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/protocol/src/protocol.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;mailbox 队列&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/session/input_queue.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;mailbox 进入模型上下文&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/session/turn.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;通信记录进历史&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/session/mod.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;wait_agent&lt;/code&gt; 超时逻辑&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/tools/handlers/multi_agents_v2/wait.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;agent 状态&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/core/src/agent/status.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;app-server 展示&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/app-server-protocol/src/protocol/thread_history.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;TUI subagent 导航&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;codex-rs/tui/src/app/agent_navigation.rs&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="最后收一句"&gt;&lt;a href="#%e6%9c%80%e5%90%8e%e6%94%b6%e4%b8%80%e5%8f%a5" class="header-anchor"&gt;&lt;/a&gt;最后收一句
&lt;/h2&gt;&lt;p&gt;如果只记一句话，我建议记这句：&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" id="hl-23-1"&gt;&lt;a class="lnlinks" href="#hl-23-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Codex 的 main agent 和 subagent 不是同步函数调用，而是多个独立 thread 通过 runtime 维护的 mailbox 互相发消息
&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;从原理上看，这是一个“多线程聊天 + 内部邮箱”的协作模型；从源码上看，这条链路则落在 tool handler、AgentControl、session mailbox、turn loop、app-server 和 TUI 几层里，刚好首尾闭环。&lt;/p&gt;</description></item><item><title>Multica 初体验：把 Codex、Claude Code 这类 coding agent 当成队友来管理</title><link>/dkblog/022-multica-coding-agent-workspace/</link><pubDate>Thu, 28 May 2026 22:16:00 +0800</pubDate><guid>/dkblog/022-multica-coding-agent-workspace/</guid><description>&lt;img src="https://images.unsplash.com/photo-1515879218367-8466d910aaa4?auto=format&amp;fit=crop&amp;w=900&amp;q=60" alt="Featured image of post Multica 初体验：把 Codex、Claude Code 这类 coding agent 当成队友来管理" /&gt;&lt;p&gt;最近看了一下 Multica，感觉它解决的不是“让 AI 写代码”这个问题，而是另一个更实际的问题：当你同时用 Codex、Claude Code、Copilot CLI、OpenCode 这类 coding agent 时，怎么管理它们干活。&lt;/p&gt;
&lt;p&gt;以前我的用法比较粗糙：开几个终端窗口，每个窗口一个 agent，谁在跑什么、结果有没有回来、上下文有没有断，全靠自己记。任务少的时候还行，一旦同时丢两三个问题，比如一个查 bug、一个写测试、一个做文档，很快就乱了。&lt;/p&gt;
&lt;p&gt;Multica 的思路是把 agent 放进一个协作空间里。它有 workspace、issue、comment、agent 这些概念。你可以把一个 issue 分配给某个 agent，也可以在评论里 &lt;code&gt;@&lt;/code&gt; 它。这个体验更像是在给同事派活，而不是在终端里继续补 prompt。&lt;/p&gt;
&lt;p&gt;我当时顺手记了一个很小的判断：如果一个工具只是在网页里多开几个聊天窗口，那它对 coding agent 的帮助有限；如果它能回答“谁在跑、跑到哪、改了什么、失败后能不能复盘”，它才像是在管理执行单元。Multica 比较接近后者。&lt;/p&gt;
&lt;h2 id="我比较在意的点"&gt;&lt;a href="#%e6%88%91%e6%af%94%e8%be%83%e5%9c%a8%e6%84%8f%e7%9a%84%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;我比较在意的点
&lt;/h2&gt;&lt;p&gt;我比较在意的一点是：Multica 并不是简单把所有东西都丢到云端跑。官方文档里说，真正执行任务的是本地 daemon。Multica server 负责 workspace、issue、成员、任务队列这些数据；daemon 跑在你自己的机器上，去调用本地安装的 Claude Code、Codex、Copilot CLI 等工具。&lt;/p&gt;
&lt;p&gt;这个设计对我来说挺关键。coding agent 最敏感的地方不是“问答内容”，而是它会读代码、跑命令、改文件。如果一个平台只是提供任务面板，但实际执行仍然在本地，那接受成本会低很多。&lt;/p&gt;
&lt;p&gt;当然这不代表完全没有风险。agent 能做什么、工具权限怎么给、仓库怎么隔离，还是要自己控制。&lt;/p&gt;
&lt;p&gt;从 provider matrix 看，Multica 对不同 coding tool 的支持不是完全一样的。Claude Code、Codex、OpenCode 在 MCP 支持、skill 注入路径、session resumption 上都有差异。这个地方我觉得文档写得比较实在，没有假装所有工具都能做到同一个水平。&lt;/p&gt;
&lt;p&gt;我会特别看这些差异，而不是只看“支持多少 agent”：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;能力&lt;/th&gt;
					&lt;th&gt;我为什么关心&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;session resumption&lt;/td&gt;
					&lt;td&gt;agent 中途断了以后能不能接着跑，不然长任务很容易废掉&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MCP support&lt;/td&gt;
					&lt;td&gt;能不能接公司内部工具，而不是只靠本地 shell&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;skill injection&lt;/td&gt;
					&lt;td&gt;能不能把团队约定、代码规范、常用命令放进 agent 上下文&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;task history&lt;/td&gt;
					&lt;td&gt;失败任务能不能复盘，而不是只剩一句“没做好”&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="如果我要试"&gt;&lt;a href="#%e5%a6%82%e6%9e%9c%e6%88%91%e8%a6%81%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;如果我要试
&lt;/h2&gt;&lt;p&gt;如果要在项目里试，我大概会先按这个方式用：&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" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt;3&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multica daemon start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multica setup self-host
&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;然后给不同 agent 分清职责，而不是所有任务都丢给一个“全能助手”：&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" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex-fixer 小范围代码修复和测试
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;claude-reviewer review、解释设计风险
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;opencode-docs 文档草稿和命令整理
&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;我现在对这类工具的看法是：不要指望它让 agent 变聪明。它真正有价值的地方是让 agent 的工作可排队、可观察、可追溯。&lt;/p&gt;
&lt;p&gt;终端里跑 agent 最大的问题是过程容易丢。Multica 把一次运行抽象成 task，有 queued、dispatched、running、completed、failed 这类状态，也有重试和历史记录。这样你至少知道它是没开始、跑挂了，还是已经完成只是结果不理想。&lt;/p&gt;
&lt;h2 id="我不会一上来就接正式仓库"&gt;&lt;a href="#%e6%88%91%e4%b8%8d%e4%bc%9a%e4%b8%80%e4%b8%8a%e6%9d%a5%e5%b0%b1%e6%8e%a5%e6%ad%a3%e5%bc%8f%e4%bb%93%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;我不会一上来就接正式仓库
&lt;/h2&gt;&lt;p&gt;更合理的方式是先拿一个 side project 或内部工具试：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只给只读或低风险仓库。&lt;/li&gt;
&lt;li&gt;agent 默认不要有生产凭证。&lt;/li&gt;
&lt;li&gt;每个任务只允许小范围修改。&lt;/li&gt;
&lt;li&gt;输出必须经过人工 review。&lt;/li&gt;
&lt;li&gt;不要把“能跑起来”误认为“能放心用”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Multica 这类项目让我感觉到一个趋势：coding agent 正在从“单次对话工具”变成“可管理的执行单元”。以前我们关心的是 prompt 怎么写，现在更关心任务怎么派、权限怎么控、结果怎么验收。&lt;/p&gt;
&lt;p&gt;这篇先记到这里。它不是那种看完文档就能断言成熟的工具，但方向我觉得值得关注。尤其是团队里已经有人混用 Codex、Claude Code、Copilot CLI 的时候，与其每个人各玩各的，不如有一个地方把任务和 agent 统一管起来。&lt;/p&gt;
&lt;p&gt;我暂时不会把它写成“推荐大家马上上”。更准确的评价是：如果你的团队还没有稳定使用 coding agent，它不是第一优先级；如果已经有人同时开好几个 agent 做修复、review、文档和测试，它解决的就是混乱本身。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://multica.ai/docs" target="_blank" rel="noopener"
 &gt;Multica Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://multica.ai/docs/how-multica-works" target="_blank" rel="noopener"
 &gt;How Multica works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://multica.ai/docs/tasks" target="_blank" rel="noopener"
 &gt;Multica Tasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://multica.ai/docs/providers" target="_blank" rel="noopener"
 &gt;Multica AI coding tools matrix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/multica-ai/multica" target="_blank" rel="noopener"
 &gt;multica-ai/multica&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Codex CLI 交互斜杠命令全集（含作用与使用场景）</title><link>/dkblog/020-codex-cli-slash-commands/</link><pubDate>Thu, 05 Mar 2026 23:40:00 +0800</pubDate><guid>/dkblog/020-codex-cli-slash-commands/</guid><description>&lt;img src="https://images.unsplash.com/photo-1677442136019-21780ecad995?auto=format&amp;fit=crop&amp;w=900&amp;q=60" alt="Featured image of post Codex CLI 交互斜杠命令全集（含作用与使用场景）" /&gt;
 &lt;blockquote&gt;
 &lt;p&gt;来源：OpenAI 官方文档（Codex CLI Slash Commands）&lt;br&gt;
更新时间：2026-03-05&lt;br&gt;
说明：以下是 &lt;code&gt;codex&lt;/code&gt; 交互界面（TUI）内输入的 &lt;code&gt;/xxx&lt;/code&gt; 命令，不是 PowerShell 系统命令。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;最近在高频用 Codex CLI，发现很多同学刚上手时，会把 &lt;code&gt;/xxx&lt;/code&gt; 斜杠命令和系统命令混在一起，导致操作路径有点乱。&lt;br&gt;
所以我把官方文档里这部分命令做了一次“能直接抄着用”的整理：每个命令不只写功能，还配了一个真实使用场景，方便你在实战里快速判断“现在该敲哪个”。&lt;/p&gt;
&lt;h2 id="使用方式"&gt;&lt;a href="#%e4%bd%bf%e7%94%a8%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;使用方式
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;启动 &lt;code&gt;codex&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在输入框输入 &lt;code&gt;/&lt;/code&gt; 打开命令列表&lt;/li&gt;
&lt;li&gt;选择命令或手动输入完整命令&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="命令清单作用--场景"&gt;&lt;a href="#%e5%91%bd%e4%bb%a4%e6%b8%85%e5%8d%95%e4%bd%9c%e7%94%a8--%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;命令清单（作用 + 场景）
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;命令&lt;/th&gt;
					&lt;th&gt;具体作用&lt;/th&gt;
					&lt;th&gt;典型使用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/permissions&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;设置当前会话的审批/权限策略&lt;/td&gt;
					&lt;td&gt;你要从“手动审批”切到“自动执行优先”时&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/sandbox-add-read-dir&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;给沙箱追加可读目录（Windows 原生 CLI）&lt;/td&gt;
					&lt;td&gt;需要读取工作区外的配置目录或共享代码目录&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/agent&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;切换当前活跃 agent 线程&lt;/td&gt;
					&lt;td&gt;同时跑了多个任务，想切回另一个线程继续&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/apps&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;浏览并插入可用 connectors/apps&lt;/td&gt;
					&lt;td&gt;需要接入外部系统数据（如任务系统、文档系统）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/clear&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;清屏并开启新会话&lt;/td&gt;
					&lt;td&gt;当前对话太长，想从干净上下文重新提问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/compact&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;压缩并总结长对话，减少上下文占用&lt;/td&gt;
					&lt;td&gt;会话很长导致响应变慢或上下文接近上限&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/copy&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;复制最近一次已完成输出&lt;/td&gt;
					&lt;td&gt;要把刚生成的命令/代码粘贴到别处使用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/diff&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看当前 Git diff（含未跟踪文件）&lt;/td&gt;
					&lt;td&gt;让模型改完代码后，先人工快速过一遍改动&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/exit&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;退出 CLI（等价 &lt;code&gt;/quit&lt;/code&gt;）&lt;/td&gt;
					&lt;td&gt;结束当前工作会话&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/experimental&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;开关实验功能&lt;/td&gt;
					&lt;td&gt;想试新能力或排查某实验特性的影响&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/feedback&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;提交日志/反馈&lt;/td&gt;
					&lt;td&gt;发现异常行为，需要把现场信息反馈给官方&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/init&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;在当前目录生成 &lt;code&gt;AGENTS.md&lt;/code&gt; 模板&lt;/td&gt;
					&lt;td&gt;新仓库首次使用 Codex，先定义协作规则&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/logout&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;退出登录并清理本地凭据&lt;/td&gt;
					&lt;td&gt;切账号、交接机器或做安全清理&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/mcp&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看已配置 MCP 工具&lt;/td&gt;
					&lt;td&gt;检查某工具是否已接入、当前可不可用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/mention&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;把文件/目录加入当前对话上下文&lt;/td&gt;
					&lt;td&gt;只想让模型关注某几个目录而不是全仓库&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/model&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;切换模型（可含推理强度）&lt;/td&gt;
					&lt;td&gt;先用快模型探索，再切强模型做最终修复&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/plan&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;切到 Plan 模式并提出规划请求&lt;/td&gt;
					&lt;td&gt;任务复杂，先要分步骤方案再执行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/personality&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;切换回答风格（friendly/pragmatic/none）&lt;/td&gt;
					&lt;td&gt;团队希望输出更简洁或更工程化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/ps&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看实验性的后台终端及输出&lt;/td&gt;
					&lt;td&gt;排查后台执行任务卡住或失败原因&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/fork&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;从当前会话分叉新线程&lt;/td&gt;
					&lt;td&gt;保留原思路，同时探索另一种实现路径&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/resume&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;恢复历史会话&lt;/td&gt;
					&lt;td&gt;第二天继续昨天做到一半的任务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/new&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;在同一 CLI 中新开对话&lt;/td&gt;
					&lt;td&gt;当前问题做完，开始另一个独立任务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/quit&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;退出 CLI&lt;/td&gt;
					&lt;td&gt;同 &lt;code&gt;/exit&lt;/code&gt;，用于结束会话&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/review&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;让 Codex 审查当前改动&lt;/td&gt;
					&lt;td&gt;提交 PR 前先自动扫一轮风险点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/status&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看会话配置与 token 使用&lt;/td&gt;
					&lt;td&gt;想确认当前模型、沙箱、审批和消耗情况&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/debug-config&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;输出配置层与策略诊断信息&lt;/td&gt;
					&lt;td&gt;配置不生效时定位是被哪一层覆盖&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/statusline&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;配置 TUI 状态栏显示项&lt;/td&gt;
					&lt;td&gt;想在界面上实时看到模型/审批等状态&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="兼容与别名"&gt;&lt;a href="#%e5%85%bc%e5%ae%b9%e4%b8%8e%e5%88%ab%e5%90%8d" class="header-anchor"&gt;&lt;/a&gt;兼容与别名
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/approvals&lt;/code&gt;：仍可用，但通常不在 slash 列表里显示；建议改用 &lt;code&gt;/permissions&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/exit&lt;/code&gt; 与 &lt;code&gt;/quit&lt;/code&gt;：功能等价，都是退出 CLI。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="两个高频命令"&gt;&lt;a href="#%e4%b8%a4%e4%b8%aa%e9%ab%98%e9%a2%91%e5%91%bd%e4%bb%a4" class="header-anchor"&gt;&lt;/a&gt;两个高频命令
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/init&lt;/code&gt;：初始化仓库级协作规则（生成 &lt;code&gt;AGENTS.md&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/model&lt;/code&gt;：任务中途切模型，平衡速度和质量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="版本差异说明"&gt;&lt;a href="#%e7%89%88%e6%9c%ac%e5%b7%ae%e5%bc%82%e8%af%b4%e6%98%8e" class="header-anchor"&gt;&lt;/a&gt;版本差异说明
&lt;/h2&gt;&lt;p&gt;不同 &lt;code&gt;codex-cli&lt;/code&gt; 版本会新增/隐藏命令。以你本机实际可见列表为准：在交互界面输入 &lt;code&gt;/&lt;/code&gt; 即可查看当前版本完整命令。&lt;/p&gt;
&lt;h2 id="写在最后"&gt;&lt;a href="#%e5%86%99%e5%9c%a8%e6%9c%80%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;写在最后
&lt;/h2&gt;&lt;p&gt;如果你是第一次系统使用 Codex CLI，我建议先记住这几个高频命令：&lt;code&gt;/model&lt;/code&gt;、&lt;code&gt;/permissions&lt;/code&gt;、&lt;code&gt;/diff&lt;/code&gt;、&lt;code&gt;/review&lt;/code&gt;、&lt;code&gt;/status&lt;/code&gt;。&lt;br&gt;
这几个覆盖了日常最常见的流程：选模型 -&amp;gt; 管权限 -&amp;gt; 看改动 -&amp;gt; 做审查 -&amp;gt; 查会话状态。&lt;/p&gt;
&lt;p&gt;另外一个实用习惯是：遇到“感觉命令不见了”或者“和别人截图不一致”时，先在当前会话里输入 &lt;code&gt;/&lt;/code&gt; 看本机版本实际列表，再决定下一步。&lt;br&gt;
这样能避免把时间花在“文档有，但你当前版本没开”这类问题上。&lt;/p&gt;
&lt;h2 id="官方链接"&gt;&lt;a href="#%e5%ae%98%e6%96%b9%e9%93%be%e6%8e%a5" class="header-anchor"&gt;&lt;/a&gt;官方链接
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://developers.openai.com/codex/cli/slash-commands" target="_blank" rel="noopener"
 &gt;https://developers.openai.com/codex/cli/slash-commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/openai/codex/blob/main/docs/slash_commands.md" target="_blank" rel="noopener"
 &gt;https://github.com/openai/codex/blob/main/docs/slash_commands.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>MCP 工具接入后的 Agent 设计：别只会调工具，还要能兜底</title><link>/dkblog/031-mcp-agent-tool-fallback/</link><pubDate>Thu, 19 Feb 2026 23:08:00 +0800</pubDate><guid>/dkblog/031-mcp-agent-tool-fallback/</guid><description>&lt;img src="https://images.unsplash.com/photo-1639322537228-f710d846310a?auto=format&amp;fit=crop&amp;w=900&amp;q=60" alt="Featured image of post MCP 工具接入后的 Agent 设计：别只会调工具，还要能兜底" /&gt;&lt;p&gt;最近接 MCP 工具时有个感受：让 agent 能调工具其实不难，难的是工具出错以后它怎么收场。&lt;/p&gt;
&lt;p&gt;很多 demo 都停在“模型发现工具，然后调用工具，最后返回结果”。这个流程当然重要，但一到真实项目里，问题马上会变多：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工具参数不完整。&lt;/li&gt;
&lt;li&gt;MCP server 连不上。&lt;/li&gt;
&lt;li&gt;工具返回了业务错误。&lt;/li&gt;
&lt;li&gt;工具返回内容太长。&lt;/li&gt;
&lt;li&gt;工具结果里混了不该给模型看的内容。&lt;/li&gt;
&lt;li&gt;用户问的是高风险操作，但 agent 没有停下来确认。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以我现在更愿意把 MCP 看成一个工具协议，而不是完整的 agent 方案。MCP 解决的是“工具怎么暴露、怎么发现、怎么调用”。但 agent 怎么规划、怎么重试、怎么审批、怎么记录，这些还是应用自己要设计。&lt;/p&gt;
&lt;p&gt;我踩到的第一个误区就是：工具列表一多，agent 看起来就“能力很强”。但真实情况是，工具越多，出错路径也越多。尤其是工具之间有依赖的时候，模型一次错误调用可能会把后面的上下文都带偏。&lt;/p&gt;
&lt;h2 id="失败要分类"&gt;&lt;a href="#%e5%a4%b1%e8%b4%a5%e8%a6%81%e5%88%86%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;失败要分类
&lt;/h2&gt;&lt;p&gt;MCP 官方规范里，tool 是 server 暴露给模型调用的能力。每个 tool 有 &lt;code&gt;name&lt;/code&gt;、&lt;code&gt;description&lt;/code&gt;、&lt;code&gt;inputSchema&lt;/code&gt;，新版本里还可以有 &lt;code&gt;outputSchema&lt;/code&gt;。client 可以通过 &lt;code&gt;tools/list&lt;/code&gt; 发现工具，通过 &lt;code&gt;tools/call&lt;/code&gt; 调用工具。&lt;/p&gt;
&lt;p&gt;工具失败不能都当成一种失败。我比较喜欢分成四类：&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" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt;4&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 参数错误：模型传错了，应该让模型修正参数后重试
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. 临时错误：网络、限流、超时，可以做有限重试
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. 权限错误：不要重试，直接提示需要授权或人工处理
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. 业务错误：工具正常执行了，但结果不满足条件，需要解释给用户
&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;如果不分这些类型，agent 很容易出现两种问题：要么无限重试，要么一失败就胡编。&lt;/p&gt;
&lt;p&gt;一个比较朴素的工具调用兜底逻辑可以这样设计：&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" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-4"&gt;&lt;a class="lnlinks" href="#hl-1-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-5"&gt;&lt;a class="lnlinks" href="#hl-1-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-6"&gt;&lt;a class="lnlinks" href="#hl-1-6"&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-7"&gt;&lt;a class="lnlinks" href="#hl-1-7"&gt;7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-8"&gt;&lt;a class="lnlinks" href="#hl-1-8"&gt;8&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户请求
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 判断是否需要工具
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 检查工具是否允许在当前场景使用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 调用工具
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 校验结果结构
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 如果失败，按失败类型处理
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 如果是敏感操作，等待人工确认
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;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;这里我会强制 agent 在最终回答里暴露两类信息：&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" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt;2&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已确认：来自工具返回、日志、数据库或 API 的事实
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;这个写法有点啰嗦，但对排障类 agent 很有用。最怕的是模型把推测写成事实，用户再拿这个“事实”去改线上配置。&lt;/p&gt;
&lt;h2 id="敏感工具不要让模型自己决定"&gt;&lt;a href="#%e6%95%8f%e6%84%9f%e5%b7%a5%e5%85%b7%e4%b8%8d%e8%a6%81%e8%ae%a9%e6%a8%a1%e5%9e%8b%e8%87%aa%e5%b7%b1%e5%86%b3%e5%ae%9a" class="header-anchor"&gt;&lt;/a&gt;敏感工具不要让模型自己决定
&lt;/h2&gt;&lt;p&gt;这里面最容易被忽略的是“工具是否允许在当前场景使用”。MCP 规范也强调了 trust &amp;amp; safety：客户端应该让用户知道哪些工具暴露给了模型，敏感操作前应该有确认。&lt;/p&gt;
&lt;p&gt;我自己的倾向是把 MCP 工具分级：&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" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt;4&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;read_only 只读查询，比如查文档、查资源、查状态
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;low_risk 可写但影响小，比如创建草稿、生成 issue
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dangerous 可能改配置、删数据、执行命令
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;external 会访问外部系统或带来数据外发风险
&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;然后给 agent 一个硬规则：&lt;code&gt;dangerous&lt;/code&gt; 和 &lt;code&gt;external&lt;/code&gt; 默认必须人工确认，不能让模型自己判断“应该没事”。&lt;/p&gt;
&lt;p&gt;我还会给工具加一个“是否可回滚”的维度。比如创建一条草稿 issue 是低风险，因为删掉就行；修改线上 ConfigMap 也许看起来只是一个字段，但一旦触发 rollout，影响就不是工具自己能兜住的。&lt;/p&gt;
&lt;h2 id="trace-比想象中重要"&gt;&lt;a href="#trace-%e6%af%94%e6%83%b3%e8%b1%a1%e4%b8%ad%e9%87%8d%e8%a6%81" class="header-anchor"&gt;&lt;/a&gt;trace 比想象中重要
&lt;/h2&gt;&lt;p&gt;如果用 OpenAI Agents SDK，它本身也提供了不少跟工程化有关的东西，比如 MCP servers、guardrails、handoffs、tracing。这里我觉得 tracing 很重要，因为 agent 一旦开始多步调用工具，事后没有 trace 基本没法排查。&lt;/p&gt;
&lt;p&gt;一次完整 trace 至少要能回答几个问题：&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" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-5"&gt;&lt;a class="lnlinks" href="#hl-4-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-6"&gt;&lt;a class="lnlinks" href="#hl-4-6"&gt;6&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户原始输入是什么？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;模型决定调用哪个工具？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;传给工具的参数是什么？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;工具原始返回是什么？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;中间有没有 guardrail 拦截？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;另外，MCP transport 也要注意。stdio 适合本地工具，client 拉起子进程，通过 stdin/stdout 通信；Streamable HTTP 适合远程或服务化工具。HTTP 场景下要考虑认证、Origin 校验和服务绑定地址。&lt;/p&gt;
&lt;p&gt;我现在更愿意把 MCP server 写得“笨”一点：输入严格校验，输出结构稳定，错误信息清楚，不在工具里偷偷做太多推理。推理留给 agent，工具只负责可靠地拿数据或执行动作。&lt;/p&gt;
&lt;p&gt;比如一个查询 Kubernetes pod 的 MCP tool，返回最好是这种结构：&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" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-2"&gt;&lt;a class="lnlinks" href="#hl-5-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-3"&gt;&lt;a class="lnlinks" href="#hl-5-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-4"&gt;&lt;a class="lnlinks" href="#hl-5-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-5"&gt;&lt;a class="lnlinks" href="#hl-5-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-6"&gt;&lt;a class="lnlinks" href="#hl-5-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-7"&gt;&lt;a class="lnlinks" href="#hl-5-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-8"&gt;&lt;a class="lnlinks" href="#hl-5-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-9"&gt;&lt;a class="lnlinks" href="#hl-5-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-10"&gt;&lt;a class="lnlinks" href="#hl-5-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-11"&gt;&lt;a class="lnlinks" href="#hl-5-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-12"&gt;&lt;a class="lnlinks" href="#hl-5-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-13"&gt;&lt;a class="lnlinks" href="#hl-5-13"&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-14"&gt;&lt;a class="lnlinks" href="#hl-5-14"&gt;14&lt;/a&gt;
&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-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pods&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;api-7f8d7c9b8d-abcde&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Running&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;restartCount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;worker-1&amp;#34;&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;warnings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;restartCount &amp;gt; 0, check container logs&amp;#34;&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&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;不要直接返回一大段混杂的自然语言。自然语言看起来顺，但后续不好校验，也不好让 agent 做稳定判断。&lt;/p&gt;
&lt;h2 id="小结"&gt;&lt;a href="#%e5%b0%8f%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;小结
&lt;/h2&gt;&lt;p&gt;接 MCP 以后我会重点看这些东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工具有没有清晰的 inputSchema 和 outputSchema。&lt;/li&gt;
&lt;li&gt;工具失败有没有分类型。&lt;/li&gt;
&lt;li&gt;是否设置超时、限流和审计日志。&lt;/li&gt;
&lt;li&gt;敏感工具有没有人工确认。&lt;/li&gt;
&lt;li&gt;工具结果进模型前有没有校验和脱敏。&lt;/li&gt;
&lt;li&gt;有没有 trace 能复盘每一步。&lt;/li&gt;
&lt;li&gt;agent 最终回答有没有区分事实和推断。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MCP 的价值是把工具接入标准化，但它不会自动让 agent 变可靠。真正影响线上可用性的，往往是这些不太显眼的兜底逻辑。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://modelcontextprotocol.io/specification/2025-06-18/server/tools" target="_blank" rel="noopener"
 &gt;MCP Tools 规范&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://modelcontextprotocol.io/specification/2025-06-18/basic/transports" target="_blank" rel="noopener"
 &gt;MCP Transports 规范&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization" target="_blank" rel="noopener"
 &gt;MCP Authorization 规范&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developers.openai.com/api/docs/guides/agents" target="_blank" rel="noopener"
 &gt;OpenAI Agents SDK Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://openai.github.io/openai-agents-python/mcp/" target="_blank" rel="noopener"
 &gt;OpenAI Agents Python SDK MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://openai.github.io/openai-agents-python/tracing/" target="_blank" rel="noopener"
 &gt;OpenAI Agents SDK Tracing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>kagent：在 Kubernetes 里跑一个完整的 AI Agent Case</title><link>/dkblog/023-kagent-kubernetes-ai-agent/</link><pubDate>Thu, 13 Nov 2025 21:34:00 +0800</pubDate><guid>/dkblog/023-kagent-kubernetes-ai-agent/</guid><description>&lt;img src="https://images.unsplash.com/photo-1544197150-b99a580bb7a8?auto=format&amp;fit=crop&amp;w=900&amp;q=60" alt="Featured image of post kagent：在 Kubernetes 里跑一个完整的 AI Agent Case" /&gt;&lt;p&gt;前面看 kagent 的时候，我只把它当成一个 Kubernetes native 的 Agent 框架来理解。后来再翻官方文档，发现更值得记录的是完整落地链路：CRD 怎么装，controller 怎么起来，模型配置怎么声明，MCPServer 怎么接进来，最后 Agent 怎么引用这些东西。&lt;/p&gt;
&lt;p&gt;这篇就按一个完整 case 记下来。目标不是让 agent 直接改生产集群，而是先做一个低风险的“集群观察 + 网页 fetch”助手：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以调用 kagent 内置 Kubernetes 工具查资源。&lt;/li&gt;
&lt;li&gt;可以通过一个 MCPServer 抓取网页内容。&lt;/li&gt;
&lt;li&gt;Agent 使用显式的 &lt;code&gt;ModelConfig&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;所有对象都用 YAML 表达，方便放进 GitOps。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="这个-case-里会出现哪些对象"&gt;&lt;a href="#%e8%bf%99%e4%b8%aa-case-%e9%87%8c%e4%bc%9a%e5%87%ba%e7%8e%b0%e5%93%aa%e4%ba%9b%e5%af%b9%e8%b1%a1" class="header-anchor"&gt;&lt;/a&gt;这个 case 里会出现哪些对象
&lt;/h2&gt;&lt;p&gt;先把关系画清楚：&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" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-6"&gt;&lt;a class="lnlinks" href="#hl-0-6"&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-7"&gt;&lt;a class="lnlinks" href="#hl-0-7"&gt;7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-8"&gt;&lt;a class="lnlinks" href="#hl-0-8"&gt;8&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Secret
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; ModelConfig
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; Agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; RemoteMCPServer: kagent-tool-server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; MCPServer: mcp-website-fetcher
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kagent CRDs + kagent controller
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kmcp CRDs + kmcp controller
&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RemoteMCPServer&lt;/code&gt;：kagent 里用来描述远程 MCP 服务的资源，内置的 &lt;code&gt;kagent-tool-server&lt;/code&gt; 就属于这一类。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MCPServer&lt;/code&gt;：kmcp 管理的 MCP server 资源，可以把一个 stdio MCP server 包成 Kubernetes 里的服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方文档里的 First MCP Tool 示例就是用 &lt;code&gt;MCPServer&lt;/code&gt; 起了一个 fetch 工具，然后 Agent 通过 &lt;code&gt;tools&lt;/code&gt; 引用它。&lt;/p&gt;
&lt;h2 id="1-安装-crd-和-controller"&gt;&lt;a href="#1-%e5%ae%89%e8%a3%85-crd-%e5%92%8c-controller" class="header-anchor"&gt;&lt;/a&gt;1. 安装 CRD 和 controller
&lt;/h2&gt;&lt;p&gt;我更偏向用 Helm 装，因为这套东西更适合放到平台环境里，不只是本地 demo。&lt;/p&gt;
&lt;p&gt;先装 kagent CRD：&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" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm install kagent-crds oci://ghcr.io/kagent-dev/kagent/helm/kagent-crds &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace kagent &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --create-namespace
&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;再装 kagent controller 和 UI。这里用 OpenAI provider 举例，实际项目里可以换 Anthropic、Gemini、Azure OpenAI、Ollama 等。&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" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt;6&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;your-api-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm install kagent oci://ghcr.io/kagent-dev/kagent/helm/kagent &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace kagent &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set providers.default&lt;span class="o"&gt;=&lt;/span&gt;openAI &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set providers.openAI.apiKey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&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;官方安装文档里提到，kagent 0.7 之后默认包含 kmcp 子项目。如果你已经单独装了 kmcp，可以通过 chart 配置关掉内置 kmcp。对第一次试用来说，先用默认就行。&lt;/p&gt;
&lt;p&gt;确认 CRD 和 controller：&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" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt;4&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get crd &lt;span class="p"&gt;|&lt;/span&gt; grep kagent.dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -n kagent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get svc -n kagent
&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;如果要单独安装 kmcp controller，官方文档给的是这条线：&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" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-5"&gt;&lt;a class="lnlinks" href="#hl-4-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-6"&gt;&lt;a class="lnlinks" href="#hl-4-6"&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-7"&gt;&lt;a class="lnlinks" href="#hl-4-7"&gt;7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-8"&gt;&lt;a class="lnlinks" href="#hl-4-8"&gt;8&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm install kmcp-crds oci://ghcr.io/kagent-dev/kmcp/helm/kmcp-crds &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace kmcp-system &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --create-namespace
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kmcp install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -n kmcp-system
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl logs -l app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;kmcp -n kmcp-system
&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;这里要注意：不要在同一个集群里重复装两套 kmcp controller。要么用 kagent 默认带的，要么明确自己单独安装并在 kagent chart 里关掉。&lt;/p&gt;
&lt;h2 id="2-准备模型-secret-和-modelconfig"&gt;&lt;a href="#2-%e5%87%86%e5%a4%87%e6%a8%a1%e5%9e%8b-secret-%e5%92%8c-modelconfig" class="header-anchor"&gt;&lt;/a&gt;2. 准备模型 Secret 和 ModelConfig
&lt;/h2&gt;&lt;p&gt;Helm 安装时如果设置了 provider，通常会带默认配置。为了把流程讲完整，我这里单独写一个 &lt;code&gt;ModelConfig&lt;/code&gt;，后续 Agent 明确引用它。&lt;/p&gt;
&lt;p&gt;先创建 API key secret：&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" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-2"&gt;&lt;a class="lnlinks" href="#hl-5-2"&gt;2&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n kagent create secret generic openai-api-key &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --from-literal&lt;span class="o"&gt;=&lt;/span&gt;api-key&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&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;ModelConfig&lt;/code&gt;：&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" id="hl-6-1"&gt;&lt;a class="lnlinks" href="#hl-6-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-2"&gt;&lt;a class="lnlinks" href="#hl-6-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-3"&gt;&lt;a class="lnlinks" href="#hl-6-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-4"&gt;&lt;a class="lnlinks" href="#hl-6-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-5"&gt;&lt;a class="lnlinks" href="#hl-6-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-6"&gt;&lt;a class="lnlinks" href="#hl-6-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-7"&gt;&lt;a class="lnlinks" href="#hl-6-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-8"&gt;&lt;a class="lnlinks" href="#hl-6-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-9"&gt;&lt;a class="lnlinks" href="#hl-6-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-10"&gt;&lt;a class="lnlinks" href="#hl-6-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-11"&gt;&lt;a class="lnlinks" href="#hl-6-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-12"&gt;&lt;a class="lnlinks" href="#hl-6-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-13"&gt;&lt;a class="lnlinks" href="#hl-6-13"&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-6-14"&gt;&lt;a class="lnlinks" href="#hl-6-14"&gt;14&lt;/a&gt;
&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-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent.dev/v1alpha2&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ModelConfig&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="nt"&gt;metadata&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ops-openai-model&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="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent&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="nt"&gt;spec&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="nt"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;OpenAI&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="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;your-openai-model&amp;gt;&amp;#34;&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="nt"&gt;apiKeySecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;openai-api-key&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="nt"&gt;apiKeySecretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;api-key&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="nt"&gt;openAI&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="nt"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;0.2&amp;#34;&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="nt"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2048&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="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&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;model&lt;/code&gt; 写成占位符，是因为不同账号、网关或代理支持的模型可能不一样。实际落地时要改成你环境里可用的模型名。&lt;/p&gt;
&lt;p&gt;应用并检查：&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" id="hl-7-1"&gt;&lt;a class="lnlinks" href="#hl-7-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-7-2"&gt;&lt;a class="lnlinks" href="#hl-7-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-7-3"&gt;&lt;a class="lnlinks" href="#hl-7-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-7-4"&gt;&lt;a class="lnlinks" href="#hl-7-4"&gt;4&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f modelconfig.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get modelconfig -n kagent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl describe modelconfig ops-openai-model -n kagent
&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;ModelConfig&lt;/code&gt; 里有几个字段比较关键：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;provider&lt;/code&gt;：模型提供方，API 里支持 OpenAI、Anthropic、AzureOpenAI、Ollama、Gemini、Bedrock 等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;model&lt;/code&gt;：具体模型名。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiKeySecret&lt;/code&gt; / &lt;code&gt;apiKeySecretKey&lt;/code&gt;：引用同 namespace 里的 Secret。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openAI&lt;/code&gt;：OpenAI provider 的额外参数，比如 temperature、maxTokens、timeout。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个对象的价值是把模型配置从 Agent 里拆出来。多个 Agent 可以引用同一个 &lt;code&gt;ModelConfig&lt;/code&gt;，模型切换也更清楚。&lt;/p&gt;
&lt;h2 id="3-创建一个-mcpserver网页-fetch-工具"&gt;&lt;a href="#3-%e5%88%9b%e5%bb%ba%e4%b8%80%e4%b8%aa-mcpserver%e7%bd%91%e9%a1%b5-fetch-%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;3. 创建一个 MCPServer：网页 fetch 工具
&lt;/h2&gt;&lt;p&gt;接下来创建一个最小 MCPServer。官方 First MCP Tool 文档里用的是 &lt;code&gt;mcp-server-fetch&lt;/code&gt;，通过 &lt;code&gt;uvx&lt;/code&gt; 启动 stdio MCP server，再由 kmcp controller 管理它的生命周期。&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" id="hl-8-1"&gt;&lt;a class="lnlinks" href="#hl-8-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-2"&gt;&lt;a class="lnlinks" href="#hl-8-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-3"&gt;&lt;a class="lnlinks" href="#hl-8-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-4"&gt;&lt;a class="lnlinks" href="#hl-8-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-5"&gt;&lt;a class="lnlinks" href="#hl-8-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-6"&gt;&lt;a class="lnlinks" href="#hl-8-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-7"&gt;&lt;a class="lnlinks" href="#hl-8-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-8"&gt;&lt;a class="lnlinks" href="#hl-8-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-9"&gt;&lt;a class="lnlinks" href="#hl-8-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-10"&gt;&lt;a class="lnlinks" href="#hl-8-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-11"&gt;&lt;a class="lnlinks" href="#hl-8-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-12"&gt;&lt;a class="lnlinks" href="#hl-8-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-8-13"&gt;&lt;a class="lnlinks" href="#hl-8-13"&gt;13&lt;/a&gt;
&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-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent.dev/v1alpha1&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;MCPServer&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="nt"&gt;metadata&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mcp-website-fetcher&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="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent&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="nt"&gt;spec&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="nt"&gt;deployment&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="nt"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;uvx&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="nt"&gt;args&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="l"&gt;mcp-server-fetch&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="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3000&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="nt"&gt;stdioTransport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="nt"&gt;transportType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;stdio&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;/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" id="hl-9-1"&gt;&lt;a class="lnlinks" href="#hl-9-1"&gt;1&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f mcp-website-fetcher.yaml
&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;/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" id="hl-10-1"&gt;&lt;a class="lnlinks" href="#hl-10-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-10-2"&gt;&lt;a class="lnlinks" href="#hl-10-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-10-3"&gt;&lt;a class="lnlinks" href="#hl-10-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-10-4"&gt;&lt;a class="lnlinks" href="#hl-10-4"&gt;4&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get mcpserver -n kagent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl describe mcpserver mcp-website-fetcher -n kagent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -n kagent &lt;span class="p"&gt;|&lt;/span&gt; grep mcp-website-fetcher
&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;如果 MCPServer 没起来，我会先看 kmcp controller 日志，而不是直接怀疑 Agent：&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" id="hl-11-1"&gt;&lt;a class="lnlinks" href="#hl-11-1"&gt;1&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl logs -l app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;kmcp -n kmcp-system
&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;如果你用的是 kagent chart 内置 kmcp，controller 的 namespace 和 label 可能跟独立安装不同，直接用下面的方式先找：&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" id="hl-12-1"&gt;&lt;a class="lnlinks" href="#hl-12-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-12-2"&gt;&lt;a class="lnlinks" href="#hl-12-2"&gt;2&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -A &lt;span class="p"&gt;|&lt;/span&gt; grep -i kmcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get deploy -A &lt;span class="p"&gt;|&lt;/span&gt; grep -i kmcp
&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;h2 id="4-创建-agent同时引用-kubernetes-工具和-fetch-工具"&gt;&lt;a href="#4-%e5%88%9b%e5%bb%ba-agent%e5%90%8c%e6%97%b6%e5%bc%95%e7%94%a8-kubernetes-%e5%b7%a5%e5%85%b7%e5%92%8c-fetch-%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;4. 创建 Agent：同时引用 Kubernetes 工具和 fetch 工具
&lt;/h2&gt;&lt;p&gt;现在创建 Agent。这个 Agent 做两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用内置 &lt;code&gt;kagent-tool-server&lt;/code&gt; 查 Kubernetes 资源。&lt;/li&gt;
&lt;li&gt;用刚才的 &lt;code&gt;mcp-website-fetcher&lt;/code&gt; 抓网页内容。&lt;/li&gt;
&lt;/ul&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" id="hl-13-1"&gt;&lt;a class="lnlinks" href="#hl-13-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-2"&gt;&lt;a class="lnlinks" href="#hl-13-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-3"&gt;&lt;a class="lnlinks" href="#hl-13-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-4"&gt;&lt;a class="lnlinks" href="#hl-13-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-5"&gt;&lt;a class="lnlinks" href="#hl-13-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-6"&gt;&lt;a class="lnlinks" href="#hl-13-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-7"&gt;&lt;a class="lnlinks" href="#hl-13-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-8"&gt;&lt;a class="lnlinks" href="#hl-13-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-9"&gt;&lt;a class="lnlinks" href="#hl-13-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-10"&gt;&lt;a class="lnlinks" href="#hl-13-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-11"&gt;&lt;a class="lnlinks" href="#hl-13-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-12"&gt;&lt;a class="lnlinks" href="#hl-13-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-13"&gt;&lt;a class="lnlinks" href="#hl-13-13"&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-14"&gt;&lt;a class="lnlinks" href="#hl-13-14"&gt;14&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-15"&gt;&lt;a class="lnlinks" href="#hl-13-15"&gt;15&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-16"&gt;&lt;a class="lnlinks" href="#hl-13-16"&gt;16&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-17"&gt;&lt;a class="lnlinks" href="#hl-13-17"&gt;17&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-18"&gt;&lt;a class="lnlinks" href="#hl-13-18"&gt;18&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-19"&gt;&lt;a class="lnlinks" href="#hl-13-19"&gt;19&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-20"&gt;&lt;a class="lnlinks" href="#hl-13-20"&gt;20&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-21"&gt;&lt;a class="lnlinks" href="#hl-13-21"&gt;21&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-22"&gt;&lt;a class="lnlinks" href="#hl-13-22"&gt;22&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-23"&gt;&lt;a class="lnlinks" href="#hl-13-23"&gt;23&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-24"&gt;&lt;a class="lnlinks" href="#hl-13-24"&gt;24&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-25"&gt;&lt;a class="lnlinks" href="#hl-13-25"&gt;25&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-26"&gt;&lt;a class="lnlinks" href="#hl-13-26"&gt;26&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-27"&gt;&lt;a class="lnlinks" href="#hl-13-27"&gt;27&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-28"&gt;&lt;a class="lnlinks" href="#hl-13-28"&gt;28&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-29"&gt;&lt;a class="lnlinks" href="#hl-13-29"&gt;29&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-30"&gt;&lt;a class="lnlinks" href="#hl-13-30"&gt;30&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-31"&gt;&lt;a class="lnlinks" href="#hl-13-31"&gt;31&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-32"&gt;&lt;a class="lnlinks" href="#hl-13-32"&gt;32&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-33"&gt;&lt;a class="lnlinks" href="#hl-13-33"&gt;33&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-34"&gt;&lt;a class="lnlinks" href="#hl-13-34"&gt;34&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-35"&gt;&lt;a class="lnlinks" href="#hl-13-35"&gt;35&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-36"&gt;&lt;a class="lnlinks" href="#hl-13-36"&gt;36&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-37"&gt;&lt;a class="lnlinks" href="#hl-13-37"&gt;37&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-38"&gt;&lt;a class="lnlinks" href="#hl-13-38"&gt;38&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-39"&gt;&lt;a class="lnlinks" href="#hl-13-39"&gt;39&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-40"&gt;&lt;a class="lnlinks" href="#hl-13-40"&gt;40&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-41"&gt;&lt;a class="lnlinks" href="#hl-13-41"&gt;41&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-42"&gt;&lt;a class="lnlinks" href="#hl-13-42"&gt;42&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-43"&gt;&lt;a class="lnlinks" href="#hl-13-43"&gt;43&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-13-44"&gt;&lt;a class="lnlinks" href="#hl-13-44"&gt;44&lt;/a&gt;
&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-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent.dev/v1alpha2&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Agent&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="nt"&gt;metadata&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ops-readonly-agent&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="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent&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="nt"&gt;spec&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="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; A read-only Kubernetes operations assistant. It can inspect Kubernetes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; resources and fetch documentation pages, but it must not perform write
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; operations.&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="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Declarative&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="nt"&gt;declarative&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="nt"&gt;modelConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ops-openai-model&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="nt"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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="nt"&gt;systemMessage&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="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; You are a Kubernetes operations assistant.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Rules:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Only use read-only tools.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Do not make up cluster state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - If a tool result is missing or ambiguous, say so.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Separate confirmed facts from guesses.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Never suggest deleting or modifying resources without human review.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Response format:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Summary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Evidence from tools
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Possible causes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Next checks&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="nt"&gt;tools&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="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;McpServer&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="nt"&gt;mcpServer&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="nt"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent.dev&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RemoteMCPServer&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kagent-tool-server&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="nt"&gt;toolNames&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="l"&gt;k8s_get_available_api_resources&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="l"&gt;k8s_get_resources&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="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;McpServer&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="nt"&gt;mcpServer&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;MCPServer&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mcp-website-fetcher&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="nt"&gt;toolNames&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="l"&gt;fetch&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;/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" id="hl-14-1"&gt;&lt;a class="lnlinks" href="#hl-14-1"&gt;1&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f ops-readonly-agent.yaml
&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;检查 Agent：&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" id="hl-15-1"&gt;&lt;a class="lnlinks" href="#hl-15-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-15-2"&gt;&lt;a class="lnlinks" href="#hl-15-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-15-3"&gt;&lt;a class="lnlinks" href="#hl-15-3"&gt;3&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get agent -n kagent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get agent ops-readonly-agent -n kagent -o yaml
&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;官方示例里 Agent 的 status 会出现类似 &lt;code&gt;Accepted=True&lt;/code&gt;、&lt;code&gt;Ready=True&lt;/code&gt; 的 condition。如果这里不 Ready，优先看三类对象：&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" id="hl-16-1"&gt;&lt;a class="lnlinks" href="#hl-16-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-2"&gt;&lt;a class="lnlinks" href="#hl-16-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-16-3"&gt;&lt;a class="lnlinks" href="#hl-16-3"&gt;3&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get modelconfig ops-openai-model -n kagent -o yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get mcpserver mcp-website-fetcher -n kagent -o yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get agent ops-readonly-agent -n kagent -o yaml
&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;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ModelConfig&lt;/code&gt; 是否能读到 secret。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MCPServer&lt;/code&gt; 是否已经被 kmcp controller 拉起来。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Agent&lt;/code&gt; 引用的 tool 名称是否存在。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Agent&lt;/code&gt; 的 namespace 是否能引用对应 MCP server。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="5-打开-ui-验证"&gt;&lt;a href="#5-%e6%89%93%e5%bc%80-ui-%e9%aa%8c%e8%af%81" class="header-anchor"&gt;&lt;/a&gt;5. 打开 UI 验证
&lt;/h2&gt;&lt;p&gt;本地打开 kagent UI：&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" id="hl-17-1"&gt;&lt;a class="lnlinks" href="#hl-17-1"&gt;1&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl port-forward -n kagent svc/kagent-ui 8080:8080
&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;/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" id="hl-18-1"&gt;&lt;a class="lnlinks" href="#hl-18-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://localhost:8080
&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;/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" id="hl-19-1"&gt;&lt;a class="lnlinks" href="#hl-19-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;列出当前集群里可以查询的 Kubernetes API resources。
&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;再问一个会用到 fetch 的问题：&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" id="hl-20-1"&gt;&lt;a class="lnlinks" href="#hl-20-1"&gt;1&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;帮我读取 https://kagent.dev/docs/kagent/getting-started/first-mcp-tool ，总结创建 MCPServer 和 Agent 的关键步骤。
&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;如果 Agent 正常，它应该会调用对应工具，而不是直接编造结果。这个时候我会重点看 UI 里有没有展示 tool call、参数和返回内容。kagent 这类工具的价值不只是回答，还在于能不能复盘它怎么回答的。&lt;/p&gt;
&lt;h2 id="6-权限边界我不会一开始就给写权限"&gt;&lt;a href="#6-%e6%9d%83%e9%99%90%e8%be%b9%e7%95%8c%e6%88%91%e4%b8%8d%e4%bc%9a%e4%b8%80%e5%bc%80%e5%a7%8b%e5%b0%b1%e7%bb%99%e5%86%99%e6%9d%83%e9%99%90" class="header-anchor"&gt;&lt;/a&gt;6. 权限边界：我不会一开始就给写权限
&lt;/h2&gt;&lt;p&gt;这个 case 里我故意只选了只读工具。原因很简单：Agent 最容易出事故的不是回答错，而是拿着写权限执行错。&lt;/p&gt;
&lt;p&gt;如果未来要开放写操作，我会分三层：&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" id="hl-21-1"&gt;&lt;a class="lnlinks" href="#hl-21-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-21-2"&gt;&lt;a class="lnlinks" href="#hl-21-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-21-3"&gt;&lt;a class="lnlinks" href="#hl-21-3"&gt;3&lt;/a&gt;
&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;read_only get/list/watch/logs/events
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;reviewed 生成 YAML、生成 patch，但不 apply
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;approved 人工确认后才允许执行写操作
&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;kagent 的 Tool 配置里支持 &lt;code&gt;requireApproval&lt;/code&gt;，可以把敏感工具列进去，让工具调用前停下来等人工确认。这个设计比“相信系统提示词不会乱来”靠谱。&lt;/p&gt;
&lt;p&gt;比如后续某个 MCP server 有一个 &lt;code&gt;restart_deployment&lt;/code&gt; 工具，我不会直接放开，而是会写成：&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" id="hl-22-1"&gt;&lt;a class="lnlinks" href="#hl-22-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-2"&gt;&lt;a class="lnlinks" href="#hl-22-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-3"&gt;&lt;a class="lnlinks" href="#hl-22-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-4"&gt;&lt;a class="lnlinks" href="#hl-22-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-5"&gt;&lt;a class="lnlinks" href="#hl-22-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-6"&gt;&lt;a class="lnlinks" href="#hl-22-6"&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-7"&gt;&lt;a class="lnlinks" href="#hl-22-7"&gt;7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-8"&gt;&lt;a class="lnlinks" href="#hl-22-8"&gt;8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-22-9"&gt;&lt;a class="lnlinks" href="#hl-22-9"&gt;9&lt;/a&gt;
&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-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tools&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="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;McpServer&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="nt"&gt;mcpServer&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="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;MCPServer&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="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ops-actions&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="nt"&gt;toolNames&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="l"&gt;restart_deployment&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="nt"&gt;requireApproval&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="l"&gt;restart_deployment&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;h2 id="这套-case-的落地判断"&gt;&lt;a href="#%e8%bf%99%e5%a5%97-case-%e7%9a%84%e8%90%bd%e5%9c%b0%e5%88%a4%e6%96%ad" class="header-anchor"&gt;&lt;/a&gt;这套 case 的落地判断
&lt;/h2&gt;&lt;p&gt;如果只是本地试用，&lt;code&gt;kagent install --profile demo&lt;/code&gt; 更快。官方 quickstart 里这条命令会预装一些 demo agents 和 MCP tools：&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" id="hl-23-1"&gt;&lt;a class="lnlinks" href="#hl-23-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-23-2"&gt;&lt;a class="lnlinks" href="#hl-23-2"&gt;2&lt;/a&gt;
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kagent install --profile demo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kagent dashboard
&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;但如果要往团队环境里放，我更建议走 Helm + YAML：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CRD 和 controller 由平台侧统一安装。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ModelConfig&lt;/code&gt;、&lt;code&gt;MCPServer&lt;/code&gt;、&lt;code&gt;Agent&lt;/code&gt; 放进 Git 仓库。&lt;/li&gt;
&lt;li&gt;每个 Agent 的工具列表明确写出来。&lt;/li&gt;
&lt;li&gt;写操作默认要求人工确认。&lt;/li&gt;
&lt;li&gt;先把目标限定为“排查摘要”和“信息收集”，不要一上来自动修复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;kagent 这类项目让我感觉 Kubernetes 后面会多出一种新对象：不是 workload，也不是 controller，而是“会调用工具的运维助手”。但真正能不能用，不取决于它能不能聊天，而取决于工具边界、权限边界和运行记录是不是足够清楚。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kagent.dev/docs/kagent/introduction/installation" target="_blank" rel="noopener"
 &gt;kagent Installing kagent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kagent.dev/docs/kagent/getting-started/first-mcp-tool" target="_blank" rel="noopener"
 &gt;kagent First MCP Tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kagent.dev/docs/kagent/resources/api-ref" target="_blank" rel="noopener"
 &gt;kagent API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kagent.dev/docs/kagent/examples/agents-mcp" target="_blank" rel="noopener"
 &gt;kagent Agents via MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.kagent.dev/docs/kmcp/deploy/install-controller" target="_blank" rel="noopener"
 &gt;kmcp Install controller&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cncf.io/projects/kagent/" target="_blank" rel="noopener"
 &gt;CNCF kagent 项目页&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cncf.io/blog/2025/04/15/kagent-bringing-agentic-ai-to-cloud-native/" target="_blank" rel="noopener"
 &gt;CNCF Blog: kagent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>用 Go 打造多轮工具调用 AI Agent：从代码到实战</title><link>/dkblog/017-aiAgent/</link><pubDate>Mon, 04 Aug 2025 14:57:57 +0800</pubDate><guid>/dkblog/017-aiAgent/</guid><description>&lt;img src="https://images.unsplash.com/photo-1620712943543-bcc4688e7485?auto=format&amp;fit=crop&amp;w=900&amp;q=60" alt="Featured image of post 用 Go 打造多轮工具调用 AI Agent：从代码到实战" /&gt;&lt;h1 id="用go打造多轮工具调用ai-agent从代码到实战"&gt;&lt;a href="#%e7%94%a8go%e6%89%93%e9%80%a0%e5%a4%9a%e8%bd%ae%e5%b7%a5%e5%85%b7%e8%b0%83%e7%94%a8ai-agent%e4%bb%8e%e4%bb%a3%e7%a0%81%e5%88%b0%e5%ae%9e%e6%88%98" class="header-anchor"&gt;&lt;/a&gt;用Go打造多轮工具调用AI Agent：从代码到实战
&lt;/h1&gt;&lt;p&gt;在大模型时代，单纯的文本生成已无法满足复杂需求，能调用工具的AI Agent才是主流。本文将基于Go语言和&lt;code&gt;go-openai&lt;/code&gt;库，带你从零构建一个支持天气查询、数学计算、时区时间查询的多轮工具调用Agent，完整覆盖工具定义、参数校验、递归调用全流程。&lt;/p&gt;
&lt;h2 id="核心原理ai-agent为何能调用工具"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8e%9f%e7%90%86ai-agent%e4%b8%ba%e4%bd%95%e8%83%bd%e8%b0%83%e7%94%a8%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;核心原理：AI Agent为何能调用工具？
&lt;/h2&gt;&lt;p&gt;AI Agent实现工具调用的核心逻辑，本质是“模型决策+工具执行+上下文管理”的闭环：&lt;/p&gt;
&lt;ol&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;/ol&gt;
&lt;p&gt;本文的Agent正是基于此逻辑，通过递归调用实现多步骤工具串联（如“查天气→算温差平均值→查时区时间”）。&lt;/p&gt;
&lt;h2 id="项目结构模块化设计拆解"&gt;&lt;a href="#%e9%a1%b9%e7%9b%ae%e7%bb%93%e6%9e%84%e6%a8%a1%e5%9d%97%e5%8c%96%e8%ae%be%e8%ae%a1%e6%8b%86%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;项目结构：模块化设计拆解
&lt;/h2&gt;&lt;p&gt;整个项目按功能划分为6个核心模块，每个模块职责单一，便于维护和扩展：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模块&lt;/th&gt;
					&lt;th&gt;核心功能&lt;/th&gt;
					&lt;th&gt;关键代码文件&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;工具参数结构体&lt;/td&gt;
					&lt;td&gt;定义各工具的输入参数格式，用于解析模型返回的调用指令&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WeatherParams&lt;/code&gt;/&lt;code&gt;CalcParams&lt;/code&gt;/&lt;code&gt;TimeParams&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;工具实现逻辑&lt;/td&gt;
					&lt;td&gt;封装具体工具的业务逻辑（模拟第三方API调用）&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;getWeather&lt;/code&gt;/&lt;code&gt;calculate&lt;/code&gt;/&lt;code&gt;getCurrentTime&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;工具定义（核心）&lt;/td&gt;
					&lt;td&gt;用&lt;code&gt;jsonschema&lt;/code&gt;标准化工具参数，传递给大模型&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;getOpenAITools()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;工具调用执行器&lt;/td&gt;
					&lt;td&gt;解析模型指令，分发并执行对应工具&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;executeToolCall()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;递归多轮调用&lt;/td&gt;
					&lt;td&gt;管理上下文，控制调用流程，处理模型终止逻辑&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;recursiveAgent()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;入口函数&lt;/td&gt;
					&lt;td&gt;初始化客户端，构造测试用例，启动Agent&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;main()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="关键实现从工具定义到多轮调用"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e5%ae%9e%e7%8e%b0%e4%bb%8e%e5%b7%a5%e5%85%b7%e5%ae%9a%e4%b9%89%e5%88%b0%e5%a4%9a%e8%bd%ae%e8%b0%83%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;关键实现：从工具定义到多轮调用
&lt;/h2&gt;&lt;h3 id="1-工具参数标准化用jsonschema约束输入"&gt;&lt;a href="#1-%e5%b7%a5%e5%85%b7%e5%8f%82%e6%95%b0%e6%a0%87%e5%87%86%e5%8c%96%e7%94%a8jsonschema%e7%ba%a6%e6%9d%9f%e8%be%93%e5%85%a5" class="header-anchor"&gt;&lt;/a&gt;1. 工具参数标准化：用jsonschema约束输入
&lt;/h3&gt;&lt;p&gt;大模型调用工具的前提，是明确“工具需要什么参数”。通过&lt;code&gt;go-openai/jsonschema&lt;/code&gt;库，我们可以标准化参数定义，让模型更精准地生成调用指令。&lt;/p&gt;
&lt;p&gt;以天气查询工具为例，参数定义需包含3个核心信息：&lt;/p&gt;
&lt;ul&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;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" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-6"&gt;&lt;a class="lnlinks" href="#hl-0-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-7"&gt;&lt;a class="lnlinks" href="#hl-0-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-8"&gt;&lt;a class="lnlinks" href="#hl-0-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-9"&gt;&lt;a class="lnlinks" href="#hl-0-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-10"&gt;&lt;a class="lnlinks" href="#hl-0-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-11"&gt;&lt;a class="lnlinks" href="#hl-0-11"&gt;11&lt;/a&gt;
&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="c1"&gt;// 天气工具参数定义（jsonschema）&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="nx"&gt;weatherCityParam&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;城市名称（如北京、上海，仅支持国内主流城市）&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&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="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="c1"&gt;// 天气工具整体参数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="nx"&gt;weatherParamsSchema&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&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;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;city&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;weatherCityParam&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;Required&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 必传字段：city&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;expr&lt;/code&gt;（表达式）参数，时间查询工具需定义&lt;code&gt;timezone&lt;/code&gt;（时区）参数，最终通过&lt;code&gt;getOpenAITools()&lt;/code&gt;函数组装成工具列表，传递给大模型。&lt;/p&gt;
&lt;h3 id="2-工具逻辑封装模拟真实api调用"&gt;&lt;a href="#2-%e5%b7%a5%e5%85%b7%e9%80%bb%e8%be%91%e5%b0%81%e8%a3%85%e6%a8%a1%e6%8b%9f%e7%9c%9f%e5%ae%9eapi%e8%b0%83%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;2. 工具逻辑封装：模拟真实API调用
&lt;/h3&gt;&lt;p&gt;工具实现需考虑3个关键点：超时控制、业务逻辑、异常处理，确保工具调用稳定可靠。&lt;/p&gt;
&lt;p&gt;以天气查询工具&lt;code&gt;getWeather()&lt;/code&gt;为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;超时控制&lt;/strong&gt;：用&lt;code&gt;context.WithTimeout&lt;/code&gt;设置3秒超时，避免工具调用阻塞。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;业务逻辑&lt;/strong&gt;：模拟网络延迟（500ms），用预定义的&lt;code&gt;weatherMap&lt;/code&gt;返回天气数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：未查询到城市时，返回明确的支持城市列表，提升用户体验。&lt;/li&gt;
&lt;/ul&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" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-4"&gt;&lt;a class="lnlinks" href="#hl-1-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-5"&gt;&lt;a class="lnlinks" href="#hl-1-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-6"&gt;&lt;a class="lnlinks" href="#hl-1-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-7"&gt;&lt;a class="lnlinks" href="#hl-1-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-8"&gt;&lt;a class="lnlinks" href="#hl-1-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-9"&gt;&lt;a class="lnlinks" href="#hl-1-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-10"&gt;&lt;a class="lnlinks" href="#hl-1-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-11"&gt;&lt;a class="lnlinks" href="#hl-1-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-12"&gt;&lt;a class="lnlinks" href="#hl-1-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-13"&gt;&lt;a class="lnlinks" href="#hl-1-13"&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-14"&gt;&lt;a class="lnlinks" href="#hl-1-14"&gt;14&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-15"&gt;&lt;a class="lnlinks" href="#hl-1-15"&gt;15&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-16"&gt;&lt;a class="lnlinks" href="#hl-1-16"&gt;16&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-17"&gt;&lt;a class="lnlinks" href="#hl-1-17"&gt;17&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-18"&gt;&lt;a class="lnlinks" href="#hl-1-18"&gt;18&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-19"&gt;&lt;a class="lnlinks" href="#hl-1-19"&gt;19&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-20"&gt;&lt;a class="lnlinks" href="#hl-1-20"&gt;20&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-21"&gt;&lt;a class="lnlinks" href="#hl-1-21"&gt;21&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-22"&gt;&lt;a class="lnlinks" href="#hl-1-22"&gt;22&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-23"&gt;&lt;a class="lnlinks" href="#hl-1-23"&gt;23&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-24"&gt;&lt;a class="lnlinks" href="#hl-1-24"&gt;24&lt;/a&gt;
&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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getWeather&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;city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&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="kt"&gt;string&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="c1"&gt;// 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="w"&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;cancel&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTimeout&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="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&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;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&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&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="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="k"&gt;select&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&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="s"&gt;&amp;#34;&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;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;查询超时：%v&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Millisecond&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&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="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="nx"&gt;weatherMap&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="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&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="s"&gt;&amp;#34;北京&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;晴，20-28℃，微风&amp;#34;&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="s"&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;多云，18-25℃，东北风3级&amp;#34;&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="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="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="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="nx"&gt;weather&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;weatherMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;city&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="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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[天气工具结果] 城市：%s → %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;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[天气工具结果] 未查询到「%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;city&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;h3 id="3-多轮调用核心递归agent的实现"&gt;&lt;a href="#3-%e5%a4%9a%e8%bd%ae%e8%b0%83%e7%94%a8%e6%a0%b8%e5%bf%83%e9%80%92%e5%bd%92agent%e7%9a%84%e5%ae%9e%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;3. 多轮调用核心：递归Agent的实现
&lt;/h3&gt;&lt;p&gt;递归是实现多轮工具调用的关键，&lt;code&gt;recursiveAgent()&lt;/code&gt;函数通过以下步骤控制流程：&lt;/p&gt;
&lt;h4 id="步骤1递归深度限制"&gt;&lt;a href="#%e6%ad%a5%e9%aa%a41%e9%80%92%e5%bd%92%e6%b7%b1%e5%ba%a6%e9%99%90%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;步骤1：递归深度限制
&lt;/h4&gt;&lt;p&gt;设置&lt;code&gt;maxRecursionDepth&lt;/code&gt;（本文设为5），避免模型逻辑异常导致无限循环。&lt;/p&gt;
&lt;h4 id="步骤2调用大模型"&gt;&lt;a href="#%e6%ad%a5%e9%aa%a42%e8%b0%83%e7%94%a8%e5%a4%a7%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;步骤2：调用大模型
&lt;/h4&gt;&lt;p&gt;向模型传递上下文消息和工具定义，让模型判断是否调用工具。这里需注意2个参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ToolChoice: openai.ChunkingStrategyTypeAuto&lt;/code&gt;：让模型自主决定是否调用工具。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Temperature: 0.3&lt;/code&gt;：降低随机性，确保工具调用逻辑稳定。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="步骤3处理模型终止原因finishreason"&gt;&lt;a href="#%e6%ad%a5%e9%aa%a43%e5%a4%84%e7%90%86%e6%a8%a1%e5%9e%8b%e7%bb%88%e6%ad%a2%e5%8e%9f%e5%9b%a0finishreason" class="header-anchor"&gt;&lt;/a&gt;步骤3：处理模型终止原因（FinishReason）
&lt;/h4&gt;&lt;p&gt;模型返回的&lt;code&gt;FinishReason&lt;/code&gt;决定了下一步操作，这是多轮调用的核心分支逻辑：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;FinishReason&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
					&lt;th&gt;处理方式&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;stop&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;模型无需调用工具，直接返回答案&lt;/td&gt;
					&lt;td&gt;终止递归，返回最终结果&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tool_calls&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;模型需要调用工具&lt;/td&gt;
					&lt;td&gt;执行工具，将结果加入上下文，继续递归&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;响应因Token不足被截断&lt;/td&gt;
					&lt;td&gt;提示增大&lt;code&gt;MaxTokens&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;content_filter&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;内容被过滤&lt;/td&gt;
					&lt;td&gt;告知用户无法处理&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="步骤4上下文管理"&gt;&lt;a href="#%e6%ad%a5%e9%aa%a44%e4%b8%8a%e4%b8%8b%e6%96%87%e7%ae%a1%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;步骤4：上下文管理
&lt;/h4&gt;&lt;p&gt;每次模型响应（无论是文本还是工具调用指令）、工具执行结果，都需加入上下文消息列表，确保模型能追溯历史交互，实现多轮连贯性。&lt;/p&gt;
&lt;h3 id="4-工具调用执行器解析与分发"&gt;&lt;a href="#4-%e5%b7%a5%e5%85%b7%e8%b0%83%e7%94%a8%e6%89%a7%e8%a1%8c%e5%99%a8%e8%a7%a3%e6%9e%90%e4%b8%8e%e5%88%86%e5%8f%91" class="header-anchor"&gt;&lt;/a&gt;4. 工具调用执行器：解析与分发
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;executeToolCall()&lt;/code&gt;函数负责解析模型返回的工具调用指令，分发到对应工具执行：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;参数解析&lt;/strong&gt;：将模型返回的JSON格式参数，反序列化为对应的结构体（如&lt;code&gt;WeatherParams&lt;/code&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;code&gt;get_weather&lt;/code&gt;/&lt;code&gt;calculate&lt;/code&gt;/&lt;code&gt;get_current_time&lt;/code&gt;）调用对应函数，返回执行结果。&lt;/li&gt;
&lt;/ol&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" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-7"&gt;&lt;a class="lnlinks" href="#hl-2-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-8"&gt;&lt;a class="lnlinks" href="#hl-2-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-9"&gt;&lt;a class="lnlinks" href="#hl-2-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-10"&gt;&lt;a class="lnlinks" href="#hl-2-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-11"&gt;&lt;a class="lnlinks" href="#hl-2-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-12"&gt;&lt;a class="lnlinks" href="#hl-2-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-13"&gt;&lt;a class="lnlinks" href="#hl-2-13"&gt;13&lt;/a&gt;
&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;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;executeToolCall&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;toolCall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&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="kt"&gt;string&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="k"&gt;switch&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="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;get_weather&amp;#34;&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="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="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WeatherParams&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="nx"&gt;err&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="kc"&gt;nil&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="s"&gt;&amp;#34;&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;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;参数解析失败：%v（请检查是否传入 city 字段）&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;err&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="nf"&gt;getWeather&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;City&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&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="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="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;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="实战测试复杂多轮场景验证"&gt;&lt;a href="#%e5%ae%9e%e6%88%98%e6%b5%8b%e8%af%95%e5%a4%8d%e6%9d%82%e5%a4%9a%e8%bd%ae%e5%9c%ba%e6%99%af%e9%aa%8c%e8%af%81" class="header-anchor"&gt;&lt;/a&gt;实战测试：复杂多轮场景验证
&lt;/h2&gt;&lt;p&gt;为验证Agent的多轮调用能力，我们设计一个复杂测试用例：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“查询上海的天气，计算其最高温与最低温的平均值，再查询 UTC 时区的当前时间”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="测试流程拆解"&gt;&lt;a href="#%e6%b5%8b%e8%af%95%e6%b5%81%e7%a8%8b%e6%8b%86%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;测试流程拆解
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一轮&lt;/strong&gt;：模型判断需先调用&lt;code&gt;get_weather&lt;/code&gt;工具，查询上海天气（返回“多云，18-25℃，东北风3级”）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二轮&lt;/strong&gt;：模型提取气温数据（18℃和25℃），调用&lt;code&gt;calculate&lt;/code&gt;工具计算平均值（(18+25)/2=21.5℃）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三轮&lt;/strong&gt;：模型调用&lt;code&gt;get_current_time&lt;/code&gt;工具，查询UTC时区时间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第四轮&lt;/strong&gt;：模型整合所有工具结果，用自然语言生成最终答案。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="最终输出示例"&gt;&lt;a href="#%e6%9c%80%e7%bb%88%e8%be%93%e5%87%ba%e7%a4%ba%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;最终输出示例
&lt;/h3&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" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-5"&gt;&lt;a class="lnlinks" href="#hl-3-5"&gt;5&lt;/a&gt;
&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;===========================================
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;最终答案：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;上海当前天气为多云，气温18-25℃，东北风3级；其最高温与最低温的平均值为21.5℃。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UTC时区的当前时间为2025-11-04 07:23:15。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;h2 id="扩展与优化建议"&gt;&lt;a href="#%e6%89%a9%e5%b1%95%e4%b8%8e%e4%bc%98%e5%8c%96%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;扩展与优化建议
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;增加更多工具&lt;/strong&gt;：按现有模式，可轻松扩展股票查询、快递跟踪等工具，只需新增参数结构体、工具实现和jsonschema定义。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数校验增强&lt;/strong&gt;：引入&lt;code&gt;go-playground/validator&lt;/code&gt;库，实现更复杂的参数校验（如城市名称合法性、数学表达式格式）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日志与监控&lt;/strong&gt;：增加结构化日志（如用&lt;code&gt;zap&lt;/code&gt;库），记录工具调用耗时、结果状态，便于问题排查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并发工具调用&lt;/strong&gt;：当前工具执行是串行的，可通过&lt;code&gt;goroutine&lt;/code&gt;实现多工具并行调用，提升效率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型切换&lt;/strong&gt;：本文使用&lt;code&gt;Qwen3-Next-80B-A3B-Instruct&lt;/code&gt;模型，可替换为&lt;code&gt;gpt-4o&lt;/code&gt;/&lt;code&gt;claude-3&lt;/code&gt;等支持工具调用的模型，只需调整客户端配置。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="完整代码获取"&gt;&lt;a href="#%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81%e8%8e%b7%e5%8f%96" class="header-anchor"&gt;&lt;/a&gt;完整代码获取
&lt;/h2&gt;&lt;p&gt;本文的完整代码已整理，包含工具定义、递归Agent、测试用例全流程，可直接运行（需替换API Key和BaseURL）。&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" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-5"&gt;&lt;a class="lnlinks" href="#hl-4-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-6"&gt;&lt;a class="lnlinks" href="#hl-4-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-7"&gt;&lt;a class="lnlinks" href="#hl-4-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-8"&gt;&lt;a class="lnlinks" href="#hl-4-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-9"&gt;&lt;a class="lnlinks" href="#hl-4-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-10"&gt;&lt;a class="lnlinks" href="#hl-4-10"&gt; 10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-11"&gt;&lt;a class="lnlinks" href="#hl-4-11"&gt; 11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-12"&gt;&lt;a class="lnlinks" href="#hl-4-12"&gt; 12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-13"&gt;&lt;a class="lnlinks" href="#hl-4-13"&gt; 13&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-14"&gt;&lt;a class="lnlinks" href="#hl-4-14"&gt; 14&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-15"&gt;&lt;a class="lnlinks" href="#hl-4-15"&gt; 15&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-16"&gt;&lt;a class="lnlinks" href="#hl-4-16"&gt; 16&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-17"&gt;&lt;a class="lnlinks" href="#hl-4-17"&gt; 17&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-18"&gt;&lt;a class="lnlinks" href="#hl-4-18"&gt; 18&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-19"&gt;&lt;a class="lnlinks" href="#hl-4-19"&gt; 19&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-20"&gt;&lt;a class="lnlinks" href="#hl-4-20"&gt; 20&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-21"&gt;&lt;a class="lnlinks" href="#hl-4-21"&gt; 21&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-22"&gt;&lt;a class="lnlinks" href="#hl-4-22"&gt; 22&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-23"&gt;&lt;a class="lnlinks" href="#hl-4-23"&gt; 23&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-24"&gt;&lt;a class="lnlinks" href="#hl-4-24"&gt; 24&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-25"&gt;&lt;a class="lnlinks" href="#hl-4-25"&gt; 25&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-26"&gt;&lt;a class="lnlinks" href="#hl-4-26"&gt; 26&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-27"&gt;&lt;a class="lnlinks" href="#hl-4-27"&gt; 27&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-28"&gt;&lt;a class="lnlinks" href="#hl-4-28"&gt; 28&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-29"&gt;&lt;a class="lnlinks" href="#hl-4-29"&gt; 29&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-30"&gt;&lt;a class="lnlinks" href="#hl-4-30"&gt; 30&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-31"&gt;&lt;a class="lnlinks" href="#hl-4-31"&gt; 31&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-32"&gt;&lt;a class="lnlinks" href="#hl-4-32"&gt; 32&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-33"&gt;&lt;a class="lnlinks" href="#hl-4-33"&gt; 33&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-34"&gt;&lt;a class="lnlinks" href="#hl-4-34"&gt; 34&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-35"&gt;&lt;a class="lnlinks" href="#hl-4-35"&gt; 35&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-36"&gt;&lt;a class="lnlinks" href="#hl-4-36"&gt; 36&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-37"&gt;&lt;a class="lnlinks" href="#hl-4-37"&gt; 37&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-38"&gt;&lt;a class="lnlinks" href="#hl-4-38"&gt; 38&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-39"&gt;&lt;a class="lnlinks" href="#hl-4-39"&gt; 39&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-40"&gt;&lt;a class="lnlinks" href="#hl-4-40"&gt; 40&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-41"&gt;&lt;a class="lnlinks" href="#hl-4-41"&gt; 41&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-42"&gt;&lt;a class="lnlinks" href="#hl-4-42"&gt; 42&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-43"&gt;&lt;a class="lnlinks" href="#hl-4-43"&gt; 43&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-44"&gt;&lt;a class="lnlinks" href="#hl-4-44"&gt; 44&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-45"&gt;&lt;a class="lnlinks" href="#hl-4-45"&gt; 45&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-46"&gt;&lt;a class="lnlinks" href="#hl-4-46"&gt; 46&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-47"&gt;&lt;a class="lnlinks" href="#hl-4-47"&gt; 47&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-48"&gt;&lt;a class="lnlinks" href="#hl-4-48"&gt; 48&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-49"&gt;&lt;a class="lnlinks" href="#hl-4-49"&gt; 49&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-50"&gt;&lt;a class="lnlinks" href="#hl-4-50"&gt; 50&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-51"&gt;&lt;a class="lnlinks" href="#hl-4-51"&gt; 51&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-52"&gt;&lt;a class="lnlinks" href="#hl-4-52"&gt; 52&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-53"&gt;&lt;a class="lnlinks" href="#hl-4-53"&gt; 53&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-54"&gt;&lt;a class="lnlinks" href="#hl-4-54"&gt; 54&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-55"&gt;&lt;a class="lnlinks" href="#hl-4-55"&gt; 55&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-56"&gt;&lt;a class="lnlinks" href="#hl-4-56"&gt; 56&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-57"&gt;&lt;a class="lnlinks" href="#hl-4-57"&gt; 57&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-58"&gt;&lt;a class="lnlinks" href="#hl-4-58"&gt; 58&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-59"&gt;&lt;a class="lnlinks" href="#hl-4-59"&gt; 59&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-60"&gt;&lt;a class="lnlinks" href="#hl-4-60"&gt; 60&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-61"&gt;&lt;a class="lnlinks" href="#hl-4-61"&gt; 61&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-62"&gt;&lt;a class="lnlinks" href="#hl-4-62"&gt; 62&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-63"&gt;&lt;a class="lnlinks" href="#hl-4-63"&gt; 63&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-64"&gt;&lt;a class="lnlinks" href="#hl-4-64"&gt; 64&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-65"&gt;&lt;a class="lnlinks" href="#hl-4-65"&gt; 65&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-66"&gt;&lt;a class="lnlinks" href="#hl-4-66"&gt; 66&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-67"&gt;&lt;a class="lnlinks" href="#hl-4-67"&gt; 67&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-68"&gt;&lt;a class="lnlinks" href="#hl-4-68"&gt; 68&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-69"&gt;&lt;a class="lnlinks" href="#hl-4-69"&gt; 69&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-70"&gt;&lt;a class="lnlinks" href="#hl-4-70"&gt; 70&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-71"&gt;&lt;a class="lnlinks" href="#hl-4-71"&gt; 71&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-72"&gt;&lt;a class="lnlinks" href="#hl-4-72"&gt; 72&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-73"&gt;&lt;a class="lnlinks" href="#hl-4-73"&gt; 73&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-74"&gt;&lt;a class="lnlinks" href="#hl-4-74"&gt; 74&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-75"&gt;&lt;a class="lnlinks" href="#hl-4-75"&gt; 75&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-76"&gt;&lt;a class="lnlinks" href="#hl-4-76"&gt; 76&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-77"&gt;&lt;a class="lnlinks" href="#hl-4-77"&gt; 77&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-78"&gt;&lt;a class="lnlinks" href="#hl-4-78"&gt; 78&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-79"&gt;&lt;a class="lnlinks" href="#hl-4-79"&gt; 79&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-80"&gt;&lt;a class="lnlinks" href="#hl-4-80"&gt; 80&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-81"&gt;&lt;a class="lnlinks" href="#hl-4-81"&gt; 81&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-82"&gt;&lt;a class="lnlinks" href="#hl-4-82"&gt; 82&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-83"&gt;&lt;a class="lnlinks" href="#hl-4-83"&gt; 83&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-84"&gt;&lt;a class="lnlinks" href="#hl-4-84"&gt; 84&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-85"&gt;&lt;a class="lnlinks" href="#hl-4-85"&gt; 85&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-86"&gt;&lt;a class="lnlinks" href="#hl-4-86"&gt; 86&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-87"&gt;&lt;a class="lnlinks" href="#hl-4-87"&gt; 87&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-88"&gt;&lt;a class="lnlinks" href="#hl-4-88"&gt; 88&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-89"&gt;&lt;a class="lnlinks" href="#hl-4-89"&gt; 89&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-90"&gt;&lt;a class="lnlinks" href="#hl-4-90"&gt; 90&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-91"&gt;&lt;a class="lnlinks" href="#hl-4-91"&gt; 91&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-92"&gt;&lt;a class="lnlinks" href="#hl-4-92"&gt; 92&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-93"&gt;&lt;a class="lnlinks" href="#hl-4-93"&gt; 93&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-94"&gt;&lt;a class="lnlinks" href="#hl-4-94"&gt; 94&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-95"&gt;&lt;a class="lnlinks" href="#hl-4-95"&gt; 95&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-96"&gt;&lt;a class="lnlinks" href="#hl-4-96"&gt; 96&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-97"&gt;&lt;a class="lnlinks" href="#hl-4-97"&gt; 97&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-98"&gt;&lt;a class="lnlinks" href="#hl-4-98"&gt; 98&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-99"&gt;&lt;a class="lnlinks" href="#hl-4-99"&gt; 99&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-100"&gt;&lt;a class="lnlinks" href="#hl-4-100"&gt;100&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-101"&gt;&lt;a class="lnlinks" href="#hl-4-101"&gt;101&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-102"&gt;&lt;a class="lnlinks" href="#hl-4-102"&gt;102&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-103"&gt;&lt;a class="lnlinks" href="#hl-4-103"&gt;103&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-104"&gt;&lt;a class="lnlinks" href="#hl-4-104"&gt;104&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-105"&gt;&lt;a class="lnlinks" href="#hl-4-105"&gt;105&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-106"&gt;&lt;a class="lnlinks" href="#hl-4-106"&gt;106&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-107"&gt;&lt;a class="lnlinks" href="#hl-4-107"&gt;107&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-108"&gt;&lt;a class="lnlinks" href="#hl-4-108"&gt;108&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-109"&gt;&lt;a class="lnlinks" href="#hl-4-109"&gt;109&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-110"&gt;&lt;a class="lnlinks" href="#hl-4-110"&gt;110&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-111"&gt;&lt;a class="lnlinks" href="#hl-4-111"&gt;111&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-112"&gt;&lt;a class="lnlinks" href="#hl-4-112"&gt;112&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-113"&gt;&lt;a class="lnlinks" href="#hl-4-113"&gt;113&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-114"&gt;&lt;a class="lnlinks" href="#hl-4-114"&gt;114&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-115"&gt;&lt;a class="lnlinks" href="#hl-4-115"&gt;115&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-116"&gt;&lt;a class="lnlinks" href="#hl-4-116"&gt;116&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-117"&gt;&lt;a class="lnlinks" href="#hl-4-117"&gt;117&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-118"&gt;&lt;a class="lnlinks" href="#hl-4-118"&gt;118&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-119"&gt;&lt;a class="lnlinks" href="#hl-4-119"&gt;119&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-120"&gt;&lt;a class="lnlinks" href="#hl-4-120"&gt;120&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-121"&gt;&lt;a class="lnlinks" href="#hl-4-121"&gt;121&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-122"&gt;&lt;a class="lnlinks" href="#hl-4-122"&gt;122&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-123"&gt;&lt;a class="lnlinks" href="#hl-4-123"&gt;123&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-124"&gt;&lt;a class="lnlinks" href="#hl-4-124"&gt;124&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-125"&gt;&lt;a class="lnlinks" href="#hl-4-125"&gt;125&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-126"&gt;&lt;a class="lnlinks" href="#hl-4-126"&gt;126&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-127"&gt;&lt;a class="lnlinks" href="#hl-4-127"&gt;127&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-128"&gt;&lt;a class="lnlinks" href="#hl-4-128"&gt;128&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-129"&gt;&lt;a class="lnlinks" href="#hl-4-129"&gt;129&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-130"&gt;&lt;a class="lnlinks" href="#hl-4-130"&gt;130&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-131"&gt;&lt;a class="lnlinks" href="#hl-4-131"&gt;131&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-132"&gt;&lt;a class="lnlinks" href="#hl-4-132"&gt;132&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-133"&gt;&lt;a class="lnlinks" href="#hl-4-133"&gt;133&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-134"&gt;&lt;a class="lnlinks" href="#hl-4-134"&gt;134&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-135"&gt;&lt;a class="lnlinks" href="#hl-4-135"&gt;135&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-136"&gt;&lt;a class="lnlinks" href="#hl-4-136"&gt;136&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-137"&gt;&lt;a class="lnlinks" href="#hl-4-137"&gt;137&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-138"&gt;&lt;a class="lnlinks" href="#hl-4-138"&gt;138&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-139"&gt;&lt;a class="lnlinks" href="#hl-4-139"&gt;139&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-140"&gt;&lt;a class="lnlinks" href="#hl-4-140"&gt;140&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-141"&gt;&lt;a class="lnlinks" href="#hl-4-141"&gt;141&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-142"&gt;&lt;a class="lnlinks" href="#hl-4-142"&gt;142&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-143"&gt;&lt;a class="lnlinks" href="#hl-4-143"&gt;143&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-144"&gt;&lt;a class="lnlinks" href="#hl-4-144"&gt;144&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-145"&gt;&lt;a class="lnlinks" href="#hl-4-145"&gt;145&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-146"&gt;&lt;a class="lnlinks" href="#hl-4-146"&gt;146&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-147"&gt;&lt;a class="lnlinks" href="#hl-4-147"&gt;147&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-148"&gt;&lt;a class="lnlinks" href="#hl-4-148"&gt;148&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-149"&gt;&lt;a class="lnlinks" href="#hl-4-149"&gt;149&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-150"&gt;&lt;a class="lnlinks" href="#hl-4-150"&gt;150&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-151"&gt;&lt;a class="lnlinks" href="#hl-4-151"&gt;151&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-152"&gt;&lt;a class="lnlinks" href="#hl-4-152"&gt;152&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-153"&gt;&lt;a class="lnlinks" href="#hl-4-153"&gt;153&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-154"&gt;&lt;a class="lnlinks" href="#hl-4-154"&gt;154&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-155"&gt;&lt;a class="lnlinks" href="#hl-4-155"&gt;155&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-156"&gt;&lt;a class="lnlinks" href="#hl-4-156"&gt;156&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-157"&gt;&lt;a class="lnlinks" href="#hl-4-157"&gt;157&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-158"&gt;&lt;a class="lnlinks" href="#hl-4-158"&gt;158&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-159"&gt;&lt;a class="lnlinks" href="#hl-4-159"&gt;159&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-160"&gt;&lt;a class="lnlinks" href="#hl-4-160"&gt;160&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-161"&gt;&lt;a class="lnlinks" href="#hl-4-161"&gt;161&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-162"&gt;&lt;a class="lnlinks" href="#hl-4-162"&gt;162&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-163"&gt;&lt;a class="lnlinks" href="#hl-4-163"&gt;163&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-164"&gt;&lt;a class="lnlinks" href="#hl-4-164"&gt;164&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-165"&gt;&lt;a class="lnlinks" href="#hl-4-165"&gt;165&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-166"&gt;&lt;a class="lnlinks" href="#hl-4-166"&gt;166&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-167"&gt;&lt;a class="lnlinks" href="#hl-4-167"&gt;167&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-168"&gt;&lt;a class="lnlinks" href="#hl-4-168"&gt;168&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-169"&gt;&lt;a class="lnlinks" href="#hl-4-169"&gt;169&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-170"&gt;&lt;a class="lnlinks" href="#hl-4-170"&gt;170&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-171"&gt;&lt;a class="lnlinks" href="#hl-4-171"&gt;171&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-172"&gt;&lt;a class="lnlinks" href="#hl-4-172"&gt;172&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-173"&gt;&lt;a class="lnlinks" href="#hl-4-173"&gt;173&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-174"&gt;&lt;a class="lnlinks" href="#hl-4-174"&gt;174&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-175"&gt;&lt;a class="lnlinks" href="#hl-4-175"&gt;175&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-176"&gt;&lt;a class="lnlinks" href="#hl-4-176"&gt;176&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-177"&gt;&lt;a class="lnlinks" href="#hl-4-177"&gt;177&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-178"&gt;&lt;a class="lnlinks" href="#hl-4-178"&gt;178&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-179"&gt;&lt;a class="lnlinks" href="#hl-4-179"&gt;179&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-180"&gt;&lt;a class="lnlinks" href="#hl-4-180"&gt;180&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-181"&gt;&lt;a class="lnlinks" href="#hl-4-181"&gt;181&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-182"&gt;&lt;a class="lnlinks" href="#hl-4-182"&gt;182&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-183"&gt;&lt;a class="lnlinks" href="#hl-4-183"&gt;183&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-184"&gt;&lt;a class="lnlinks" href="#hl-4-184"&gt;184&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-185"&gt;&lt;a class="lnlinks" href="#hl-4-185"&gt;185&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-186"&gt;&lt;a class="lnlinks" href="#hl-4-186"&gt;186&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-187"&gt;&lt;a class="lnlinks" href="#hl-4-187"&gt;187&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-188"&gt;&lt;a class="lnlinks" href="#hl-4-188"&gt;188&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-189"&gt;&lt;a class="lnlinks" href="#hl-4-189"&gt;189&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-190"&gt;&lt;a class="lnlinks" href="#hl-4-190"&gt;190&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-191"&gt;&lt;a class="lnlinks" href="#hl-4-191"&gt;191&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-192"&gt;&lt;a class="lnlinks" href="#hl-4-192"&gt;192&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-193"&gt;&lt;a class="lnlinks" href="#hl-4-193"&gt;193&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-194"&gt;&lt;a class="lnlinks" href="#hl-4-194"&gt;194&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-195"&gt;&lt;a class="lnlinks" href="#hl-4-195"&gt;195&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-196"&gt;&lt;a class="lnlinks" href="#hl-4-196"&gt;196&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-197"&gt;&lt;a class="lnlinks" href="#hl-4-197"&gt;197&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-198"&gt;&lt;a class="lnlinks" href="#hl-4-198"&gt;198&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-199"&gt;&lt;a class="lnlinks" href="#hl-4-199"&gt;199&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-200"&gt;&lt;a class="lnlinks" href="#hl-4-200"&gt;200&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-201"&gt;&lt;a class="lnlinks" href="#hl-4-201"&gt;201&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-202"&gt;&lt;a class="lnlinks" href="#hl-4-202"&gt;202&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-203"&gt;&lt;a class="lnlinks" href="#hl-4-203"&gt;203&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-204"&gt;&lt;a class="lnlinks" href="#hl-4-204"&gt;204&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-205"&gt;&lt;a class="lnlinks" href="#hl-4-205"&gt;205&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-206"&gt;&lt;a class="lnlinks" href="#hl-4-206"&gt;206&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-207"&gt;&lt;a class="lnlinks" href="#hl-4-207"&gt;207&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-208"&gt;&lt;a class="lnlinks" href="#hl-4-208"&gt;208&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-209"&gt;&lt;a class="lnlinks" href="#hl-4-209"&gt;209&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-210"&gt;&lt;a class="lnlinks" href="#hl-4-210"&gt;210&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-211"&gt;&lt;a class="lnlinks" href="#hl-4-211"&gt;211&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-212"&gt;&lt;a class="lnlinks" href="#hl-4-212"&gt;212&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-213"&gt;&lt;a class="lnlinks" href="#hl-4-213"&gt;213&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-214"&gt;&lt;a class="lnlinks" href="#hl-4-214"&gt;214&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-215"&gt;&lt;a class="lnlinks" href="#hl-4-215"&gt;215&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-216"&gt;&lt;a class="lnlinks" href="#hl-4-216"&gt;216&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-217"&gt;&lt;a class="lnlinks" href="#hl-4-217"&gt;217&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-218"&gt;&lt;a class="lnlinks" href="#hl-4-218"&gt;218&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-219"&gt;&lt;a class="lnlinks" href="#hl-4-219"&gt;219&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-220"&gt;&lt;a class="lnlinks" href="#hl-4-220"&gt;220&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-221"&gt;&lt;a class="lnlinks" href="#hl-4-221"&gt;221&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-222"&gt;&lt;a class="lnlinks" href="#hl-4-222"&gt;222&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-223"&gt;&lt;a class="lnlinks" href="#hl-4-223"&gt;223&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-224"&gt;&lt;a class="lnlinks" href="#hl-4-224"&gt;224&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-225"&gt;&lt;a class="lnlinks" href="#hl-4-225"&gt;225&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-226"&gt;&lt;a class="lnlinks" href="#hl-4-226"&gt;226&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-227"&gt;&lt;a class="lnlinks" href="#hl-4-227"&gt;227&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-228"&gt;&lt;a class="lnlinks" href="#hl-4-228"&gt;228&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-229"&gt;&lt;a class="lnlinks" href="#hl-4-229"&gt;229&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-230"&gt;&lt;a class="lnlinks" href="#hl-4-230"&gt;230&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-231"&gt;&lt;a class="lnlinks" href="#hl-4-231"&gt;231&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-232"&gt;&lt;a class="lnlinks" href="#hl-4-232"&gt;232&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-233"&gt;&lt;a class="lnlinks" href="#hl-4-233"&gt;233&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-234"&gt;&lt;a class="lnlinks" href="#hl-4-234"&gt;234&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-235"&gt;&lt;a class="lnlinks" href="#hl-4-235"&gt;235&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-236"&gt;&lt;a class="lnlinks" href="#hl-4-236"&gt;236&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-237"&gt;&lt;a class="lnlinks" href="#hl-4-237"&gt;237&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-238"&gt;&lt;a class="lnlinks" href="#hl-4-238"&gt;238&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-239"&gt;&lt;a class="lnlinks" href="#hl-4-239"&gt;239&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-240"&gt;&lt;a class="lnlinks" href="#hl-4-240"&gt;240&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-241"&gt;&lt;a class="lnlinks" href="#hl-4-241"&gt;241&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-242"&gt;&lt;a class="lnlinks" href="#hl-4-242"&gt;242&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-243"&gt;&lt;a class="lnlinks" href="#hl-4-243"&gt;243&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-244"&gt;&lt;a class="lnlinks" href="#hl-4-244"&gt;244&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-245"&gt;&lt;a class="lnlinks" href="#hl-4-245"&gt;245&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-246"&gt;&lt;a class="lnlinks" href="#hl-4-246"&gt;246&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-247"&gt;&lt;a class="lnlinks" href="#hl-4-247"&gt;247&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-248"&gt;&lt;a class="lnlinks" href="#hl-4-248"&gt;248&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-249"&gt;&lt;a class="lnlinks" href="#hl-4-249"&gt;249&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-250"&gt;&lt;a class="lnlinks" href="#hl-4-250"&gt;250&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-251"&gt;&lt;a class="lnlinks" href="#hl-4-251"&gt;251&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-252"&gt;&lt;a class="lnlinks" href="#hl-4-252"&gt;252&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-253"&gt;&lt;a class="lnlinks" href="#hl-4-253"&gt;253&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-254"&gt;&lt;a class="lnlinks" href="#hl-4-254"&gt;254&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-255"&gt;&lt;a class="lnlinks" href="#hl-4-255"&gt;255&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-256"&gt;&lt;a class="lnlinks" href="#hl-4-256"&gt;256&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-257"&gt;&lt;a class="lnlinks" href="#hl-4-257"&gt;257&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-258"&gt;&lt;a class="lnlinks" href="#hl-4-258"&gt;258&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-259"&gt;&lt;a class="lnlinks" href="#hl-4-259"&gt;259&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-260"&gt;&lt;a class="lnlinks" href="#hl-4-260"&gt;260&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-261"&gt;&lt;a class="lnlinks" href="#hl-4-261"&gt;261&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-262"&gt;&lt;a class="lnlinks" href="#hl-4-262"&gt;262&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-263"&gt;&lt;a class="lnlinks" href="#hl-4-263"&gt;263&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-264"&gt;&lt;a class="lnlinks" href="#hl-4-264"&gt;264&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-265"&gt;&lt;a class="lnlinks" href="#hl-4-265"&gt;265&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-266"&gt;&lt;a class="lnlinks" href="#hl-4-266"&gt;266&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-267"&gt;&lt;a class="lnlinks" href="#hl-4-267"&gt;267&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-268"&gt;&lt;a class="lnlinks" href="#hl-4-268"&gt;268&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-269"&gt;&lt;a class="lnlinks" href="#hl-4-269"&gt;269&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-270"&gt;&lt;a class="lnlinks" href="#hl-4-270"&gt;270&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-271"&gt;&lt;a class="lnlinks" href="#hl-4-271"&gt;271&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-272"&gt;&lt;a class="lnlinks" href="#hl-4-272"&gt;272&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-273"&gt;&lt;a class="lnlinks" href="#hl-4-273"&gt;273&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-274"&gt;&lt;a class="lnlinks" href="#hl-4-274"&gt;274&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-275"&gt;&lt;a class="lnlinks" href="#hl-4-275"&gt;275&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-276"&gt;&lt;a class="lnlinks" href="#hl-4-276"&gt;276&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-277"&gt;&lt;a class="lnlinks" href="#hl-4-277"&gt;277&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-278"&gt;&lt;a class="lnlinks" href="#hl-4-278"&gt;278&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-279"&gt;&lt;a class="lnlinks" href="#hl-4-279"&gt;279&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-280"&gt;&lt;a class="lnlinks" href="#hl-4-280"&gt;280&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-281"&gt;&lt;a class="lnlinks" href="#hl-4-281"&gt;281&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-282"&gt;&lt;a class="lnlinks" href="#hl-4-282"&gt;282&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-283"&gt;&lt;a class="lnlinks" href="#hl-4-283"&gt;283&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-284"&gt;&lt;a class="lnlinks" href="#hl-4-284"&gt;284&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-285"&gt;&lt;a class="lnlinks" href="#hl-4-285"&gt;285&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-286"&gt;&lt;a class="lnlinks" href="#hl-4-286"&gt;286&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-287"&gt;&lt;a class="lnlinks" href="#hl-4-287"&gt;287&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-288"&gt;&lt;a class="lnlinks" href="#hl-4-288"&gt;288&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-289"&gt;&lt;a class="lnlinks" href="#hl-4-289"&gt;289&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-290"&gt;&lt;a class="lnlinks" href="#hl-4-290"&gt;290&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-291"&gt;&lt;a class="lnlinks" href="#hl-4-291"&gt;291&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-292"&gt;&lt;a class="lnlinks" href="#hl-4-292"&gt;292&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-293"&gt;&lt;a class="lnlinks" href="#hl-4-293"&gt;293&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-294"&gt;&lt;a class="lnlinks" href="#hl-4-294"&gt;294&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-295"&gt;&lt;a class="lnlinks" href="#hl-4-295"&gt;295&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-296"&gt;&lt;a class="lnlinks" href="#hl-4-296"&gt;296&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-297"&gt;&lt;a class="lnlinks" href="#hl-4-297"&gt;297&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-298"&gt;&lt;a class="lnlinks" href="#hl-4-298"&gt;298&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-299"&gt;&lt;a class="lnlinks" href="#hl-4-299"&gt;299&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-300"&gt;&lt;a class="lnlinks" href="#hl-4-300"&gt;300&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-301"&gt;&lt;a class="lnlinks" href="#hl-4-301"&gt;301&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-302"&gt;&lt;a class="lnlinks" href="#hl-4-302"&gt;302&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-303"&gt;&lt;a class="lnlinks" href="#hl-4-303"&gt;303&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-304"&gt;&lt;a class="lnlinks" href="#hl-4-304"&gt;304&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-305"&gt;&lt;a class="lnlinks" href="#hl-4-305"&gt;305&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-306"&gt;&lt;a class="lnlinks" href="#hl-4-306"&gt;306&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-307"&gt;&lt;a class="lnlinks" href="#hl-4-307"&gt;307&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-308"&gt;&lt;a class="lnlinks" href="#hl-4-308"&gt;308&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-309"&gt;&lt;a class="lnlinks" href="#hl-4-309"&gt;309&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-310"&gt;&lt;a class="lnlinks" href="#hl-4-310"&gt;310&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-311"&gt;&lt;a class="lnlinks" href="#hl-4-311"&gt;311&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-312"&gt;&lt;a class="lnlinks" href="#hl-4-312"&gt;312&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-313"&gt;&lt;a class="lnlinks" href="#hl-4-313"&gt;313&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-314"&gt;&lt;a class="lnlinks" href="#hl-4-314"&gt;314&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-315"&gt;&lt;a class="lnlinks" href="#hl-4-315"&gt;315&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-316"&gt;&lt;a class="lnlinks" href="#hl-4-316"&gt;316&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-317"&gt;&lt;a class="lnlinks" href="#hl-4-317"&gt;317&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-318"&gt;&lt;a class="lnlinks" href="#hl-4-318"&gt;318&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-319"&gt;&lt;a class="lnlinks" href="#hl-4-319"&gt;319&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-320"&gt;&lt;a class="lnlinks" href="#hl-4-320"&gt;320&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-321"&gt;&lt;a class="lnlinks" href="#hl-4-321"&gt;321&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-322"&gt;&lt;a class="lnlinks" href="#hl-4-322"&gt;322&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-323"&gt;&lt;a class="lnlinks" href="#hl-4-323"&gt;323&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-324"&gt;&lt;a class="lnlinks" href="#hl-4-324"&gt;324&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-325"&gt;&lt;a class="lnlinks" href="#hl-4-325"&gt;325&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-326"&gt;&lt;a class="lnlinks" href="#hl-4-326"&gt;326&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-327"&gt;&lt;a class="lnlinks" href="#hl-4-327"&gt;327&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-328"&gt;&lt;a class="lnlinks" href="#hl-4-328"&gt;328&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-329"&gt;&lt;a class="lnlinks" href="#hl-4-329"&gt;329&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-330"&gt;&lt;a class="lnlinks" href="#hl-4-330"&gt;330&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-331"&gt;&lt;a class="lnlinks" href="#hl-4-331"&gt;331&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-332"&gt;&lt;a class="lnlinks" href="#hl-4-332"&gt;332&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-333"&gt;&lt;a class="lnlinks" href="#hl-4-333"&gt;333&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-334"&gt;&lt;a class="lnlinks" href="#hl-4-334"&gt;334&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-335"&gt;&lt;a class="lnlinks" href="#hl-4-335"&gt;335&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-336"&gt;&lt;a class="lnlinks" href="#hl-4-336"&gt;336&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-337"&gt;&lt;a class="lnlinks" href="#hl-4-337"&gt;337&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-338"&gt;&lt;a class="lnlinks" href="#hl-4-338"&gt;338&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-339"&gt;&lt;a class="lnlinks" href="#hl-4-339"&gt;339&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-340"&gt;&lt;a class="lnlinks" href="#hl-4-340"&gt;340&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-341"&gt;&lt;a class="lnlinks" href="#hl-4-341"&gt;341&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-342"&gt;&lt;a class="lnlinks" href="#hl-4-342"&gt;342&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-343"&gt;&lt;a class="lnlinks" href="#hl-4-343"&gt;343&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-344"&gt;&lt;a class="lnlinks" href="#hl-4-344"&gt;344&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-345"&gt;&lt;a class="lnlinks" href="#hl-4-345"&gt;345&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-346"&gt;&lt;a class="lnlinks" href="#hl-4-346"&gt;346&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-347"&gt;&lt;a class="lnlinks" href="#hl-4-347"&gt;347&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-348"&gt;&lt;a class="lnlinks" href="#hl-4-348"&gt;348&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-349"&gt;&lt;a class="lnlinks" href="#hl-4-349"&gt;349&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-350"&gt;&lt;a class="lnlinks" href="#hl-4-350"&gt;350&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-351"&gt;&lt;a class="lnlinks" href="#hl-4-351"&gt;351&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-352"&gt;&lt;a class="lnlinks" href="#hl-4-352"&gt;352&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-353"&gt;&lt;a class="lnlinks" href="#hl-4-353"&gt;353&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-354"&gt;&lt;a class="lnlinks" href="#hl-4-354"&gt;354&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-355"&gt;&lt;a class="lnlinks" href="#hl-4-355"&gt;355&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-356"&gt;&lt;a class="lnlinks" href="#hl-4-356"&gt;356&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-357"&gt;&lt;a class="lnlinks" href="#hl-4-357"&gt;357&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-358"&gt;&lt;a class="lnlinks" href="#hl-4-358"&gt;358&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-359"&gt;&lt;a class="lnlinks" href="#hl-4-359"&gt;359&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-360"&gt;&lt;a class="lnlinks" href="#hl-4-360"&gt;360&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-361"&gt;&lt;a class="lnlinks" href="#hl-4-361"&gt;361&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-362"&gt;&lt;a class="lnlinks" href="#hl-4-362"&gt;362&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-363"&gt;&lt;a class="lnlinks" href="#hl-4-363"&gt;363&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-364"&gt;&lt;a class="lnlinks" href="#hl-4-364"&gt;364&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-365"&gt;&lt;a class="lnlinks" href="#hl-4-365"&gt;365&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-366"&gt;&lt;a class="lnlinks" href="#hl-4-366"&gt;366&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-367"&gt;&lt;a class="lnlinks" href="#hl-4-367"&gt;367&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-368"&gt;&lt;a class="lnlinks" href="#hl-4-368"&gt;368&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-369"&gt;&lt;a class="lnlinks" href="#hl-4-369"&gt;369&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-370"&gt;&lt;a class="lnlinks" href="#hl-4-370"&gt;370&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-371"&gt;&lt;a class="lnlinks" href="#hl-4-371"&gt;371&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-372"&gt;&lt;a class="lnlinks" href="#hl-4-372"&gt;372&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-373"&gt;&lt;a class="lnlinks" href="#hl-4-373"&gt;373&lt;/a&gt;
&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="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&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="kn"&gt;import&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="s"&gt;&amp;#34;context&amp;#34;&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="s"&gt;&amp;#34;encoding/json&amp;#34;&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="s"&gt;&amp;#34;fmt&amp;#34;&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="s"&gt;&amp;#34;math&amp;#34;&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="s"&gt;&amp;#34;time&amp;#34;&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="w"&gt;	&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;github.com/sashabaranov/go-openai&amp;#34;&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="s"&gt;&amp;#34;github.com/sashabaranov/go-openai/jsonschema&amp;#34;&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;// ========================== 1. 工具参数结构体（用于解析工具调用结果） ==========================&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="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="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WeatherParams&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&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;City&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="s"&gt;`json:&amp;#34;city&amp;#34;`&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;// 数学计算参数&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CalcParams&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&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;Expr&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="s"&gt;`json:&amp;#34;expr&amp;#34;`&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;// 时间查询参数&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeParams&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&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;Timezone&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="s"&gt;`json:&amp;#34;timezone&amp;#34;`&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;// ========================== 2. 工具实现逻辑（无修改） ==========================&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="c1"&gt;// 天气查询工具（模拟第三方 API 调用）&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="nf"&gt;getWeather&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;city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&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="kt"&gt;string&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="c1"&gt;// 超时控制（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="w"&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;cancel&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTimeout&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="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&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;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&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&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="c1"&gt;// 模拟 API 网络延迟&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;select&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&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="s"&gt;&amp;#34;&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;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;查询超时：%v&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Millisecond&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&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="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="nx"&gt;weatherMap&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="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&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="s"&gt;&amp;#34;北京&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;晴，20-28℃，微风&amp;#34;&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="s"&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;多云，18-25℃，东北风3级&amp;#34;&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="s"&gt;&amp;#34;广州&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;雷阵雨，22-26℃，南风2级&amp;#34;&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="s"&gt;&amp;#34;深圳&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;阴，23-27℃，东风2级&amp;#34;&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="s"&gt;&amp;#34;杭州&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;晴转多云，19-26℃，西北风2级&amp;#34;&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&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="nx"&gt;weather&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;weatherMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;city&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="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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[天气工具结果] 城市：%s → %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;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[天气工具结果] 未查询到「%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;city&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;// 数学计算工具（支持基础运算）&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="nf"&gt;calculate&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;expr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&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="kt"&gt;string&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="c1"&gt;// 超时控制（2秒）&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;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;cancel&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTimeout&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="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&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;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&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&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="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="k"&gt;select&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&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="s"&gt;&amp;#34;&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;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;计算超时：%v&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Millisecond&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&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="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="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expr&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;100*200&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[计算工具结果] %s = %d&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;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sqrt(16)&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[计算工具结果] %s = %.2f&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;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(25+18)/2&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[计算工具结果] %s = %d&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;expr&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="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;200/5&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[计算工具结果] %s = %d&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;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(18+25)/2&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[计算工具结果] %s = %.1f&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;expr&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="mi"&gt;18&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;default&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[计算工具结果] 暂不支持表达式：%s（当前支持：100*200、sqrt(16)、(a+b)/2、a/b）&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;expr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="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;// 时间查询工具（支持多时区）&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="nf"&gt;getCurrentTime&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;timezone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&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="kt"&gt;string&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="c1"&gt;// 超时控制（1秒）&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;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;cancel&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTimeout&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="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&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;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&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&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="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="k"&gt;select&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&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="s"&gt;&amp;#34;&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;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;时间查询超时：%v&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Millisecond&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&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="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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;timezone&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="s"&gt;&amp;#34;&amp;#34;&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;timezone&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 class="s"&gt;&amp;#34;Asia/Shanghai&amp;#34;&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&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="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="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timezone&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="nx"&gt;err&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="kc"&gt;nil&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="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[时间工具结果] 无效时区：%s（示例有效时区：Asia/Shanghai、UTC、America/New_York）&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;timezone&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="nx"&gt;currentTime&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;In&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;2006-01-02 15:04:05&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[时间工具结果] 时区「%s」的当前时间：%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;timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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;// ========================== 3. 核心修改：用 go-openai/jsonschema 定义工具参数 ==========================&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="c1"&gt;// getOpenAITools：通过 jsonschema.Definition 构建 OpenAI 工具定义&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="nf"&gt;getOpenAITools&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Tool&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="c1"&gt;// -------------------------- 3.1 天气工具参数（jsonschema 定义） --------------------------&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;weatherCityParam&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;城市名称（如北京、上海，仅支持国内主流城市）&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&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="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="c1"&gt;// 天气工具的整体参数 Schema（对象类型，包含 city 字段）&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;weatherParamsSchema&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&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;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;city&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;weatherCityParam&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;Required&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&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="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="w"&gt;	&lt;/span&gt;&lt;span class="c1"&gt;// -------------------------- 3.2 计算工具参数（jsonschema 定义） --------------------------&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;calcExprParam&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&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;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;数学表达式（支持乘法*、除法/、开方sqrt()、两数平均值(a+b)/2）&amp;#34;&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="nx"&gt;calcParamsSchema&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&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;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;expr&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;calcExprParam&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;Required&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;expr&amp;#34;&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&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="c1"&gt;// -------------------------- 3.3 时间工具参数（jsonschema 定义） --------------------------&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;timezoneParam&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&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;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;时区（如 Asia/Shanghai 表示上海时区，UTC 表示世界协调时间）&amp;#34;&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="nx"&gt;timeParamsSchema&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;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&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;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Definition&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;timezone&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;timezoneParam&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;Required&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{},&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="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="w"&gt;	&lt;/span&gt;&lt;span class="c1"&gt;// -------------------------- 3.4 组装 OpenAI 工具列表 --------------------------&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="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&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&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="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToolTypeFunction&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;Function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionDefinition&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;查询指定城市的实时天气（包含气温、天气状况、风力信息）&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;weatherParamsSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 绑定上述定义的参数 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="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="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;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToolTypeFunction&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;Function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionDefinition&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;calculate&amp;#34;&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;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;执行基础数学计算（支持四则运算和简单函数，不支持复杂公式）&amp;#34;&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;Parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcParamsSchema&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="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="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToolTypeFunction&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;Function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionDefinition&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;get_current_time&amp;#34;&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;Description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;查询指定时区的当前时间（默认返回上海时区时间，支持全球主流时区）&amp;#34;&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;Parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;timeParamsSchema&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="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="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;// ========================== 4. 工具调用执行器（解析+执行） ==========================&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="nf"&gt;executeToolCall&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;toolCall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&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="kt"&gt;string&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n[工具调用] 名称：%s，参数：%s\n&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;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&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 class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arguments&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&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="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="k"&gt;switch&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="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;get_weather&amp;#34;&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="c1"&gt;// 解析工具参数（绑定到 WeatherParams 结构体）&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="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WeatherParams&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="nx"&gt;err&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="kc"&gt;nil&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="s"&gt;&amp;#34;&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;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;参数解析失败：%v（请检查是否传入 city 字段）&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;err&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="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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;City&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="s"&gt;&amp;#34;&amp;#34;&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="s"&gt;&amp;#34;&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;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;城市名称不能为空（工具要求必传 city 参数）&amp;#34;&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="nf"&gt;getWeather&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;City&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&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;calculate&amp;#34;&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="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CalcParams&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="nx"&gt;err&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="kc"&gt;nil&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="s"&gt;&amp;#34;&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;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;参数解析失败：%v（请检查是否传入 expr 字段）&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;err&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;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Expr&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="s"&gt;&amp;#34;&amp;#34;&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="s"&gt;&amp;#34;&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;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;数学表达式不能为空（工具要求必传 expr 参数）&amp;#34;&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="nf"&gt;calculate&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Expr&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&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;get_current_time&amp;#34;&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="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeParams&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="nx"&gt;err&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="kc"&gt;nil&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="s"&gt;&amp;#34;&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;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;参数解析失败：%v（请检查 timezone 字段格式）&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;err&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="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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timezone&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="s"&gt;&amp;#34;&amp;#34;&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timezone&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 class="s"&gt;&amp;#34;Asia/Shanghai&amp;#34;&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="nf"&gt;getCurrentTime&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timezone&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&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;default&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="s"&gt;&amp;#34;&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;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;未知工具：%s（当前支持工具：get_weather、calculate、get_current_time）&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;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&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="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;// ========================== 5. 递归多轮工具调用逻辑（FinishReason 校验） ==========================&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;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maxRecursionDepth&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 class="mi"&gt;5&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&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="nf"&gt;recursiveAgent&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;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&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="kt"&gt;string&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="c1"&gt;// 1. 递归深度限制：超过阈值直接终止（避免模型逻辑异常导致循环）&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="nx"&gt;depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maxRecursionDepth&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;errMsg&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;递归深度超过阈值（最大 %d 轮），可能存在工具调用循环或模型逻辑异常&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;maxRecursionDepth&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[错误]&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;errMsg&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="s"&gt;&amp;#34;&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;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="nx"&gt;errMsg&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&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="c1"&gt;// 2. 调用 OpenAI 模型（传入工具定义，让模型判断是否调用工具）&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;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateChatCompletion&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionRequest&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;Model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Qwen3-Next-80B-A3B-Instruct&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 必须使用支持函数调用的模型（gpt-4o 更优）&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;Messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;Tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getOpenAITools&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 传入用 jsonschema 定义的工具列表&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;ToolChoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChunkingStrategyTypeAuto&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;Temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;MaxTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 限制单轮响应长度，避免 token 不足&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;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="kc"&gt;nil&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="s"&gt;&amp;#34;&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;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;模型请求失败：%v（请检查 API Key 和网络连接）&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;err&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&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="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="nx"&gt;choice&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;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;modelMsg&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;choice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Message&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;finishReason&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;choice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FinishReason&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="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="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n[模型响应] 深度：%d | FinishReason：%s | 角色：%s | 内容：%s\n&amp;#34;&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;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;finishReason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;modelMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;modelMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Content&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&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="c1"&gt;// 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="w"&gt;	&lt;/span&gt;&lt;span class="nx"&gt;messages&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 class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;modelMsg&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&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="c1"&gt;// 4. 根据 FinishReason 分支处理（核心逻辑：判断是否继续调用工具）&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;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;finishReason&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FinishReasonStop&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="c1"&gt;// 场景1：模型正常终止（无需工具）→ 返回最终答案&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[递归终止] FinishReason=stop，模型无需调用工具，直接返回最终答案&amp;#34;&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;modelMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&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="w"&gt;	&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FinishReasonToolCalls&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="c1"&gt;// 场景2：模型明确需要调用工具 → 执行工具并递归&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="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToolCalls&lt;/span&gt;&lt;span class="p"&gt;)&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="mi"&gt;0&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;errMsg&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="s"&gt;&amp;#34;异常：FinishReason=function_call，但模型未返回任何工具调用指令（可能是模型响应不完整）&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[错误]&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;errMsg&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="s"&gt;&amp;#34;&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;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="nx"&gt;errMsg&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&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="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="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;toolCall&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="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;modelMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToolCalls&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;toolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="nf"&gt;executeToolCall&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;toolCall&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="nx"&gt;err&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="kc"&gt;nil&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="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="nx"&gt;errorMsg&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[工具调用失败] 工具：%s | 错误：%v&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;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Function&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 class="nx"&gt;err&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorMsg&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;messages&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 class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionMessage&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;Role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatMessageRoleTool&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;Function&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="nx"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errorMsg&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;ToolCallID&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 必须关联工具调用 ID，模型才能对应上下文&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;continue&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&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="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="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[工具结果] %s\n&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;toolResult&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;messages&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 class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionMessage&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;Role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatMessageRoleTool&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;Function&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="nx"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;toolResult&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;ToolCallID&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;ID&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="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="w"&gt;		&lt;/span&gt;&lt;span class="c1"&gt;// 递归继续：传入更新后的上下文，发起下一轮模型请求（深度+1）&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n[递归继续] 第 %d 轮工具调用完成，发起下一轮模型请求\n&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;depth&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&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="nf"&gt;recursiveAgent&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;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&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&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FinishReasonLength&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="c1"&gt;// 场景3：token 不足导致响应截断 → 提示用户增大 MaxTokens&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;errMsg&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="s"&gt;&amp;#34;模型响应因 token 长度限制被截断 → 解决方案：将 MaxTokens 参数从 1024 增大（如 2048）&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[错误]&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;errMsg&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="s"&gt;&amp;#34;&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;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="nx"&gt;errMsg&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&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FinishReasonContentFilter&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="c1"&gt;// 场景4：内容被过滤 → 告知用户无法处理&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;errMsg&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="s"&gt;&amp;#34;模型响应因内容过滤政策被截断（可能包含敏感信息），无法继续处理&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[错误]&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;errMsg&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="s"&gt;&amp;#34;&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;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="nx"&gt;errMsg&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&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;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FinishReasonNull&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="c1"&gt;// 场景5：响应不完整（如流式响应未结束）→ 提示网络异常&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;errMsg&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="s"&gt;&amp;#34;模型响应不完整，可能是网络波动或 API 服务临时故障&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[错误]&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;errMsg&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="s"&gt;&amp;#34;&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;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="nx"&gt;errMsg&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&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;default&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="c1"&gt;// 场景6：未知 FinishReason → 降级终止&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;errMsg&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;未知的 FinishReason：%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;finishReason&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[错误]&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;errMsg&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="s"&gt;&amp;#34;&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;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="nx"&gt;errMsg&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="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;// ========================== 6. 完整的 main 方法 ==========================&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="nf"&gt;main&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="c1"&gt;// 1. 校验 OpenAI API Key（必须设置环境变量）&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="cp"&gt;//apiKey := os.Getenv(&amp;#34;OPENAI_API_KEY&amp;#34;)&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;config&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;xxx&amp;#34;&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BaseURL&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 class="s"&gt;&amp;#34;xxx&amp;#34;&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;client&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewClientWithConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&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;ctx&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Background&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&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;userQuery&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="s"&gt;&amp;#34;查询上海的天气，计算其最高温与最低温的平均值，再查询 UTC 时区的当前时间&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;===========================================\n&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;用户提问：%s\n&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;userQuery&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;===========================================\n&amp;#34;&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&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;initMessages&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="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionMessage&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="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatMessageRoleSystem&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;Content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`你是一个智能 AI Agent，严格按照以下规则工作：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;1. 必须根据用户问题，自主判断是否需要调用工具（天气、计算、时间）；
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;2. 若需要多步工具调用，需逐步执行，直到获取所有必要信息后再整理答案；
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;3. 工具返回结果后，用自然语言清晰、简洁地回复用户，无需额外冗余信息；
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;4. 若工具调用失败，需告知用户失败原因，无需继续重试。`&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="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;Role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatMessageRoleUser&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;Content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;userQuery&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="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="w"&gt;	&lt;/span&gt;&lt;span class="nx"&gt;finalAnswer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="nf"&gt;recursiveAgent&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;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;initMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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="nx"&gt;err&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="kc"&gt;nil&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n===========================================\n&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;执行失败：%v\n&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;err&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;===========================================\n&amp;#34;&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&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&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n===========================================\n&amp;#34;&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;最终答案：\n%s\n&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;finalAnswer&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;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;===========================================\n&amp;#34;&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="代码执行logs"&gt;&lt;a href="#%e4%bb%a3%e7%a0%81%e6%89%a7%e8%a1%8clogs" class="header-anchor"&gt;&lt;/a&gt;代码执行Logs
&lt;/h2&gt;&lt;p&gt;&lt;img alt="aiagent" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="/dkblog/assets/ai/go-ai-agent.png"&gt;&lt;/p&gt;
&lt;p&gt;通过本文的实践，你可以掌握了Go语言实现 AI Agent 的核心逻辑，更理解了多轮工具调用的底层原理。&lt;/p&gt;</description></item><item><title>Model Context Protocol 与 OpenAI 工具调用实践</title><link>/dkblog/016-mcp/</link><pubDate>Sun, 27 Jul 2025 10:16:57 +0800</pubDate><guid>/dkblog/016-mcp/</guid><description>&lt;img src="https://images.unsplash.com/photo-1639322537228-f710d846310a?auto=format&amp;fit=crop&amp;w=900&amp;q=60" alt="Featured image of post Model Context Protocol 与 OpenAI 工具调用实践" /&gt;&lt;h2 id="引言从问答机器人到行动型ai助手"&gt;&lt;a href="#%e5%bc%95%e8%a8%80%e4%bb%8e%e9%97%ae%e7%ad%94%e6%9c%ba%e5%99%a8%e4%ba%ba%e5%88%b0%e8%a1%8c%e5%8a%a8%e5%9e%8bai%e5%8a%a9%e6%89%8b" class="header-anchor"&gt;&lt;/a&gt;引言：从“问答机器人”到“行动型AI助手”
&lt;/h2&gt;&lt;p&gt;传统的聊天机器人大多停留在“理解-回答”的单向交互模式。而随着大模型能力的演进，我们不再满足于“知道答案”，更希望 AI 能“采取行动”——比如查询天气、发送邮件、执行数据库操作等。&lt;/p&gt;
&lt;p&gt;这正是 &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; 和 &lt;strong&gt;OpenAI 函数调用（Function Calling）&lt;/strong&gt; 技术的用武之地。本文将带你从零开始，用 Go 语言实现一个基于 MCP 协议与 OpenAI SDK 深度整合的 Demo，构建一个能动态发现并调用远程工具的智能对话系统。&lt;/p&gt;
&lt;p&gt;我们将深入剖析核心架构、关键设计模式，并最终实现一个可扩展、高可用的 AI 助手原型。&lt;/p&gt;
&lt;h3 id="核心目标"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e7%9b%ae%e6%a0%87" class="header-anchor"&gt;&lt;/a&gt;核心目标：
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多轮递归调用&lt;/strong&gt;：支持 LLM 决策后多次调用工具。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上下文累积&lt;/strong&gt;：确保每一轮工具调用的结果都能被 LLM 看到。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高效并发处理&lt;/strong&gt;：利用 Go 的并发特性提升响应速度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;精准流程控制&lt;/strong&gt;：正确解析 &lt;code&gt;FinishReason&lt;/code&gt;，避免冗余请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="一技术选型与核心概念"&gt;&lt;a href="#%e4%b8%80%e6%8a%80%e6%9c%af%e9%80%89%e5%9e%8b%e4%b8%8e%e6%a0%b8%e5%bf%83%e6%a6%82%e5%bf%b5" class="header-anchor"&gt;&lt;/a&gt;一、技术选型与核心概念
&lt;/h2&gt;&lt;h3 id="11-openai-function-calling函数调用"&gt;&lt;a href="#11-openai-function-calling%e5%87%bd%e6%95%b0%e8%b0%83%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;1.1 OpenAI Function Calling（函数调用）
&lt;/h3&gt;&lt;p&gt;OpenAI 的 &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; 及以上模型支持“函数调用”功能。你可以向模型描述一组函数（工具）的能力，模型会根据用户输入决定是否调用、调用哪个函数，并生成符合函数签名的参数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心优势&lt;/strong&gt;：将自然语言指令转化为结构化 API 调用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参考实现&lt;/strong&gt;：&lt;a class="link" href="https://github.com/sashabaranov/go-openai/blob/master/examples/chatbot/main.go" target="_blank" rel="noopener"
 &gt;sashabaranov/go-openai&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="12-model-context-protocol-mcp"&gt;&lt;a href="#12-model-context-protocol-mcp" class="header-anchor"&gt;&lt;/a&gt;1.2 Model Context Protocol (MCP)
&lt;/h3&gt;&lt;p&gt;MCP 是一种新兴的标准化协议，用于描述和暴露 LLM 可调用的“工具”（Tools）。它允许 LLM 客户端动态发现远程服务提供的功能，并安全地执行调用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心价值&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动态发现&lt;/strong&gt;：无需硬编码工具列表，运行时自动获取。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解耦架构&lt;/strong&gt;：LLM 核心与工具提供方完全分离，便于微服务部署。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标准化接口&lt;/strong&gt;：统一的 &lt;code&gt;ListTools&lt;/code&gt;、&lt;code&gt;CallTool&lt;/code&gt; 接口，降低集成成本。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参考实现&lt;/strong&gt;：&lt;a class="link" href="https://github.com/modelcontextprotocol/go-sdk/tree/main/examples/client" target="_blank" rel="noopener"
 &gt;modelcontextprotocol/go-sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="13-为什么选择-go"&gt;&lt;a href="#13-%e4%b8%ba%e4%bb%80%e4%b9%88%e9%80%89%e6%8b%a9-go" class="header-anchor"&gt;&lt;/a&gt;1.3 为什么选择 Go？
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高性能&lt;/strong&gt;：适合高并发的 API 网关场景。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强类型与并发支持&lt;/strong&gt;：&lt;code&gt;sync&lt;/code&gt;, &lt;code&gt;errgroup&lt;/code&gt; 等包让并发控制更安全。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;丰富的生态&lt;/strong&gt;：&lt;code&gt;go-openai&lt;/code&gt;, &lt;code&gt;gin&lt;/code&gt;, &lt;code&gt;grpc&lt;/code&gt; 等库成熟稳定。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="二系统架构设计"&gt;&lt;a href="#%e4%ba%8c%e7%b3%bb%e7%bb%9f%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;二、系统架构设计
&lt;/h2&gt;&lt;p&gt;我们的系统由三部分组成：&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" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-6"&gt;&lt;a class="lnlinks" href="#hl-0-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-7"&gt;&lt;a class="lnlinks" href="#hl-0-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-8"&gt;&lt;a class="lnlinks" href="#hl-0-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-9"&gt;&lt;a class="lnlinks" href="#hl-0-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-10"&gt;&lt;a class="lnlinks" href="#hl-0-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-11"&gt;&lt;a class="lnlinks" href="#hl-0-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-0-12"&gt;&lt;a class="lnlinks" href="#hl-0-12"&gt;12&lt;/a&gt;
&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+----------------+ +---------------------+ +------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| | | | | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| 用户请求 | --&amp;gt; | OpenAI LLM | --&amp;gt; | MCP 工具服务 |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| (DingTalk等) | | (决策与生成) | | (天气、数据库等) |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| | | | | |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+----------------+ +----------+----------+ +------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | MCP Manager |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | (工具发现与路由) |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;ol&gt;
&lt;li&gt;&lt;strong&gt;LLM 核心&lt;/strong&gt;：负责理解用户意图，决定是否调用工具。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Manager&lt;/strong&gt;：管理多个 MCP 服务连接，实现工具发现与负载均衡。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP 服务&lt;/strong&gt;：实际提供工具能力的微服务（如天气查询、数据库操作）。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="三核心代码解析"&gt;&lt;a href="#%e4%b8%89%e6%a0%b8%e5%bf%83%e4%bb%a3%e7%a0%81%e8%a7%a3%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;三、核心代码解析
&lt;/h2&gt;&lt;h3 id="31-mcpmanager工具服务的指挥官"&gt;&lt;a href="#31-mcpmanager%e5%b7%a5%e5%85%b7%e6%9c%8d%e5%8a%a1%e7%9a%84%e6%8c%87%e6%8c%a5%e5%ae%98" class="header-anchor"&gt;&lt;/a&gt;3.1 MCPManager：工具服务的“指挥官”
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;MCPManager&lt;/code&gt; 是整个系统的核心调度器，负责管理所有 MCP 服务的生命周期和工具映射。&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" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-1-4"&gt;&lt;a class="lnlinks" href="#hl-1-4"&gt;4&lt;/a&gt;
&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;MCPManager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&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;clients&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;MCPClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 所有 MCP 客户端&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;toolMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 工具-服务器映射: toolName -&amp;gt; []*MCPClient&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;h4 id="关键方法"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;关键方法：
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;NewMCPManager()&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
从配置文件加载所有 MCP 服务地址，初始化客户端列表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ConnectAll(ctx)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
使用 &lt;code&gt;errgroup&lt;/code&gt; 并发连接所有 MCP 服务，提升启动效率。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;buildToolMap(ctx)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
核心逻辑！遍历所有已连接的服务，调用 &lt;code&gt;ListTools()&lt;/code&gt; 获取其提供的工具，并构建全局映射表 &lt;code&gt;toolMap&lt;/code&gt;。&lt;br&gt;
✅ &lt;strong&gt;优势&lt;/strong&gt;：支持多服务提供同一工具（负载均衡），自动去重。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ExecuteTool(ctx, toolName, args)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
根据 &lt;code&gt;toolMap&lt;/code&gt; 找到提供该工具的服务，尝试调用。支持失败重试，增强系统鲁棒性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="32-mcpclientmcp-服务的通信桥梁"&gt;&lt;a href="#32-mcpclientmcp-%e6%9c%8d%e5%8a%a1%e7%9a%84%e9%80%9a%e4%bf%a1%e6%a1%a5%e6%a2%81" class="header-anchor"&gt;&lt;/a&gt;3.2 MCPClient：MCP 服务的“通信桥梁”
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;MCPClient&lt;/code&gt; 封装了与单个 MCP 服务的通信细节。&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" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt;6&lt;/a&gt;
&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;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;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;MCPClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Connect&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="kt"&gt;error&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;client&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;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&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&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;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connect&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StreamableClientTransport&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;},&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&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;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&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 class="nx"&gt;session&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;err&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;h4 id="关键方法-1"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e6%96%b9%e6%b3%95-1" class="header-anchor"&gt;&lt;/a&gt;关键方法：
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ListTools(ctx)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
调用 MCP 的 &lt;code&gt;ListTools&lt;/code&gt; 接口，并将返回的工具描述&lt;strong&gt;转换为 OpenAI 兼容的 &lt;code&gt;openai.Tool&lt;/code&gt; 格式&lt;/strong&gt;。这是实现协议桥接的关键一步。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ExecuteTool(ctx, toolName, args)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
使用 &lt;code&gt;CallTool&lt;/code&gt; 接口执行工具调用，并内置了&lt;strong&gt;3次重试机制&lt;/strong&gt;，避免因网络抖动导致失败。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="33-llmclientopenai-的代言人"&gt;&lt;a href="#33-llmclientopenai-%e7%9a%84%e4%bb%a3%e8%a8%80%e4%ba%ba" class="header-anchor"&gt;&lt;/a&gt;3.3 LLMClient：OpenAI 的“代言人”
&lt;/h3&gt;&lt;p&gt;封装了 &lt;code&gt;sashabaranov/go-openai&lt;/code&gt; 客户端，提供更简洁的调用接口。&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" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-5"&gt;&lt;a class="lnlinks" href="#hl-3-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-6"&gt;&lt;a class="lnlinks" href="#hl-3-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-7"&gt;&lt;a class="lnlinks" href="#hl-3-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-8"&gt;&lt;a class="lnlinks" href="#hl-3-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-9"&gt;&lt;a class="lnlinks" href="#hl-3-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-3-10"&gt;&lt;a class="lnlinks" href="#hl-3-10"&gt;10&lt;/a&gt;
&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;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;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;LLMClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;CreateCompletion&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;messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&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="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;interface&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;req&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChatCompletionRequest&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;Model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&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;Messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;messages&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;Tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tools&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;ToolChoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;auto&amp;#34;&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;Stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&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="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="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;ul&gt;
&lt;li&gt;&lt;strong&gt;线程安全初始化&lt;/strong&gt;：使用 &lt;code&gt;sync.OnceFunc&lt;/code&gt; 确保 &lt;code&gt;LLMClientO&lt;/code&gt; 全局单例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活配置&lt;/strong&gt;：支持自定义 &lt;code&gt;baseURL&lt;/code&gt;（用于私有化部署的 LLM）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="34-chatsession对话状态的管理者"&gt;&lt;a href="#34-chatsession%e5%af%b9%e8%af%9d%e7%8a%b6%e6%80%81%e7%9a%84%e7%ae%a1%e7%90%86%e8%80%85" class="header-anchor"&gt;&lt;/a&gt;3.4 ChatSession：对话状态的“管理者”
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;ChatSession&lt;/code&gt; 维护了完整的对话上下文（&lt;code&gt;messages&lt;/code&gt;），并驱动整个交互流程。&lt;/p&gt;
&lt;h4 id="核心流程-handleuserinput"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%b5%81%e7%a8%8b-handleuserinput" class="header-anchor"&gt;&lt;/a&gt;核心流程 &lt;code&gt;HandleUserInput&lt;/code&gt;：
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;添加用户消息&lt;/strong&gt; → 2. &lt;strong&gt;调用 LLM&lt;/strong&gt; → 3. &lt;strong&gt;处理响应&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;若 LLM 返回文本，则直接回复用户。&lt;/li&gt;
&lt;li&gt;若 LLM 返回工具调用，则执行 &lt;code&gt;handleToolCalls&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="buildsystempromptllm-的操作手册"&gt;&lt;a href="#buildsystempromptllm-%e7%9a%84%e6%93%8d%e4%bd%9c%e6%89%8b%e5%86%8c" class="header-anchor"&gt;&lt;/a&gt;&lt;code&gt;buildSystemPrompt()&lt;/code&gt;：LLM 的“操作手册”
&lt;/h4&gt;&lt;p&gt;这是最精妙的设计之一！我们动态生成系统提示，将所有可用工具的描述注入给 LLM：&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" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-5"&gt;&lt;a class="lnlinks" href="#hl-4-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-6"&gt;&lt;a class="lnlinks" href="#hl-4-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-7"&gt;&lt;a class="lnlinks" href="#hl-4-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-8"&gt;&lt;a class="lnlinks" href="#hl-4-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-9"&gt;&lt;a class="lnlinks" href="#hl-4-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-10"&gt;&lt;a class="lnlinks" href="#hl-4-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-11"&gt;&lt;a class="lnlinks" href="#hl-4-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-12"&gt;&lt;a class="lnlinks" href="#hl-4-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-4-13"&gt;&lt;a class="lnlinks" href="#hl-4-13"&gt;13&lt;/a&gt;
&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="nx"&gt;你是一个有帮助的助手&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="nx"&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&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 class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;getWeather&amp;#34;&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="s"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;获取天气情况&amp;#34;&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="s"&gt;&amp;#34;parameters&amp;#34;&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 class="o"&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="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="nx"&gt;重要提示&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;当你需要使用工具时&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="nx"&gt;必须只返回以下格式的JSON对象&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 class="s"&gt;&amp;#34;tool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tool-name&amp;#34;&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="s"&gt;&amp;#34;arguments&amp;#34;&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 class="o"&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="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;strong&gt;优势&lt;/strong&gt;：LLM 始终知道“我能做什么”，无需重新训练。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四深入理解-finishreasonllm-的决策信号灯"&gt;&lt;a href="#%e5%9b%9b%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3-finishreasonllm-%e7%9a%84%e5%86%b3%e7%ad%96%e4%bf%a1%e5%8f%b7%e7%81%af" class="header-anchor"&gt;&lt;/a&gt;四、深入理解 &lt;code&gt;FinishReason&lt;/code&gt;：LLM 的“决策信号灯”
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;FinishReason&lt;/code&gt; 是 LLM 返回响应时附带的状态码，它告诉客户端“我为什么停止生成”。正确解析它，是实现智能交互的前提。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;code&gt;FinishReason&lt;/code&gt;&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
					&lt;th&gt;我们的处理策略&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;stop&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;模型自然结束，通常已生成最终答案&lt;/td&gt;
					&lt;td&gt;✅ &lt;strong&gt;直接回复用户&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;因达到最大 token 限制而截断&lt;/td&gt;
					&lt;td&gt;⚠️ &lt;strong&gt;可选择扩展上下文或警告用户&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tool_calls&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;模型决定调用一个或多个工具&lt;/td&gt;
					&lt;td&gt;🔧 &lt;strong&gt;执行工具调用，并将结果返回给模型继续思考&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;function_call&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;旧版函数调用（与 &lt;code&gt;tool_calls&lt;/code&gt; 类似）&lt;/td&gt;
					&lt;td&gt;🔧 &lt;strong&gt;兼容处理，同 &lt;code&gt;tool_calls&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;content_filter&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;内容被安全策略拦截&lt;/td&gt;
					&lt;td&gt;🛡️ &lt;strong&gt;向用户返回安全提示&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;生成未完成（流式响应中常见）&lt;/td&gt;
					&lt;td&gt;🔄 &lt;strong&gt;继续接收流数据&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;

 &lt;blockquote&gt;
 &lt;p&gt;✅ &lt;strong&gt;重点&lt;/strong&gt;：&lt;code&gt;tool_calls&lt;/code&gt; 和 &lt;code&gt;function_call&lt;/code&gt; 是我们进行&lt;strong&gt;工具调用&lt;/strong&gt;的触发信号。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="五核心交互流程的增强设计"&gt;&lt;a href="#%e4%ba%94%e6%a0%b8%e5%bf%83%e4%ba%a4%e4%ba%92%e6%b5%81%e7%a8%8b%e7%9a%84%e5%a2%9e%e5%bc%ba%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;五、核心交互流程的增强设计
&lt;/h2&gt;&lt;p&gt;我们对 &lt;code&gt;ChatSession.HandleUserInput&lt;/code&gt; 方法进行了全面升级，使其支持&lt;strong&gt;递归式多轮工具调用&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="增强版交互流程图"&gt;&lt;a href="#%e5%a2%9e%e5%bc%ba%e7%89%88%e4%ba%a4%e4%ba%92%e6%b5%81%e7%a8%8b%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;增强版交互流程图
&lt;/h3&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" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-2"&gt;&lt;a class="lnlinks" href="#hl-5-2"&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-3"&gt;&lt;a class="lnlinks" href="#hl-5-3"&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-4"&gt;&lt;a class="lnlinks" href="#hl-5-4"&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-5"&gt;&lt;a class="lnlinks" href="#hl-5-5"&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-6"&gt;&lt;a class="lnlinks" href="#hl-5-6"&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-7"&gt;&lt;a class="lnlinks" href="#hl-5-7"&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-8"&gt;&lt;a class="lnlinks" href="#hl-5-8"&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-9"&gt;&lt;a class="lnlinks" href="#hl-5-9"&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-10"&gt;&lt;a class="lnlinks" href="#hl-5-10"&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-11"&gt;&lt;a class="lnlinks" href="#hl-5-11"&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-12"&gt;&lt;a class="lnlinks" href="#hl-5-12"&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-13"&gt;&lt;a class="lnlinks" href="#hl-5-13"&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-14"&gt;&lt;a class="lnlinks" href="#hl-5-14"&gt;14&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-15"&gt;&lt;a class="lnlinks" href="#hl-5-15"&gt;15&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-16"&gt;&lt;a class="lnlinks" href="#hl-5-16"&gt;16&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-17"&gt;&lt;a class="lnlinks" href="#hl-5-17"&gt;17&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-18"&gt;&lt;a class="lnlinks" href="#hl-5-18"&gt;18&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-19"&gt;&lt;a class="lnlinks" href="#hl-5-19"&gt;19&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-20"&gt;&lt;a class="lnlinks" href="#hl-5-20"&gt;20&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-21"&gt;&lt;a class="lnlinks" href="#hl-5-21"&gt;21&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-22"&gt;&lt;a class="lnlinks" href="#hl-5-22"&gt;22&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-23"&gt;&lt;a class="lnlinks" href="#hl-5-23"&gt;23&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-24"&gt;&lt;a class="lnlinks" href="#hl-5-24"&gt;24&lt;/a&gt;
&lt;/span&gt;&lt;span class="lnt" id="hl-5-25"&gt;&lt;a class="lnlinks" href="#hl-5-25"&gt;25&lt;/a&gt;
&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-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+------------------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;用户输入问题&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+--------+---------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+------------------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;调用&lt;/span&gt; &lt;span class="n"&gt;LLM&lt;/span&gt; &lt;span class="err"&gt;生成响应&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+--------+---------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+-------------------------+-------------------------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;+---------------------+&lt;/span&gt; &lt;span class="o"&gt;+---------------------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;执行所有工具调用&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;直接回复用户&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;并发执行（可选）&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;+---------------------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;累积结果到&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;+----------+----------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+---------------------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;将结果喂回&lt;/span&gt; &lt;span class="n"&gt;LLM&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;再次调用&lt;/span&gt; &lt;span class="n"&gt;HandleUserInput&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+----------+----------+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+--------&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;循环，直到&lt;/span&gt; &lt;span class="n"&gt;LLM&lt;/span&gt; &lt;span class="err"&gt;返回&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&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;hr&gt;
&lt;h2 id="六实战演示多轮工具调用"&gt;&lt;a href="#%e5%85%ad%e5%ae%9e%e6%88%98%e6%bc%94%e7%a4%ba%e5%a4%9a%e8%bd%ae%e5%b7%a5%e5%85%b7%e8%b0%83%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;六、实战演示：多轮工具调用
&lt;/h2&gt;&lt;p&gt;假设用户提问：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“请查询北京和杭州的天气，并告诉我哪个更热？”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="系统执行流程"&gt;&lt;a href="#%e7%b3%bb%e7%bb%9f%e6%89%a7%e8%a1%8c%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;系统执行流程：
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一轮 LLM 调用&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;LLM 分析问题，决定需要调用 &lt;code&gt;getWeather&lt;/code&gt; 工具两次。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;FinishReason: tool_calls&lt;/code&gt;，并包含两个 &lt;code&gt;ToolCall&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;执行工具调用&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MCPManager&lt;/code&gt; 并发调用两次 &lt;code&gt;getWeather&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;假设返回：
&lt;ul&gt;
&lt;li&gt;北京：30°C&lt;/li&gt;
&lt;li&gt;杭州：25°C&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;将结果注入上下文&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;两条 &lt;code&gt;Role: tool&lt;/code&gt; 的消息被加入 &lt;code&gt;messages&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;strong&gt;第二轮 LLM 调用&lt;/strong&gt;（递归调用 &lt;code&gt;HandleUserInput(&amp;quot;&amp;quot;, ctx)&lt;/code&gt;）：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;LLM “看到”了两个城市的天气数据。&lt;/li&gt;
&lt;li&gt;经过推理，生成最终回复：

 &lt;blockquote&gt;
 &lt;p&gt;“北京的气温是 30°C，杭州是 25°C，因此北京更热。”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;FinishReason: stop&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;&lt;strong&gt;回复用户&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;系统调用 &lt;code&gt;replyToUser()&lt;/code&gt;，将 &lt;code&gt;Content&lt;/code&gt; 中的内容直接发送给用户。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ &lt;strong&gt;成功实现多轮推理与决策，且在 &lt;code&gt;stop&lt;/code&gt; 时正确终止流程！&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七设计亮点与最佳实践"&gt;&lt;a href="#%e4%b8%83%e8%ae%be%e8%ae%a1%e4%ba%ae%e7%82%b9%e4%b8%8e%e6%9c%80%e4%bd%b3%e5%ae%9e%e8%b7%b5" class="header-anchor"&gt;&lt;/a&gt;七、设计亮点与最佳实践
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;动态工具发现&lt;/strong&gt;：无需重启即可接入新服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高可用设计&lt;/strong&gt;：&lt;code&gt;errgroup&lt;/code&gt; 并发连接、工具调用重试、服务健康检查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议桥接&lt;/strong&gt;：无缝整合 MCP 与 OpenAI 两种协议。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展架构&lt;/strong&gt;：轻松支持数据库查询、邮件发送、内部 API 调用等更多工具。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;清晰的职责分离&lt;/strong&gt;：&lt;code&gt;MCPManager&lt;/code&gt;, &lt;code&gt;ChatSession&lt;/code&gt;, &lt;code&gt;LLMClient&lt;/code&gt; 各司其职。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="结语"&gt;&lt;a href="#%e7%bb%93%e8%af%ad" class="header-anchor"&gt;&lt;/a&gt;结语
&lt;/h2&gt;&lt;p&gt;通过将 MCP 的动态工具发现能力与 OpenAI 强大的语言理解能力相结合，我们构建了一个真正“能做事”的 AI 助手。这不仅是一个 Demo，更是一种可落地的架构范式。&lt;/p&gt;
&lt;p&gt;在微服务与 AI 融合的浪潮中，掌握这种“LLM + 工具调用”的模式，将为你打开通往下一代智能应用的大门。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;代码已开源&lt;/strong&gt;：&lt;a class="link" href="http://www.baidu.com" target="_blank" rel="noopener"
 &gt;GitHub 仓库链接&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;欢迎 Star 与贡献&lt;/strong&gt;！&lt;/p&gt;</description></item></channel></rss>