跳至内容

使用 Knative 的 LLM 代理:概述

发布时间:2024-07-17

使用 Knative 的 LLM 代理:概述

作者:Calum Murray,红帽软件工程实习生

LLM 是一项变革性技术,它实现了用户与软件系统之间的新型交互方式。但我们如何确保 LLM 为用户提供正确的答案?我们可以将 LLM 用作不仅仅是问答/摘要工具,并让它们代表我们采取行动吗?

LLM 代理和工具调用是该领域中的一些新兴模式,它们允许 LLM 与其他工具和模型交互,解决了大部分正确性问题,并赋予 LLM 为用户采取行动的能力。凭借所有这些优势,我们一直在 Knative 社区寻找方法,以便您可以更轻松地构建 LLM 代理系统,并以更符合您构建系统其余部分的方式构建:声明性和云原生。在这篇博文中,我们将首先介绍 LLM 代理到底是什么以及 LLM 如何调用工具的一些背景信息,然后我们将讨论我们设想 Knative 如何融入这种范式。

什么是代理?

在人工智能领域,代理被定义为能够接收有关其环境的信息并使用该信息做出决策并采取行动以实现目标的系统。在 LLM 的上下文中,代理是一个以 LLM 为核心的系统,它能够在努力回答收到的提示时做出关于采取哪些行动的决策。LLM 代理可以被构建为采取的最常见的行动包括:向用户发送文本或其他媒体,调用工具以帮助回答用户,以及调用另一个代理以帮助回答用户。一般来说,LLM 代理还将具有一个系统提示,解释其作用并为其提供一些关于何时调用工具和/或回复用户的规则。对于大多数代理,控制流程可以显示如下

与其他形式的代理相比,LLM 代理的关键属性之一是,关于是否调用更多工具、再次调用 LLM、调用另一个代理或完成处理的决策是由 LLM 做出的 - 而不是其他形式的逻辑(手工编码或基于 AI/ML)。因此,虽然上图将 LLM 和代理显示为两个独立的实体,但它们通常是同一个实体。每当工具或代理调用完成时,该信息就会发送到 LLM,并且它会根据该信息做出决策。有时 LLM 会决定再次调用自身 - 例如,我们观察到这种情况发生在 LLM 想要在继续调用其他代理或工具之前向用户发送一些文本时。

LLM 如何调用工具?

LLM 调用工具所需的全部内容是某种方法,使 LLM 能够传达它想要调用的工具,以及它想要提供给该工具的任何参数(如果有)。由于 LLM 输出只是一系列标记,因此需要一些外部系统能够从输出中解析此信息,因此 LLM 需要以一致的方式输出结构化或半结构化数据以调用工具。有多种不同的 API 可以执行此操作,并且目前在某种程度上取决于您正在使用的确切模型(因为它们已被训练以不同的方式处理此问题)。出于本博文的目的,我们将专门研究 OpenAI Chat API 来执行此操作,但是对于其他模型,概念通常相同。

在 OpenAI Chat API 中,您需要传入 LLM 能够使用的工具列表。您必须提供的关键信息是工具的name,以及工具可能接受的任何parameters。对于每个参数,还必须提供有关参数类型、用途和名称的信息,以便 LLM 能够尽可能准确地调用工具。例如,以下 JSON 对象是一个有效的工具

{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "Get the current weather in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA",
                },
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            },
            "required": ["location"],
        },
    },
}

此 JSON 对象将包含在对 OpenAI Chat API 的任何调用中,其中 LLM 可能希望选择调用get_current_weather 函数。如果未包含,则 LLM 将不知道该工具,并且无法调用它。

当 LLM 结合一组工具被调用时,它能够决定是否调用任何工具以及使用哪些参数调用哪些工具。检查模型是否想要进行任何工具调用的方法是查看响应消息的tool_calls 属性。例如,在 python 中,您将执行以下操作

response_message = response.choices[0].message
tool_calls = response_message.tool_calls

从那里,您将获得要调用的工具数组以及传递给每个工具的参数。因此,要调用工具,您必须使用 LLM 选择提供的参数调用一些函数或类方法。

使用 Knative 进行 LLM 工具调用

