rune_chain_agent/lib.rs
1//! ReAct agent loop for `rune-chain`: LLM reasoning + tool invocation + scratchpad.
2//!
3//! `rune-chain-agent` implements the ReAct (Reasoning + Acting) pattern from the paper
4//! [ReAct: Synergizing Reasoning and Acting in Language Models](https://arxiv.org/abs/2210.03629).
5//! The agent sends the user's question to an LLM, parses the response for a tool call,
6//! executes the tool, feeds the result back as an observation, and repeats until the LLM
7//! produces a `Final Answer:` or the iteration limit is reached.
8//!
9//! # Features
10//!
11//! - [`AgentExecutor`] — configurable ReAct loop with any [`rune_chain_core::Llm`] backend
12//! - [`AgentResult`] — structured output: final answer, iteration count, full scratchpad
13//! - Implements [`rune_chain_core::Chain`] so agents compose with sequential and conversational chains
14//! - Tool dispatch via [`rune_chain_core::Tool`] — synchronous, zero-allocation tool runs
15//! - Automatic system prompt generation from registered tools
16//! - Configurable `max_iterations` guard prevents runaway loops
17//!
18//! # Quick Start
19//!
20//! ```rust,no_run
21//! use std::sync::Arc;
22//! use rune_chain_core::{Llm, Tool};
23//! use rune_chain_agent::AgentExecutor;
24//!
25//! struct Upper;
26//! impl Tool for Upper {
27//! fn name(&self) -> &str { "upper" }
28//! fn description(&self) -> &str { "Converts text to upper case." }
29//! fn run(&self, input: &str) -> String { input.to_uppercase() }
30//! }
31//!
32//! # async fn run() -> Result<(), rune_chain_core::ChainError> {
33//! # let llm: Arc<dyn Llm> = unimplemented!();
34//! let agent = AgentExecutor::new(llm)
35//! .tool(Upper)
36//! .max_iterations(5);
37//!
38//! let result = agent.run("Convert 'hello' to upper case.").await?;
39//! println!("Answer: {}", result.output);
40//! println!("Iterations: {}", result.iterations);
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! # Using as a `Chain`
46//!
47//! [`AgentExecutor`] implements [`rune_chain_core::Chain`], so it can slot into any
48//! chain composition that expects a `Chain`:
49//!
50//! ```rust,no_run
51//! use std::sync::Arc;
52//! use rune_chain_core::{Chain, Llm, prompt_args};
53//! use rune_chain_agent::AgentExecutor;
54//!
55//! # async fn run() -> Result<(), rune_chain_core::ChainError> {
56//! # let llm: Arc<dyn Llm> = unimplemented!();
57//! let agent = AgentExecutor::new(llm);
58//! let output = agent.invoke(prompt_args! { "input" => "What is 2 + 2?" }).await?;
59//! println!("{output}");
60//! # Ok(())
61//! # }
62//! ```
63//!
64//! # ReAct Format
65//!
66//! The agent instructs the LLM to follow this scratchpad format:
67//!
68//! ```text
69//! Thought: <reason about what to do>
70//! Action: <tool_name>
71//! Action Input: <input to the tool>
72//! Observation: <result of the action>
73//! ... (repeat as needed)
74//! Thought: I now have enough information.
75//! Final Answer: <your final answer>
76//! ```
77
78pub mod agent;
79
80pub use agent::{AgentExecutor, AgentResult};