Agents Agents — Rust examples backed by real provider calls. rust examples examples/short-agents src/examples/rust/short-agents example Agents

These Rust examples are real runnable files. Edit the source file first; this page is rebuilt from the checked-in example and its metadata header.

Rust Grounded Support Agent

Answers a support question grounded in a handbook that is kept out of the model prompt via contextFields.

Rust
use axllm::runtime::quickjs::QuickJsCodeRuntime;
use axllm::{agent_with_options, AxResult, OpenAICompatibleClient};
use serde_json::json;
use std::env;

fn openai_client() -> AxResult<OpenAICompatibleClient> {
    let api_key = env::var("OPENAI_API_KEY")
        .or_else(|_| env::var("OPENAI_APIKEY"))
        .map_err(|_| {
            axllm::AxError::runtime("Set OPENAI_API_KEY or OPENAI_APIKEY to run this example.")
        })?;
    let model = env::var("AX_OPENAI_MODEL").unwrap_or_else(|_| "gpt-5.4-mini".to_string());
    Ok(OpenAICompatibleClient::new(api_key, model).with_model_config(json!({"temperature": 0})))
}

fn main() -> AxResult<()> {
    let mut client = openai_client()?;

    // The handbook can be arbitrarily large. Listing it in `contextFields` keeps
    // it in the agent's runtime so it never inflates the model prompt -- the agent
    // reads it through code, not through tokens.
    let handbook = r#"
# Acme Cloud -- Support Handbook

## Billing
- Invoices are issued on the 1st of each month and are due net-15.
- Plan downgrades take effect at the END of the current billing cycle, not immediately.
- Refunds are issued to the original payment method within 5 business days.

## Access
- Seats can be added by any workspace Owner under Settings -> Members.
- SSO (SAML) is available on Enterprise; SCIM provisioning is Owner-only.

## Incidents
- Status and uptime are published at status.acme.example.
- Sev-1 incidents page the on-call within 5 minutes; updates post every 30 minutes.

## Data
- Exports are available in CSV and JSON from Settings -> Data.
- Deleted workspaces are recoverable for 30 days, then permanently purged.
"#;

    // `with_runtime` attaches the embedded JS engine so the agent loop can run.
    let mut assistant = agent_with_options(
        "question:string, handbook:string -> answer:string, citations:string[] \"Handbook sections the answer relies on\"",
        json!({"contextFields": ["handbook"], "runtime": {"language": "JavaScript"}}),
    )?
    .with_runtime(Box::new(QuickJsCodeRuntime::new()))?;

    let output = assistant.forward_with_options(
        &mut client,
        json!({
            "question": "A customer downgraded their plan today. When does it take effect, and can they get a refund for the current cycle?",
            "handbook": handbook,
        }),
        json!({"max_actor_steps": 12}),
    )?;

    println!("{}", serde_json::to_string_pretty(&output)?);
    Ok(())
}

Rust Incident Triage Agent

Triages a noisy incident report held in contextFields, using a lean contextPolicy to keep the raw log out of the prompt while it reasons.

Rust
use axllm::runtime::quickjs::QuickJsCodeRuntime;
use axllm::{agent_with_options, AxResult, OpenAICompatibleClient};
use serde_json::json;
use std::env;

fn openai_client() -> AxResult<OpenAICompatibleClient> {
    let api_key = env::var("OPENAI_API_KEY")
        .or_else(|_| env::var("OPENAI_APIKEY"))
        .map_err(|_| {
            axllm::AxError::runtime("Set OPENAI_API_KEY or OPENAI_APIKEY to run this example.")
        })?;
    let model = env::var("AX_OPENAI_MODEL").unwrap_or_else(|_| "gpt-5.4-mini".to_string());
    Ok(OpenAICompatibleClient::new(api_key, model).with_model_config(json!({"temperature": 0})))
}

