Skip to content

Heartbit

Multi-agent enterprise runtime in Rust

Orchestrator spawns sub-agents that execute LLM-powered reasoning loops with parallel tool execution. Zero-copy. Three execution paths. Production-grade.

cargo add heartbit

Why Heartbit

Zero-copy Agent Loops

ReAct cycle in pure Rust. No Python or Node overhead. Minimal allocations, maximum throughput.

Three Execution Paths

Standalone (zero infra), Durable (Restate), or Daemon (Kafka). Pick the right path for your workload.

Parallel Tool Execution

tokio::JoinSet runs tools concurrently within each turn. Flat agent hierarchy prevents runaway spawning.

8 Guardrails + Memory

Content fence, injection classifier, PII, tool policy, LLM judge, and more. MemGPT-style memory with hybrid retrieval.

Built-in Eval Framework

Trajectory scoring, keyword matching, and similarity scoring. Test agent behavior with real assertions.

Local-first Embeddings

Offline semantic search via ONNX Runtime (fastembed). No API keys required. Runs entirely on your hardware.

Architecture

Quick Examples

use std::sync::Arc;
use heartbit::{AgentRunner, AnthropicProvider};

let provider = Arc::new(AnthropicProvider::new(api_key, "claude-sonnet-4-20250514"));

let agent = AgentRunner::builder(provider)
    .system_prompt("You are a helpful assistant.")
    .build()?;

let output = agent.execute("Analyze the Rust ecosystem").await?;
println!("{}", output.result);
use std::sync::Arc;
use heartbit::{AnthropicProvider, Orchestrator};

let provider = Arc::new(AnthropicProvider::new(api_key, "claude-sonnet-4-20250514"));

let mut orchestrator = Orchestrator::builder(provider)
    .sub_agent("researcher", "Finds facts and data", "You research.")
    .sub_agent("writer", "Writes polished prose", "You write.")
    .build()?;

let output = orchestrator.run("Write an article about Rust").await?;
use heartbit::{Tool, ToolDefinition, ToolOutput};
use serde_json::json;

struct PriceLookup;

impl Tool for PriceLookup {
    fn definition(&self) -> ToolDefinition {
        ToolDefinition {
            name: "price_lookup".into(),
            description: "Look up product prices".into(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "product": { "type": "string", "description": "Product name" }
                },
                "required": ["product"]
            }),
        }
    }

    fn execute(&self, input: Value) -> Pin<Box<dyn Future<...>>> {
        Box::pin(async move {
            let product = input["product"].as_str().unwrap_or_default();
            Ok(ToolOutput::success(format!("Price: $9.99")))
        })
    }
}

Three Execution Paths

Standalone

No infrastructure

In-process agent execution. CLI tasks, scripts, library embedding. Zero setup.

Durable

Restate server

Crash-resilient workflows with exactly-once tool execution. Survives restarts.

Daemon

Kafka + Axum

Long-running services with HTTP API, cron scheduling, WebSocket, and Telegram.

2700+
Tests
14
Built-in Tools
8
Guardrails
3
Execution Paths
7
Sensor Sources