详细教学:Agent + Function Calling

Agent + Function Calling 详细教学

在大型语言模型(LLM)的浪潮下,如何让这些强大的模型不仅仅停留在“聊天”层面,而是能真正地与外部世界交互、执行复杂任务,成为了AI应用落地的关键。Agent(AI代理)Function Calling(函数调用) 正是解决这一挑战的利器。

graph TD User[用户指令] --> Agent[Agent AI代理]; Agent --> Reasoning[思考/规划]; Reasoning --> |需要外部工具?| Function_Calling{Function Calling}; Function_Calling --> Tool_Execution[工具执行]; Tool_Execution --> Observation[观察结果]; Observation --> Reasoning; Reasoning --> |任务完成?| Final_Response[最终回复];

💭 什么是 Agent(AI代理)?

Agent 是指一个能够感知环境、进行思考、规划行动并执行任务的自主实体。在AI领域,特别是与LLM结合时,一个Agent通常包含以下核心组件:

Agent 的目标是让LLM超越简单的问答,具备更高级的推理和执行能力,使其能够处理更广泛、更复杂的实际问题。

💫 什么是 Function Calling(函数调用)?

Function Calling 是一种让LLM能够感知并调用外部工具或API 的机制。简单来说,它不是让LLM直接执行代码,而是让LLM在理解用户意图后,判断是否需要调用某个预定义的外部函数来获取信息或执行操作,并生成调用该函数所需的参数。

当LLM识别到用户请求需要使用某个工具时,它会输出一个结构化的响应(通常是JSON格式),其中包含要调用的函数名称和相应的参数。这个响应不会直接呈现给用户,而是由开发者在后端代码中捕获,并实际执行对应的函数。函数执行的结果会再次作为上下文输入给LLM,供其生成最终的回复。

核心思想: LLM负责“思考”和“决策”要用什么工具以及如何用,而实际的“执行”则交给外部系统。

Function Calling 的工作原理

理解 Function Calling 的内部流程至关重要。它通常遵循以下步骤:

  1. 定义工具函数(Tool Definition): 开发者需要向LLM描述其可用的工具函数,包括函数名、用途描述以及所需的参数及其类型。这通常通过结构化的数据(如JSON Schema)进行。
  2. 用户查询(User Query): 用户向LLM提出请求。
  3. LLM 推理(LLM Inference): LLM接收用户查询,并结合其被提供的工具定义,进行推理。它会判断:
    • 用户意图是否可以通过现有工具解决?
    • 如果可以,应该调用哪个工具?
    • 调用这个工具需要哪些参数?这些参数能否从用户查询中提取?
  4. 生成函数调用(Generate Function Call): 如果LLM认为需要调用工具,它不会生成自然语言回复,而是生成一个特定的、表示函数调用的结构化输出(例如,一个JSON对象,包含"tool_name""arguments")。
  5. 执行函数(Execute Function): 开发者在后端代码中捕获到这个结构化输出,然后根据其中的函数名和参数,实际调用预定义的外部函数。
  6. 获取函数结果(Get Function Result): 外部函数执行完成后,将其结果返回。
  7. 结果反馈给LLM(Feed Result back to LLM): 函数执行的结果作为新的上下文信息,再次发送给LLM。
  8. LLM 最终回复(LLM Final Response): LLM结合用户查询、它之前的思考以及工具的执行结果,生成最终的自然语言回复给用户。
sequenceDiagram participant User as 用户 participant Frontend as 应用前端 participant Backend as 应用后端 participant LLM as 大型语言模型 participant ExternalTool as 外部工具/API User->>Frontend: 发送请求 (例如: "明天北京天气如何?") Frontend->>Backend: 转发用户请求 Backend->>LLM: 1. 发送用户请求 + 工具定义 LLM->>LLM: 2. LLM 推理: 判断是否需要调用工具 LLM-->>Backend: 3. 返回函数调用指令 (例如: {"tool": "get_weather", "args": {"city": "北京", "date": "明天"}}) Backend->>Backend: 4. 解析指令 Backend->>ExternalTool: 5. 执行工具函数 (例如: 调用天气API) ExternalTool-->>Backend: 6. 返回函数结果 (例如: {"temperature": "25C", "condition": "晴"}) Backend->>LLM: 7. 将函数结果作为上下文发送给LLM LLM->>LLM: 8. LLM 生成最终回复 (结合原始请求和工具结果) LLM-->>Backend: 9. 返回最终回复 (例如: "明天北京晴朗,气温25摄氏度。") Backend->>Frontend: 转发最终回复 Frontend->>User: 显示最终回复

