重磅上线!AiDocZh.com发布全新AutoGen中文文档,带你解锁大模型多Agent对话新时代

重磅上线!AiDocZh.com发布全新AutoGen中文文档,带你解锁大模型多Agent对话新时代

一、AutoGen中文文档

网站地址http://www.aidoczh.com/autogen/

二、AutoGen简介

AutoGen 是一个框架,可以使用多个代理进行对话,解决任务,从而实现 LLM 应用的开发。AutoGen 代理是可定制、可对话的,并且可以无缝地允许人类参与。它们可以在使用 LLM、人类输入和工具的各种模式下运行。

主要特点

  • AutoGen 可以基于多代理对话的方式,以最小的工作量构建下一代 LLM 应用。它简化了复杂 LLM 工作流的编排、自动化和优化。它最大化了 LLM 模型的性能,并克服了它们的弱点。
  • 它支持多样化的对话模式,适用于复杂的工作流程。开发人员可以使用可定制和可对话的代理,使用 AutoGen 构建涉及对话自治性、代理数量和代理对话拓扑的各种对话模式。
  • 它提供了一系列具有不同复杂性的工作系统。这些系统涵盖了各种领域和复杂度的应用。这展示了 AutoGen 如何轻松支持多样化的对话模式。

AutoGen 的研究成果来自于 Microsoft、宾夕法尼亚州立大学和华盛顿大学的合作研究项目

快速入门

pip install pyautogen
import os
from autogen import AssistantAgent, UserProxyAgent
llm_config = {"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}
assistant = AssistantAgent("assistant", llm_config=llm_config)
user_proxy = UserProxyAgent("user_proxy", code_execution_config=False)
# 开始对话
user_proxy.initiate_chat(
    assistant,
    message="Tell me a joke about NVDA and TESLA stock prices.",
)

三、为什么选择AutoGen?

虽然有很多关于代理的定义,但在AutoGen中,代理是一个可以发送消息、接收消息并使用模型、工具、人工输入或它们的混合生成回复的实体。这种抽象不仅允许代理模拟现实世界和抽象实体(如人和算法),还简化了作为代理之间协作的复杂工作流程的实现。

此外,AutoGen是可扩展和可组合的:您可以使用可自定义的组件扩展一个简单的代理,并创建可以组合这些代理并为更复杂的代理提供动力的工作流程,从而实现模块化且易于维护的实现。

最重要的是,AutoGen是由一群充满活力的研究人员和工程师开发的。它融合了多代理系统的最新研究成果,并在许多实际应用中得到了应用,包括代理平台、广告、AI员工、博客/文章写作、区块链、通过野火计算烧毁区域、客户支持、网络安全、数据分析、辩论、教育、金融、游戏、法律咨询、研究、机器人技术、销售/营销、社会模拟、软件工程、软件安全、供应链、T恤设计、训练数据生成、YouTube服务…

代理

在AutoGen中,代理是一个可以在其环境中与其他代理发送和接收消息的实体。代理可以由模型(例如像GPT-4这样的大型语言模型)、代码执行器(例如IPython内核)、人工或这些和其他可插拔和可自定义组件的组合来提供动力。

这样的代理的一个示例是内置的ConversableAgent,它支持以下组件:

  1. 一个LLM列表
  2. 一个代码执行器
  3. 一个函数和工具执行器
  4. 一个用于保持人工参与的组件

您可以打开或关闭每个组件,并对其进行自定义以适应您的需求。 对于高级用户,您可以使用registered_reply向代理添加其他组件。

例如,LLM(Language Model)使代理能够以自然语言交流并在结构化和非结构化文本之间进行转换。以下示例展示了一个带有开启了 GPT-4 LLM 的 ConversableAgent,并关闭了其他组件:

import os
from autogen import ConversableAgent
agent = ConversableAgent(
    "chatbot",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ.get("OPENAI_API_KEY")}]},
    code_execution_config=False,  # 关闭代码执行,默认情况下是关闭的。
    function_map=None,  # 没有注册的函数,默认情况下为 None。
    human_input_mode="NEVER",  # 从不要求人类输入。
)

