Skip to main content

lellm_agent/
hook.rs

1//! Agent-level Hook — AgentLoop 执行扩展点。
2//!
3//! v0.4+: Hook 仅用于观测(日志、指标等),不再修改 State。
4//! State 变更应通过 Mutation 模型进行。
5
6use crate::runtime::{AgentEvent, ToolUseResult};
7
8/// Agent 执行上下文 — 传递给 Hook 的执行信息。
9pub struct AgentHookContext {
10    /// Agent 节点名称
11    pub node_name: String,
12    /// 输入消息数量
13    pub input_message_count: usize,
14}
15
16/// Agent 执行快照 — after_agent 收到的执行结果。
17pub struct AgentHookSnapshot {
18    /// 执行结果
19    pub result: ToolUseResult,
20    /// 收到的事件流(用于审计)
21    pub events: Vec<AgentEvent>,
22}
23
24/// Agent-level Hook trait。
25///
26/// 在 AgentFlowNode 执行 Agent Loop 前后调用。
27/// v0.4+: 仅用于观测,不修改 State。
28pub trait AgentHook: Send + Sync {
29    /// Agent loop 执行前调用。
30    fn before_agent(&self, _ctx: &AgentHookContext) {}
31
32    /// Agent loop 执行后调用。
33    fn after_agent(&self, _snapshot: &AgentHookSnapshot) {}
34}
35
36/// 无操作 Hook — 默认行为。
37#[derive(Debug, Clone, Default)]
38pub struct NoOpAgentHook;
39
40impl AgentHook for NoOpAgentHook {}
41
42/// 日志 Hook — 将 agent 执行事件输出为 tracing 日志。
43#[derive(Debug, Clone)]
44pub struct TracingAgentHook;
45
46impl AgentHook for TracingAgentHook {
47    fn before_agent(&self, ctx: &AgentHookContext) {
48        tracing::debug!(
49            node = %ctx.node_name,
50            input_messages = ctx.input_message_count,
51            "agent loop starting"
52        );
53    }
54
55    fn after_agent(&self, snapshot: &AgentHookSnapshot) {
56        tracing::debug!(
57            iterations = snapshot.result.iterations,
58            tool_calls = snapshot.result.tool_calls_executed,
59            stop_reason = ?snapshot.result.stop_reason,
60            "agent loop completed"
61        );
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_noop_agent_hook() {
71        let hook = NoOpAgentHook;
72        let ctx = AgentHookContext {
73            node_name: "test".to_string(),
74            input_message_count: 0,
75        };
76        hook.before_agent(&ctx);
77    }
78
79    #[test]
80    fn test_tracing_agent_hook() {
81        let hook = TracingAgentHook;
82        let ctx = AgentHookContext {
83            node_name: "test".to_string(),
84            input_message_count: 5,
85        };
86        hook.before_agent(&ctx);
87    }
88}