Skip to main content

ace_playbook/
ace_playbook.rs

1use axllm::{ax, playbook, AxAIClient, AxResult};
2use serde_json::{json, Value};
3use std::cell::RefCell;
4use std::rc::Rc;
5
6// A scripted client stands in for a real provider so this example runs without a
7// key. Swap it for a real client (e.g. OpenAICompatibleClient) to grow a playbook
8// against a live model. The canned JSON satisfies the bound program AND the
9// playbook's internal reflector/curator sub-programs, so the full ACE loop is
10// exercised offline.
11struct ScriptedClient;
12
13impl AxAIClient for ScriptedClient {
14    fn chat(&mut self, _request: Value) -> AxResult<Value> {
15        let content = json!({
16            "answer": "Ax composes typed LLM programs.",
17            "reasoning": "The playbook lacked a brevity rule.",
18            "errorIdentification": "Answer was too verbose.",
19            "rootCauseAnalysis": "No guidance on conciseness.",
20            "correctApproach": "Add a concise-answer guideline.",
21            "keyInsight": "Prefer one-sentence answers.",
22            "bulletTags": [],
23            "operations": [
24                {"type": "ADD", "section": "Guidelines", "content": "Answer in one concise sentence."}
25            ]
26        })
27        .to_string();
28        Ok(json!({"results": [{"content": content}]}))
29    }
30}
31
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}