Agent 与 Function Calling 的结合

Agent 和 Function Calling 是相互赋能的。Function Calling 是 Agent 能够与外部世界交互的“手脚”,而 Agent 则为 LLM 提供了更高级的“大脑”,使其能够自主决定何时以及如何运用这些“手脚”来完成复杂任务。

一个Agent可以通过多次Function Calling迭代,来实现一个复杂目标:

实践:构建一个简单的 Agent

我们将使用 Python 和 OpenAI 的 API 来构建一个简单的 Agent,它能够查询当前时间或天气。请确保您已经安装了 OpenAI Python 库并设置了 API Key。

1. 准备工作


pip install openai
        

注意: 请将您的 OpenAI API Key 设置为环境变量 OPENAI_API_KEY,或者直接在代码中替换 'YOUR_OPENAI_API_KEY'。出于安全考虑,强烈建议使用环境变量。

2. 定义可用的工具函数

我们定义两个简单的 Python 函数:get_current_timeget_current_weather。同时,我们需要为LLM提供这些函数的结构化描述。


import datetime
import json
import os
# from openai import OpenAI # 如果你使用最新的openai库,请取消注释
# client = OpenAI() # 如果你使用最新的openai库,请取消注释

# 模拟的外部工具函数
def get_current_time(timezone: str = "UTC") -> str:
    """
    获取当前UTC时间,或指定时区的时间。
    :param timezone: 目标时区,例如 "Asia/Shanghai", "America/New_York", "UTC"。
    :return: 当前时间的字符串表示。
    """
    try:
        if timezone == "UTC":
            now = datetime.datetime.now(datetime.timezone.utc)
            return now.strftime("%Y-%m-%d %H:%M:%S UTC")
        else:
            # 这是一个简化的示例,实际中需要更复杂的时区处理库,如 pytz
            # 这里仅做示例,不处理复杂的时区转换逻辑
            # print(f"Warning: 时区 '{timezone}' 的精确转换需要更专业的库。")
            now = datetime.datetime.now() # 默认为本地时间
            return f"无法精确转换时区'{timezone}',当前本地时间:{now.strftime('%Y-%m-%d %H:%M:%S')}"
    except Exception as e:
        return f"获取时间失败: {e}"

def get_current_weather(location: str, unit: str = "celsius") -> str:
    """
    获取指定地点的当前天气信息。
    :param location: 地点名称 (例如: "北京", "上海").
    :param unit: 温度单位, 可选 "celsius" (摄氏度) 或 "fahrenheit" (华氏度). 默认为 "celsius".
    :return: 天气信息的字符串表示。
    """
    # 这是一个模拟函数,实际应调用天气API
    weather_data = {
        "北京": {"celsius": "28°C 晴", "fahrenheit": "82.4°F Sunny"},
        "上海": {"celsius": "25°C 多云", "fahrenheit": "77°F Cloudy"},
        "纽约": {"celsius": "20°C 小雨", "fahrenheit": "68°F Light Rain"},
    }
    if location in weather_data:
        return weather_data[location].get(unit, "未知单位")
    return f"抱歉,无法获取 {location} 的天气信息。"

# 定义LLM可以调用的工具函数描述
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "获取当前时间,可以指定时区。",
            "parameters": {
                "type": "object",
                "properties": {
                    "timezone": {
                        "type": "string",
                        "description": "目标时区,例如 'Asia/Shanghai', 'America/New_York', 'UTC'。",
                        "default": "UTC"
                    }
                },
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "获取指定地点的当前天气信息。",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "地点名称,例如 '北京', '上海'。",
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位,'celsius' (摄氏度) 或 'fahrenheit' (华氏度)。",
                    },
                },
                "required": ["location"],
            },
        },
    },
]

