Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quickstart

This guide builds a small agent, adds a typed tool, and runs it with deterministic model output. The examples use TestModel so they compile and run without provider credentials.

Add the SDK

From a published release:

[dependencies]
starweaver-agent = "X.Y.Z"

From a checkout:

[dependencies]
starweaver-agent = { path = "crates/starweaver-agent" }

Run an agent

#![allow(unused)]
fn main() {
use std::sync::Arc;

use starweaver_agent::{AgentBuilder, TestModel};

async fn example() -> Result<(), starweaver_agent::AgentError> {
let agent = AgentBuilder::new(Arc::new(TestModel::with_text("Paris")))
    .instruction("Answer in one word.")
    .build();

let result = agent.run("What is the capital of France?").await?;
assert_eq!(result.output, "Paris");
Ok(())
}
}

AgentBuilder::build() returns the reusable runtime agent. Use build_app() when your application needs SDK sessions, subagents, or app-level helper protocols.

Add a typed tool

Tools are ordinary async Rust functions with typed JSON arguments. Starweaver derives the model-facing JSON Schema from the argument type.

#![allow(unused)]
fn main() {
use std::sync::Arc;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use starweaver_agent::{typed_tool, AgentBuilder, TestModel, ToolContext, ToolResult};

#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
struct TicketArgs {
    /// Ticket identifier to fetch.
    id: String,
}

async fn example() -> Result<(), starweaver_agent::AgentError> {
let fetch_ticket = typed_tool::<TicketArgs, _, _>(
    "fetch_ticket",
    Some("Fetch a support ticket".to_string()),
    |_ctx: ToolContext, args: TicketArgs| async move {
        Ok(ToolResult::new(serde_json::json!({
            "id": args.id,
            "priority": "high"
        })))
    },
);

let agent = AgentBuilder::new(Arc::new(TestModel::with_text("high")))
    .instruction("Check available evidence before answering.")
    .tool(Arc::new(fetch_ticket))
    .build();

let result = agent.run("What is the priority for ticket T-1?").await?;
assert_eq!(result.output, "high");
Ok(())
}
}

Return structured output

Use OutputPolicy::typed_named when the application wants typed JSON output.

#![allow(unused)]
fn main() {
use std::sync::Arc;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use starweaver_agent::{AgentBuilder, OutputPolicy, TestModel};

#[derive(Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
struct Answer {
    answer: String,
}

async fn example() -> Result<(), starweaver_agent::AgentError> {
let agent = AgentBuilder::new(Arc::new(TestModel::with_text(r#"{"answer":"Paris"}"#)))
    .output_policy(OutputPolicy::typed_named::<Answer>("answer"))
    .build();

let result = agent.run("Return JSON.").await?;
let answer: Answer = result.structured()?;
assert_eq!(answer, Answer { answer: "Paris".to_string() });
Ok(())
}
}

Keep a session

AgentSession keeps AgentContext across turns. Use it when you need message history, usage, state, dependencies, notes, or export/restore.

#![allow(unused)]
fn main() {
use std::sync::Arc;

use starweaver_agent::{AgentBuilder, TestModel};
use starweaver_model::ModelResponse;

async fn example() -> Result<(), starweaver_agent::AgentError> {
let app = AgentBuilder::new(Arc::new(TestModel::with_responses(vec![
    ModelResponse::text("ok"),
    ModelResponse::text("ok"),
])))
.build_app();
let mut session = app.session();

let first = session.run("hello").await?;
let second = session.run("again").await?;

assert_eq!(first.output, "ok");
assert_eq!(second.output, "ok");
Ok(())
}
}

Run the CLI locally

make cli -- -p "hello" --output text
make sw -- version