Introduction
AutoAgents is a modern multi‑agent framework in Rust for building intelligent, autonomous agents powered by Large Language Models (LLMs/SLMs) and Ractor.
Designed for performance, safety, and scalability, AutoAgents provides a robust foundation for AI systems that can reason, act, remember, and collaborate. You can build cloud‑native agents, edge‑native agents, and hybrid deployments — including WASM for the browser.
What Is AutoAgents?
AutoAgents helps you create agents that can:
- Reason: Use execution strategies like ReAct and Basic for problem solving
- Act: Call tools and interact with external systems safely
- Remember: Maintain context with configurable memory providers
- Collaborate: Coordinate through an actor runtime and pub/sub topics
High‑Level Architecture
graph TD
Executor["Executor Layer"]
Memory["Memory Layer"]
Agent["Agent Definition"]
DirectAgent["Direct Agent"]
ActorAgent["Actor Based Agent"]
Tools["Tools"]
MCP["MCP"]
Runtime["Runtime Engine"]
Providers["LLM Providers"]
CloudLLM["Cloud LLM Providers"]
LocalLLM["Local LLM Providers"]
Accelerators["Accelerators"]
Executor --> Agent
Memory --> Agent
Tools --> Agent
MCP --> Agent
Agent --> ActorAgent
Agent --> DirectAgent
ActorAgent --> Runtime
DirectAgent --> Runtime
Runtime --> Providers
Providers --> LocalLLM
Providers --> CloudLLM
LocalLLM --> Accelerators
Community and Support
AutoAgents is developed by the Liquidos AI team and maintained by a growing community.
- 📖 Documentation: Guides and reference
- 💬 Discord: discord.gg/Ghau8xYn
- 🐛 Issues: GitHub
- 🤝 Contributing: PRs welcome
Quick Start
Installation
Before using AutoAgents, ensure you have:
- Rust 1.91.1 or later - Install using rustup
- Cargo package manager (comes with Rust)
Verify your installation:
rustc --version
cargo --version
Create a Rust Project
cargo new MyAgent
cd MyAgent
Add Dependencies
Add the core crate and macros. Enable a provider feature (e.g., openai) to use that backend.
# Cargo.toml
[dependencies]
autoagents = { version = "0.3.0", features = ["openai"] }
autoagents-derive = "0.3.0"
Optional tools (filesystem, search) live in autoagents-toolkit:
autoagents-toolkit = { version = "0.3.0", features = ["filesystem", "search"] }
Provider features available on autoagents: openai, anthropic, openrouter, groq, google, azure_openai, xai, deepseek, ollama. Use only what you need.
Minimal Agent
A single‑turn agent using the Basic executor:
use autoagents::core::agent::prebuilt::executor::BasicAgent;
use autoagents::core::agent::{AgentBuilder, DirectAgent};
use autoagents::core::agent::task::Task;
use autoagents::llm::builder::LLMBuilder;
use autoagents::llm::backends::openai::OpenAI;
use autoagents_derive::{agent, AgentHooks};
use std::sync::Arc;
#[agent(name = "hello", description = "Helpful assistant")]
#[derive(Clone, AgentHooks, Default)]
struct HelloAgent;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Set OPENAI_API_KEY in your env
let llm: Arc<OpenAI> = LLMBuilder::<OpenAI>::new()
.api_key(std::env::var("OPENAI_API_KEY")?)
.model("gpt-4o")
.build()?;
let agent = BasicAgent::new(HelloAgent);
let handle = AgentBuilder::<_, DirectAgent>::new(agent)
.llm(llm)
.build()
.await?;
let out = handle.agent.run(Task::new("Say hi in one short sentence"))
.await?;
println!("{}", String::from(out));
Ok(())
}
Run
cargo run
Tip: for a ReAct multi‑turn agent with tools and streaming, see the examples in examples/basic and examples/coding_agent.
Your First Agent
This guide creates a minimal ReAct agent that can call a tool and return a structured answer.
1) Dependencies
[dependencies]
autoagents = { version = "0.3.0", features = ["openai"] }
autoagents-derive = "0.3.0"
# Optional if you want ready-made tools
autoagents-toolkit = { version = "0.3.0", features = ["filesystem", "search"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
2) Define a Tool
#![allow(unused)]
fn main() {
use autoagents::async_trait;
use autoagents::core::tool::{ToolCallError, ToolInputT, ToolRuntime, ToolT};
use autoagents_derive::{tool, ToolInput};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Serialize, Deserialize, ToolInput, Debug)]
struct AddArgs { left: i64, right: i64 }
#[tool(name = "addition", description = "Add two numbers", input = AddArgs)]
struct Addition;
#[async_trait]
impl ToolRuntime for Addition {
async fn execute(&self, args: Value) -> Result<Value, ToolCallError> {
let a: AddArgs = serde_json::from_value(args)?;
Ok((a.left + a.right).into())
}
}
}
3) Define Output (optional)
If you want type‑safe structured output, derive AgentOutput and convert from executor output.
#![allow(unused)]
fn main() {
use autoagents_derive::AgentOutput;
#[derive(Debug, Serialize, Deserialize, AgentOutput)]
struct MathOut {
#[output(description = "The result value")] value: i64,
#[output(description = "Short explanation")] explanation: String,
}
}
4) Define the Agent
#![allow(unused)]
fn main() {
use autoagents_derive::{agent, AgentHooks};
use autoagents::core::agent::prebuilt::executor::{ReActAgent, ReActAgentOutput};
#[agent(
name = "math_agent",
description = "Solve basic math using tools and return JSON",
tools = [Addition],
output = MathOut
)]
#[derive(Clone, AgentHooks, Default)]
struct MathAgent;
impl From<ReActAgentOutput> for MathOut {
fn from(out: ReActAgentOutput) -> Self {
serde_json::from_str(&out.response).unwrap_or(MathOut { value: 0, explanation: out.response })
}
}
}
5) Build LLM and Run
use autoagents::core::agent::{AgentBuilder, DirectAgent};
use autoagents::core::agent::memory::SlidingWindowMemory;
use autoagents::core::agent::task::Task;
use autoagents::llm::builder::LLMBuilder;
use autoagents::llm::backends::openai::OpenAI;
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let llm: Arc<OpenAI> = LLMBuilder::<OpenAI>::new()
.api_key(std::env::var("OPENAI_API_KEY")?)
.model("gpt-4o")
.build()?;
let agent = ReActAgent::new(MathAgent);
let handle = AgentBuilder::<_, DirectAgent>::new(agent)
.llm(llm)
.memory(Box::new(SlidingWindowMemory::new(10)))
.build()
.await?;
let out = handle.agent.run(Task::new("Add 20 and 5 and explain"))
.await?;
println!("{:?}", out);
Ok(())
}
Environment Variables
Set provider keys as needed:
export OPENAI_API_KEY=...
export ANTHROPIC_API_KEY=...
export OPENROUTER_API_KEY=...
export GROQ_API_KEY=...
export AZURE_OPENAI_API_KEY=...
For Brave Search (toolkit): BRAVE_SEARCH_API_KEY or BRAVE_API_KEY.
That’s it — you’ve built a ReAct agent with a tool and structured output.
Architecture Overview
AutoAgents is built with a modular, extensible architecture that prioritizes performance, safety, and developer experience. This document provides a comprehensive overview of the framework’s design and core components.
High-Level Architecture
Key layers:
- Agent Definition: your agent’s metadata, tools, and output
- Executors: Basic (single‑turn) and ReAct (multi‑turn with tools, streaming)
- Memory: context storage (e.g., sliding window)
- Tools/MCP: capabilities the agent can call
- Runtime: optional actor system for multi‑agent workflows
- Providers: pluggable LLM backends (cloud/local)
graph TD
Executor["Executor Layer"]
Memory["Memory Layer"]
Agent["Agent Definition"]
DirectAgent["Direct Agent"]
ActorAgent["Actor Based Agent"]
Tools["Tools"]
MCP["MCP"]
Runtime["Runtime Engine"]
Providers["LLM Providers"]
CloudLLM["Cloud LLM Providers"]
LocalLLM["Local LLM Providers"]
Accelerators["Accelerators"]
Executor --> Agent
Memory --> Agent
Tools --> Agent
MCP --> Agent
Agent --> ActorAgent
Agent --> DirectAgent
ActorAgent --> Runtime
DirectAgent --> Runtime
Runtime --> Providers
Providers --> LocalLLM
Providers --> CloudLLM
LocalLLM --> Accelerators
Agents
Agents are the core building blocks of AutoAgents. They understand tasks, apply an execution strategy (e.g., ReAct or Basic), optionally call tools, and produce outputs (string or structured JSON).
What Is an Agent?
An agent in AutoAgents typically:
- Defines metadata (name, description) and available tools
- Chooses an executor (Basic or ReAct)
- Uses an LLM provider
- Optionally has memory for context
- Emits events (task started/completed, tool calls, streaming chunks)
Agent Lifecycle
- Build:
AgentBuilderwraps your agent into a runnableBaseAgentwith an LLM, optional memory, and event channel - Run: call
run(Task)(orrun_stream) to execute - Hooks: optional
AgentHooksfire on create, run start/complete, per‑turn, and tool activity - Output: executor output is converted into your agent output type (
From<...>)
Direct Agents
Direct agents expose simple run/run_stream APIs and return results to the caller.
#![allow(unused)]
fn main() {
use autoagents::core::agent::{AgentBuilder, DirectAgent};
let handle = AgentBuilder::<_, DirectAgent>::new(my_executor)
.llm(llm)
.build()
.await?;
let result = handle.agent.run(Task::new("Prompt")) .await?;
}
Actor Based Agents
Actor agents integrate with a runtime for pub/sub and cross‑agent messaging. Use AgentBuilder::<_, ActorAgent> with .runtime(...) and optional .subscribe(topic).
High‑level flow:
- Create a
SingleThreadedRuntime - Build the agent with
.runtime(runtime.clone()) - Register runtime in an
Environmentand run it - Publish
Tasks to topics or send direct messages
Use actor agents for multi‑agent collaboration, routing, or background workflows.
Hooks
Agents can implement the AgentHooks trait to observe and customize execution at well‑defined points. This is useful for logging, metrics, guardrails, or side‑effects.
Lifecycle hooks:
on_agent_create(&self)— called when the agent is constructedon_run_start(&self, task, ctx) -> HookOutcome— called before execution; returnAbortto cancelon_run_complete(&self, task, result, ctx)— called after execution succeedson_turn_start(&self, turn_index, ctx)— called at the start of each executor turn (e.g., ReAct)on_turn_complete(&self, turn_index, ctx)— called when a turn completeson_tool_call(&self, tool_call, ctx) -> HookOutcome— gate a tool call before it executeson_tool_start(&self, tool_call, ctx)— tool execution startedon_tool_result(&self, tool_call, result, ctx)— tool execution completed successfullyon_tool_error(&self, tool_call, err, ctx)— tool execution failedon_agent_shutdown(&self)— actor shutdown hook (actor agents only)
Example:
#![allow(unused)]
fn main() {
use autoagents::prelude::*;
#[derive(Clone, Default, AgentHooks)]
struct MyAgent;
#[autoagents::agent(name = "my_agent", description = "Example agent")]
impl MyAgent {}
#[autoagents::async_trait]
impl AgentHooks for MyAgent {
async fn on_run_start(&self, task: &Task, _ctx: &Context) -> HookOutcome {
if task.prompt.len() > 2_000 { HookOutcome::Abort } else { HookOutcome::Continue }
}
async fn on_tool_error(&self, _call: &autoagents::llm::ToolCall, err: serde_json::Value, _ctx: &Context) {
eprintln!("tool failed: {}", err);
}
}
}
Tips:
- Hooks should be fast and side‑effect aware, particularly in streaming contexts.
on_agent_shutdownonly fires for actor agents (not for direct agents).- Use
Contextto access config, memory, and event sender.
Tools
Tools let agents act on the world. Each tool provides a name, description, JSON schema for its input, and an async execute implementation.
Custom Tools
Define tool inputs and the tool with derive macros, and implement ToolRuntime:
#![allow(unused)]
fn main() {
use autoagents::async_trait;
use autoagents::core::tool::{ToolCallError, ToolInputT, ToolRuntime, ToolT};
use autoagents_derive::{tool, ToolInput};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Serialize, Deserialize, ToolInput, Debug)]
struct WriteArgs { path: String, contents: String }
#[tool(name = "write_file", description = "Write text to a file", input = WriteArgs)]
struct WriteFile;
#[async_trait]
impl ToolRuntime for WriteFile {
async fn execute(&self, args: Value) -> Result<Value, ToolCallError> {
let a: WriteArgs = serde_json::from_value(args)?;
std::fs::write(a.path, a.contents).map_err(|e| ToolCallError::RuntimeError(e.into()))?;
Ok(serde_json::json!({"ok": true}))
}
}
}
Attach tools in the #[agent(..., tools = [ .. ])] macro. Tools can also be built dynamically; when sharing Arc<dyn ToolT> across agents use shared_tools_to_boxes.
Toolkit
Reusable tools are in autoagents-toolkit:
- Filesystem tools:
ListDir,ReadFile,WriteFile,CopyFile,MoveFile,DeleteFile,SearchFile(feature:filesystem) - Web search:
BraveSearch(feature:search, requiresBRAVE_SEARCH_API_KEYorBRAVE_API_KEY)
Enable features in your Cargo.toml as needed.
MCP
Model Context Protocol (MCP) integrations are available via autoagents-toolkit::mcp — load tool definitions from MCP servers and expose them as ToolT. See McpTools for managing connections and wrapping MCP tools for use by agents.
Memory
Memory lets agents retain context across turns. AutoAgents exposes a MemoryProvider trait and includes a simple sliding‑window implementation.
Sliding Window Memory
SlidingWindowMemory stores the most recent N messages (FIFO). Use it when recent context is sufficient and you want predictable memory usage.
#![allow(unused)]
fn main() {
use autoagents::core::agent::memory::SlidingWindowMemory;
let memory = Box::new(SlidingWindowMemory::new(10));
}
Attach memory via AgentBuilder:
#![allow(unused)]
fn main() {
let handle = AgentBuilder::<_, DirectAgent>::new(agent)
.llm(llm)
.memory(Box::new(SlidingWindowMemory::new(10)))
.build()
.await?;
}
The ReAct executor automatically stores user/assistant messages and tool interactions in memory each turn.
Custom Memory
Implement MemoryProvider to support alternate strategies (e.g., vector‑store, summaries, persistence). Key methods:
remember(&ChatMessage)— store messagerecall(query, limit)— retrieve relevant contextclear()— resetsize()/memory_type()— diagnostics
The trait includes convenience hooks for summarization and export/import if you need persistence.
Executors
AutoAgents ships with two primary executors. You can also implement your own by conforming to AgentExecutor.
- Basic: single‑turn chat without tool calls
- ReAct: multi‑turn reasoning with tool calls and streaming
Basic
Use BasicAgent<T> to run a single prompt/response cycle. Suitable for simple Q&A and when tools are not needed.
Key points:
- No tool calls
- Optional streaming variant
- Output type:
BasicAgentOutput→ convert to your agent output viaFrom<...>
#![allow(unused)]
fn main() {
use autoagents::core::agent::prebuilt::executor::BasicAgent;
let agent = BasicAgent::new(MyAgent);
}
ReAct
Use ReActAgent<T> for iterative reasoning + acting. Supports tool calls, multi‑turn loops (with max_turns), and streaming with intermediate events.
Key points:
- Tool calls are serialized, executed, and their results fed back to the LLM
- Emits events: tool requested/completed, turn started/completed, stream chunks
- Output type:
ReActAgentOutput→ convert to your agent output viaFrom<...>
#![allow(unused)]
fn main() {
use autoagents::core::agent::prebuilt::executor::ReActAgent;
let agent = ReActAgent::new(MyAgent);
}
Tip: ReActAgentOutput::extract_agent_output<T> can deserialize a structured JSON response into your type when you expect strict JSON.
Executors: When To Use What
Executors: When To Use What
AutoAgents ships two execution strategies out of the box. Pick based on your task shape and integration needs.
BasicAgent
- Single-turn request with no intermediate tool calls.
- Great for:
- Prompt → response without orchestration
- Simple chat replies
- Fast endpoints or CLI utilities
- Streaming: Yes (
execute_streamyields delta content) - Output:
BasicAgentOutput { response, done }— can be converted to your structured type withparse_or_map.
Use if you need a quick, single-shot LLM call.
ReActAgent
- Multi-turn loop with tool calling support and memory integration.
- Great for:
- Tool-augmented tasks (retrieval, search, filesystem, MCP)
- Step-by-step reasoning with intermediate state
- Streaming UI with turn events
- Streaming: Yes (delta content and tool-call accumulation)
- Output:
ReActAgentOutput { response, tool_calls, done }— parse viatry_parse/parse_or_maporextract_agent_output.
Use if your agent must call tools or orchestrate multiple turns.
Structured Output Helpers
Both executors provide helpers to reduce parsing boilerplate:
#![allow(unused)]
fn main() {
// Best-effort parse with fallback to raw text mapping
let my_struct = output.parse_or_map(|raw| MyStruct { text: raw.to_string() });
// Strict parse
let parsed: MyStruct = output.try_parse()?;
}
Actor Agents
Actor-based agents run inside a runtime and communicate via typed messages and protocol events. Use them when you need:
- Event streaming for UI updates (turn started/completed, tool calls, streaming chunks)
- Pub/Sub between agents or external actors
- Long-running orchestrations rather than a single direct call
Core Building Blocks
RuntimeandEnvironment: Manage event routing and lifecycle of actor systems.Topic<M>: Typed pub/sub channels for messages of typeM.ActorAgentandActorAgentHandle: Wrap your agent with an actor reference so it can receiveTasks via pub/sub/direct messaging.Event: Streamed protocol events (task start/complete, tool calls, streaming chunks) published by agents during execution.
Typical Wiring Pattern
- Create a runtime and register it in an
Environment. - Spawn your agent as
ActorAgentand subscribe it to one or moreTopic<Task>. - Take the environment’s event receiver and forward events to your UI/log sink.
- Publish
Taskmessages to the relevant topic to trigger work.
Minimal Example
#![allow(unused)]
fn main() {
use autoagents::core::{
agent::prebuilt::executor::ReActAgent,
agent::{AgentBuilder, ActorAgent},
agent::task::Task,
environment::Environment,
runtime::{SingleThreadedRuntime, TypedRuntime},
actor::Topic,
};
use std::sync::Arc;
// 1) Create runtime and environment
let runtime = SingleThreadedRuntime::new(None);
let mut env = Environment::new(None);
env.register_runtime(runtime.clone()).await?;
// 2) Build actor agent and subscribe to a topic
let chat_topic = Topic::<Task>::new("chat");
let handle = AgentBuilder::<_, ActorAgent>::new(ReActAgent::new(MyAgent {}))
.llm(my_llm)
.runtime(runtime.clone())
.subscribe(chat_topic.clone())
.build()
.await?;
// 3) Consume events (UI updates, tool calls, streaming)
let receiver = env.take_event_receiver(None).await?;
tokio::spawn(async move { /* forward events to UI */ });
// 4) Publish tasks
runtime.publish(&chat_topic, Task::new("Hello!"))
.await?;
}
Event Handling Patterns
- Pub/Sub: Publish
TasktoTopic<Task>; all subscribed agents receive the message. - Direct send: Use
TypedRuntime::send_messageto deliver a message directly to a specific actor. - Protocol events:
Event::TaskStarted,Event::TurnStarted,Event::ToolCallRequested,Event::StreamChunk, etc. are emitted by agents while running.
Protocol Events Reference
These map to autoagents::core::protocol::Event variants emitted by actor agents and the runtime:
TaskStarted { sub_id, actor_id, actor_name, task_description }- Emitted when an agent begins processing a task.
TaskComplete { sub_id, actor_id, actor_name, result }- Final result for a task.
resultis a pretty JSON string; parse into your agent output type when needed.
- Final result for a task.
TaskError { sub_id, actor_id, error }- Any executor/provider error surfaced during execution.
TurnStarted { turn_number, max_turns }- Multi-turn executors (e.g., ReAct) mark each turn start.
TurnCompleted { turn_number, final_turn }- Marks turn completion;
final_turnis true when the loop ends.
- Marks turn completion;
ToolCallRequested { id, tool_name, arguments }- The LLM requested a tool call with JSON arguments (as string).
ToolCallCompleted { id, tool_name, result }- Tool finished successfully;
resultis JSON.
- Tool finished successfully;
ToolCallFailed { id, tool_name, error }- Tool failed; error string is included.
StreamChunk { sub_id, chunk }- Streaming delta content;
chunkmatches provider’s streaming shape.
- Streaming delta content;
StreamToolCall { sub_id, tool_call }- Streaming tool call delta (when provider emits incremental tool-call info).
StreamComplete { sub_id }- Streaming finished for the current task.
Internally, the runtime also routes PublishMessage for typed pub/sub (Topic<M>), but that variant is skipped in serde and used only inside the runtime.
When To Use Actor Agents vs Direct Agents
- Use Direct agents for one-shot calls (no runtime, minimal wiring).
- Use Actor agents when you need: real-time events, multiple agents, pub/sub routing, or running agents as durable tasks.
Advanced Patterns
Advanced Patterns (Core)
This chapter collects practical patterns using the core library APIs.
1) Shared Tools Across Agents
Use SharedTool or shared_tools_to_boxes to reuse Arc<dyn ToolT> across many agents without cloning tool instances.
2) Memory Strategies
- Start with
SlidingWindowMemoryfor compact histories. - Persist memory using your own storage if you want durable conversations.
- Mix recalled messages from memory with system instructions in
Context.
3) Streaming UI Integration
- Prefer
execute_streamand consume stream items directly. - Subscribe to protocol
Events for fine-grained updates likeTurnStarted,ToolCallRequested, andStreamChunk.
4) Multi-Agent Topologies (Pub/Sub)
- Use
Topic<M>to broadcast tasks to a group of actor agents. - Combine with
Environment+Runtimeto route events and messages.
Serve and CLI
AutoAgents includes a serving library (autoagents-serve) and a CLI (autoagents-cli) for running agent workflows defined in YAML.
To install the CLI, clone the AutoAgents repository and run:
cargo install --path ./crates/autoagents-cli.
CLI
Binary: autoagents
Subcommands:
run --workflow <file> --input <text>- Loads a workflow YAML and executes it once with the given input.
serve [--workflow <file> | --directory <dir>] [--name <name>] [--host <host>] [--port <port>]- Serves one or many workflows over HTTP.
Examples:
# Run a single workflow once
OPENAI_API_KEY=... cargo run -p autoagents-cli -- run --workflow examples/serve/workflows/direct.yaml --input "2 + 2"
# Serve a single workflow
OPENAI_API_KEY=... cargo run -p autoagents-cli -- serve --workflow examples/serve/workflows/direct.yaml --host 127.0.0.1 --port 8080
# Serve all workflows in a directory
OPENAI_API_KEY=... cargo run -p autoagents-cli -- serve --directory examples/serve/workflows --host 127.0.0.1 --port 8080
Workflow YAML
Top-level fields:
kind:Direct|Sequential|Parallel|Routingname: Optional workflow namedescription: Optional descriptionversion: Optional versionstream:true/falseto enable streamingmemory_persistence: Optional persistence policy{ mode: "memory" | "file" }workflow: Workflow spec (see below)environment: Optional{ working_directory, timeout_seconds }runtime: Optional{ type: "single_threaded", max_concurrent }
Workflow spec variants:
Direct:workflow.agent: Single agent to runworkflow.output: Output type
Sequential/Parallel:workflow.agents: List of agents to run (in order or concurrently)workflow.output: Output type
Routing:workflow.router: Router agentworkflow.handlers:[{ condition, agent }, ...]routing targetsworkflow.output: Output type
Agent
agent:
name: MathAgent
description: "Helpful math agent"
instructions: |
You are a math expert. Solve the problem step by step.
executor: ReAct # or Basic
memory:
kind: sliding_window
parameters:
window_size: 10 # or `n_slide`
model:
kind: llm
backend:
kind: Cloud # or Local
base_url: ... # optional
provider: OpenAI # e.g., OpenAI, Anthropic, Groq, OpenRouter, Ollama
model_name: gpt-4o-mini
parameters:
temperature: 0.1
max_tokens: 500
top_p: 0.95
top_k: 40
tools: [ ] # list of tool names (see Tools)
output:
type: text # text | json | structured
Tools
Tool entries are resolved by the server’s registry; for built-in toolkits (e.g., search), enable features like search-tools on autoagents-serve.
tools:
- name: brave_search
options: { }
config: { }
Output
Output config allows specifying the format:
type: text— plain texttype: json— JSON valuetype: structured— JSON Schema viaschema
Examples
Study the sample workflows in examples/serve/workflows/:
- Direct:
examples/serve/workflows/direct.yaml - Sequential:
examples/serve/workflows/sequential.yaml - Parallel:
examples/serve/workflows/parallel.yaml - Routing:
examples/serve/workflows/routing.yaml - Research (with search tool):
examples/serve/workflows/research.yaml
Library Usage (Serve)
For embedding in your app, use WorkflowBuilder and (optionally) HTTPServer from autoagents-serve:
#![allow(unused)]
fn main() {
use autoagents_serve::{WorkflowBuilder, HTTPServer, ServerConfig};
// Build and run in-process
let built = WorkflowBuilder::from_yaml_file("examples/serve/workflows/direct.yaml")?
.build()?;
let result = built.run("2 + 2".to_string()).await?;
// Serve over HTTP
let server = HTTPServer::new(ServerConfig { host: "127.0.0.1".into(), port: 8080 },
std::collections::HashMap::from([
("calc".into(), "examples/serve/workflows/direct.yaml".into())
]));
server.serve().await?;
}
See crate docs for endpoint details and streaming support.
Overview
AutoAgents supports a wide range of LLM providers, allowing you to choose the best fit for your use case:
Cloud Providers
| Provider | Status |
|---|---|
| OpenAI | ✅ |
| OpenRouter | ✅ |
| Anthropic | ✅ |
| DeepSeek | ✅ |
| xAI | ✅ |
| Phind | ✅ |
| Groq | ✅ |
| ✅ | |
| Azure OpenAI | ✅ |
Local Providers
| Provider | Status |
|---|---|
| Mistral-rs | ⚠️ Under Development |
| Burn | ⚠️ Experimental |
| Onnx | ⚠️ Experimental |
| Ollama | ✅ |
Provider support is actively expanding based on community needs.
Using Providers
Providers are accessed via LLMBuilder and enabled via autoagents crate features. Choose only what you need (e.g.,
openai, anthropic, ollama).
#![allow(unused)]
fn main() {
use autoagents::llm::builder::LLMBuilder;
use autoagents::llm::backends::openai::OpenAI;
use std::sync::Arc;
let llm: Arc<OpenAI> = LLMBuilder::<OpenAI>::new()
.api_key(std::env::var("OPENAI_API_KEY")?)
.model("gpt-4o")
.build()?;
}
Local providers like Ollama:
#![allow(unused)]
fn main() {
use autoagents::llm::backends::ollama::Ollama;
let llm: Arc<Ollama> = LLMBuilder::<Ollama>::new()
.base_url("http://localhost:11434")
.model("llama3.2:3b")
.build()?;
}
Feature Flags
Enable providers on the autoagents crate:
autoagents = { version = "0.3.0", features = ["openai"] }
Common API key environment variables:
OPENAI_API_KEYANTHROPIC_API_KEYOPENROUTER_API_KEYGROQ_API_KEYGOOGLE_API_KEYAZURE_OPENAI_API_KEYXAI_API_KEY
Architecture
All LLM backends implement the unified LLMProvider trait; chat/completion/embedding/model listing are composed from
sub‑traits. This keeps agents provider‑agnostic.
Capability Snapshot
This snapshot reflects the current code paths in autoagents-llm and may vary by specific model or provider changes.
-
OpenAI
- Chat + Streaming: Yes
- Tool Calls: Yes
- Structured Output (JSON Schema): Yes
- Embeddings: Yes
- Notes: Some options vary by model; check provider docs.
-
Anthropic (Claude)
- Chat + Streaming: Yes
- Tool Calls: Yes (Anthropic tool-use format)
- Structured Output: Not standardized; return text + tool events
- Embeddings: No
-
Groq (OpenAI-compatible)
- Chat + Streaming: Yes
- Tool Calls: Yes
- Structured Output: Yes
- Embeddings: No (not implemented)
-
OpenRouter (OpenAI-compatible)
- Chat + Streaming: Yes
- Tool Calls: Yes
- Structured Output: Yes
- Embeddings: No (not implemented)
For other providers (Azure OpenAI, Google, XAI, DeepSeek, Ollama), consult their module docs and service documentation; support can vary by model and API.
Development Setup
If you want to contribute to AutoAgents or build from source, follow these additional steps:
Additional Prerequisites
- LeftHook - Git hooks manager for code quality
- Cargo Tarpaulin - Test coverage tool (optional)
Installing LeftHook
LeftHook is essential for maintaining code quality and is required for development.
macOS (using Homebrew):
brew install lefthook
Linux (Ubuntu/Debian):
# using npm
npm install -g lefthook
Clone and Setup Repository
# Clone the repository
git clone https://github.com/liquidos-ai/AutoAgents.git
cd AutoAgents
# Install Git hooks using lefthook
lefthook install
# Build the project
cargo build --release
# Run tests to verify setup
cargo test --all-features
Installing Additional Development Tools
# For test coverage (optional)
cargo install cargo-tarpaulin
# For documentation generation (mdBook)
cargo install mdbook mdbook-mermaid
# For security auditing (recommended)
cargo install cargo-audit
System Dependencies
macOS
# Install Xcode command line tools (if not already installed)
xcode-select --install
# Install additional dependencies via Homebrew
brew install pkg-config openssl
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install -y \
build-essential \
pkg-config \
libssl-dev \
curl \
git
Windows
Install the following:
- Visual Studio Build Tools or Visual Studio Community with C++ build tools
- Git for Windows
- Windows Subsystem for Linux (WSL) - recommended for better compatibility
Verification
After installation, verify everything is working:
# Check Rust installation
cargo --version
rustc --version
# Check lefthook installation (for development)
lefthook --version
# Build AutoAgents
cd AutoAgents
cargo build --all-features
# Run tests
cargo test --all-features
# Check git hooks are installed (for development)
lefthook run pre-commit
Git Hooks (Development)
The project uses LeftHook to manage Git hooks that ensure code quality:
Pre-commit Hooks
- Formatting:
cargo fmt --check- Ensures consistent code formatting - Linting:
cargo clippy --all-features --all-targets -- -D warnings- Catches common mistakes - Testing:
cargo test --all-features- Runs the test suite - Type Checking:
cargo check --all-features --all-targets- Validates compilation
Pre-push Hooks
- Full Testing:
cargo test --all-features --release- Comprehensive test suite - Documentation:
cargo doc --all-features --no-deps- Ensures docs build correctly
Running Tests with Coverage
# Install tarpaulin if not already installed
cargo install cargo-tarpaulin
# Run tests with coverage
cargo tarpaulin --all-features --out html
Documentation
Build the docs locally with mdBook:
cd docs
mdbook serve -p 4000
Code Style
Follow idiomatic Rust style and keep warnings at zero.
- Rust 2021 edition; 4‑space indent; format with
rustfmt - Names: crates
kebab-case; modules/functionssnake_case; types/traitsUpperCamelCase; constantsSCREAMING_SNAKE_CASE - Prefer
Resultover panics; avoidunwrap()in library code - Keep
clippywarnings at zero (cargo clippy --all-features --all-targets -- -D warnings) - Small, focused modules; clear error types; explicit conversions via
From/Into - Tests live alongside code (
mod tests) and as integration tests under each crate’stests/
Testing
Use Rust’s test harness for unit and integration tests. Async tests use #[tokio::test] where needed.
Fast Test Cycle
cargo test --workspace --features default \
--exclude autoagents-burn \
--exclude autoagents-mistral-rs \
--exclude wasm_agent
Lint and Format
cargo fmt --all
cargo clippy --all-features --all-targets -- -D warnings
Coverage
cargo install cargo-tarpaulin
cargo tarpaulin --all-features --out html
Keep tests focused and avoid unrelated changes. Match the style and structure used in existing tests across crates.
Workspace Architecture
This page maps the crates (excluding the CLI and Serve crates) and how they compose so contributors can keep boundaries clear.
Crate Layout
autoagents: Facade crate that re-exports the public surface fromautoagents-core,autoagents-llm, and derive macros; holds only feature wiring and logging initialization.autoagents-core: Agent engine (agent config, executors, memory, protocol/events, vector store traits, runtime abstractions). Usesractoronly on non-WASM targets; compiled out for wasm viacfg.autoagents-llm: Provider-agnostic LLM traits plus concrete backend implementations (OpenAI, Anthropic, Ollama, etc.) and theLLMBuilderto configure them. Purely networking + request/response normalization, no agent logic.autoagents-derive: Proc macros for#[agent],#[tool], and derive helpers (AgentOutput,ToolInput,AgentHooks) that generate glue code while keeping downstream code ergonomic.autoagents-toolkit: Shared, reusable tools and MCP helpers. Feature-gated (filesystem,search,mcp) so downstream crates only pull what they need.autoagents-qdrant: Vector store implementation backed by Qdrant. Implements theVectorStoreIndextrait fromautoagents-coreand depends on an embedding provider viaSharedEmbeddingProvider.- Inference crates (optional):
autoagents-onnx,autoagents-burn, andautoagents-mistral-rsprovide local/runtime-specific inference backends. They plug into the LLM traits but are isolated to keep the core light. autoagents-test-utils: Shared fixtures and helpers for integration tests across crates.examples/*: Runnable end-to-end examples that demonstrate wiring agents, executors, and providers; each example is its own crate to keep dependencies scoped.
Layering and Dependencies
- Top-level dependency direction is
autoagents→ (autoagents-core,autoagents-llm,autoagents-derive). autoagents-coredepends onautoagents-llmfor message/LLM types but keeps provider-specific details out of the core execution logic.autoagents-toolkitandautoagents-qdrantdepend on the core traits and optionally onautoagents-llm/providers for embeddings.- Inference crates implement the LLM traits so they can be swapped with remote providers without changing agent code.
- Examples pull only the crates they exercise (e.g.,
autoagents-qdrantfor vector store examples), which keeps build times predictable and dependencies modular.
Agent/Runtime Flow (non-Serve/CLI)
- Agent definition (usually via
#[agent]fromautoagents-derive) describes tools, output type, and hooks. - Executors in
autoagents-core(Basic, ReAct, direct or actor-backed) drive the conversation loop, calling into:- Memory providers (sliding window, etc.) from
autoagents-core. - Tools (from
autoagents-toolkitor custom example-local tools). - LLM providers implementing the
LLMProvidertraits (autoagents-llmbackends or local inference crates).
- Memory providers (sliding window, etc.) from
- Optional vector store operations go through
VectorStoreIndex(e.g.,autoagents-qdrant). - On non-WASM targets, the actor runtime (
ractor) manages multi-agent orchestration; on WASM targets those pieces arecfg-gated out.
Modularity Guidelines
- Keep provider concerns inside
autoagents-llm(or inference crates); avoid leaking HTTP/provider structs intoautoagents-core. - Add reusable tools to
autoagents-toolkit; example-specific tools should stay within their example crate. - Prefer feature flags on extension crates (
autoagents-toolkit,autoagents-llm, inference crates) so downstream users can opt in without pulling heavy dependencies. - When adding new storage or provider integrations, implement the existing traits (
VectorStoreIndex,EmbeddingProvider,LLMProvider) to preserve swappability and testability.