基于工具调用的智能体设计与实现
一、这是什么?(概念解释)
基于工具调用的Agent(Tool Calling Agent) 是利用LLM原生函数调用能力实现的智能体。
与ReACT的区别:
- ReACT:依赖Prompt让LLM按特定格式输出(Thought/Action/Observation)
- 工具调用Agent:利用模型原生的
tool_calls功能,更稳定、更高效
核心优势:
- 原生支持:使用模型内置的函数调用能力
- 结构化输出:自动返回结构化的工具调用指令
- 多轮对话:支持聊天历史,可以实现对话式Agent
- 更简洁:无需复杂的Prompt模板
二、有什么用?(应用场景)
| 场景 | 说明 |
|---|---|
| 对话式助手 | 支持多轮对话的智能助手 |
| 任务自动化 | 自动调用工具完成复杂任务 |
| 信息检索 | 结合搜索工具回答问题 |
| 内容生成 | 调用图片/视频生成工具 |
| 数据分析 | 调用数据分析工具生成报告 |
| API集成 - | 调用外部API服务 |
| 多工具协同 | 自动选择和组合多个工具 |
三、完整示例代码
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import dotenv
import os
import requests
from datetime import datetime
from langchain_classic.agents import create_tool_calling_agent, AgentExecutor
from langchain_community.tools import GoogleSerperRun
from langchain_community.utilities import GoogleSerperAPIWrapper
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
# ============ 第一步:定义工具 ============
@tool
def generate_image(prompt: str) -> str:
"""
使用通义万相生成图片
Args:
prompt: 图片描述提示词
Returns:
生成图片保存到当前目录
"""
try:
# 调用通义万相API生成图片
response = ImageSynthesis.call(
model='wanx-v1',
prompt=prompt,
n=1,
size='1024*1024'
)
if response.status_code == 200:
image_url = response.output.results[0].url
# 下载图片并保存
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"generated_image_{timestamp}.png"
img_response = requests.get(image_url)
if img_response.status_code == 200:
with open(filename, 'wb') as f:
f.write(img_response.content)
return f"图片生成成功!已保存到: {filename}"
else:
return f"图片下载失败"
else:
return f"图片生成失败"
except Exception as e:
return f"图片生成异常: {str(e)}"
# 定义搜索工具
google_serper = GoogleSerperRun(
name="google_serper",
description="一个低成本的谷歌搜索API。当你需要回答有关时事的问题时,可以调用该工具。",
args_schema=GoogleSerperArgsSchema,
api_wrapper=GoogleSerperAPIWrapper(),
)
tools = [google_serper, generate_image]
# ============ 第二步:定义 Prompt ============
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个智能助手,善于帮助用户解决问题。"),
("placeholder", "{chat_history}"), # 聊天历史
("human", "{input}"), # 用户输入
("placeholder", "{agent_scratchpad}"), # Agent执行历史
])
# ============ 第三步:创建工具调用 Agent ============
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
agent = create_tool_calling_agent(
llm=llm,
prompt=prompt,
tools=tools,
)
# ============ 第四步:创建 Agent 执行器 ============
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 打印执行过程
max_iterations=5, # 最大迭代次数
handle_parsing_errors=True,
)
# ============ 第五步:执行 Agent ============
result = agent_executor.invoke({
"input": "帮我绘制一幅鲨鱼在天上游泳的场景"
})
print(result)四、工具调用Agent vs ReACT Agent
┌─────────────────────────────────────────────────────────────────────────┐
│ 工具调用Agent VS ReACT Agent 对比 │
└─────────────────────────────────────────────────────────────────────────┘
================== ReACT Agent ==================
Prompt 复杂度:高
需要精心设计 Prompt 模板
用户问题 LLM 工具执行
│ │ │
▼ ▼ │
"生成鲨鱼图片" ────────────▶│ Thought: 我需要生成图片 │
│ │ │
│ │ Action: generate_image │
│ │ │
│ │ Action Input: "鲨鱼..." │
│◀──────────────────────│ │
▼ │ │
解析 Action Input │ │
│ │ │
▼ ▼ ▼
调用工具 ─────────────────────────────────────────────▶│
│ 生成图片
│◀───────────────────────────────────────│
▼ ▼ ▼
观察 (Observation) "图片已保存" 返回结果
│ │ │
▼ ▼ │
Thought: 我知道答案了 Final Answer: "图片... │
│ │ │
▼ ▼ ▼
返回结果 完成 完成
================== 工具调用 Agent ==================
Prompt 复杂度:低
只需简单的系统提示
用户问题 LLM 工具执行
│ │ │
▼ ▼ │
"生成鲨鱼图片" ────────────▶│ 自动分析需求 │
│ │ │
│ │ 返回 tool_calls: │
│◀──────────────────────│ [{name: "generate_image",│
│ │ args: {prompt: "..."]}}│
▼ │ │
自动解析 tool_calls │ │
│ │ │
▼ ▼ ▼
自动调用工具 ──────────────────────────────────────────▶│
│ 生成图片
│◀───────────────────────────────────────│
▼ ▼ ▼
自动获取工具结果 "图片已保存" 返回结果
│ │ │
▼ ▼ │
自动生成最终回答 "图片已生成并保存..." │
│ │ │
▼ ▼ ▼
返回结果 完成 完成
┌─────────────────────────────────────────────────────────────────────────┐
│ 核心差异 │
└─────────────────────────────────────────────────────────────────────────┘
特性 ReACT Agent 工具调用 Agent
─────────────────────────────────────────────────────────────────────────
Prompt 复杂度 高(需要严格格式) 低(简单提示)
模型兼容性 广(几乎所有模型) 有限(需要支持函数调用)
解析方式 手动解析文本 自动解析 tool_calls
错误处理 容易出错 更稳定
Token消耗 高 低
多轮对话 需要手动管理 自动支持五、执行流程示例
假设用户问:"帮我搜索一下深圳今天的天气,然后生成一张天气预报图片"
第1轮迭代:
> Entering new AgentExecutor chain...
Invoking: google_serper
with: {'query': '深圳今天天气'}
Observation: 深圳今天晴天,温度25°C,湿度60%第2轮迭代:
Thought: 我需要根据天气信息生成一张天气预报图片
Invoking: generate_image
with: {'prompt': '深圳今天晴天,温度25度,湿度60%,天气预报插图'}
Observation: 图片生成成功!已保存到: generated_image_20260305.png最终回答:
> Finished chain.
根据搜索结果,深圳今天晴天,温度25°C,湿度60%。
我已经为您生成了一张天气预报插图,保存为:generated_image_20260305.png六、关键代码解析
| 代码 | 作用 |
|---|---|
create_tool_calling_agent() | 创建基于工具调用的Agent |
AgentExecutor | Agent执行器,管理工具调用循环 |
("placeholder", "{chat_history}") | 聊天历史占位符 |
("placeholder", "{agent_scratchpad}") | Agent执行历史占位符 |
verbose=True | 打印详细的执行过程 |
max_iterations=5 | 限制最大迭代次数 |
handle_parsing_errors=True | 自动处理解析错误 |
七、支持多轮对话
python
from langchain_core.messages import HumanMessage, AIMessage
# 定义聊天历史
chat_history = [
HumanMessage(content="你好"),
AIMessage(content="你好!有什么可以帮助你的?"),
HumanMessage(content="帮我生成一张猫的图片"),
AIMessage(content="好的,正在为您生成猫的图片..."),
]
# 执行Agent(带历史)
result = agent_executor.invoke({
"input": "再生成一张狗的图片",
"chat_history": chat_history
})八、添加更多工具
python
@tool
def calculator(expression: str) -> str:
"""计算数学表达式"""
try:
result = eval(expression)
return f"结果: {result}"
except Exception as e:
return f"计算错误: {e}"
@tool
def get_weather(city: str) -> str:
"""查询城市天气"""
# 调用天气API
return f"{city}今天晴天,25°C"
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""发送邮件"""
# 调用邮件API
return f"邮件已发送给 {to}"
tools = [google_serper, generate_image, calculator, get_weather, send_email]
# Agent会自动选择合适的工具
agent = create_tool_calling_agent(llm=llm, tools=tools, prompt=prompt)九、最佳实践
1. 工具描述要清晰
python
@tool
def search_weather(query: str) -> str:
"""
搜索天气信息
Args:
query: 搜索查询,例如"北京今天天气"或"上海明天天气预报"
Returns:
天气信息的文字描述
"""
# 实现...2. 设置合理的迭代次数
python
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=5, # 防止无限循环
early_stopping_method="generate" # 达到最大次数时强制生成答案
)3. 添加错误处理
python
@tool
def safe_tool(param: str) -> str:
"""带错误处理的工具"""
try:
# 执行操作
result = do_something(param)
return f"操作成功: {result}"
except Exception as e:
return f"操作失败: {str(e)}"4. 使用流式输出
python
async for chunk in agent_executor.astream({"input": "生成一张图片"}):
print(chunk, end="", flush=True)十、学习建议
- 模型选择:确保使用的模型支持函数调用(如GPT-4、Claude 3)
- 工具设计:每个工具只做一件事,保持简单
- 描述优化:清晰的工具描述能让Agent更好地选择工具
- 测试调试:使用
verbose=True查看详细执行过程 - 逐步扩展:从1-2个工具开始,逐步增加功能
十一、参考资源
- LangChain Tool Calling Agents:https://python.langchain.com/docs/modules/agents/agent_types/tool_calling/
- OpenAI Function Calling:https://platform.openai.com/docs/guides/function-calling
- AgentExecutor 文档:https://python.langchain.com/docs/modules/agents/agent_executors/
总结一句话:工具调用Agent利用模型原生函数调用能力,比ReACT更简洁、更稳定,是构建现代AI应用的首选方案!