1use super::types::{AgentAction, AgentFinish, AgentOutput, AgentStep};
5use super::streaming::state::AgentStreamEvent;
6use async_trait::async_trait;
7use lc_callbacks::{CallbackManager, RunTree, RunType};
8use lc_core::tools::BaseTool;
9use lc_memory::BaseMemory;
10use serde_json::json;
11use std::collections::HashMap;
12use std::pin::Pin;
13use std::sync::Arc;
14use futures_util::Stream;
15
16#[derive(Debug, thiserror::Error)]
18pub enum AgentError {
19 #[error("Output parsing error: {0}")]
21 OutputParsingError(String),
22
23 #[error("Tool not found: {0}")]
25 ToolNotFound(String),
26
27 #[error("Tool execution error: {0}")]
29 ToolExecutionError(String),
30
31 #[error("Max iterations reached")]
33 MaxIterationsReached,
34
35 #[error("Agent error: {0}")]
37 Other(String),
38}
39
40#[async_trait]
45pub trait BaseAgent: Send + Sync {
46 async fn plan(
56 &self,
57 intermediate_steps: &[AgentStep],
58 inputs: &HashMap<String, String>,
59 ) -> Result<AgentOutput, AgentError>;
60
61 fn input_keys(&self) -> Vec<&str> {
63 vec!["input"]
64 }
65
66 fn get_allowed_tools(&self) -> Option<Vec<&str>> {
68 None
69 }
70
71 fn return_stopped_response(&self, _intermediate_steps: &[AgentStep]) -> AgentFinish {
73 AgentFinish::new(
74 "Agent stopped due to iteration limit or time limit.".to_string(),
75 String::new(),
76 )
77 }
78}
79
80pub struct AgentExecutor {
84 agent: Arc<dyn BaseAgent>,
86
87 tools: Vec<Arc<dyn BaseTool>>,
89
90 max_iterations: usize,
92
93 verbose: bool,
95
96 memory: Option<Arc<tokio::sync::Mutex<dyn BaseMemory>>>,
98
99 callbacks: Option<Arc<CallbackManager>>,
101}
102
103impl AgentExecutor {
104 pub fn new(agent: Arc<dyn BaseAgent>, tools: Vec<Arc<dyn BaseTool>>) -> Self {
106 Self {
107 agent,
108 tools,
109 max_iterations: 10,
110 verbose: false,
111 memory: None,
112 callbacks: None,
113 }
114 }
115
116 pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
118 self.max_iterations = max_iterations;
119 self
120 }
121
122 pub fn with_verbose(mut self, verbose: bool) -> Self {
124 self.verbose = verbose;
125 self
126 }
127
128 pub fn with_memory(mut self, memory: Arc<tokio::sync::Mutex<dyn BaseMemory>>) -> Self {
130 self.memory = Some(memory);
131 self
132 }
133
134 pub fn with_callbacks(mut self, callbacks: Arc<CallbackManager>) -> Self {
136 self.callbacks = Some(callbacks);
137 self
138 }
139
140 pub async fn invoke(&self, input: String) -> Result<String, AgentError> {
142 let mut root_run = RunTree::new(
143 "AgentExecutor",
144 RunType::Chain,
145 json!({"input": input.clone()}),
146 );
147
148 if let Some(ref callbacks) = self.callbacks {
149 for handler in callbacks.handlers() {
150 handler.on_chain_start(&root_run, &root_run.inputs).await;
151 }
152 }
153
154 let mut inputs = HashMap::new();
155 inputs.insert("input".to_string(), input.clone());
156
157 if let Some(memory) = &self.memory {
158 let memory_vars = memory
159 .lock()
160 .await
161 .load_memory_variables(&inputs)
162 .await
163 .map_err(|e| AgentError::Other(format!("Failed to load memory: {}", e)))?;
164
165 if let Some(history) = memory_vars.get("history") {
166 if let Some(history_str) = history.as_str() {
167 inputs.insert("history".to_string(), history_str.to_string());
168 }
169 }
170 }
171
172 let intermediate_steps: Vec<AgentStep> = Vec::new();
173
174 let result = self
175 .run_agent_loop(inputs.clone(), intermediate_steps, &mut root_run)
176 .await;
177
178 if let Some(memory) = &self.memory {
179 if let Ok(ref output) = result {
180 let mut outputs = HashMap::new();
181 outputs.insert("output".to_string(), output.clone());
182
183 memory
184 .lock()
185 .await
186 .save_context(&inputs, &outputs)
187 .await
188 .map_err(|e| AgentError::Other(format!("Failed to save memory: {}", e)))?;
189 }
190 }
191
192 match &result {
193 Ok(output) => {
194 root_run.end(json!({"output": output}));
195 if let Some(ref callbacks) = self.callbacks {
196 if let Some(ref outputs) = root_run.outputs {
197 for handler in callbacks.handlers() {
198 handler.on_chain_end(&root_run, outputs).await;
199 }
200 }
201 }
202 }
203 Err(e) => {
204 root_run.end_with_error(e.to_string());
205 if let Some(ref callbacks) = self.callbacks {
206 for handler in callbacks.handlers() {
207 handler.on_chain_error(&root_run, &e.to_string()).await;
208 }
209 }
210 }
211 }
212
213 result
214 }
215
216 pub fn stream(&self, input: String) -> Pin<Box<dyn Stream<Item = Result<AgentStreamEvent, AgentError>> + Send>> {
235 let (tx, rx) = tokio::sync::mpsc::channel(32);
236
237 let agent = self.agent.clone();
238 let tools = self.tools.clone();
239 let max_iterations = self.max_iterations;
240 let verbose = self.verbose;
241
242 tokio::spawn(async move {
243 let mut intermediate_steps: Vec<AgentStep> = Vec::new();
244 let mut inputs = HashMap::new();
245 inputs.insert("input".to_string(), input);
246
247 for iteration in 0..max_iterations {
248 if verbose {
249 log::info!("=== Stream Iteration {} ===", iteration + 1);
250 }
251
252 let output = match agent.plan(&intermediate_steps, &inputs).await {
253 Ok(o) => o,
254 Err(e) => {
255 let _ = tx.send(Ok(AgentStreamEvent::Error { message: e.to_string() })).await;
256 return;
257 }
258 };
259
260 match output {
261 AgentOutput::Finish(finish) => {
262 let content = finish.output().unwrap_or("").to_string();
263 let _ = tx.send(Ok(AgentStreamEvent::FinalAnswer { content })).await;
264 return;
265 }
266
267 AgentOutput::Action(action) => {
268 let tool_name = action.tool.clone();
269 let tool_input_str = match &action.tool_input {
270 super::types::ToolInput::String { value: s } => s.clone(),
271 super::types::ToolInput::Object { value: v } => {
272 serde_json::to_string(v).unwrap_or_default()
273 }
274 };
275
276 let _ = tx.send(Ok(AgentStreamEvent::ToolStart {
277 name: tool_name.clone(),
278 input: tool_input_str.clone(),
279 })).await;
280
281 let observation = match execute_tool_for_stream(&tools, &action).await {
283 Ok(obs) => obs,
284 Err(e) => {
285 let _ = tx.send(Ok(AgentStreamEvent::Error { message: e.to_string() })).await;
286 return;
287 }
288 };
289
290 let _ = tx.send(Ok(AgentStreamEvent::ToolEnd {
291 name: tool_name,
292 output: observation.clone(),
293 })).await;
294
295 intermediate_steps.push(AgentStep::new(action, observation));
296 }
297
298 AgentOutput::Actions(actions) => {
299 for action in &actions {
300 let tool_name = action.tool.clone();
301 let tool_input_str = match &action.tool_input {
302 super::types::ToolInput::String { value: s } => s.clone(),
303 super::types::ToolInput::Object { value: v } => {
304 serde_json::to_string(v).unwrap_or_default()
305 }
306 };
307
308 let _ = tx.send(Ok(AgentStreamEvent::ToolStart {
309 name: tool_name.clone(),
310 input: tool_input_str,
311 })).await;
312 }
313
314 let observations = execute_tools_parallel_for_stream(&tools, &actions).await;
315
316 for (action, observation) in actions.into_iter().zip(observations.into_iter()) {
317 let _ = tx.send(Ok(AgentStreamEvent::ToolEnd {
318 name: action.tool.clone(),
319 output: observation.clone(),
320 })).await;
321
322 intermediate_steps.push(AgentStep::new(action, observation));
323 }
324 }
325 }
326 }
327
328 let finish = agent.return_stopped_response(&intermediate_steps);
330 let content = finish.output().unwrap_or("").to_string();
331 let _ = tx.send(Ok(AgentStreamEvent::FinalAnswer { content })).await;
332 });
333
334 Box::pin(tokio_stream::wrappers::ReceiverStream::new(rx))
335 }
336
337 async fn run_agent_loop(
339 &self,
340 inputs: HashMap<String, String>,
341 mut intermediate_steps: Vec<AgentStep>,
342 root_run: &mut RunTree,
343 ) -> Result<String, AgentError> {
344 for iteration in 0..self.max_iterations {
345 if self.verbose {
346 log::info!("=== Iteration {} ===", iteration + 1);
347 }
348
349 let output = self.agent.plan(&intermediate_steps, &inputs).await?;
350
351 match output {
352 AgentOutput::Finish(finish) => {
353 if self.verbose {
354 log::info!("Final answer: {:?}", finish.return_values);
355 }
356 return Ok(finish.output().unwrap_or("").to_string());
357 }
358
359 AgentOutput::Action(action) => {
360 if self.verbose {
361 log::info!("Action: {}({})", action.tool, action.tool_input);
362 }
363
364 let observation = self.execute_tool(&action, root_run).await?;
365
366 if self.verbose {
367 log::info!("Observation: {}", observation);
368 }
369
370 intermediate_steps.push(AgentStep::new(action, observation));
371 }
372
373 AgentOutput::Actions(actions) => {
374 if self.verbose {
375 log::info!("Parallel actions: {} count", actions.len());
376 for action in &actions {
377 log::info!(" - {}({})", action.tool, action.tool_input);
378 }
379 }
380
381 let observations = self.execute_tools_parallel(&actions, root_run).await?;
382
383 if self.verbose {
384 for (i, obs) in observations.iter().enumerate() {
385 log::info!("Observation {}: {}", i + 1, obs);
386 }
387 }
388
389 for (action, observation) in actions.into_iter().zip(observations.into_iter()) {
390 intermediate_steps.push(AgentStep::new(action, observation));
391 }
392 }
393 }
394 }
395
396 if self.verbose {
397 log::info!("Max iterations reached: {}", self.max_iterations);
398 }
399
400 let finish = self.agent.return_stopped_response(&intermediate_steps);
401 Ok(finish.output().unwrap_or("").to_string())
402 }
403
404 async fn execute_tools_parallel(
409 &self,
410 actions: &[super::types::AgentAction],
411 root_run: &RunTree,
412 ) -> Result<Vec<String>, AgentError> {
413 use futures_util::future::join_all;
414
415 let futures: Vec<_> = actions
416 .iter()
417 .map(|action| self.execute_tool(action, root_run))
418 .collect();
419
420 let results = join_all(futures).await;
421 let mut observations = Vec::with_capacity(results.len());
422 for result in results {
423 match result {
424 Ok(output) => observations.push(output),
425 Err(e) => observations.push(format!("[Tool execution error: {}]", e)),
426 }
427 }
428 Ok(observations)
429 }
430
431 async fn execute_tool(
433 &self,
434 action: &AgentAction,
435 root_run: &RunTree,
436 ) -> Result<String, AgentError> {
437 let tool = self
438 .tools
439 .iter()
440 .find(|t| t.name() == action.tool)
441 .ok_or_else(|| AgentError::ToolNotFound(action.tool.clone()))?;
442
443 let input_str = match &action.tool_input {
444 super::types::ToolInput::String { value: s } => s.clone(),
445 super::types::ToolInput::Object { value: v } => serde_json::to_string(v)
446 .map_err(|e| AgentError::Other(format!("Failed to serialize tool input: {}", e)))?,
447 };
448
449 let mut tool_run = root_run.create_child(
450 &action.tool,
451 RunType::Tool,
452 json!({"input": input_str.clone()}),
453 );
454
455 if let Some(ref callbacks) = self.callbacks {
456 for handler in callbacks.handlers() {
457 handler
458 .on_tool_start(&tool_run, &action.tool, &input_str)
459 .await;
460 }
461 }
462
463 let result = tool.run(input_str.clone()).await;
464
465 match result {
466 Ok(output) => {
467 tool_run.end(json!({"output": output.clone()}));
468 if let Some(ref callbacks) = self.callbacks {
469 for handler in callbacks.handlers() {
470 handler.on_tool_end(&tool_run, &output).await;
471 }
472 }
473 Ok(output)
474 }
475 Err(e) => {
476 tool_run.end_with_error(e.to_string());
477 if let Some(ref callbacks) = self.callbacks {
478 for handler in callbacks.handlers() {
479 handler.on_tool_error(&tool_run, &e.to_string()).await;
480 }
481 }
482 Err(AgentError::ToolExecutionError(e.to_string()))
483 }
484 }
485 }
486}
487
488impl std::fmt::Debug for AgentExecutor {
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490 f.debug_struct("AgentExecutor")
491 .field("max_iterations", &self.max_iterations)
492 .field("verbose", &self.verbose)
493 .field("tools_count", &self.tools.len())
494 .field("has_memory", &self.memory.is_some())
495 .finish()
496 }
497}
498
499async fn execute_tool_for_stream(
501 tools: &[Arc<dyn BaseTool>],
502 action: &AgentAction,
503) -> Result<String, AgentError> {
504 let tool = tools
505 .iter()
506 .find(|t| t.name() == action.tool)
507 .ok_or_else(|| AgentError::ToolNotFound(action.tool.clone()))?;
508
509 let input_str = match &action.tool_input {
510 super::types::ToolInput::String { value: s } => s.clone(),
511 super::types::ToolInput::Object { value: v } => serde_json::to_string(v)
512 .map_err(|e| AgentError::Other(format!("Failed to serialize tool input: {}", e)))?,
513 };
514
515 tool.run(input_str)
516 .await
517 .map_err(|e| AgentError::ToolExecutionError(e.to_string()))
518}
519
520async fn execute_tools_parallel_for_stream(
522 tools: &[Arc<dyn BaseTool>],
523 actions: &[AgentAction],
524) -> Vec<String> {
525 use futures_util::future::join_all;
526
527 let futures: Vec<_> = actions
528 .iter()
529 .map(|action| execute_tool_for_stream(tools, action))
530 .collect();
531
532 let results = join_all(futures).await;
533 results
534 .into_iter()
535 .map(|result| result.unwrap_or_else(|e| format!("[Tool execution error: {}]", e)))
536 .collect()
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542 use lc_memory::ConversationBufferMemory;
543
544 #[tokio::test]
546 async fn test_agent_executor_with_memory() {
547 struct TestAgent;
549
550 #[async_trait]
551 impl BaseAgent for TestAgent {
552 async fn plan(
553 &self,
554 _intermediate_steps: &[AgentStep],
555 inputs: &HashMap<String, String>,
556 ) -> Result<AgentOutput, AgentError> {
557 if let Some(history) = inputs.get("history") {
559 if history.contains("Zhang San") {
560 return Ok(AgentOutput::Finish(AgentFinish::new(
561 "Your name is Zhang San".to_string(),
562 String::new(),
563 )));
564 }
565 }
566
567 let input = inputs.get("input").unwrap();
569 Ok(AgentOutput::Finish(AgentFinish::new(
570 format!("Received: {}", input),
571 String::new(),
572 )))
573 }
574 }
575
576 let memory = Arc::new(tokio::sync::Mutex::new(ConversationBufferMemory::new()));
578
579 let executor = AgentExecutor::new(Arc::new(TestAgent), vec![]).with_memory(memory);
581
582 let result1 = executor
584 .invoke("My name is Zhang San".to_string())
585 .await
586 .unwrap();
587 println!("Round 1: {}", result1);
588
589 let result2 = executor
591 .invoke("What is my name?".to_string())
592 .await
593 .unwrap();
594 println!("Round 2: {}", result2);
595
596 assert!(result2.contains("Zhang San"));
597 }
598}