Skip to main content

lc_agents/orchestrator/
mod.rs

1//! Common trait for high-level orchestrators (P1-1)
2//!
3//! `PlanExecuteAgent` / `DeepResearchAgent` / `CorrectiveRAGAgent` / `AdaptiveRAG`
4//! each used to define its own `run()`, with incompatible signatures that could
5//! not be composed or plugged into LCEL. This module unifies them:
6//!
7//! - [`Orchestrator`] defines `run_with_context(input, ctx)`, with errors
8//!   unified to [`AgentError`].
9//! - [`RunContext`] carries `trace_id` (P1-4 observability) and a cross-step
10//!   shared workspace.
11//! - [`crate::adapter::OrchestratorRunnable`] lets orchestrators enter LCEL pipelines.
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! use lc_agents::orchestration::{Orchestrator, RunContext};
17//!
18//! let plan_agent = PlanExecuteAgent::new(llm, tools);
19//! let ctx = RunContext::new("trace-abc");
20//! let output = plan_agent.run_with_context("目标".to_string(), &ctx).await?;
21//! ```
22
23use std::sync::{Arc, Mutex};
24
25use async_trait::async_trait;
26use lc_core::runnables::RunnableConfig;
27use serde_json::Value;
28
29use crate::AgentError;
30
31mod fan_out_fan_in;
32mod impls;
33mod review;
34mod sequential;
35mod task_adapter;
36#[cfg(test)]
37mod tests;
38
39pub use fan_out_fan_in::FanOutFanIn;
40pub use review::{parse_review_verdict, review_envelope, ReviewOrchestrator, ReviewVerdict};
41pub use sequential::SequentialPipeline;
42pub use task_adapter::{task_adapter, TaskAdapter};
43
44/// Common trait for high-level orchestrators.
45///
46/// Associated types express each orchestrator's different input/output
47/// (PlanExecute→String, AdaptiveRAG→AdaptiveRAGResult, etc.); `run_with_context`
48/// unifies the signature + [`AgentError`], so orchestrators are composable and
49/// can enter LCEL.
50#[async_trait]
51pub trait Orchestrator: Send + Sync {
52    /// Input type (usually a `String` objective/question).
53    type Input;
54    /// Output type.
55    type Output;
56
57    /// Execution entry point carrying the run context.
58    async fn run_with_context(
59        &self,
60        input: Self::Input,
61        ctx: &RunContext,
62    ) -> Result<Self::Output, AgentError>;
63}
64
65/// Orchestrator run context.
66///
67/// `trace_id` propagates across multi-agent / cross-step call chains (P1-4);
68/// `shared_state` provides a JSON workspace shared across steps.
69#[derive(Debug, Clone)]
70pub struct RunContext {
71    /// Trace ID: shared across the whole call chain, for log/audit/metric correlation.
72    pub trace_id: String,
73    /// Workspace shared across steps (optional).
74    pub shared_state: Option<Arc<Mutex<Value>>>,
75}
76
77/// Generates a lightweight trace_id (hex timestamp).
78pub fn generate_trace_id() -> String {
79    use std::time::{SystemTime, UNIX_EPOCH};
80    let nanos = SystemTime::now()
81        .duration_since(UNIX_EPOCH)
82        .map(|d| d.as_nanos())
83        .unwrap_or(0);
84    format!("trace-{:x}", nanos)
85}
86
87impl RunContext {
88    /// Creates a context with the given `trace_id`.
89    pub fn new(trace_id: impl Into<String>) -> Self {
90        Self {
91            trace_id: trace_id.into(),
92            shared_state: None,
93        }
94    }
95
96    /// Creates a context, auto-generating `trace_id`.
97    pub fn new_random() -> Self {
98        Self::new(generate_trace_id())
99    }
100
101    /// Carries the shared workspace.
102    pub fn with_shared_state(mut self, shared_state: Arc<Mutex<Value>>) -> Self {
103        self.shared_state = Some(shared_state);
104        self
105    }
106
107    /// Extracts `trace_id` from the LCEL [`RunnableConfig`] (reads
108    /// `metadata["trace_id"]`), generating one if missing. Used to thread the
109    /// LCEL pipeline's trace through to the orchestrator.
110    pub fn from_config(config: &RunnableConfig) -> Self {
111        let trace_id = config
112            .metadata
113            .get("trace_id")
114            .and_then(|v| v.as_str())
115            .map(|s| s.to_string())
116            .unwrap_or_else(generate_trace_id);
117        Self::new(trace_id)
118    }
119}