# 存储可调用的函数映射,以便根据LLM的指令执行
available_functions = {
    "get_current_time": get_current_time,
    "get_current_weather": get_current_weather,
}

3. 构建 Agent 核心逻辑

这里的核心逻辑是一个循环,模拟Agent的“思考-行动-观察”循环。它会不断向LLM发送请求,直到LLM给出最终的自然语言回复。


# 请替换为您的OpenAI API Key,或者确保已设置为环境变量
# os.environ['OPENAI_API_KEY'] = 'YOUR_OPENAI_API_KEY' 
# 请注意,最新版本的openai库初始化方式有所不同
# from openai import OpenAI
# client = OpenAI()

# 为了兼容老版本和简化示例,这里直接用 requests 模拟
import requests

def call_openai_api(messages, tools=None):
    """
    模拟调用OpenAI Chat Completion API
    """
    # 这是一个简化版本,实际使用时请安装 openai 库并使用 client.chat.completions.create
    # 例如:
    # response = client.chat.completions.create(
    #     model="gpt-3.5-turbo-0125", # 或 "gpt-4-turbo" 等支持 function calling 的模型
    #     messages=messages,
    #     tools=tools,
    #     tool_choice="auto", # 允许模型决定是否调用工具
    # )
    # return response.choices[0].message

    # 模拟 API 调用的 JSON 结构
    # 实际API调用会更复杂,这里仅为演示
    # 实际使用时,请配置正确的API Endpoint和Headers
    api_key = os.getenv('OPENAI_API_KEY') # 或者直接 'YOUR_OPENAI_API_KEY'
    if not api_key:
        raise ValueError("请设置 OPENAI_API_KEY 环境变量或在代码中硬编码。")

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }
    payload = {
        "model": "gpt-3.5-turbo-0125", # 或 "gpt-4-turbo", "gpt-4o" 等支持 function calling 的模型
        "messages": messages,
        "tools": tools,
        "tool_choice": "auto"
    }

    try:
        response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
        response.raise_for_status() # 检查HTTP响应状态
        return response.json()['choices'][0]['message']
    except requests.exceptions.RequestException as e:
        print(f"Error calling OpenAI API: {e}")
        return {"role": "assistant", "content": "抱歉,暂时无法连接到AI服务,请稍后再试。"}


def run_conversation(user_query):
    messages = [{"role": "user", "content": user_query}]

    # 第一轮:用户问题 -> LLM
    response = call_openai_api(messages, tools)
    messages.append(response) # 将LLM的回复(可能是函数调用指令)添加到对话历史

    # 检查LLM是否决定调用工具
    if response.tool_calls:
        print(f"\nAI决定调用工具: {response.tool_calls}")
        for tool_call in response.tool_calls:
            function_name = tool_call.function.name
            function_to_call = available_functions.get(function_name)

            if function_to_call:
                function_args = json.loads(tool_call.function.arguments)
                print(f"  正在执行函数: {function_name} with args: {function_args}")
                function_response = function_to_call(**function_args)
                print(f"  函数执行结果: {function_response}")

                messages.append(
                    {
                        "tool_call_id": tool_call.id,
                        "role": "tool",
                        "name": function_name,
                        "content": function_response, # 将工具执行结果作为 tool 角色消息发送给LLM
                    }
                )
            else:
                messages.append(
                    {
                        "tool_call_id": tool_call.id,
                        "role": "tool",
                        "name": function_name,
                        "content": f"错误: 未知函数 {function_name}",
                    }
                )
        
        # 第二轮:将工具执行结果反馈给LLM,让LLM生成最终回复
        print("\n将工具结果反馈给AI,等待最终回复...")
        final_response = call_openai_api(messages)
        return final_response.content
    else:
        # LLM没有调用工具,直接返回其生成的回复
        return response.content

4. 测试 Agent


