青蛙小白
博客 / 2025/02

AI Agents for Beginners笔记: 4.工具使用设计模式

2025/02/19 · — 字 · 阅读约 — 分钟 ·
目录

工具之所以有趣,是因为它们使AI智能体能够拥有更广泛的能力范围。通过添加工具,智能体不再局限于一组有限的动作,而是能够执行多种多样的动作。在本章中,我们将探讨工具使用设计模式,该模式描述了AI智能体如何使用特定工具来实现其目标。

什么是工具使用设计模式?

工具使用设计模式聚焦于赋予大语言模型(LLMs)与外部工具交互的能力,以实现特定目标。工具是可以由智能体执行的代码,用于执行动作。一个工具可以是简单的函数,例如计算器,或者是对第三方服务的API调用,例如查询股票价格或天气预报。在AI智能体的背景下,工具被设计为由智能体根据模型生成的函数调用来执行。

工具使用设计模有哪些试用场景?

AI智能体可以利用工具完成复杂任务、检索信息或做出决策。工具使用设计模式通常用于需要与外部系统(如数据库、Web服务或代码解释器)动态交互的场景。这种能力适用于多种不同的用例,包括:

  • 动态信息检索: 智能体可以查询外部API或数据库以获取最新数据(例如,查询SQLite数据库进行数据分析,获取股票价格或天气信息)。
  • 代码执行与解释: 智能体可以执行代码或脚本来解决数学问题、生成报告或执行模拟。
  • 工作流自动化: 通过集成任务调度器、电子邮件服务或数据管道等工具,自动化重复性或多步骤工作流。
  • 客户支持: 智能体可以与客户关系管理系统(CRM)、票务平台或知识库交互,以解决用户查询。
  • 内容生成与编辑: 智能体可以利用语法检查器、文本摘要工具或内容安全评估器等工具来协助内容创建任务。

实施工具使用设计模式需要哪些元素/构建模块?

这些构建模块使AI智能体能够执行广泛的任务。让我们来看看实施工具使用设计模式所需的关键元素:

  • 函数/工具调用(Function/Tool Calling):这是使大语言模型(LLMs)与工具交互的主要方式。函数或工具是智能体用来执行任务的可重用代码块。这些可以是从简单的计算器函数到调用第三方服务API的复杂操作,例如查询股票价格或天气预报。

接下来,我们将更详细地探讨函数/工具调用。

函数/工具调用

函数调用是我们使大语言模型(LLMs)与工具交互的主要方式。你会经常看到“函数”和“工具”这两个词互换使用,因为“函数”(可重用的代码块)就是智能体用来执行任务的“工具”。为了调用函数的代码,大语言模型必须将用户的请求与函数的描述进行比较。为此,会将包含所有可用函数描述的模式(schema)发送给大语言模型。然后,大语言模型会选择最适合任务的函数,并返回其名称和参数。所选函数被调用,其响应被送回大语言模型,大语言模型利用这些信息来回复用户的请求。

对于开发者来说,要为智能体实现函数调用,你需要:

  1. 一个支持函数调用的大语言模型
  2. 一个包含函数描述的模式(schema)
  3. 每个描述中提到的函数的代码

让我们以获取城市当前时间为例来说明:

  1. 初始化一个支持函数调用的大语言模型:

    并非所有模型都支持函数调用,因此检查你使用的大语言模型是否支持这一点很重要。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"
    )
  2. 创建函数模式

    接下来,我们将定义一个JSON模式,其中包含函数名称、函数功能的描述,以及函数参数的名称和描述。然后,我们将这个模式连同用户查找旧金山时间的请求一起传递给之前创建的客户端。需要注意的是,返回的是工具调用(tool call)而不是问题的最终答案。如前所述,大语言模型返回其为任务选择的函数名称以及将传递给它的参数。

    # Function description for the model to read
    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"],
                },
            }
        }
    ]
    
    # Initial user message
    messages = [{"role": "user", "content": "What's the current time in San Francisco"}] 
    
    # First API call: Ask the model to use the function
      response = client.chat.completions.create(
          model=deployment_name,
          messages=messages,
          tools=tools,
          tool_choice="auto",
      )
    
      # Process the model's response
      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')])
  3. 执行任务所需的函数代码:

    现在,大语言模型已经选择了需要运行的函数,接下来需要实现并执行执行任务的代码。我们可以用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"})
    # Handle function calls
     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.")  
    
     # Second API call: Get the final response from the model
     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.探索Agent框架中所学,智能体框架为我们提供了预构建的构建模块来实现工具使用。