fn main() -> AxResult<()> {
    let mut client = openai_client()?;

    // A raw, noisy incident report. It lives in `contextFields`, so the agent works
    // it inside the runtime; `contextPolicy: lean` keeps the prompt compact by
    // preferring live runtime state and summaries over replaying the raw text.
    let report = r#"[2026-03-02 14:01:22Z] INFO  gateway       deploy svc-checkout-edge v812 -> prod (channel: canary 10%)
[2026-03-02 14:03:10Z] WARN  checkout-api  p95 latency 1180ms (baseline 240ms) region=eu-west-1
[2026-03-02 14:04:55Z] ERROR checkout-api  502 from svc-payments-gw: upstream timeout (10s) tenant_tier=enterprise
[2026-03-02 14:05:01Z] ERROR checkout-api  502 from svc-payments-gw: upstream timeout (10s) tenant_tier=enterprise
[2026-03-02 14:05:40Z] WARN  payments-gw   circuit half-open, 3 retries exhausted for order=ord_99214
[2026-03-02 14:06:12Z] INFO  gateway       canary widened 10% -> 50% for svc-checkout-edge v812
[2026-03-02 14:07:33Z] ERROR checkout-api  502 from svc-payments-gw: upstream timeout (10s) tenant_tier=enterprise
[2026-03-02 14:08:02Z] ERROR checkout-api  user-visible: "Payment could not be processed" shown to 1,284 sessions
[2026-03-02 14:09:48Z] WARN  payments-gw   connection pool exhausted (max=64) waiting=210
[2026-03-02 14:11:20Z] INFO  on-call       paged: SEV-2 opened (eu-west-1 checkout error rate 38%)
[2026-03-02 14:14:05Z] INFO  gateway       rollback svc-checkout-edge v812 -> v811 (channel: prod 100%)
[2026-03-02 14:17:41Z] INFO  checkout-api  p95 latency 260ms, error rate 0.4% region=eu-west-1
[2026-03-02 14:19:10Z] INFO  on-call       SEV-2 mitigated, monitoring for 30m"#;

    // `with_runtime` attaches the embedded JS engine so the agent loop can run.
    let mut triage = agent_with_options(
        "report:string, question:string -> severity:class \"low, medium, high, critical\", rootCause:string, nextSteps:string[], evidence:string[] \"Quoted log lines that support the assessment\"",
        json!({
            "contextFields": ["report"],
            "contextPolicy": {"preset": "lean", "budget": "balanced"},
            "runtime": {"language": "JavaScript"},
        }),
    )?
    .with_runtime(Box::new(QuickJsCodeRuntime::new()))?;

    let output = triage.forward_with_options(
        &mut client,
        json!({
            "report": report,
            "question": "What happened, how bad was it, and what should the on-call do next? Cite the lines you relied on.",
        }),
        json!({"max_actor_steps": 12}),
    )?;

    println!("{}", serde_json::to_string_pretty(&output)?);
    Ok(())
}

Rust Agent Background Tools

Runs declared background agent tools with steering and verifies final result incorporation.

Rust
use axllm::{ai, agent_with_options, run_control, tool, AxForwardOptions, AxResult};
use serde_json::json;
use axllm::runtime::quickjs::QuickJsCodeRuntime;
use std::{
    env,
    sync::{
        atomic::{AtomicBool, AtomicUsize, Ordering},
        Arc,
    },
    time::Duration,
};

