An agentic feedback loop that evolves Rust functions.
The LLM generates type-safe code, the harness compiles and hot-swaps it into your running binary, evaluates the results, and feeds performance back to the LLM for the next iteration — bare-metal execution, zero interpreter overhead.

How it works

backpressureprompt steeringUSERspecify signatureLLMgenerate bodyVALIDATEparse + signatureCOMPILEnative .soHOT-SWAPin-placeEVALUATEagainst metricHARNESScatch panics, errorsevolution loopconstrained generation

Declare the function signature and provide an evaluation function. The agent autonomously implements, and refines the code each iteration — the harness validates, compiles, and hot-swaps the native code in-place. Diagnostics and failures are fed back as prompt steering — constrained generation, with bare-metal execution.

Quick Start

symbiont::evolvable! {
    fn step(counter: &mut usize) {
        // Default body will be entrely evolved by the Agent
        *counter += 1;
        println!("doing stuff in iteration {}", counter);
    }
}

#[tokio::main]
async fn main() -> symbiont::Result<()> {
    let runtime = symbiont::Runtime::init(SYMBIONT_DECLS).await?;
    let agent = symbiont::inference::init_agent()?;
    let fn_sigs = runtime.fn_sigs();
    let prompt = format!(
        "Give a concise implementation for this function signature: ```{}```, \
        that increments the counter by a constant in the range (5..20). \
        Give Rust Code Only.",
        fn_sigs[0]
    );

    let mut counter = 0;
    loop {
        step(&mut counter);  // bare-metal: native dylib call
        println!("counter: {counter}");

        // LLM rewrites the fn, harness validates + compiles + hot-swaps
        runtime.evolve(&agent, &prompt).await?;
        // `step` function was updated and new Agent code runs in the next loop iteration
    }
}

Similar code to the above can be run with cargo run --bin counter-example in the repo to showcase the function evolution.
Here the function is evolved every 5 seconds.

Now that you grok the core concept, a whole new world of opportunity opens up...

Showcase: Fractal Studio

An interactive window whose per-pixel shader is written by the LLM and hot-swapped into the running binary as optimized native code. Type a prompt — "an animated Julia set, c orbiting the main cardioid, with a glowing sunset palette" — and the agent implements fn shade(x: f64, y: f64, t: f64) -> u32; the live animation morphs in place, no restart.

Explore the fractal-studio example →

Showcase: Batched Evolution

evolve_batch runs one lane per prompt, concurrently — thirty-two slightly different prompts, thirty-two independent retry budgets, thirty-two candidate implementations. Against a server that batches (vLLM, SGLang, llama-server --parallel n) the lanes merge into shared forward passes, so a whole population round costs a fraction of the same evolutions run in a loop.

Population round

41s
32 candidates — 407s one at a time

Decode throughput

9.1×
72 → 656 tok/s, still climbing at 32 lanes

Prefix cache

74–76%
of prompt tokens, shared across every lane
Decode throughput by concurrency tokens/second, Bonsai-8B on vLLM, RTX PRO 6000 Blackwell
60040020001 lane in flight — 72 tok/s, 407.1s wall2 lanes in flight — 88 tok/s, 679.6s wall (retry-heavy outlier)4 lanes in flight — 196 tok/s, 149.8s wall8 lanes in flight — 300 tok/s, 102.3s wall16 lanes in flight — 475 tok/s, 57.2s wall32 lanes in flight — 656 tok/s, 40.8s wall7265612481632lanes in flight
Measured data
Lanes in flightWallDecode tok/sLanes converged
1407.1s7218/32
2679.6s8818/32
4149.8s19618/32
8102.3s30016/32
1657.2s47516/32
3240.8s65617/32

The 2-lane row is an outlier: it drew a set of lanes that ground through their full retry budgets and generated twice the tokens of every other level. Retries within a lane are sequential, so they lengthen that lane's critical path — and a level's wall clock is its slowest lane.

Explore the batched-evolution example →

Core Highlights

Type-safe agentic code

LLms express intent as Rust functions with enforced signatures. The compiler is the guardrail.

Constrained generation

Parse errors, signature mismatches, and compiler diagnostics steer the LLM until it produces valid code.

Hot-swap dylibs

Functions compile to native shared libraries and swap in-place via libloading — no process restart.

Bare-metal performance

~1 ns dispatch overhead. Lock-free hot path via AtomicPtr. Multi-thread safe.

Batched evolution

One lane per prompt, run concurrently and merged into shared forward passes by the inference server. A whole population per round, for a fraction of the wall clock.

Revision registry

Every hot-swapped dylib stays loaded and addressable. Roll back, A/B compare, or run an ensemble of past revisions concurrently — no recompilation.

Panic catching

Runtime panics in LLM code are caught inside the dylib and fed back as prompt context automatically.

Plug-in inference

Any OpenAI-compatible provider via rig. Local or cloud.

Performance

Dispatch overhead

~1ns
per function call

Compilation

~120ms
per evolution, depending on Agent code.

Inference latency

Seconds
depends on model and hardware of your choice

When Symbiont wins

Symbiont is purpose-built for workloads with fast evaluation requirements and expensive state that can't be reloaded on every iteration.

Positioning Quadrant — When Symbiont Wins

Examples