(点击上方图片观看本课视频)
工具很有趣,因为它们使 AI 代理拥有更广泛的能力范围。代理不再仅限于执行有限的一组操作,通过添加工具,代理现在可以执行各种操作。在本章中,我们将介绍工具使用设计模式,该模式描述了 AI 代理如何使用特定工具来实现其目标。
在本课中,我们希望回答以下问题:
完成本课后,您将能够:
工具使用设计模式侧重于赋予大型语言模型(LLM)与外部工具交互以实现特定目标的能力。工具是可以由代理执行的代码块,用于完成操作。工具可以是简单的函数,比如计算器,也可以是调用第三方服务的 API,例如股票价格查询或天气预报。在 AI 代理的上下文中,工具被设计为响应模型生成的函数调用由代理执行。
AI 代理可以利用工具完成复杂任务、检索信息或做出决策。工具使用设计模式常用于需要与外部系统动态交互的场景,如数据库、Web 服务或代码解释器。此能力适用于多种用例,包括:
这些构建模块使 AI 代理能够执行广泛任务。下面是实现工具使用设计模式的关键元素:
函数/工具模式定义:包含可用工具的详细定义,包括函数名称、目的、所需参数和预期输出。这些模式让 LLM 理解有哪些工具可用以及如何构造有效请求。
函数执行逻辑:管理何时以及如何根据用户意图和对话上下文调用工具。可能包括规划模块、路由机制或动态确定工具使用的条件流程。
消息处理系统:管理用户输入、LLM 响应、工具调用及工具输出之间的对话流。
工具集成框架:连接代理与各种工具的基础设施,无论是简单函数还是复杂的外部服务。
错误处理与验证:处理工具执行失败、参数验证及意外响应的机制。
状态管理:跟踪对话上下文、先前工具交互及持久数据,确保多轮交互的一致性。
接下来,我们将更详细地讲解函数/工具调用。
函数调用是使大型语言模型(LLM)与工具交互的主要方式。常会看到“函数”和“工具”交替使用,因为“函数”(可复用代码块)就是代理用来完成任务的“工具”。为了调用函数代码,LLM 必须将用户请求与函数描述进行匹配。为此,会向 LLM 发送包含所有可用函数描述的模式(schema)。LLM 会选择最适合该任务的函数,并返回其名称与参数。随后调用该函数,函数响应返回给 LLM,LLM 使用这些信息回应用户请求。
开发者实现代理的函数调用时需要:
以获取某城市当前时间为例说明:
初始化支持函数调用的 LLM:
并非所有模型都支持函数调用,因此需确认所用 LLM 支持。Azure OpenAI 支持函数调用。我们可以从初始化 Azure OpenAI 客户端开始。
# 初始化 Azure OpenAI 客户端
client = AzureOpenAI(
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"),
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-05-01-preview"
)
创建函数模式:
接下来定义包含函数名称、功能描述及函数参数名称和描述的 JSON 模式。 然后将此模式与用户请求(例如查询旧金山时间)一起传给先前建立的客户端。需要注意的是,返回的结果是工具调用,而非问题的最终答案。如前所述,LLM 返回它为任务选择的函数名称及将传递的参数。
# 供模型读取的功能描述
tools = [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "Get the current time in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city name, e.g. San Francisco",
},
},
"required": ["location"],
},
}
}
]
# 初始用户消息
messages = [{"role": "user", "content": "What's the current time in San Francisco"}]
# 第一次API调用:请求模型使用函数
response = client.chat.completions.create(
model=deployment_name,
messages=messages,
tools=tools,
tool_choice="auto",
)
# 处理模型的回复
response_message = response.choices[0].message
messages.append(response_message)
print("Model's response:")
print(response_message)
Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_pOsKdUlqvdyttYB67MOj434b', function=Function(arguments='{"location":"San Francisco"}', name='get_current_time'), type='function')])
完成任务所需的函数代码:
既然 LLM 已选择执行哪个函数,就需要实现该函数的代码并执行。 我们可以用 Python 实现获取当前时间的代码。还需编写从 response_message 中提取函数名称和参数以获取最终结果的代码。
def get_current_time(location):
"""Get the current time for a given location"""
print(f"get_current_time called with location: {location}")
location_lower = location.lower()
for key, timezone in TIMEZONE_DATA.items():
if key in location_lower:
print(f"Timezone found for {key}")
current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
return json.dumps({
"location": location,
"current_time": current_time
})
print(f"No timezone data found for {location_lower}")
return json.dumps({"location": location, "current_time": "unknown"})
# 处理函数调用
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
if tool_call.function.name == "get_current_time":
function_args = json.loads(tool_call.function.arguments)
time_response = get_current_time(
location=function_args.get("location")
)
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": "get_current_time",
"content": time_response,
})
else:
print("No tool calls were made by the model.")
# 第二次API调用:从模型获取最终响应
final_response = client.chat.completions.create(
model=deployment_name,
messages=messages,
)
return final_response.choices[0].message.content
get_current_time called with location: San Francisco
Timezone found for san francisco
The current time in San Francisco is 09:24 AM.
函数调用是大多数(如果不是全部)代理工具使用设计的核心,但从零实现有时存在挑战。 正如我们在第 2 课中了解到的,代理框架为我们提供了预构建的构建模块,以实现工具使用。
以下是使用不同代理框架实现工具使用设计模式的一些示例:
Semantic Kernel 是一个开源 AI 框架,面向 .NET、Python 和 Java 开发者,用于处理大型语言模型(LLM)。它通过一种称为序列化的过程,自动向模型描述函数及其参数,从而简化函数调用。此外它还处理模型与代码间的往返通信。使用如 Semantic Kernel 这样的代理框架的另一个优势是,可以访问预构建工具,如文件搜索和代码解释器。
下图展示了使用 Semantic Kernel 进行函数调用的流程:

