"工具是智能的延伸,Agent 是工具的灵魂。"
大语言模型的知识受限于训练数据,无法获取实时信息,也难以执行精确计算或与外部系统交互。工具调用(Function Calling / Tool Use)技术让模型能够突破这些限制,而 Agent 架构则将这种能力提升到新的高度------让 AI 系统能够自主规划、决策并执行复杂任务。
本章将系统性地讲解工具调用的原理与实践,从基础函数调用到复杂的 Agent 协作架构,帮助您构建能够与现实世界交互的智能系统。
12.1 大模型工具调用的基本原理
12.1.1 核心概念与价值
工具调用(Function Calling / Tool Use) 是大语言模型的原生能力,它允许模型:
- 自主判断是否需要调用外部工具
- 生成结构化的函数调用请求(包含函数名和参数)
- 接收执行结果并整合到回复中
scss
┌─────────────────────────────────────────────────────────────────┐
│ 工具调用工作流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 用户查询 │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 大模型 │───▶│ 生成工具调用 │───▶│ 应用层执行 │ │
│ │ (推理决策) │ │ (JSON格式) │ │ (调用API) │ │
│ └─────────────┘ └─────────────┘ └──────┬──────┘ │
│ ▲ │ │
│ │ ▼ │
│ │ ┌─────────────┐ │
│ │ │ 外部工具 │ │
│ │ │ (天气/API/DB)│ │
│ │ └──────┬──────┘ │
│ │ │ │
│ └────────────────────────────────────────┘ │
│ (接收执行结果,生成最终回复) │
│ │
└─────────────────────────────────────────────────────────────────┘
工具调用的三大价值:
| 价值维度 | 说明 | 示例 |
|---|---|---|
| 增强知识 | 访问训练数据之外的信息 | 实时天气、股价、新闻 |
| 扩展能力 | 执行模型本身不擅长的任务 | 精确计算、数据可视化 |
| 执行操作 | 与外部系统交互 | 发送邮件、创建日程、控制设备 |
12.1.2 主流平台实现对比
OpenAI Function Calling
OpenAI 于 2023 年 6 月首次推出 Function Calling,现已成为行业标准。2025 年已统一为 OpenAI Tool Definition Format。
基本流程:
python
from openai import OpenAI
import json
client = OpenAI()
# 1. 定义工具(函数声明)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气。当用户询问天气、温度、降雨等信息时调用。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,如'北京'、'上海'或'San Francisco'"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认为摄氏度"
}
},
"required": ["location"],
"additionalProperties": False
},
"strict": True
}
}
]
# 2. 调用模型,传入工具定义
response = client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": "北京今天天气如何?"}],
tools=tools,
tool_choice="auto" # auto | required | none | {"type": "function", "function": {"name": "xxx"}}
)
# 3. 处理工具调用
message = response.choices[0].message
if message.tool_calls:
# 模型决定调用工具
tool_call = message.tool_calls[0]
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f"模型请求调用: {function_name}")
print(f"参数: {arguments}")
# 4. 应用层执行函数
if function_name == "get_weather":
result = get_weather_api(**arguments) # 实际调用天气API
# 5. 将结果返回给模型
messages = [
{"role": "user", "content": "北京今天天气如何?"},
message.model_dump(), # 包含tool_calls的助手消息
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
}
]
final_response = client.chat.completions.create(
model="gpt-4.1",
messages=messages
)
print(final_response.choices[0].message.content)
else:
# 模型直接回复
print(message.content)
OpenAI Function Calling Guide, platform.openai.com/docs/guides...
Anthropic Tool Use
Anthropic Claude 的 Tool Use 与 OpenAI Function Calling 本质相同,使用类似的 JSON Schema 定义。
python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "获取指定城市的天气",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,如'北京'、'上海'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["location"]
}
}
],
messages=[{"role": "user", "content": "北京天气如何?"}]
)
# 检查是否有工具使用请求
if response.stop_reason == "tool_use":
tool_use = response.content[-1] # 最后一个content块是tool_use
print(f"工具名称: {tool_use.name}")
print(f"输入参数: {tool_use.input}")
关键特性:
- 支持
strict: true确保工具调用严格符合 schema - 支持客户端工具(Client Tools)和服务器端工具(Server Tools)
- 工具访问是 Agent 最高杠杆的原语之一
Anthropic Tool Use Documentation, docs.anthropic.com/en/docs/too...
Google Gemini Function Calling
Gemini API 支持 Function Calling,与 OpenAI 格式兼容。
python
from google import genai
from google.genai import types
client = genai.Client()
# 定义函数声明
schedule_meeting_function = {
"name": "schedule_meeting",
"description": "在指定时间安排会议",
"parameters": {
"type": "object",
"properties": {
"attendees": {
"type": "array",
"items": {"type": "string"},
"description": "参会人员邮箱列表"
},
"date": {
"type": "string",
"description": "会议日期 (格式: '2024-07-29')"
},
"time": {
"type": "string",
"description": "会议时间 (格式: '15:00')"
},
"duration_minutes": {
"type": "integer",
"description": "会议时长(分钟)"
}
},
"required": ["attendees", "date", "time"]
}
}
# 创建工具配置
tools = types.Tool(function_declarations=[schedule_meeting_function])
config = types.GenerateContentConfig(tools=[tools])
# 调用模型
response = client.models.generate_content(
model="gemini-2.5-flash-preview",
contents="安排一个与 Bob (bob@example.com) 和 Alice (alice@example.com) 的会议,时间 2025-03-14 10:00,时长30分钟",
config=config
)
# 处理函数调用
if response.candidates[0].content.parts[0].function_call:
function_call = response.candidates[0].content.parts[0].function_call
print(f"函数: {function_call.name}")
print(f"参数: {function_call.args}")
特性:
- 支持并行函数调用(Parallel Function Calling)
- 支持组合函数调用(Compositional Function Calling)
- Gemini 为每个函数调用生成唯一 ID
Google Gemini Function Calling, ai.google.dev/gemini-api/...
12.1.3 工具调用的内部机制
模型如何决定调用工具?
当提供工具定义时,模型实际上是在进行一个特殊的"分类"任务:
- 理解用户意图:分析查询需要什么信息或操作
- 匹配工具:将意图与可用工具的描述进行匹配
- 参数提取:从查询中提取工具所需的参数
- 生成调用:输出结构化的函数调用请求
css
用户查询: "北京今天天气怎么样?"
模型思考过程:
1. 用户想知道北京的天气信息
2. 可用工具中有 "get_weather" 可以获取天气
3. 需要从查询中提取 "location" 参数
4. 生成调用: get_weather(location="北京")
工具调用 vs 普通回复的决策边界:
| 场景 | 模型行为 | 示例 |
|---|---|---|
| 需要实时/外部数据 | 调用工具 | "今天股价多少?" |
| 需要精确计算 | 调用工具 | "计算 2345 × 6789" |
| 需要执行操作 | 调用工具 | "帮我预订机票" |
| 纯知识问答 | 直接回复 | "解释量子力学" |
| 创意生成 | 直接回复 | "写一首诗" |
12.1.4 适用场景与局限性
最佳适用场景:
- ✅ 需要实时数据:天气、股价、新闻、航班状态
- ✅ 需要精确计算:数学运算、数据分析、统计
- ✅ 需要与外部系统交互:数据库查询、API 调用、文件操作
- ✅ 需要执行操作:发送邮件、创建日程、下单购买
局限性:
- ❌ 模型不直接执行函数:需要应用层处理调用请求
- ❌ 增加延迟:需要多轮 API 调用(请求→调用→结果→回复)
- ❌ 依赖工具描述质量:描述不清晰会导致错误调用
- ❌ 可能产生幻觉调用:生成不存在的函数或错误参数
12.2 ReAct 模式:推理与行动的协同提示
12.2.1 核心概念与原理
ReAct (Reasoning + Acting)由普林斯顿大学和 Google Research 团队(Yao et al., 2022)提出,ICLR 2023 发表。核心理念是让 LLM 交替进行推理(Reasoning)和行动(Acting),通过与外部工具交互解决复杂问题。
scss
┌─────────────────────────────────────────────────────────────────┐
│ ReAct 循环模式 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Thought │─────▶│ Action │─────▶│Observation│ │
│ │ (思考) │ │ (行动) │ │ (观察) │ │
│ └──────────┘ └──────────┘ └─────┬────┘ │
│ ▲ │ │
│ │ │ │
│ └────────────────────────────────────┘ │
│ (基于观察结果进行下一轮思考) │
│ │
│ Thought: 分析当前情况,规划下一步 │
│ Action: 调用工具执行操作 │
│ Observation: 接收工具返回结果 │
│ │
└─────────────────────────────────────────────────────────────────┘
ReAct 的核心优势:
- 解决 CoT 的幻觉问题:通过外部工具验证事实,减少错误传播
- 可解释性强:思考过程显式展示,便于理解和调试
- 动态调整:根据观察结果灵活调整策略
- 人机协作友好:人类可以介入任何步骤进行指导
12.2.2 ReAct 论文核心贡献
论文:ReAct: Synergizing Reasoning and Acting in Language Models (arXiv:2210.03629)
主要实验结果:
| 任务 | 基线方法 | ReAct | 提升 |
|---|---|---|---|
| HotpotQA | CoT | ReAct | 超越基线 |
| Fever | 标准提示 | ReAct | 超越基线 |
| ALFWorld (决策) | 模仿学习 | ReAct | +34% |
| WebShop (决策) | 强化学习 | ReAct | +10% |
核心发现:
- ReAct 通过与 Wikipedia API 交互,生成类似人类的任务解决轨迹
- 解决了 Chain-of-Thought 的幻觉和错误传播问题
- 在知识密集型任务和决策任务上都表现优异
Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models", ICLR 2023, arxiv.org/abs/2210.03...
12.2.3 ReAct 提示模板设计
标准 ReAct 提示结构:
yaml
尽可能好地回答以下问题。你可以使用以下工具:
{tools}
使用以下格式:
Question: 你必须回答的输入问题
Thought: 你应该始终思考该怎么做
Action: 要采取的行动,应该是 [{tool_names}] 之一
Action Input: 行动的输入
Observation: 行动的结果
... (这个 Thought/Action/Action Input/Observation 可以重复 N 次)
Thought: 我现在知道最终答案
Final Answer: 对原始输入问题的最终答案
开始!
Question: {input}
Thought:
完整示例:
python
from langchain import hub
from langchain.agents import create_react_agent, AgentExecutor
from langchain.tools import BaseTool
from langchain_openai import ChatOpenAI
# 1. 初始化模型
llm = ChatOpenAI(model="gpt-4", temperature=0)
# 2. 定义工具
class WeatherTool(BaseTool):
name = "get_weather"
description = "获取指定城市的天气信息。输入应该是城市名称。"
def _run(self, city: str) -> str:
# 实际实现会调用天气 API
weather_data = {
"北京": {"temp": 25, "condition": "晴朗"},
"上海": {"temp": 28, "condition": "多云"},
"广州": {"temp": 32, "condition": "雷阵雨"}
}
data = weather_data.get(city, {"temp": 20, "condition": "未知"})
return f"{city}当前天气:{data['condition']},温度 {data['temp']}°C"
class CalculatorTool(BaseTool):
name = "calculator"
description = "用于数学计算。输入应该是数学表达式。"
def _run(self, expression: str) -> str:
try:
result = eval(expression)
return str(result)
except:
return "计算错误"
tools = [WeatherTool(), CalculatorTool()]
# 3. 获取 ReAct 提示模板
prompt = hub.pull("hwchase17/react")
# 4. 创建 ReAct Agent
agent = create_react_agent(llm, tools, prompt)
# 5. 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 显示思考过程
handle_parsing_errors=True,
max_iterations=10 # 防止无限循环
)
# 6. 运行
result = agent_executor.invoke({
"input": "北京今天气温比上海高多少度?"
})
print(result["output"])
执行轨迹示例:
yaml
> 进入新的 AgentExecutor 链...
Thought: 我需要比较北京和上海的天气温度。
Action: get_weather
Action Input: 北京
Observation: 北京当前天气:晴朗,温度 25°C
Thought: 现在我需要获取上海的天气。
Action: get_weather
Action Input: 上海
Observation: 上海当前天气:多云,温度 28°C
Thought: 北京25°C,上海28°C。北京比上海低3度,或者说上海比北京高3度。
Action: calculator
Action Input: 28 - 25
Observation: 3
Thought: 我现在知道最终答案了。
Final Answer: 北京今天气温比上海低3度(北京25°C,上海28°C)。
> 链结束。
12.2.4 ReAct 变体与优化
1. ReAct + Self-Consistency
结合 Self-Consistency 技术,多次运行 ReAct 并投票选择最佳答案:
python
def react_with_self_consistency(query: str, n_samples: int = 5) -> str:
"""ReAct + Self-Consistency"""
answers = []
for _ in range(n_samples):
result = agent_executor.invoke({"input": query})
answers.append(result["output"])
# 多数投票
from collections import Counter
return Counter(answers).most_common(1)[0][0]
2. ReAct + Reflection
添加反思步骤,让 Agent 评估自己的表现:
python
reflection_prompt = """基于以上执行轨迹,请反思:
1. 是否有更高效的解决路径?
2. 是否有错误的决策?
3. 如何改进?
反思:"""
3. 异步 ReAct
支持并行工具调用:
python
async def async_react_agent(query: str):
"""异步 ReAct Agent"""
# 同时发起多个独立工具调用
tasks = [
get_weather("北京"),
get_weather("上海"),
get_weather("广州")
]
results = await asyncio.gather(*tasks)
# 综合结果...
12.2.5 ReAct 最佳实践
1. 清晰的工具描述
每个工具的描述应明确说明其用途和使用时机:
python
# 好的描述
"获取指定城市的天气信息。当用户询问天气、温度、降雨等信息时调用。输入应该是城市名称。"
# 差的描述
"天气工具"
2. 提供示例(Few-shot)
在提示中提供 Thought-Action-Observation 的示例:
python
examples = """
Question: 纽约的天气如何?
Thought: 用户想知道纽约的天气,我应该使用 get_weather 工具。
Action: get_weather
Action Input: 纽约
Observation: 纽约当前天气:多云,温度 18°C
Thought: 我已经获得了天气信息,可以回复用户了。
Final Answer: 纽约当前天气多云,温度18°C。
"""
3. 设置安全限制
python
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=10, # 最大迭代次数
max_execution_time=60, # 最大执行时间(秒)
early_stopping_method="generate", # 超时后生成回复
handle_parsing_errors=True # 处理解析错误
)
4. 观察结果格式化
确保观察结果易于模型理解:
python
# 好的观察结果
"北京当前天气:晴朗,温度25°C,湿度60%,风速3级"
# 差的观察结果
"{'city': '北京', 'temp': 25, 'cond': 'sunny'}"
12.3 函数调用提示词设计:定义函数与参数
12.3.1 函数定义 Schema 详解
OpenAI Function Calling Schema 结构:
json
{
"type": "function",
"function": {
"name": "function_name", // 函数名称
"description": "函数描述", // 详细的功能和使用时机说明
"parameters": { // 参数定义(JSON Schema)
"type": "object",
"properties": { // 参数列表
"param1": {
"type": "string", // 参数类型
"description": "参数描述", // 参数说明
"enum": ["a", "b", "c"] // 可选:枚举值
}
},
"required": ["param1"], // 必需参数
"additionalProperties": false // 禁止额外属性
},
"strict": true // 严格模式
}
}
字段说明表:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | 是 | 固定为 "function" |
name |
string | 是 | 函数名称,只能包含 a-z、A-Z、0-9、_、- |
description |
string | 是 | 函数用途和调用时机的详细描述 |
parameters |
object | 是 | JSON Schema 格式的参数定义 |
strict |
boolean | 否 | 启用严格模式,确保输出符合 schema |
12.3.2 函数描述(Description)编写最佳实践
原则 1:明确使用时机
描述中应说明"何时"调用该函数:
python
# ✅ 好的示例
"当用户需要查询订单状态时调用此函数。输入订单号,返回订单的当前状态、预计送达时间和物流信息。"
# ❌ 差的示例
"这是一个订单查询函数。"
原则 2:具体而非笼统
提供具体的使用场景和示例:
python
# ✅ 好的示例
"搜索产品信息。当用户询问'有没有红色连衣裙'、'iPhone 15多少钱'或'推荐一款蓝牙耳机'时调用。支持按关键词、价格范围、品牌筛选。"
# ❌ 差的示例
"搜索产品。"
原则 3:包含参数格式说明
在描述中提供参数格式示例:
python
# ✅ 好的示例
"创建会议预约。参数说明:\n"
"- title: 会议标题,如'产品评审会'\n"
"- attendees: 参会人员邮箱列表,如['alice@example.com', 'bob@example.com']\n"
"- start_time: 开始时间,ISO 8601格式,如'2024-12-25T14:00:00Z'\n"
"- duration_minutes: 会议时长(分钟)"
# ❌ 差的示例
"创建会议。"
原则 4:避免过度描述
保持简洁,避免冗余信息:
python
# ❌ 过度描述(包含实现细节)
"此函数通过调用内部REST API(端点 /api/v2/search,使用GET方法,需要Bearer Token认证)来搜索产品..."
# ✅ 简洁描述
"搜索产品信息。当用户询问产品相关问题时调用。"
12.3.3 参数定义(Properties/Required)设计原则
Properties 设计原则:
python
{
"properties": {
"location": {
"type": "string",
"description": "城市名称,如'北京'、'Shanghai'或'New York'"
},
"date": {
"type": "string",
"description": "日期,格式为YYYY-MM-DD,如'2024-12-25'"
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"],
"description": "优先级:low(低)、medium(中)、high(高)、urgent(紧急)"
},
"tags": {
"type": "array",
"items": {"type": "string"},
"description": "标签列表,如['重要', '待跟进']"
},
"metadata": {
"type": "object",
"properties": {
"source": {"type": "string"},
"created_by": {"type": "string"}
},
"description": "额外元数据"
}
}
}
Required 设计原则:
python
{
"required": ["location", "date"], # 只标记真正必需的参数
# "optional_field" 不放入 required 列表
}
设计建议:
- 最小化 required 参数:过多的必需参数会增加模型负担
- 为可选参数提供默认值说明:在描述中说明默认值
- 使用 enum 限制取值范围:帮助模型选择正确的值
- 嵌套对象要清晰定义:复杂参数使用嵌套 schema
12.3.4 完整函数定义示例
示例 1:电商订单查询
python
order_query_tool = {
"type": "function",
"function": {
"name": "query_order",
"description": "查询订单信息。当用户询问订单状态、物流信息、退款进度时调用。支持按订单号、手机号或邮箱查询。",
"parameters": {
"type": "object",
"properties": {
"query_type": {
"type": "string",
"enum": ["order_id", "phone", "email"],
"description": "查询方式:order_id(订单号)、phone(手机号)、email(邮箱)"
},
"query_value": {
"type": "string",
"description": "查询值。订单号格式为'ORD'+8位数字,如'ORD20240001'"
},
"include_details": {
"type": "boolean",
"description": "是否包含详细信息,默认为false"
}
},
"required": ["query_type", "query_value"],
"additionalProperties": False
},
"strict": True
}
}
示例 2:智能日程管理
python
schedule_tool = {
"type": "function",
"function": {
"name": "manage_schedule",
"description": "管理用户日程。支持创建、查询、修改、删除日程安排。当用户提到'会议'、'预约'、'提醒'、'日程'时调用。",
"parameters": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["create", "query", "update", "delete"],
"description": "操作类型:create(创建)、query(查询)、update(修改)、delete(删除)"
},
"event": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "事件标题,如'产品评审会'"
},
"start_time": {
"type": "string",
"description": "开始时间,ISO 8601格式,如'2024-12-25T14:00:00+08:00'"
},
"duration_minutes": {
"type": "integer",
"description": "持续时间(分钟),如60表示1小时"
},
"attendees": {
"type": "array",
"items": {"type": "string"},
"description": "参会人员邮箱列表"
},
"location": {
"type": "string",
"description": "地点,如'会议室A'或'线上'"
},
"description": {
"type": "string",
"description": "事件描述"
}
},
"required": ["title", "start_time"]
},
"event_id": {
"type": "string",
"description": "事件ID,修改或删除时需要"
}
},
"required": ["action"],
"additionalProperties": False
},
"strict": True
}
}
12.3.5 命名空间(Namespaces)组织
当工具数量庞大时,使用命名空间组织:
python
# 定义命名空间
crm_namespace = {
"type": "namespace",
"name": "crm",
"description": "CRM客户管理系统工具,用于客户查询、订单管理和售后支持",
"tools": [
{
"type": "function",
"name": "get_customer_profile",
"description": "根据客户ID获取客户资料",
"parameters": {...}
},
{
"type": "function",
"name": "list_open_orders",
"description": "列出客户的未完成订单",
"defer_loading": True, # 延迟加载
"parameters": {...}
},
{
"type": "function",
"name": "create_support_ticket",
"description": "创建售后工单",
"parameters": {...}
}
]
}
# 使用命名空间
agent = Agent(
name="客服助手",
tools=[crm_namespace, other_tools]
)
12.4 多工具协作提示:让模型自主选择和组合工具
12.4.1 工具自主选择(Tool Choice)
OpenAI 提供 tool_choice 参数控制工具选择行为:
| 值 | 说明 | 使用场景 |
|---|---|---|
"auto" |
默认模式,模型自主决定是否调用工具 | 通用场景 |
"required" |
强制模型至少调用一个工具 | 必须执行操作的场景 |
"none" |
禁止模型调用工具 | 纯对话场景 |
{"type": "function", "function": {"name": "xxx"}} |
强制调用指定工具 | 已知需要特定工具 |
python
# 强制调用工具
response = client.chat.completions.create(
model="gpt-4.1",
messages=messages,
tools=tools,
tool_choice="required" # 强制至少调用一个工具
)
# 强制调用特定工具
response = client.chat.completions.create(
model="gpt-4.1",
messages=messages,
tools=tools,
tool_choice={
"type": "function",
"function": {"name": "get_weather"}
}
)
12.4.2 并行调用 vs 串行调用
并行函数调用(Parallel Function Calling):
当多个工具调用相互独立时,模型可以在一次响应中同时请求调用多个工具:
python
# 用户查询:"北京、上海、广州的天气如何?"
# 模型可以同时请求三个独立的天气查询
response = client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": "北京、上海、广州的天气如何?"}],
tools=[weather_tool],
tool_choice="auto",
parallel_tool_calls=True # 启用并行调用
)
# 处理多个工具调用
if response.choices[0].message.tool_calls:
tool_calls = response.choices[0].message.tool_calls
# 并行执行所有工具调用
import concurrent.futures
def execute_tool(tool_call):
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
if function_name == "get_weather":
return get_weather_api(**arguments)
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(execute_tool, tool_calls))
# 将结果返回给模型
for i, tool_call in enumerate(tool_calls):
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(results[i])
})
串行调用(Sequential/Compositional):
当工具调用有依赖关系时,需要按顺序执行:
python
# 示例:查询用户最近的订单,然后查询该订单的物流信息
# 必须先获取订单ID,才能查询物流
# 第一轮:获取订单
response1 = client.chat.completions.create(...)
order_info = execute_tool(response1.choices[0].message.tool_calls[0])
# 第二轮:使用订单ID查询物流
messages.append({"role": "tool", "content": json.dumps(order_info)})
response2 = client.chat.completions.create(...)
logistics_info = execute_tool(response2.choices[0].message.tool_calls[0])
12.4.3 工具调用链设计模式
模式 1:顺序链(Sequential Chain)
css
用户查询 → 工具A → 结果A → 工具B → 结果B → 最终回复
适用场景:有明确依赖关系的多步骤任务
python
def sequential_chain(query: str) -> str:
"""顺序链示例:查询股票 → 分析新闻 → 生成报告"""
# 步骤1:获取股票数据
stock_data = call_tool("get_stock_price", {"symbol": "AAPL"})
# 步骤2:获取相关新闻
news = call_tool("get_news", {"query": "Apple stock"})
# 步骤3:生成分析报告
analysis = call_tool("generate_report", {
"stock_data": stock_data,
"news": news
})
return analysis
模式 2:并行聚合(Parallel Aggregation)
css
用户查询 → [工具A, 工具B, 工具C] → 聚合结果 → 最终回复
适用场景:需要从多个来源收集信息的任务
python
def parallel_aggregation(query: str) -> str:
"""并行聚合示例:同时查询多个数据源"""
import concurrent.futures
tasks = [
("search_database", {"query": query}),
("search_web", {"query": query}),
("search_docs", {"query": query})
]
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = [executor.submit(call_tool, name, args) for name, args in tasks]
results = [f.result() for f in futures]
# 聚合结果
combined = synthesize_results(results)
return combined
模式 3:条件分支(Conditional Branching)
用户查询 → 分类工具 → 根据类别选择不同工具链 → 最终回复
适用场景:不同类型查询需要不同处理流程
python
def conditional_branching(query: str) -> str:
"""条件分支示例:客服路由"""
# 步骤1:分类查询
category = call_tool("classify_query", {"query": query})
# 步骤2:根据类别路由
if category == "billing":
return call_tool("handle_billing", {"query": query})
elif category == "technical":
return call_tool("handle_technical", {"query": query})
elif category == "sales":
return call_tool("handle_sales", {"query": query})
else:
return call_tool("handle_general", {"query": query})
模式 4:循环迭代(Loop Iteration)
css
用户查询 → 工具A → 检查结果 → 需要继续? → 是 → 工具A → ...
↓ 否
最终回复
适用场景:需要多次迭代直到满足条件
python
def loop_iteration(query: str, max_iterations: int = 5) -> str:
"""循环迭代示例:数据补全"""
context = query
iteration = 0
while iteration < max_iterations:
# 尝试获取信息
result = call_tool("search_and_extract", {"context": context})
# 检查是否完整
if result["is_complete"]:
return result["data"]
# 更新上下文继续搜索
context = result["next_query"]
iteration += 1
return "无法在规定时间内完成"
12.4.4 Tool Search(工具搜索)
当工具数量庞大时,使用 Tool Search 延迟加载:
python
from agents import Agent, ToolSearchTool, function_tool, tool_namespace
# 定义大量工具,标记为延迟加载
@function_tool(defer_loading=True)
def get_customer_profile(customer_id: str) -> str:
"""获取 CRM 客户资料"""
pass
@function_tool(defer_loading=True)
def list_customer_orders(customer_id: str) -> str:
"""列出客户订单"""
pass
@function_tool(defer_loading=True)
def create_support_ticket(issue: str) -> str:
"""创建售后工单"""
pass
# 组织为命名空间
crm_tools = tool_namespace(
name="crm",
description="CRM 客户查询工具",
tools=[get_customer_profile, list_customer_orders, create_support_ticket]
)
# 创建带工具搜索的 Agent
agent = Agent(
name="客服助手",
tools=[
*crm_tools,
ToolSearchTool() # 工具搜索工具
]
)
# 当 Agent 需要特定工具时,ToolSearchTool 会帮助找到并加载它
12.4.5 多工具协作最佳实践
1. 工具命名规范
python
# ✅ 好的命名
"get_weather" # 动词 + 名词
"search_products" # 动词 + 名词
"create_appointment" # 动词 + 名词
# ❌ 差的命名
"weather" # 缺少动词
"product_search" # 名词在前
"appointment_creator" # 过于冗长
2. 避免工具冲突
python
# 当多个工具有相似功能时,明确区分
{
"name": "search_products",
"description": "搜索电商产品目录。当用户想购买商品时使用。"
}
{
"name": "search_knowledge_base",
"description": "搜索内部知识库。当用户询问产品使用方法、故障排除时使用。"
}
3. 工具结果标准化
python
# 统一工具返回格式
{
"success": True,
"data": {...}, # 成功时的数据
"error": None, # 错误信息
"metadata": { # 额外信息
"timestamp": "...",
"source": "..."
}
}
12.5 Agent 提示工程:多 Agent 协作与通信协议设计
12.5.1 Agent 架构基础
Workflow vs Agent 的区别(Anthropic 定义):
| 维度 | Workflow(工作流) | Agent(智能体) |
|---|---|---|
| 控制方式 | 预定义代码路径 | LLM 动态决策 |
| 灵活性 | 确定性、可预测 | 自适应、动态 |
| 复杂度 | 相对简单 | 更复杂 |
| 透明度 | 高 | 需要额外设计 |
| 适用场景 | 明确步骤的任务 | 开放式、探索性任务 |
scss
┌─────────────────────────────────────────────────────────────────┐
│ Workflow vs Agent 架构对比 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Workflow(预定义路径) Agent(动态决策) │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 输入 │ │ 输入 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 步骤1 │ │ LLM │ │
│ │ (代码) │ │ (决策) │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 步骤2 │◄─────────────────│ 工具调用 │ │
│ │ (代码) │ 或继续决策 │ 或回复 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 输出 │ │ 输出 │ │
│ └─────────┘ └─────────┘ │
│ │
│ 特点: 特点: │
│ - 流程固定 - 流程动态 │
│ - 易于调试 - 更灵活 │
│ - 可预测 - 需要监控 │
│ │
└─────────────────────────────────────────────────────────────────┘
Anthropic, "Building Effective Agents", www.anthropic.com/engineering...
12.5.2 常见 Workflow 模式
模式 1:Prompt Chaining(提示链)
将任务分解为顺序步骤,每步调用 LLM:
python
# 示例:营销文案生成
# 步骤1:生成大纲
outline = llm.generate("为产品X生成营销文案大纲")
# 步骤2:根据大纲生成内容
draft = llm.generate(f"根据以下大纲撰写文案:{outline}")
# 步骤3:润色
final = llm.generate(f"润色以下文案:{draft}")
模式 2:Routing(路由)
分类输入并路由到专门处理流程:
python
def routing_workflow(query: str) -> str:
# 分类
category = llm.classify(query, categories=["billing", "technical", "sales"])
# 路由
handlers = {
"billing": billing_agent,
"technical": tech_agent,
"sales": sales_agent
}
return handlers[category].process(query)
模式 3:Parallelization(并行化)
同时处理子任务并聚合结果:
python
def parallelization_workflow(query: str) -> str:
# 并行调用
results = parallel_map([
lambda: llm.process(query, aspect="pros"),
lambda: llm.process(query, aspect="cons"),
lambda: llm.process(query, aspect="neutral")
])
# 聚合
return synthesize(results)
模式 4:Orchestrator-Workers(编排器-工作者)
中央 LLM 动态分解任务并委派:
python
def orchestrator_workers(task: str) -> str:
# 编排器分解任务
subtasks = orchestrator_llm.decompose(task)
# 工作者并行处理
results = parallel_map(
lambda subtask: worker_llm.process(subtask),
subtasks
)
# 编排器综合结果
return orchestrator_llm.synthesize(results)
模式 5:Evaluator-Optimizer(评估器-优化器)
生成-评估循环直到满足质量标准:
python
def evaluator_optimizer(initial_content: str) -> str:
content = initial_content
for iteration in range(max_iterations):
# 生成器生成/改进
content = generator_llm.improve(content)
# 评估器评估
evaluation = evaluator_llm.evaluate(content)
if evaluation.score >= threshold:
return content
return content
12.5.3 OpenAI Agents SDK
OpenAI Agents SDK 是用于构建多 Agent 工作流的轻量级框架。
核心概念:
| 概念 | 说明 |
|---|---|
| Agents | 配置指令、工具和 handoffs 的 LLM |
| Handoffs | Agent 之间的任务委托 |
| Tools | 函数工具、MCP、托管工具 |
| Guardrails | 输入输出安全检查 |
| Tracing | 内置追踪和调试 |
基础示例:
python
from agents import Agent, Runner
# 创建 Agent
agent = Agent(
name="Assistant",
instructions="你是一个有用的助手"
)
# 运行
result = Runner.run_sync(
agent,
"写一首关于编程中递归的俳句"
)
print(result.final_output)
Agents as Tools 模式:
python
from agents import Agent
# 定义专家 Agent
research_agent = Agent(
name="ResearchAgent",
instructions="你是一位研究专家,负责收集和整理信息。"
)
writing_agent = Agent(
name="WritingAgent",
instructions="你是一位写作专家,负责撰写高质量内容。"
)
# 主 Agent 将专家作为工具
main_agent = Agent(
name="MainAgent",
instructions="你是主助手,可以调用研究专家和写作专家来完成任务。",
tools=[
research_agent.as_tool(
tool_name="do_research",
tool_description="需要收集信息时调用"
),
writing_agent.as_tool(
tool_name="do_writing",
tool_description="需要撰写内容时调用"
)
]
)
Handoffs 模式:
python
from agents import Agent, handoff
# 定义专业 Agent
billing_agent = Agent(
name="BillingAgent",
instructions="你负责处理账单相关问题。"
)
refund_agent = Agent(
name="RefundAgent",
instructions="你负责处理退款相关问题。"
)
technical_agent = Agent(
name="TechnicalAgent",
instructions="你负责处理技术支持问题。"
)
# 路由 Agent
triage_agent = Agent(
name="TriageAgent",
instructions="你是问题分类专家,将用户问题路由给合适的专家。",
handoffs=[
billing_agent,
handoff(refund_agent), # 可以包装为 handoff 对象
technical_agent
]
)
# 运行
result = Runner.run_sync(
triage_agent,
"我想申请退款,上周买的商品质量有问题"
)
# 会自动 handoff 到 RefundAgent
来源 :OpenAI Agents SDK, openai.github.io/openai-agen...
12.5.4 多 Agent 协作架构
两种编排方式对比:
| 方式 | 控制方式 | 适用场景 |
|---|---|---|
| LLM 编排 | 由 LLM 自主决定流程 | 开放式任务、需要灵活决策 |
| 代码编排 | 通过代码确定流程 | 确定性任务、需要可预测性 |
Agent as Tools vs Handoffs:
| 模式 | 控制方式 | 适用场景 |
|---|---|---|
| Agents as Tools | 管理者 Agent 保持控制 | 需要组合多个专家输出、统一安全策略 |
| Handoffs | 专家 Agent 接管对话 | 需要专家直接回复、切换上下文 |
代码编排示例:
python
from agents import Agent, Runner
import asyncio
# 定义专业 Agent
researcher = Agent(
name="Researcher",
instructions="你是研究专家,负责收集和分析信息。提供详细的事实和数据。"
)
writer = Agent(
name="Writer",
instructions="你是写作专家,负责撰写高质量报告。使用清晰、专业的语言。"
)
reviewer = Agent(
name="Reviewer",
instructions="你是审校专家,负责检查内容质量、准确性和可读性。提供具体改进建议。"
)
# 代码编排的工作流
async def write_report_workflow(topic: str) -> str:
"""研究报告生成工作流"""
print(f"开始生成关于'{topic}'的报告...")
# 阶段1:研究
print("阶段1:信息收集...")
research_result = await Runner.run(
researcher,
f"请深入研究以下主题,收集关键信息和数据:{topic}"
)
# 阶段2:写作
print("阶段2:撰写报告...")
draft = await Runner.run(
writer,
f"基于以下研究内容撰写一份专业报告:\n\n{research_result.final_output}"
)
# 阶段3:审校
print("阶段3:质量审校...")
review_result = await Runner.run(
reviewer,
f"请审校以下报告,评估质量并提供改进建议:\n\n{draft.final_output}"
)
# 阶段4:根据反馈修改(可选)
print("阶段4:最终修订...")
final = await Runner.run(
writer,
f"根据以下审校意见修改报告:\n\n"
f"原报告:{draft.final_output}\n\n"
f"审校意见:{review_result.final_output}"
)
return final.final_output
# 运行工作流
result = asyncio.run(write_report_workflow("人工智能在医疗诊断中的应用"))
print(result)
12.5.5 Agent 通信协议设计
设计原则:
- 标准化接口:使用结构化输入输出
- 上下文传递:明确传递对话历史
- 元数据传递:包含优先级、原因等信息
Input Filter 模式:
python
from agents import handoff
from agents.extensions import handoff_filters
# 创建 handoff,过滤掉工具定义
handoff_obj = handoff(
agent=specialist_agent,
input_filter=handoff_filters.remove_all_tools
)
# 其他可用的 filter
# - remove_all_tools: 移除所有工具定义
# - remove_system_instructions: 移除系统指令
# - custom filter: 自定义过滤逻辑
结构化 Handoff 输入:
python
from pydantic import BaseModel
from agents import handoff
class EscalationData(BaseModel):
"""升级数据结构"""
reason: str # 升级原因
priority: str # 优先级:low/medium/high/urgent
customer_tier: str # 客户等级
previous_attempts: int # 之前尝试次数
async def on_handoff(ctx, input_data: EscalationData):
"""Handoff 回调函数"""
print(f"升级到人工客服")
print(f"原因: {input_data.reason}")
print(f"优先级: {input_data.priority}")
# 可以在这里记录日志、发送通知等
# 创建带结构化输入的 handoff
escalation_handoff = handoff(
agent=human_support_agent,
on_handoff=on_handoff,
input_type=EscalationData
)
# 在主 Agent 中使用
triage_agent = Agent(
name="TriageAgent",
instructions="...",
handoffs=[escalation_handoff]
)
Agent 间消息格式:
python
# 标准化的 Agent 间通信消息格式
agent_message = {
"from_agent": "Researcher",
"to_agent": "Writer",
"message_type": "task_handoff", # task_handoff / result / clarification
"content": {
"task": "撰写报告",
"context": "已完成信息收集",
"data": {...},
"requirements": [...]
},
"metadata": {
"timestamp": "2024-12-25T10:00:00Z",
"priority": "high",
"deadline": "2024-12-26T10:00:00Z"
}
}
12.5.6 规划(Planning)与执行(Execution)分离
架构设计:
scss
┌─────────────────────────────────────────────────────────────────┐
│ 规划-执行分离架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ Planning Agent│ │Execution Agent│ │
│ │ (规划器) │ │ (执行器) │ │
│ └───────┬───────┘ └───────┬───────┘ │
│ │ │ │
│ │ 1. 生成执行计划 │ │
│ │─────────────────────────▶│ │
│ │ │ │
│ │ │ 2. 按计划执行工具调用 │
│ │ │────▶ 工具A │
│ │ │────▶ 工具B │
│ │ │────▶ 工具C │
│ │ │ │
│ │ 3. 返回执行结果 │ │
│ │◀─────────────────────────│ │
│ │ │ │
│ │ 4. 评估并调整计划 │ │
│ │ (如果需要,生成新计划) │ │
│ │ │ │
└─────────────────────────────────────────────────────────────────┘
实现模式:
python
from agents import Agent, Runner
from typing import List, Dict
import json
# 规划 Agent
planner = Agent(
name="Planner",
instructions="""你是一个规划专家。分析用户请求并生成详细的执行计划。
输出格式必须是JSON:
{
"goal": "明确的目标",
"steps": [
{
"step_number": 1,
"action": "具体行动描述",
"tool": "需要使用的工具名称",
"input": {"参数": "值"},
"expected_output": "预期结果"
}
],
"tools_needed": ["工具A", "工具B"],
"success_criteria": ["标准1", "标准2"]
}"""
)
# 执行 Agent
executor = Agent(
name="Executor",
instructions="""你是一个执行专家。严格按照给定的计划执行工具调用。
执行原则:
1. 严格按照计划顺序执行
2. 每个步骤完成后报告结果
3. 遇到错误立即停止并报告
4. 不要擅自修改计划
可用工具:{tools}""",
tools=[tool1, tool2, tool3] # 实际工具
)
# 评估 Agent
evaluator = Agent(
name="Evaluator",
instructions="""你是一个评估专家。评估执行结果是否满足原始目标。
输出格式:
{
"success": true/false,
"score": 0-100,
"feedback": "评估意见",
"needs_replanning": true/false
}"""
)
async def plan_and_execute(request: str, max_iterations: int = 3) -> Dict:
"""规划-执行-评估循环"""
iteration = 0
history = []
while iteration < max_iterations:
print(f"\n=== 迭代 {iteration + 1}/{max_iterations} ===")
# 规划阶段
print("生成执行计划...")
plan_result = await Runner.run(
planner,
f"用户请求: {request}\n\n历史执行记录: {json.dumps(history, ensure_ascii=False) if history else '无'}"
)
plan = json.loads(plan_result.final_output)
print(f"计划: {json.dumps(plan, indent=2, ensure_ascii=False)}")
# 执行阶段
print("执行计划...")
execution_result = await Runner.run(
executor,
f"执行以下计划:\n{json.dumps(plan, indent=2, ensure_ascii=False)}"
)
print(f"执行结果: {execution_result.final_output}")
# 评估阶段
print("评估结果...")
eval_result = await Runner.run(
evaluator,
f"原始请求: {request}\n\n执行结果: {execution_result.final_output}"
)
evaluation = json.loads(eval_result.final_output)
print(f"评估: {json.dumps(evaluation, indent=2, ensure_ascii=False)}")
history.append({
"iteration": iteration + 1,
"plan": plan,
"execution": execution_result.final_output,
"evaluation": evaluation
})
# 检查是否完成
if evaluation["success"] and not evaluation["needs_replanning"]:
print("任务完成!")
return {
"success": True,
"final_result": execution_result.final_output,
"history": history
}
iteration += 1
return {
"success": False,
"final_result": execution_result.final_output,
"history": history,
"reason": "达到最大迭代次数"
}
# 使用示例
# result = asyncio.run(plan_and_execute("帮我预订下周去北京的高铁票,要上午出发的"))
12.5.7 Agent 设计最佳实践
来自 Anthropic 官方指南的核心建议:
-
保持简单(Simplicity)
- 从简单提示开始,仅在必要时增加复杂度
- 优先考虑 Workflow 而非 Agent
- 避免过度工程化
-
优先透明(Transparency)
- 显式展示 Agent 的规划步骤
- 记录决策过程便于调试
- 提供清晰的执行轨迹
-
精心设计工具(Tool Design)
- 通过完善的文档说明工具用途
- 充分测试工具边界情况
- 构建良好的 Agent-计算机接口(ACI)
-
直接调用 API
- 许多模式可用几行代码实现
- 避免过度依赖框架
- 保持对流程的控制
Guardrails(护栏)设计:
python
from agents import Agent, GuardrailFunctionOutput, input_guardrail
@input_guardrail
async def safety_check(ctx, agent, input_data):
"""安全检查护栏"""
# 检查输入是否包含敏感内容
if contains_sensitive_info(input_data):
return GuardrailFunctionOutput(
output_info={"reason": "检测到敏感信息"},
tripwire_triggered=True
)
return GuardrailFunctionOutput(
output_info={"safe": True},
tripwire_triggered=False
)
agent = Agent(
name="SafeAgent",
instructions="你是一个安全的助手",
input_guardrails=[safety_check]
)
本章小结
本章系统性地讲解了工具调用与 Agent 提示工程的核心技术:
核心概念回顾
| 技术 | 核心思想 | 关键价值 |
|---|---|---|
| Function Calling | 让模型生成结构化工具调用请求 | 扩展模型能力边界 |
| ReAct | Thought-Action-Observation 循环 | 推理与行动协同 |
| 多工具协作 | 模型自主选择和组合工具 | 解决复杂任务 |
| Agent 架构 | Workflow vs Agent,多 Agent 协作 | 构建智能系统 |
关键要点
- 工具调用三要素:清晰的函数描述、合理的参数设计、恰当的示例引导
- ReAct 核心循环:Thought → Action → Observation,交替进行推理和行动
- 多工具协作模式:顺序链、并行聚合、条件分支、循环迭代
- Agent 设计原则:从简单开始、保持透明、精心设计工具、直接调用 API
- 多 Agent 架构:Workflow(预定义)vs Agent(动态),Agents as Tools vs Handoffs
技术选型指南
| 场景 | 推荐技术 |
|---|---|
| 简单工具调用 | Function Calling + 单次调用 |
| 需要推理的复杂任务 | ReAct Agent |
| 多步骤确定性流程 | Workflow (Prompt Chaining / Routing) |
| 探索性任务 | Agent (LLM 动态决策) |
| 需要多专家协作 | Multi-Agent (Orchestrator-Workers) |
| 质量要求高的生成任务 | Evaluator-Optimizer |
下一章预告:第13章将深入讲解"代码生成与程序理解提示工程",探讨如何让模型生成高质量代码、进行代码审查和程序分析。
参考来源:
| 来源 | 链接 |
|---|---|
| OpenAI Function Calling | platform.openai.com/docs/guides... |
| Anthropic Tool Use | docs.anthropic.com/en/docs/too... |
| Anthropic Building Effective Agents | www.anthropic.com/engineering... |
| Google Gemini Function Calling | ai.google.dev/gemini-api/... |
| OpenAI Agents SDK | openai.github.io/openai-agen... |
| ReAct Paper (Yao et al., 2022) | arxiv.org/abs/2210.03... |
| ReAct Project | react-lm.github.io/ |
| LangChain Agent Executor | python.langchain.com/docs/how_to... |