llm_config 参数包含了 LLM 的配置列表。详见LLM 配置了解更多细节。

您可以使用 generate_reply 方法向该代理提问并生成回复:

reply = agent.generate_reply(messages=[{"content": "Tell me a joke.", "role": "user"}])
print(reply)
当然,这里有一个轻松的笑话给你:
为什么科学家不相信原子?
因为它们构成了一切!

角色和对话

在 AutoGen 中,您可以为代理分配角色,并让它们参与对话或相互聊天。对话是代理之间交换的一系列消息。您可以利用这些对话来推进任务的进展。例如,在下面的示例中,我们通过设置 system_message 为两个代理分配不同的角色。

cathy = ConversableAgent(
    "cathy",
    system_message="Your name is Cathy and you are a part of a duo of comedians.",
    llm_config={"config_list": [{"model": "gpt-4", "temperature": 0.9, "api_key": os.environ.get("OPENAI_API_KEY")}]},
    human_input_mode="NEVER",  # 从不要求人类输入。
)
joe = ConversableAgent(
    "joe",
    system_message="Your name is Joe and you are a part of a duo of comedians.",
    llm_config={"config_list": [{"model": "gpt-4", "temperature": 0.7, "api_key": os.environ.get("OPENAI_API_KEY")}]},
    human_input_mode="NEVER",  # 从不要求人类输入。
)

现在我们有了两个喜剧演员代理,我们可以让他们开始一场喜剧表演。可以使用 initiate_chat 方法来实现。我们将 max_turns 设置为 2,以保持对话的简短。

result = joe.initiate_chat(cathy, message="Cathy, tell me a joke.", max_turns=2)
joe (对 cathy):
Cathy,给我讲个笑话吧。
------
cathy (对 joe):
好的,给你讲一个:
为什么科学家不相信原子?
因为它们构成了一切!
------
joe (对 cathy):
哈哈,这个真不错,Cathy!轮到我了。
为什么我们在农场上从不说秘密?
因为土豆有眼睛,玉米有耳朵,豆子有茎。
------
cathy (对 joe):
哈哈,太棒了!农场绝对不是保守秘密的地方。轮到我了。
为什么自行车不能自己站起来?
因为它太累了!
------
喜剧演员们互相碰撞!

四、终止代理之间的对话

在本章中,我们将探讨如何终止 AutoGen 代理之间的对话。

为什么这很重要? 这是因为在任何复杂的自主工作流中,知道何时停止工作流是至关重要的。例如,当任务完成时,或者当流程消耗了足够的资源并且需要停止或采用不同的策略(如用户干预)时。因此,AutoGen 本身支持多种机制来终止对话。

如何使用 AutoGen 控制终止?目前有两种广泛的机制来控制代理之间对话的终止:

  1. initiate_chat 中指定参数:在启动对话时,您可以定义确定对话何时结束的参数。
  2. 配置代理以触发终止:在定义各个代理时,您可以指定允许代理根据特定(可配置的)条件终止对话的参数。

initiate_chat 中的参数

在上一章中,当我们使用 max_turns 参数限制回合数时,实际上就演示了这一点。如果我们将 max_turns 增加到 3,可以注意到对话需要更多的回合才能终止:

