Skip to main content

swarm_engine_core/agent/
manager.rs

1//! ManagerAgent - 観察・判断・指示を行う上位Agent
2//!
3//! # 型の正規定義
4//!
5//! 以下の型はこのモジュールで正規定義されています:
6//! - `ManagerId`
7//! - `BatchDecisionRequest`
8//! - `WorkerDecisionRequest`
9//! - `ManagementDecision`
10//! - `ManagementStrategy`
11//!
12//! これらは `batch.rs` からも re-export されます。
13//!
14//! # 設計変更(v2)
15//!
16//! ManagerAgent は TaskContext を受け取り、Request を生成する。
17//! SwarmState からの分析は Analyzer が担当。
18//!
19//! ```text
20//! SwarmState → [Analyzer] → TaskContext → [Manager] → BatchDecisionRequest
21//!                                              ↓
22//!                                     [BatchInvoker] → Response
23//!                                              ↓
24//!                                   [Manager.finalize] → ManagementDecision
25//! ```
26
27use std::collections::HashMap;
28
29use crate::types::{LoraConfig, WorkerId};
30
31use super::batch::DecisionResponse;
32use super::worker::Guidance;
33use crate::context::TaskContext;
34
35// ============================================================================
36// Management Strategy
37// ============================================================================
38
39/// ManagerAgent の起動タイミング戦略
40#[derive(Debug, Clone)]
41pub enum ManagementStrategy {
42    /// 毎 Tick 起動(LLM 不要なフロー向け)
43    ///
44    /// V2 ExplorationSpace など、LLM 呼び出しなしで Guidance を生成できる場合に使用。
45    /// Worker がアクションを完了したら即座に次のノードを割り当てられる。
46    EveryTick,
47
48    /// N Tick ごとに ManagerAgent を起動
49    FixedInterval { interval: u64 },
50
51    /// 全 WorkerAgent の完了を待って起動
52    CompletionBased {
53        /// 最大待機Tick(タイムアウト)
54        max_wait_ticks: u64,
55    },
56
57    /// ハイブリッド: 完了待ちだが、最大N Tickで強制起動
58    Hybrid {
59        preferred_interval: u64,
60        force_after_ticks: u64,
61    },
62
63    /// Escalation ベース: 定期 + Escalation 発生時に即時起動
64    EscalationBased {
65        /// 定期起動間隔
66        interval: u64,
67        /// Escalation 発生時に即時起動するか
68        immediate_on_escalation: bool,
69    },
70}
71
72impl Default for ManagementStrategy {
73    fn default() -> Self {
74        ManagementStrategy::FixedInterval { interval: 10 }
75    }
76}
77
78// ============================================================================
79// Manager ID
80// ============================================================================
81
82/// Manager ID
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub struct ManagerId(pub usize);
85
86// ============================================================================
87// Batch Request
88// ============================================================================
89
90/// Batch リクエスト(1 Manager が 1 Tick で送る全リクエスト)
91///
92/// Manager が各 Worker に対して判断を求めるリクエストをまとめたもの。
93/// ContextStore → ContextResolver → ResolvedContext の流れで生成される。
94#[derive(Debug, Clone)]
95pub struct BatchDecisionRequest {
96    /// 発行元 Manager ID
97    pub manager_id: ManagerId,
98    /// 各 Worker への判断リクエスト
99    pub requests: Vec<WorkerDecisionRequest>,
100}
101
102use crate::context::ResolvedContext;
103
104/// 個別 Worker への判断リクエスト
105///
106/// Worker が判断に必要な情報を含む。
107/// `context` は構造化データとして保持し、文字列化は LLM 層で行う。
108#[derive(Debug, Clone)]
109pub struct WorkerDecisionRequest {
110    /// 対象 Worker ID
111    pub worker_id: WorkerId,
112    /// タスク目標(query)
113    pub query: String,
114    /// Worker 固有のコンテキスト(構造化データ)
115    pub context: ResolvedContext,
116    /// LoRA アダプター設定(オプション)
117    ///
118    /// 指定すると、このリクエストに対して特定の LoRA アダプターを適用する。
119    /// llama.cpp の per-request LoRA 機能を使用。
120    pub lora: Option<LoraConfig>,
121}
122
123// ============================================================================
124// Management Decision
125// ============================================================================
126
127/// 非同期タスクリクエスト
128#[derive(Debug, Clone, Default)]
129pub struct AsyncTaskRequest {
130    pub task_type: String,
131    pub params: HashMap<String, String>,
132}
133
134/// ManagerAgent の決定結果
135#[derive(Default, Clone)]
136pub struct ManagementDecision {
137    /// WorkerAgent への Guidance(Worker ID -> Guidance)
138    pub guidances: HashMap<WorkerId, Guidance>,
139    /// 戦略の動的変更(オプション)
140    pub strategy_update: Option<ManagementStrategy>,
141    /// 非同期タスクの発行(重いLLM呼び出し等)
142    pub async_tasks: Vec<AsyncTaskRequest>,
143}
144
145impl ManagementDecision {
146    /// 単一 Worker への Guidance を設定
147    pub fn with_guidance(mut self, worker_id: WorkerId, guidance: Guidance) -> Self {
148        self.guidances.insert(worker_id, guidance);
149        self
150    }
151
152    /// 全 Worker に同じ Guidance を設定
153    pub fn broadcast_guidance(mut self, worker_ids: &[WorkerId], guidance: Guidance) -> Self {
154        for &id in worker_ids {
155            self.guidances.insert(id, guidance.clone());
156        }
157        self
158    }
159}
160
161// ============================================================================
162// ManagerAgent Trait
163// ============================================================================
164
165/// 観察・判断・指示を行う上位Agent(Batch 対応)
166///
167/// # 設計
168///
169/// ManagerAgent は TaskContext を受け取り、Request を生成する。
170/// SwarmState からの分析は Analyzer が担当。
171///
172/// ```text
173/// ┌─────────────────────────────────────────────────────────────┐
174/// │                      Tick N                                  │
175/// │                                                              │
176/// │  SwarmState → [Analyzer] → TaskContext                      │
177/// │                                 ↓                            │
178/// │  Manager[0].prepare(ctx) ─→ N Requests ─┐                   │
179/// │  Manager[1].prepare(ctx) ─→ N Requests ─┼─→ Batch           │
180/// │  ...                                    │                    │
181/// │  Manager[M].prepare(ctx) ─→ N Requests ─┘                   │
182/// │                                                              │
183/// │                    ↓ LLM Batch Call (~1秒)                  │
184/// │                                                              │
185/// │  M×N Responses → 各 Manager.finalize(ctx, responses)        │
186/// │                    ↓                                         │
187/// │  Worker Instructions 配布                                    │
188/// └─────────────────────────────────────────────────────────────┘
189/// ```
190pub trait ManagerAgent: Send + Sync {
191    /// TaskContext を見て Batch リクエストを生成(軽量、同期)
192    ///
193    /// どの Worker に何を聞くかを決定する。
194    /// Orchestrator がこれらを集約して LLM Batch Call を実行する。
195    fn prepare(&self, context: &TaskContext) -> BatchDecisionRequest;
196
197    /// Batch 結果受取後: 指示を生成(軽量、同期)
198    ///
199    /// LLM Batch Call の結果を受けて、Worker への具体的な指示を生成する。
200    fn finalize(
201        &self,
202        context: &TaskContext,
203        responses: Vec<(WorkerId, DecisionResponse)>,
204    ) -> ManagementDecision;
205
206    /// Manager ID を取得
207    fn id(&self) -> ManagerId;
208
209    /// 名前を取得
210    fn name(&self) -> &str;
211}
212
213/// Blanket implementation for Box<dyn ManagerAgent>
214impl ManagerAgent for Box<dyn ManagerAgent> {
215    fn prepare(&self, context: &TaskContext) -> BatchDecisionRequest {
216        (**self).prepare(context)
217    }
218
219    fn finalize(
220        &self,
221        context: &TaskContext,
222        responses: Vec<(WorkerId, DecisionResponse)>,
223    ) -> ManagementDecision {
224        (**self).finalize(context, responses)
225    }
226
227    fn id(&self) -> ManagerId {
228        (**self).id()
229    }
230
231    fn name(&self) -> &str {
232        (**self).name()
233    }
234}