fn main() -> AxResult<()> {
    let key = env::var("OPENAI_API_KEY")
        .or_else(|_| env::var("OPENAI_APIKEY"))
        .expect("Set OPENAI_API_KEY or OPENAI_APIKEY.");
    let mut client = ai(
        "openai",
        json!({"api_key":key,"model":"gpt-6-astra","model_config":{"thinkingTokenBudget":"low","max_tokens":4096}}),
    )?;
    let pending = Arc::new(AtomicBool::new(false));
    let finished = Arc::new(AtomicBool::new(false));
    let overlap = Arc::new(AtomicBool::new(false));
    let applied = Arc::new(AtomicUsize::new(0));
    let control = run_control();
    let steering = control.clone();
    let updates = applied.clone();
    control.on_event(move |event| {
        if event["type"] == "applied" {
            updates.fetch_add(1, Ordering::SeqCst);
        }
    });
    let started = pending.clone();
    let done = finished.clone();
    let steered = AtomicBool::new(false);
    let slow = tool("slow_reference")
        .description("Look up a reference; takes a few seconds.")
        .execution("background")
        .handler(move |_| {
            started.store(true, Ordering::SeqCst);
            if !steered.swap(true, Ordering::SeqCst) {
                steering.steer("Include the word VERIFIED in the final answer.")?;
                steering.set_thinking_token_budget("medium")?;
            }
            std::thread::sleep(Duration::from_secs(6));
            done.store(true, Ordering::SeqCst);
            Ok(json!("REF-42"))
        });
    let observed = overlap.clone();
    let label = tool("local_label")
        .description("Read an independent local label immediately.")
        .execution("background")
        .handler(move |_| {
            for _ in 0..300 {
                if pending.load(Ordering::SeqCst) {
                    break;
                }
                std::thread::sleep(Duration::from_millis(10));
            }
            observed.fetch_or(
                pending.load(Ordering::SeqCst) && !finished.load(Ordering::SeqCst),
                Ordering::SeqCst,
            );
            Ok(json!("LAUNCH"))
        });
    let mut program = agent_with_options("question -> answer", json!({"runtime":{"language":"JavaScript"},"directResponse":"off"}))?.with_tool_module("tools", vec![slow, label])?.with_runtime(Box::new(QuickJsCodeRuntime::new()))?;
    let result=program.forward_with_options(&mut client,json!({"question":"Use the native tools tools_slow_reference and tools_local_label. First call tools_slow_reference. While it is pending, call tools_local_label. Call each tool only once; do not call a tool again while its result is pending. In the executor, call the native tools directly rather than invoking them from actor code; then use final(...) in the code runtime to pass their results to the responder. Return both exact results in one sentence."}),AxForwardOptions::from(json!({"serviceTier":"standard","maxSteps":6,"max_actor_steps":12})).with_control(control))?;
    let answer = result.to_string();
    for word in ["REF-42", "LAUNCH", "VERIFIED"] {
        assert!(answer.contains(word), "Missing final result: {answer}");
    }
    assert!(
        overlap.load(Ordering::SeqCst),
        "No independent work while background tool was pending"
    );
    assert!(applied.load(Ordering::SeqCst) >= 2);
    println!("{answer}\nBackground overlap verified; steering and reasoning applied at the next response.");
    Ok(())
}

Rust Child Agent Controls

Delegates through a real actor runtime and applies controls to the child scope.

Rust
use axllm::runtime::quickjs::QuickJsCodeRuntime;
use axllm::{agent_with_options, ai, run_control, AxForwardOptions, AxResult};
use serde_json::json;
use std::{
    env,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc,
    },
};

fn main() -> AxResult<()> {
    let key = env::var("OPENAI_API_KEY")
        .or_else(|_| env::var("OPENAI_APIKEY"))
        .expect("Set OPENAI_API_KEY or OPENAI_APIKEY.");
    let mut client = ai(
        "openai",
        json!({"api_key":key,"model":"gpt-6-astra","model_config":{"thinkingTokenBudget":"low","max_tokens":4096}}),
    )?;
    let control = run_control();
    let applied = Arc::new(AtomicBool::new(false));
    let observed = applied.clone();
    control.on_event(move |event| {
        if event["type"] == "applied" && event["path"] == "root/team.researcher/executor" {
            observed.store(true, Ordering::SeqCst);
        }
    });
    control.steer("Include VERIFIED in the final answer.")?;
    control.steer_at(
        "Include CHILD-CHECK in the final answer.",
        "root/team.researcher",
    )?;
    control.set_thinking_token_budget_at("medium", "root/team.researcher/executor")?;
    let runtime =
        || Box::new(QuickJsCodeRuntime::new().with_runtime_policy(json!({"timeoutMs":180000})));
    let child = agent_with_options("question -> answer", json!({"directResponse":"off"}))?
        .with_runtime(runtime())?;
    let mut parent = agent_with_options("question -> answer", json!({"directResponse":"off"}))?
        .with_runtime(runtime())?
        .with_child_agent("team", "researcher", child)?;
    let result=parent.forward_with_options(&mut client,json!({"question":"Delegate to team.researcher exactly once by calling await team.researcher({question: \"Compute 37 + 5 and return the exact sum.\"}) in actor code. Pass the complete child answer as evidence to final(...), then report it in your final answer."}),AxForwardOptions::from(json!({"serviceTier":"standard","max_actor_steps":8})).with_control(control))?;
    for word in ["42", "VERIFIED", "CHILD-CHECK"] {
        assert!(result.to_string().contains(word), "{result}");
    }
    assert!(
        applied.load(Ordering::SeqCst),
        "Child control did not apply"
    );
    println!("{}", json!({"result":result,"usage":parent.get_usage()}));
    Ok(())
}

