Agent Communication
Communication between agents is a fundamental aspect of the Agent Swarm Protocol (ASP). This guide covers the direct agent-to-agent communication patterns, message structures, and best practices for effective agent collaboration.
Message Structure
All communication in ASP follows a standardized message format:
interface Message {
id?: string; // Unique message ID
type: string; // Message type (e.g., 'agent.request')
content?: any; // Message payload
requestId?: string; // ID of the original request (for responses)
timestamp?: number; // When the message was created
metadata?: Record<string, any>; // Additional contextual information
}
Agent-to-Agent Communication
ASP now supports direct agent-to-agent communication through the orchestrator. This allows agents to collaborate and delegate tasks without the need for complex workflow configurations.
Request-Response Pattern
The most common pattern is request-response, where one agent sends a request to another agent and expects a response.
Agent SDK Example:
// From within an agent implementation:
class MyAgent extends SwarmAgentSDK {
async handleCustomTask(task) {
// Request information from the Research Agent
try {
const researchResult = await this.requestAgentTask(
'Research Agent', // Target agent name
{
taskType: 'research.query',
query: 'latest advancements in AI',
context: {
originAgent: 'My Agent'
}
},
30000 // Timeout in ms (optional)
);
console.log('Research results:', researchResult);
return { success: true, results: researchResult };
} catch (error) {
console.error('Error requesting research:', error);
return { success: false, error: error.message };
}
}
}
How Agent-to-Agent Communication Works
- Request Initiation: An agent sends a request to the orchestrator specifying the target agent and task data.
- Orchestrator Routing: The orchestrator locates the target agent and forwards the request as a task.
- Task Processing: The target agent processes the task and returns a result.
- Response Delivery: The orchestrator delivers the response back to the requesting agent.
Agent Request Message Structure
When an agent makes a request to another agent, it sends a message with the following structure:
{
type: 'agent.request',
content: {
targetAgentName: string, // Name of the target agent
taskData: { // Task data to send to the target agent
taskType: string, // Type of task (e.g., 'research.query')
// Additional task-specific data...
}
}
}
Agent Response Message Structure
The response from the target agent is wrapped by the orchestrator and sent back with the following structure:
{
type: 'agent.response',
content: { // Response content from the target agent
// Task-specific response data...
},
requestId: string // Original request ID
}
Implementation Details
Orchestrator Handling
The orchestrator handles agent-to-agent communication with a dedicated handler method:
async handleAgentRequest(message, ws) {
const { targetAgentName, taskData } = message.content;
// Get the requesting agent
const requestingAgent = this.agents.getAgentByConnectionId(ws.id);
// Get the target agent
const targetAgent = this.agents.getAgentByName(targetAgentName);
// Create a task for the target agent
const taskId = uuidv4();
const taskMessage = {
id: taskId,
type: 'task.execute',
content: {
input: taskData,
metadata: {
requestingAgentId: requestingAgent.id,
requestingAgentName: requestingAgent.name,
timestamp: new Date().toISOString()
}
}
};
// Send task to target agent and wait for response
const response = await this.sendAndWaitForResponse(
targetAgent.connection,
taskMessage
);
// Send the result back to the requesting agent
this.send(ws, {
type: 'agent.response',
content: response.content,
requestId: message.id
});
}
Agent SDK Integration
The SwarmAgentSDK provides a convenient method for requesting tasks from other agents:
async requestAgentTask(targetAgentName, taskData, timeout = 30000) {
const message = {
type: 'agent.request',
content: {
targetAgentName,
taskData
}
};
try {
const response = await this.sendAndWaitForResponse(message, timeout);
return response.content;
} catch (error) {
this.emit('error', new Error(
`Failed to request task from agent ${targetAgentName}: ${error.message}`
));
throw error;
}
}
Example: Conversation Agent and Research Agent Collaboration
Here's an example of how the Conversation Agent communicates with the Research Agent to answer user queries:
// Inside Conversation Agent's message handler
async handleConversationMessage(task) {
const { message, context } = task.data;
// Detect if this is a research query
const isResearchQuery = this.isResearchQuery(message);
let researchResult = null;
if (isResearchQuery && context.availableAgents) {
// Find the research agent from available agents
const researchAgent = context.availableAgents.find(agent =>
agent.name === 'Research Agent' ||
(agent.capabilities && agent.capabilities.includes('research'))
);
if (researchAgent) {
try {
// Request research from the Research Agent
researchResult = await this.requestAgentTask(researchAgent.name, {
taskType: 'research.query',
query: message,
context: {
conversationId: task.data.conversationId,
originAgent: 'Conversation Agent'
}
});
} catch (error) {
this.logger.error(`Error requesting research: ${error.message}`);
}
}
}
// Generate response using research results if available
const responseData = this.generateResponse(
task.data.conversationId,
message,
context,
this.detectIntents(message),
this.analyzeSentiment(message),
researchResult
);
return responseData;
}
Client Example: Facilitating Agent-to-Agent Communication
Clients can facilitate agent-to-agent communication by providing context in task requests:
// Using the SDK client to facilitate agent-to-agent communication
const response = await client.sendTask('Conversation Agent', {
taskType: 'conversation.message',
conversationId: 'conv-123',
message: 'What are the latest advancements in AI?',
context: {
// Provide information about available agents
availableAgents: [
{
name: 'Research Agent',
capabilities: ['research', 'web-search', 'knowledge-retrieval']
}
]
}
});
Benefits of Direct Agent-to-Agent Communication
- Simplicity: Removes the need for complex workflow configurations
- Flexibility: Agents can dynamically decide when and with whom to collaborate
- Autonomy: Agents can operate more independently and make their own collaboration decisions
- Scalability: Enables more organic scaling of agent ecosystems with less central coordination
- Fault Tolerance: Failure of a single agent doesn't break a predefined workflow
Best Practices
- Include Context: Always include relevant context in agent-to-agent requests
- Implement Timeouts: Set reasonable timeouts for agent-to-agent requests
- Error Handling: Implement robust error handling for failed agent-to-agent communications
- Metadata: Use metadata to provide additional information about the request
- Idempotency: Design agent communications to be idempotent when possible
- Batching: Batch related requests when appropriate to reduce communication overhead
Agent-to-agent communication opens up powerful collaboration possibilities within the Agent Swarm Protocol ecosystem, enabling more autonomous and flexible agent interactions.
Communication Channels
ASP supports several communication channels:
- Direct Agent-to-Agent: Messages sent directly between agents
- Agent-to-Orchestrator: Requests for orchestrator services
- Orchestrator-to-Agent: Responses and notifications from the orchestrator
- Broadcast: Messages sent to all agents in the swarm
Communication Patterns
Publish-Subscribe Pattern
For one-to-many communication, agents can use the publish-subscribe pattern.
// Agent A: Subscribe to a topic
await agentA.send({
type: 'subscribe',
content: { topic: 'market.updates' }
});
// Agent A: Handle updates
agentA.on('message', (message) => {
if (message.type === 'market.update') {
console.log('Market update:', message.content);
}
});
// Agent B: Publish an update
await agentB.send({
type: 'market.update',
content: {
symbol: 'AAPL',
price: 150.25
},
metadata: {
topic: 'market.updates'
}
});
The orchestrator routes these messages based on subscription registrations.
Broadcast Pattern
For system-wide announcements, agents can broadcast messages to all agents.
// Send a broadcast message
await agent.send({
type: 'broadcast',
content: {
alert: 'System maintenance starting in 5 minutes'
}
});
Streaming Pattern
For continuous data flow, agents can use streaming communication.
// Agent A: Start streaming data
const streamId = generateUniqueId();
await agentA.send({
type: 'stream.start',
content: {
streamId: streamId,
dataType: 'sensor-readings'
}
});
// Agent A: Send stream data
for (const reading of sensorReadings) {
await agentA.send({
type: 'stream.data',
content: reading,
metadata: { streamId }
});
await sleep(100); // Simulate delay between readings
}
// Agent A: End the stream
await agentA.send({
type: 'stream.end',
metadata: { streamId }
});
// Agent B: Handle the stream
agentB.on('message', (message) => {
switch (message.type) {
case 'stream.start':
console.log('Stream started:', message.content.streamId);
break;
case 'stream.data':
console.log('Stream data:', message.content);
break;
case 'stream.end':
console.log('Stream ended:', message.metadata.streamId);
break;
}
});
Message Routing
The ASP orchestrator handles message routing between agents. Several routing mechanisms are supported:
Direct Routing
Messages are sent directly to a specific agent by ID or name.
await agent.send({
type: 'process.request',
recipient: 'data-processing-agent',
content: { data: 'example' }
});
Capability-based Routing
Messages can be routed based on agent capabilities.
await agent.send({
type: 'search.request',
content: { query: 'example' },
metadata: {
routing: {
capability: 'web-search',
strategy: 'round-robin' // or 'first-available', 'least-busy', etc.
}
}
});
Topic-based Routing
Messages can be routed based on topics (publish-subscribe).
await agent.send({
type: 'market.update',
content: { symbol: 'AAPL', price: 150.25 },
metadata: { topic: 'market.updates' }
});
Bidirectional Communication
ASP's WebSocket-based communication enables true bidirectional communication between agents and the orchestrator.
// Agent connects to orchestrator
agent.connect().then(() => {
console.log('Connected to orchestrator');
// Agent can immediately send and receive messages
agent.send({
type: 'status.update',
content: { status: 'online' }
});
});
// Agent listens for messages from orchestrator or other agents
agent.on('message', (message) => {
console.log('Received message:', message);
});
Parent-Child Communication
ASP supports hierarchical agent relationships, where a parent agent can spawn and communicate with child agents.
// Parent creates a child agent
const childAgent = await parentAgent.requestService('agent', {
action: 'start',
agentName: 'specialized-processor'
});
// Parent sends message to child
const response = await parentAgent.requestService('agent', {
action: 'send',
agentId: childAgent.id,
message: {
type: 'process.request',
content: { data: 'to process' }
},
waitForResponse: true
});
// Parent can later stop the child agent
await parentAgent.requestService('agent', {
action: 'stop',
agentId: childAgent.id
});
Error Handling in Communication
Proper error handling is crucial for robust agent communication:
// Sending with error handling
try {
const response = await agent.sendAndWaitForResponse({
recipient: 'data-processor',
type: 'process.request',
content: { data: 'example' }
}, {
timeout: 5000,
retries: 3,
retryDelay: 1000
});
console.log('Received response:', response);
} catch (error) {
if (error.code === 'TIMEOUT') {
console.error('Request timed out');
} else if (error.code === 'AGENT_NOT_FOUND') {
console.error('Agent not found');
} else {
console.error('Communication error:', error);
}
}
// Receiving with error handling
agent.on('message', async (message) => {
try {
if (message.type === 'process.request') {
const result = await processData(message.content.data);
await agent.send({
type: 'process.response',
content: result,
requestId: message.id
});
}
} catch (error) {
// Send error response
await agent.send({
type: 'error.response',
content: {
error: error.message,
code: error.code || 'PROCESSING_ERROR'
},
requestId: message.id
});
}
});
Message Serialization
ASP automatically handles message serialization, but it's important to ensure messages contain only serializable data:
// Good - fully serializable
await agent.send({
type: 'data.response',
content: {
results: [1, 2, 3],
metadata: {
timestamp: new Date().toISOString(), // String representation of date
source: 'sensor-123'
}
}
});
// Bad - contains non-serializable elements
await agent.send({
type: 'data.response',
content: {
results: [1, 2, 3],
processor: function() { /* ... */ }, // Functions can't be serialized
timestamp: new Date() // Date objects need conversion
}
});
Message Validation
ASP allows you to define message schemas for validation:
// Register message schema
agent.registerMessageSchema('search.request', {
type: 'object',
properties: {
query: { type: 'string', minLength: 1 },
limit: { type: 'number', default: 10 }
},
required: ['query']
});
// Messages will be validated automatically
await agent.send({
type: 'search.request',
content: { query: 'example' } // Valid
});
try {
await agent.send({
type: 'search.request',
content: { query: '' } // Invalid - will throw validation error
});
} catch (error) {
console.error('Validation error:', error);
}
Communication Security
ASP includes several security features for agent communication:
- Authentication: Agents authenticate with the orchestrator
- Authorization: Agents can only send messages to authorized recipients
- Message Signing: Messages can be cryptographically signed
- Encryption: Communication channels are encrypted
// Creating an agent with security options
const agent = new Agent({
manifestPath: './manifest.json',
security: {
authToken: 'your-auth-token',
signMessages: true,
encryptionKey: process.env.ENCRYPTION_KEY
}
});
Debugging Communication
ASP provides tools for debugging agent communication:
// Enable communication logging
agent.enableMessageLogging({
logLevel: 'debug', // 'debug', 'info', 'warn', 'error'
includeContent: true,
logFile: './agent-messages.log'
});
// Monitor specific message types
agent.on('message:search.request', (message) => {
console.log('Search request received:', message);
});
// Monitor all messages
agent.on('message', (message) => {
console.log(`[${message.type}] Message received`);
});
Communication Patterns Examples
Chain of Responsibility
Multiple agents can process a request in sequence:
// Define a workflow with sequential processing
const workflow = {
name: 'data-pipeline',
steps: [
{
id: 'extract',
agent: 'data-extractor',
message: {
type: 'extract.request',
content: { source: 'database' }
}
},
{
id: 'transform',
agent: 'data-transformer',
message: {
type: 'transform.request',
content: { data: '{{extract.response.content}}' }
},
dependsOn: ['extract']
},
{
id: 'load',
agent: 'data-loader',
message: {
type: 'load.request',
content: { data: '{{transform.response.content}}' }
},
dependsOn: ['transform']
}
]
};
// Execute the workflow
const result = await orchestrator.executeWorkflow('data-pipeline');
Fan Out / Fan In
Distribute work to multiple agents and collect results:
// Fan out to multiple processors
const processorAgents = ['processor-1', 'processor-2', 'processor-3'];
const dataChunks = splitData(largeData, processorAgents.length);
// Send data to each processor
const promises = processorAgents.map((agentId, index) => {
return agent.sendAndWaitForResponse({
recipient: agentId,
type: 'process.request',
content: { data: dataChunks[index] }
});
});
// Fan in - collect all results
const results = await Promise.all(promises);
const combinedResult = combineResults(results.map(r => r.content));
Event-Driven Agents
Agents can react to events in the system:
// Register interest in events
await agent.send({
type: 'subscribe',
content: {
topics: [
'user.login',
'user.logout',
'system.alert'
]
}
});
// Handle events
agent.on('message', async (message) => {
switch (message.type) {
case 'user.login':
await handleUserLogin(message.content);
break;
case 'user.logout':
await handleUserLogout(message.content);
break;
case 'system.alert':
await handleSystemAlert(message.content);
break;
}
});
Best Practices
Message Design
- Consistent Types: Use consistent message types (e.g., 'entity.action')
- Versioning: Consider including version information in messages
- Minimalism: Send only the data that is needed
- Completeness: Include all necessary context in each message
- Idempotency: Design messages to be safely redelivered
Communication Efficiency
- Batching: Batch related messages when possible
- Throttling: Implement rate limiting for high-frequency messages
- Compression: Consider compressing large message payloads
- Streaming: Use streaming for large datasets instead of single large messages
Reliability
- Acknowledgments: Implement message acknowledgments for critical flows
- Retries: Implement retry logic with backoff for important messages
- Timeouts: Set appropriate timeouts for waiting for responses
- Circuit Breakers: Implement circuit breakers for failing communications
Organization
- Namespacing: Use namespacing in message types (e.g., 'domain.entity.action')
- Documentation: Document message types and their payload structures
- Schemas: Define schemas for message validation
Next Steps
Now that you understand agent communication, you can:
- Explore advanced workflows
- Learn about the agent marketplace
- Dive into orchestrator configuration