引言

大模型(LLM)虽然聪明,但它们存在“幻觉”且知识往往停留在训练数据截止的那一天。为了让大模型真正落地,我们需要赋予它与外部世界交互的能力——也就是调用工具(Function Calling / Tool Calling)

今天,我们将从零开始,用 Python 手搓一个最基础但五脏俱全的 Agent。

本期 Demo 任务:打造一个“气象小助手”,它能够自主判定用户意图,调用外部天气 API 获取实时数据,并整理成一份天气日报。


1. 系统流程:Agent 是如何调用工具的?

很多人以为给大模型加工具非常复杂,其实核心流程就是一个**“多轮对话”的闭环**:

  1. 用户提问:用户输入“今天北京天气怎么样?适合穿什么?”

  2. 大模型决策(意图识别):LLM 分析问题,发现自己不知道实时天气,但它知道你给过它一个叫 get_weather 的工具。于是,LLM 返回一段特殊格式的消息,告诉代码:“我要调用 get_weather,参数是 location="北京"”。

  3. 本地代码执行:我们的本地代码拦截到 LLM 的请求,执行真正的 get_weather("北京") 函数,拿到结果(例如:25度,晴)。

  4. 结果回传与生成:代码将“25度,晴”作为一条新的消息发送给 LLM。LLM 结合这个事实,生成最终的自然语言回复给用户。


2. 核心代码实现

我们使用 Python 和 openai 官方库来实现。即使你使用的是国产大模型(如通义千问、智谱等),只要兼容 OpenAI API 格式,这套代码也能直接跑。

2.1 定义本地工具

首先,我们需要一个能获取天气的 Python 函数。这里为了演示,我们用 Mock 数据代替真实的 API 请求。

import json

def get_weather(location: str) -> str:
    """模拟一个调用外部天气的 API"""
    # 实际应用中这里会调用如和风天气、OpenWeather的真实API
    weather_db = {
        "北京": {"temp": "25°C", "condition": "晴朗", "wind": "微风"},
        "上海": {"temp": "28°C", "condition": "多云", "wind": "东南风"},
    }
    
    result = weather_db.get(location, {"error": "未找到该城市天气"})
    return json.dumps(result, ensure_ascii=False)

2.2 定义工具描述 (Tool Schema)

为了让 LLM 知道有这个工具可用,我们需要按照 JSON Schema 的格式向它描述这个工具的名称、作用和参数。

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前实时天气情况",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,例如:北京、上海",
                    }
                },
                "required": ["location"],
            },
        }
    }
]

2.3 编写 Agent 核心逻辑

这是整个系统的灵魂所在:处理 LLM 的响应,执行函数,并将其塞回对话历史中。

from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY")

def run_agent(user_input: str):
    messages = [{"role": "user", "content": user_input}]

    # 第一轮:询问大模型
    response = client.chat.completions.create(
        model="gpt-4o-mini", # 或其他支持 Tool Calling 的模型
        messages=messages,
        tools=tools,
        tool_choice="auto" 
    )
    
    response_message = response.choices[0].message
    tool_calls = response_message.tool_calls

    # 判断大模型是否决定调用工具
    if tool_calls:
        print(f"⚙️ [系统日志] Agent 决定调用工具: {tool_calls[0].function.name}")
        
        # 将大模型的调用请求也存入历史
        messages.append(response_message) 

        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            
            # 本地执行对应的函数
            if function_name == "get_weather":
                function_response = get_weather(location=function_args.get("location"))
                
                # 将函数的执行结果封装进 message 传给 LLM
                messages.append(
                    {
                        "tool_call_id": tool_call.id,
                        "role": "tool",
                        "name": function_name,
                        "content": function_response,
                    }
                )
                
        # 第二轮:带着工具返回的结果,再次呼叫大模型
        final_response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
        )
        return final_response.choices[0].message.content
        
    return response_message.content

# 运行测试
if __name__ == "__main__":
    user_query = "帮我查一下北京今天的天气,并写一段简短的出行日报。"
    print(f"👤 用户:{user_query}\n")
    
    result = run_agent(user_query)
    print(f"🤖 Agent:\n{result}")

3. 踩坑与问题分析

在实际开发和将 Agent 部署到生产环境的过程中,你一定会遇到以下几个坑:

坑 1:参数幻觉(Hallucinated Arguments)

现象:工具需要 location,但大模型偏偏传给你一个拼写错误的字段,或者凭空捏造了你没有定义的参数,导致本地 Python 代码报错(如 KeyError)。 解法:在代码层面必须做好异常处理(try-except)。如果解析 JSON 失败或缺少必需参数,不要直接崩溃,而是把错误信息(例如 "Error: Missing required argument 'location'")作为 tool 的结果返回给大模型,聪明的大模型会自动发现错误并自我修正,重新发起调用。

坑 2:死循环调用(Infinite Loops)

现象:大模型调用 API 失败,然后它不认输,反复发起同样的调用,导致 Tokens 被迅速耗尽。 解法:在系统外层强行限制最大工具调用次数(Max Steps)。例如设定一个 step < 5 的计数器,如果循环超过 5 次还没给出最终回复,直接强制中断并向用户抛出兜底提示。

坑 3:不知道什么时候该用工具

现象:你问大模型“今天天气如何”,它却直接开始胡编乱造,完全不触发 get_weather解法

  1. 优化 Tool Schema:大模型是根据 description 来判断是否调用的。你的 description 必须足够精确。不要只写“天气工具”,要写“当用户询问任何关于实时天气、气温、降水概率时,必须调用此工具”。

  2. System Prompt 加持:在系统的 System 消息中明确立规矩:“你是一个拥有实时网络搜索能力的助手。面对你不确定的事实,必须优先使用工具查询,禁止凭空捏造。”

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