Rust Specialist Planner Agent

A specialist that plans a migration from a long brief held in contextFields, using a checkpointed contextPolicy and a runtime-output cap to stay compact.

Rust
use axllm::runtime::quickjs::QuickJsCodeRuntime;
use axllm::{agent_with_options, AxResult, OpenAICompatibleClient};
use serde_json::json;
use std::env;

fn openai_client() -> AxResult<OpenAICompatibleClient> {
    let api_key = env::var("OPENAI_API_KEY")
        .or_else(|_| env::var("OPENAI_APIKEY"))
        .map_err(|_| {
            axllm::AxError::runtime("Set OPENAI_API_KEY or OPENAI_APIKEY to run this example.")
        })?;
    let model = env::var("AX_OPENAI_MODEL").unwrap_or_else(|_| "gpt-5.4-mini".to_string());
    Ok(OpenAICompatibleClient::new(api_key, model).with_model_config(json!({"temperature": 0})))
}

fn main() -> AxResult<()> {
    let mut client = openai_client()?;

    // A long, messy brief -- exactly the kind of input you do not want replayed into
    // the prompt on every turn. `contextFields` holds it in the runtime, the
    // `checkpointed` policy compacts older turns once the prompt grows, and
    // `maxRuntimeChars` caps how much runtime output is echoed back.
    let brief = r#"# Migration brief: monolith -> services (draft, unordered notes)

Current: single Rails monolith, Postgres primary + 1 replica, Sidekiq for jobs.
Pain: deploys take 40m, one bad migration locks the orders table, on-call burnout.
Constraints: no downtime windows > 5m, PCI scope must shrink, team of 6, 2 quarters.
Hot paths: checkout (writes orders, payments), search (read-heavy), notifications (async).
Known landmines: payments code has no tests; search shares the orders DB; a nightly
cron rebuilds the catalog and pins CPU for ~20m; the replica lags up to 90s under load.
Org wants: independent deploys for checkout, smaller blast radius, an audit trail.
Nice to have: event log for orders, read-model for search, feature flags.
Hard no: a big-bang rewrite; introducing Kubernetes this year."#;

    // `with_runtime` attaches the embedded JS engine so the agent loop can run.
    let mut specialist = agent_with_options(
        "brief:string, goal:string -> plan:string[] \"Ordered, concrete steps\", answer:string, risks:string[]",
        json!({
            "contextFields": ["brief"],
            "contextPolicy": {"preset": "checkpointed", "budget": "balanced"},
            "maxRuntimeChars": 3000,
            "runtime": {"language": "JavaScript"},
        }),
    )?
    .with_runtime(Box::new(QuickJsCodeRuntime::new()))?;

    let output = specialist.forward_with_options(
        &mut client,
        json!({
            "brief": brief,
            "goal": "Propose a safe, incremental 2-quarter plan to split checkout out first, respecting the hard constraints.",
        }),
        json!({"max_actor_steps": 12}),
    )?;

    println!("{}", serde_json::to_string_pretty(&output)?);
    Ok(())
}

Rust Multi-Model Panel

