Expand description
§AgentAI
AgentAI is a Rust library designed to simplify the creation of AI agents. It leverages the GenAI library to interface with a wide range of popular Large Language Models (LLMs), making it versatile and powerful. Written in Rust, AgentAI benefits from strong static typing and robust error handling, ensuring reliable and maintainable code. Whether you’re developing simple or complex AI agents, AgentAI provides a streamlined and efficient development process.
§Warning
This library is under heavy development. The interface may change at any time without notice.
§Features
- Connect to any major LLM provider: Support for OpenAI, Anthropic, Gemini, Ollama, and other OpenAI-compatible APIs.
- Choose the right model for the job: Flexibly select the best-suited model for each step in your agent’s workflow.
- Build custom tools with ease: A simple interface for creating and managing your own tools using the
ToolBox. - MCP Server Support, local and remote: Use tools from any Model Context Protocol server, whether you start it locally over stdio or connect to a running or hosted server over Streamable HTTP (with optional bearer token). Built on the official MCP Rust SDK.
- Structured Output: No need to parse raw text from model, just provide structure, and AI agent will provide response in defined format.
- Streaming: Show the answer as the model writes it, together with the tool calls the agent makes. The stream is
Send + 'static, ready for spawned tasks and server-sent events. - Images for vision models: Send text and images together in a
Prompt, by URL or from bytes. Plain text prompts work as before. - You own the conversation: An agent is shareable configuration. You keep the history, decide what to store, trim or persist, and get token usage and the stop reason from every run.
§What’s New
§0.3.0 - Streaming, conversation history in your hands, run details
- Streaming:
Agent::run_streamtakes the same configuration asAgent::run, and awaiting it returns a stream ofAgentEvents: text and reasoning as the model writes them, each tool call and its result, and finally the result of the run. Runs with tool calls and structured output stream too. The stream isSend + 'static, so you can spawn it or return it from a web handler. - You manage the history: the agent no longer keeps the conversation. Pass your history with
.with_history(&history)and add the run’snew_messagesto it when you want to continue:history.extend(result.new_messages). Store, trim, persist or reset it however you like:Messagehas its own stable JSON format. A failed or cancelled run never changes your history, and the system prompt is not part of it: the agent sends it with every request. - One agent, many conversations: the model and toolbox now belong to the agent
(
Agent::with_model,Agent::with_toolbox), and running takes&self, so one agent can be cloned cheaply and shared between tasks. Change the model, toolbox, iteration limit or options for a single run with thewith_*methods ofAgentRequestor a reusableRunOptions. - Run details: awaiting a run returns a
AgentResultwith the answer, token usage summed over all model calls, the stop reason, the model’s reasoning and the number of model calls. An answer cut off by the token limit is reported asAgentError::MaxTokensinstead of a confusing parse error.
Breaking changes: agent.run(model, prompt, toolbox) is replaced by
agent.run(prompt).with_history(&history).await, which returns an AgentResult instead of the bare answer;
the model and toolbox are set with Agent::with_model and Agent::with_toolbox (or per run), and the agent
no longer remembers earlier runs. Agent::new_with_provider takes a Provider, and Agent::new_with_client
is removed. Follow the migration guide to upgrade from 0.2.0: it shows each change with before and
after code.
§0.2.0 - Agent providers, remote MCP and configuration
- Creating agents:
Agent::new_with_providerbinds an agent to any genai provider with an explicit API key, for example the native OpenRouter adapter, which needs no base URL:Agent::new_with_provider(AdapterKind::OpenRouter, None, &api_key, system)?.Agent::newandAgent::new_with_urlnow returnResult, and the agent reports typed errors (AgentError). Tool errors are either recoverable (sent back to the model, which can try again) or fatal (they stop the run). - Remote MCP servers, not only stdio:
McpToolBoxnow connects to MCP servers over Streamable HTTP, so agents can use running and hosted servers, including ones that need a bearer token (McpToolBox::new_http,McpToolBox::new_http_with_bearer). Starting a local server over stdio (McpToolBox::new) works as before. MCP support is now built onrmcp, the official MCP Rust SDK. - Agent configuration:
Agent::with_max_iterationsandAgent::with_chat_optionsset the loop limit and the model options (temperature, max tokens, tool choice, reasoning effort, …). - Strict tools:
#[tool(strict)]marks a tool as strict, so providers that support it make the model’s arguments match the parameter schema exactly. - Dependencies: genai 0.7 (re-exported as
agentai::genai; tool calls are no longer lost when a model also returns text), schemars 1, reqwest 0.13, syn 3, Rust edition 2024 and MSRV 1.88.
Breaking changes: Agent::new, Agent::new_with_url and Agent::run return AgentError instead of
anyhow errors, ToolError variants are renamed and split, Tool is built with Tool::new, and answer
and tool parameter types need schemars 1.x. Read the
CHANGELOG before upgrading: it lists
every breaking change and how to migrate.
§0.1.5 - ToolBox
This release introduces the ToolBox, a new feature providing an easy-to-use interface for supplying tools to AI agents.
§Future Plans
We are continuously working to improve AgentAI. These are the next features we plan; the linked issues have the details:
- Agent Memory: control what the agent remembers between runs, such as persistence, record limits and context management.
- User input during a run: let an agent ask the user for more information or confirmation before it continues.
§Installation
To add the AgentAI crate to your project, run the following command in your project’s root directory:
cargo add agentaiThis command adds the crate and its dependencies to your project.
§Feature Flags
Available features for agentai crate.
To enable any of these features, you need to enter this command:
cargo add agentai -F mcp-clientFeatures list:
mcp-client(enabled by default) — Enables support for Agent Tools served by MCP servers (stdio and Streamable HTTP, viarmcp)tokio(enabled by default) — Enables tokio integrations, such as writing a stream to a tokio writer withAgentStream::print_to_tokiomacros(enabled by default) — Enables support for macro#[toolbox]tools-buildin(enabled by default) — Enables support for buildin toolstools-web(enabled by default) — Enables support for web tools
§Usage
Here is a basic example of how to create an AI agent using AgentAI:
use agentai::{Agent, Message, AgentResult};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let agent = Agent::new("gpt-4o", "You are a useful assistant")?;
// The conversation history is yours
let mut history: Vec<Message> = Vec::new();
let result: AgentResult<String> = agent.run("Why is the sky blue?").with_history(&history).await?;
println!("Answer: {}", result.answer);
// Keep the question and the answer to continue the conversation
history.extend(result.new_messages);
Ok(())
}Models that support images take a Prompt with text and images, by URL or from bytes:
use agentai::{Agent, AgentResult, Prompt};
let prompt = Prompt::text("Which of these logos is brighter?")
.with_image_url("https://example.com/logo.png")
.with_image_bytes("image/png", &png);
let result: AgentResult<String> = agent.run(prompt).await?;To show the answer while it is written, use run_stream with the same configuration and
write the stream to the console, a file or any other std::io::Write:
use agentai::{Agent, AgentResult};
let result: AgentResult<String> = agent
.run_stream("Why is the sky blue?")
.await?
.print_to(std::io::stdout())
.await?;For your own handling, read the AgentEvents of the stream:
use agentai::{Agent, AgentEvent};
use futures_util::StreamExt;
let mut stream = agent.run_stream::<String>("Why is the sky blue?").await?;
while let Some(event) = stream.next().await {
match event? {
AgentEvent::TextDelta(text) => print!("{text}"),
AgentEvent::Finished(result) => println!("\n{} tokens", result.usage.total_tokens),
_ => {}
}
}§Error handling
A run returns an AgentError that you can match on. Provider failures that may
succeed on a second try report it with AgentError::is_transient. Your history is not
changed by a failed run, so you can simply try again:
use agentai::{Agent, AgentError};
match agent.run::<String>("Why is the sky blue?").await {
Ok(result) => println!("Answer: {}", result.answer),
Err(err) if err.is_transient() => println!("Try again later: {err}"),
Err(AgentError::InvalidAnswer { raw, .. }) => println!("Unexpected answer: {raw}"),
Err(err) => println!("Failed: {err}"),
}Tools report failures with ToolError. Recoverable errors
(NotFound, InvalidArguments, Failed) are sent to the model as the tool result and the
run continues. Fatal errors (Configuration, Fatal) stop the run with
AgentError::Tool. ToolError::is_recoverable
tells the two groups apart.
§Examples
For more examples, check out the examples directory. To run an example, use the following command, replacing <example_name> with the name of the example file (without the .rs extension):
cargo run --example <example_name>For instance, to run the simple example:
cargo run --example simpleRe-exports§
pub use message::Media;pub use message::MediaSource;pub use message::Message;pub use message::Part;pub use message::ProviderData;pub use message::Role;pub use message::ToolCall;pub use message::ToolResponse;pub use prompt::IntoPrompt;pub use prompt::Prompt;pub use genai;pub use agent::*;