Skip to main content

swarm_engine_eval/scenario/
builtin.rs

1//! 組み込みシナリオ定義
2//!
3//! 評価フレームワークに組み込みで提供されるシナリオ。
4
5use super::conditions::{CompareOp, Condition, EvalConditions, TimeoutBehavior};
6use super::milestone::{Milestone, PartialConfig};
7use super::types::*;
8
9/// 全ての builtin シナリオを取得
10pub fn builtin_scenarios() -> Vec<EvalScenario> {
11    vec![
12        resource_gathering(),
13        task_queue_processing(),
14        coordination_stress(),
15    ]
16}
17
18/// リソース収集シナリオ
19///
20/// 複数のエージェントが協調してリソースを収集するタスク。
21/// 基本的な協調能力の評価に使用。
22pub fn resource_gathering() -> EvalScenario {
23    EvalScenario {
24        meta: ScenarioMeta {
25            name: "Resource Gathering".to_string(),
26            version: "1.0.0".to_string(),
27            id: ScenarioId::new("builtin:resource_gathering:v1"),
28            description: "Multiple agents coordinate to collect resources in a grid world"
29                .to_string(),
30            tags: vec![
31                "coordination".to_string(),
32                "basic".to_string(),
33                "resource".to_string(),
34            ],
35        },
36        task: TaskConfig::default(),
37        llm: LlmConfig::default(),
38        manager: ManagerConfig::default(),
39        batch_processor: BatchProcessorConfig::default(),
40        dependency_graph: None,
41        actions: ScenarioActions::default(),
42        app_config: AppConfigTemplate {
43            tick_duration_ms: 10,
44            max_ticks: 500,
45            management_strategy: ManagementStrategyConfig::IntervalBased { max_interval: 20 },
46            enable_exploration: false,
47        },
48        environment: EnvironmentConfig {
49            env_type: "grid_world".to_string(),
50            params: serde_json::json!({
51                "width": 10,
52                "height": 10,
53                "resource_count": 5,
54                "obstacle_density": 0.1
55            }),
56            initial_state: Some(InitialStateConfig::SeededRandom {}),
57        },
58        agents: AgentsConfig {
59            workers: vec![WorkerTemplate {
60                id_pattern: "gatherer_{i}".to_string(),
61                count: 4,
62                role: "gatherer".to_string(),
63                config: serde_json::json!({
64                    "speed": 1.0,
65                    "capacity": 2
66                }),
67            }],
68            managers: vec![ManagerTemplate {
69                id: Some("coordinator".to_string()),
70                id_pattern: None,
71                count: 1,
72                role: "coordinator".to_string(),
73                activation: ManagerActivationConfig::Hybrid {
74                    interval: 10,
75                    triggers: vec!["resource_found".to_string(), "collision".to_string()],
76                },
77                config: serde_json::Value::Null,
78            }],
79        },
80        conditions: EvalConditions {
81            success: vec![
82                Condition::new(
83                    "all_resources_collected",
84                    "environment.resources_collected",
85                    CompareOp::Gte,
86                    5,
87                ),
88                Condition::new("within_time_limit", "tick", CompareOp::Lte, 400),
89            ],
90            failure: vec![Condition::new(
91                "deadlock_detected",
92                "coordination.deadlock_count",
93                CompareOp::Gte,
94                3,
95            )],
96            on_timeout: TimeoutBehavior::MilestoneScore,
97        },
98        milestones: vec![
99            Milestone::new(
100                "first_collection",
101                Condition::new(
102                    "first",
103                    "environment.resources_collected",
104                    CompareOp::Gte,
105                    1,
106                ),
107                0.15,
108            )
109            .with_description("Collect the first resource"),
110            Milestone::new(
111                "half_collected",
112                Condition::new("half", "environment.resources_collected", CompareOp::Gte, 3),
113                0.25,
114            )
115            .with_description("Collect half of the resources"),
116            Milestone::new(
117                "efficiency_bonus",
118                Condition::new("efficiency", "tick", CompareOp::Lte, 300),
119                0.20,
120            )
121            .with_description("Complete within 300 ticks")
122            .with_partial(PartialConfig::Linear {
123                min: Some(300.0),
124                max: Some(400.0),
125                descending: true, // tick が小さいほどスコアが高い
126            }),
127            Milestone::new(
128                "all_collected",
129                Condition::new("all", "environment.resources_collected", CompareOp::Gte, 5),
130                0.40,
131            )
132            .with_description("Collect all resources"),
133        ],
134        variants: Vec::new(),
135    }
136}
137
138/// タスクキュー処理シナリオ
139///
140/// タスクキューから順次タスクを取り出して処理する。
141/// スループットとレイテンシの評価に使用。
142pub fn task_queue_processing() -> EvalScenario {
143    EvalScenario {
144        meta: ScenarioMeta {
145            name: "Task Queue Processing".to_string(),
146            version: "1.0.0".to_string(),
147            id: ScenarioId::new("builtin:task_queue:v1"),
148            description: "Process tasks from a queue with varying complexity".to_string(),
149            tags: vec![
150                "throughput".to_string(),
151                "latency".to_string(),
152                "basic".to_string(),
153            ],
154        },
155        task: TaskConfig::default(),
156        llm: LlmConfig::default(),
157        manager: ManagerConfig::default(),
158        batch_processor: BatchProcessorConfig::default(),
159        dependency_graph: None,
160        actions: ScenarioActions::default(),
161        app_config: AppConfigTemplate {
162            tick_duration_ms: 10,
163            max_ticks: 1000,
164            management_strategy: ManagementStrategyConfig::EventDriven {
165                triggers: vec!["task_completed".to_string(), "queue_empty".to_string()],
166            },
167            enable_exploration: false,
168        },
169        environment: EnvironmentConfig {
170            env_type: "task_queue".to_string(),
171            params: serde_json::json!({
172                "initial_tasks": 100,
173                "task_complexity": {
174                    "min": 1,
175                    "max": 10
176                },
177                "arrival_rate": 0.0  // バッチ処理
178            }),
179            initial_state: Some(InitialStateConfig::SeededRandom {}),
180        },
181        agents: AgentsConfig {
182            workers: vec![WorkerTemplate {
183                id_pattern: "processor_{i}".to_string(),
184                count: 4,
185                role: "processor".to_string(),
186                config: serde_json::json!({
187                    "processing_speed": 1.0
188                }),
189            }],
190            managers: vec![ManagerTemplate {
191                id: Some("scheduler".to_string()),
192                id_pattern: None,
193                count: 1,
194                role: "scheduler".to_string(),
195                activation: ManagerActivationConfig::Event {
196                    triggers: vec!["task_completed".to_string()],
197                },
198                config: serde_json::Value::Null,
199            }],
200        },
201        conditions: EvalConditions {
202            success: vec![Condition::new(
203                "all_tasks_processed",
204                "task.completed_count",
205                CompareOp::Gte,
206                100,
207            )],
208            failure: vec![Condition::new(
209                "task_timeout",
210                "task.timeout_count",
211                CompareOp::Gte,
212                10,
213            )],
214            on_timeout: TimeoutBehavior::PartialSuccess,
215        },
216        milestones: vec![
217            Milestone::new(
218                "quarter_processed",
219                Condition::new("quarter", "task.completed_count", CompareOp::Gte, 25),
220                0.10,
221            ),
222            Milestone::new(
223                "half_processed",
224                Condition::new("half", "task.completed_count", CompareOp::Gte, 50),
225                0.20,
226            ),
227            Milestone::new(
228                "three_quarter_processed",
229                Condition::new("three_quarter", "task.completed_count", CompareOp::Gte, 75),
230                0.30,
231            ),
232            Milestone::new(
233                "all_processed",
234                Condition::new("all", "task.completed_count", CompareOp::Gte, 100),
235                0.40,
236            ),
237        ],
238        variants: Vec::new(),
239    }
240}
241
242/// 協調ストレステストシナリオ
243///
244/// 多数のエージェントと頻繁なイベントによる高負荷シナリオ。
245/// スケーラビリティとロバスト性の評価に使用。
246pub fn coordination_stress() -> EvalScenario {
247    EvalScenario {
248        meta: ScenarioMeta {
249            name: "Coordination Stress Test".to_string(),
250            version: "1.0.0".to_string(),
251            id: ScenarioId::new("builtin:coordination_stress:v1"),
252            description: "High-load scenario with many agents and frequent events".to_string(),
253            tags: vec![
254                "stress".to_string(),
255                "scalability".to_string(),
256                "advanced".to_string(),
257            ],
258        },
259        task: TaskConfig::default(),
260        llm: LlmConfig::default(),
261        manager: ManagerConfig::default(),
262        batch_processor: BatchProcessorConfig::default(),
263        dependency_graph: None,
264        actions: ScenarioActions::default(),
265        app_config: AppConfigTemplate {
266            tick_duration_ms: 5, // より短いインターバル
267            max_ticks: 2000,
268            management_strategy: ManagementStrategyConfig::Hybrid {
269                max_interval: 10,
270                triggers: vec!["conflict".to_string(), "resource_contention".to_string()],
271            },
272            enable_exploration: false,
273        },
274        environment: EnvironmentConfig {
275            env_type: "multi_resource".to_string(),
276            params: serde_json::json!({
277                "width": 20,
278                "height": 20,
279                "resource_types": 3,
280                "resources_per_type": 10,
281                "dynamic_spawning": true,
282                "spawn_interval": 50
283            }),
284            initial_state: Some(InitialStateConfig::SeededRandom {}),
285        },
286        agents: AgentsConfig {
287            workers: vec![
288                WorkerTemplate {
289                    id_pattern: "collector_a_{i}".to_string(),
290                    count: 4,
291                    role: "collector_type_a".to_string(),
292                    config: serde_json::json!({
293                        "target_resource": "type_a",
294                        "speed": 1.2
295                    }),
296                },
297                WorkerTemplate {
298                    id_pattern: "collector_b_{i}".to_string(),
299                    count: 4,
300                    role: "collector_type_b".to_string(),
301                    config: serde_json::json!({
302                        "target_resource": "type_b",
303                        "speed": 1.0
304                    }),
305                },
306                WorkerTemplate {
307                    id_pattern: "collector_c_{i}".to_string(),
308                    count: 4,
309                    role: "collector_type_c".to_string(),
310                    config: serde_json::json!({
311                        "target_resource": "type_c",
312                        "speed": 0.8
313                    }),
314                },
315            ],
316            managers: vec![
317                ManagerTemplate {
318                    id: Some("global_coordinator".to_string()),
319                    id_pattern: None,
320                    count: 1,
321                    role: "coordinator".to_string(),
322                    activation: ManagerActivationConfig::Interval { interval: 20 },
323                    config: serde_json::json!({
324                        "strategy": "load_balancing"
325                    }),
326                },
327                ManagerTemplate {
328                    id: Some("conflict_resolver".to_string()),
329                    id_pattern: None,
330                    count: 1,
331                    role: "resolver".to_string(),
332                    activation: ManagerActivationConfig::Event {
333                        triggers: vec!["conflict".to_string()],
334                    },
335                    config: serde_json::json!({
336                        "strategy": "priority_based"
337                    }),
338                },
339            ],
340        },
341        conditions: EvalConditions {
342            success: vec![
343                Condition::new(
344                    "total_collected",
345                    "environment.total_collected",
346                    CompareOp::Gte,
347                    50,
348                ),
349                Condition::new(
350                    "low_conflict_rate",
351                    "coordination.conflict_rate",
352                    CompareOp::Lte,
353                    0.1,
354                ),
355            ],
356            failure: vec![
357                Condition::new(
358                    "system_overload",
359                    "performance.tick_miss_rate",
360                    CompareOp::Gte,
361                    0.2,
362                ),
363                Condition::new(
364                    "excessive_conflicts",
365                    "coordination.conflict_count",
366                    CompareOp::Gte,
367                    100,
368                ),
369            ],
370            on_timeout: TimeoutBehavior::MilestoneScore,
371        },
372        milestones: vec![
373            Milestone::new(
374                "system_stable",
375                Condition::new("stable", "performance.tick_miss_rate", CompareOp::Lte, 0.05),
376                0.20,
377            )
378            .with_description("System remains stable under load"),
379            Milestone::new(
380                "efficient_coordination",
381                Condition::new(
382                    "efficient",
383                    "coordination.conflict_rate",
384                    CompareOp::Lte,
385                    0.05,
386                ),
387                0.25,
388            )
389            .with_description("Low conflict rate maintained"),
390            Milestone::new(
391                "good_throughput",
392                Condition::new(
393                    "throughput",
394                    "environment.total_collected",
395                    CompareOp::Gte,
396                    30,
397                ),
398                0.25,
399            )
400            .with_description("Achieve good throughput"),
401            Milestone::new(
402                "excellent_throughput",
403                Condition::new(
404                    "excellent",
405                    "environment.total_collected",
406                    CompareOp::Gte,
407                    50,
408                ),
409                0.30,
410            )
411            .with_description("Achieve excellent throughput"),
412        ],
413        variants: Vec::new(),
414    }
415}
416
417#[cfg(test)]
418mod tests {
419    use super::*;
420
421    #[test]
422    fn test_builtin_scenarios_exist() {
423        let scenarios = builtin_scenarios();
424        assert_eq!(scenarios.len(), 3);
425    }
426
427    #[test]
428    fn test_resource_gathering_scenario() {
429        let scenario = resource_gathering();
430        assert_eq!(scenario.meta.name, "Resource Gathering");
431        assert_eq!(scenario.agents.workers.len(), 1);
432        assert_eq!(scenario.agents.workers[0].count, 4);
433        assert_eq!(scenario.milestones.len(), 4);
434    }
435
436    #[test]
437    fn test_scenario_serialization_json() {
438        // JSON でのラウンドトリップをテスト
439        let scenario = resource_gathering();
440        let json_str = serde_json::to_string_pretty(&scenario).unwrap();
441
442        // 再度デシリアライズできることを確認
443        let deserialized: EvalScenario = serde_json::from_str(&json_str).unwrap();
444        assert_eq!(deserialized.meta.name, scenario.meta.name);
445        assert_eq!(deserialized.milestones.len(), scenario.milestones.len());
446    }
447
448    #[test]
449    fn test_scenario_ids_unique() {
450        let scenarios = builtin_scenarios();
451        let ids: std::collections::HashSet<_> = scenarios.iter().map(|s| &s.meta.id).collect();
452        assert_eq!(ids.len(), scenarios.len(), "Scenario IDs must be unique");
453    }
454}