-
Notifications
You must be signed in to change notification settings - Fork 43.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
394 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
### Other stuff | ||
Debugging may be easier because we can inspect the exact components that were called and where the pipeline failed (current WIP pipeline): | ||
|
||
![](../imgs/modular-pipeline.png) | ||
|
||
Also that makes it possible to call component/pipeline/function again when failed and recover. | ||
|
||
If it's necessary to get a component in a random place, agent provides generic, type safe `get_component(type[T]) -> T | None` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# 🤖 Agents | ||
|
||
is composed of components. It's responsible for executing pipelines and managing the components. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# 🛠️ Commands | ||
|
||
Commands a way for the agent to do anything; e.g. intercting with user or APIs and using tools. They are provided by components that implement the `CommandProvider` protocol. | ||
|
||
```py | ||
class CommandProvider(Protocol): | ||
def get_commands(self) -> Iterator[Command]: | ||
... | ||
``` | ||
|
||
## Command decorator | ||
|
||
The easiest way to provide a command is to use `command` decorator on a component method and then yield `Command.from_decorated_function(...)`. Each command needs a name, description and a parameter schema using `JSONSchema`. By default method name is used as a command name, and first part of docstring for the description (before `Args:` or `Returns:`) and schema can be provided in the decorator. | ||
|
||
- Simplified | ||
- Full | ||
|
||
## Direct construction | ||
|
||
|
||
|
||
|
||
```py | ||
from autogpt.agents.components import Component | ||
from autogpt.agents.protocols import CommandProvider | ||
from autogpt.core.utils.json_schema import JSONSchema | ||
from autogpt.command_decorator import command | ||
|
||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# 🧩 Components | ||
|
||
Components are the building blocks of [🤖 Agents](./agents.md). They are classes inherited from `Component` that implement one or more [⚙️ Protocols](./protocols.md) that give agent additional abilities or processing. | ||
|
||
Components assigned to attributes (fields) in agent's `__init__` are automatically discovered upon instantiation. | ||
Each component can implement multiple protocols and can rely on other components if needed. | ||
|
||
```py | ||
from autogpt.agents import Agent | ||
from autogpt.agents.components import Component | ||
|
||
class MyAgent(Agent): | ||
def __init__(self): | ||
# These components will be automatically discovered and used | ||
self.hello_component = HelloComponent() | ||
# We pass HelloComponent to CalculatorComponent | ||
self.calculator_component = CalculatorComponent(self.hello_component) | ||
``` | ||
## Ordering components | ||
|
||
For some protocols, the order of components is important because the latter ones may depend on the results of the former ones. | ||
|
||
### Implicit order | ||
|
||
Components can be ordered implicitly by the agent; each component can set `run_after` list to specify which components should run before it. This is useful when components rely on each other or need to be executed in a specific order. Otherwise, the order of components is alphabetical. | ||
|
||
```py | ||
# This component will run after HelloComponent | ||
class CalculatorComponent(Component): | ||
run_after = [HelloComponent] | ||
|
||
def __init__(self, hello_component: HelloComponent): | ||
self.hello_component = hello_component | ||
``` | ||
|
||
### Explicit order | ||
|
||
Sometimes it may be easier to order components explicitly by setting `self.components` list in the agent's `__init__` method. This way you can also ensure there's no circular dependencies and `run_after` is ignored. | ||
|
||
> ⚠️ Be sure to include all components - by setting `self.components` list, you're overriding the default behavior of discovering components automatically. Since it's usually not intended agent will inform you in the terminal if some components were skipped. | ||
```py | ||
class MyAgent(Agent): | ||
def __init__(self): | ||
self.hello_component = HelloComponent() | ||
self.calculator_component = CalculatorComponent(self.hello_component) | ||
# Explicitly set components list | ||
self.components = [self.hello_component, self.calculator_component] | ||
``` | ||
|
||
## Disabling components | ||
|
||
You can control which components are enabled by setting their `enabled` attribute. You can either provide a `bool` value or a `callable[[], bool]` that will be called each time the component is about to be executed. This way you can dynamically enable or disable components based on some conditions. | ||
You can also provide a reason for disabling the component by setting `disabled_reason`. The reason will be visible in the debug information. | ||
|
||
```py | ||
class DisabledComponent(Component, MessageProvider): | ||
def __init__(self): | ||
# Disable this component | ||
self.enabled = False | ||
self.disabled_reason = "This component is disabled because of reasons." | ||
# Or disable based on some condition | ||
self.enabled = self.some_condition | ||
|
||
# This method will never be called | ||
def get_messages(self) -> Iterator[ChatMessage]: | ||
yield ChatMessage.user("This message won't be seen!") | ||
|
||
def some_condition(self) -> bool: | ||
return False | ||
``` | ||
|
||
If you don't want the component at all, you can just remove it from the agent's `__init__` method. If you want to remove components you inherit from the parent class you can set the relevant attribute to `None`: | ||
|
||
```py | ||
class MyAgent(Agent): | ||
def __init__(self): | ||
super().__init__(...) | ||
# Disable WatchdogComponent that is in the parent class | ||
self.watchdog = None | ||
|
||
``` | ||
|
||
## Exceptions | ||
|
||
Custom errors are provided which can be used to control the execution flow in case something went wrong. All those errors can be raised in protocol methods and will be caught by the agent. | ||
By default agent will retry three times and then re-raise an exception if it's still not resolved. All passed arguments are automatically handled and the values are reverted when needed. | ||
All errors accept an optional `str` message. | ||
|
||
1. `ComponentError`: A single component failed to execute. Agent will retry the execution of the component. | ||
2. `ProtocolError`: An entire protocol failed to execute. Agent will retry the execution of the protocol method for all components. | ||
3. `PipelineError`: An entire pipeline failed to execute. Agent will retry the execution of the pipeline for all protocols. This isn't implemented yet. | ||
4. `ComponentSystemError`: The highest-level error occurred in the component system. This isn't used. | ||
|
||
**Example** | ||
|
||
```py | ||
from autogpt.agents.components import Component, ComponentError | ||
from autogpt.agents.protocols import MessageProvider | ||
|
||
# Example of raising an error | ||
class MyComponent(Component, MessageProvider): | ||
def get_messages(self) -> Iterator[ChatMessage]: | ||
raise ComponentError("Component error!") | ||
``` |
44 changes: 44 additions & 0 deletions
44
docs/content/AutoGPT/component agent/creating-components.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Creating Components | ||
|
||
## The minimal component | ||
|
||
Let's create a simple component that adds "Hello World!" message to the agent prompt. | ||
To create a component you just make a class that inherits from `Component`: | ||
|
||
```py | ||
# We recommend *Component suffix to make the type clear | ||
class HelloComponent(Component): | ||
pass | ||
``` | ||
|
||
This is already a valid component but it doesn't have any functionality yet. | ||
To make it do something we need to write a method that can be found and called by the agent. To put messages to the agent's prompt we need to implement `MessageProvider` Protocol in our component. `MessageProvider` is an interface with `get_messages` method: | ||
|
||
```py | ||
class HelloComponent(Component, MessageProvider): | ||
def get_messages(self) -> Iterator[ChatMessage]: | ||
yield ChatMessage.user("Hello World!") | ||
``` | ||
|
||
Now we can add our component to an existing agent or create a new Agent class and add it there: | ||
|
||
```py | ||
class MyAgent(Agent): | ||
self.hello_component = HelloComponent() | ||
``` | ||
|
||
`get_messsages` will called by the agent each time it needs to build a new prompt and the yielded messages will be added accordingly. | ||
|
||
## Full example | ||
|
||
|
||
|
||
```py | ||
|
||
``` | ||
|
||
## Learn more | ||
|
||
Guide on how to extend the built-in agent and build your own: [🤖 Agents](./agents.md) | ||
Order of some components matters, see [🧩 Components](./components.md) to learn more about components and how they can be customized. | ||
To see built-in protocols with accompanying examples visit [⚙️ Protocols](./protocols.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Component Agents | ||
|
||
This guide explains the component-based architecture of AutoGPT agents. It's a new way of building agents that is more flexible and easier to extend. Components replace plugins with a more modular and composable system. | ||
|
||
Agent is composed of *components*, and each `Component` implements a range of `Protocol`s (interfaces), each one providing a specific functionality, e.g. additional commands or messages. Each *protocol* is handled in a specific order, defined by the agent. This allows for a clear separation of concerns and a more modular design. | ||
|
||
This system is simple, flexible, requires basically no configuration, and doesn't hide any data - anything can still be passed or accessed directly from or between components. | ||
|
||
### Definitions & Guides | ||
|
||
See quick guide [Creating Components](./creating-components.md) to get started! Or you can explore the following topics in detail: | ||
|
||
- [🧩 Component](./components.md): a class that implements one or more *protocols*. It can be added to an agent to provide additional functionality. | ||
- [⚙️ Protocol](./protocols.md): an interface that defines a set of methods that a component must implement. Protocols are used to group related functionality. | ||
- [🛠️ Command](./commands.md): | ||
- [🤖 Agent](./agents.md): a class that is composed of components. It's responsible for executing pipelines and managing the components. | ||
- **Pipeline**: a sequence of method calls on components. Pipelines are used to execute a series of actions in a specific order. As of now there's no formal class for a pipeline, it's just a sequence of method calls on components. There are two default pipelines implemented in the default agent: `propose_action` and `execute`. See [🤖 Agent](./agents.md) to learn more. |
Oops, something went wrong.