rcrewai 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/api/agent.md +429 -0
- data/docs/api/task.md +494 -0
- data/docs/examples/api-integration.md +829 -0
- data/docs/examples/async-execution.md +893 -0
- data/docs/examples/code-review-crew.md +660 -0
- data/docs/examples/content-marketing-pipeline.md +681 -0
- data/docs/examples/custom-tools.md +1224 -0
- data/docs/examples/customer-support.md +717 -0
- data/docs/examples/data-analysis-team.md +677 -0
- data/docs/examples/database-operations.md +1298 -0
- data/docs/examples/ecommerce-operations.md +990 -0
- data/docs/examples/financial-analysis.md +857 -0
- data/docs/examples/hierarchical-crew.md +479 -0
- data/docs/examples/product-development.md +688 -0
- data/docs/examples/production-ready-crew.md +384 -408
- data/docs/examples/research-development.md +1225 -0
- data/docs/examples/social-media.md +1073 -0
- data/docs/examples/task-automation.md +527 -0
- data/docs/examples/tool-composition.md +1075 -0
- data/docs/examples/web-scraping.md +1201 -0
- data/docs/tutorials/advanced-agents.md +1014 -0
- data/docs/tutorials/custom-tools.md +1242 -0
- data/docs/tutorials/deployment.md +1836 -0
- data/docs/tutorials/index.md +184 -0
- data/docs/tutorials/multiple-crews.md +1692 -0
- data/lib/rcrewai/llm_clients/anthropic.rb +1 -1
- data/lib/rcrewai/version.rb +1 -1
- metadata +26 -2
@@ -0,0 +1,1692 @@
|
|
1
|
+
---
|
2
|
+
layout: tutorial
|
3
|
+
title: Working with Multiple Crews
|
4
|
+
description: Learn how to coordinate multiple AI crews for complex, multi-phase operations and large-scale workflows
|
5
|
+
---
|
6
|
+
|
7
|
+
# Working with Multiple Crews
|
8
|
+
|
9
|
+
This tutorial demonstrates how to work with multiple crews to handle complex, multi-phase operations that require different specialized teams working together. You'll learn crew coordination, inter-crew communication, resource sharing, and orchestration patterns.
|
10
|
+
|
11
|
+
## Table of Contents
|
12
|
+
1. [Understanding Multi-Crew Architecture](#understanding-multi-crew-architecture)
|
13
|
+
2. [Crew Coordination Patterns](#crew-coordination-patterns)
|
14
|
+
3. [Sequential Crew Execution](#sequential-crew-execution)
|
15
|
+
4. [Parallel Crew Operations](#parallel-crew-operations)
|
16
|
+
5. [Resource Sharing Between Crews](#resource-sharing-between-crews)
|
17
|
+
6. [Cross-Crew Communication](#cross-crew-communication)
|
18
|
+
7. [Orchestration Strategies](#orchestration-strategies)
|
19
|
+
8. [Production Multi-Crew Systems](#production-multi-crew-systems)
|
20
|
+
|
21
|
+
## Understanding Multi-Crew Architecture
|
22
|
+
|
23
|
+
Multiple crews are useful when you have distinct phases or domains that require different specialized teams.
|
24
|
+
|
25
|
+
### When to Use Multiple Crews
|
26
|
+
|
27
|
+
- **Phase-based workflows**: Development → Testing → Deployment
|
28
|
+
- **Domain separation**: Research → Engineering → Marketing
|
29
|
+
- **Scale requirements**: Multiple independent operations
|
30
|
+
- **Resource optimization**: Different crews need different resources
|
31
|
+
- **Fault isolation**: Failures in one crew don't affect others
|
32
|
+
|
33
|
+
### Basic Multi-Crew Setup
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'rcrewai'
|
37
|
+
|
38
|
+
# Configure RCrewAI
|
39
|
+
RCrewAI.configure do |config|
|
40
|
+
config.llm_provider = :openai
|
41
|
+
config.temperature = 0.3
|
42
|
+
end
|
43
|
+
|
44
|
+
# ===== RESEARCH CREW =====
|
45
|
+
research_crew = RCrewAI::Crew.new("research_team")
|
46
|
+
|
47
|
+
market_researcher = RCrewAI::Agent.new(
|
48
|
+
name: "market_researcher",
|
49
|
+
role: "Market Research Analyst",
|
50
|
+
goal: "Gather comprehensive market intelligence and consumer insights",
|
51
|
+
backstory: "Expert analyst with deep knowledge of market trends and consumer behavior.",
|
52
|
+
tools: [RCrewAI::Tools::WebSearch.new]
|
53
|
+
)
|
54
|
+
|
55
|
+
trend_analyst = RCrewAI::Agent.new(
|
56
|
+
name: "trend_analyst",
|
57
|
+
role: "Trend Analysis Specialist",
|
58
|
+
goal: "Identify emerging trends and forecast market direction",
|
59
|
+
backstory: "Specialist in trend analysis with predictive modeling expertise.",
|
60
|
+
tools: [RCrewAI::Tools::WebSearch.new, RCrewAI::Tools::FileReader.new]
|
61
|
+
)
|
62
|
+
|
63
|
+
research_crew.add_agent(market_researcher)
|
64
|
+
research_crew.add_agent(trend_analyst)
|
65
|
+
|
66
|
+
# ===== DEVELOPMENT CREW =====
|
67
|
+
dev_crew = RCrewAI::Crew.new("development_team", process: :hierarchical)
|
68
|
+
|
69
|
+
tech_lead = RCrewAI::Agent.new(
|
70
|
+
name: "tech_lead",
|
71
|
+
role: "Technical Lead",
|
72
|
+
goal: "Coordinate development efforts and ensure technical excellence",
|
73
|
+
backstory: "Senior technical leader with expertise in system architecture and team coordination.",
|
74
|
+
manager: true,
|
75
|
+
allow_delegation: true,
|
76
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
77
|
+
)
|
78
|
+
|
79
|
+
senior_developer = RCrewAI::Agent.new(
|
80
|
+
name: "senior_developer",
|
81
|
+
role: "Senior Software Developer",
|
82
|
+
goal: "Build robust and scalable software solutions",
|
83
|
+
backstory: "Experienced developer skilled in multiple programming languages and frameworks.",
|
84
|
+
tools: [RCrewAI::Tools::FileWriter.new, RCrewAI::Tools::FileReader.new]
|
85
|
+
)
|
86
|
+
|
87
|
+
dev_crew.add_agent(tech_lead)
|
88
|
+
dev_crew.add_agent(senior_developer)
|
89
|
+
```
|
90
|
+
|
91
|
+
## Crew Coordination Patterns
|
92
|
+
|
93
|
+
### 1. Pipeline Pattern (Sequential)
|
94
|
+
|
95
|
+
Crews execute one after another, passing results downstream:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
class CrewPipeline
|
99
|
+
def initialize(*crews)
|
100
|
+
@crews = crews
|
101
|
+
@pipeline_results = []
|
102
|
+
end
|
103
|
+
|
104
|
+
def execute(initial_data = {})
|
105
|
+
current_data = initial_data
|
106
|
+
|
107
|
+
@crews.each_with_index do |crew, index|
|
108
|
+
puts "🚀 Executing pipeline stage #{index + 1}: #{crew.name}"
|
109
|
+
|
110
|
+
# Pass previous results as context
|
111
|
+
if index > 0
|
112
|
+
crew.add_shared_context(@pipeline_results[index - 1])
|
113
|
+
end
|
114
|
+
|
115
|
+
# Execute crew
|
116
|
+
stage_result = crew.execute
|
117
|
+
@pipeline_results << stage_result
|
118
|
+
|
119
|
+
# Transform result for next stage
|
120
|
+
current_data = transform_for_next_stage(stage_result, index)
|
121
|
+
|
122
|
+
puts "✅ Stage #{index + 1} completed"
|
123
|
+
end
|
124
|
+
|
125
|
+
{
|
126
|
+
pipeline_results: @pipeline_results,
|
127
|
+
final_result: current_data,
|
128
|
+
success_rate: calculate_success_rate
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def transform_for_next_stage(result, stage_index)
|
135
|
+
# Transform data between pipeline stages
|
136
|
+
{
|
137
|
+
stage: stage_index + 1,
|
138
|
+
previous_result: result,
|
139
|
+
timestamp: Time.now
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
def calculate_success_rate
|
144
|
+
successful = @pipeline_results.count { |r| r[:success_rate] > 80 }
|
145
|
+
(successful.to_f / @pipeline_results.length * 100).round(1)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Usage
|
150
|
+
pipeline = CrewPipeline.new(research_crew, dev_crew)
|
151
|
+
results = pipeline.execute(project_requirements: "Build AI-powered analytics dashboard")
|
152
|
+
```
|
153
|
+
|
154
|
+
### 2. Fan-Out Pattern (Parallel)
|
155
|
+
|
156
|
+
Multiple crews work on different aspects simultaneously:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class ParallelCrewOrchestrator
|
160
|
+
def initialize
|
161
|
+
@crews = []
|
162
|
+
@execution_threads = []
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_crew(crew, priority: :normal)
|
166
|
+
@crews << { crew: crew, priority: priority }
|
167
|
+
end
|
168
|
+
|
169
|
+
def execute_parallel(max_concurrency: 3)
|
170
|
+
puts "🚀 Starting parallel execution of #{@crews.length} crews"
|
171
|
+
|
172
|
+
# Sort by priority
|
173
|
+
sorted_crews = @crews.sort_by do |crew_config|
|
174
|
+
priority_order = { high: 0, normal: 1, low: 2 }
|
175
|
+
priority_order[crew_config[:priority]]
|
176
|
+
end
|
177
|
+
|
178
|
+
results = {}
|
179
|
+
|
180
|
+
# Execute crews in batches
|
181
|
+
sorted_crews.each_slice(max_concurrency) do |crew_batch|
|
182
|
+
threads = crew_batch.map do |crew_config|
|
183
|
+
Thread.new do
|
184
|
+
crew = crew_config[:crew]
|
185
|
+
|
186
|
+
begin
|
187
|
+
puts " Starting crew: #{crew.name}"
|
188
|
+
start_time = Time.now
|
189
|
+
|
190
|
+
result = crew.execute
|
191
|
+
duration = Time.now - start_time
|
192
|
+
|
193
|
+
{
|
194
|
+
crew_name: crew.name,
|
195
|
+
success: true,
|
196
|
+
result: result,
|
197
|
+
duration: duration,
|
198
|
+
priority: crew_config[:priority]
|
199
|
+
}
|
200
|
+
rescue => e
|
201
|
+
{
|
202
|
+
crew_name: crew.name,
|
203
|
+
success: false,
|
204
|
+
error: e.message,
|
205
|
+
duration: Time.now - start_time
|
206
|
+
}
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Wait for batch to complete
|
212
|
+
threads.each do |thread|
|
213
|
+
result = thread.value
|
214
|
+
results[result[:crew_name]] = result
|
215
|
+
|
216
|
+
status = result[:success] ? "✅" : "❌"
|
217
|
+
puts " #{status} #{result[:crew_name]} completed in #{result[:duration].round(2)}s"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
results
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Create specialized crews
|
226
|
+
content_crew = RCrewAI::Crew.new("content_creation")
|
227
|
+
seo_crew = RCrewAI::Crew.new("seo_optimization")
|
228
|
+
social_media_crew = RCrewAI::Crew.new("social_media")
|
229
|
+
|
230
|
+
# Setup orchestrator
|
231
|
+
orchestrator = ParallelCrewOrchestrator.new
|
232
|
+
orchestrator.add_crew(content_crew, priority: :high)
|
233
|
+
orchestrator.add_crew(seo_crew, priority: :normal)
|
234
|
+
orchestrator.add_crew(social_media_crew, priority: :low)
|
235
|
+
|
236
|
+
# Execute all crews in parallel
|
237
|
+
parallel_results = orchestrator.execute_parallel(max_concurrency: 2)
|
238
|
+
```
|
239
|
+
|
240
|
+
## Sequential Crew Execution
|
241
|
+
|
242
|
+
Complex workflows often require sequential execution with data flow between crews:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
# ===== E-COMMERCE LAUNCH PIPELINE =====
|
246
|
+
|
247
|
+
# Crew 1: Market Research
|
248
|
+
research_crew = RCrewAI::Crew.new("market_research")
|
249
|
+
|
250
|
+
research_crew.add_agent(RCrewAI::Agent.new(
|
251
|
+
name: "market_analyst",
|
252
|
+
role: "Market Research Analyst",
|
253
|
+
goal: "Analyze market opportunity and customer segments",
|
254
|
+
tools: [RCrewAI::Tools::WebSearch.new]
|
255
|
+
))
|
256
|
+
|
257
|
+
research_task = RCrewAI::Task.new(
|
258
|
+
name: "market_analysis",
|
259
|
+
description: "Conduct comprehensive market research for new e-commerce platform. Analyze target demographics, competition, pricing strategies, and market size.",
|
260
|
+
expected_output: "Detailed market analysis report with customer personas, competitive landscape, and go-to-market recommendations"
|
261
|
+
)
|
262
|
+
|
263
|
+
research_crew.add_task(research_task)
|
264
|
+
|
265
|
+
# Crew 2: Product Development
|
266
|
+
product_crew = RCrewAI::Crew.new("product_development")
|
267
|
+
|
268
|
+
product_crew.add_agent(RCrewAI::Agent.new(
|
269
|
+
name: "product_manager",
|
270
|
+
role: "Product Manager",
|
271
|
+
goal: "Define product requirements based on market research",
|
272
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
273
|
+
))
|
274
|
+
|
275
|
+
product_crew.add_agent(RCrewAI::Agent.new(
|
276
|
+
name: "ux_designer",
|
277
|
+
role: "UX Designer",
|
278
|
+
goal: "Design user experience based on customer insights",
|
279
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
280
|
+
))
|
281
|
+
|
282
|
+
product_task = RCrewAI::Task.new(
|
283
|
+
name: "product_specification",
|
284
|
+
description: "Based on market research findings, create detailed product specifications and user experience designs for the e-commerce platform.",
|
285
|
+
expected_output: "Product requirements document with feature specifications, user journeys, and design wireframes"
|
286
|
+
)
|
287
|
+
|
288
|
+
product_crew.add_task(product_task)
|
289
|
+
|
290
|
+
# Crew 3: Technical Implementation
|
291
|
+
tech_crew = RCrewAI::Crew.new("technical_team", process: :hierarchical)
|
292
|
+
|
293
|
+
tech_lead = RCrewAI::Agent.new(
|
294
|
+
name: "tech_lead",
|
295
|
+
role: "Technical Lead",
|
296
|
+
goal: "Coordinate technical implementation",
|
297
|
+
manager: true,
|
298
|
+
allow_delegation: true,
|
299
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
300
|
+
)
|
301
|
+
|
302
|
+
backend_dev = RCrewAI::Agent.new(
|
303
|
+
name: "backend_developer",
|
304
|
+
role: "Backend Developer",
|
305
|
+
goal: "Build scalable backend systems",
|
306
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
307
|
+
)
|
308
|
+
|
309
|
+
tech_crew.add_agent(tech_lead)
|
310
|
+
tech_crew.add_agent(backend_dev)
|
311
|
+
|
312
|
+
tech_task = RCrewAI::Task.new(
|
313
|
+
name: "technical_implementation",
|
314
|
+
description: "Implement the e-commerce platform based on product specifications. Build API, database, and core functionality.",
|
315
|
+
expected_output: "Working e-commerce platform with API documentation and deployment guide"
|
316
|
+
)
|
317
|
+
|
318
|
+
tech_crew.add_task(tech_task)
|
319
|
+
|
320
|
+
# Sequential Execution with Data Flow
|
321
|
+
class ECommerceOrchestrator
|
322
|
+
def initialize(research_crew, product_crew, tech_crew)
|
323
|
+
@research_crew = research_crew
|
324
|
+
@product_crew = product_crew
|
325
|
+
@tech_crew = tech_crew
|
326
|
+
@execution_log = []
|
327
|
+
end
|
328
|
+
|
329
|
+
def execute_launch_pipeline
|
330
|
+
puts "🚀 Starting E-commerce Launch Pipeline"
|
331
|
+
puts "="*50
|
332
|
+
|
333
|
+
# Phase 1: Market Research
|
334
|
+
log_phase("Market Research Phase")
|
335
|
+
research_results = @research_crew.execute
|
336
|
+
|
337
|
+
@execution_log << {
|
338
|
+
phase: "research",
|
339
|
+
crew: @research_crew.name,
|
340
|
+
results: research_results,
|
341
|
+
timestamp: Time.now
|
342
|
+
}
|
343
|
+
|
344
|
+
# Phase 2: Product Development (uses research data)
|
345
|
+
log_phase("Product Development Phase")
|
346
|
+
|
347
|
+
# Pass research context to product crew
|
348
|
+
research_context = extract_key_insights(research_results)
|
349
|
+
@product_crew.add_shared_context(research_context)
|
350
|
+
|
351
|
+
product_results = @product_crew.execute
|
352
|
+
|
353
|
+
@execution_log << {
|
354
|
+
phase: "product",
|
355
|
+
crew: @product_crew.name,
|
356
|
+
results: product_results,
|
357
|
+
context_from: "research",
|
358
|
+
timestamp: Time.now
|
359
|
+
}
|
360
|
+
|
361
|
+
# Phase 3: Technical Implementation (uses product specs)
|
362
|
+
log_phase("Technical Implementation Phase")
|
363
|
+
|
364
|
+
# Pass product specs to tech crew
|
365
|
+
product_context = extract_technical_requirements(product_results)
|
366
|
+
@tech_crew.add_shared_context(product_context)
|
367
|
+
|
368
|
+
tech_results = @tech_crew.execute
|
369
|
+
|
370
|
+
@execution_log << {
|
371
|
+
phase: "technical",
|
372
|
+
crew: @tech_crew.name,
|
373
|
+
results: tech_results,
|
374
|
+
context_from: "product",
|
375
|
+
timestamp: Time.now
|
376
|
+
}
|
377
|
+
|
378
|
+
# Generate final report
|
379
|
+
generate_launch_report
|
380
|
+
end
|
381
|
+
|
382
|
+
private
|
383
|
+
|
384
|
+
def log_phase(phase_name)
|
385
|
+
puts "\n🎯 #{phase_name}"
|
386
|
+
puts "-" * phase_name.length
|
387
|
+
end
|
388
|
+
|
389
|
+
def extract_key_insights(research_results)
|
390
|
+
# Extract actionable insights for product team
|
391
|
+
{
|
392
|
+
target_customers: "Extract customer personas from research",
|
393
|
+
market_size: "Extract market opportunity data",
|
394
|
+
competitive_analysis: "Extract competitive insights",
|
395
|
+
pricing_strategy: "Extract pricing recommendations"
|
396
|
+
}
|
397
|
+
end
|
398
|
+
|
399
|
+
def extract_technical_requirements(product_results)
|
400
|
+
# Extract technical requirements for development team
|
401
|
+
{
|
402
|
+
feature_list: "Extract feature specifications",
|
403
|
+
performance_requirements: "Extract performance criteria",
|
404
|
+
integration_needs: "Extract third-party integrations",
|
405
|
+
scalability_targets: "Extract scaling requirements"
|
406
|
+
}
|
407
|
+
end
|
408
|
+
|
409
|
+
def generate_launch_report
|
410
|
+
puts "\n📊 LAUNCH PIPELINE COMPLETED"
|
411
|
+
puts "="*50
|
412
|
+
|
413
|
+
total_duration = @execution_log.last[:timestamp] - @execution_log.first[:timestamp]
|
414
|
+
|
415
|
+
puts "Total Execution Time: #{total_duration.round(2)} seconds"
|
416
|
+
puts "Phases Completed: #{@execution_log.length}"
|
417
|
+
|
418
|
+
@execution_log.each_with_index do |phase, index|
|
419
|
+
success_rate = phase[:results][:success_rate] || 0
|
420
|
+
status = success_rate > 80 ? "✅" : "⚠️"
|
421
|
+
|
422
|
+
puts "#{index + 1}. #{status} #{phase[:phase].capitalize} Phase (#{phase[:crew]})"
|
423
|
+
puts " Success Rate: #{success_rate}%"
|
424
|
+
puts " Context: #{phase[:context_from] || 'None'}"
|
425
|
+
end
|
426
|
+
|
427
|
+
# Save comprehensive report
|
428
|
+
save_launch_report
|
429
|
+
end
|
430
|
+
|
431
|
+
def save_launch_report
|
432
|
+
report = {
|
433
|
+
pipeline: "E-commerce Launch",
|
434
|
+
execution_log: @execution_log,
|
435
|
+
summary: {
|
436
|
+
phases: @execution_log.length,
|
437
|
+
overall_success: @execution_log.all? { |p| p[:results][:success_rate] > 80 },
|
438
|
+
total_crews: 3
|
439
|
+
}
|
440
|
+
}
|
441
|
+
|
442
|
+
File.write("ecommerce_launch_report.json", JSON.pretty_generate(report))
|
443
|
+
puts "\n💾 Launch report saved: ecommerce_launch_report.json"
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
# Execute the complete pipeline
|
448
|
+
orchestrator = ECommerceOrchestrator.new(research_crew, product_crew, tech_crew)
|
449
|
+
orchestrator.execute_launch_pipeline
|
450
|
+
```
|
451
|
+
|
452
|
+
## Parallel Crew Operations
|
453
|
+
|
454
|
+
When crews can work independently, parallel execution dramatically improves performance:
|
455
|
+
|
456
|
+
```ruby
|
457
|
+
# ===== CONTENT MARKETING CAMPAIGN =====
|
458
|
+
|
459
|
+
# Multiple specialized crews working in parallel
|
460
|
+
class ContentMarketingCampaign
|
461
|
+
def initialize
|
462
|
+
@crews = {}
|
463
|
+
@shared_resources = {
|
464
|
+
brand_guidelines: "Brand voice and style guidelines",
|
465
|
+
target_audience: "Target customer personas",
|
466
|
+
campaign_theme: "Q4 Holiday Campaign"
|
467
|
+
}
|
468
|
+
end
|
469
|
+
|
470
|
+
def setup_crews
|
471
|
+
# Content Creation Crew
|
472
|
+
@crews[:content] = create_content_crew
|
473
|
+
|
474
|
+
# SEO Optimization Crew
|
475
|
+
@crews[:seo] = create_seo_crew
|
476
|
+
|
477
|
+
# Social Media Crew
|
478
|
+
@crews[:social] = create_social_crew
|
479
|
+
|
480
|
+
# Email Marketing Crew
|
481
|
+
@crews[:email] = create_email_crew
|
482
|
+
|
483
|
+
# Analytics Crew
|
484
|
+
@crews[:analytics] = create_analytics_crew
|
485
|
+
end
|
486
|
+
|
487
|
+
def execute_campaign
|
488
|
+
puts "🚀 Launching Multi-Crew Content Marketing Campaign"
|
489
|
+
puts "Crews: #{@crews.keys.join(', ')}"
|
490
|
+
puts "="*60
|
491
|
+
|
492
|
+
# Execute crews in parallel with different priorities
|
493
|
+
parallel_executor = ThreadPoolExecutor.new(max_threads: 3)
|
494
|
+
|
495
|
+
futures = {}
|
496
|
+
|
497
|
+
@crews.each do |crew_type, crew|
|
498
|
+
futures[crew_type] = parallel_executor.submit do
|
499
|
+
execute_crew_with_monitoring(crew_type, crew)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# Wait for all crews to complete
|
504
|
+
results = {}
|
505
|
+
futures.each do |crew_type, future|
|
506
|
+
results[crew_type] = future.value
|
507
|
+
end
|
508
|
+
|
509
|
+
parallel_executor.shutdown
|
510
|
+
|
511
|
+
# Analyze cross-crew results
|
512
|
+
analyze_campaign_results(results)
|
513
|
+
end
|
514
|
+
|
515
|
+
private
|
516
|
+
|
517
|
+
def create_content_crew
|
518
|
+
crew = RCrewAI::Crew.new("content_creation")
|
519
|
+
|
520
|
+
content_strategist = RCrewAI::Agent.new(
|
521
|
+
name: "content_strategist",
|
522
|
+
role: "Content Strategy Lead",
|
523
|
+
goal: "Create compelling content that drives engagement and conversions",
|
524
|
+
tools: [RCrewAI::Tools::WebSearch.new, RCrewAI::Tools::FileWriter.new]
|
525
|
+
)
|
526
|
+
|
527
|
+
copywriter = RCrewAI::Agent.new(
|
528
|
+
name: "copywriter",
|
529
|
+
role: "Senior Copywriter",
|
530
|
+
goal: "Write persuasive copy that connects with target audience",
|
531
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
532
|
+
)
|
533
|
+
|
534
|
+
crew.add_agent(content_strategist)
|
535
|
+
crew.add_agent(copywriter)
|
536
|
+
|
537
|
+
# Add shared context
|
538
|
+
crew.add_shared_context(@shared_resources)
|
539
|
+
|
540
|
+
# Content tasks
|
541
|
+
crew.add_task(RCrewAI::Task.new(
|
542
|
+
name: "blog_content",
|
543
|
+
description: "Create 5 high-quality blog posts for Q4 campaign",
|
544
|
+
expected_output: "5 blog posts with SEO optimization and engagement hooks",
|
545
|
+
async: true
|
546
|
+
))
|
547
|
+
|
548
|
+
crew.add_task(RCrewAI::Task.new(
|
549
|
+
name: "landing_pages",
|
550
|
+
description: "Create compelling landing page copy for campaign",
|
551
|
+
expected_output: "Landing page copy with clear CTAs and value propositions",
|
552
|
+
async: true
|
553
|
+
))
|
554
|
+
|
555
|
+
crew
|
556
|
+
end
|
557
|
+
|
558
|
+
def create_seo_crew
|
559
|
+
crew = RCrewAI::Crew.new("seo_optimization")
|
560
|
+
|
561
|
+
seo_specialist = RCrewAI::Agent.new(
|
562
|
+
name: "seo_specialist",
|
563
|
+
role: "SEO Specialist",
|
564
|
+
goal: "Optimize content for maximum organic visibility",
|
565
|
+
tools: [RCrewAI::Tools::WebSearch.new, RCrewAI::Tools::FileReader.new]
|
566
|
+
)
|
567
|
+
|
568
|
+
crew.add_agent(seo_specialist)
|
569
|
+
crew.add_shared_context(@shared_resources)
|
570
|
+
|
571
|
+
crew.add_task(RCrewAI::Task.new(
|
572
|
+
name: "keyword_research",
|
573
|
+
description: "Research high-value keywords for Q4 campaign",
|
574
|
+
expected_output: "Keyword strategy with search volumes and competition analysis",
|
575
|
+
async: true
|
576
|
+
))
|
577
|
+
|
578
|
+
crew.add_task(RCrewAI::Task.new(
|
579
|
+
name: "content_optimization",
|
580
|
+
description: "Optimize existing content for SEO best practices",
|
581
|
+
expected_output: "SEO-optimized content with meta descriptions and schema markup",
|
582
|
+
async: true
|
583
|
+
))
|
584
|
+
|
585
|
+
crew
|
586
|
+
end
|
587
|
+
|
588
|
+
def create_social_crew
|
589
|
+
crew = RCrewAI::Crew.new("social_media")
|
590
|
+
|
591
|
+
social_manager = RCrewAI::Agent.new(
|
592
|
+
name: "social_manager",
|
593
|
+
role: "Social Media Manager",
|
594
|
+
goal: "Create engaging social content that drives community growth",
|
595
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
596
|
+
)
|
597
|
+
|
598
|
+
crew.add_agent(social_manager)
|
599
|
+
crew.add_shared_context(@shared_resources)
|
600
|
+
|
601
|
+
crew.add_task(RCrewAI::Task.new(
|
602
|
+
name: "social_content",
|
603
|
+
description: "Create 30 days of social media content for multiple platforms",
|
604
|
+
expected_output: "Social media calendar with platform-specific content and hashtags",
|
605
|
+
async: true
|
606
|
+
))
|
607
|
+
|
608
|
+
crew
|
609
|
+
end
|
610
|
+
|
611
|
+
def create_email_crew
|
612
|
+
crew = RCrewAI::Crew.new("email_marketing")
|
613
|
+
|
614
|
+
email_specialist = RCrewAI::Agent.new(
|
615
|
+
name: "email_specialist",
|
616
|
+
role: "Email Marketing Specialist",
|
617
|
+
goal: "Create high-converting email campaigns",
|
618
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
619
|
+
)
|
620
|
+
|
621
|
+
crew.add_agent(email_specialist)
|
622
|
+
crew.add_shared_context(@shared_resources)
|
623
|
+
|
624
|
+
crew.add_task(RCrewAI::Task.new(
|
625
|
+
name: "email_sequence",
|
626
|
+
description: "Create automated email sequence for Q4 campaign",
|
627
|
+
expected_output: "Email sequence with subject lines, templates, and automation rules",
|
628
|
+
async: true
|
629
|
+
))
|
630
|
+
|
631
|
+
crew
|
632
|
+
end
|
633
|
+
|
634
|
+
def create_analytics_crew
|
635
|
+
crew = RCrewAI::Crew.new("analytics_tracking")
|
636
|
+
|
637
|
+
data_analyst = RCrewAI::Agent.new(
|
638
|
+
name: "data_analyst",
|
639
|
+
role: "Marketing Data Analyst",
|
640
|
+
goal: "Set up tracking and measurement for campaign success",
|
641
|
+
tools: [RCrewAI::Tools::FileWriter.new]
|
642
|
+
)
|
643
|
+
|
644
|
+
crew.add_agent(data_analyst)
|
645
|
+
crew.add_shared_context(@shared_resources)
|
646
|
+
|
647
|
+
crew.add_task(RCrewAI::Task.new(
|
648
|
+
name: "tracking_setup",
|
649
|
+
description: "Set up comprehensive tracking for all campaign channels",
|
650
|
+
expected_output: "Analytics setup guide with KPIs, tracking codes, and dashboards",
|
651
|
+
async: true
|
652
|
+
))
|
653
|
+
|
654
|
+
crew
|
655
|
+
end
|
656
|
+
|
657
|
+
def execute_crew_with_monitoring(crew_type, crew)
|
658
|
+
start_time = Time.now
|
659
|
+
|
660
|
+
puts "🔄 Starting #{crew_type} crew..."
|
661
|
+
|
662
|
+
begin
|
663
|
+
results = crew.execute
|
664
|
+
duration = Time.now - start_time
|
665
|
+
|
666
|
+
puts "✅ #{crew_type} crew completed in #{duration.round(2)}s"
|
667
|
+
|
668
|
+
{
|
669
|
+
crew_type: crew_type,
|
670
|
+
success: true,
|
671
|
+
results: results,
|
672
|
+
duration: duration,
|
673
|
+
crew_name: crew.name
|
674
|
+
}
|
675
|
+
rescue => e
|
676
|
+
duration = Time.now - start_time
|
677
|
+
|
678
|
+
puts "❌ #{crew_type} crew failed: #{e.message}"
|
679
|
+
|
680
|
+
{
|
681
|
+
crew_type: crew_type,
|
682
|
+
success: false,
|
683
|
+
error: e.message,
|
684
|
+
duration: duration,
|
685
|
+
crew_name: crew.name
|
686
|
+
}
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
def analyze_campaign_results(results)
|
691
|
+
puts "\n📊 CAMPAIGN EXECUTION RESULTS"
|
692
|
+
puts "="*50
|
693
|
+
|
694
|
+
successful_crews = results.values.count { |r| r[:success] }
|
695
|
+
total_duration = results.values.map { |r| r[:duration] }.max
|
696
|
+
|
697
|
+
puts "Success Rate: #{successful_crews}/#{results.length} crews (#{(successful_crews.to_f/results.length*100).round(1)}%)"
|
698
|
+
puts "Total Duration: #{total_duration.round(2)} seconds (parallel execution)"
|
699
|
+
puts "Sequential Duration: #{results.values.sum { |r| r[:duration] }.round(2)} seconds (estimated)"
|
700
|
+
puts "Time Saved: #{((results.values.sum { |r| r[:duration] } - total_duration) / results.values.sum { |r| r[:duration] } * 100).round(1)}%"
|
701
|
+
|
702
|
+
puts "\nCrew Performance:"
|
703
|
+
results.each do |crew_type, result|
|
704
|
+
status = result[:success] ? "✅" : "❌"
|
705
|
+
puts " #{status} #{crew_type.to_s.capitalize}: #{result[:duration].round(2)}s"
|
706
|
+
|
707
|
+
if result[:success]
|
708
|
+
success_rate = result[:results][:success_rate] || 0
|
709
|
+
puts " Success Rate: #{success_rate}%"
|
710
|
+
puts " Tasks: #{result[:results][:completed_tasks] || 0}/#{result[:results][:total_tasks] || 0}"
|
711
|
+
else
|
712
|
+
puts " Error: #{result[:error]}"
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
# Save campaign results
|
717
|
+
campaign_report = {
|
718
|
+
campaign: "Q4 Content Marketing",
|
719
|
+
execution_type: "parallel",
|
720
|
+
crews: results,
|
721
|
+
summary: {
|
722
|
+
success_rate: (successful_crews.to_f/results.length*100).round(1),
|
723
|
+
total_duration: total_duration,
|
724
|
+
time_saved_percentage: ((results.values.sum { |r| r[:duration] } - total_duration) / results.values.sum { |r| r[:duration] } * 100).round(1)
|
725
|
+
}
|
726
|
+
}
|
727
|
+
|
728
|
+
File.write("campaign_results.json", JSON.pretty_generate(campaign_report))
|
729
|
+
puts "\n💾 Campaign report saved: campaign_results.json"
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
# Thread pool executor for parallel crew execution
|
734
|
+
class ThreadPoolExecutor
|
735
|
+
def initialize(max_threads: 5)
|
736
|
+
@max_threads = max_threads
|
737
|
+
@threads = []
|
738
|
+
end
|
739
|
+
|
740
|
+
def submit(&block)
|
741
|
+
Future.new(&block).tap do |future|
|
742
|
+
if @threads.length < @max_threads
|
743
|
+
thread = Thread.new { future.execute }
|
744
|
+
@threads << thread
|
745
|
+
else
|
746
|
+
# Wait for a thread to finish
|
747
|
+
@threads.first.join
|
748
|
+
@threads.shift
|
749
|
+
thread = Thread.new { future.execute }
|
750
|
+
@threads << thread
|
751
|
+
end
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
def shutdown
|
756
|
+
@threads.each(&:join)
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
class Future
|
761
|
+
def initialize(&block)
|
762
|
+
@block = block
|
763
|
+
@executed = false
|
764
|
+
@result = nil
|
765
|
+
@error = nil
|
766
|
+
end
|
767
|
+
|
768
|
+
def execute
|
769
|
+
return if @executed
|
770
|
+
|
771
|
+
begin
|
772
|
+
@result = @block.call
|
773
|
+
rescue => e
|
774
|
+
@error = e
|
775
|
+
ensure
|
776
|
+
@executed = true
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def value
|
781
|
+
return @result if @executed && @error.nil?
|
782
|
+
raise @error if @error
|
783
|
+
@result
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
# Execute the campaign
|
788
|
+
campaign = ContentMarketingCampaign.new
|
789
|
+
campaign.setup_crews
|
790
|
+
campaign.execute_campaign
|
791
|
+
```
|
792
|
+
|
793
|
+
## Resource Sharing Between Crews
|
794
|
+
|
795
|
+
Crews can share resources, data, and configurations:
|
796
|
+
|
797
|
+
```ruby
|
798
|
+
class SharedResourceManager
|
799
|
+
def initialize
|
800
|
+
@shared_data = {}
|
801
|
+
@resource_locks = {}
|
802
|
+
end
|
803
|
+
|
804
|
+
def store_resource(key, value, crew_name)
|
805
|
+
@shared_data[key] = {
|
806
|
+
value: value,
|
807
|
+
created_by: crew_name,
|
808
|
+
created_at: Time.now,
|
809
|
+
accessed_by: []
|
810
|
+
}
|
811
|
+
|
812
|
+
puts "📦 Resource '#{key}' stored by #{crew_name}"
|
813
|
+
end
|
814
|
+
|
815
|
+
def get_resource(key, crew_name)
|
816
|
+
return nil unless @shared_data[key]
|
817
|
+
|
818
|
+
resource = @shared_data[key]
|
819
|
+
resource[:accessed_by] << crew_name unless resource[:accessed_by].include?(crew_name)
|
820
|
+
|
821
|
+
puts "📤 Resource '#{key}' accessed by #{crew_name}"
|
822
|
+
resource[:value]
|
823
|
+
end
|
824
|
+
|
825
|
+
def lock_resource(key, crew_name)
|
826
|
+
@resource_locks[key] = crew_name
|
827
|
+
puts "🔒 Resource '#{key}' locked by #{crew_name}"
|
828
|
+
end
|
829
|
+
|
830
|
+
def unlock_resource(key, crew_name)
|
831
|
+
if @resource_locks[key] == crew_name
|
832
|
+
@resource_locks.delete(key)
|
833
|
+
puts "🔓 Resource '#{key}' unlocked by #{crew_name}"
|
834
|
+
end
|
835
|
+
end
|
836
|
+
|
837
|
+
def resource_stats
|
838
|
+
@shared_data.each do |key, resource|
|
839
|
+
puts "Resource: #{key}"
|
840
|
+
puts " Created by: #{resource[:created_by]}"
|
841
|
+
puts " Accessed by: #{resource[:accessed_by].join(', ')}"
|
842
|
+
puts " Created at: #{resource[:created_at]}"
|
843
|
+
puts
|
844
|
+
end
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
# Global resource manager
|
849
|
+
$resource_manager = SharedResourceManager.new
|
850
|
+
|
851
|
+
# Enhanced crew with resource sharing
|
852
|
+
class ResourceAwareCrew < RCrewAI::Crew
|
853
|
+
def initialize(name, **options)
|
854
|
+
super
|
855
|
+
@resource_manager = $resource_manager
|
856
|
+
end
|
857
|
+
|
858
|
+
def share_resource(key, value)
|
859
|
+
@resource_manager.store_resource(key, value, @name)
|
860
|
+
end
|
861
|
+
|
862
|
+
def get_shared_resource(key)
|
863
|
+
@resource_manager.get_resource(key, @name)
|
864
|
+
end
|
865
|
+
|
866
|
+
def execute_with_resources
|
867
|
+
puts "🚀 #{@name} starting execution with shared resources"
|
868
|
+
|
869
|
+
# Get shared resources before execution
|
870
|
+
setup_shared_context
|
871
|
+
|
872
|
+
# Execute normally
|
873
|
+
results = execute
|
874
|
+
|
875
|
+
# Share results with other crews
|
876
|
+
share_execution_results(results)
|
877
|
+
|
878
|
+
results
|
879
|
+
end
|
880
|
+
|
881
|
+
private
|
882
|
+
|
883
|
+
def setup_shared_context
|
884
|
+
# Get available shared resources
|
885
|
+
shared_config = get_shared_resource("global_config")
|
886
|
+
shared_data = get_shared_resource("processed_data")
|
887
|
+
|
888
|
+
if shared_config
|
889
|
+
add_shared_context(shared_config)
|
890
|
+
end
|
891
|
+
|
892
|
+
if shared_data
|
893
|
+
add_shared_context({ shared_data: shared_data })
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
def share_execution_results(results)
|
898
|
+
# Share key results with other crews
|
899
|
+
share_resource("#{@name}_results", {
|
900
|
+
success_rate: results[:success_rate],
|
901
|
+
key_outputs: extract_key_outputs(results),
|
902
|
+
execution_time: Time.now
|
903
|
+
})
|
904
|
+
end
|
905
|
+
|
906
|
+
def extract_key_outputs(results)
|
907
|
+
# Extract the most useful outputs for other crews
|
908
|
+
results[:results]&.map { |r| r[:result] }&.join("\n\n")[0..500] || ""
|
909
|
+
end
|
910
|
+
end
|
911
|
+
|
912
|
+
# Example: Product Launch with Resource Sharing
|
913
|
+
research_crew = ResourceAwareCrew.new("market_research")
|
914
|
+
development_crew = ResourceAwareCrew.new("product_development")
|
915
|
+
marketing_crew = ResourceAwareCrew.new("marketing_launch")
|
916
|
+
|
917
|
+
# Share global configuration
|
918
|
+
$resource_manager.store_resource("global_config", {
|
919
|
+
product_name: "AI Analytics Platform",
|
920
|
+
target_market: "B2B SaaS",
|
921
|
+
launch_date: "2024-Q2",
|
922
|
+
budget: "$500K"
|
923
|
+
}, "orchestrator")
|
924
|
+
|
925
|
+
# Execute crews with resource sharing
|
926
|
+
puts "🌐 Multi-Crew Product Launch with Resource Sharing"
|
927
|
+
puts "="*60
|
928
|
+
|
929
|
+
# Research phase shares market data
|
930
|
+
research_results = research_crew.execute_with_resources
|
931
|
+
|
932
|
+
# Development phase uses market data
|
933
|
+
development_results = development_crew.execute_with_resources
|
934
|
+
|
935
|
+
# Marketing phase uses both previous results
|
936
|
+
marketing_results = marketing_crew.execute_with_resources
|
937
|
+
|
938
|
+
# Show resource usage
|
939
|
+
puts "\n📊 SHARED RESOURCE STATISTICS"
|
940
|
+
puts "-"*40
|
941
|
+
$resource_manager.resource_stats
|
942
|
+
```
|
943
|
+
|
944
|
+
## Cross-Crew Communication
|
945
|
+
|
946
|
+
Enable crews to communicate and coordinate during execution:
|
947
|
+
|
948
|
+
```ruby
|
949
|
+
class CrewCommunicationHub
|
950
|
+
def initialize
|
951
|
+
@message_queues = {}
|
952
|
+
@subscriptions = {}
|
953
|
+
@message_history = []
|
954
|
+
end
|
955
|
+
|
956
|
+
def register_crew(crew_name)
|
957
|
+
@message_queues[crew_name] = []
|
958
|
+
@subscriptions[crew_name] = []
|
959
|
+
end
|
960
|
+
|
961
|
+
def subscribe(subscriber_crew, publisher_crew, message_types: :all)
|
962
|
+
@subscriptions[subscriber_crew] << {
|
963
|
+
publisher: publisher_crew,
|
964
|
+
message_types: message_types
|
965
|
+
}
|
966
|
+
|
967
|
+
puts "📞 #{subscriber_crew} subscribed to #{publisher_crew}"
|
968
|
+
end
|
969
|
+
|
970
|
+
def send_message(from_crew, to_crew, message_type, content)
|
971
|
+
message = {
|
972
|
+
from: from_crew,
|
973
|
+
to: to_crew,
|
974
|
+
type: message_type,
|
975
|
+
content: content,
|
976
|
+
timestamp: Time.now,
|
977
|
+
id: SecureRandom.uuid[0..8]
|
978
|
+
}
|
979
|
+
|
980
|
+
@message_queues[to_crew] << message
|
981
|
+
@message_history << message
|
982
|
+
|
983
|
+
puts "💌 Message sent from #{from_crew} to #{to_crew}: #{message_type}"
|
984
|
+
|
985
|
+
# Notify subscribers
|
986
|
+
notify_subscribers(message)
|
987
|
+
end
|
988
|
+
|
989
|
+
def broadcast_message(from_crew, message_type, content)
|
990
|
+
@message_queues.keys.each do |crew_name|
|
991
|
+
next if crew_name == from_crew
|
992
|
+
send_message(from_crew, crew_name, message_type, content)
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
def get_messages(crew_name)
|
997
|
+
messages = @message_queues[crew_name] || []
|
998
|
+
@message_queues[crew_name] = [] # Clear after reading
|
999
|
+
messages
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def get_messages_by_type(crew_name, message_type)
|
1003
|
+
get_messages(crew_name).select { |m| m[:type] == message_type }
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
private
|
1007
|
+
|
1008
|
+
def notify_subscribers(message)
|
1009
|
+
@subscriptions.each do |subscriber, subscriptions|
|
1010
|
+
subscriptions.each do |sub|
|
1011
|
+
if sub[:publisher] == message[:from]
|
1012
|
+
if sub[:message_types] == :all || sub[:message_types].include?(message[:type])
|
1013
|
+
@message_queues[subscriber] << message.merge(subscription: true)
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# Global communication hub
|
1022
|
+
$comm_hub = CrewCommunicationHub.new
|
1023
|
+
|
1024
|
+
class CommunicatingCrew < RCrewAI::Crew
|
1025
|
+
def initialize(name, **options)
|
1026
|
+
super
|
1027
|
+
@comm_hub = $comm_hub
|
1028
|
+
@comm_hub.register_crew(@name)
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def send_message(to_crew, message_type, content)
|
1032
|
+
@comm_hub.send_message(@name, to_crew, message_type, content)
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
def broadcast(message_type, content)
|
1036
|
+
@comm_hub.broadcast_message(@name, message_type, content)
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
def get_messages(type: nil)
|
1040
|
+
if type
|
1041
|
+
@comm_hub.get_messages_by_type(@name, type)
|
1042
|
+
else
|
1043
|
+
@comm_hub.get_messages(@name)
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def subscribe_to(other_crew, message_types: :all)
|
1048
|
+
@comm_hub.subscribe(@name, other_crew, message_types: message_types)
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
def execute_with_communication
|
1052
|
+
puts "🚀 #{@name} starting execution with communication enabled"
|
1053
|
+
|
1054
|
+
# Check for incoming messages before execution
|
1055
|
+
process_incoming_messages
|
1056
|
+
|
1057
|
+
# Notify others of start
|
1058
|
+
broadcast(:status, "Starting execution")
|
1059
|
+
|
1060
|
+
# Execute with periodic message checking
|
1061
|
+
results = execute_with_message_monitoring
|
1062
|
+
|
1063
|
+
# Notify completion
|
1064
|
+
broadcast(:completion, {
|
1065
|
+
success_rate: results[:success_rate],
|
1066
|
+
key_results: summarize_results(results)
|
1067
|
+
})
|
1068
|
+
|
1069
|
+
results
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
private
|
1073
|
+
|
1074
|
+
def process_incoming_messages
|
1075
|
+
messages = get_messages
|
1076
|
+
|
1077
|
+
messages.each do |message|
|
1078
|
+
puts "📨 #{@name} received #{message[:type]} from #{message[:from]}: #{message[:content].to_s[0..50]}..."
|
1079
|
+
|
1080
|
+
case message[:type]
|
1081
|
+
when :status
|
1082
|
+
handle_status_message(message)
|
1083
|
+
when :request_help
|
1084
|
+
handle_help_request(message)
|
1085
|
+
when :share_data
|
1086
|
+
handle_data_sharing(message)
|
1087
|
+
when :coordination
|
1088
|
+
handle_coordination(message)
|
1089
|
+
end
|
1090
|
+
end
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
def execute_with_message_monitoring
|
1094
|
+
# Custom execution with message checking
|
1095
|
+
total_tasks = @tasks.length
|
1096
|
+
completed_tasks = 0
|
1097
|
+
results = []
|
1098
|
+
|
1099
|
+
@tasks.each_with_index do |task, index|
|
1100
|
+
# Check messages before each task
|
1101
|
+
process_incoming_messages
|
1102
|
+
|
1103
|
+
# Execute task
|
1104
|
+
begin
|
1105
|
+
result = task.execute
|
1106
|
+
completed_tasks += 1
|
1107
|
+
|
1108
|
+
results << {
|
1109
|
+
task: task,
|
1110
|
+
result: result,
|
1111
|
+
status: :completed
|
1112
|
+
}
|
1113
|
+
|
1114
|
+
# Notify progress
|
1115
|
+
progress = (completed_tasks.to_f / total_tasks * 100).round
|
1116
|
+
broadcast(:progress, { task: task.name, progress: progress })
|
1117
|
+
|
1118
|
+
rescue => e
|
1119
|
+
results << {
|
1120
|
+
task: task,
|
1121
|
+
error: e,
|
1122
|
+
status: :failed
|
1123
|
+
}
|
1124
|
+
|
1125
|
+
# Request help if task fails
|
1126
|
+
send_message("support_crew", :request_help, {
|
1127
|
+
failed_task: task.name,
|
1128
|
+
error: e.message
|
1129
|
+
})
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Calculate final results
|
1134
|
+
success_rate = (completed_tasks.to_f / total_tasks * 100).round
|
1135
|
+
|
1136
|
+
{
|
1137
|
+
success_rate: success_rate,
|
1138
|
+
total_tasks: total_tasks,
|
1139
|
+
completed_tasks: completed_tasks,
|
1140
|
+
results: results
|
1141
|
+
}
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
def handle_status_message(message)
|
1145
|
+
puts "📊 Status update from #{message[:from]}: #{message[:content]}"
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def handle_help_request(message)
|
1149
|
+
puts "🆘 Help request from #{message[:from]}: #{message[:content]}"
|
1150
|
+
|
1151
|
+
# Offer assistance if available
|
1152
|
+
send_message(message[:from], :offer_help, {
|
1153
|
+
from_crew: @name,
|
1154
|
+
available_agents: @agents.map(&:name),
|
1155
|
+
response_to: message[:id]
|
1156
|
+
})
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
def handle_data_sharing(message)
|
1160
|
+
puts "📊 Data shared from #{message[:from]}"
|
1161
|
+
add_shared_context(message[:content])
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
def handle_coordination(message)
|
1165
|
+
puts "🤝 Coordination message from #{message[:from]}"
|
1166
|
+
# Handle coordination logic
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
def summarize_results(results)
|
1170
|
+
# Create summary for other crews
|
1171
|
+
completed = results[:results].select { |r| r[:status] == :completed }
|
1172
|
+
completed.map { |r| r[:result][0..100] }.join(" | ")
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
# Example: Coordinated Product Development
|
1177
|
+
frontend_crew = CommunicatingCrew.new("frontend_team")
|
1178
|
+
backend_crew = CommunicatingCrew.new("backend_team")
|
1179
|
+
qa_crew = CommunicatingCrew.new("qa_team")
|
1180
|
+
|
1181
|
+
# Setup communication subscriptions
|
1182
|
+
frontend_crew.subscribe_to("backend_team", message_types: [:api_ready, :schema_update])
|
1183
|
+
qa_crew.subscribe_to("frontend_team", message_types: [:feature_complete])
|
1184
|
+
qa_crew.subscribe_to("backend_team", message_types: [:api_ready])
|
1185
|
+
|
1186
|
+
puts "🌐 Coordinated Product Development with Cross-Crew Communication"
|
1187
|
+
puts "="*70
|
1188
|
+
|
1189
|
+
# Execute crews with communication
|
1190
|
+
backend_results = backend_crew.execute_with_communication
|
1191
|
+
frontend_results = frontend_crew.execute_with_communication
|
1192
|
+
qa_results = qa_crew.execute_with_communication
|
1193
|
+
|
1194
|
+
puts "\n💬 Communication completed successfully!"
|
1195
|
+
```
|
1196
|
+
|
1197
|
+
## Orchestration Strategies
|
1198
|
+
|
1199
|
+
Advanced patterns for managing complex multi-crew operations:
|
1200
|
+
|
1201
|
+
```ruby
|
1202
|
+
class MultiCrewOrchestrator
|
1203
|
+
def initialize
|
1204
|
+
@crews = {}
|
1205
|
+
@execution_graph = {}
|
1206
|
+
@resource_manager = SharedResourceManager.new
|
1207
|
+
@communication_hub = CrewCommunicationHub.new
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
def add_crew(name, crew, dependencies: [], priority: :normal)
|
1211
|
+
@crews[name] = {
|
1212
|
+
crew: crew,
|
1213
|
+
dependencies: dependencies,
|
1214
|
+
priority: priority,
|
1215
|
+
status: :pending
|
1216
|
+
}
|
1217
|
+
|
1218
|
+
@execution_graph[name] = dependencies
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
def execute_orchestrated
|
1222
|
+
puts "🎼 Starting Multi-Crew Orchestration"
|
1223
|
+
puts "Crews: #{@crews.keys.join(', ')}"
|
1224
|
+
puts "="*60
|
1225
|
+
|
1226
|
+
# Calculate execution order based on dependencies
|
1227
|
+
execution_order = calculate_execution_order
|
1228
|
+
|
1229
|
+
puts "Execution Order: #{execution_order.join(' → ')}"
|
1230
|
+
|
1231
|
+
results = {}
|
1232
|
+
|
1233
|
+
execution_order.each do |crew_name|
|
1234
|
+
puts "\n🎯 Executing #{crew_name}"
|
1235
|
+
|
1236
|
+
crew_config = @crews[crew_name]
|
1237
|
+
crew = crew_config[:crew]
|
1238
|
+
|
1239
|
+
# Wait for dependencies
|
1240
|
+
wait_for_dependencies(crew_name)
|
1241
|
+
|
1242
|
+
# Mark as running
|
1243
|
+
crew_config[:status] = :running
|
1244
|
+
|
1245
|
+
begin
|
1246
|
+
# Execute crew
|
1247
|
+
result = crew.execute
|
1248
|
+
|
1249
|
+
# Mark as completed
|
1250
|
+
crew_config[:status] = :completed
|
1251
|
+
results[crew_name] = {
|
1252
|
+
success: true,
|
1253
|
+
result: result,
|
1254
|
+
crew_config: crew_config
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
puts "✅ #{crew_name} completed successfully"
|
1258
|
+
|
1259
|
+
# Share results with dependent crews
|
1260
|
+
share_results_with_dependents(crew_name, result)
|
1261
|
+
|
1262
|
+
rescue => e
|
1263
|
+
crew_config[:status] = :failed
|
1264
|
+
results[crew_name] = {
|
1265
|
+
success: false,
|
1266
|
+
error: e.message,
|
1267
|
+
crew_config: crew_config
|
1268
|
+
}
|
1269
|
+
|
1270
|
+
puts "❌ #{crew_name} failed: #{e.message}"
|
1271
|
+
|
1272
|
+
# Handle failure - may need to skip dependent crews
|
1273
|
+
handle_crew_failure(crew_name, e)
|
1274
|
+
end
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
generate_orchestration_report(results)
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
private
|
1281
|
+
|
1282
|
+
def calculate_execution_order
|
1283
|
+
# Topological sort for dependency resolution
|
1284
|
+
visited = Set.new
|
1285
|
+
temp_visited = Set.new
|
1286
|
+
result = []
|
1287
|
+
|
1288
|
+
def visit_crew(crew_name, visited, temp_visited, result)
|
1289
|
+
return if visited.include?(crew_name)
|
1290
|
+
|
1291
|
+
if temp_visited.include?(crew_name)
|
1292
|
+
raise "Circular dependency detected involving #{crew_name}"
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
temp_visited.add(crew_name)
|
1296
|
+
|
1297
|
+
@execution_graph[crew_name].each do |dependency|
|
1298
|
+
visit_crew(dependency, visited, temp_visited, result)
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
temp_visited.delete(crew_name)
|
1302
|
+
visited.add(crew_name)
|
1303
|
+
result.unshift(crew_name)
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
@crews.keys.each do |crew_name|
|
1307
|
+
visit_crew(crew_name, visited, temp_visited, result) unless visited.include?(crew_name)
|
1308
|
+
end
|
1309
|
+
|
1310
|
+
result
|
1311
|
+
end
|
1312
|
+
|
1313
|
+
def wait_for_dependencies(crew_name)
|
1314
|
+
dependencies = @execution_graph[crew_name]
|
1315
|
+
return if dependencies.empty?
|
1316
|
+
|
1317
|
+
puts "⏳ Waiting for dependencies: #{dependencies.join(', ')}"
|
1318
|
+
|
1319
|
+
dependencies.each do |dep_name|
|
1320
|
+
while @crews[dep_name][:status] != :completed
|
1321
|
+
if @crews[dep_name][:status] == :failed
|
1322
|
+
raise "Dependency #{dep_name} failed, cannot execute #{crew_name}"
|
1323
|
+
end
|
1324
|
+
|
1325
|
+
sleep(1)
|
1326
|
+
end
|
1327
|
+
end
|
1328
|
+
|
1329
|
+
puts "✅ All dependencies ready for #{crew_name}"
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
def share_results_with_dependents(crew_name, results)
|
1333
|
+
# Find crews that depend on this one
|
1334
|
+
dependents = @execution_graph.select { |name, deps| deps.include?(crew_name) }.keys
|
1335
|
+
|
1336
|
+
dependents.each do |dependent_name|
|
1337
|
+
dependent_crew = @crews[dependent_name][:crew]
|
1338
|
+
|
1339
|
+
# Share results as context
|
1340
|
+
if dependent_crew.respond_to?(:add_shared_context)
|
1341
|
+
dependent_crew.add_shared_context({
|
1342
|
+
"#{crew_name}_results" => results
|
1343
|
+
})
|
1344
|
+
end
|
1345
|
+
end
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
def handle_crew_failure(failed_crew, error)
|
1349
|
+
# Find all crews that depend on the failed crew
|
1350
|
+
affected_crews = find_affected_crews(failed_crew)
|
1351
|
+
|
1352
|
+
puts "⚠️ Failure in #{failed_crew} affects: #{affected_crews.join(', ')}"
|
1353
|
+
|
1354
|
+
# Mark affected crews as blocked
|
1355
|
+
affected_crews.each do |crew_name|
|
1356
|
+
@crews[crew_name][:status] = :blocked
|
1357
|
+
end
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
def find_affected_crews(failed_crew)
|
1361
|
+
affected = []
|
1362
|
+
|
1363
|
+
@execution_graph.each do |crew_name, dependencies|
|
1364
|
+
if dependencies.include?(failed_crew) ||
|
1365
|
+
dependencies.any? { |dep| find_affected_crews(dep).include?(failed_crew) }
|
1366
|
+
affected << crew_name
|
1367
|
+
end
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
affected
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
def generate_orchestration_report(results)
|
1374
|
+
puts "\n📊 ORCHESTRATION RESULTS"
|
1375
|
+
puts "="*50
|
1376
|
+
|
1377
|
+
successful = results.values.count { |r| r[:success] }
|
1378
|
+
total = results.length
|
1379
|
+
|
1380
|
+
puts "Overall Success Rate: #{successful}/#{total} (#{(successful.to_f/total*100).round(1)}%)"
|
1381
|
+
puts
|
1382
|
+
|
1383
|
+
results.each do |crew_name, result|
|
1384
|
+
status = result[:success] ? "✅" : "❌"
|
1385
|
+
priority = result[:crew_config][:priority]
|
1386
|
+
|
1387
|
+
puts "#{status} #{crew_name} (#{priority})"
|
1388
|
+
|
1389
|
+
if result[:success]
|
1390
|
+
success_rate = result[:result][:success_rate] || 0
|
1391
|
+
puts " Success Rate: #{success_rate}%"
|
1392
|
+
puts " Tasks: #{result[:result][:completed_tasks]}/#{result[:result][:total_tasks]}"
|
1393
|
+
else
|
1394
|
+
puts " Error: #{result[:error]}"
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
dependencies = result[:crew_config][:dependencies]
|
1398
|
+
puts " Dependencies: #{dependencies.empty? ? 'None' : dependencies.join(', ')}"
|
1399
|
+
puts
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
# Save orchestration report
|
1403
|
+
File.write("orchestration_report.json", JSON.pretty_generate({
|
1404
|
+
orchestration: "Multi-Crew Execution",
|
1405
|
+
results: results,
|
1406
|
+
execution_graph: @execution_graph,
|
1407
|
+
summary: {
|
1408
|
+
success_rate: (successful.to_f/total*100).round(1),
|
1409
|
+
total_crews: total,
|
1410
|
+
successful_crews: successful
|
1411
|
+
}
|
1412
|
+
}))
|
1413
|
+
|
1414
|
+
puts "💾 Orchestration report saved: orchestration_report.json"
|
1415
|
+
end
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
# Example: Complex Software Release Pipeline
|
1419
|
+
orchestrator = MultiCrewOrchestrator.new
|
1420
|
+
|
1421
|
+
# Add crews with dependencies
|
1422
|
+
orchestrator.add_crew("planning", planning_crew, dependencies: [])
|
1423
|
+
orchestrator.add_crew("development", dev_crew, dependencies: ["planning"])
|
1424
|
+
orchestrator.add_crew("testing", qa_crew, dependencies: ["development"])
|
1425
|
+
orchestrator.add_crew("security", security_crew, dependencies: ["development"])
|
1426
|
+
orchestrator.add_crew("deployment", deploy_crew, dependencies: ["testing", "security"])
|
1427
|
+
orchestrator.add_crew("monitoring", monitoring_crew, dependencies: ["deployment"])
|
1428
|
+
|
1429
|
+
# Execute orchestrated pipeline
|
1430
|
+
orchestrator.execute_orchestrated
|
1431
|
+
```
|
1432
|
+
|
1433
|
+
## Production Multi-Crew Systems
|
1434
|
+
|
1435
|
+
Production-ready patterns with monitoring, error handling, and scalability:
|
1436
|
+
|
1437
|
+
```ruby
|
1438
|
+
class ProductionMultiCrewSystem
|
1439
|
+
def initialize
|
1440
|
+
@logger = Logger.new($stdout)
|
1441
|
+
@metrics = MetricsCollector.new
|
1442
|
+
@health_monitor = HealthMonitor.new
|
1443
|
+
@crew_registry = {}
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
def register_crew(name, crew, config = {})
|
1447
|
+
@crew_registry[name] = {
|
1448
|
+
crew: crew,
|
1449
|
+
config: config.merge(
|
1450
|
+
max_retries: config[:max_retries] || 3,
|
1451
|
+
timeout: config[:timeout] || 300,
|
1452
|
+
health_check_interval: config[:health_check_interval] || 60
|
1453
|
+
),
|
1454
|
+
status: :registered,
|
1455
|
+
health: :unknown,
|
1456
|
+
last_execution: nil
|
1457
|
+
}
|
1458
|
+
|
1459
|
+
@logger.info("Crew registered: #{name}")
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
def execute_system(execution_plan)
|
1463
|
+
@logger.info("Starting production multi-crew system execution")
|
1464
|
+
@metrics.start_execution
|
1465
|
+
|
1466
|
+
begin
|
1467
|
+
results = execute_with_monitoring(execution_plan)
|
1468
|
+
@metrics.record_success
|
1469
|
+
results
|
1470
|
+
rescue => e
|
1471
|
+
@metrics.record_failure(e)
|
1472
|
+
@logger.error("System execution failed: #{e.message}")
|
1473
|
+
raise
|
1474
|
+
ensure
|
1475
|
+
@metrics.finish_execution
|
1476
|
+
end
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
private
|
1480
|
+
|
1481
|
+
def execute_with_monitoring(execution_plan)
|
1482
|
+
# Start health monitoring
|
1483
|
+
monitoring_thread = start_health_monitoring
|
1484
|
+
|
1485
|
+
# Execute according to plan
|
1486
|
+
results = case execution_plan[:type]
|
1487
|
+
when :sequential
|
1488
|
+
execute_sequential(execution_plan[:crews])
|
1489
|
+
when :parallel
|
1490
|
+
execute_parallel(execution_plan[:crews])
|
1491
|
+
when :orchestrated
|
1492
|
+
execute_orchestrated(execution_plan)
|
1493
|
+
else
|
1494
|
+
raise "Unknown execution plan type: #{execution_plan[:type]}"
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
# Stop monitoring
|
1498
|
+
monitoring_thread.kill if monitoring_thread
|
1499
|
+
|
1500
|
+
results
|
1501
|
+
end
|
1502
|
+
|
1503
|
+
def start_health_monitoring
|
1504
|
+
Thread.new do
|
1505
|
+
loop do
|
1506
|
+
@crew_registry.each do |name, crew_info|
|
1507
|
+
check_crew_health(name, crew_info)
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
sleep(30) # Check every 30 seconds
|
1511
|
+
end
|
1512
|
+
end
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
def check_crew_health(name, crew_info)
|
1516
|
+
begin
|
1517
|
+
# Basic health check
|
1518
|
+
crew = crew_info[:crew]
|
1519
|
+
|
1520
|
+
health_status = if crew.respond_to?(:health_check)
|
1521
|
+
crew.health_check
|
1522
|
+
else
|
1523
|
+
# Default health check
|
1524
|
+
{ status: :healthy, agents: crew.agents.length }
|
1525
|
+
end
|
1526
|
+
|
1527
|
+
crew_info[:health] = health_status[:status]
|
1528
|
+
crew_info[:last_health_check] = Time.now
|
1529
|
+
|
1530
|
+
@health_monitor.record_health_check(name, health_status)
|
1531
|
+
|
1532
|
+
rescue => e
|
1533
|
+
crew_info[:health] = :unhealthy
|
1534
|
+
@logger.error("Health check failed for #{name}: #{e.message}")
|
1535
|
+
end
|
1536
|
+
end
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
class MetricsCollector
|
1540
|
+
def initialize
|
1541
|
+
@metrics = {
|
1542
|
+
executions: 0,
|
1543
|
+
successes: 0,
|
1544
|
+
failures: 0,
|
1545
|
+
total_duration: 0,
|
1546
|
+
crew_performance: {}
|
1547
|
+
}
|
1548
|
+
@current_execution = nil
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
def start_execution
|
1552
|
+
@current_execution = {
|
1553
|
+
start_time: Time.now,
|
1554
|
+
crew_metrics: {}
|
1555
|
+
}
|
1556
|
+
end
|
1557
|
+
|
1558
|
+
def record_crew_start(crew_name)
|
1559
|
+
@current_execution[:crew_metrics][crew_name] = {
|
1560
|
+
start_time: Time.now
|
1561
|
+
}
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
def record_crew_completion(crew_name, success, result = nil)
|
1565
|
+
crew_metrics = @current_execution[:crew_metrics][crew_name]
|
1566
|
+
crew_metrics[:end_time] = Time.now
|
1567
|
+
crew_metrics[:duration] = crew_metrics[:end_time] - crew_metrics[:start_time]
|
1568
|
+
crew_metrics[:success] = success
|
1569
|
+
crew_metrics[:result_size] = result&.to_s&.length || 0
|
1570
|
+
|
1571
|
+
# Update overall metrics
|
1572
|
+
@metrics[:crew_performance][crew_name] ||= {
|
1573
|
+
executions: 0,
|
1574
|
+
successes: 0,
|
1575
|
+
avg_duration: 0
|
1576
|
+
}
|
1577
|
+
|
1578
|
+
crew_perf = @metrics[:crew_performance][crew_name]
|
1579
|
+
crew_perf[:executions] += 1
|
1580
|
+
crew_perf[:successes] += 1 if success
|
1581
|
+
crew_perf[:avg_duration] = (
|
1582
|
+
crew_perf[:avg_duration] * (crew_perf[:executions] - 1) + crew_metrics[:duration]
|
1583
|
+
) / crew_perf[:executions]
|
1584
|
+
end
|
1585
|
+
|
1586
|
+
def record_success
|
1587
|
+
@metrics[:executions] += 1
|
1588
|
+
@metrics[:successes] += 1
|
1589
|
+
end
|
1590
|
+
|
1591
|
+
def record_failure(error)
|
1592
|
+
@metrics[:executions] += 1
|
1593
|
+
@metrics[:failures] += 1
|
1594
|
+
@metrics[:last_error] = error.message
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
def finish_execution
|
1598
|
+
return unless @current_execution
|
1599
|
+
|
1600
|
+
duration = Time.now - @current_execution[:start_time]
|
1601
|
+
@metrics[:total_duration] += duration
|
1602
|
+
@metrics[:avg_duration] = @metrics[:total_duration] / @metrics[:executions]
|
1603
|
+
|
1604
|
+
@current_execution = nil
|
1605
|
+
end
|
1606
|
+
|
1607
|
+
def get_metrics
|
1608
|
+
@metrics.merge(
|
1609
|
+
success_rate: @metrics[:executions] > 0 ?
|
1610
|
+
(@metrics[:successes].to_f / @metrics[:executions] * 100).round(1) : 0
|
1611
|
+
)
|
1612
|
+
end
|
1613
|
+
end
|
1614
|
+
|
1615
|
+
class HealthMonitor
|
1616
|
+
def initialize
|
1617
|
+
@health_history = {}
|
1618
|
+
end
|
1619
|
+
|
1620
|
+
def record_health_check(crew_name, health_status)
|
1621
|
+
@health_history[crew_name] ||= []
|
1622
|
+
@health_history[crew_name] << {
|
1623
|
+
timestamp: Time.now,
|
1624
|
+
status: health_status[:status],
|
1625
|
+
details: health_status
|
1626
|
+
}
|
1627
|
+
|
1628
|
+
# Keep only last 100 health checks
|
1629
|
+
@health_history[crew_name] = @health_history[crew_name].last(100)
|
1630
|
+
end
|
1631
|
+
|
1632
|
+
def get_health_summary
|
1633
|
+
summary = {}
|
1634
|
+
|
1635
|
+
@health_history.each do |crew_name, checks|
|
1636
|
+
recent_checks = checks.last(10)
|
1637
|
+
healthy_count = recent_checks.count { |c| c[:status] == :healthy }
|
1638
|
+
|
1639
|
+
summary[crew_name] = {
|
1640
|
+
current_status: recent_checks.last&.[](:status) || :unknown,
|
1641
|
+
health_percentage: (healthy_count.to_f / recent_checks.length * 100).round(1),
|
1642
|
+
last_check: recent_checks.last&.[](:timestamp)
|
1643
|
+
}
|
1644
|
+
end
|
1645
|
+
|
1646
|
+
summary
|
1647
|
+
end
|
1648
|
+
end
|
1649
|
+
```
|
1650
|
+
|
1651
|
+
## Best Practices
|
1652
|
+
|
1653
|
+
### 1. **Crew Design Principles**
|
1654
|
+
- **Clear Boundaries**: Each crew should have distinct responsibilities
|
1655
|
+
- **Minimal Coupling**: Reduce dependencies between crews
|
1656
|
+
- **Resource Isolation**: Crews should manage their own resources
|
1657
|
+
- **Communication Protocols**: Establish clear communication patterns
|
1658
|
+
|
1659
|
+
### 2. **Execution Patterns**
|
1660
|
+
- **Sequential**: Use for workflows with strict dependencies
|
1661
|
+
- **Parallel**: Use when crews can work independently
|
1662
|
+
- **Orchestrated**: Use for complex coordination requirements
|
1663
|
+
- **Hybrid**: Combine patterns for optimal performance
|
1664
|
+
|
1665
|
+
### 3. **Resource Management**
|
1666
|
+
- **Shared Resources**: Use resource managers for shared data
|
1667
|
+
- **Resource Locking**: Prevent conflicts with locking mechanisms
|
1668
|
+
- **Resource Cleanup**: Ensure resources are released properly
|
1669
|
+
- **Resource Monitoring**: Track resource usage and performance
|
1670
|
+
|
1671
|
+
### 4. **Error Handling**
|
1672
|
+
- **Graceful Degradation**: Handle crew failures without system collapse
|
1673
|
+
- **Retry Logic**: Implement intelligent retry strategies
|
1674
|
+
- **Fallback Options**: Provide alternative execution paths
|
1675
|
+
- **Error Propagation**: Manage error propagation between crews
|
1676
|
+
|
1677
|
+
### 5. **Monitoring and Observability**
|
1678
|
+
- **Health Checks**: Monitor crew health continuously
|
1679
|
+
- **Performance Metrics**: Track execution times and success rates
|
1680
|
+
- **Resource Usage**: Monitor memory, CPU, and other resources
|
1681
|
+
- **Alerting**: Set up alerts for critical failures
|
1682
|
+
|
1683
|
+
## Next Steps
|
1684
|
+
|
1685
|
+
Now that you understand multi-crew operations:
|
1686
|
+
|
1687
|
+
1. Try the [Production Deployment]({{ site.baseurl }}/tutorials/deployment) tutorial
|
1688
|
+
2. Review the [API Documentation]({{ site.baseurl }}/api/) for detailed reference
|
1689
|
+
3. Check out [Advanced Examples]({{ site.baseurl }}/examples/) for complex scenarios
|
1690
|
+
4. Explore [Integration Patterns]({{ site.baseurl }}/guides/) for enterprise use
|
1691
|
+
|
1692
|
+
Multi-crew systems are essential for building scalable AI operations that can handle complex, enterprise-level workflows with reliability and efficiency.
|