Advanced Patterns¶
Complex multi-agent workflows and architectural patterns for production systems.
Agent Chaining¶
Execute agents sequentially, passing results between them:
import { Agent, Rule, Tool } from '@arcaelas/agent';
const research_agent = new Agent({
rules: [new Rule("Information gathering specialist.")],
tools: [search_tool, scrape_tool],
providers: [openai_provider]
});
const analyzer_agent = new Agent({
rules: [new Rule("Data analysis specialist.")],
tools: [stats_tool, chart_tool],
providers: [openai_provider]
});
const writer_agent = new Agent({
rules: [new Rule("Content writing specialist.")],
tools: [grammar_tool, style_tool],
providers: [openai_provider]
});
async function research_and_report(topic: string) {
// Step 1: Research
const [research_msgs, success1] = await research_agent.call(
`Research current trends about: ${topic}`
);
if (!success1) throw new Error("Research failed");
// Step 2: Analyze
const research_data = research_msgs[research_msgs.length - 1].content;
const [analysis_msgs, success2] = await analyzer_agent.call(
`Analyze this research data and find key insights: ${research_data}`
);
if (!success2) throw new Error("Analysis failed");
// Step 3: Write report
const analysis_data = analysis_msgs[analysis_msgs.length - 1].content;
const [report_msgs, success3] = await writer_agent.call(
`Write a comprehensive report based on this analysis: ${analysis_data}`
);
if (!success3) throw new Error("Writing failed");
return report_msgs[report_msgs.length - 1].content;
}
// Usage
const report = await research_and_report("AI Agent Architectures");
console.log(report);
Parallel Agent Execution¶
Run multiple agents concurrently for better performance:
async function parallel_analysis(text: string) {
const [sentiment_result, summary_result, keywords_result] = await Promise.allSettled([
sentiment_agent.call(`Analyze sentiment: ${text}`),
summary_agent.call(`Summarize: ${text}`),
keywords_agent.call(`Extract keywords: ${text}`)
]);
return {
sentiment: sentiment_result.status === 'fulfilled'
? sentiment_result.value[0]
: null,
summary: summary_result.status === 'fulfilled'
? summary_result.value[0]
: null,
keywords: keywords_result.status === 'fulfilled'
? keywords_result.value[0]
: null
};
}
Supervisor Pattern¶
One agent coordinates multiple specialized agents:
const supervisor = new Agent({
rules: [new Rule("Coordinates specialized agents for complex tasks.")],
tools: [
new Tool('delegate_to_researcher', async (agent, { query }) => {
const [msgs, ok] = await research_agent.call(query);
return ok ? msgs[msgs.length - 1].content : "Research failed";
}),
new Tool('delegate_to_analyst', async (agent, { data }) => {
const [msgs, ok] = await analyzer_agent.call(data);
return ok ? msgs[msgs.length - 1].content : "Analysis failed";
}),
new Tool('delegate_to_writer', async (agent, { content }) => {
const [msgs, ok] = await writer_agent.call(content);
return ok ? msgs[msgs.length - 1].content : "Writing failed";
})
],
providers: [openai_provider]
});
// Supervisor decides which agents to use and in what order
const [result, ok] = await supervisor.call(
"Create a comprehensive report about quantum computing trends"
);
State Machine Pattern¶
Agent behavior based on conversation state:
type ConversationState = 'greeting' | 'gathering_info' | 'processing' | 'delivering_result' | 'closing';
class StatefulAgent {
private agent: Agent;
private state: ConversationState = 'greeting';
constructor() {
this.agent = new Agent({
rules: [new Rule("Agent with conversation state management.")],
providers: [openai_provider]
});
}
async chat(message: string) {
// Update state based on message
if (message.toLowerCase().includes('bye')) {
this.state = 'closing';
} else if (this.state === 'greeting') {
this.state = 'gathering_info';
}
// Add state to context
this.agent.metadata.set('current_state', this.state);
this.agent.rules = [
new Rule(`Current conversation state: ${this.state}. Act accordingly.`)
];
const [msgs, ok] = await this.agent.call(message);
// Transition to next state
if (this.state === 'gathering_info' && ok) {
this.state = 'processing';
} else if (this.state === 'processing' && ok) {
this.state = 'delivering_result';
}
return [msgs, ok];
}
}
const stateful = new StatefulAgent();
await stateful.chat("Hello"); // state: greeting → gathering_info
await stateful.chat("I need help with..."); // state: gathering_info → processing
await stateful.chat("Thanks!"); // state: processing → delivering_result
await stateful.chat("Goodbye"); // state: closing
Event-Driven Architecture¶
Agents respond to events:
import { EventEmitter } from 'events';
class EventDrivenAgentSystem extends EventEmitter {
private agents: Map<string, Agent> = new Map();
register_agent(name: string, agent: Agent, events: string[]) {
this.agents.set(name, agent);
events.forEach(event => {
this.on(event, async (data) => {
console.log(`Agent ${name} handling event: ${event}`);
await agent.call(data.message);
});
});
}
async trigger(event: string, data: any) {
this.emit(event, data);
}
}
// Setup
const system = new EventDrivenAgentSystem();
system.register_agent('monitor', monitoring_agent, ['user_action', 'system_alert']);
system.register_agent('support', support_agent, ['user_question', 'error_report']);
system.register_agent('logger', logging_agent, ['user_action', 'user_question', 'system_alert']);
// Trigger events
await system.trigger('user_action', { message: 'User clicked button' });
await system.trigger('user_question', { message: 'How do I reset my password?' });
await system.trigger('system_alert', { message: 'High memory usage detected' });
Tool Composition¶
Combine simple tools into complex workflows:
const fetch_tool = new Tool('fetch_url', async (agent, { url }) => {
const response = await fetch(url);
return await response.text();
});
const parse_tool = new Tool('parse_html', async (agent, { html }) => {
// Parse HTML and extract structured data
return JSON.stringify({ title: "...", content: "..." });
});
const summarize_tool = new Tool('summarize_text', async (agent, { text }) => {
// Use another agent for summarization
const [msgs, ok] = await summary_agent.call(`Summarize: ${text}`);
return ok ? msgs[msgs.length - 1].content : "Failed to summarize";
});
// Complex tool that combines others
const web_research_tool = new Tool('research_url', async (agent, { url }) => {
// Fetch → Parse → Summarize
const html = await fetch_tool.func(agent, { url });
const data = await parse_tool.func(agent, { html });
const summary = await summarize_tool.func(agent, { text: data });
return summary;
});
const research_agent = new Agent({
rules: [new Rule("Researches and summarizes web content.")],
tools: [web_research_tool],
providers: [openai_provider]
});
Memory and Persistence¶
Maintain long-term conversation memory:
interface ConversationMemory {
user_id: string;
messages: Message[];
metadata: Record<string, any>;
created_at: Date;
updated_at: Date;
}
class PersistentAgent {
private agent: Agent;
private db: Database; // Your database interface
constructor(db: Database) {
this.db = db;
this.agent = new Agent({
rules: [new Rule("Agent with conversation persistence.")],
providers: [openai_provider]
});
}
async chat(user_id: string, message: string) {
// Load conversation history
const memory = await this.db.get_conversation(user_id);
if (memory) {
// Restore previous messages
this.agent.messages = memory.messages;
// Restore metadata
Object.entries(memory.metadata).forEach(([k, v]) => {
this.agent.metadata.set(k, v);
});
}
// Process new message
const [msgs, ok] = await this.agent.call(message);
// Save updated conversation
await this.db.save_conversation({
user_id,
messages: msgs,
metadata: this.agent.metadata.toJSON(),
created_at: memory?.created_at || new Date(),
updated_at: new Date()
});
return [msgs, ok];
}
}
Rate Limiting and Throttling¶
Control request frequency:
class ThrottledProvider {
private last_call: number = 0;
private min_interval: number = 1000; // 1 second between calls
async provider(ctx: Context): Promise<ChatCompletionResponse> {
const now = Date.now();
const elapsed = now - this.last_call;
if (elapsed < this.min_interval) {
await new Promise(resolve =>
setTimeout(resolve, this.min_interval - elapsed)
);
}
this.last_call = Date.now();
return await openai.chat.completions.create({
model: "gpt-4",
messages: ctx.messages.map(m => ({ role: m.role, content: m.content }))
});
}
}
const throttled = new ThrottledProvider();
const agent = new Agent({
rules: [new Rule("Agent with rate limiting.")],
providers: [throttled.provider.bind(throttled)]
});
Circuit Breaker Pattern¶
Prevent cascading failures:
class CircuitBreaker {
private failures: number = 0;
private last_failure: number = 0;
private readonly threshold: number = 3;
private readonly timeout: number = 60000; // 1 minute
private state: 'closed' | 'open' | 'half-open' = 'closed';
async call_provider(provider: Provider, ctx: Context): Promise<ChatCompletionResponse | null> {
// Check circuit state
if (this.state === 'open') {
if (Date.now() - this.last_failure > this.timeout) {
this.state = 'half-open';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const response = await provider(ctx);
// Success - reset if in half-open
if (this.state === 'half-open') {
this.state = 'closed';
this.failures = 0;
}
return response;
} catch (error) {
this.failures++;
this.last_failure = Date.now();
if (this.failures >= this.threshold) {
this.state = 'open';
console.log('Circuit breaker opened due to repeated failures');
}
throw error;
}
}
}
const breaker = new CircuitBreaker();
const protected_provider: Provider = async (ctx) => {
return await breaker.call_provider(openai_provider, ctx);
};
Best Practices¶
- Error Boundaries - Isolate failures to prevent system-wide crashes
- Idempotency - Ensure operations can be safely retried
- Observability - Log all agent interactions for debugging
- Resource Limits - Set timeouts and max retries
- Graceful Degradation - Provide fallback responses
- Testing - Unit test each agent and integration test workflows
Next Steps¶
- API Reference - Complete API documentation
- Best Practices - Production guidelines
- Architecture - System design patterns