3. 工具调用基础

工具调用基础 学习卡片

任务目标

本案例将构建一个支持工具调用的Agent基础框架。Agent在收到用户问题时,能够自主判断是否需要调用外部工具(计算器、天气API),并根据工具返回结果生成最终回答。通过本案例,你将理解工具调用的核心机制:意图识别 → 工具执行 → 结果整合。

环境依赖

  • Python 3.9+
  • 安装依赖:pip install (需配置有效的)

核心实现

以下代码实现了一个最小可用的工具调用Agent。它通过的Function Calling能力,让模型在需要时输出结构化工具调用请求,Agent执行工具后把结果回传给模型生成最终回答。代码中内置了两个工具:计算器(执行四则运算)和天气查询(模拟返回固定城市天气)。

import json
from  import 

client = ()  # 请确保环境变量 _ 已设置

# ---------- 工具定义 ----------
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "calculator",
            "description": "执行四则运算,支持 + - * /",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "数学表达式,如 (3+5)*2"
                    }
                },
                "required": ["expression"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如 北京"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

# ---------- 工具执行逻辑 ----------
def execute_tool(name: str, args: dict) -> str:
    if name == "calculator":
        # 安全计算:仅允许数字和运算符
        expr = args["expression"].replace(" ", "")
        if not all(c in "0123456789+-*/()." for c in expr):
            return "错误:表达式包含非法字符"
        try:
            return str(eval(expr))  # 教学演示用,生产环境应使用安全解析器
        except Exception as e:
            return f"计算错误: {e}"
    elif name == "get_weather":
        # 模拟天气数据,实际可替换为真实API调用
        mock_data = {"北京": "晴 25°C", "上海": "多云 28°C", "广州": "雷阵雨 30°C"}
        return mock_data.get(args["city"], f"暂无{city}的天气数据")
    return "未知工具"

# ---------- Agent 主循环 ----------
def agent_chat(user_input: str, max_turns: int = 3):
    messages = [{"role": "user", "content": user_input}]
    
    for _ in range(max_turns):
        response = client.chat.completions.create(
            model="-mini",
            messages=messages,
            tools=TOOLS,
            tool_choice="auto"
        )
        msg = response.choices[0].message
        
        # 情况1:模型没有请求调用工具,直接返回最终回答
        if not msg.tool_calls:
            return msg.content
        
        # 情况2:模型请求调用工具,执行并回传结果
        messages.append(msg)  # 保留助手消息(含工具调用请求)
        for tool_call in msg.tool_calls:
            result = execute_tool(tool_call.function.name, 
                                  json.loads(tool_call.function.arguments))
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result
            })
    
    return "已达到最大工具调用轮次,请简化问题后重试。"

# ---------- 测试入口 ----------
if __name__ == "__main__":
    # 测试1:触发计算器工具
    print("用户:计算 (23+17)*4 的结果")
    print("Agent:", agent_chat("计算 (23+17)*4 的结果"))
    print()
    # 测试2:触发天气工具
    print("用户:北京今天天气怎么样?")
    print("Agent:", agent_chat("北京今天天气怎么样?"))
    print()
    # 测试3:无需工具直接回答
    print("用户:你好,介绍一下你自己")
    print("Agent:", agent_chat("你好,介绍一下你自己"))

运行与验证

  1. 将上述代码保存为 tool_agent.py。
  2. 在终端运行:python tool_agent.py
  3. 通过条件:观察输出,应满足以下三点——
    ① 第一个问题返回 160(即 (23+17)*4 的计算结果);
    ② 第二个问题返回包含 晴 25°C 的天气描述;
    ③ 第三个问题返回正常的自我介绍文本,且不触发任何工具调用。

关键说明

  • 工具调用流程:模型在需要时返回 tool_calls 结构,Agent 负责执行对应函数并把结果以 role="tool" 的消息回传,模型据此生成最终回答。这是 Function Calling的标准交互模式。
  • 安全提示:示例中计算器使用 eval 仅为教学演示。生产环境应使用 ast.literal_eval 或专用表达式解析库(如 asteval)来防止代码注入。
  • 扩展真实API:将 get_weather 中的模拟数据替换为对真实天气API(如OpenWeatherMap)的HTTP请求即可接入实际服务,无需改动Agent主循环逻辑。
用户问题 "计算(23+17)*4" Agent 决策 LLM 判断需要工具 → 输出 tool_calls 工具执行 calculator("(23+17)*4") → 返回 "160" tool 消息回传 LLM 结果整合 结合工具结果生成回答 最终回答 "结果是 160" 用户 收到回答 无需工具时:LLM 直接返回文本回答,不经过工具执行分支 例如:"你好,介绍一下你自己" → 直接生成自我介绍