docs/content/forge/components/protocols.md
Protocols are interfaces implemented by Components 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.
Components implementing exclusively order-independent protocols can added in any order, including in-between ordered protocols.
DirectiveProviderYields 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.
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.
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
CommandProviderProvides a command that can be executed by the agent.
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.
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.
The order of components implementing order-dependent protocols is important. Some components may depend on the results of components before them.
MessageProviderYields 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.
class MessageProvider(AgentComponent):
def get_messages(self) -> Iterator[ChatMessage]:
...
Example Component that provides a message to the agent's prompt.
class HelloComponent(MessageProvider):
def get_messages(self) -> Iterator[ChatMessage]:
yield ChatMessage.user("Hello World!")
AfterParseProtocol called after the response is parsed.
class AfterParse(AgentComponent):
def after_parse(self, response: ThoughtProcessOutput) -> None:
...
Example Component that logs the response after it's parsed.
class LoggerComponent(AfterParse):
def after_parse(self, response: ThoughtProcessOutput) -> None:
logger.info(f"Response: {response}")
ExecutionFailureProtocol called when the execution of the command fails.
class ExecutionFailure(AgentComponent):
@abstractmethod
def execution_failure(self, error: Exception) -> None:
...
Example Component that logs the error when the command fails.
class LoggerComponent(ExecutionFailure):
def execution_failure(self, error: Exception) -> None:
logger.error(f"Command execution failed: {error}")
AfterExecuteProtocol called after the command is successfully executed by the agent.
class AfterExecute(AgentComponent):
def after_execute(self, result: ActionResult) -> None:
...
Example Component that logs the result after the command is executed.
class LoggerComponent(AfterExecute):
def after_execute(self, result: ActionResult) -> None:
logger.info(f"Result: {result}")