Skip to main content

lellm_agent/runtime/
flow_node.rs

1//! AgentFlowNode — 将 ToolUseLoop 包装为 Graph FlowNode。
2//!
3//! 在 Graph 编排中作为节点执行 Agent Loop,读写 State 中的消息。
4
5use async_trait::async_trait;
6
7use lellm_graph::node::StreamNodeResult;
8use lellm_graph::{FlowEvent, FlowNode, GraphError, NextStep, NodeOutput};
9use lellm_graph::{GraphEvent, SpanId, State, StateDelta, TerminalError};
10
11use crate::hook::{AgentHook, AgentHookContext, AgentHookSnapshot};
12use crate::runtime::{AgentEvent, ToolUseLoop, ToolUseResult};
13
14/// Agent 在 Graph 中的节点包装。
15///
16/// 将 `ToolUseLoop` 适配为 `FlowNode`,使其可以作为 Graph 的节点执行。
17///
18/// # State 约定
19///
20/// - 输入: `state.get("messages")` → `Vec<serde_json::Value>` 或 `serde_json::Value` 数组
21/// - 输出: `state.set("messages")` → 更新后的消息列表
22/// - 自定义 key: 通过 `message_key` 配置
23///
24/// # 示例
25///
26/// ```rust,ignore
27/// use lellm_agent::AgentFlowNode;
28/// use lellm_graph::{GraphBuilder, NodeKind};
29///
30/// let agent = AgentFlowNode::new("agent", tool_use_loop);
31/// let mut graph = GraphBuilder::new("my_graph");
32/// graph.node("agent", NodeKind::External(Arc::new(agent)));
33/// ```
34#[derive(Clone)]
35pub struct AgentFlowNode {
36    /// 节点名称
37    name: String,
38    /// Agent 执行循环
39    loop_: ToolUseLoop,
40    /// State 中消息的 key(默认 "messages")
41    message_key: String,
42    /// 是否使用流式模式(仅在 execute_stream 中生效)
43    stream_events: bool,
44    /// Agent-level hooks(在 agent loop 前后调用)
45    hooks: Vec<std::sync::Arc<dyn AgentHook>>,
46}
47
48impl AgentFlowNode {
49    /// 创建新的 AgentFlowNode。
50    pub fn new(name: impl Into<String>, loop_: ToolUseLoop) -> Self {
51        Self {
52            name: name.into(),
53            loop_,
54            message_key: "messages".to_string(),
55            stream_events: true,
56            hooks: Vec::new(),
57        }
58    }
59
60    /// 设置 State 中消息的 key(默认 "messages")。
61    pub fn message_key(mut self, key: impl Into<String>) -> Self {
62        self.message_key = key.into();
63        self
64    }
65
66    /// 是否发射流式事件到 sink(默认 true)。
67    pub fn stream_events(mut self, enabled: bool) -> Self {
68        self.stream_events = enabled;
69        self
70    }
71
72    /// 添加 Agent-level hook。
73    ///
74    /// Hook 在 agent loop 执行前后调用,返回的 Deltas 会合并到 State。
75    pub fn hook(mut self, hook: impl AgentHook + 'static) -> Self {
76        self.hooks.push(std::sync::Arc::new(hook));
77        self
78    }
79
80    /// 从 State 中提取输入消息。
81    fn extract_messages(&self, state: &State) -> Vec<lellm_core::Message> {
82        // 尝试从 State 中读取 messages
83        // 格式: Vec<serde_json::Value>,每个 Value 是一个 Message 的 JSON 表示
84        if let Some(value) = state.get(&self.message_key) {
85            // 如果是数组,逐个解析
86            if let Some(arr) = value.as_array() {
87                let mut messages = Vec::new();
88                for v in arr {
89                    if let Ok(msg) = serde_json::from_value::<lellm_core::Message>(v.clone()) {
90                        messages.push(msg);
91                    }
92                }
93                messages
94            } else if let Ok(msg) = serde_json::from_value::<lellm_core::Message>(value.clone()) {
95                vec![msg]
96            } else {
97                Vec::new()
98            }
99        } else {
100            Vec::new()
101        }
102    }
103
104    /// 将执行结果转换为 StateDelta 列表。
105    fn collect_deltas(&self, result: &ToolUseResult) -> Vec<StateDelta> {
106        // 将最终消息列表序列化为 JSON
107        let messages: Vec<serde_json::Value> = result
108            .messages
109            .iter()
110            .filter_map(|m| serde_json::to_value(m).ok())
111            .collect();
112
113        vec![
114            StateDelta::put(&self.message_key, serde_json::json!(messages)),
115            StateDelta::put(
116                format!("{}_stop_reason", self.name),
117                serde_json::json!(format!("{:?}", result.stop_reason)),
118            ),
119            StateDelta::put(
120                format!("{}_iterations", self.name),
121                serde_json::json!(result.iterations),
122            ),
123            StateDelta::put(
124                format!("{}_tool_calls", self.name),
125                serde_json::json!(result.tool_calls_executed),
126            ),
127        ]
128    }
129}
130
131#[async_trait]
132impl FlowNode for AgentFlowNode {
133    /// 阻塞执行 — 运行完整的 Agent Loop。
134    async fn execute(&self, state: &State) -> Result<NodeOutput, GraphError> {
135        let messages = self.extract_messages(state);
136
137        // 如果没有消息,发送一个警告但仍继续执行(agent 可能只需要 system prompt)
138        if messages.is_empty() {
139            tracing::debug!(
140                agent = %self.name,
141                "no input messages found in state key '{}'",
142                self.message_key
143            );
144        }
145
146        // 调用 before_agent hooks
147        let ctx = AgentHookContext {
148            node_name: self.name.clone(),
149            input_message_count: messages.len(),
150        };
151        let mut hook_deltas: Vec<StateDelta> = self
152            .hooks
153            .iter()
154            .flat_map(|h| h.before_agent(&ctx))
155            .collect();
156
157        let result = self.loop_.execute(messages).await.map_err(|e| {
158            GraphError::Terminal(TerminalError::NodeExecutionFailed {
159                node: self.name.clone(),
160                source: e.into(),
161            })
162        })?;
163
164        // 调用 after_agent hooks
165        let snapshot = AgentHookSnapshot {
166            result: result.clone(),
167            events: Vec::new(), // 非流式模式无事件
168        };
169        hook_deltas.extend(self.hooks.iter().flat_map(|h| h.after_agent(&snapshot)));
170
171        let mut deltas = self.collect_deltas(&result);
172        deltas.extend(hook_deltas);
173
174        tracing::debug!(
175            agent = %self.name,
176            iterations = result.iterations,
177            tool_calls = result.tool_calls_executed,
178            stop_reason = ?result.stop_reason,
179            "agent execution completed"
180        );
181
182        Ok(NodeOutput {
183            deltas,
184            next: NextStep::GoToNext,
185            metadata: None,
186        })
187    }
188
189    /// 流式执行 — 运行 Agent Loop 并转发事件。
190    async fn execute_stream(
191        &self,
192        state: &State,
193        sink: &tokio::sync::mpsc::Sender<GraphEvent>,
194        span_id: SpanId,
195    ) -> Result<StreamNodeResult, GraphError> {
196        let messages = self.extract_messages(state);
197
198        // 调用 before_agent hooks
199        let ctx = AgentHookContext {
200            node_name: self.name.clone(),
201            input_message_count: messages.len(),
202        };
203        let hook_deltas: Vec<StateDelta> = self
204            .hooks
205            .iter()
206            .flat_map(|h| h.before_agent(&ctx))
207            .collect();
208
209        // 发射 NodeStarted 事件
210        let _ = sink
211            .send(GraphEvent::Node {
212                span_id,
213                node_name: self.name.clone(),
214                event: FlowEvent::NodeStarted {
215                    node_id: self.name.clone(),
216                    span_id,
217                },
218            })
219            .await;
220
221        // 启动流式 Agent Loop
222        let mut agent_stream = self.loop_.execute_stream(messages);
223
224        let mut final_result: Option<ToolUseResult> = None;
225        let mut error_delta: Option<StateDelta> = None;
226        let mut events: Vec<AgentEvent> = Vec::new();
227
228        while let Some(agent_event) = agent_stream.recv().await {
229            // 检查是否是终态事件
230            let is_terminal = matches!(
231                &agent_event,
232                AgentEvent::LoopEnd { .. } | AgentEvent::LoopError { .. }
233            );
234
235            // 收集事件(用于 after_agent hook)
236            events.push(agent_event.clone());
237
238            // 转发事件(如果启用)
239            if self.stream_events {
240                let _ = sink
241                    .send(GraphEvent::Node {
242                        span_id,
243                        node_name: self.name.clone(),
244                        event: FlowEvent::Custom {
245                            node_id: self.name.clone(),
246                            payload: Box::new(agent_event.clone()),
247                        },
248                    })
249                    .await;
250            }
251
252            // 处理终态事件
253            if is_terminal {
254                match &agent_event {
255                    AgentEvent::LoopEnd { result } => {
256                        final_result = Some(result.clone());
257                    }
258                    AgentEvent::LoopError { error, .. } => {
259                        // 错误信息转为 Delta
260                        error_delta = Some(StateDelta::put(
261                            format!("{}_error", self.name),
262                            serde_json::json!(error.to_string()),
263                        ));
264                    }
265                    _ => {}
266                }
267            }
268        }
269
270        // 如果有错误,返回 Fallback 让 Graph 决定如何处理
271        if let Some(err_delta) = error_delta {
272            return Ok(StreamNodeResult::Fallback {
273                deltas: hook_deltas
274                    .into_iter()
275                    .chain(std::iter::once(err_delta))
276                    .collect(),
277                reason: format!("agent loop error in '{}'", self.name),
278                node_name: self.name.clone(),
279            });
280        }
281
282        // 写入最终结果
283        if let Some(result) = final_result {
284            // 调用 after_agent hooks
285            let snapshot = AgentHookSnapshot {
286                result: result.clone(),
287                events,
288            };
289            let after_deltas: Vec<StateDelta> = self
290                .hooks
291                .iter()
292                .flat_map(|h| h.after_agent(&snapshot))
293                .collect();
294
295            let mut deltas = self.collect_deltas(&result);
296            deltas.extend(hook_deltas);
297            deltas.extend(after_deltas);
298
299            // 发射 NodeCompleted 事件
300            let _ = sink
301                .send(GraphEvent::Node {
302                    span_id,
303                    node_name: self.name.clone(),
304                    event: FlowEvent::NodeCompleted {
305                        node_id: self.name.clone(),
306                        span_id,
307                        duration: std::time::Duration::ZERO, // 由 executor 计算
308                    },
309                })
310                .await;
311
312            return Ok(StreamNodeResult::Continue {
313                deltas,
314                next: NextStep::GoToNext,
315                span_id,
316                observed: None,
317                metadata: None,
318            });
319        }
320
321        // 没有收到终态事件(channel 意外关闭)
322        Ok(StreamNodeResult::Fallback {
323            deltas: hook_deltas,
324            reason: "agent stream ended without terminal event".into(),
325            node_name: self.name.clone(),
326        })
327    }
328}