agent_playbook/
agent_playbook.rs1use axllm::{
2 agent_with_options, AxAIClient, AxCodeRuntime, AxCodeSession, AxResult, RuntimeEnvelope,
3};
4use serde_json::{json, Value};
5use std::cell::RefCell;
6use std::rc::Rc;
7
8struct ScriptedClient;
11
12impl AxAIClient for ScriptedClient {
13 fn chat(&mut self, _request: Value) -> AxResult<Value> {
14 let content = json!({
15 "pythonCode": "final('Answer', {'answer': 'Ax composes typed LLM programs.'})",
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 "weaknessDescription": "The agent does not verify its final step.",
23 "rootCause": "The final step is accepted without a check.",
24 "proposedGuidance": "Verify the final step before completing the task.",
25 "evidenceQuotes": ["final", "snapshot", "Answer"],
26 "configRecommendations": [],
27 "bulletTags": [],
28 "operations": [
29 {"type": "ADD", "section": "Guidelines", "content": "Answer in one concise sentence."}
30 ]
31 })
32 .to_string();
33 Ok(json!({"results": [{"content": content}]}))
34 }
35}
36
37struct RuntimeSession;
38
39impl AxCodeSession for RuntimeSession {
40 fn execute(&mut self, code: &str, _options: Value) -> AxResult<RuntimeEnvelope> {
41 assert!(
42 !code.contains("pythonCode"),
43 "runtime received a response wrapper instead of code"
44 );
45 Ok(RuntimeEnvelope::final_payload(
46 json!({"answer": "Ax composes typed LLM programs."}),
47 ))
48 }
49
50 fn snapshot_globals(&mut self, _options: Value) -> AxResult<Value> {
51 Ok(json!({"version": 1, "bindings": {}, "globals": {}, "closed": false}))
52 }
53
54 fn patch_globals(&mut self, snapshot: Value, _options: Value) -> AxResult<Value> {
55 Ok(snapshot)
56 }
57}
58
59struct Runtime;
60
61impl AxCodeRuntime for Runtime {
62 fn language(&self) -> &str {
63 "Python"
64 }
65
66 fn create_session(
67 &mut self,
68 _globals: Value,
69 _options: Value,
70 ) -> AxResult<Box<dyn AxCodeSession>> {
71 Ok(Box::new(RuntimeSession))
72 }
73}
74
75fn main() -> AxResult<()> {
76 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 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}