Skip to main content

AxPlaybook

Struct AxPlaybook 

Source
pub struct AxPlaybook<S: AxAIClient, T: AxAIClient> { /* private fields */ }
Expand description

A live, evolving context playbook bound to a program. Mirrors the TypeScript AxPlaybook: grow it offline from examples (evolve), keep it growing online from live feedback (update), render it into the program context (apply_to), and persist/restore it (to_json/load). The evolution engine (ACE) is hidden behind this surface, just as optimize() hides GEPA.

The bound program runs with the student client; the real reflector/curator are focused AxGen sub-programs driven by the teacher client. The program, the clients, the sub-programs, and the last prediction are held behind Rc<RefCell> so the generator/reflector/curator closures installed into AxACE can mutate them without statically aliasing the engine that owns those closures.

Implementations§

Source§

impl<S: AxAIClient + 'static, T: AxAIClient + 'static> AxPlaybook<S, T>

Source

pub fn new( program: AxGen, student: Rc<RefCell<S>>, teacher: Option<Rc<RefCell<T>>>, options: Value, ) -> Self

Source

pub fn from_shared( program: Rc<RefCell<AxGen>>, student: Rc<RefCell<S>>, teacher: Option<Rc<RefCell<T>>>, options: Value, ) -> Self

Build a playbook that SHARES an already-Rc<RefCell>-wrapped program. Used by AxAgent::playbook() so the playbook injects its composed instruction straight into the live pipeline stage instead of a private clone.

Source

pub fn evolve( &mut self, examples: &[Value], metric_fn: &mut dyn FnMut(&Value) -> Value, options: &Value, ) -> AxResult<Value>

Grow the playbook offline from labeled examples, scoring each rollout with metric_fn ({“prediction”, “example”}), then render the result into the bound program. Returns {bestScore, playbook}.

Examples found in repository?
examples/ace_playbook.rs (line 61)
32fn main() -> AxResult<()> {
33    let mut program = ax("question:string -> answer:string")?;
34    program.set_instruction("Answer the question.");
35
36    let student = Rc::new(RefCell::new(ScriptedClient));
37    let mut pb = playbook(
38        program,
39        student,
40        None::<Rc<RefCell<ScriptedClient>>>,
41        json!({"maxEpochs": 1}),
42    );
43
44    let mut metric = |args: &Value| -> Value {
45        let answer = args
46            .get("prediction")
47            .and_then(|p| p.get("answer"))
48            .and_then(Value::as_str)
49            .unwrap_or("");
50        if answer.is_empty() {
51            json!(0.0)
52        } else {
53            json!(1.0)
54        }
55    };
56
57    let examples = vec![
58        json!({"question": "What is Ax?"}),
59        json!({"question": "Why typed signatures?"}),
60    ];
61    let result = pb.evolve(&examples, &mut metric, &json!({}))?;
62    let rendered = pb.render();
63    let state = pb.to_json();
64    assert!(
65        result.get("bestScore").is_some(),
66        "missing bestScore: {result}"
67    );
68    assert!(state.get("playbook").is_some(), "missing playbook: {state}");
69    assert!(state.get("artifact").is_some(), "missing artifact: {state}");
70    println!("rendered: {rendered}");
71    println!("rust-ace-playbook-ok");
72    Ok(())
73}
Source

pub fn evolve_agent<C: AxAIClient>( &mut self, agent: &mut AxAgent, client: &mut C, dataset: &Value, options: &Value, ) -> AxResult<Value>

Verified agent-layer playbook learning from train/validation task sets. The agent is passed explicitly because Rust cannot safely store a self-reference inside the stage-bound playbook handle.