使用智能体框架的工具使用示例

以下是一些使用不同智能体框架实现工具使用设计模式的示例:

Semantic Kernel

Semantic Kernel是一个面向 .NET、Python 和 Java 开发者的开源 AI 框架,适用于处理大语言模型(LLMs)。它通过一个名为序列化的过程,自动向模型描述你的函数及其参数,从而简化了函数调用的使用过程。它还处理模型与你的代码之间的来回通信。使用像 Semantic Kernel 这样的智能体框架的另一个优势是,它允许你访问预构建的工具,例如文件搜索代码解释器

下图说明了使用 Semantic Kernel 进行函数调用的过程:

在Semantic Kernel中,函数/工具被称为插件(Plugins)。我们可以通过将之前看到的 get_current_time 函数转换为一个带有该函数的类,来将其变成一个插件。我们还可以导入 kernel_function 装饰器,它接受函数的描述。然后,当你使用GetCurrentTimePlugin创建一个Kernel(内核)时,Kernel会自动序列化该函数及其参数,在此过程中创建发送给大语言模型的模式(schema)。

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

# Create the kernel
kernel = Kernel()

# Create the plugin
get_current_time_plugin = GetCurrentTimePlugin(location)

# Add the plugin to the kernel
kernel.add_plugin(get_current_time_plugin)

Azure AI Agent Service

Azure AI Agent Service是一个较新的智能体框架,旨在赋能开发者安全地构建、部署和扩展高质量且可扩展的AI智能体,而无需管理底层的计算和存储资源。它特别适用于企业应用程序,因为它是一个具备企业级安全性的完全托管服务。

与直接使用大语言模型API开发相比,Azure AI Agent Service提供了一些优势,包括:

  • 自动工具调用 – 无需解析工具调用、调用工具并处理响应;这一切现在都在服务器端完成
  • 安全管理的数据 – 无需自己管理对话状态,你可以使用Thread来存储所需的所有信息
  • 开箱即用的工具 – 你可以用来与数据源交互的工具,例如Bing, Azure AI Search, and Azure Functions。

Azure AI智能体服务中可用的工具可以分为两类:

  1. 知识工具:
  1. 动作工具:

智能体服务允许我们将这些工具作为一个工具集(toolset)一起使用。它还利用Thread来跟踪特定对话的消息历史。

想象你是一家名为Contoso的公司的销售代理。你想开发一个能够回答有关销售数据的对话智能体。

下图展示了如何使用Azure AI Agent Service来分析你的销售数据:

要使用服务中的任何这些工具,我们可以创建一个客户端并定义一个工具或工具集。为了实际实现这一点,我们可以使用以下Python代码。大语言模型将能够查看工具集,并根据用户请求决定是使用用户创建的函数fetch_sales_data_using_sqlite_query,还是使用预构建的代码解释器。

import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from fecth_sales_data_functions import fetch_sales_data_using_sqlite_query # fetch_sales_data_using_sqlite_query function which can be found in a fetch_sales_data_functions.py file.
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool

project_client = AIProjectClient.from_connection_string(
    credential=DefaultAzureCredential(),
    conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)

# Initialize function calling agent with the fetch_sales_data_using_sqlite_query function and adding it to the toolset
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset = ToolSet()
toolset.add(fetch_data_function)

# Initialize Code Interpreter tool and adding it to the toolset. 
code_interpreter = code_interpreter = CodeInterpreterTool()
toolset = ToolSet()
toolset.add(code_interpreter)

agent = project_client.agents.create_agent(
    model="gpt-4o-mini", name="my-agent", instructions="You are helpful agent", 
    toolset=toolset
)

使用工具使用设计模式构建可信AI智能体有哪些特殊考虑?

由大型语言模型(LLMs)动态生成的SQL常见的安全性问题,特别是SQL注入或恶意操作的风险,例如删除或篡改数据库。虽然这些担忧是合理的,但通过正确配置数据库访问权限可以有效缓解这些问题。对于大多数数据库来说,这涉及将数据库配置为只读。对于像PostgreSQL或Azure SQL这样的数据库服务,应用程序应被分配一个只读(SELECT)角色。

在安全环境中运行应用程序可以进一步增强保护。在企业场景中,数据通常从运营系统提取并转换到一个只读数据库或数据仓库中,并采用用户友好的模式。这种方法确保数据安全、性能和可访问性得到优化,同时应用程序仅具有受限的只读访问权限。

其他资源

参考

评论