ace_playbook/
ace_playbook.rs1use axllm::{ax, playbook, AxAIClient, AxResult};
2use serde_json::{json, Value};
3use std::cell::RefCell;
4use std::rc::Rc;
5
6struct 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}