跳到内容

创建组件

最小组件

组件可用于实现各种功能,例如向 prompt 提供消息、执行代码或与外部服务交互。

Component 是一个继承自 AgentComponent 类或实现一个或多个 protocols 的类。每个 protocol 都继承自 AgentComponent,因此一旦你继承任何 protocol,你的类就会自动成为一个 component

class MyComponent(AgentComponent):
    pass

这已经是一个有效的组件了,但它还没有任何功能。要为其添加一些功能,你需要实现一个或多个 protocols

让我们创建一个简单的组件,该组件会向 agent 的 prompt 添加“Hello World!”消息。为此,我们需要在组件中实现 MessageProvider protocolMessageProvider 是一个带有 get_messages 方法的接口

# No longer need to inherit AgentComponent, because MessageProvider already does it
class HelloComponent(MessageProvider):
    def get_messages(self) -> Iterator[ChatMessage]:
        yield ChatMessage.user("Hello World!")

现在我们可以将我们的组件添加到现有 agent 中,或者创建一个新的 Agent 类并将其添加进去

class MyAgent(Agent):
    self.hello_component = HelloComponent()

agent 在每次需要构建新的 prompt 时都会调用 get_messages,并且相应的 yield 的消息将被添加。

在组件之间传递数据

由于组件是常规类,你可以通过 __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 属性。使用配置可以从文件加载配置,并且方便地序列化和反序列化配置供任何 agent 使用。要了解有关配置的更多信息,包括存储敏感信息和序列化,请参阅组件配置

# Example component configuration
class UserGreeterConfiguration(BaseModel):
    user_name: str

class UserGreeterComponent(MessageProvider, ConfigurableComponent[UserGreeterConfiguration]):
    def __init__(self):
        # Creating configuration instance
        # You could also pass it to the component constructor
        # e.g. `def __init__(self, config: UserGreeterConfiguration):`
        config = UserGreeterConfiguration(user_name="World")
        # Passing the configuration instance to the parent class
        UserGreeterComponent.__init__(self, config)
        # This has the same effect as the line above:
        # self.config = UserGreeterConfiguration(user_name="World")

    def get_messages(self) -> Iterator[ChatMessage]:
        # You can use the configuration like a regular model
        yield ChatMessage.system(f"Hello, {self.config.user_name}!")

提供命令

要扩展 agent 的能力,你需要使用 CommandProvider protocol 提供命令。例如,要允许 agent 乘以两个数字,你可以创建一个如下所示的组件

class MultiplicatorComponent(CommandProvider):
    def get_commands(self) -> Iterator[Command]:
        # Yield the command so the agent can use it
        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:
        """
        Multiplies two numbers.

        Args:
            a: First number
            b: Second number

        Returns:
            Result of multiplication
        """
        return str(a * b)

要了解有关命令的更多信息,请参阅 🛠️ 命令

Prompt 结构

组件提供了所有必需的数据后,agent 需要构建将发送到 llm 的最终 prompt。目前,PromptStrategy不是 protocol)负责构建最终 prompt。

如果你想改变 prompt 的构建方式,你需要创建一个新的 PromptStrategy 类,然后在你的 agent 类中调用相关方法。你可以查看 AutoGPT Agent 使用的默认策略:OneShotAgentPromptStrategy,以及它在 Agent 中的使用方式(搜索 self.prompt_strategy)。

UserInteractionComponent 示例

让我们创建一个内置 agent 使用的组件的简化版本。它让 agent 能够在终端中向用户请求输入。

  1. 为该组件创建一个继承自 CommandProvider 的类。

    class MyUserInteractionComponent(CommandProvider):
        """Provides commands to interact with the user."""
        pass
    
  2. 实现一个命令方法,该方法将向用户请求输入并返回它。

    def ask_user(self, question: str) -> str:
        """If you need more details or information regarding the given goals,
        you can ask the user for input."""
        print(f"\nQ: {question}")
        resp = input("A:")
        return f"The user's answer: '{resp}'"
    
  3. 该命令需要用 @command 装饰器进行装饰。

    @command(
        parameters={
            "question": JSONSchema(
                type=JSONSchema.Type.STRING,
                description="The question or prompt to the user",
                required=True,
            )
        },
    )
    def ask_user(self, question: str) -> str:
        """If you need more details or information regarding the given goals,
        you can ask the user for input."""
        print(f"\nQ: {question}")
        resp = input("A:")
        return f"The user's answer: '{resp}'"
    
  4. 我们需要实现 CommandProviderget_commands 方法来 yield 命令。

    def get_commands(self) -> Iterator[Command]:
        yield self.ask_user
    
  5. 由于 agent 并非总是在终端或交互模式下运行,因此在无法请求用户输入时,我们需要通过设置 self._enabled=False 来禁用此组件。

    def __init__(self, interactive_mode: bool):
        self.config = config
        self._enabled = interactive_mode
    

最终的组件应如下所示

# 1.
class MyUserInteractionComponent(CommandProvider):
    """Provides commands to interact with the user."""

    # We pass config to check if we're in noninteractive mode
    def __init__(self, interactive_mode: bool):
        self.config = config
        # 5.
        self._enabled = interactive_mode

    # 4.
    def get_commands(self) -> Iterator[Command]:
        # Yielding the command so the agent can use it
        # This won't be yielded if the component is disabled
        yield self.ask_user

    # 3.
    @command(
        # We need to provide a schema for ALL the command parameters
        parameters={
            "question": JSONSchema(
                type=JSONSchema.Type.STRING,
                description="The question or prompt to the user",
                required=True,
            )
        },
    )
    # 2.
    # Command name will be its method name and description will be its docstring
    def ask_user(self, question: str) -> str:
        """If you need more details or information regarding the given goals,
        you can ask the user for input."""
        print(f"\nQ: {question}")
        resp = input("A:")
        return f"The user's answer: '{resp}'"

现在,如果我们想使用我们自己的用户交互方式而不是默认的,我们需要以某种方式移除默认的(如果我们的 agent 继承自 Agent,则默认的会被继承),然后添加我们自己的。我们可以简单地在 __init__ 方法中覆盖 user_interaction

class MyAgent(Agent):
    def __init__(
        self,
        settings: AgentSettings,
        llm_provider: MultiProvider,
        file_storage: FileStorage,
        app_config: Config,
    ):
        # Call the parent constructor to bring in the default components
        super().__init__(settings, llm_provider, file_storage, app_config)
        # Disable the default user interaction component by overriding it
        self.user_interaction = MyUserInteractionComponent()

或者,我们可以通过将其设置为 None 来禁用默认组件

class MyAgent(Agent):
    def __init__(
        self,
        settings: AgentSettings,
        llm_provider: MultiProvider,
        file_storage: FileStorage,
        app_config: Config,
    ):
        # Call the parent constructor to bring in the default components
        super().__init__(settings, llm_provider, file_storage, app_config)
        # Disable the default user interaction component
        self.user_interaction = None
        # Add our own component
        self.my_user_interaction = MyUserInteractionComponent(app_config)

了解更多

查看更多示例的最佳位置是查看 classic/original_autogpt/componentsclassic/original_autogpt/commands 目录中的内置组件。

关于如何扩展内置 agent 并构建自己的 agent 的指南:🤖 Agents
某些组件的顺序很重要,请参阅 🧩 组件 了解有关组件以及如何自定义它们的更多信息。
要查看带有相应示例的内置 protocols,请访问 ⚙️ Protocols