cel-brief
Composable prompt briefing for AI agents. Gather memory, perception, history, tools, and user messages from pluggable sources; enforce token budgets; apply governance; and emit receipts.
Status: Phases 1–4 shipped — core types, the Source trait and all built-in sources, the Governance trait, and the BriefBuilder (tokenizer + priority/budget pruning + BriefReceipt). The crate is feature-complete for assembling per-turn briefs.
Purpose
Use cel-brief when an agent has many possible prompt inputs and needs one
structured, budgeted, governed package for a model call. Sources contribute
facts, messages, tools, history, or memory; the builder prunes to budget, runs
governance, and emits a receipt of what the model saw.
Why
Every non-trivial AI agent — chat, code, computer-use, robotics — has to decide
what to put in the prompt: memory retrievals, current screen state, recent
actions, tool schemas, and the user's message. Today many projects solve that
inside the agent loop with string concatenation and homegrown budget code.
cel-brief makes that step explicit, structured, and inspectable.
Three commitments
- Everything is a
Source. Memory, perception, history, tools — all the same trait. Plug incel-memory, Mem0, Letta, or your own —cel-briefdoesn't care. - Structured output, not a string.
Brief { messages, tools, system, receipt }is provider-agnostic; renderers map to OpenAI / Anthropic / local-model wire formats. - Governance and budget are first-class. Importance scoring, redaction hooks, token budgets, receipts — built in, not bolted on.
Comparison
| cel-brief | LangChain prompts | LlamaIndex ServiceContext |
ad-hoc concat | |
|---|---|---|---|---|
| Pluggable sources | ✓ trait-based | ✗ template strings | partial (retrievers only) | n/a |
| Token budgeting | ✓ priority-aware, per-source floor | ✗ caller's problem | ✗ caller's problem | ✗ rebuilt each turn |
| Importance-aware pruning | ✓ [0.0, 1.0] per contribution |
✗ | ✗ | ✗ |
| Governance / redaction hooks | ✓ Governance trait + receipts |
✗ | ✗ | ✗ |
| Receipts (what the model saw and why) | ✓ BriefReceipt with per-source stats |
✗ | ✗ | ✗ |
| Provider-agnostic output | ✓ structured Brief |
partial | ✓ | depends |
| Async fan-out across sources | ✓ async fn contribute |
✗ sync | ✓ | n/a |
| Memory integration as a Source | ✓ MemorySource over any MemoryProvider |
partial (Memory class) |
✓ | n/a |
| Perception / screen state as a Source | ✓ PerceptionSource over any backend |
n/a | n/a | n/a |
| Language | Rust | Python | Python | n/a |
Built-in sources
| Source | Feature | Priority | Notes |
|---|---|---|---|
SystemPromptSource |
default | Critical | Static system text. Never redactable. |
UserMessageSource |
default | Critical | Pulls ctx.user_message. Never redactable. |
ToolCatalogSource |
default | High | Owns Vec<ToolSchema>. |
HistorySource<H> |
default | Normal | Window of past N entries from any HistoryStore. Redactable. |
MemorySource<P> |
memory |
Normal | Hybrid retrieval over any cel_memory::MemoryProvider. Redactable. |
PerceptionSource<P> |
perception |
High | Defines the PerceptionSnapshot trait; downstream runtimes adapt their own perception engine into it. Redactable. |
Quick start
use ;
let ctx = new
.with_user_message;
let sys = new;
let user = new;
let cs = sys.contribute.await?;
assert_eq!;
# Ok::
See examples/standalone.rs for a self-contained hand-fanout and examples/with_memory.rs for the cel-memory integration:
The governance example shows a custom redaction hook and the resulting
BriefReceipt redaction records.
BriefBuilder
The BriefBuilder fans out to every registered source, tokenizes and prunes to budget, runs governance, and returns a Brief plus its BriefReceipt:
let brief = new
.source
.source
.source
.source
.governance // swap in your own Governance
.budget
.build.await?;
let response = openai.chat.await?;
println!;
Governance
Governance::review(&mut draft, &ctx) runs after budget pruning, before the brief is returned. The verdict is one of:
Allow— the brief is fine as-is.Redacted(Vec<RedactionRecord>)— the hook mutated redactable content; the records describe what changed and which rule did it.Rejected(String)— policy violation; the builder returnsBriefError::Rejected.
The default NoOpGovernance always allows. Production callers can plug in a real implementation that consults their own rules engine.
Features
memory— enableMemorySource<P>(depends oncel-memory).perception— enable thePerceptionSnapshottrait +PerceptionSource<P>. Perception backends live downstream: a runtime adapts its own live perception engine into aPerceptionSnapshot. This feature adds no dependency on any perception crate.
Benchmark
A microbenchmark for hand-assembled briefs lives at benches/build.rs. Target: under 50 ms p95 for a realistic brief once the BriefBuilder ships. Run with:
License
Apache-2.0