Examples found in repository?
examples/agent_playbook.rs (lines 97-102)
75fn main() -> AxResult<()> {
76    // agent.playbook() binds an evolving context playbook to an agent stage. The
77    // "responder" target grows the user-facing answer stage; ACE remains an
78    // implementation detail behind playbook(), just as optimize() hides GEPA.
79    let mut agent = agent_with_options(
80        "question:string -> answer:string",
81        json!({"name": "qa", "description": "Answer the question.", "runtime": {"language": "Python"}}),
82    )?
83    .with_runtime(Box::new(Runtime))?;
84
85    let student = Rc::new(RefCell::new(ScriptedClient));
86    let mut pb = agent.playbook(
87        student,
88        None::<Rc<RefCell<ScriptedClient>>>,
89        json!({"target": "responder", "maxEpochs": 1}),
90    )?;
91
92    let dataset = json!({"train": [{"input": {"question": "Answer briefly."}, "score": 0}]});
93    let mut eval_client = ScriptedClient;
94
95    // A zero minimum gain exercises verified acceptance. A positive minimum gain
96    // rejects the same flat score and must restore the exact pre-proposal snapshot.
97    let accepted = pb.evolve_agent(
98        &mut agent,
99        &mut eval_client,
100        &dataset,
101        &json!({"verify": true, "minHeldInGain": 0, "maxProposals": 1, "maxMetricCalls": 2}),
102    )?;
103    let before_rejection = serde_json::to_string(&pb.to_json())?;
104    let rejected = pb.evolve_agent(
105        &mut agent,
106        &mut eval_client,
107        &dataset,
108        &json!({"verify": true, "minHeldInGain": 0.1, "maxProposals": 1, "maxMetricCalls": 2}),
109    )?;
110    let after_rejection = serde_json::to_string(&pb.to_json())?;
111
112    assert_eq!(
113        accepted["metricCallsUsed"].as_u64(),
114        Some(2),
115        "bad metric budget: {accepted}"
116    );
117    assert_eq!(
118        accepted["outcomes"][0]["accepted"].as_bool(),
119        Some(true),
120        "verified acceptance failed: {accepted}"
121    );
122    assert_eq!(
123        rejected["metricCallsUsed"].as_u64(),
124        Some(2),
125        "bad metric budget: {rejected}"
126    );
127    assert_eq!(
128        rejected["outcomes"][0]["accepted"].as_bool(),
129        Some(false),
130        "verified rejection failed: {rejected}"
131    );
132    assert_eq!(
133        after_rejection, before_rejection,
134        "rejected proposal was not rolled back exactly"
135    );
136    assert!(
137        pb.to_json().get("playbook").is_some(),
138        "missing playbook: {}",
139        pb.to_json()
140    );
141    println!("accepted: {}", accepted["outcomes"][0]);
142    println!("rejected: {}", rejected["outcomes"][0]);
143    println!("rust-agent-playbook-ok");
144    Ok(())
145}
Source

pub fn update(&mut self, args: &Value) -> AxResult<Value>

Refine the playbook online from a single live interaction. Safe to call without a prior evolve/load; the engine is hydrated lazily on first use.

Source

pub fn apply_to(&mut self, program: Option<&mut AxGen>)

Render the current playbook into a program context (defaults to the bound program). When a different program is passed, its instruction is composed with the rendered playbook in place.

Source

pub fn render(&self) -> String

Return the current playbook as a markdown block.

Examples found in repository?
examples/ace_playbook.rs (line 62)
32fn main() -> AxResult<()> {
33    let mut program = ax("question:string -> answer:string")?;
34    program.set_instruction("Answer the question.");
35
36    let student = Rc::new(RefCell::new(ScriptedClient));
37    let mut pb = playbook(
38        program,
39        student,
40        None::<Rc<RefCell<ScriptedClient>>>,
41        json!({"maxEpochs": 1}),
42    );
43
44    let mut metric = |args: &Value| -> Value {
45        let answer = args
46            .get("prediction")
47            .and_then(|p| p.get("answer"))
48            .and_then(Value::as_str)
49            .unwrap_or("");
50        if answer.is_empty() {
51            json!(0.0)
52        } else {
53            json!(1.0)
54        }
55    };
56
57    let examples = vec![
58        json!({"question": "What is Ax?"}),
59        json!({"question": "Why typed signatures?"}),
60    ];
61    let result = pb.evolve(&examples, &mut metric, &json!({}))?;
62    let rendered = pb.render();
63    let state = pb.to_json();
64    assert!(
65        result.get("bestScore").is_some(),
66        "missing bestScore: {result}"
67    );
68    assert!(state.get("playbook").is_some(), "missing playbook: {state}");
69    assert!(state.get("artifact").is_some(), "missing artifact: {state}");
70    println!("rendered: {rendered}");
71    println!("rust-ace-playbook-ok");
72    Ok(())
73}
Source

pub fn get_state(&self) -> Value

Return a serializable snapshot of the playbook and its history.

Source

pub fn to_json(&self) -> Value

Alias for get_state.

