1use super::types::{AgentAction, AgentFinish, AgentOutput, AgentStep};
5use async_trait::async_trait;
6use lc_callbacks::{CallbackManager, RunTree, RunType};
7use lc_core::tools::BaseTool;
8use lc_memory::BaseMemory;
9use serde_json::json;
10use std::collections::HashMap;
11use std::sync::Arc;
12
13#[derive(Debug, thiserror::Error)]
15pub enum AgentError {
16 #[error("Output parsing error: {0}")]
18 OutputParsingError(String),
19
20 #[error("Tool not found: {0}")]
22 ToolNotFound(String),
23
24 #[error("Tool execution error: {0}")]
26 ToolExecutionError(String),
27
28 #[error("Max iterations reached")]
30 MaxIterationsReached,
31
32 #[error("Agent error: {0}")]
34 Other(String),
35}
36
37#[async_trait]
42pub trait BaseAgent: Send + Sync {
43 async fn plan(
53 &self,
54 intermediate_steps: &[AgentStep],
55 inputs: &HashMap<String, String>,
56 ) -> Result<AgentOutput, AgentError>;
57
58 fn input_keys(&self) -> Vec<&str> {
60 vec!["input"]
61 }
62
63 fn get_allowed_tools(&self) -> Option<Vec<&str>> {
65 None
66 }
67
68 fn return_stopped_response(&self, _intermediate_steps: &[AgentStep]) -> AgentFinish {
70 AgentFinish::new(
71 "Agent stopped due to iteration limit or time limit.".to_string(),
72 String::new(),
73 )
74 }
75}
76
77pub struct AgentExecutor {
81 agent: Arc<dyn BaseAgent>,
83
84 tools: Vec<Arc<dyn BaseTool>>,
86
87 max_iterations: usize,
89
90 verbose: bool,
92
93 memory: Option<Arc<tokio::sync::Mutex<dyn BaseMemory>>>,
95
96 callbacks: Option<Arc<CallbackManager>>,
98}
99
100impl AgentExecutor {
101 pub fn new(agent: Arc<dyn BaseAgent>, tools: Vec<Arc<dyn BaseTool>>) -> Self {
103 Self {
104 agent,
105 tools,
106 max_iterations: 10,
107 verbose: false,
108 memory: None,
109 callbacks: None,
110 }
111 }
112
113 pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
115 self.max_iterations = max_iterations;
116 self
117 }
118
119 pub fn with_verbose(mut self, verbose: bool) -> Self {
121 self.verbose = verbose;
122 self
123 }
124
125 pub fn with_memory(mut self, memory: Arc<tokio::sync::Mutex<dyn BaseMemory>>) -> Self {
127 self.memory = Some(memory);
128 self
129 }
130
131 pub fn with_callbacks(mut self, callbacks: Arc<CallbackManager>) -> Self {
133 self.callbacks = Some(callbacks);
134 self
135 }
136
137 pub async fn invoke(&self, input: String) -> Result<String, AgentError> {
139 let mut root_run = RunTree::new(
140 "AgentExecutor",
141 RunType::Chain,
142 json!({"input": input.clone()}),
143 );
144
145 if let Some(ref callbacks) = self.callbacks {
146 for handler in callbacks.handlers() {
147 handler.on_chain_start(&root_run, &root_run.inputs).await;
148 }
149 }
150
151 let mut inputs = HashMap::new();
152 inputs.insert("input".to_string(), input.clone());
153
154 if let Some(memory) = &self.memory {
155 let memory_vars = memory
156 .lock()
157 .await
158 .load_memory_variables(&inputs)
159 .await
160 .map_err(|e| AgentError::Other(format!("Failed to load memory: {}", e)))?;
161
162 if let Some(history) = memory_vars.get("history") {
163 if let Some(history_str) = history.as_str() {
164 inputs.insert("history".to_string(), history_str.to_string());
165 }
166 }
167 }
168
169 let intermediate_steps: Vec<AgentStep> = Vec::new();
170
171 let result = self
172 .run_agent_loop(inputs.clone(), intermediate_steps, &mut root_run)
173 .await;
174
175 if let Some(memory) = &self.memory {
176 if let Ok(ref output) = result {
177 let mut outputs = HashMap::new();
178 outputs.insert("output".to_string(), output.clone());
179
180 memory
181 .lock()
182 .await
183 .save_context(&inputs, &outputs)
184 .await
185 .map_err(|e| AgentError::Other(format!("Failed to save memory: {}", e)))?;
186 }
187 }
188
189 match &result {
190 Ok(output) => {
191 root_run.end(json!({"output": output}));
192 if let Some(ref callbacks) = self.callbacks {
193 if let Some(ref outputs) = root_run.outputs {
194 for handler in callbacks.handlers() {
195 handler.on_chain_end(&root_run, outputs).await;
196 }
197 }
198 }
199 }
200 Err(e) => {
201 root_run.end_with_error(e.to_string());
202 if let Some(ref callbacks) = self.callbacks {
203 for handler in callbacks.handlers() {
204 handler.on_chain_error(&root_run, &e.to_string()).await;
205 }
206 }
207 }
208 }
209
210 result
211 }
212
213 async fn run_agent_loop(
215 &self,
216 inputs: HashMap<String, String>,
217 mut intermediate_steps: Vec<AgentStep>,
218 root_run: &mut RunTree,
219 ) -> Result<String, AgentError> {
220 for iteration in 0..self.max_iterations {
221 if self.verbose {
222 log::info!("=== Iteration {} ===", iteration + 1);
223 }
224
225 let output = self.agent.plan(&intermediate_steps, &inputs).await?;
226
227 match output {
228 AgentOutput::Finish(finish) => {
229 if self.verbose {
230 log::info!("Final answer: {:?}", finish.return_values);
231 }
232 return Ok(finish.output().unwrap_or("").to_string());
233 }
234
235 AgentOutput::Action(action) => {
236 if self.verbose {
237 log::info!("Action: {}({})", action.tool, action.tool_input);
238 }
239
240 let observation = self.execute_tool(&action, root_run).await?;
241
242 if self.verbose {
243 log::info!("Observation: {}", observation);
244 }
245
246 intermediate_steps.push(AgentStep::new(action, observation));
247 }
248
249 AgentOutput::Actions(actions) => {
250 if self.verbose {
251 log::info!("Parallel actions: {} count", actions.len());
252 for action in &actions {
253 log::info!(" - {}({})", action.tool, action.tool_input);
254 }
255 }
256
257 let observations = self.execute_tools_parallel(&actions, root_run).await?;
258
259 if self.verbose {
260 for (i, obs) in observations.iter().enumerate() {
261 log::info!("Observation {}: {}", i + 1, obs);
262 }
263 }
264
265 for (action, observation) in actions.into_iter().zip(observations.into_iter()) {
266 intermediate_steps.push(AgentStep::new(action, observation));
267 }
268 }
269 }
270 }
271
272 if self.verbose {
273 log::info!("Max iterations reached: {}", self.max_iterations);
274 }
275
276 let finish = self.agent.return_stopped_response(&intermediate_steps);
277 Ok(finish.output().unwrap_or("").to_string())
278 }
279
280 async fn execute_tools_parallel(
285 &self,
286 actions: &[super::types::AgentAction],
287 root_run: &RunTree,
288 ) -> Result<Vec<String>, AgentError> {
289 use futures_util::future::join_all;
290
291 let futures: Vec<_> = actions
292 .iter()
293 .map(|action| self.execute_tool(action, root_run))
294 .collect();
295
296 let results = join_all(futures).await;
297 let mut observations = Vec::with_capacity(results.len());
298 for result in results {
299 match result {
300 Ok(output) => observations.push(output),
301 Err(e) => observations.push(format!("[Tool execution error: {}]", e)),
302 }
303 }
304 Ok(observations)
305 }
306
307 async fn execute_tool(
309 &self,
310 action: &AgentAction,
311 root_run: &RunTree,
312 ) -> Result<String, AgentError> {
313 let tool = self
314 .tools
315 .iter()
316 .find(|t| t.name() == action.tool)
317 .ok_or_else(|| AgentError::ToolNotFound(action.tool.clone()))?;
318
319 let input_str = match &action.tool_input {
320 super::types::ToolInput::String { value: s } => s.clone(),
321 super::types::ToolInput::Object { value: v } => serde_json::to_string(v)
322 .map_err(|e| AgentError::Other(format!("Failed to serialize tool input: {}", e)))?,
323 };
324
325 let mut tool_run = root_run.create_child(
326 &action.tool,
327 RunType::Tool,
328 json!({"input": input_str.clone()}),
329 );
330
331 if let Some(ref callbacks) = self.callbacks {
332 for handler in callbacks.handlers() {
333 handler
334 .on_tool_start(&tool_run, &action.tool, &input_str)
335 .await;
336 }
337 }
338
339 let result = tool.run(input_str.clone()).await;
340
341 match result {
342 Ok(output) => {
343 tool_run.end(json!({"output": output.clone()}));
344 if let Some(ref callbacks) = self.callbacks {
345 for handler in callbacks.handlers() {
346 handler.on_tool_end(&tool_run, &output).await;
347 }
348 }
349 Ok(output)
350 }
351 Err(e) => {
352 tool_run.end_with_error(e.to_string());
353 if let Some(ref callbacks) = self.callbacks {
354 for handler in callbacks.handlers() {
355 handler.on_tool_error(&tool_run, &e.to_string()).await;
356 }
357 }
358 Err(AgentError::ToolExecutionError(e.to_string()))
359 }
360 }
361 }
362}
363
364impl std::fmt::Debug for AgentExecutor {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 f.debug_struct("AgentExecutor")
367 .field("max_iterations", &self.max_iterations)
368 .field("verbose", &self.verbose)
369 .field("tools_count", &self.tools.len())
370 .field("has_memory", &self.memory.is_some())
371 .finish()
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use lc_memory::ConversationBufferMemory;
379
380 #[tokio::test]
382 async fn test_agent_executor_with_memory() {
383 struct TestAgent;
385
386 #[async_trait]
387 impl BaseAgent for TestAgent {
388 async fn plan(
389 &self,
390 _intermediate_steps: &[AgentStep],
391 inputs: &HashMap<String, String>,
392 ) -> Result<AgentOutput, AgentError> {
393 if let Some(history) = inputs.get("history") {
395 if history.contains("Zhang San") {
396 return Ok(AgentOutput::Finish(AgentFinish::new(
397 "Your name is Zhang San".to_string(),
398 String::new(),
399 )));
400 }
401 }
402
403 let input = inputs.get("input").unwrap();
405 Ok(AgentOutput::Finish(AgentFinish::new(
406 format!("Received: {}", input),
407 String::new(),
408 )))
409 }
410 }
411
412 let memory = Arc::new(tokio::sync::Mutex::new(ConversationBufferMemory::new()));
414
415 let executor = AgentExecutor::new(Arc::new(TestAgent), vec![]).with_memory(memory);
417
418 let result1 = executor
420 .invoke("My name is Zhang San".to_string())
421 .await
422 .unwrap();
423 println!("Round 1: {}", result1);
424
425 let result2 = executor
427 .invoke("What is my name?".to_string())
428 .await
429 .unwrap();
430 println!("Round 2: {}", result2);
431
432 assert!(result2.contains("Zhang San"));
433 }
434}