S.C.T.P.M.A Operator Algebra
Six namespace modules for declaratively composing agent primitives. Each maps to a dimension of agent configuration: State, Context, Tools, Prompt, Middleware, Artifacts. They compose using Rust operators so agent definitions read like algebraic expressions.
The Six Operators
Section titled “The Six Operators”| Namespace | Operator | Purpose | Key Methods |
|---|---|---|---|
S:: | >> | State transforms | pick, rename, merge, flatten, set, defaults, drop, map |
C:: | + | Context engineering | window, user_only, model_only, head, truncate, filter, from_state |
T:: | | | Tool composition | simple, function, google_search, code_execution, toolset |
P:: | + | Prompt composition | role, task, constraint, format, example, persona, guidelines |
M:: | | | Middleware layers | log, latency, timeout, retry, audit, circuit_breaker |
A:: | + | Artifact schemas | output, input, json_output, json_input, text_output, text_input |
S — State Transforms
Section titled “S — State Transforms”State transforms mutate a serde_json::Value representing agent state. Chain them with >> for sequential application.
Methods
Section titled “Methods”| Method | What it does |
|---|---|
S::pick(&["a", "b"]) | Keep only the listed keys, drop everything else |
S::drop(&["x"]) | Remove the listed keys |
S::rename(&[("old", "new")]) | Rename keys according to mappings |
S::merge(&["x", "y"], "combined") | Merge listed keys into a single nested object |
S::flatten("nested") | Flatten a nested object into the top level |
S::set("key", json!(42)) | Set a key to a fixed value |
S::defaults(json!({"k": "v"})) | Set default values for missing keys |
S::map(fn) | Apply a custom transformation function |
State Predicates
Section titled “State Predicates”S also provides predicates for phase transition guards and .when() modifiers:
| Predicate | What it checks |
|---|---|
S::is_true("key") | Key holds a truthy boolean |
S::eq("key", "value") | Key equals a specific string |
S::one_of("key", &["a", "b"]) | Key matches any of the listed strings |
Example
Section titled “Example”use gemini_adk_fluent_rs::compose::S;
// Chain transforms: pick keys, then renamelet transform = S::pick(&["name", "age"]) >> S::rename(&[("name", "customer_name")]);
let mut state = json!({"name": "Alice", "age": 30, "internal_id": "x123"});transform.apply(&mut state);// Result: {"customer_name": "Alice", "age": 30}
// Predicates for phase transitionsLive::builder() .phase("verify") .instruction("Verify the customer's identity") .transition("main", S::is_true("verified")) .done() .phase("main") .instruction("Handle the request") .transition("billing", S::eq("issue_type", "billing")) .transition("tech", S::one_of("issue_type", &["technical", "setup"])) .done()C — Context Engineering
Section titled “C — Context Engineering”Context policies filter and transform conversation history. Compose them with + to combine multiple policies.
Methods
Section titled “Methods”| Method | What it does |
|---|---|
C::window(n) | Keep only the last n messages |
C::head(n) | Keep only the first n messages |
C::user_only() | Keep only user messages |
C::model_only() | Keep only model messages |
C::text_only() | Keep only messages containing text parts |
C::exclude_tools() | Remove messages with function call/response parts |
C::sample(n) | Keep every n-th message |
C::truncate(max_chars) | Truncate to approximately max_chars total text (keeps most recent) |
C::prepend(content) | Add a message at the start of context |
C::append(content) | Add a message at the end of context |
C::from_state(&["key1", "key2"]) | Inject state values as a context preamble |
C::dedup() | Remove adjacent duplicate messages |
C::empty() | Return empty context (for isolated agents) |
C::filter(fn) | Filter messages by a custom predicate on Content |
C::map(fn) | Transform each message with a custom function |
C::custom(fn) | Full custom filter over the entire history |
Example
Section titled “Example”use gemini_adk_fluent_rs::compose::C;
// Keep recent context, no tool noise, inject statelet policy = C::window(20) + C::exclude_tools() + C::from_state(&["user:name", "app:balance"]);
// For isolated sub-agents that should not see conversation historylet isolated = C::empty();
// Character-budget context for cost controllet budget = C::truncate(4000) + C::dedup();T — Tool Composition
Section titled “T — Tool Composition”Compose tools with |. Mix runtime function tools with built-in Gemini tools.
Methods
Section titled “Methods”| Method | What it does |
|---|---|
T::simple(name, desc, fn) | Create a tool from a name, description, and async closure |
T::function(arc_fn) | Register an existing Arc<dyn ToolFunction> |
T::google_search() | Add built-in Google Search |
T::url_context() | Add built-in URL context fetching |
T::code_execution() | Add built-in code execution |
T::toolset(vec) | Combine multiple tool functions into one composite |
Example
Section titled “Example”use gemini_adk_fluent_rs::compose::T;
// Combine custom tools with built-inslet tools = T::simple("get_weather", "Get weather for a city", |args| async move { let city = args["city"].as_str().unwrap_or("Unknown"); Ok(json!({"temp": 22, "city": city})) }) | T::google_search() | T::code_execution();
assert_eq!(tools.len(), 3);
// Use in a Live session builderLive::builder() .tools(tools)P — Prompt Composition
Section titled “P — Prompt Composition”Compose structured prompt sections with +. Each section has a semantic kind that determines its rendering format.
Methods
Section titled “Methods”| Method | Renders as | Kind |
|---|---|---|
P::role("analyst") | "You are analyst." | Role |
P::task("analyze data") | "Your task: analyze data" | Task |
P::constraint("be concise") | "Constraint: be concise" | Constraint |
P::format("JSON") | "Output format: JSON" | Format |
P::example("input", "output") | "Example:\nInput: ...\nOutput: ..." | Example |
P::context("background info") | "Context: background info" | Context |
P::persona("friendly, direct") | "Persona: friendly, direct" | Persona |
P::guidelines(&["be clear", ...]) | "Guidelines:\n- be clear\n- ..." | Guidelines |
P::text("free-form text") | The text as-is | Text |
PromptSection Kinds
Section titled “PromptSection Kinds”The PromptSectionKind enum provides semantic categories:
| Kind | Purpose |
|---|---|
Role | Agent role definition |
Task | Task description |
Constraint | Behavioral constraint |
Format | Output format specification |
Example | Input/output example |
Context | Background context |
Persona | Personality description |
Guidelines | Bulleted guideline list |
Text | Free-form text |
Instruction Modifiers
Section titled “Instruction Modifiers”P also provides instruction modifier factories that bridge the prompt module to the live phase system:
| Method | What it does |
|---|---|
P::show_state(&["key1", "key2"]) | Append selected state keys to the instruction |
P::when(predicate, text) | Conditionally append text based on state |
P::context_fn(fn) | Append dynamic text from a formatting function |
Example
Section titled “Example”use gemini_adk_fluent_rs::compose::P;
// Build a structured promptlet prompt = P::role("a senior financial analyst") + P::task("Review the quarterly earnings report and identify trends") + P::constraint("Use only data from the provided report") + P::constraint("Flag any numbers that seem inconsistent") + P::format("Markdown with headers for each section") + P::guidelines(&[ "Start with an executive summary", "Include specific numbers when citing trends", "End with a risk assessment", ]);
// Render to a single instruction stringlet instruction: String = prompt.into();// "You are a senior financial analyst.\n\nYour task: Review the quarterly...\n\n..."
// Instruction modifiers for phasesLive::builder() .phase("negotiation") .instruction("Negotiate a payment arrangement") .modifiers(vec![ P::show_state(&["emotional_state", "willingness_to_pay"]), P::when( |s| s.get::<String>("risk").unwrap_or_default() == "high", "IMPORTANT: Show extra empathy and offer flexible options.", ), P::context_fn(|s| { let name = s.get::<String>("user:name").unwrap_or_default(); format!("Customer: {name}") }), ]) .done()M — Middleware Composition
Section titled “M — Middleware Composition”Compose middleware layers with |. Middleware intercepts agent events, tool calls, and errors.
Methods
Section titled “Methods”| Method | What it does |
|---|---|
M::log() | Log all agent events |
M::latency() | Track execution latency |
M::timeout(duration) | Enforce a time limit |
M::retry(max) | Retry on failure up to max times |
M::cost() | Track tool call counts as a cost proxy |
M::rate_limit(rps) | Enforce max requests per second |
M::circuit_breaker(threshold) | Open circuit after consecutive failures |
M::trace() | Create distributed tracing spans |
M::audit() | Record all tool calls for review |
M::tap(fn) | Custom event observer |
M::before_tool(fn) | Custom filter before each tool invocation |
M::validate(fn) | Validate tool input arguments |
Example
Section titled “Example”use gemini_adk_fluent_rs::compose::M;use std::time::Duration;
// Production middleware stacklet middleware = M::log() | M::latency() | M::timeout(Duration::from_secs(30)) | M::retry(3) | M::circuit_breaker(5) | M::audit();
assert_eq!(middleware.len(), 6);
// Custom validationlet validated = M::validate(|call| { if call.name == "delete_account" && call.args.get("confirm").is_none() { return Err("delete_account requires 'confirm' argument".into()); } Ok(())});A — Artifact Schemas
Section titled “A — Artifact Schemas”Declare input/output artifact schemas with +. Artifacts describe data that flows between agents as typed, named entities.
Methods
Section titled “Methods”| Method | What it does |
|---|---|
A::output(name, mime, desc) | Declare an output artifact |
A::input(name, mime, desc) | Declare an input artifact |
A::json_output(name, desc) | Shorthand for application/json output |
A::json_input(name, desc) | Shorthand for application/json input |
A::text_output(name, desc) | Shorthand for text/plain output |
A::text_input(name, desc) | Shorthand for text/plain input |
Example
Section titled “Example”use gemini_adk_fluent_rs::compose::A;
// Declare what an analysis agent produces and consumeslet artifacts = A::text_input("source_document", "The document to analyze") + A::json_output("analysis_report", "Structured analysis results") + A::json_output("risk_assessment", "Risk scores and flags");
assert_eq!(artifacts.all_inputs().len(), 1);assert_eq!(artifacts.all_outputs().len(), 2);Composable Operators
Section titled “Composable Operators”Beyond the six namespace modules, the operators module provides structural composition via Rust operators on AgentBuilder:
| Operator | Type | Meaning |
|---|---|---|
>> | Shr | Sequential pipeline |
| | BitOr | Parallel fan-out |
* | Mul<u32> | Fixed-count loop |
* | Mul<LoopPredicate> | Conditional loop |
/ | Div | Fallback chain |
These produce Composable nodes that form a tree. Call .compile(llm) to turn the tree into an executable TextAgent.
use gemini_adk_fluent_rs::prelude::*;
// Build a treelet workflow = AgentBuilder::new("research").instruction("Research the topic") >> (AgentBuilder::new("tech").instruction("Technical review") | AgentBuilder::new("biz").instruction("Business review")) >> AgentBuilder::new("merge").instruction("Merge perspectives");
// Compile and executelet agent = workflow.compile(llm)?;let result = agent.run(&state).await?;The tree auto-flattens: a >> b >> c produces a single Pipeline with 3 steps, not nested pipelines.
Combining Operators into Full Agent Configuration
Section titled “Combining Operators into Full Agent Configuration”The six namespaces compose orthogonally. Each configures a separate dimension:
use gemini_adk_fluent_rs::prelude::*;
// S: transform state before agent sees itlet state_prep = S::pick(&["customer", "order"]) >> S::defaults(json!({"priority": "normal"}));
// C: control what context the agent seeslet context = C::window(10) + C::exclude_tools();
// T: equip the agent with toolslet tools = T::simple("lookup", "Look up order status", |args| async move { Ok(json!({"status": "shipped"})) }) | T::google_search();
// P: compose the instructionlet prompt = P::role("a customer support specialist") + P::task("Help the customer with their order inquiry") + P::constraint("Never reveal internal order IDs") + P::format("Conversational, friendly tone");
// A: declare I/O artifactslet artifacts = A::json_output("resolution", "How the issue was resolved");
// M: add operational middlewarelet middleware = M::log() | M::latency() | M::audit();Builder Integration
Section titled “Builder Integration”AgentBuilder is the entry point for compiling these into executable agents:
use gemini_adk_fluent_rs::builder::AgentBuilder;
let agent = AgentBuilder::new("support") .model(ModelId::FLASH_LATEST) .instruction("Help the customer") // or use P:: composition .temperature(0.5) .google_search() // or use T:: composition .thinking(2048) .writes("resolution") .reads("customer_name") .build(llm)?;
let result = agent.run(&state).await?;Copy-on-write semantics mean every setter returns a new builder. Use builders as templates:
let base = AgentBuilder::new("analyst") .instruction("You are a data analyst") .temperature(0.3);
// Variants share the base configurationlet conservative = base.clone().temperature(0.1);let creative = base.clone().temperature(0.9);Pre-Built Patterns
Section titled “Pre-Built Patterns”The patterns module provides common multi-agent workflows built from these operators:
use gemini_adk_fluent_rs::patterns::*;
// Review loop: worker -> reviewer -> repeat until quality targetlet reviewed = review_loop(writer, reviewer, "quality", "good", 3);
// Cascade: try each agent until one succeedslet robust = cascade(vec![primary, secondary, fallback]);
// Fan-out merge: run all in parallel, merge resultslet multi = fan_out_merge(vec![analyst_a, analyst_b, analyst_c]);
// Supervised: worker -> supervisor -> repeat until approvallet approved = supervised(drafter, supervisor, "approved", 5);
// Map-over: apply agent to every item of the JSON array at state["items"]// (a Composable node — compiles to MapOverTextAgent, composes with >>)let batch = map_over(item_processor, "items");See also
Section titled “See also”- Text Agent Combinators — the full combinator reference (
SequentialTextAgent,ParallelTextAgent, etc.) - Tools — defining tools to equip your agents with
- cookbook 07 — state transforms
- cookbook 08 — prompt composition
- cookbook 09 — tool composition
- cookbook 21 — full algebra