import os
from autogen import ConversableAgent
cathy = ConversableAgent(
    "cathy",
    system_message="你的名字是 Cathy,你是一对喜剧演员中的一员。",
    llm_config={"config_list": [{"model": "gpt-4", "temperature": 0.9, "api_key": os.environ.get("OPENAI_API_KEY")}]},
    human_input_mode="NEVER",  # 从不要求人类输入。
)
joe = ConversableAgent(
    "joe",
    system_message="你的名字是 Joe,你是一对喜剧演员中的一员。",
    llm_config={"config_list": [{"model": "gpt-4", "temperature": 0.7, "api_key": os.environ.get("OPENAI_API_KEY")}]},
    human_input_mode="NEVER",  # 从不要求人类输入。
)
result = joe.initiate_chat(cathy, message="Cathy,给我讲个笑话。", max_turns=2)
joe(对cathy):
Cathy,给我讲个笑话吧。
------
cathy(对joe):
好的,给你讲一个:
为什么科学家不相信原子?
因为它们组成了一切!
------
joe(对cathy):
哈哈,这个真好笑,Cathy!轮到我了。
为什么我们从不在农场里说秘密?
因为土豆有眼睛,玉米有耳朵,豆子有茎。
------
cathy(对joe):
哈哈,太棒了!农场绝对不是保守秘密的地方。好了,轮到我了。
为什么自行车不能自己站起来?
因为它太累了!
------
result = joe.initiate_chat(
    cathy, message="Cathy, tell me a joke.", max_turns=3, max_consecutive_auto_reply=1
)
joe = ConversableAgent(
    "joe",
    system_message="你叫Joe,是一对喜剧演员中的一员。",
    llm_config={"config_list": [{"model": "gpt-4", "temperature": 0.7, "api_key": os.environ.get("OPENAI_API_KEY")}]},
    human_input_mode="NEVER",  # 不会要求人类输入。
    max_consecutive_auto_reply=1,  # 限制连续自动回复的次数。
)
result = joe.initiate_chat(cathy, message="Cathy,给我讲个笑话。")
joe (对 cathy):
Cathy,给我讲个笑话吧。
------
cathy (对 joe):
好的,给你讲一个:
为什么科学家不相信原子?
因为它们构成了一切!
------
joe (对 cathy):
哈哈,这个很好笑,Cathy!轮到我了。
为什么我们从不在农场里说秘密?
因为土豆有眼睛,玉米有耳朵,豆子有茎。
------
cathy (对 joe):
哈哈,太棒了!农场绝对不是保守秘密的地方。轮到我了。
为什么自行车不能自己站起来?
因为它太累了!
------