Examples found in repository?
examples/ace_playbook.rs (line 63)
32fn main() -> AxResult<()> {
33    let mut program = ax("question:string -> answer:string")?;
34    program.set_instruction("Answer the question.");
35
36    let student = Rc::new(RefCell::new(ScriptedClient));
37    let mut pb = playbook(
38        program,
39        student,
40        None::<Rc<RefCell<ScriptedClient>>>,
41        json!({"maxEpochs": 1}),
42    );
43
44    let mut metric = |args: &Value| -> Value {
45        let answer = args
46            .get("prediction")
47            .and_then(|p| p.get("answer"))
48            .and_then(Value::as_str)
49            .unwrap_or("");
50        if answer.is_empty() {
51            json!(0.0)
52        } else {
53            json!(1.0)
54        }
55    };
56
57    let examples = vec![
58        json!({"question": "What is Ax?"}),
59        json!({"question": "Why typed signatures?"}),
60    ];
61    let result = pb.evolve(&examples, &mut metric, &json!({}))?;
62    let rendered = pb.render();
63    let state = pb.to_json();
64    assert!(
65        result.get("bestScore").is_some(),
66        "missing bestScore: {result}"
67    );
68    assert!(state.get("playbook").is_some(), "missing playbook: {state}");
69    assert!(state.get("artifact").is_some(), "missing artifact: {state}");
70    println!("rendered: {rendered}");
71    println!("rust-ace-playbook-ok");
72    Ok(())
73}
More examples
Hide additional examples
examples/agent_playbook.rs (line 103)
75fn main() -> AxResult<()> {
76    // agent.playbook() binds an evolving context playbook to an agent stage. The
77    // "responder" target grows the user-facing answer stage; ACE remains an
78    // implementation detail behind playbook(), just as optimize() hides GEPA.
79    let mut agent = agent_with_options(
80        "question:string -> answer:string",
81        json!({"name": "qa", "description": "Answer the question.", "runtime": {"language": "Python"}}),
82    )?
83    .with_runtime(Box::new(Runtime))?;
84
85    let student = Rc::new(RefCell::new(ScriptedClient));
86    let mut pb = agent.playbook(
87        student,
88        None::<Rc<RefCell<ScriptedClient>>>,
89        json!({"target": "responder", "maxEpochs": 1}),
90    )?;
91
92    let dataset = json!({"train": [{"input": {"question": "Answer briefly."}, "score": 0}]});
93    let mut eval_client = ScriptedClient;
94
95    // A zero minimum gain exercises verified acceptance. A positive minimum gain
96    // rejects the same flat score and must restore the exact pre-proposal snapshot.
97    let accepted = pb.evolve_agent(
98        &mut agent,
99        &mut eval_client,
100        &dataset,
101        &json!({"verify": true, "minHeldInGain": 0, "maxProposals": 1, "maxMetricCalls": 2}),
102    )?;
103    let before_rejection = serde_json::to_string(&pb.to_json())?;
104    let rejected = pb.evolve_agent(
105        &mut agent,
106        &mut eval_client,
107        &dataset,
108        &json!({"verify": true, "minHeldInGain": 0.1, "maxProposals": 1, "maxMetricCalls": 2}),
109    )?;
110    let after_rejection = serde_json::to_string(&pb.to_json())?;
111
112    assert_eq!(
113        accepted["metricCallsUsed"].as_u64(),
114        Some(2),
115        "bad metric budget: {accepted}"
116    );
117    assert_eq!(
118        accepted["outcomes"][0]["accepted"].as_bool(),
119        Some(true),
120        "verified acceptance failed: {accepted}"
121    );
122    assert_eq!(
123        rejected["metricCallsUsed"].as_u64(),
124        Some(2),
125        "bad metric budget: {rejected}"
126    );
127    assert_eq!(
128        rejected["outcomes"][0]["accepted"].as_bool(),
129        Some(false),
130        "verified rejection failed: {rejected}"
131    );
132    assert_eq!(
133        after_rejection, before_rejection,
134        "rejected proposal was not rolled back exactly"
135    );
136    assert!(
137        pb.to_json().get("playbook").is_some(),
138        "missing playbook: {}",
139        pb.to_json()
140    );
141    println!("accepted: {}", accepted["outcomes"][0]);
142    println!("rejected: {}", rejected["outcomes"][0]);
143    println!("rust-agent-playbook-ok");
144    Ok(())
145}
Source

pub fn load(&mut self, snapshot: &Value) -> &mut Self

Restore a snapshot into this handle and render it into the bound program.

Source

pub fn configure_auto(&mut self, level: &str)

Set the evolution intensity preset ("light", "medium", or "heavy").

Source

pub fn reset(&mut self)

Clear the playbook back to its initial state.

Source

pub fn set_apply_hook(&mut self, hook: Box<dyn FnMut(&str)>)

Redirect playbook injection. Used to push the rendered playbook into a pipeline stage instead of the bound program.

Auto Trait Implementations§

§

impl<S, T> !RefUnwindSafe for AxPlaybook<S, T>

§

impl<S, T> !Send for AxPlaybook<S, T>

§

impl<S, T> !Sync for AxPlaybook<S, T>

§

impl<S, T> !UnwindSafe for AxPlaybook<S, T>

§

impl<S, T> Freeze for AxPlaybook<S, T>

§

impl<S, T> Unpin for AxPlaybook<S, T>
where S: Unpin, T: Unpin,

§

impl<S, T> UnsafeUnpin for AxPlaybook<S, T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more