if __name__ == "__main__":
    # Test cases
    print("--- 测试案例 1: 查询时间 ---")
    query1 = "现在几点了?"
    result1 = run_conversation(query1)
    print(f"\n用户提问: {query1}")
    print(f"AI回复: {result1}\n")

    print("--- 测试案例 2: 查询特定时区时间 ---")
    query2 = "告诉我纽约当前时间。"
    result2 = run_conversation(query2)
    print(f"\n用户提问: {query2}")
    print(f"AI回复: {result2}\n")

    print("--- 测试案例 3: 查询天气 ---")
    query3 = "上海天气怎么样?"
    result3 = run_conversation(query3)
    print(f"\n用户提问: {query3}")
    print(f"AI回复: {result3}\n")

    print("--- 测试案例 4: 查询华氏度天气 ---")
    query4 = "纽约的华氏度天气如何?"
    result4 = run_conversation(query4)
    print(f"\n用户提问: {query4}")
    print(f"AI回复: {result4}\n")

    print("--- 测试案例 5: 普通聊天 ---")
    query5 = "你好,你有什么功能?"
    result5 = run_conversation(query5)
    print(f"\n用户提问: {query5}")
    print(f"AI回复: {result5}\n")
        

运行效果示例(输出可能因OpenAI模型版本和实际调用而异):


--- 测试案例 1: 查询时间 ---
AI决定调用工具: [tool_call(id='call_xxx', function=Function(arguments='{}', name='get_current_time'), type='function')]
  正在执行函数: get_current_time with args: {}
  函数执行结果: 2023-10-26 10:30:00 UTC

将工具结果反馈给AI,等待最终回复...

用户提问: 现在几点了?
AI回复: 当前UTC时间是 2023-10-26 10:30:00。

--- 测试案例 2: 查询特定时区时间 ---
AI决定调用工具: [tool_call(id='call_yyy', function=Function(arguments='{"timezone": "America/New_York"}', name='get_current_time'), type='function')]
  正在执行函数: get_current_time with args: {'timezone': 'America/New_York'}
  函数执行结果: 无法精确转换时区'America/New_York',当前本地时间:2023-10-26 18:30:00

将工具结果反馈给AI,等待最终回复...

用户提问: 告诉我纽约当前时间。
AI回复: 无法精确转换时区'America/New_York',当前本地时间是:2023-10-26 18:30:00。

--- 测试案例 3: 查询天气 ---
AI决定调用工具: [tool_call(id='call_zzz', function=Function(arguments='{"location": "上海"}', name='get_current_weather'), type='function')]
  正在执行函数: get_current_weather with args: {'location': '上海'}
  函数执行结果: 25°C 多云

将工具结果反馈给AI,等待最终回复...

用户提问: 上海天气怎么样?
AI回复: 上海当前天气是25°C,多云。

--- 测试案例 4: 查询华氏度天气 ---
AI决定调用工具: [tool_call(id='call_www', function=Function(arguments='{"location": "纽约", "unit": "fahrenheit"}', name='get_current_weather'), type='function')]
  正在执行函数: get_current_weather with args: {'location': '纽约', 'unit': 'fahrenheit'}
  函数执行结果: 68°F Light Rain

将工具结果反馈给AI,等待最终回复...

用户提问: 纽约的华氏度天气如何?
AI回复: 纽约当前是68°F,小雨。

--- 测试案例 5: 普通聊天 ---

用户提问: 你好,你有什么功能?
AI回复: 你好!我是一个大型语言模型,可以进行对话、回答问题、提供信息等。你有什么需要帮助的吗?
        

Function Calling 的高级应用与注意事项

除了上述基本用法,Function Calling 还有一些高级特性和需要注意的事项:

总结

Agent 和 Function Calling 是构建强大、智能且能与真实世界交互的AI应用的关键技术。Function Calling 为LLM提供了“眼睛”和“手”,让它们能够看到和操作外部世界,而Agent则提供了“大脑”和“规划能力”,让LLM能够自主地利用这些工具来解决复杂问题。

掌握这些技术,您将能够开发出超越传统聊天机器人的AI产品,它们可以查询实时数据、执行自动化任务、控制外部设备,从而在各个行业带来革命性的变革。

互动区域

登录后可以点赞此内容

参与互动

登录后可以点赞和评论此内容,与作者互动交流