Expand description
§agent-sdk-rs
Pure-Rust SDK for tool-using agents with explicit control flow. Minimal by design: one agent loop, explicit completion, and provider adapters with a shared interface.
§Why this crate?
| Capability | agent-sdk-rs | Typical abstraction-heavy frameworks | Why this helps agents |
|---|---|---|---|
| Agent core | Explicit loop in Agent::query_stream | Hidden planners / wrappers | Fewer moving parts, easier debugging |
| Action space | User-defined tools via ToolSpec JSON schema | Fixed or opinionated primitives | Start broad, then restrict by policy |
| Completion semantics | Optional explicit done via ToolOutcome::Done + AgentBuilder::require_done_tool | Implicit stop when no tool calls | Prevents premature “done” |
| Provider interface | One trait (ChatModel) and swappable adapters | Provider-specific runtime behavior | Swap models without rewriting agent logic |
| Reliability guards | Retries/backoff + max-iteration limit + schema validation | Often ad-hoc in app code | Safer autonomous runs |
§Philosophy
This crate follows the “small loop, large action space, explicit exit” direction described by Browser Use:
In this crate, that maps to:
- Tools define capability surface (
ToolSpec). - The run loop is explicit and inspectable via events (
AgentEvent). - Completion can be explicit with
donemode (ToolOutcome::Done). - Model adapters stay thin and replaceable (
ChatModel,AnthropicModel,GoogleModel,GrokModel).
§Quickstart
use agent_sdk_rs::{Agent, AnthropicModel};
let model = AnthropicModel::from_env("claude-sonnet-4-5")?;
let mut agent = Agent::builder().model(model).build()?;
let answer = agent.query("Summarize the task in one line.").await?;
println!("{answer}");§Streaming events
use agent_sdk_rs::{Agent, AgentEvent, GoogleModel};
use futures_util::StreamExt;
let model = GoogleModel::from_env("gemini-2.5-flash")?;
let mut agent = Agent::builder().model(model).build()?;
let stream = agent.query_stream("Solve this step by step.");
futures_util::pin_mut!(stream);
while let Some(event) = stream.next().await {
match event? {
AgentEvent::ToolCall { tool, .. } => println!("tool: {tool}"),
AgentEvent::FinalResponse { content } => println!("final: {content}"),
_ => {}
}
}§Explicit done mode
For autonomous runs, require an explicit completion signal:
use agent_sdk_rs::{Agent, AnthropicModel};
use agent_sdk_rs::tools::claude_code::all_tools;
let model = AnthropicModel::from_env("claude-sonnet-4-5")?;
let mut agent = Agent::builder()
.model(model)
.tools(all_tools())
.require_done_tool(true)
.max_iterations(64)
.build()?;
let _ = agent.query("Inspect the repo and summarize open risks.").await?;§Evidence in this repository
- Done-tool stop semantics and max-iteration guard:
src/agent/tests.rs - Dependency override behavior for tools:
src/agent/tests.rs - Tool schema and argument validation:
src/tools/mod.rs - Provider adapters with the same core interface:
src/llm/
Re-exports§
pub use agent::Agent;pub use agent::AgentBuilder;pub use agent::AgentConfig;pub use agent::AgentEvent;pub use agent::AgentRole;pub use agent::AgentToolChoice;pub use agent::StepStatus;pub use agent::query;pub use agent::query_stream;pub use error::AgentError;pub use error::ProviderError;pub use error::SchemaError;pub use error::ToolError;pub use llm::AnthropicModel;pub use llm::AnthropicModelConfig;pub use llm::ChatModel;pub use llm::GoogleModel;pub use llm::GoogleModelConfig;pub use llm::GrokModel;pub use llm::GrokModelConfig;pub use tools::DependencyMap;pub use tools::ToolOutcome;pub use tools::ToolSpec;