Spaces:
Build error
Build error
# ⚙️ Protocols | |
Protocols are *interfaces* implemented by [Components](./components.md) used to group related functionality. Each protocol needs to be handled explicitly by the agent at some point of the execution. We provide a comprehensive list of built-in protocols that are already handled in the built-in `Agent`, so when you inherit from the base agent all built-in protocols will work! | |
**Protocols are listed in the order of the default execution.** | |
## Order-independent protocols | |
Components implementing exclusively order-independent protocols can added in any order, including in-between ordered protocols. | |
### `DirectiveProvider` | |
Yields constraints, resources and best practices for the agent. This has no direct impact on other protocols; is purely informational and will be passed to a llm when the prompt is built. | |
```py | |
class DirectiveProvider(AgentComponent): | |
def get_constraints(self) -> Iterator[str]: | |
return iter([]) | |
def get_resources(self) -> Iterator[str]: | |
return iter([]) | |
def get_best_practices(self) -> Iterator[str]: | |
return iter([]) | |
``` | |
**Example** A web-search component can provide a resource information. Keep in mind that this actually doesn't allow the agent to access the internet. To do this a relevant `Command` needs to be provided. | |
```py | |
class WebSearchComponent(DirectiveProvider): | |
def get_resources(self) -> Iterator[str]: | |
yield "Internet access for searches and information gathering." | |
# We can skip "get_constraints" and "get_best_practices" if they aren't needed | |
``` | |
### `CommandProvider` | |
Provides a command that can be executed by the agent. | |
```py | |
class CommandProvider(AgentComponent): | |
def get_commands(self) -> Iterator[Command]: | |
... | |
``` | |
The easiest way to provide a command is to use `command` decorator on a component method and then yield the method. 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. | |
**Example** Calculator component that can perform multiplication. Agent is able to call this command if it's relevant to a current task and will see the returned result. | |
```py | |
from forge.agent import CommandProvider, Component | |
from forge.command import command | |
from forge.models.json_schema import JSONSchema | |
class CalculatorComponent(CommandProvider): | |
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: | |
""" | |
Multiplies two numbers. | |
Args: | |
a: First number | |
b: Second number | |
Returns: | |
Result of multiplication | |
""" | |
return str(a * b) | |
``` | |
The agent will be able to call this command, named `multiply` with two arguments and will receive the result. The command description will be: `Multiplies two numbers.` | |
To learn more about commands see [🛠️ Commands](./commands.md). | |
## Order-dependent protocols | |
The order of components implementing order-dependent protocols is important. | |
Some components may depend on the results of components before them. | |
### `MessageProvider` | |
Yields messages that will be added to the agent's prompt. You can use either `ChatMessage.user()`: this will interpreted as a user-sent message or `ChatMessage.system()`: that will be more important. | |
```py | |
class MessageProvider(AgentComponent): | |
def get_messages(self) -> Iterator[ChatMessage]: | |
... | |
``` | |
**Example** Component that provides a message to the agent's prompt. | |
```py | |
class HelloComponent(MessageProvider): | |
def get_messages(self) -> Iterator[ChatMessage]: | |
yield ChatMessage.user("Hello World!") | |
``` | |
### `AfterParse` | |
Protocol called after the response is parsed. | |
```py | |
class AfterParse(AgentComponent): | |
def after_parse(self, response: ThoughtProcessOutput) -> None: | |
... | |
``` | |
**Example** Component that logs the response after it's parsed. | |
```py | |
class LoggerComponent(AfterParse): | |
def after_parse(self, response: ThoughtProcessOutput) -> None: | |
logger.info(f"Response: {response}") | |
``` | |
### `ExecutionFailure` | |
Protocol called when the execution of the command fails. | |
```py | |
class ExecutionFailure(AgentComponent): | |
@abstractmethod | |
def execution_failure(self, error: Exception) -> None: | |
... | |
``` | |
**Example** Component that logs the error when the command fails. | |
```py | |
class LoggerComponent(ExecutionFailure): | |
def execution_failure(self, error: Exception) -> None: | |
logger.error(f"Command execution failed: {error}") | |
``` | |
### `AfterExecute` | |
Protocol called after the command is successfully executed by the agent. | |
```py | |
class AfterExecute(AgentComponent): | |
def after_execute(self, result: ActionResult) -> None: | |
... | |
``` | |
**Example** Component that logs the result after the command is executed. | |
```py | |
class LoggerComponent(AfterExecute): | |
def after_execute(self, result: ActionResult) -> None: | |
logger.info(f"Result: {result}") | |
``` | |