Agents overview
In the Multi-Agent Orchestrator, an agent is a fundamental building block designed to process user requests and generate a response. The Agent
abstract class serves as the foundation for all specific agent implementations, providing a common structure and interface.
Agent selection process
The Multi-Agent Orchestrator uses a Classifier, typically an LLM, to select the most appropriate agent for each user request.
At the heart of this process are the agent descriptions. These descriptions are critical and should be as detailed and comprehensive as possible.
A well-crafted agent description:
- Clearly outlines the agent’s capabilities and expertise
- Provides specific examples of tasks it can handle
- Distinguishes it from other agents in the system
The more detailed and precise these descriptions are, the more accurately the Classifier can route requests to the right agent. This is especially important in complex systems with multiple specialized agents.
For a more detailed explanation of the agent selection process, please refer to the How it works section section in our documentation.
To optimize agent selection:
- Invest time in crafting thorough, specific agent descriptions
- Regularly review and refine these descriptions
- Use the framework’s agent overlap analysis to ensure clear differentiation between agents
By prioritizing detailed agent descriptions and fine-tuning the selection process, you can significantly enhance the efficiency and accuracy of your Multi-Agent Orchestrator implementation.
The Agent Abstract Class
The Agent
class is an abstract base class that defines the essential properties and methods that all agents in the system must have. It’s designed to be flexible, allowing for a wide range of implementations from simple API callers to complex LLM-powered conversational agents.
Key Properties
name
: A string representing the name of the agent.id
: A unique identifier for the agent, automatically generated from the name.description
: A string describing the agent’s capabilities and expertise.save_chat
: A boolean indicating whether to save the chat history for this agent.callbacks
: An optionalAgentCallbacks
object for handling events like new tokens in streaming responses.
Abstract Method: process_request
The core functionality of any agent is encapsulated in the process_request
method. This method must be implemented by all concrete agent classes:
abstract processRequest( inputText: string, userId: string, sessionId: string, chatHistory: Message[], additionalParams?: Record<string, any>): Promise<Message | AsyncIterable<any>>;
from abc import abstractmethodfrom typing import Union, AsyncIterable, Optional, Dict, Listfrom multi_agent_orchestrator.types import ConversationMessage
class Agent(ABC): @abstractmethod async def process_request( self, input_text: str, user_id: str, session_id: str, chat_history: List[ConversationMessage], additional_params: Optional[Dict[str, str]] = None ) -> Union[ConversationMessage, AsyncIterable[any]]: pass
input_text
: The user’s input or query.user_id
: A unique identifier for the user.session_id
: An identifier for the current conversation session.chat_history
: A list of previous messages in the conversation.additional_params
: Optional parameters for additional context or configuration. This is a powerful feature that allows for dynamic customization of agent behavior- It’s an optional dictionary of key-value pairs that can be passed when calling
route_request
on the orchestrator. - These parameters are then forwarded to the appropriate agent’s
process_request
method. - Custom agents can use these parameters to adjust their behavior or provide additional context for processing the request.
- It’s an optional dictionary of key-value pairs that can be passed when calling
The method returns either a ConversationMessage
for single responses or an AsyncIterable
for streaming responses.
Example usage:
// When calling routeRequestconst response = await orchestrator.routeRequest( userInput, userId, sessionId, { location: "New York", units: "metric" });
// In a custom agent's processRequest methodclass WeatherAgent extends Agent { async processRequest( inputText: string, userId: string, sessionId: string, chatHistory: Message[], additionalParams?: Record<string, any> ): Promise<Message> { const location = additionalParams?.location || "default location"; const units = additionalParams?.units || "metric"; // Use location and units to fetch weather data // ... }}
# When calling route_requestresponse = await orchestrator.route_request( user_input, user_id, session_id, additional_params={"location": "New York", "units": "metric"})
# In a custom agent's process_request methodclass WeatherAgent(Agent): async def process_request( self, input_text: str, user_id: str, session_id: str, chat_history: List[ConversationMessage], additional_params: Optional[Dict[str, str]] = None ) -> ConversationMessage: location = additional_params.get('location', 'default location') units = additional_params.get('units', 'metric') # Use location and units to fetch weather data # ...
Agent Options
When creating a new agent, you can specify various options using the AgentOptions
class:
interface AgentOptions { name: string; description: string; modelId?: string; region?: string; saveChat?: boolean; callbacks?: AgentCallbacks;}
@dataclassclass AgentOptions: name: str description: str model_id: Optional[str] = None region: Optional[str] = None save_chat: bool = True callbacks: Optional[AgentCallbacks] = None
Direct Agent Usage
When you have a single agent use case, you can bypass the orchestrator and call the agent directly. This approach leverages the power of the Multi Agent Orchestrator framework while focusing on a single agent scenario:
// Initialize the agentconst agent = new BedrockLLMAgent({ name: "custom-agent", description: "Handles specific tasks"});
// Call the agent directlyconst response = await agent.agentProcessRequest( userInput, userId, sessionId, chatHistory, { param1: "value1" });
# Initialize the agentagent = BedrockLLMAgent( name="custom-agent", description="Handles specific tasks")
# Call the agent directlyresponse = await agent.agent_process_request( input_text=user_input, user_id=user_id, session_id=session_id, chat_history=chat_history, additional_params={"param1": "value1"})
This approach is useful for single agent scenarios where you don’t need orchestration but want to leverage the powerful capabilities of the Multi Agent Orchestrator framework.
These options allow you to customize various aspects of the agent’s behavior and configuration.