Prompt in. Tested Rust container out.
Sigil is a self-forging Rust agent framework inspired by DSPy. You compose typed LM programs and controlled tools instead of maintaining brittle prompt scripts. Sigil can then build software, inspect its work, repair compiler failures, optimize its behavior, and package the result for deployment.
intent → typed LM program → controlled tools → Cargo gates → deployable container
- Typed LM programs — signatures, ordered fields, structured outputs, and derive macros.
- Composable reasoning —
Predict,ChainOfThought,ReAct,ProgramOfThought,CodeAct, ensembles, retrieval, and evaluation. - Real optimization — few-shot search, COPRO, SIMBA, MIPROv2-style TPE search, and GEPA reflective mutation with Pareto selection.
- Compiler-grounded agents — restricted workspace tools plus
fmt,check,test, and Clippy feedback. - Prompt-to-production — generated servers receive a multi-stage Dockerfile, Compose config, and an exact launch command.
- Visible execution — 🧠 turns, 🔧 tool calls, ✅ results, ❌ failures, and 🏁 completion.
- Live MCP tools — connect remote tool servers and absorb additions, schema changes, and removals while the agent is already running.
- Fleet mode — launch isolated builders concurrently without giving agents the Docker socket.
Behavioral coverage and deliberate differences from DSPy are tracked in PARITY.md.
Sigil defaults to Ollama at port 11434 using glm-5.2:cloud. Sigil itself needs no API token;
Ollama may require sign-in for cloud-model access.
docker compose build
docker compose run --rm sigil "Why is the sky blue?"Build a real project into the persistent ./workspace directory:
docker compose run --rm sigil build \
"Create a Rust status dashboard with a health endpoint"The agent inspects and writes only inside its mounted workspace. Before claiming success it must pass formatting, compilation, tests, and Clippy. Hosted applications also receive deployment files and a command such as:
cd workspace
docker compose up --buildOverride the model or any OpenAI-compatible endpoint when needed:
SIGIL_LM_ENDPOINT=https://example.com/v1/chat/completions \
SIGIL_LM_MODEL=my-model \
SIGIL_LM_API_KEY=... \
docker compose run --rm sigil "Your task"The host-side controller builds the Sigil image once and starts isolated agent containers:
cargo run --release --bin sigilctl -- fleet 4 \
"Build an alternative Rust API design"Results land in:
workspace/fleet/agent-001
workspace/fleet/agent-002
workspace/fleet/agent-003
workspace/fleet/agent-004
Each agent gets its own filesystem and bounded tool loop. Agents do not receive the Docker socket and cannot control sibling containers.
[dependencies]
sigil = { path = "../sigil" }
schemars = "1"
serde_json = "1"use std::sync::Arc;
use sigil::{Context, Example, JsonAdapter, Module, OpenAiCompatible, Predict, Signature};
# async fn demo() -> sigil::Result<()> {
let lm = OpenAiCompatible::new(
"http://127.0.0.1:11434/v1/chat/completions",
"glm-5.2:cloud",
None,
);
let context = Context::new(Arc::new(lm));
let program = Predict::new(
Signature::parse("question -> answer")?,
Arc::new(JsonAdapter::default()),
);
let input = Example::from_typed(&serde_json::json!({"question": "Why is the sky blue?"}))?;
let prediction = program.forward(&context, &input).await?;
println!("{}", prediction.values["answer"]);
# Ok(())
# }Rust tools are async, schema-described, and validated before execution. Invalid calls become observations so the model can repair them instead of crashing the agent.
use std::sync::Arc;
use serde_json::json;
use sigil::{FunctionTool, Tool};
let double: Arc<dyn Tool> = Arc::new(FunctionTool::new(
"double",
"Double an integer",
schemars::json_schema!({
"type": "object",
"properties": {"value": {"type": "integer"}},
"required": ["value"]
}),
|args| async move { Ok(json!({"value": args["value"].as_i64().unwrap() * 2})) },
));Enable MCP support when embedding Sigil:
[dependencies]
sigil = { path = "../sigil", features = ["mcp"] }
schemars = "1"
serde_json = "1"An MCP client peer becomes a live source for the primary agent loop:
let registry = Arc::new(McpRegistry::new(Arc::new(mcp_peer)));
registry.refresh().await?;
let agent = ReActV2::new(signature, local_tools, 20)?
.with_tool_source(registry.clone());
// Call this from an MCP tools/list_changed notification handler—or poll it.
// The existing `agent` sees the new snapshot on its next reasoning turn.
registry.tools_list_changed().await?;ReActV2 supports multiple native tool calls per turn, preserves call IDs, forces the final
submit tool when needed, and resamples every attached live registry each turn. No agent,
process, or container reload is required.
Sigil’s coding tools are deliberately narrower than a shell:
- Workspace paths cannot escape through absolute paths or
..traversal. - Self-edits run in disposable detached Git worktrees.
- Protected runtime and policy files can be denied to the agent.
- Cargo execution is restricted to approved verification gates.
- Deployment is generated deterministically; agents are not handed host Docker control.
The default image is self-hosting and includes source, Git, Rust, Cargo, rustfmt, and Clippy. A smaller runtime-only image is also available:
docker build --target runtime -t sigil:runtime .