在调查 LLM 如何调用工具时,我们注意到许多工具都是简单的 API 包装器:它们获取 LLM 提供的参数并将它们映射到某种形式的 API 调用中。我们还注意到,由于 LLM 只知道工具的名称以及它可以接受哪些参数,因此任何不是 API 包装器的工具都可以非常轻松地转换为 API 包装器(例如,通过将工具的逻辑放入构建为 Linux 容器的 Knative 函数中,将其部署为 Knative 服务)。但这为什么重要呢?

当所有工具都只是 API 包装器时,在系统中定义任何工具所需的全部内容是

  1. 工具的名称
  2. 工具的描述
  3. 工具接受的参数
  4. 如何将参数映射到工具的 API

请注意,上述列表中的所有内容都只是有关工具的元数据,并且使用这四部分元数据,我们可以定义 LLM 可以调用的任何工具。这是一个极其重要的结果,因为它允许我们泛化工具调用并将工具的定义从调用工具的代码中提取出来。换句话说,我们可以编写一次基于 LLM 消息调用工具的代码,并使用系统中工具的元数据来处理每个单独工具的细节。

在 Knative 中,我们已经有一个用于记录此元数据的自定义资源:EventType!EventType 最初是为了模拟 Knative Eventing 系统中存在的 CloudEvent 类型而创建的,以便 Event Consumer 的开发人员可以轻松地了解可用的资源以及如何使用它们。但是,相同的信息可用于描述服务期望接收的内容(在本例中,作为来自 LLM 的工具调用)。EventType 可以无需任何更改地描述 LLM Agent 可用的工具以及如何调用它们。

例如,假设我有一个服务可以返回某个位置的当前天气。我可以使用 EventType 表示此服务期望的契约,如下所示:

apiVersion: eventing.knative.dev/v1beta2
kind: EventType
metadata:
  name: get.current.weather
spec:
  reference:
    apiVersion: serving.knative.dev/v1
    kind: Service
    name: get-current-weather
  description: "Get the current weather in a given location."
  schemaData: '{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","description":"One of [celsius, farenheit]"}}'
  type: "get.current.weather"

EventType 的描述可以直接映射到 LLM 预期的工具描述,名称可以从 EventType 资源的名称中获取(但请注意:OpenAI 模型要求工具的名称仅使用 a-z、A-Z、0-9、下划线和连字符,最大长度为 64,因此我们可能需要对名称进行一些清理)。对于函数的参数,我们可以通过解析 EventType 的 schemaData 来获取它们。

如上所述,EventType 资源包含所有信息,可以告诉 LLM 如何调用服务。要使用包含工具调用的 LLM 消息调用工具,我们只需使用 schemaData 以及 EventType 中的 type 字段来构建具有正确数据的 CloudEvent,该事件将发送到正确的服务。要将其发送到服务,我们可以:

  1. 从规范解析引用到 URI,并直接调用它。
  2. 使用 Knative Broker 处理所有工具调用到正确 Knative 服务的调度。

鉴于使用 Broker 的诸多优势(例如交付重试、使用 Apache Kafka 等持久消息技术支持 Broker 等),我们选择使用 Broker 将所有工具调用调度到正确的服务。为了获取包含工具调用结果的响应(而不仅仅是 Broker 确认事件已收到),我们在 Broker 前面添加了一个部署,该部署提供请求-回复语义。我们计划通过一个新的自定义资源添加此部署 - 请查看 此处的 issue 以获取更多信息以及其工作原理。

这样,您的 LLM Agent 了解如何调用您的服务之一所需的全部操作是:

  1. 使用选择特定事件类型的过滤器,从您的 Broker 创建到服务的触发器。
  2. 创建一个具有相同 type 的 EventType,该类型使用 schemaData 来描述服务的 API。

总结

在这篇博文中,我们介绍了什么是 LLM Agent、LLM 如何调用工具以及如何使用 Knative 使用元数据简化工具调用。如果您有兴趣了解如何构建使用 Knative 简化工具调用和发现的 LLM Agent 系统,以及我们对这项技术未来发展方向的愿景,请关注我们即将发布的关于此主题的下一篇博文!如果您对此主题有任何疑问,我们邀请您在 CNCF Slack 实例 中的 Knative 频道与我们互动。

我们使用分析和 Cookie 来了解网站流量。为此目的,有关您使用我们网站的信息将与 Google 共享。 了解更多。