Skip to main content

adk_core/
agent.rs

1use crate::{InvocationContext, Result, event::Event};
2use async_trait::async_trait;
3use futures::stream::Stream;
4use std::pin::Pin;
5use std::sync::Arc;
6
7/// A pinned, boxed stream of [`Event`] results emitted by an agent during execution.
8pub type EventStream = Pin<Box<dyn Stream<Item = Result<Event>> + Send>>;
9
10/// The fundamental trait for all ADK agents.
11///
12/// Every agent — whether a simple LLM wrapper, a multi-step workflow, or a
13/// composite orchestrator — implements this trait. The runtime invokes
14/// [`run`](Self::run) with an [`InvocationContext`] and consumes the returned
15/// [`EventStream`].
16#[async_trait]
17pub trait Agent: Send + Sync {
18    /// Returns the unique name of this agent.
19    fn name(&self) -> &str;
20    /// Returns a human-readable description of this agent's purpose.
21    fn description(&self) -> &str;
22    /// Returns the child agents managed by this agent.
23    fn sub_agents(&self) -> &[Arc<dyn Agent>];
24
25    /// Whether this agent participates in LLM-driven agent transfer and may be
26    /// resumed directly across conversation turns.
27    ///
28    /// When a session persists across turns, the runner inspects history to
29    /// decide which agent should handle the next user message. LLM-based and
30    /// custom agents return the default `true`, so the runner can hand a new
31    /// turn back to whichever agent responded last.
32    ///
33    /// Deterministic workflow agents (sequential, parallel, loop, conditional)
34    /// override this to return `false`. Their sub-agents must not be resumed
35    /// individually: doing so would skip the workflow's other sub-agents on
36    /// subsequent turns. Returning `false` makes the runner resume the workflow
37    /// root instead, so every sub-agent runs again on each turn.
38    fn supports_agent_transfer(&self) -> bool {
39        true
40    }
41
42    /// Executes the agent and returns a stream of events.
43    async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream>;
44}
45
46/// A validated context containing engineered instructions and resolved tool instances.
47///
48/// This structure serves as the "Atomic Unit of Capability" for an agent. It guarantees
49/// that the agent's cognitive frame (the instructions telling it what it can do) is
50/// perfectly aligned with its physical capabilities (the binary tool instances bound
51/// to the session).
52///
53/// By using `ResolvedContext`, the framework eliminates "Phantom Tool" hallucinations,
54/// where an agent tries to call a tool that was mentioned in its prompt but never
55/// actually registered in the runtime.
56#[derive(Clone)]
57pub struct ResolvedContext {
58    /// The engineered system instruction.
59    pub system_instruction: String,
60    /// The resolved, executable tools.
61    pub active_tools: Vec<Arc<dyn crate::Tool>>,
62}
63
64impl std::fmt::Debug for ResolvedContext {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("ResolvedContext")
67            .field("system_instruction_len", &self.system_instruction.len())
68            .field("active_tools_count", &self.active_tools.len())
69            .finish()
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::{Content, ReadonlyContext, RunConfig};
77    use async_stream::stream;
78
79    struct TestAgent {
80        name: String,
81    }
82
83    use crate::{CallbackContext, Session, State};
84    use std::collections::HashMap;
85
86    struct MockState;
87    impl State for MockState {
88        fn get(&self, _key: &str) -> Option<serde_json::Value> {
89            None
90        }
91        fn set(&mut self, _key: String, _value: serde_json::Value) {}
92        fn all(&self) -> HashMap<String, serde_json::Value> {
93            HashMap::new()
94        }
95    }
96
97    struct MockSession;
98    impl Session for MockSession {
99        fn id(&self) -> &str {
100            "session"
101        }
102        fn app_name(&self) -> &str {
103            "app"
104        }
105        fn user_id(&self) -> &str {
106            "user"
107        }
108        fn state(&self) -> &dyn State {
109            &MockState
110        }
111        fn conversation_history(&self) -> Vec<Content> {
112            Vec::new()
113        }
114    }
115
116    #[allow(dead_code)]
117    struct TestContext {
118        content: Content,
119        config: RunConfig,
120        session: MockSession,
121    }
122
123    #[allow(dead_code)]
124    impl TestContext {
125        fn new() -> Self {
126            Self {
127                content: Content::new("user"),
128                config: RunConfig::default(),
129                session: MockSession,
130            }
131        }
132    }
133
134    #[async_trait]
135    impl ReadonlyContext for TestContext {
136        fn invocation_id(&self) -> &str {
137            "test"
138        }
139        fn agent_name(&self) -> &str {
140            "test"
141        }
142        fn user_id(&self) -> &str {
143            "user"
144        }
145        fn app_name(&self) -> &str {
146            "app"
147        }
148        fn session_id(&self) -> &str {
149            "session"
150        }
151        fn branch(&self) -> &str {
152            ""
153        }
154        fn user_content(&self) -> &Content {
155            &self.content
156        }
157    }
158
159    #[async_trait]
160    impl CallbackContext for TestContext {
161        fn artifacts(&self) -> Option<Arc<dyn crate::Artifacts>> {
162            None
163        }
164    }
165
166    #[async_trait]
167    impl InvocationContext for TestContext {
168        fn agent(&self) -> Arc<dyn Agent> {
169            unimplemented!()
170        }
171        fn memory(&self) -> Option<Arc<dyn crate::Memory>> {
172            None
173        }
174        fn session(&self) -> &dyn Session {
175            &self.session
176        }
177        fn run_config(&self) -> &RunConfig {
178            &self.config
179        }
180        fn end_invocation(&self) {}
181        fn ended(&self) -> bool {
182            false
183        }
184    }
185
186    #[async_trait]
187    impl Agent for TestAgent {
188        fn name(&self) -> &str {
189            &self.name
190        }
191
192        fn description(&self) -> &str {
193            "test agent"
194        }
195
196        fn sub_agents(&self) -> &[Arc<dyn Agent>] {
197            &[]
198        }
199
200        async fn run(&self, _ctx: Arc<dyn InvocationContext>) -> Result<EventStream> {
201            let s = stream! {
202                yield Ok(Event::new("test"));
203            };
204            Ok(Box::pin(s))
205        }
206    }
207
208    #[test]
209    fn test_agent_trait() {
210        let agent = TestAgent { name: "test".to_string() };
211        assert_eq!(agent.name(), "test");
212        assert_eq!(agent.description(), "test agent");
213    }
214}