使用 `is_termination_msg

让我们将终止消息设置为“再见”,看看对话如何终止。

joe = ConversableAgent(
    "joe",
    system_message="你叫 Joe,是一对喜剧演员中的一员。",
    llm_config={"config_list": [{"model": "gpt-4", "temperature": 0.7, "api_key": os.environ.get("OPENAI_API_KEY")}]},
    human_input_mode="NEVER",  # 不会要求人类输入。
    is_termination_msg=lambda msg: "再见" in msg["content"],
)
result = joe.initiate_chat(cathy, message="Cathy,给我讲个笑话,然后说“再见”。")
joe (对 cathy):
Cathy,给我讲个笑话,然后说“再见”。
--------------------------------------------------------------------------------
cathy (对 joe):
为什么科学家不相信原子?
因为它们构成了一切!
再见!
--------------------------------------------------------------------------------

注意对话是如何根据 cathy 的消息内容结束的!

五、允许人类对代理进行反馈

在前两章中,我们介绍了 ConversableAgent 类,并展示了如何使用它创建可以完成任务的自主(human_input_mode=NEVER)代理。我们还展示了如何正确终止代理之间的对话。

但是,许多应用程序可能需要将人类与代理放在一个循环中。例如,允许人类反馈来引导代理朝正确的方向发展、指定目标等。在本章中,我们将展示 AutoGen 如何支持人类干预。

在 AutoGen 的 ConversableAgent 中,人类循环组件位于自动回复组件之前。它可以拦截传入的消息,并决定是将其传递给自动回复组件还是提供人类反馈。下图展示了该设计。

人类循环组件可以通过 human_input_mode 参数进行自定义。我们将在下面的章节中向您展示如何使用它。

人类输入模式

目前,AutoGen 支持三种人类输入模式。模式是通过 ConversableAgenthuman_input_mode 参数指定的。这三种模式分别是:

  1. NEVER:从不请求人类输入。
  2. TERMINATE(默认):仅在满足终止条件时请求人类输入。请注意,在此模式下,如果人类选择拦截并回复,对话将继续进行,并且 max_consecutive_auto_reply 使用的计数器将被重置。
  3. ALWAYS:始终请求人类输入,人类可以选择跳过并触发自动回复,拦截并提供反馈,或终止对话。请注意,在此模式下,基于 max_consecutive_auto_reply 的终止将被忽略。

前面的章节已经展示了许多 human_input_modeNEVER 的情况的示例。下面我们再次展示一个这种情况的示例,并展示当将此模式设置为 ALWAYSNEVER 时的区别。

人类输入模式 = NEVER[]

在此模式下,从不请求人类输入,并使用终止条件来终止对话。当您希望代理完全自主地行动时,此模式非常有用。

以下是使用此模式运行简单的猜数字游戏的示例,其中两个代理参与,终止消息设置为检查是否猜对了数字。

import os
from autogen import ConversableAgent
# 创建一个名为 agent_with_number 的 ConversableAgent 对象
agent_with_number = ConversableAgent(
    "agent_with_number",
    system_message="你正在玩一个猜数字的游戏。你心里想的数字是53,我会尝试猜出来。如果我猜得太高,请说'太高';如果我猜得太低,请说'太低'。",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
    is_termination_msg=lambda msg: "53" in msg["content"],  # 如果对方猜中了数字,终止对话
    human_input_mode="NEVER",  # 不需要人类输入
)
# 创建一个名为 agent_guess_number 的 ConversableAgent 对象
agent_guess_number = ConversableAgent(
    "agent_guess_number",
    system_message="我心里有一个数字,你要猜出来。如果我说'太高',你应该猜一个更小的数字。如果我说'太低',你应该猜一个更大的数字。",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
    human_input_mode="NEVER",
)
# agent_with_number 发起对话,并将 agent_guess_number 作为对话对象
result = agent_with_number.initiate_chat(
    agent_guess_number,
    message="我心里有一个1到100之间的数字。猜猜看吧!",
)
agent_with_number (对 agent_guess_number 说):
我心里有一个数字,范围在1到100之间。猜一下吧!
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是50吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number 说):
太低了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是75吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number 说):
太高了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是63吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number 说):
太高了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是57吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number 说):
太高了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是54吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number 说):
太高了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是52吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number 说):
太低了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number 说):
是53吗?
--------------------------------------------------------------------------------

太棒了!游戏结束了。猜测的代理使用了二分查找,非常高效!你可以看到,在猜测的代理说出正确的数字后,对话被终止了,这触发了基于消息的终止条件。

人类输入模式 = `ALWAYS

在这种模式下,总是要求人类输入,人类可以选择跳过、拦截或终止对话。让我们通过与之前的代理一起玩同样的游戏,以人类的身份参与游戏,我们将成为猜测数字的代理,并与之前的代理进行对战。

human_proxy = ConversableAgent(
    "human_proxy",
    llm_config=False,  # 人类代理不使用LLM
    human_input_mode="ALWAYS",  # 总是要求人类输入
)
# 与之前的代理开始对话,初始猜测为10。
result = human_proxy.initiate_chat(
    agent_with_number,  # 这是之前的代理,拥有一个数字
    message="10",
)
human_proxy (to agent_with_number):
10
--------------------------------------------------------------------------------
agent_with_number (to human_proxy):
太小了。
--------------------------------------------------------------------------------
human_proxy (to agent_with_number):
79
--------------------------------------------------------------------------------
agent_with_number (to human_proxy):
太大了。
--------------------------------------------------------------------------------
human_proxy (to agent_with_number):
76
--------------------------------------------------------------------------------
agent_with_number (to human_proxy):
太大了。
--------------------------------------------------------------------------------
human_proxy (to agent_with_number):
我放弃了。
--------------------------------------------------------------------------------
agent_with_number (to human_proxy):
没关系!我想的数字是53。
--------------------------------------------------------------------------------

