agent_brain/agent_brain.rs
1//! A tiny decision brain: an assistant deciding what to do with a request.
2//!
3//! It shows the pieces working together — graded considerations, a hard
4//! constraint via `gate`, abstaining via `decide_above`, and an explanation.
5//! Run with: `cargo run -p reliakit-decide --example agent_brain`
6
7use reliakit_decide::{Action, Curve, Reasoner, Score};
8
9fn main() {
10 // Signals about the current request, each normalized to 0.0..=1.0.
11 let urgency = Score::from_ratio(80, 100);
12 let confidence = Score::from_ratio(40, 100); // how well we already understand it
13 let llm_available = true; // e.g. circuit breaker closed AND rate limiter has tokens
14
15 let mut brain = Reasoner::new();
16
17 // Answer from a template — strong when we are already confident.
18 brain.add(Action::new("answer_template").consider_labeled(
19 "confidence",
20 Curve::Linear,
21 confidence,
22 ));
23
24 // Escalate to the LLM — strong when urgent, but only if the LLM is available.
25 brain.add(
26 Action::new("call_llm")
27 .gate(llm_available) // constraint-aware: skipped entirely if the LLM is down/limited
28 .consider_labeled("urgency", Curve::Linear, urgency),
29 );
30
31 // An always-available, low-weight fallback so a decision still resolves.
32 brain.add(Action::new("defer").with_base(Score::from_ratio(1, 10)));
33
34 // Abstain if nothing clears the bar — the caller would escalate to a human.
35 let threshold = Score::from_ratio(5, 100);
36 match brain.decide_above(threshold) {
37 Some(decision) => {
38 println!(
39 "chose: {} (utility {}/10000)",
40 decision.id,
41 decision.utility.raw()
42 );
43 if let Some(why) = brain.explain() {
44 for c in why.contributions {
45 println!(
46 " {} : input {} -> {}",
47 c.label,
48 c.input.raw(),
49 c.output.raw()
50 );
51 }
52 }
53 }
54 None => println!("nothing good enough — escalate to a human"),
55 }
56}