⚙️ Protocols¶
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.
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.
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
CommandProvider
¶
Provides 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.
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.
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!")
AfterParse
¶
Protocol 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}")
ExecutionFailure
¶
Protocol 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}")
AfterExecute
¶
Protocol 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}")