Skip to main content

swarm_engine_core/agent/
worker.rs

1//! WorkerAgent - 毎 Tick 実行する Agent
2
3use std::collections::HashMap;
4use std::time::Duration;
5
6use crate::state::SwarmState;
7use crate::types::{Action, ActionOutput, ActionResult, WorkerId};
8
9use super::escalation::EscalationReason;
10use super::manager::AsyncTaskRequest;
11
12// ============================================================================
13// WorkerScope - Worker に渡す情報のスコープ
14// ============================================================================
15
16/// Worker に渡す情報のスコープ
17///
18/// Manager が Guidance で指定し、ContextResolver がこれに応じて
19/// ResolvedContext を生成する。
20///
21/// # スコープレベル
22///
23/// - `Minimal`: 自分の last_output のみ(最小限)
24/// - `SelfDetail`: 自分の詳細情報(履歴、失敗数など)
25/// - `WithTeamSummary`: 自分 + チームのサマリー
26/// - `WithTeamDetail`: 自分 + チームの詳細(フル情報)
27/// - `Idle`: 何もしない(Worker は即座に Idle を返す)
28#[derive(Debug, Clone, Default, PartialEq, Eq)]
29pub enum WorkerScope {
30    /// 最小: 自分の last_output のみ
31    #[default]
32    Minimal,
33    /// 自分の詳細(履歴、失敗数など)
34    SelfDetail,
35    /// 自分 + チームのサマリー(ID + 最新アクションのみ)
36    WithTeamSummary,
37    /// 自分 + チームの詳細(フル情報)
38    WithTeamDetail,
39    /// 何もしない
40    Idle,
41}
42
43// ============================================================================
44// ScopeStrategy - Scope 決定戦略
45// ============================================================================
46
47use crate::context::TaskContext;
48
49/// Scope 決定戦略
50///
51/// Manager が Worker に渡す情報のスコープを決定する戦略。
52/// 外部から注入可能で、状況に応じた動的なスコープ制御を実現する。
53///
54/// # 使用例
55///
56/// ```ignore
57/// // 固定スコープ(全 Worker に同じスコープ)
58/// let strategy = FixedScopeStrategy::new(WorkerScope::Minimal);
59///
60/// // 適応的スコープ(状況に応じて変化)
61/// let strategy = AdaptiveScopeStrategy::new()
62///     .with_default(WorkerScope::Minimal)
63///     .with_on_escalation(WorkerScope::SelfDetail);
64/// ```
65pub trait ScopeStrategy: Send + Sync {
66    /// Worker の Scope を決定
67    ///
68    /// TaskContext と WorkerId を受け取り、その Worker に適用する
69    /// WorkerScope を返す。
70    fn determine_scope(&self, context: &TaskContext, worker_id: WorkerId) -> WorkerScope;
71}
72
73/// 固定 Scope 戦略
74///
75/// 全ての Worker に同じスコープを適用する最もシンプルな戦略。
76/// デフォルトは `WorkerScope::Minimal`。
77#[derive(Debug, Clone)]
78pub struct FixedScopeStrategy {
79    scope: WorkerScope,
80}
81
82impl FixedScopeStrategy {
83    /// 新しい FixedScopeStrategy を作成
84    pub fn new(scope: WorkerScope) -> Self {
85        Self { scope }
86    }
87
88    /// Minimal スコープの戦略を作成
89    pub fn minimal() -> Self {
90        Self::new(WorkerScope::Minimal)
91    }
92
93    /// SelfDetail スコープの戦略を作成
94    pub fn self_detail() -> Self {
95        Self::new(WorkerScope::SelfDetail)
96    }
97
98    /// WithTeamDetail スコープの戦略を作成
99    pub fn with_team_detail() -> Self {
100        Self::new(WorkerScope::WithTeamDetail)
101    }
102}
103
104impl Default for FixedScopeStrategy {
105    fn default() -> Self {
106        Self::minimal()
107    }
108}
109
110impl ScopeStrategy for FixedScopeStrategy {
111    fn determine_scope(&self, _context: &TaskContext, _worker_id: WorkerId) -> WorkerScope {
112        self.scope.clone()
113    }
114}
115
116/// 適応的 Scope 戦略
117///
118/// Worker の状態に応じて動的にスコープを変更する戦略。
119///
120/// - 通常時: `default` スコープ
121/// - Escalation 中: `on_escalation` スコープ
122/// - 連続失敗時: `on_high_failure` スコープ
123#[derive(Debug, Clone)]
124pub struct AdaptiveScopeStrategy {
125    /// 通常時のスコープ
126    pub default: WorkerScope,
127    /// Escalation 時のスコープ
128    pub on_escalation: WorkerScope,
129    /// 連続失敗時のスコープ
130    pub on_high_failure: WorkerScope,
131    /// 失敗閾値(この回数以上連続失敗すると on_high_failure を適用)
132    pub failure_threshold: u32,
133}
134
135impl AdaptiveScopeStrategy {
136    /// 新しい AdaptiveScopeStrategy を作成
137    pub fn new() -> Self {
138        Self::default()
139    }
140
141    /// 通常時のスコープを設定
142    pub fn with_default(mut self, scope: WorkerScope) -> Self {
143        self.default = scope;
144        self
145    }
146
147    /// Escalation 時のスコープを設定
148    pub fn with_on_escalation(mut self, scope: WorkerScope) -> Self {
149        self.on_escalation = scope;
150        self
151    }
152
153    /// 連続失敗時のスコープを設定
154    pub fn with_on_high_failure(mut self, scope: WorkerScope) -> Self {
155        self.on_high_failure = scope;
156        self
157    }
158
159    /// 失敗閾値を設定
160    pub fn with_failure_threshold(mut self, threshold: u32) -> Self {
161        self.failure_threshold = threshold;
162        self
163    }
164}
165
166impl Default for AdaptiveScopeStrategy {
167    fn default() -> Self {
168        Self {
169            default: WorkerScope::Minimal,
170            on_escalation: WorkerScope::SelfDetail,
171            on_high_failure: WorkerScope::SelfDetail,
172            failure_threshold: 3,
173        }
174    }
175}
176
177impl ScopeStrategy for AdaptiveScopeStrategy {
178    fn determine_scope(&self, context: &TaskContext, worker_id: WorkerId) -> WorkerScope {
179        // Escalation 中の場合
180        if context.has_escalation_for(worker_id) {
181            return self.on_escalation.clone();
182        }
183
184        // 連続失敗が閾値以上の場合
185        if let Some(summary) = context.workers.get(&worker_id) {
186            if summary.consecutive_failures >= self.failure_threshold {
187                return self.on_high_failure.clone();
188            }
189        }
190
191        // 通常時
192        self.default.clone()
193    }
194}
195
196// ============================================================================
197// Basic Types
198// ============================================================================
199
200/// 優先度
201#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
202pub enum Priority {
203    Low,
204    #[default]
205    Normal,
206    High,
207    Critical,
208}
209
210/// Task 説明
211#[derive(Debug, Clone)]
212pub struct TaskDescription {
213    pub name: String,
214    pub details: String,
215}
216
217/// Issue - Worker が困っていること
218#[derive(Debug, Clone)]
219pub struct Issue {
220    pub description: String,
221    pub severity: Priority,
222}
223
224/// 提案オプション
225#[derive(Debug, Clone)]
226pub struct ProposedOption {
227    pub description: String,
228    pub pros: Vec<String>,
229    pub cons: Vec<String>,
230}
231
232/// 関連 State のスナップショット
233#[derive(Debug, Clone, Default)]
234pub struct RelevantState {
235    pub data: Vec<u8>,
236}
237
238// ============================================================================
239// Guidance
240// ============================================================================
241
242/// ManagerAgent から Worker への指示・方針
243///
244/// RuleBased / LLMBased 両方のパターンに対応するシンプルな構造。
245///
246/// # 使用パターン
247///
248/// ## RuleBased: 具体的な Action を1つ指示
249/// ```ignore
250/// Guidance::action(read_action)
251/// ```
252///
253/// ## LLMBased: 複数の Action 候補 + 方針テキスト
254/// ```ignore
255/// Guidance::with_candidates(vec![read, grep, write], "ファイル探索を優先")
256/// ```
257#[derive(Debug, Clone, Default)]
258pub struct Guidance {
259    /// 実行すべき/候補となる Action のリスト
260    pub actions: Vec<Action>,
261    /// 方針・ヒント・追加コンテキスト(オプション)
262    pub content: Option<String>,
263    /// 拡張用プロパティ(将来の追加データ用)
264    pub props: HashMap<String, Vec<u8>>,
265    /// 探索ターゲット(Manager からの探索指示)
266    pub exploration_target: Option<crate::exploration::ExplorationTarget>,
267    /// Worker が見る情報のスコープ
268    pub scope: WorkerScope,
269}
270
271impl Guidance {
272    /// 単一 Action の指示を作成(RuleBased 向け)
273    pub fn action(action: Action) -> Self {
274        Self {
275            actions: vec![action],
276            content: None,
277            props: HashMap::new(),
278            exploration_target: None,
279            scope: WorkerScope::Minimal,
280        }
281    }
282
283    /// 複数 Action 候補 + 方針テキストを作成(LLMBased 向け)
284    pub fn with_candidates(actions: Vec<Action>, content: impl Into<String>) -> Self {
285        Self {
286            actions,
287            content: Some(content.into()),
288            props: HashMap::new(),
289            exploration_target: None,
290            scope: WorkerScope::Minimal,
291        }
292    }
293
294    /// 方針テキストのみ(Action なし)
295    pub fn hint(content: impl Into<String>) -> Self {
296        Self {
297            actions: Vec::new(),
298            content: Some(content.into()),
299            props: HashMap::new(),
300            exploration_target: None,
301            scope: WorkerScope::Minimal,
302        }
303    }
304
305    /// Idle 指示を作成(何もしない)
306    pub fn idle() -> Self {
307        Self {
308            actions: Vec::new(),
309            content: None,
310            props: HashMap::new(),
311            exploration_target: None,
312            scope: WorkerScope::Idle,
313        }
314    }
315
316    /// Idle 指示かどうか
317    pub fn is_idle(&self) -> bool {
318        matches!(self.scope, WorkerScope::Idle)
319    }
320
321    /// スコープを設定
322    pub fn with_scope(mut self, scope: WorkerScope) -> Self {
323        self.scope = scope;
324        self
325    }
326
327    /// プロパティを追加
328    pub fn with_prop(mut self, key: impl Into<String>, value: Vec<u8>) -> Self {
329        self.props.insert(key.into(), value);
330        self
331    }
332
333    /// 探索ターゲットを設定
334    pub fn with_exploration_target(
335        mut self,
336        target: crate::exploration::ExplorationTarget,
337    ) -> Self {
338        self.exploration_target = Some(target);
339        self
340    }
341}
342
343// ============================================================================
344// From<&MapNode<ActionNodeData, S>> for Guidance
345// ============================================================================
346
347impl<S: crate::exploration::map::MapState>
348    From<&crate::exploration::MapNode<crate::exploration::ActionNodeData, S>> for Guidance
349{
350    /// MapNode から Guidance への変換
351    ///
352    /// V2 ExplorationSpace の select_nodes() 結果を直接 Guidance に変換する。
353    /// Manager はこれをそのまま Worker に渡せる。
354    ///
355    /// # 変換内容
356    ///
357    /// - `node.data` → `Action` (ActionNodeData から変換)
358    /// - `node.data.discovery` → `hint` (ExplorationTarget に設定)
359    /// - `node.id` → `exploration_target.node_id`
360    fn from(node: &crate::exploration::MapNode<crate::exploration::ActionNodeData, S>) -> Self {
361        use crate::exploration::{ExplorationTarget, NodeId};
362
363        let action = Action::from(&node.data);
364
365        // discovery があれば hint として設定
366        let hint = node.data.discovery.as_ref().map(|d| match d {
367            serde_json::Value::String(s) => s.clone(),
368            other => other.to_string(),
369        });
370
371        // ExplorationTarget を構築
372        let target = ExplorationTarget::new(NodeId::from(node.id)).with_action(action.clone());
373        let target = if let Some(h) = hint {
374            target.with_hint(h)
375        } else {
376            target
377        };
378
379        Guidance::action(action).with_exploration_target(target)
380    }
381}
382
383// ============================================================================
384// ManagerInstruction - Manager 指示の軽量表現(Prompt 埋め込み用)
385// ============================================================================
386
387/// Manager から Worker への指示(Prompt 埋め込み用の軽量版)
388///
389/// 前回の Manager 判断を次回の Worker Prompt に埋め込むために使用。
390/// Guidance から必要な情報のみを抽出した構造体。
391///
392/// # 使用フロー
393///
394/// ```text
395/// Tick N: Manager.finalize() → Guidance → ManagerInstruction::from(&guidance)
396/// Tick N+1: Manager.prepare() → ResolvedContext.manager_instruction = Some(...)
397///           → PromptBuilder が "Manager's Instruction" セクションを生成
398/// ```
399#[derive(Debug, Clone, Default)]
400pub struct ManagerInstruction {
401    /// Manager からの指示テキスト(Guidance.content)
402    pub instruction: Option<String>,
403    /// 推奨されたアクション名(Guidance.actions[0].name)
404    pub suggested_action: Option<String>,
405    /// 推奨されたアクションの対象(Guidance.actions[0].params.target)
406    pub suggested_target: Option<String>,
407    /// 探索ヒント(ExplorationTarget からの情報)
408    pub exploration_hint: Option<String>,
409}
410
411impl ManagerInstruction {
412    /// 新しい ManagerInstruction を作成
413    pub fn new() -> Self {
414        Self::default()
415    }
416
417    /// 指示テキストを設定
418    pub fn with_instruction(mut self, instruction: impl Into<String>) -> Self {
419        self.instruction = Some(instruction.into());
420        self
421    }
422
423    /// 推奨アクションを設定
424    pub fn with_suggested_action(mut self, action: impl Into<String>) -> Self {
425        self.suggested_action = Some(action.into());
426        self
427    }
428
429    /// 推奨ターゲットを設定
430    pub fn with_suggested_target(mut self, target: impl Into<String>) -> Self {
431        self.suggested_target = Some(target.into());
432        self
433    }
434
435    /// 探索ヒントを設定
436    pub fn with_exploration_hint(mut self, hint: impl Into<String>) -> Self {
437        self.exploration_hint = Some(hint.into());
438        self
439    }
440
441    /// Guidance から ManagerInstruction を生成
442    pub fn from_guidance(guidance: &Guidance) -> Self {
443        let mut mi = Self::new();
444
445        // content → instruction
446        if let Some(ref content) = guidance.content {
447            mi.instruction = Some(content.clone());
448        }
449
450        // actions[0] → suggested_action, suggested_target
451        if let Some(action) = guidance.actions.first() {
452            mi.suggested_action = Some(action.name.clone());
453            if let Some(ref target) = action.params.target {
454                mi.suggested_target = Some(target.clone());
455            }
456        }
457
458        // exploration_target → exploration_hint
459        if let Some(ref target) = guidance.exploration_target {
460            let hint = format!(
461                "Node {} ({})",
462                target.node_id.0,
463                target.hint.as_deref().unwrap_or("no hint")
464            );
465            mi.exploration_hint = Some(hint);
466        }
467
468        mi
469    }
470
471    /// 情報があるかどうか
472    pub fn has_content(&self) -> bool {
473        self.instruction.is_some()
474            || self.suggested_action.is_some()
475            || self.exploration_hint.is_some()
476    }
477}
478
479// ============================================================================
480// WorkResult
481// ============================================================================
482
483/// Worker の実行結果
484#[derive(Debug)]
485pub enum WorkResult {
486    /// 自律的に行動した結果
487    Acted {
488        /// Action の実行結果
489        action_result: ActionResult,
490        /// 状態変更リクエスト(Runtime が Phase 6 でマージ)
491        state_delta: Option<WorkerStateDelta>,
492    },
493    /// 継続中(自分で判断して次Tickも続ける)
494    Continuing { progress: f32 },
495    /// ManagerAgent の判断を仰ぎたい
496    NeedsGuidance {
497        reason: String,
498        context: GuidanceContext,
499    },
500    /// Agent 自身が Escalation を要求(Manager への介入依頼)
501    Escalate {
502        reason: EscalationReason,
503        context: Option<String>,
504    },
505    /// 待機中(やることがない、または他の Worker を待っている)
506    Idle,
507    /// タスク完了(Environment から done=true を受け取った)
508    Done {
509        /// 成功/失敗
510        success: bool,
511        /// 完了メッセージ
512        message: Option<String>,
513    },
514}
515
516impl WorkResult {
517    /// ActionResult から簡易的に Acted を生成(state_delta なし)
518    pub fn acted(action_result: ActionResult) -> Self {
519        Self::Acted {
520            action_result,
521            state_delta: None,
522        }
523    }
524
525    /// ActionResult と state_delta から Acted を生成
526    pub fn acted_with_delta(action_result: ActionResult, state_delta: WorkerStateDelta) -> Self {
527        Self::Acted {
528            action_result,
529            state_delta: Some(state_delta),
530        }
531    }
532
533    /// 成功で完了
534    pub fn done_success(message: impl Into<String>) -> Self {
535        Self::Done {
536            success: true,
537            message: Some(message.into()),
538        }
539    }
540
541    /// 失敗で完了
542    pub fn done_failure(message: impl Into<String>) -> Self {
543        Self::Done {
544            success: false,
545            message: Some(message.into()),
546        }
547    }
548
549    /// タスク完了かどうか
550    pub fn is_done(&self) -> bool {
551        matches!(self, Self::Done { .. })
552    }
553
554    // ------------------------------------------------------------------------
555    // Environment 向けヘルパー
556    // ------------------------------------------------------------------------
557
558    /// Environment からの成功結果
559    pub fn env_success(message: impl Into<String>) -> Self {
560        Self::Acted {
561            action_result: ActionResult {
562                success: true,
563                output: Some(ActionOutput::Text(message.into())),
564                error: None,
565                duration: Duration::ZERO,
566                discovered_targets: Vec::new(),
567            },
568            state_delta: None,
569        }
570    }
571
572    /// Environment からの成功結果(データ付き)
573    pub fn env_success_with_data(_message: impl Into<String>, data: impl Into<String>) -> Self {
574        Self::Acted {
575            action_result: ActionResult {
576                success: true,
577                output: Some(ActionOutput::Text(data.into())),
578                error: None,
579                duration: Duration::ZERO,
580                discovered_targets: Vec::new(),
581            },
582            state_delta: None,
583        }
584    }
585
586    /// Environment からの成功結果(構造化データ付き)
587    pub fn env_success_structured(data: serde_json::Value) -> Self {
588        Self::Acted {
589            action_result: ActionResult {
590                success: true,
591                output: Some(ActionOutput::Structured(data)),
592                error: None,
593                duration: Duration::ZERO,
594                discovered_targets: Vec::new(),
595            },
596            state_delta: None,
597        }
598    }
599
600    /// Environment からの成功結果(発見したターゲット付き)
601    ///
602    /// Search 系アクションで使用。discovered_targets は ExploMap で
603    /// 新しいノードとして展開される。
604    pub fn env_success_with_discoveries(
605        _message: impl Into<String>,
606        data: impl Into<String>,
607        discovered_targets: Vec<String>,
608    ) -> Self {
609        Self::Acted {
610            action_result: ActionResult {
611                success: true,
612                output: Some(ActionOutput::Text(data.into())),
613                error: None,
614                duration: Duration::ZERO,
615                discovered_targets,
616            },
617            state_delta: None,
618        }
619    }
620
621    /// Environment からの失敗結果
622    pub fn env_failure(message: impl Into<String>) -> Self {
623        Self::Acted {
624            action_result: ActionResult {
625                success: false,
626                output: None,
627                error: Some(message.into()),
628                duration: Duration::ZERO,
629                discovered_targets: Vec::new(),
630            },
631            state_delta: None,
632        }
633    }
634
635    /// 未サポートアクション
636    pub fn unsupported(action_name: &str) -> Self {
637        Self::env_failure(format!("Unsupported action: {}", action_name))
638    }
639}
640
641// ============================================================================
642// WorkerStateDelta
643// ============================================================================
644
645/// Worker の状態変更リクエスト(Runtime が Phase 6 でマージ)
646///
647/// Worker は `&self` で実行されるため、直接 State を変更できない。
648/// 代わりに変更リクエストを `WorkResult` に含めて返し、
649/// Runtime が Phase 6 で一括適用する。
650#[derive(Debug, Clone, Default)]
651pub struct WorkerStateDelta {
652    /// ローカルキャッシュ更新リクエスト
653    pub cache_updates: Vec<CacheUpdate>,
654    /// SharedData 更新リクエスト
655    pub shared_updates: Vec<SharedUpdate>,
656    /// 非同期タスク発行リクエスト
657    pub async_tasks: Vec<AsyncTaskRequest>,
658}
659
660impl WorkerStateDelta {
661    pub fn new() -> Self {
662        Self::default()
663    }
664
665    /// キャッシュ更新を追加
666    pub fn with_cache(mut self, key: impl Into<String>, value: Vec<u8>, ttl_ticks: u64) -> Self {
667        self.cache_updates.push(CacheUpdate {
668            key: key.into(),
669            value,
670            ttl_ticks,
671        });
672        self
673    }
674
675    /// SharedData 更新を追加
676    pub fn with_shared(mut self, key: impl Into<String>, value: Vec<u8>) -> Self {
677        self.shared_updates.push(SharedUpdate {
678            key: key.into(),
679            value,
680        });
681        self
682    }
683
684    /// 非同期タスク発行を追加
685    pub fn with_async_task(
686        mut self,
687        task_type: impl Into<String>,
688        params: HashMap<String, String>,
689    ) -> Self {
690        self.async_tasks.push(AsyncTaskRequest {
691            task_type: task_type.into(),
692            params,
693        });
694        self
695    }
696}
697
698/// ローカルキャッシュ更新リクエスト
699#[derive(Debug, Clone)]
700pub struct CacheUpdate {
701    /// キャッシュキー
702    pub key: String,
703    /// 値
704    pub value: Vec<u8>,
705    /// TTL(Tick数)
706    pub ttl_ticks: u64,
707}
708
709/// SharedData 更新リクエスト
710#[derive(Debug, Clone)]
711pub struct SharedUpdate {
712    /// キー
713    pub key: String,
714    /// 値
715    pub value: Vec<u8>,
716}
717
718// ============================================================================
719// GuidanceContext
720// ============================================================================
721
722/// NeedsGuidance 時に ManagerAgent に渡すコンテキスト
723#[derive(Debug, Clone)]
724pub struct GuidanceContext {
725    /// 何に困っているか
726    pub issue: Issue,
727    /// 自分で考えた選択肢(あれば)
728    pub options: Vec<ProposedOption>,
729    /// 関連する State のスナップショット
730    pub relevant_state: RelevantState,
731}
732
733// ============================================================================
734// ScheduledAction
735// ============================================================================
736
737/// スケジュールされたアクション
738#[derive(Debug)]
739pub struct ScheduledAction {
740    /// 対象Agent
741    pub agent_id: WorkerId,
742    /// 実行するAction
743    pub action: Action,
744    /// 優先度
745    pub priority: Priority,
746}
747
748// ============================================================================
749// WorkerAgent Trait
750// ============================================================================
751
752/// 毎 Tick 実行する Agent
753///
754/// RuleBased・LLMBased 両方のパターンを想定した汎用的な Worker trait です。
755/// SwarmApp は登録された全 WorkerAgent を毎 Tick 並列実行します。
756///
757/// # 設計方針
758///
759/// この trait は LLMRouter 等の使用を強制しません。
760/// システムの要件に応じて、以下のいずれかのパターンで実装できます:
761///
762/// - **RuleBased**: 固定ロジックで高速実行(< 1ms)
763/// - **LLMBased**: ActionRouter/LLM を内部で使用し自律判断
764///
765/// # ライフサイクル
766///
767/// ```text
768/// SwarmApp.run()
769///     └─ loop {
770///            for worker in workers {
771///                worker.think_and_act(state, guidance)  // ← 毎 Tick 呼ばれる
772///            }
773///        }
774/// ```
775pub trait WorkerAgent: Send + Sync {
776    /// 自律的に判断して行動(毎 Tick 呼ばれる)
777    ///
778    /// # 並列実行のための設計
779    ///
780    /// `&self` で実行し、状態変更は `WorkResult` で返す。
781    /// これにより `par_iter()` での並列実行が可能。
782    ///
783    /// # Arguments
784    ///
785    /// * `state` - 現在の Swarm 状態(ReadOnly)
786    /// * `guidance` - Manager からの方針・ヒント(オプション)
787    ///
788    /// # Returns
789    ///
790    /// * [`WorkResult::Acted`] - Action を実行した
791    /// * [`WorkResult::Continuing`] - 処理継続中(次 Tick も続ける)
792    /// * [`WorkResult::NeedsGuidance`] - Manager の判断を仰ぎたい
793    /// * [`WorkResult::Idle`] - やることがない
794    fn think_and_act(&self, state: &SwarmState, guidance: Option<&Guidance>) -> WorkResult;
795
796    /// Worker ID を取得
797    fn id(&self) -> WorkerId;
798
799    /// 名前を取得
800    fn name(&self) -> &str;
801}