创建组件¶
最小组件¶
组件可用于实现各种功能,例如向提示提供消息、执行代码或与外部服务交互。
组件*是一个继承自 AgentComponent
或实现一个或多个*协议*的类。每个*协议*都继承自 AgentComponent
,因此一旦你的类继承了任何*协议,它就会自动成为一个*组件*。
class MyComponent(AgentComponent):
pass
这已经是一个有效的组件,但它还没有做任何事情。要为其添加一些功能,你需要实现一个或多个*协议*。
让我们创建一个简单的组件,向代理的提示中添加“Hello World!”消息。为此,我们需要在我们的组件中实现 MessageProvider
协议。MessageProvider
是一个带有 get_messages
方法的接口:
# 不再需要继承 AgentComponent,因为 MessageProvider 已经继承了它
class HelloComponent(MessageProvider):
def get_messages(self) -> Iterator[ChatMessage]:
yield ChatMessage.user("Hello World!")
现在我们可以将我们的组件添加到现有代理中,或者创建一个新的 Agent 类并在其中添加它:
class MyAgent(Agent):
self.hello_component = HelloComponent()
每次代理需要构建新提示时,都会调用 get_messages
,生成的消息将相应地添加。
传递数据给组件和在组件之间传递数据¶
由于组件是常规类,你可以通过 __init__
方法将数据(包括其他组件)传递给它们。例如,我们可以传递一个配置对象,然后在需要时从中检索 API 密钥:
class DataComponent(MessageProvider):
def __init__(self, config: Config):
self.config = config
def get_messages(self) -> Iterator[ChatMessage]:
if self.config.openai_credentials.api_key:
yield ChatMessage.system("API key found!")
else:
yield ChatMessage.system("API key not found!")
注意
尚未实现组件特定的配置处理。
配置组件¶
可以使用 pydantic 模型配置组件。
要使组件可配置,它必须继承自 ConfigurableComponent[BM]
,其中 BM
是继承自 pydantic 的 BaseModel
的配置类。你应该将配置实例传递给 ConfigurableComponent
的 __init__
或直接设置其 config
属性。使用配置允许你从文件加载配置,并且还可以轻松地序列化和反序列化任何代理的配置。要了解更多关于配置的信息,包括存储敏感信息和序列化,请参阅 组件配置。
# 示例组件配置
class UserGreeterConfiguration(BaseModel):
user_name: str
class UserGreeterComponent(MessageProvider, ConfigurableComponent[UserGreeterConfiguration]):
def __init__(self):
# 创建配置实例
# 你也可以将其传递给组件构造函数
# 例如 `def __init__(self, config: UserGreeterConfiguration):`
config = UserGreeterConfiguration(user_name="World")
# 将配置实例传递给父类
UserGreeterComponent.__init__(self, config)
# 这与上面的行效果相同:
# self.config = UserGreeterConfiguration(user_name="World")
def get_messages(self) -> Iterator[ChatMessage]:
# 你可以像使用常规模型一样使用配置
yield ChatMessage.system(f"Hello, {self.config.user_name}!")
提供命令¶
要扩展代理的功能,你需要使用 CommandProvider
协议提供命令。例如,要允许代理将两个数字相乘,你可以创建一个这样的组件:
class MultiplicatorComponent(CommandProvider):
def get_commands(self) -> Iterator[Command]:
# 生成命令以便代理可以使用它
yield self.multiply
@command(
parameters={
"a": JSONSchema(
type=JSONSchema.Type.INTEGER,
description="The first number",
required=True,
),
"b": JSONSchema(
type=JSONSchema.Type.INTEGER,
description="The second number",
required=True,
)})
def multiply(self, a: int, b: int) -> str:
"""
将两个数字相乘。
参数:
a: 第一个数字
b: 第二个数字
返回:
乘法结果
"""
return str(a * b)
要了解更多关于命令的信息,请参阅 🛠️ 命令。
提示结构¶
在组件提供所有必要数据后,代理需要构建最终的提示,该提示将被发送到 llm。
目前,PromptStrategy
(*不是*协议)负责构建最终提示。
如果你想改变提示的构建方式,你需要创建一个新的 PromptStrategy
类,然后在你的代理类中调用相关方法。
你可以查看 AutoGPT Agent 使用的默认策略:OneShotAgentPromptStrategy,以及它在 Agent 中的使用方式(搜索 self.prompt_strategy
)。
示例 UserInteractionComponent
¶
让我们创建一个稍微简化版本的组件,该组件由内置代理使用。 它赋予代理在终端中向用户请求输入的能力。
-
创建一个继承自
CommandProvider
的组件类。class MyUserInteractionComponent(CommandProvider): """提供与用户交互的命令。""" pass
-
实现命令方法,该方法将向用户请求输入并返回结果。
def ask_user(self, question: str) -> str: """如果你需要更多关于给定目标的详细信息或信息, 你可以向用户请求输入。""" print(f"\nQ: {question}") resp = input("A:") return f"用户的回答: '{resp}'"
-
需要使用
@command
装饰命令。@command( parameters={ "question": JSONSchema( type=JSONSchema.Type.STRING, description="向用户提出的问题或提示", required=True, ) }, ) def ask_user(self, question: str) -> str: """如果你需要更多关于给定目标的详细信息或信息, 你可以向用户请求输入。""" print(f"\nQ: {question}") resp = input("A:") return f"用户的回答: '{resp}'"
-
我们需要实现
CommandProvider
的get_commands
方法来生成命令。def get_commands(self) -> Iterator[Command]: yield self.ask_user
-
由于代理并不总是在终端或交互模式下运行,我们需要在无法请求用户输入时通过设置
self._enabled=False
来禁用此组件。def __init__(self, interactive_mode: bool): self.config = config self._enabled = interactive_mode
最终的组件应如下所示:
# 1.
class MyUserInteractionComponent(CommandProvider):
"""提供与用户交互的命令。"""
# 我们传递配置以检查是否处于非交互模式
def __init__(self, interactive_mode: bool):
self.config = config
# 5.
self._enabled = interactive_mode
# 4.
def get_commands(self) -> Iterator[Command]:
# 生成命令以便代理可以使用
# 如果组件被禁用,则不会生成
yield self.ask_user
# 3.
@command(
# 我们需要为所有命令参数提供一个模式
parameters={
"question": JSONSchema(
type=JSONSchema.Type.STRING,
description="向用户提出的问题或提示",
required=True,
)
},
)
# 2.
# 命令名称将是其方法名称,描述将是其文档字符串
def ask_user(self, question: str) -> str:
"""如果你需要更多关于给定目标的详细信息或信息,
你可以向用户请求输入。"""
print(f"\nQ: {question}")
resp = input("A:")
return f"用户的回答: '{resp}'"
现在,如果我们想使用我们的用户交互组件 代替 默认的组件,我们需要以某种方式移除默认的组件(如果我们的代理继承自 Agent
,则默认组件被继承)并添加我们自己的。我们可以简单地在 __init__
方法中覆盖 user_interaction
:
class MyAgent(Agent):
def __init__(
self,
settings: AgentSettings,
llm_provider: MultiProvider,
file_storage: FileStorage,
app_config: Config,
):
# 调用父构造函数以引入默认组件
super().__init__(settings, llm_provider, file_storage, app_config)
# 通过覆盖来禁用默认的用户交互组件
self.user_interaction = MyUserInteractionComponent()
或者我们可以通过将其设置为 None
来禁用默认组件:
class MyAgent(Agent):
def __init__(
self,
settings: AgentSettings,
llm_provider: MultiProvider,
file_storage: FileStorage,
app_config: Config,
):
# 调用父类构造函数以引入默认组件
super().__init__(settings, llm_provider, file_storage, app_config)
# 禁用默认的用户交互组件
self.user_interaction = None
# 添加我们自己的组件
self.my_user_interaction = MyUserInteractionComponent(app_config)
了解更多¶
查看更多示例的最佳地点是浏览 classic/original_autogpt/components 和 classic/original_autogpt/commands 目录中的内置组件。
关于如何扩展内置代理并构建自己的指南:🤖 代理
某些组件的顺序很重要,请参阅 🧩 Components 以了解更多关于组件及其自定义方式的信息。
要查看内置协议及其示例,请访问 ⚙️ Protocols。