Skip to content

基于工具调用的智能体设计与实现

一、这是什么?(概念解释)

基于工具调用的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
AgentExecutorAgent执行器,管理工具调用循环
("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)

十、学习建议

  1. 模型选择:确保使用的模型支持函数调用(如GPT-4、Claude 3)
  2. 工具设计:每个工具只做一件事,保持简单
  3. 描述优化:清晰的工具描述能让Agent更好地选择工具
  4. 测试调试:使用 verbose=True 查看详细执行过程
  5. 逐步扩展:从1-2个工具开始,逐步增加功能

十一、参考资源


总结一句话:工具调用Agent利用模型原生函数调用能力,比ReACT更简洁、更稳定,是构建现代AI应用的首选方案!