如果你运行上面的代码,每次轮到你说话时都会提示你输入回答。你可以看到,人类在猜测数字方面并不是很好…但是嘿,代理最终还是很好心地给出了数字。

人类输入模式 = `TERMINATE

在这种模式下,只有在满足终止条件时才会要求人类输入。如果人类选择拦截并回复,计数器将被重置;如果人类选择跳过,将使用自动回复机制;如果人类选择终止,对话将被终止。

让我们再次玩同样的游戏,但这次猜测的代理只有两次机会猜测数字,如果失败,将要求人类提供反馈,并且对话将被终止。 猜数字的代理获得了两次猜测的机会。如果最终猜对了数字,对话将终止。

agent_with_number = ConversableAgent(
    "agent_with_number",
    system_message="你正在玩一个猜数字的游戏。在第一局中,你心里想的数字是53,我会试着猜出来。如果我猜得太高,请说'太高',如果我猜得太低,请说'太低'。",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
    max_consecutive_auto_reply=1,  # 在要求人类输入之前,连续自动回复的最大次数
    is_termination_msg=lambda msg: "53" in msg["content"],  # 如果其他代理猜对了数字,终止对话
    human_input_mode="TERMINATE",  # 直到游戏终止前一直要求人类输入
)
agent_guess_number = ConversableAgent(
    "agent_guess_number",
    system_message="我心里有一个数字,你要试着猜出来。如果我说'太高',你应该猜一个更小的数字。如果我说'太低',你应该猜一个更大的数字。",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
    human_input_mode="NEVER",
)
result = agent_with_number.initiate_chat(
    agent_guess_number,
    message="我心里有一个1到100之间的数字。猜猜看吧!",
)
agent_with_number (对 agent_guess_number):
我有一个介于1和100之间的数字。猜猜看吧!
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是50吗?
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
agent_with_number (对 agent_guess_number):
太低了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是75吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number):
太高了,朋友。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是60吗?
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
agent_with_number (对 agent_guess_number):
太高了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是55吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number):
还是太高了,但你很接近了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是52吗?
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
agent_with_number (对 agent_guess_number):
太低了。
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是54吗?
--------------------------------------------------------------------------------
agent_with_number (对 agent_guess_number):
快了!
--------------------------------------------------------------------------------
agent_guess_number (对 agent_with_number):
是53吗?
--------------------------------------------------------------------------------

在之前的对话中,

  1. 当代理猜测“74”时,人类说:“太高了,我的朋友。”
  2. 当代理猜测“55”时,人类说:“还是太高了,但你很接近了。”
  3. 当代理猜测“54”时,人类说:“快了!”

每次代理给出一个数字后,都会要求人类提供反馈。一旦人类提供了反馈,计数器就会重置。在代理正确猜到“53”后,对话终止。

六、工具使用

在前一章中,我们探索了代码执行器,它赋予了代理程序编程的超能力。代理程序编写任意代码是有用的,然而,控制代理程序编写的代码可以是具有挑战性的。这就是工具的作用。

工具是预定义的函数,代理程序可以使用它们。代理程序可以调用工具来执行操作,例如搜索网络、进行计算、读取文件或调用远程 API,而不是编写任意代码。因为您可以控制哪些工具对代理程序可用,所以您可以控制代理程序可以执行的操作。

NOTE

目前只有支持 OpenAI 兼容工具调用 API 的 LLMs 可以使用工具。

创建工具

工具可以像普通的 Python 函数一样创建。例如,让我们创建一个计算器工具,它一次只能执行一个操作。

from typing import Annotated, Literal
Operator = Literal["+", "-", "*", "/"]
def calculator(a: int, b: int, operator: Annotated[Operator, "operator"]) -> int:
    if operator == "+":
        return a + b
    elif operator == "-":
        return a - b
    elif operator == "*":
        return a * b
    elif operator == "/":
        return int(a / b)
    else:
        raise ValueError("无效的操作符")

上面的函数接受三个参数:ab 是要进行操作的整数;operator 是要执行的操作。我们使用类型提示来定义参数和返回值的类型。

TIP

始终使用类型提示来定义参数和返回值的类型,因为它们为代理程序提供了关于工具使用的有用提示。

注册工具

一旦您创建了一个工具,您可以将其注册到参与对话的代理程序中。

import os
from autogen import ConversableAgent
# 首先定义一个助手代理,用于提供工具调用建议。
assistant = ConversableAgent(
    name="助手",
    system_message="你是一个有用的AI助手。你可以帮助进行简单的计算。当任务完成时,请返回'TERMINATE'。",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)
# 用户代理用于与助手代理进行交互并执行工具调用。
user_proxy = ConversableAgent(
    name="用户",
    llm_config=False,
    is_termination_msg=lambda msg: msg.get("content") is not None and "TERMINATE" in msg["content"],
    human_input_mode="NEVER",
)
# 使用助手代理注册工具签名。
assistant.register_for_llm(name="calculator", description="一个简单的计算器")(calculator)
# 使用用户代理注册工具函数。
user_proxy.register_for_execution(name="calculator")(calculator)
<function __main__.calculator(a: int, b: int, operator: Annotated[Literal['+', '-', '*', '/'], 'operator']) -> int>

在上面的代码中,我们将 calculator 函数注册为助手和用户代理的工具。我们还为工具提供了一个名称和描述,以便助手代理能够理解其用途。

TIP

始终为工具提供清晰简明的描述,这有助于助手的底层LLM理解工具的用途。

与代码执行器类似,工具必须在至少两个代理中注册,才能在对话中发挥作用。通过 register_for_llm 将工具的签名注册给代理,代理可以调用该工具;通过 register_for_execution 将工具的函数对象注册给代理,代理可以执行该工具的函数。

或者,您可以使用 autogen.register_function 函数一次性将工具注册给两个代理。

from autogen import register_function
# 将 calculator 函数注册给两个代理。
register_function(
    calculator,
    caller=assistant,  # 助手代理可以建议调用 calculator。
    executor=user_proxy,  # 用户代理可以执行 calculator 的调用。
    name="calculator",  # 默认情况下,使用函数名作为工具名。
    description="一个简单的计算器",  # 工具的描述。
)

使用工具

一旦工具注册成功,我们就可以在对话中使用它。在下面的代码中,我们要求助手使用 calculator 工具进行一些算术计算。

chat_result = user_proxy.initiate_chat(assistant, message="What is (44232 + 13312 / (232 - 32)) * 5?")
用户 (对助手说):
(44232 + 13312 / (232 - 32)) * 5 等于多少?
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
助手 (对用户说):
***** 建议的工具调用 (call_4rElPoLggOYJmkUutbGaSTX1): calculator *****
参数: 
{
  "a": 232,
  "b": 32,
  "operator": "-"
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 执行函数 calculator...
用户 (对助手说):
用户 (对助手说):
***** 调用工具的响应 (call_4rElPoLggOYJmkUutbGaSTX1) *****
200
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
助手 (对用户说):
***** 建议的工具调用 (call_SGtr8tK9A4iOCJGdCqkKR2Ov): calculator *****
参数: 
{
  "a": 13312,
  "b": 200,
  "operator": "/"
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 执行函数 calculator...
用户 (对助手说):
用户 (对助手说):
***** 调用工具的响应 (call_SGtr8tK9A4iOCJGdCqkKR2Ov) *****
66
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
助手 (对用户说):
***** 建议的工具调用 (call_YsR95CM1Ice2GZ7ZoStYXI6M): calculator *****
参数: 
{
  "a": 44232,
  "b": 66,
  "operator": "+"
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 执行函数 calculator...
用户 (对助手说):
用户 (对助手说):
***** 调用工具的响应 (call_YsR95CM1Ice2GZ7ZoStYXI6M) *****
44298
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
助手 (对用户说):
***** 建议的工具调用 (call_oqZn4rTjyvXYcmjAXkvVaJm1): calculator *****
参数: 
{
  "a": 44298,
  "b": 5,
  "operator": "*"
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 执行函数 calculator...
用户 (对助手说):
用户 (对助手说):
***** 调用工具的响应 (call_oqZn4rTjyvXYcmjAXkvVaJm1) *****
221490
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> 使用自动回复...
助手 (对用户说):
计算结果为 221490。终止。
--------------------------------------------------------------------------------

让我们验证一下答案:

(44232 + int(13312 / (232 - 32))) * 5
221490

答案是正确的。你可以看到助手能够理解工具的使用方法并正确进行计算。

工具模式

如果你熟悉 OpenAI 的工具使用 API,你可能会想知道为什么我们没有创建工具模式。实际上,工具模式是根据函数签名和类型提示自动生成的。你可以通过检查 agent 的 llm_config 属性来查看工具模式。

assistant.llm_config["tools"]
[{'type': 'function',
  'function': {'description': '一个简单的计算器',
   'name': 'calculator',
   'parameters': {'type': 'object',
    'properties': {'a': {'type': 'integer', 'description': 'a'},
     'b': {'type': 'integer', 'description': 'b'},
     'operator': {'enum': ['+', '-', '*', '/'],
      'type': 'string',
      'description': 'operator'}},
    'required': ['a', 'b', 'operator']}}}]

你可以看到工具模式是根据函数签名、类型提示和描述自动生成的。这就是为什么使用类型提示并为工具提供清晰的描述非常重要,因为LLM使用它们来理解工具的使用方法。

你还可以使用 Pydantic 模型来提供更复杂的类型模式。在下面的示例中,我们使用 Pydantic 模型来定义计算器的输入。

from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
    a: Annotated[int, Field(description="第一个数字。")]
    b: Annotated[int, Field(description="第二个数字。")]
    operator: Annotated[Operator, Field(description="运算符。")]
def calculator(input: Annotated[CalculatorInput, "计算器的输入。"]) -> int:
    if input.operator == "+":
        return input.a + input.b
    elif input.operator == "-":
        return input.a - input.b
    elif input.operator == "*":
        return input.a * input.b
    elif input.operator == "/":
        return int(input.a / input.b)
    else:
        raise ValueError("无效的运算符")

与之前一样,我们使用名称 "calculator" 将工具注册到代理中。

TIP

将工具注册到相同的名称将覆盖之前的工具。

assistant.register_for_llm(name="calculator", description="一个接受嵌套表达式作为输入的计算器工具")(
    calculator
)
user_proxy.register_for_execution(name="calculator")(calculator)
<function __main__.calculator(input: typing.Annotated[__main__.CalculatorInput, '计算器的输入。']) -> int>

你可以看到工具模式已经更新以反映新的类型模式。

assistant.llm_config["tools"]
[{'type': 'function',
  'function': {'description': '一个接受嵌套表达式作为输入的计算器工具',
   'name': 'calculator',
   'parameters': {'type': 'object',
    'properties': {'input': {'properties': {'a': {'description': '第一个数字。',
        'title': 'A',
        'type': 'integer'},
       'b': {'description': '第二个数字。',
        'title': 'B',
        'type': 'integer'},
       'operator': {'description': '运算符。',
        'enum': ['+', '-', '*', '/'],
        'title': 'Operator',
        'type': 'string'}},
      'required': ['a', 'b', 'operator'],
      'title': 'CalculatorInput',
      'type': 'object',
      'description': '计算器的输入。'}},
    'required': ['input']}}}]

让我们在对话中使用这个工具。

chat_result = user_proxy.initiate_chat(assistant, message="(1423 - 123)/ 3 + (32 + 23)* 5 是多少?")
User (to Assistant):
What is (1423 - 123) / 3 + (32 + 23) * 5?
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
***** Suggested tool call (call_Uu4diKtxlTfkwXuY6MmJEb4E): calculator *****
Arguments: 
{
    "input": {
        "a": (1423 - 123) / 3,
        "b": (32 + 23) * 5,
        "operator": "+"
    }
}
***************************************************************************
--------------------------------------------------------------------------------
User (to Assistant):
User (to Assistant):
***** Response from calling tool (call_Uu4diKtxlTfkwXuY6MmJEb4E) *****
Error: Expecting value: line 1 column 29 (char 28)
 You argument should follow json format.
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
I apologize for the confusion, I seem to have made a mistake. Let me recalculate the expression properly.
First, we need to do the calculations within the brackets. So, calculating (1423 - 123), (32 + 23), and then performing remaining operations.
***** Suggested tool call (call_mx3M3fNOwikFNoqSojDH1jIr): calculator *****
Arguments: 
{
    "input": {
        "a": 1423,
        "b": 123,
        "operator": "-"
    }
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> EXECUTING FUNCTION calculator...
User (to Assistant):
User (to Assistant):
***** Response from calling tool (call_mx3M3fNOwikFNoqSojDH1jIr) *****
1300
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
***** Suggested tool call (call_hBAL2sYi6Y5ZtTHCNPCmxdN3): calculator *****
Arguments: 
{
    "input": {
        "a": 32,
        "b": 23,
        "operator": "+"
    }
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> EXECUTING FUNCTION calculator...
User (to Assistant):
User (to Assistant):
***** Response from calling tool (call_hBAL2sYi6Y5ZtTHCNPCmxdN3) *****
55
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
***** Suggested tool call (call_wO3AP7EDeJvsVLCpvv5LohUa): calculator *****
Arguments: 
{
    "input": {
        "a": 1300,
        "b": 3,
        "operator": "/"
    }
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> EXECUTING FUNCTION calculator...
User (to Assistant):
User (to Assistant):
***** Response from calling tool (call_wO3AP7EDeJvsVLCpvv5LohUa) *****
433
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
***** Suggested tool call (call_kQ2hDhqem8BHNlaHaE9ezvvQ): calculator *****
Arguments: 
{
    "input": {
        "a": 55,
        "b": 5,
        "operator": "*"
    }
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> EXECUTING FUNCTION calculator...
User (to Assistant):
User (to Assistant):
***** Response from calling tool (call_kQ2hDhqem8BHNlaHaE9ezvvQ) *****
275
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
***** Suggested tool call (call_1FLDUdvAZmjlSD7g5GFFJOpO): calculator *****
Arguments: 
{
    "input": {
        "a": 433,
        "b": 275,
        "operator": "+"
    }
}
***************************************************************************
--------------------------------------------------------------------------------
>>>>>>>> EXECUTING FUNCTION calculator...
User (to Assistant):
User (to Assistant):
***** Response from calling tool (call_1FLDUdvAZmjlSD7g5GFFJOpO) *****
708
**********************************************************************
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
Assistant (to User):
The calculation result of the expression (1423 - 123) / 3 + (32 + 23) * 5 is 708. Let's proceed to the next task.
TERMINATE
--------------------------------------------------------------------------------

让我们验证一下答案:

int((1423 - 123) / 3) + (32 + 23) * 5
708

再次,答案是正确的。你可以看到助手能够理解新的工具模式并正确执行计算。

如何在单个代理中隐藏工具使用和代码执行?

有时候,我们希望将工具使用隐藏在单个代理内部,即工具调用和工具响应消息对外部不可见,代理以“内部独白”的形式响应外部消息并使用工具。例如,你可能想构建一个类似于OpenAI的助手的代理,它在内部执行内置工具。

要实现这一点,你可以使用嵌套对话。嵌套对话允许你在代理内部创建“内部独白”,以调用和执行工具。这对于代码执行也适用。请参考nested chats for tool use中的示例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值