Fans one question across three providers (OpenAI, Gemini, Anthropic), then judges the candidates and synthesizes a single grounded answer.

  • Provider: openai
  • Env: OPENAI_API_KEY, OPENAI_APIKEY, GOOGLE_APIKEY, ANTHROPIC_APIKEY
  • Level: advanced
  • Run: npm run example -- rust src/examples/rust/short-agents/model_panel.rs
  • Source: src/examples/rust/short-agents/model_panel.rs
Rust
use axllm::{ax, AnthropicClient, AxResult, GoogleGeminiClient, OpenAICompatibleClient};
use serde_json::{json, Value};
use std::env;

fn main() -> AxResult<()> {
    let openai_key = env::var("OPENAI_API_KEY")
        .or_else(|_| env::var("OPENAI_APIKEY"))
        .ok();
    let google_key = env::var("GOOGLE_APIKEY")
        .or_else(|_| env::var("GOOGLE_API_KEY"))
        .ok();
    let anthropic_key = env::var("ANTHROPIC_APIKEY")
        .or_else(|_| env::var("ANTHROPIC_API_KEY"))
        .ok();
    let (Some(openai_key), Some(google_key), Some(anthropic_key)) =
        (openai_key, google_key, anthropic_key)
    else {
        return Err(axllm::AxError::runtime(
            "Set OPENAI_APIKEY, GOOGLE_APIKEY, and ANTHROPIC_APIKEY to run this multi-provider panel.",
        ));
    };

    // A panel of three different providers, each answering the same question
    // independently. Plain ax() composition (no agent runtime): fan out to the
    // panel, judge the candidates, then synthesize one grounded answer.
    let mut openai = OpenAICompatibleClient::new(openai_key, "gpt-5.4-mini")
        .with_model_config(json!({"temperature": 0}));
    let mut gemini =
        GoogleGeminiClient::new(google_key, "gemini-3.5-flash").with_profile("google-gemini");
    let mut anthropic =
        AnthropicClient::new(anthropic_key, "claude-haiku-4-5").with_profile("anthropic");

    let mut researcher = ax(
        "question:string -> answer:string, keyFindings:string[], citations:string[], confidence:number",
    )?;
    researcher.set_instruction(
        "Answer independently. Use evidence. Call out uncertainty. Do not optimize for consensus.",
    );

    let mut judge = ax(
        "question:string, candidates:json -> consensus:string[], contradictions:string[], uniqueInsights:string[], blindSpots:string[]",
    )?;
    judge.set_instruction(
        "Compare the candidates. Find agreement, conflicts, missing coverage, and unique useful points.",
    );

    let mut synthesizer = ax(
        "question:string, candidates:json, review:json -> answer:string, citations:string[], caveats:string[]",
    )?;
    synthesizer.set_instruction(
        "Write one final answer grounded in the candidates and review. Resolve conflicts explicitly.",
    );

    let question = "What are the strongest arguments for and against a national carbon tax?";

    // Fan the same question across every provider, tagging each candidate with
    // the model that produced it.
    let mut candidates: Vec<Value> = Vec::new();
    let panel: [(&str, &mut OpenAICompatibleClient); 3] = [
        ("openai/gpt-5.4-mini", &mut openai),
        ("google/gemini-3.5-flash", &mut gemini),
        ("anthropic/claude-haiku-4.5", &mut anthropic),
    ];
    for (model, client) in panel {
        let mut response = researcher.forward(client, json!({"question": question}))?;
        if let Value::Object(map) = &mut response {
            map.insert("model".to_string(), json!(model));
        }
        candidates.push(response);
    }

    // The judge + synthesizer run on one of the panel clients (OpenAI here).
    let candidates = Value::Array(candidates);
    let review = judge.forward(
        &mut openai,
        json!({"question": question, "candidates": candidates}),
    )?;
    let final_answer = synthesizer.forward(
        &mut openai,
        json!({"question": question, "candidates": candidates, "review": review}),
    )?;

    println!("{}", serde_json::to_string_pretty(&final_answer)?);
    Ok(())
}
Docs