在 Semantic Kernel 中,函数/工具被称为插件。我们可以将之前的 get_current_time 函数通过封装为带函数的类转换成插件。还可导入 kernel_function 装饰器,传入函数描述。当用 GetCurrentTimePlugin 创建内核时,内核会自动序列化函数及其参数,创建发送给 LLM 的模式。
from semantic_kernel.functions import kernel_function
class GetCurrentTimePlugin:
async def __init__(self, location):
self.location = location
@kernel_function(
description="Get the current time for a given location"
)
def get_current_time(location: str = ""):
...
from semantic_kernel import Kernel
# 创建内核
kernel = Kernel()
# 创建插件
get_current_time_plugin = GetCurrentTimePlugin(location)
# 将插件添加到内核
kernel.add_plugin(get_current_time_plugin)
Azure AI 代理服务 是一个较新的代理框架,旨在帮助开发者安全构建、部署和扩展高质量且可扩展的 AI 代理,无需管理底层计算和存储资源。该服务对企业应用尤为有用,因为它是一个完全托管的服务,具备企业级安全。
与直接使用 LLM API 开发相比,Azure AI 代理服务的优势包括:
Azure AI 代理服务中的工具分为两类:
代理服务使我们能够将这些工具作为一个 toolset 一起使用。它还利用了threads,用于跟踪特定对话的消息历史。
假设你是 Contoso 公司的销售代理,想开发一个可以回答关于销售数据问题的对话代理。
下图演示了如何使用 Azure AI 代理服务分析销售数据:

要使用服务中的任一工具,我们可以创建客户端并定义工具或工具集。以下 Python 代码演示了该实现。LLM 会查看工具集,并根据用户请求决定使用用户创建的函数 fetch_sales_data_using_sqlite_query 还是预构建的代码解释器。
import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from fetch_sales_data_functions import fetch_sales_data_using_sqlite_query # fetch_sales_data_using_sqlite_query 函数,位于 fetch_sales_data_functions.py 文件中。
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool
project_client = AIProjectClient.from_connection_string(
credential=DefaultAzureCredential(),
conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)
# 初始化工具集
toolset = ToolSet()
# 使用 fetch_sales_data_using_sqlite_query 函数初始化函数调用代理,并将其添加到工具集中
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset.add(fetch_data_function)
# 初始化代码解释器工具并将其添加到工具集中。
code_interpreter = code_interpreter = CodeInterpreterTool()
toolset.add(code_interpreter)
agent = project_client.agents.create_agent(
model="gpt-4o-mini", name="my-agent", instructions="You are helpful agent",
toolset=toolset
)
LLM 动态生成的 SQL 最常见的担忧是安全性,特别是 SQL 注入或恶意操作(如删除或篡改数据库)的风险。尽管这些担忧是合理的,但通过正确配置数据库访问权限可以有效缓解。对于大多数数据库,需将数据库配置为只读。在 PostgreSQL 或 Azure SQL 等数据库服务中,应用应被分配只读(SELECT)角色。 在安全环境中运行应用程序可以进一步增强保护。在企业场景中,数据通常从运营系统中提取并转换到一个只读数据库或数据仓库,且采用用户友好的架构。这种方法确保数据安全、性能和可访问性得到优化,并且应用程序具有受限的只读访问权限。
Join the Azure AI Foundry Discord to meet with other learners, attend office hours and get your AI Agents questions answered.
Understanding Agentic Design Patterns
免责声明:
本文档由AI翻译服务Co-op Translator翻译完成。虽然我们力求准确,但请注意自动翻译可能存在错误或不准确之处。请以文档的原始语言版本为权威来源。对于重要信息,建议使用专业人工翻译。因使用本翻译版本所产生的任何误解或误释,我们不承担任何责任。