<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI应用开发 | 林子杨的个人网站</title><link>https://ziyanglin.netlify.app/zh/tags/ai%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91/</link><atom:link href="https://ziyanglin.netlify.app/zh/tags/ai%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91/index.xml" rel="self" type="application/rss+xml"/><description>AI应用开发</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh-Hans</language><lastBuildDate>Mon, 30 Jun 2025 07:00:00 +0000</lastBuildDate><image><url>https://ziyanglin.netlify.app/img/icon-192.png</url><title>AI应用开发</title><link>https://ziyanglin.netlify.app/zh/tags/ai%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91/</link></image><item><title>LLM工具调用：打破AI能力边界的关键技术</title><link>https://ziyanglin.netlify.app/zh/post/llm-tool-calling/</link><pubDate>Mon, 30 Jun 2025 07:00:00 +0000</pubDate><guid>https://ziyanglin.netlify.app/zh/post/llm-tool-calling/</guid><description>&lt;h2 id="1-llm">1. 宏观概述：为什么工具调用是LLM的&amp;quot;超级外挂&amp;rdquo;？&lt;/h2>
&lt;p>大型语言模型（LLM）的出现，彻底改变了我们与机器交互的方式。然而，LLM本身存在一个固有的、无法回避的&amp;quot;天花板&amp;rdquo;：它们本质上是基于海量文本数据训练出来的&amp;quot;概率预测机器&amp;rdquo;，其知识被冻结在训练数据截止的那一刻。这意味着，LLM无法得知&amp;quot;今天的天气怎么样？&amp;quot;，也无法访问你公司的内部数据库，更不能帮你预订一张机票。&lt;/p>
&lt;p>&lt;strong>LLM工具调用（Tool Calling / Function Calling）&lt;/strong> 机制的出现，正是为了打破这层天花板。它赋予了LLM一个前所未有的能力：在需要的时候，&lt;strong>调用外部工具（API、函数、数据库等）来获取实时信息、执行特定任务，或与外部世界进行交互&lt;/strong>。&lt;/p>
&lt;p>简而言之，工具调用机制将LLM从一个&amp;quot;博学的对话者&amp;quot;升级为了一个能知能行的&amp;quot;智能代理&amp;rdquo;（Intelligent Agent）。它允许LLM：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>获取实时信息&lt;/strong>：通过调用天气API、新闻API、搜索引擎等，获取模型训练数据之外的最新信息。&lt;/li>
&lt;li>&lt;strong>操作外部系统&lt;/strong>：连接到企业内部的CRM、ERP系统，查询数据；或者连接到IoT设备，控制智能家居。&lt;/li>
&lt;li>&lt;strong>执行复杂任务&lt;/strong>：将用户的复杂指令（如&amp;quot;帮我找找下周去上海的便宜机票并预订&amp;rdquo;）拆解，并通过调用多个API组合来完成。&lt;/li>
&lt;li>&lt;strong>提供更精确、可验证的答案&lt;/strong>：对于需要精确计算或结构化数据的查询，LLM可以调用计算器或数据库，而不是依赖其可能不准确的内部知识。&lt;/li>
&lt;/ul>
&lt;p>因此，工具调用不仅是LLM功能的一个简单扩展，更是通往构建真正强大的、能够与物理和数字世界深度融合的AI应用的核心基石。&lt;/p>
&lt;h2 id="2-llm">2. 核心理念与工作流程：LLM如何&amp;quot;学会&amp;quot;使用工具？&lt;/h2>
&lt;p>要理解工具调用的底层逻辑，我们需要将其看作是一个由三个核心角色协同工作的精妙流程：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>大型语言模型 (LLM)&lt;/strong>：大脑和决策者。&lt;/li>
&lt;li>&lt;strong>工具定义 (Tool Definitions)&lt;/strong>：一本详细的&amp;quot;工具使用说明书&amp;rdquo;。&lt;/li>
&lt;li>&lt;strong>开发者/客户端 (Client-side Code)&lt;/strong>：最终的&amp;quot;执行者&amp;rdquo;。&lt;/li>
&lt;/ol>
&lt;p>LLM本身&lt;strong>永远不会真正地执行任何代码&lt;/strong>。它的唯一任务是，在理解了用户的意图和它所拥有的&amp;quot;工具说明书&amp;quot;后，&lt;strong>生成一段精确描述了应该调用哪个工具、以及使用什么参数的JSON数据&lt;/strong>。&lt;/p>
&lt;p>下面是这个流程的可视化解释：&lt;/p>
&lt;pre>&lt;code class="language-mermaid">sequenceDiagram
participant User as 用户
participant Client as 客户端/应用层
participant LLM as 大型语言模型
participant Tools as 外部工具/API
User-&amp;gt;&amp;gt;+Client: &amp;quot;帮我查一下北京今天的天气&amp;quot;
Client-&amp;gt;&amp;gt;+LLM: 提交用户请求 + 工具定义 (Tool Definitions)
Note over LLM: 1. 理解用户意图&amp;lt;br/&amp;gt;2. 匹配最合适的工具 (get_weather)&amp;lt;br/&amp;gt;3. 提取所需参数 (location: &amp;quot;北京&amp;quot;)
LLM--&amp;gt;&amp;gt;-Client: 返回JSON：{&amp;quot;tool_calls&amp;quot;: [{&amp;quot;function&amp;quot;: {&amp;quot;name&amp;quot;: &amp;quot;get_weather&amp;quot;, &amp;quot;arguments&amp;quot;: &amp;quot;{\&amp;quot;location\&amp;quot;: \&amp;quot;北京\&amp;quot;}&amp;quot;}}]}
Client-&amp;gt;&amp;gt;+Tools: 2. 根据LLM返回的JSON，调用真正的get_weather(&amp;quot;北京&amp;quot;)函数
Tools--&amp;gt;&amp;gt;-Client: 返回天气数据 (例如: {&amp;quot;temperature&amp;quot;: &amp;quot;25°C&amp;quot;, &amp;quot;condition&amp;quot;: &amp;quot;晴&amp;quot;})
Client-&amp;gt;&amp;gt;+LLM: 3. 将工具执行结果提交回LLM
Note over LLM: 4. 理解工具返回的数据
LLM--&amp;gt;&amp;gt;-Client: 5. 生成对用户友好的自然语言回答
Client-&amp;gt;&amp;gt;-User: &amp;quot;北京今天天气晴，温度是25摄氏度。&amp;quot;
&lt;/code>&lt;/pre>
&lt;h3 id="heading">流程详解：&lt;/h3>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>定义与描述 (Define &amp;amp; Describe)&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>开发者首先需要用一种结构化的方式（通常是JSON Schema）来定义好可用的工具。这份&amp;quot;说明书&amp;quot;是整个流程的关键，它必须清晰地告诉LLM：
&lt;ul>
&lt;li>&lt;strong>工具名称&lt;/strong> (&lt;code>name&lt;/code>)：例如 &lt;code>get_weather&lt;/code>。&lt;/li>
&lt;li>&lt;strong>工具功能描述&lt;/strong> (&lt;code>description&lt;/code>)：例如&amp;quot;获取指定城市的实时天气信息&amp;rdquo;。这是LLM理解工具用途的最重要依据。&lt;/li>
&lt;li>&lt;strong>工具参数&lt;/strong> (&lt;code>parameters&lt;/code>)：详细定义工具需要哪些输入，每个输入的名称、类型（字符串、数字、布尔等）、是否必需，以及对参数的描述。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>意图识别与参数提取 (Intent Recognition &amp;amp; Parameter Extraction)&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>当用户发出请求时（例如&amp;quot;查查北京天气&amp;rdquo;），开发者的应用会将用户的原始请求&lt;strong>连同第一步中定义的所有工具说明书&lt;/strong>一起发送给LLM。&lt;/li>
&lt;li>LLM的核心任务就是进行两件事：
&lt;ul>
&lt;li>&lt;strong>意图识别&lt;/strong>：在所有可用的工具中，判断用户的请求最符合哪个工具的功能描述。在这个例子中，它会匹配到&lt;code>get_weather&lt;/code>。&lt;/li>
&lt;li>&lt;strong>参数提取&lt;/strong>：从用户的请求中，找出并提取满足工具参数要求的值。在这里，它会识别出&lt;code>location&lt;/code>参数的值是&amp;quot;北京&amp;rdquo;。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>完成这两步后，LLM会生成一个或多个&lt;code>tool_calls&lt;/code>对象，其内容本质上是&amp;quot;我建议你调用名为&lt;code>get_weather&lt;/code>的函数，并传入&lt;code>{ &amp;quot;location&amp;quot;: &amp;quot;北京&amp;quot; }&lt;/code>这个参数&amp;rdquo;。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>执行与观察 (Execute &amp;amp; Observe)&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>开发者的应用层代码接收到LLM返回的JSON后，会解析这个&amp;quot;调用建议&amp;rdquo;。&lt;/li>
&lt;li>应用层代码&lt;strong>在本地或服务器端，实际地执行&lt;/strong>&lt;code>get_weather(&amp;quot;北京&amp;quot;)&lt;/code>这个函数。&lt;/li>
&lt;li>执行后，会得到一个真实的返回结果，例如一个包含天气信息的JSON对象。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>总结与回应 (Summarize &amp;amp; Respond)&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>为了完成闭环，应用层需要将上一步中工具的真实执行结果，再次提交给LLM。&lt;/li>
&lt;li>这一次，LLM的任务是理解这个工具返回的原始数据（例如&lt;code>{&amp;quot;temperature&amp;quot;: &amp;quot;25°C&amp;quot;, &amp;quot;condition&amp;quot;: &amp;quot;晴&amp;quot;}&lt;/code>），并将其转换成一句通顺、自然的、对用户友好的答复。&lt;/li>
&lt;li>最终，用户收到了&amp;quot;北京今天天气晴，温度是25摄氏度&amp;quot;的回复，整个流程结束。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>这个流程精妙地结合了LLM强大的自然语言理解能力和外部工具强大的功能执行能力，实现了1+1&amp;gt;2的效果。&lt;/p>
&lt;h2 id="3--openai-tool-calling">3. 技术深潜：剖析行业标准 (OpenAI Tool Calling)&lt;/h2>
&lt;p>OpenAI的API是目前LLM工具调用领域的事实标准，其设计被广泛借鉴。理解其实现细节对于任何希望在应用中集成LLM工具调用的开发者都至关重要。&lt;/p>
&lt;h3 id="31-api">3.1. 核心API参数&lt;/h3>
&lt;p>在调用OpenAI的Chat Completions API时，与工具调用相关的核心参数主要有两个：&lt;code>tools&lt;/code> 和 &lt;code>tool_choice&lt;/code>。&lt;/p>
&lt;h4 id="tools-">&lt;code>tools&lt;/code> 参数：你的&amp;quot;工具箱&amp;rdquo;&lt;/h4>
&lt;p>&lt;code>tools&lt;/code>参数是一个数组，你可以在其中定义一个或多个工具。每个工具都遵循一个固定的结构，其核心是&lt;code>function&lt;/code>对象，该对象基于&lt;strong>JSON Schema&lt;/strong>规范来定义。&lt;/p>
&lt;p>&lt;strong>示例：定义一个获取天气和一个预订机票的工具&lt;/strong>&lt;/p>
&lt;pre>&lt;code class="language-json">[
{
&amp;quot;type&amp;quot;: &amp;quot;function&amp;quot;,
&amp;quot;function&amp;quot;: {
&amp;quot;name&amp;quot;: &amp;quot;get_current_weather&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;获取指定地点的实时天气信息&amp;quot;,
&amp;quot;parameters&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;object&amp;quot;,
&amp;quot;properties&amp;quot;: {
&amp;quot;location&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;城市和省份名称，例如：'北京市'&amp;quot;
},
&amp;quot;unit&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,
&amp;quot;enum&amp;quot;: [&amp;quot;celsius&amp;quot;, &amp;quot;fahrenheit&amp;quot;],
&amp;quot;description&amp;quot;: &amp;quot;温度单位&amp;quot;
}
},
&amp;quot;required&amp;quot;: [&amp;quot;location&amp;quot;]
}
}
},
{
&amp;quot;type&amp;quot;: &amp;quot;function&amp;quot;,
&amp;quot;function&amp;quot;: {
&amp;quot;name&amp;quot;: &amp;quot;book_flight&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;为用户预订从出发地到目的地的机票&amp;quot;,
&amp;quot;parameters&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;object&amp;quot;,
&amp;quot;properties&amp;quot;: {
&amp;quot;departure&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;出发机场或城市&amp;quot;
},
&amp;quot;destination&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;目的机场或城市&amp;quot;
},
&amp;quot;date&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;希望出发的日期，格式为 YYYY-MM-DD&amp;quot;
}
},
&amp;quot;required&amp;quot;: [&amp;quot;departure&amp;quot;, &amp;quot;destination&amp;quot;, &amp;quot;date&amp;quot;]
}
}
}
]
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>关键点剖析&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>type&lt;/code>&lt;/strong>: 目前固定为&lt;code>&amp;quot;function&amp;quot;&lt;/code>。&lt;/li>
&lt;li>&lt;strong>&lt;code>function.name&lt;/code>&lt;/strong>: 函数名。必须是字母、数字和下划线的组合，长度不超过64。这是你的代码用来识别调用哪个函数的关键。&lt;/li>
&lt;li>&lt;strong>&lt;code>function.description&lt;/code>&lt;/strong>: &lt;strong>至关重要&lt;/strong>。这是LLM决定是否选择该工具的主要依据。描述应该清晰、准确、无歧义地说明该函数能做什么。好的描述能极大提升LLM的调用准确率。&lt;/li>
&lt;li>&lt;strong>&lt;code>function.parameters&lt;/code>&lt;/strong>: 一个标准的JSON Schema对象。
&lt;ul>
&lt;li>&lt;strong>&lt;code>type&lt;/code>&lt;/strong>: 必须是&lt;code>&amp;quot;object&amp;quot;&lt;/code>。&lt;/li>
&lt;li>&lt;strong>&lt;code>properties&lt;/code>&lt;/strong>: 定义每个参数的名称、类型 (&lt;code>string&lt;/code>, &lt;code>number&lt;/code>, &lt;code>boolean&lt;/code>, &lt;code>array&lt;/code>, &lt;code>object&lt;/code>) 和描述。参数的描述同样重要，它能帮助LLM理解应该从用户输入中提取什么信息来填充这个参数。&lt;/li>
&lt;li>&lt;strong>&lt;code>required&lt;/code>&lt;/strong>: 一个字符串数组，列出哪些参数是必须的。如果用户请求中缺少必要信息，LLM可能会追问用户，或者选择不调用该工具。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="toolchoice-llm">&lt;code>tool_choice&lt;/code> 参数：控制LLM的选择&lt;/h4>
&lt;p>默认情况下，LLM会根据用户的输入自主决定是回答文本，还是调用一个或多个工具。&lt;code>tool_choice&lt;/code>参数允许你更精确地控制这个行为。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>&amp;quot;none&amp;quot;&lt;/code>&lt;/strong>: 强制LLM不调用任何工具，直接返回文本回复。&lt;/li>
&lt;li>&lt;strong>&lt;code>&amp;quot;auto&amp;quot;&lt;/code>&lt;/strong> (默认值): LLM可以自由选择是回复文本还是调用工具。&lt;/li>
&lt;li>&lt;strong>&lt;code>{&amp;quot;type&amp;quot;: &amp;quot;function&amp;quot;, &amp;quot;function&amp;quot;: {&amp;quot;name&amp;quot;: &amp;quot;my_function&amp;quot;}}&lt;/code>&lt;/strong>: 强制LLM必须调用名为&lt;code>my_function&lt;/code>的这个特定工具。&lt;/li>
&lt;/ul>
&lt;p>这个参数在需要固定执行某个流程或限制LLM能力的场景下非常有用。&lt;/p>
&lt;h3 id="32-">3.2. 请求-响应生命周期&lt;/h3>
&lt;p>一次完整的工具调用交互包含至少两次API请求。&lt;/p>
&lt;p>&lt;strong>第一次请求：从用户到LLM&lt;/strong>&lt;/p>
&lt;pre>&lt;code class="language-python"># request
response = client.chat.completions.create(
model=&amp;quot;gpt-4o&amp;quot;,
messages=[{&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: &amp;quot;明天从北京到上海的机票帮我订一张&amp;quot;}],
tools=my_tools, # 上面定义的工具列表
tool_choice=&amp;quot;auto&amp;quot;
)
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>第一次响应：LLM的&amp;quot;调用建议&amp;rdquo;&lt;/strong>&lt;/p>
&lt;p>如果LLM决定调用工具，API的响应中&lt;code>finish_reason&lt;/code>会是&lt;code>tool_calls&lt;/code>，并且&lt;code>message&lt;/code>对象会包含一个&lt;code>tool_calls&lt;/code>数组。&lt;/p>
&lt;pre>&lt;code class="language-json">{
&amp;quot;choices&amp;quot;: [
{
&amp;quot;finish_reason&amp;quot;: &amp;quot;tool_calls&amp;quot;,
&amp;quot;message&amp;quot;: {
&amp;quot;role&amp;quot;: &amp;quot;assistant&amp;quot;,
&amp;quot;content&amp;quot;: null,
&amp;quot;tool_calls&amp;quot;: [
{
&amp;quot;id&amp;quot;: &amp;quot;call_abc123&amp;quot;,
&amp;quot;type&amp;quot;: &amp;quot;function&amp;quot;,
&amp;quot;function&amp;quot;: {
&amp;quot;name&amp;quot;: &amp;quot;book_flight&amp;quot;,
&amp;quot;arguments&amp;quot;: &amp;quot;{\&amp;quot;departure\&amp;quot;:\&amp;quot;北京\&amp;quot;,\&amp;quot;destination\&amp;quot;:\&amp;quot;上海\&amp;quot;,\&amp;quot;date\&amp;quot;:\&amp;quot;2025-07-01\&amp;quot;}&amp;quot;
}
}
]
}
}
],
...
}
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>关键点剖析&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>finish_reason&lt;/code>&lt;/strong>: 值为&lt;code>&amp;quot;tool_calls&amp;quot;&lt;/code>标志着LLM希望你执行工具调用，而不是对话结束。&lt;/li>
&lt;li>&lt;strong>&lt;code>message.role&lt;/code>&lt;/strong>: &lt;code>assistant&lt;/code>。&lt;/li>
&lt;li>&lt;strong>&lt;code>message.tool_calls&lt;/code>&lt;/strong>: 这是一个数组，意味着LLM可以要求一次性调用多个工具。
&lt;ul>
&lt;li>&lt;strong>&lt;code>id&lt;/code>&lt;/strong>: 一个唯一的调用ID。在后续请求中，你需要用这个ID来关联工具的执行结果。&lt;/li>
&lt;li>&lt;strong>&lt;code>function.name&lt;/code>&lt;/strong>: LLM建议调用的函数名。&lt;/li>
&lt;li>&lt;strong>&lt;code>function.arguments&lt;/code>&lt;/strong>: &lt;strong>一个字符串形式的JSON对象&lt;/strong>。你需要解析这个字符串来获取调用函数所需的具体参数。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>第二次请求：将工具结果返回给LLM&lt;/strong>&lt;/p>
&lt;p>在你的代码中执行完工具后，你需要将结果再次发送给LLM以完成对话。这时，你需要构造一个新的&lt;code>messages&lt;/code>列表，其中包含：&lt;/p>
&lt;ol>
&lt;li>原始的用户消息。&lt;/li>
&lt;li>上一步中LLM返回的&lt;code>assistant&lt;/code>消息（包含&lt;code>tool_calls&lt;/code>）。&lt;/li>
&lt;li>一个新的&lt;code>tool&lt;/code>角色的消息，其中包含工具的执行结果。&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-python"># message history
messages = [
{&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: &amp;quot;明天从北京到上海的机票帮我订一张&amp;quot;},
response.choices[0].message, # Assistant's 'tool_calls' message
{
&amp;quot;tool_call_id&amp;quot;: &amp;quot;call_abc123&amp;quot;, # 必须和上一步的ID匹配
&amp;quot;role&amp;quot;: &amp;quot;tool&amp;quot;,
&amp;quot;name&amp;quot;: &amp;quot;book_flight&amp;quot;,
&amp;quot;content&amp;quot;: &amp;quot;{\&amp;quot;status\&amp;quot;: \&amp;quot;success\&amp;quot;, \&amp;quot;ticket_id\&amp;quot;: \&amp;quot;TICKET-45678\&amp;quot;}&amp;quot; # 工具的真实返回值
}
]
# second request
second_response = client.chat.completions.create(
model=&amp;quot;gpt-4o&amp;quot;,
messages=messages
)
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>第二次响应：LLM的最终回复&lt;/strong>&lt;/p>
&lt;p>这次，LLM会基于工具返回的结果，生成一段自然的语言回复给用户。&lt;/p>
&lt;pre>&lt;code class="language-json">{
&amp;quot;choices&amp;quot;: [
{
&amp;quot;finish_reason&amp;quot;: &amp;quot;stop&amp;quot;,
&amp;quot;message&amp;quot;: {
&amp;quot;role&amp;quot;: &amp;quot;assistant&amp;quot;,
&amp;quot;content&amp;quot;: &amp;quot;好的，已经为您预订了明天从北京到上海的机票，订单号是 TICKET-45678。&amp;quot;
}
}
],
...
}
&lt;/code>&lt;/pre>
&lt;p>至此，一个完整的工具调用周期完成。&lt;/p>
&lt;h2 id="4-python">4. 代码实现：一个完整的Python示例&lt;/h2>
&lt;p>下面是一个端到端的Python示例，使用OpenAI的Python库来演示如何实现一个查询天气的功能。&lt;/p>
&lt;pre>&lt;code class="language-python">import os
import json
from openai import OpenAI
from dotenv import load_dotenv
# --- 1. 初始化设置 ---
load_dotenv() # 加载 .env 文件中的环境变量
client = OpenAI(api_key=os.getenv(&amp;quot;OPENAI_API_KEY&amp;quot;))
# --- 2. 定义我们本地的工具函数 ---
# 这是一个模拟函数，实际应用中它会调用真正的天气API
def get_current_weather(location, unit=&amp;quot;celsius&amp;quot;):
&amp;quot;&amp;quot;&amp;quot;获取指定地点的实时天气信息&amp;quot;&amp;quot;&amp;quot;
if &amp;quot;北京&amp;quot; in location:
return json.dumps({
&amp;quot;location&amp;quot;: &amp;quot;北京&amp;quot;,
&amp;quot;temperature&amp;quot;: &amp;quot;10&amp;quot;,
&amp;quot;unit&amp;quot;: unit,
&amp;quot;forecast&amp;quot;: [&amp;quot;晴&amp;quot;, &amp;quot;微风&amp;quot;]
})
elif &amp;quot;上海&amp;quot; in location:
return json.dumps({
&amp;quot;location&amp;quot;: &amp;quot;上海&amp;quot;,
&amp;quot;temperature&amp;quot;: &amp;quot;15&amp;quot;,
&amp;quot;unit&amp;quot;: unit,
&amp;quot;forecast&amp;quot;: [&amp;quot;小雨&amp;quot;, &amp;quot;东北风&amp;quot;]
})
else:
return json.dumps({&amp;quot;location&amp;quot;: location, &amp;quot;temperature&amp;quot;: &amp;quot;未知&amp;quot;})
# --- 3. 主执行流程 ---
def run_conversation(user_prompt: str):
print(f&amp;quot;👤 用户: {user_prompt}&amp;quot;)
# 步骤1: 将用户的消息和工具定义发送给LLM
messages = [{&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: user_prompt}]
tools = [
{
&amp;quot;type&amp;quot;: &amp;quot;function&amp;quot;,
&amp;quot;function&amp;quot;: {
&amp;quot;name&amp;quot;: &amp;quot;get_current_weather&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;获取指定城市的实时天气信息&amp;quot;,
&amp;quot;parameters&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;object&amp;quot;,
&amp;quot;properties&amp;quot;: {
&amp;quot;location&amp;quot;: {
&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,
&amp;quot;description&amp;quot;: &amp;quot;城市名称, e.g., 北京市&amp;quot;,
},
&amp;quot;unit&amp;quot;: {&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;, &amp;quot;enum&amp;quot;: [&amp;quot;celsius&amp;quot;, &amp;quot;fahrenheit&amp;quot;]},
},
&amp;quot;required&amp;quot;: [&amp;quot;location&amp;quot;],
},
},
}
]
response = client.chat.completions.create(
model=&amp;quot;gpt-4o&amp;quot;,
messages=messages,
tools=tools,
tool_choice=&amp;quot;auto&amp;quot;,
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
# 步骤2: 检查LLM是否决定调用工具
if tool_calls:
print(f&amp;quot;🤖 LLM决定调用工具: {tool_calls[0].function.name}&amp;quot;)
# 将LLM的回复添加到消息历史中
messages.append(response_message)
# 步骤3: 执行工具调用
# 注意: 目前示例仅处理第一个工具调用
tool_call = tool_calls[0]
function_name = tool_call.function.name
function_to_call = globals().get(function_name) # 从全局作用域中获取函数
if not function_to_call:
print(f&amp;quot;❌ 错误: 函数 {function_name} 未定义&amp;quot;)
return
function_args = json.loads(tool_call.function.arguments)
# 调用函数并获取结果
function_response = function_to_call(
location=function_args.get(&amp;quot;location&amp;quot;),
unit=function_args.get(&amp;quot;unit&amp;quot;),
)
print(f&amp;quot;🛠️ 工具 '{function_name}' 返回: {function_response}&amp;quot;)
# 步骤4: 将工具的执行结果返回给LLM
messages.append(
{
&amp;quot;tool_call_id&amp;quot;: tool_call.id,
&amp;quot;role&amp;quot;: &amp;quot;tool&amp;quot;,
&amp;quot;name&amp;quot;: function_name,
&amp;quot;content&amp;quot;: function_response,
}
)
print(&amp;quot;🗣️ 将工具结果提交回LLM，生成最终回复...&amp;quot;)
second_response = client.chat.completions.create(
model=&amp;quot;gpt-4o&amp;quot;,
messages=messages,
)
final_response = second_response.choices[0].message.content
print(f&amp;quot;🤖 LLM最终回复: {final_response}&amp;quot;)
return final_response
else:
# 如果LLM没有调用工具，直接返回其文本内容
final_response = response_message.content
print(f&amp;quot;🤖 LLM直接回复: {final_response}&amp;quot;)
return final_response
# --- 运行示例 ---
if __name__ == &amp;quot;__main__&amp;quot;:
run_conversation(&amp;quot;上海今天天气怎么样？&amp;quot;)
print(&amp;quot;\n&amp;quot; + &amp;quot;=&amp;quot;*50 + &amp;quot;\n&amp;quot;)
run_conversation(&amp;quot;你好吗？&amp;quot;)
&lt;/code>&lt;/pre>
&lt;p>这个示例清晰地展示了从定义工具、发送请求、处理&lt;code>tool_calls&lt;/code>、执行本地函数、再到将结果发回给模型以获得最终答案的全过程。&lt;/p>
&lt;h2 id="5-">5. 高级主题与最佳实践&lt;/h2>
&lt;p>掌握了基础流程后，我们还需要了解一些高级用法和设计原则，以构建更健壮、更可靠的工具调用系统。&lt;/p>
&lt;h3 id="51--parallel-tool-calling">5.1. 并行工具调用 (Parallel Tool Calling)&lt;/h3>
&lt;p>较新的模型（如&lt;code>gpt-4o&lt;/code>）支持并行工具调用。这意味着模型可以在一次响应中，要求同时调用多个不同的、独立的工具。&lt;/p>
&lt;p>&lt;strong>场景示例&lt;/strong>: 用户问：&amp;ldquo;北京和上海今天的天气怎么样？&amp;rdquo;&lt;/p>
&lt;p>模型可能会返回一个包含两个&lt;code>tool_calls&lt;/code>的响应：&lt;/p>
&lt;ol>
&lt;li>&lt;code>get_current_weather(location=&amp;quot;北京&amp;quot;)&lt;/code>&lt;/li>
&lt;li>&lt;code>get_current_weather(location=&amp;quot;上海&amp;quot;)&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>你的代码需要能够迭代处理&lt;code>message.tool_calls&lt;/code>数组中的每一个&lt;code>tool_call&lt;/code>对象，分别执行它们，收集所有结果，然后将这些结果在一个新的请求中一并提交给模型。&lt;/p>
&lt;p>&lt;strong>代码处理逻辑&lt;/strong>：&lt;/p>
&lt;pre>&lt;code class="language-python"># ... (接收到包含多个tool_calls的response_message)
messages.append(response_message) # Add assistant's reply to messages
# 为每个工具调用执行函数并收集结果
tool_outputs = []
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
output = function_to_call(**function_args)
tool_outputs.append({
&amp;quot;tool_call_id&amp;quot;: tool_call.id,
&amp;quot;role&amp;quot;: &amp;quot;tool&amp;quot;,
&amp;quot;name&amp;quot;: function_name,
&amp;quot;content&amp;quot;: output,
})
# 将所有工具的输出都添加到消息历史中
messages.extend(tool_outputs)
# 再次调用模型
second_response = client.chat.completions.create(
model=&amp;quot;gpt-4o&amp;quot;,
messages=messages
)
&lt;/code>&lt;/pre>
&lt;h3 id="52-">5.2. 错误处理&lt;/h3>
&lt;p>工具调用并不总是成功的。API可能会超时，数据库可能无法连接，或者函数执行本身可能抛出异常。优雅地处理这些错误至关重要。&lt;/p>
&lt;p>当工具执行失败时，你应该捕获异常，并将一个描述错误的、结构化的信息作为工具调用的结果返回给LLM。&lt;/p>
&lt;p>&lt;strong>示例&lt;/strong>：&lt;/p>
&lt;pre>&lt;code class="language-python">try:
# 尝试调用API
result = some_flaky_api()
content = json.dumps({&amp;quot;status&amp;quot;: &amp;quot;success&amp;quot;, &amp;quot;data&amp;quot;: result})
except Exception as e:
# 如果失败，返回错误信息
content = json.dumps({&amp;quot;status&amp;quot;: &amp;quot;error&amp;quot;, &amp;quot;message&amp;quot;: f&amp;quot;API调用失败: {str(e)}&amp;quot;})
# 将结果（无论成功或失败）返回给LLM
messages.append({
&amp;quot;tool_call_id&amp;quot;: tool_call.id,
&amp;quot;role&amp;quot;: &amp;quot;tool&amp;quot;,
&amp;quot;name&amp;quot;: function_name,
&amp;quot;content&amp;quot;: content,
})
&lt;/code>&lt;/pre>
&lt;p>LLM在接收到错误信息后，通常会向用户回复一个歉意的、能反映出问题的答案（例如：&amp;ldquo;抱歉，我暂时无法查询到天气信息，请稍后再试。&amp;quot;），而不是让整个应用崩溃。&lt;/p>
&lt;h3 id="53-">5.3. 设计高效的工具描述&lt;/h3>
&lt;p>&lt;strong>工具描述 (&lt;code>description&lt;/code>) 的质量直接决定了LLM的调用准确率。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>清晰具体&lt;/strong>: 避免使用模糊的词汇。
&lt;ul>
&lt;li>&lt;strong>不好&lt;/strong>: &amp;ldquo;获取数据&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>好&lt;/strong>: &amp;ldquo;从公司的CRM系统中，根据用户ID查询该用户的订单历史记录&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>包含关键信息和限制&lt;/strong>: 如果工具有特定限制，一定要在描述中说明。
&lt;ul>
&lt;li>&lt;strong>示例&lt;/strong>: &amp;ldquo;查询航班信息。注意：本工具只能查询未来30天内的航班，无法查询历史航班。&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>使用动词开头&lt;/strong>: 用一个清晰的动词来描述函数的核心功能。&lt;/li>
&lt;li>&lt;strong>参数描述也要清晰&lt;/strong>: 参数的&lt;code>description&lt;/code>同样重要，它指导LLM如何从用户对话中正确提取信息。
&lt;ul>
&lt;li>&lt;strong>不好&lt;/strong>: &lt;code>&amp;quot;date&amp;quot;: &amp;quot;一个日期&amp;quot;&lt;/code>&lt;/li>
&lt;li>&lt;strong>好&lt;/strong>: &lt;code>&amp;quot;date&amp;quot;: &amp;quot;预订的日期，必须是YYYY-MM-DD格式的字符串&amp;quot;&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="54-">5.4. 安全性考量&lt;/h3>
&lt;p>赋予LLM调用代码的能力是一把双刃剑，必须谨慎处理其安全性。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>永远不要执行LLM生成的代码&lt;/strong>: LLM的输出是&amp;quot;调用建议&amp;rdquo;，而不是可执行代码。永远不要使用&lt;code>eval()&lt;/code>或类似的方法直接执行LLM生成的字符串。你应该解析它建议的函数名和参数，然后调用你已经预先定义好的、安全可信的本地函数。&lt;/li>
&lt;li>&lt;strong>确认与授权&lt;/strong>: 对于会产生严重后果的操作（如删除数据、发送邮件、进行支付），应该在执行前实现一个确认机制。可以是在代码层面强制要求用户确认，或者让LLM在生成调用建议后，再生成一句向用户确认的话术。&lt;/li>
&lt;li>&lt;strong>最小权限原则&lt;/strong>: 只向LLM提供完成其任务所必需的最少工具。不要暴露整个代码库或不相关的API。&lt;/li>
&lt;/ul>
&lt;h2 id="6-">6. 总结与未来展望&lt;/h2>
&lt;p>LLM工具调用是近年来人工智能领域最具突破性的进展之一。它将LLM从一个封闭的&amp;quot;语言大脑&amp;quot;转变为一个开放的、可扩展的、能够与世界交互的&amp;quot;智能代理&amp;quot;核心。通过将LLM强大的自然语言理解能力与外部工具的无限功能相结合，我们得以构建出前所未有的智能应用。&lt;/p>
&lt;p>从查询天气、预订酒店，到控制智能家居、分析企业财报、自动化软件开发流程，工具调用正在解锁无数的可能性。随着模型能力的不断增强，工具描述的理解会愈发精准，多工具的协同会更加复杂和智能，错误处理和自我修正的能力也会变得更强。&lt;/p>
&lt;p>未来，我们可能会看到更加复杂的Agentic架构，其中LLM不仅调用工具，还能动态地创建、组合甚至优化工具。掌握LLM工具调用的原理与实践，不仅是跟上当前AI技术浪潮的必备技能，更是通往未来智能应用开发的关键钥匙。&lt;/p></description></item></channel></rss>