在大型语言模型(LLM)的浪潮下,如何让这些强大的模型不仅仅停留在“聊天”层面,而是能真正地与外部世界交互、执行复杂任务,成为了AI应用落地的关键。Agent(AI代理) 和 Function Calling(函数调用) 正是解决这一挑战的利器。
Agent 是指一个能够感知环境、进行思考、规划行动并执行任务的自主实体。在AI领域,特别是与LLM结合时,一个Agent通常包含以下核心组件:
Agent 的目标是让LLM超越简单的问答,具备更高级的推理和执行能力,使其能够处理更广泛、更复杂的实际问题。
Function Calling 是一种让LLM能够感知并调用外部工具或API 的机制。简单来说,它不是让LLM直接执行代码,而是让LLM在理解用户意图后,判断是否需要调用某个预定义的外部函数来获取信息或执行操作,并生成调用该函数所需的参数。
当LLM识别到用户请求需要使用某个工具时,它会输出一个结构化的响应(通常是JSON格式),其中包含要调用的函数名称和相应的参数。这个响应不会直接呈现给用户,而是由开发者在后端代码中捕获,并实际执行对应的函数。函数执行的结果会再次作为上下文输入给LLM,供其生成最终的回复。
核心思想: LLM负责“思考”和“决策”要用什么工具以及如何用,而实际的“执行”则交给外部系统。
理解 Function Calling 的内部流程至关重要。它通常遵循以下步骤:
"tool_name"和"arguments")。Agent 和 Function Calling 是相互赋能的。Function Calling 是 Agent 能够与外部世界交互的“手脚”,而 Agent 则为 LLM 提供了更高级的“大脑”,使其能够自主决定何时以及如何运用这些“手脚”来完成复杂任务。
一个Agent可以通过多次Function Calling迭代,来实现一个复杂目标:
get_weather(city, date)。get_weather(city='上海', date='未来三天')。我们将使用 Python 和 OpenAI 的 API 来构建一个简单的 Agent,它能够查询当前时间或天气。请确保您已经安装了 OpenAI Python 库并设置了 API Key。
pip install openai
注意: 请将您的 OpenAI API Key 设置为环境变量 OPENAI_API_KEY,或者直接在代码中替换 'YOUR_OPENAI_API_KEY'。出于安全考虑,强烈建议使用环境变量。
我们定义两个简单的 Python 函数:get_current_time 和 get_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,
}
这里的核心逻辑是一个循环,模拟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
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")
--- 测试案例 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 还有一些高级特性和需要注意的事项:
"tool_choice": {"type": "function", "function": {"name": "my_tool"}}),或者完全禁止它调用工具 ("tool_choice": "none")。默认是 "auto",让LLM自行决定。role: "tool"的消息反馈给LLM,它通常能理解并向用户解释,或尝试其他方法。description和parameters的描述越清晰、越准确,LLM就越能正确地理解何时以及如何使用该工具。Agent 和 Function Calling 是构建强大、智能且能与真实世界交互的AI应用的关键技术。Function Calling 为LLM提供了“眼睛”和“手”,让它们能够看到和操作外部世界,而Agent则提供了“大脑”和“规划能力”,让LLM能够自主地利用这些工具来解决复杂问题。
掌握这些技术,您将能够开发出超越传统聊天机器人的AI产品,它们可以查询实时数据、执行自动化任务、控制外部设备,从而在各个行业带来革命性的变革。