@amitdeshmukh/ax-crew 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,329 @@
1
+ /**
2
+ * ACE Feedback Loop Demo - Google Flights Assistant
3
+ *
4
+ * This example demonstrates:
5
+ * - An agent using Google Flights MCP server for real flight searches
6
+ * - ACE learning from user feedback to improve recommendations
7
+ * - Interactive CLI for flight queries
8
+ * - Playbook persistence and display
9
+ *
10
+ * ═══════════════════════════════════════════════════════════════════
11
+ * SETUP: Google Flights MCP Server
12
+ * ═══════════════════════════════════════════════════════════════════
13
+ *
14
+ * This example uses the Google Flights MCP Server:
15
+ * https://github.com/opspawn/Google-Flights-MCP-Server
16
+ *
17
+ * Installation:
18
+ *
19
+ * 1. Clone the MCP server:
20
+ * git clone https://github.com/opspawn/Google-Flights-MCP-Server.git
21
+ * cd Google-Flights-MCP-Server
22
+ *
23
+ * 2. Create virtual environment and install dependencies:
24
+ * python -m venv .venv
25
+ * source .venv/bin/activate # On Windows: .venv\Scripts\activate
26
+ * pip install -r requirements.txt
27
+ *
28
+ * 3. Install Playwright browsers (needed by fast_flights):
29
+ * playwright install
30
+ *
31
+ * 4. Set environment variable and run from activated venv:
32
+ * export GOOGLE_FLIGHTS_SERVER_PATH="/path/to/Google-Flights-MCP-Server/server.py"
33
+ * # Python is auto-detected from your PATH (uses activated venv)
34
+ *
35
+ * Available MCP Tools:
36
+ * - get_flights_on_date: One-way flights for a specific date
37
+ * - get_round_trip_flights: Round-trip flights with departure/return dates
38
+ * - find_all_flights_in_range: Find flights within a date range
39
+ *
40
+ * ═══════════════════════════════════════════════════════════════════
41
+ *
42
+ * Usage: npx tsx examples/ace-feedback-routing.ts
43
+ */
44
+
45
+ import { AxCrew } from "../dist/index.js";
46
+ import { AxCrewFunctions } from "../dist/functions/index.js";
47
+ import type { AxCrewConfig } from "../dist/types.js";
48
+ import type { Provider } from "../dist/types.js";
49
+ import "dotenv/config";
50
+ import * as readline from "readline";
51
+
52
+ // --- 1. Configuration ---
53
+
54
+ // Google Flights MCP Server paths - set via env vars
55
+ // See: https://github.com/opspawn/Google-Flights-MCP-Server
56
+ const GOOGLE_FLIGHTS_SERVER = process.env.GOOGLE_FLIGHTS_SERVER_PATH
57
+ || "/path/to/Google-Flights-MCP-Server/server.py";
58
+ // Uses system python by default - activate your venv before running!
59
+ const GOOGLE_FLIGHTS_PYTHON = process.env.PYTHON_PATH || "python";
60
+
61
+ const crewConfig: AxCrewConfig = {
62
+ crew: [
63
+ {
64
+ name: "FlightAssistant",
65
+ description: `A helpful flight booking assistant that searches Google Flights and provides recommendations.`,
66
+ signature: "query:string -> recommendation:string",
67
+ provider: "google-gemini" as Provider,
68
+ providerKeyName: "GEMINI_API_KEY",
69
+ ai: {
70
+ model: "gemini-flash-latest",
71
+ temperature: 0.7,
72
+ },
73
+ options: {
74
+ debug: false,
75
+ stream: false
76
+ },
77
+ // Google Flights MCP Server - see https://github.com/opspawn/Google-Flights-MCP-Server
78
+ mcpServers: {
79
+ "google-flights": {
80
+ command: GOOGLE_FLIGHTS_PYTHON,
81
+ args: [GOOGLE_FLIGHTS_SERVER],
82
+ env: {}
83
+ }
84
+ },
85
+ // Enable ACE for learning from feedback
86
+ ace: {
87
+ teacher: {
88
+ provider: "google-gemini" as Provider,
89
+ providerKeyName: "GEMINI_API_KEY",
90
+ ai: { model: "gemini-flash-latest" }
91
+ },
92
+ options: {
93
+ maxEpochs: 1,
94
+ allowDynamicSections: true
95
+ },
96
+ persistence: {
97
+ playbookPath: "playbooks/flight-assistant.json",
98
+ autoPersist: true
99
+ },
100
+ metric: { primaryOutputField: "recommendation" },
101
+ compileOnStart: false,
102
+ }
103
+ }
104
+ ]
105
+ };
106
+
107
+ // --- 2. CLI Helper Functions ---
108
+
109
+ const rl = readline.createInterface({
110
+ input: process.stdin,
111
+ output: process.stdout
112
+ });
113
+
114
+ const prompt = (question: string): Promise<string> => {
115
+ return new Promise((resolve) => {
116
+ rl.question(question, (answer) => {
117
+ resolve(answer.trim());
118
+ });
119
+ });
120
+ };
121
+
122
+ const displayPlaybook = (playbook: any, agentName: string) => {
123
+ console.log(`\n📘 ACE Playbook for ${agentName}:`);
124
+ console.log("─".repeat(60));
125
+
126
+ if (!playbook) {
127
+ console.log(" (No playbook yet - will be created after first feedback)");
128
+ console.log("─".repeat(60));
129
+ return;
130
+ }
131
+
132
+ if (playbook.sections) {
133
+ for (const [sectionName, bullets] of Object.entries(playbook.sections)) {
134
+ console.log(`\n 📂 ${sectionName}:`);
135
+ if (Array.isArray(bullets)) {
136
+ bullets.forEach((bullet: any, i: number) => {
137
+ const content = typeof bullet === 'string' ? bullet : bullet.content || JSON.stringify(bullet);
138
+ console.log(` ${i + 1}. ${content}`);
139
+ });
140
+ }
141
+ }
142
+ } else {
143
+ console.log(" " + JSON.stringify(playbook, null, 2).replace(/\n/g, "\n "));
144
+ }
145
+
146
+ if (playbook.updatedAt) {
147
+ console.log(` 🕐 Last updated: ${new Date(playbook.updatedAt).toLocaleString()}`);
148
+ }
149
+
150
+ console.log("─".repeat(60));
151
+ };
152
+
153
+ const displayHelp = () => {
154
+ console.log("\n📋 Example flight queries:");
155
+ console.log(" • Find flights from JFK to LAX on 2025-02-15");
156
+ console.log(" • Round trip from SFO to LHR, leaving 2025-03-01 returning 2025-03-10");
157
+ console.log(" • Cheapest flights from NYC to Tokyo in March 2025");
158
+ console.log("\n📝 Feedback examples to train the assistant:");
159
+ console.log(" • Always show price in USD");
160
+ console.log(" • Prioritize direct flights");
161
+ console.log(" • Include flight duration");
162
+ };
163
+
164
+ // --- 3. Main Interactive Loop ---
165
+
166
+ async function main() {
167
+ console.log("\n✈️ ACE Flight Assistant - Google Flights Edition");
168
+ console.log("═".repeat(60));
169
+ console.log("Search real flights and train the assistant with feedback.");
170
+ console.log("MCP Server: https://github.com/opspawn/Google-Flights-MCP-Server\n");
171
+
172
+ // Check configuration
173
+ const isConfigured = !GOOGLE_FLIGHTS_SERVER.includes("/path/to/");
174
+ if (!isConfigured) {
175
+ console.log("⚠️ MCP Server not configured!");
176
+ console.log(" 1. Clone: git clone https://github.com/opspawn/Google-Flights-MCP-Server.git");
177
+ console.log(" 2. Setup: cd Google-Flights-MCP-Server && python -m venv .venv");
178
+ console.log(" 3. Activate: source .venv/bin/activate");
179
+ console.log(" 4. Install: pip install -r requirements.txt && playwright install");
180
+ console.log(" 5. Export: export GOOGLE_FLIGHTS_SERVER_PATH=/path/to/server.py");
181
+ console.log("\n Demo will continue but flight lookups won't work.\n");
182
+ }
183
+
184
+ // Initialize AxCrew
185
+ const crew = new AxCrew(crewConfig, AxCrewFunctions);
186
+
187
+ try {
188
+ console.log("⏳ Initializing Flight Assistant...");
189
+ await crew.addAgentsToCrew(["FlightAssistant"]);
190
+ const assistant = crew.agents!.get("FlightAssistant")!;
191
+
192
+ console.log("✅ Flight Assistant ready with ACE enabled");
193
+ if (isConfigured) {
194
+ console.log("✅ Google Flights MCP server connected\n");
195
+ }
196
+
197
+ // Show initial playbook (if loaded from persistence)
198
+ const initialPlaybook = (assistant as any).getPlaybook?.();
199
+ displayPlaybook(initialPlaybook, "FlightAssistant");
200
+
201
+ displayHelp();
202
+
203
+ let continueLoop = true;
204
+ let queryCount = 0;
205
+ let feedbackCount = 0;
206
+
207
+ while (continueLoop) {
208
+ console.log(`\n${"═".repeat(60)}`);
209
+ console.log(`✈️ Search #${queryCount + 1}`);
210
+ console.log("═".repeat(60));
211
+
212
+ // Get flight query from user
213
+ const query = await prompt("\n🔍 Flight query (or 'help'/'quit'): ");
214
+
215
+ if (query.toLowerCase() === 'quit' || query.toLowerCase() === 'exit' || query === '') {
216
+ continueLoop = false;
217
+ continue;
218
+ }
219
+
220
+ if (query.toLowerCase() === 'help') {
221
+ displayHelp();
222
+ continue;
223
+ }
224
+
225
+ if (query.toLowerCase() === 'playbook') {
226
+ const currentPlaybook = (assistant as any).getPlaybook?.();
227
+ displayPlaybook(currentPlaybook, "FlightAssistant");
228
+ continue;
229
+ }
230
+
231
+ queryCount++;
232
+ console.log("\n⏳ Searching Google Flights...\n");
233
+
234
+ try {
235
+ // Execute the query
236
+ const result = await assistant.forward({ query });
237
+ const taskId = (result as any)._taskId;
238
+
239
+ console.log("─".repeat(60));
240
+ console.log("🛫 Flight Results:");
241
+ console.log("─".repeat(60));
242
+ console.log(result.recommendation);
243
+ console.log("─".repeat(60));
244
+
245
+ // Get feedback from user
246
+ console.log("\n💬 Train the assistant (Enter feedback or press Enter to skip):");
247
+
248
+ const feedback = await prompt("📝 Feedback: ");
249
+
250
+ if (feedback.toLowerCase() === 'quit' || feedback.toLowerCase() === 'exit') {
251
+ continueLoop = false;
252
+ continue;
253
+ }
254
+
255
+ if (feedback && feedback.length > 0) {
256
+ console.log("\n⏳ Updating ACE playbook...");
257
+
258
+ // Apply feedback via ACE
259
+ if (taskId) {
260
+ await crew.applyTaskFeedback({
261
+ taskId,
262
+ feedback,
263
+ strategy: "all"
264
+ });
265
+ } else {
266
+ await (assistant as any).applyOnlineUpdate?.({
267
+ example: { query },
268
+ prediction: result,
269
+ feedback
270
+ });
271
+ }
272
+
273
+ feedbackCount++;
274
+ console.log("✅ Playbook updated!\n");
275
+
276
+ // Display updated playbook
277
+ const updatedPlaybook = (assistant as any).getPlaybook?.();
278
+ displayPlaybook(updatedPlaybook, "FlightAssistant");
279
+
280
+ console.log("\n💡 The assistant has learned from your feedback!");
281
+ } else {
282
+ console.log("\n⏭️ Skipped feedback.");
283
+ }
284
+
285
+ } catch (error: any) {
286
+ console.error(`\n❌ Error: ${error.message}`);
287
+ if (error.message.includes("MCP") || error.message.includes("transport")) {
288
+ console.log(" Check MCP server configuration.");
289
+ console.log(" See: https://github.com/opspawn/Google-Flights-MCP-Server");
290
+ }
291
+ }
292
+ }
293
+
294
+ // Final summary
295
+ console.log("\n" + "═".repeat(60));
296
+ console.log("📊 Session Summary");
297
+ console.log("═".repeat(60));
298
+ console.log(` Searches: ${queryCount}`);
299
+ console.log(` Feedback given: ${feedbackCount}`);
300
+
301
+ const finalPlaybook = (assistant as any).getPlaybook?.();
302
+ if (finalPlaybook?.sections) {
303
+ const bulletCount = Object.values(finalPlaybook.sections)
304
+ .reduce((acc: number, bullets: any) => acc + (Array.isArray(bullets) ? bullets.length : 0), 0);
305
+ console.log(` Playbook insights: ${bulletCount}`);
306
+ }
307
+ console.log(` Saved to: playbooks/flight-assistant.json`);
308
+
309
+ console.log("\n✈️ Thanks for using ACE Flight Assistant!");
310
+ console.log(" Your preferences are saved for next time.\n");
311
+
312
+ } catch (error: any) {
313
+ console.error("\n❌ Error:", error.message);
314
+ console.log("\nTroubleshooting:");
315
+ console.log("• Ensure OPENAI_API_KEY is set");
316
+ console.log("• For MCP: https://github.com/opspawn/Google-Flights-MCP-Server");
317
+ } finally {
318
+ crew.cleanupOldExecutions(60000);
319
+ crew.destroy();
320
+ rl.close();
321
+ }
322
+ }
323
+
324
+ // --- 4. Run ---
325
+
326
+ main().catch((error) => {
327
+ console.error("Fatal error:", error);
328
+ process.exit(1);
329
+ });
@@ -111,7 +111,6 @@ async function main() {
111
111
  const crew = new AxCrew(
112
112
  crewConfig,
113
113
  AxCrewFunctions,
114
- undefined, // Use default crewId
115
114
  {
116
115
  telemetry: {
117
116
  tracer,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@amitdeshmukh/ax-crew",
4
- "version": "7.0.0",
4
+ "version": "8.0.0",
5
5
  "description": "Build and launch a crew of AI agents with shared state. Built with axllm.dev",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
package/plan.md ADDED
@@ -0,0 +1,255 @@
1
+ # Integrate AxACE into Ax-Crew (per-agent)
2
+
3
+ ### Scope
4
+
5
+ - Add optional AxACE support to `StatefulAxAgent` so each agent can: (a) run offline compile with examples + metric, (b) apply online updates with feedback, and (c) persist playbooks in memory or to file/DB. Reference: [ACE docs](https://axllm.dev/ace/).
6
+ - Add simple execution tracking at crew level for intelligent feedback routing across agent dependency chains.
7
+
8
+ ### Key Design
9
+
10
+ - **Per-agent playbooks**: stored in-memory, optionally persisted via path or callback.
11
+ - **Student/Teacher AIs**: default student = agent's existing `AxAI`; teacher configured per agent (provider + model) or default to student.
12
+ - **Non-breaking**: all ACE fields are optional; no behavior changes unless enabled.
13
+ - **Execution tracking for feedback routing**: Simple in-memory tracking of agent involvement in task execution, enabling feedback distribution across agent dependency chains.
14
+
15
+ ### Architecture Clarification
16
+
17
+ **OpenTelemetry Telemetry** (observability) and **ACE Feedback Routing** (execution tracking) are **separate concerns**:
18
+
19
+ | Feature | Purpose | Where Configured |
20
+ |---------|---------|------------------|
21
+ | **Telemetry** | Observability, tracing, metrics | `AxAI` options via `AxCrewOptions.telemetry` |
22
+ | **Execution Tracking** | Track which agents handled a task for ACE feedback | `AxCrew.executionHistory` in-memory Map |
23
+
24
+ Telemetry is an `AxAI` feature (not `AxAgent`). The Ax framework automatically creates OpenTelemetry spans for all LLM operations when `tracer`/`meter` are passed to `AxAI`.
25
+
26
+ ### Files to Update/Add
27
+
28
+ - Update `src/types.ts`: add `ACEConfig` and optional `ace?: ACEConfig` to `AgentConfig`. ✅ Done
29
+ - **Add `src/agents/ace.ts`**: helper to build optimizer using real `AxACE` from `@ax-llm/ax`, load/save playbooks, run offline/online flows.
30
+ - Update `src/agents/index.ts` (`StatefulAxAgent`): hold `aceConfig`, optional `aceOptimizer`, and methods: `initACE()`, `optimizeOffline(...)`, `applyOnlineUpdate(...)`, `getPlaybook()`, `applyPlaybook(...)`.
31
+ - Update `src/agents/index.ts` (`AxCrew`): add `executionHistory` Map for tracking agent involvement, `applyTaskFeedback()` for routing.
32
+ - Update `src/agents/agentConfig.ts`: parse `ace` block; optionally load initial playbook and pass into agent. ✅ Telemetry already correctly passed to `AxAI`.
33
+ - Add `examples/ace-feedback-routing.ts`: demonstrates execution-based feedback routing across agent dependency chains.
34
+
35
+ ### New Types (in src/types.ts) ✅ Already Implemented
36
+
37
+ ```ts
38
+ export interface ACETeacherConfig {
39
+ provider?: Provider;
40
+ providerKeyName?: string;
41
+ apiURL?: string;
42
+ ai?: AxModelConfig & { model: string };
43
+ providerArgs?: Record<string, unknown>;
44
+ }
45
+
46
+ export interface ACEPersistenceConfig {
47
+ playbookPath?: string;
48
+ initialPlaybook?: Record<string, any>;
49
+ autoPersist?: boolean;
50
+ onPersist?: (pb: any) => Promise<void> | void;
51
+ onLoad?: () => Promise<any> | any;
52
+ }
53
+
54
+ export interface ACEOptionsConfig {
55
+ maxEpochs?: number;
56
+ allowDynamicSections?: boolean;
57
+ tokenBudget?: number;
58
+ reflectorPrompt?: string;
59
+ curatorPrompt?: string;
60
+ }
61
+
62
+ export interface ACEMetricConfig {
63
+ metricFnName?: string;
64
+ primaryOutputField?: string;
65
+ }
66
+
67
+ export interface ACEConfig {
68
+ teacher?: ACETeacherConfig;
69
+ persistence?: ACEPersistenceConfig;
70
+ options?: ACEOptionsConfig;
71
+ metric?: ACEMetricConfig;
72
+ compileOnStart?: boolean;
73
+ }
74
+ ```
75
+
76
+ ### Helper Module (src/agents/ace.ts)
77
+
78
+ ```ts
79
+ import { AxACE, type AxMetricFn } from "@ax-llm/ax";
80
+ import { ai as buildAI } from "@ax-llm/ax";
81
+ import type { ACEConfig, ACEPersistenceConfig, ACEMetricConfig, FunctionRegistryType } from "../types.js";
82
+ import type { StatefulAxAgent } from "./index.js";
83
+
84
+ // Build AxACE optimizer with student (agent's AI) and optional teacher
85
+ export function buildACEOptimizer(
86
+ studentAI: any,
87
+ cfg: ACEConfig
88
+ ): AxACE {
89
+ const teacherAI = buildTeacherAI(cfg.teacher, studentAI);
90
+ return new AxACE(
91
+ { studentAI, teacherAI, verbose: cfg.options?.maxEpochs ? true : false },
92
+ {
93
+ maxEpochs: cfg.options?.maxEpochs,
94
+ allowDynamicSections: cfg.options?.allowDynamicSections,
95
+ initialPlaybook: cfg.persistence?.initialPlaybook
96
+ }
97
+ );
98
+ }
99
+
100
+ // Load playbook from file or callback
101
+ export async function loadInitialPlaybook(cfg?: ACEPersistenceConfig): Promise<any | undefined> { /*...*/ }
102
+
103
+ // Persist playbook to file or callback
104
+ export async function persistPlaybook(pb: any, cfg?: ACEPersistenceConfig): Promise<void> { /*...*/ }
105
+
106
+ // Resolve metric function from registry or create equality metric
107
+ export function resolveMetric(cfg: ACEMetricConfig | undefined, registry: FunctionRegistryType): AxMetricFn | undefined { /*...*/ }
108
+
109
+ // Run offline compile
110
+ export async function runOfflineCompile(args: {
111
+ program: any;
112
+ optimizer: AxACE;
113
+ metric: AxMetricFn;
114
+ examples: any[];
115
+ persistence?: ACEPersistenceConfig;
116
+ }): Promise<any> { /*...*/ }
117
+
118
+ // Run online update
119
+ export async function runOnlineUpdate(args: {
120
+ optimizer: AxACE;
121
+ example: any;
122
+ prediction: any;
123
+ feedback?: string;
124
+ persistence?: ACEPersistenceConfig;
125
+ }): Promise<any> { /*...*/ }
126
+ ```
127
+
128
+ ### Agent Class Changes (minimal API)
129
+
130
+ ```ts
131
+ class StatefulAxAgent extends AxAgent<any, any> {
132
+ private aceConfig?: ACEConfig;
133
+ private aceOptimizer?: AxACE;
134
+ private acePlaybook?: any;
135
+
136
+ async initACE(ace?: ACEConfig): Promise<void> { /* build optimizer, load playbook */ }
137
+ async optimizeOffline(params?: { metric?: AxMetricFn; examples?: any[] }): Promise<void> { /* compile, persist */ }
138
+ async applyOnlineUpdate(params: { example: any; prediction: any; feedback?: string }): Promise<void> { /* update + persist */ }
139
+ getPlaybook(): any | undefined { return this.acePlaybook; }
140
+ applyPlaybook(pb: any): void { /* apply to optimizer */ }
141
+ }
142
+ ```
143
+
144
+ **Note**: No telemetry fields in agent - telemetry is handled by `AxAI`.
145
+
146
+ ### Crew-Level Execution Tracking for Feedback Routing
147
+
148
+ **Problem Solved**: "How does the crew know which agent to pass online feedback to?"
149
+
150
+ **Solution**: Simple in-memory execution history (not OpenTelemetry - that's for observability):
151
+
152
+ ```ts
153
+ class AxCrew {
154
+ // Track agent execution for ACE feedback routing
155
+ private executionHistory: Map<string, {
156
+ taskId: string;
157
+ rootAgent: string;
158
+ involvedAgents: Set<string>;
159
+ taskInput: any;
160
+ results: Map<string, any>;
161
+ startTime: number;
162
+ endTime?: number;
163
+ }> = new Map();
164
+
165
+ // Track agent involvement during execution
166
+ trackAgentExecution(taskId: string, agentName: string, input: any): void { /*...*/ }
167
+ recordAgentResult(taskId: string, agentName: string, result: any): void { /*...*/ }
168
+
169
+ // Get involvement info for feedback routing
170
+ getTaskAgentInvolvement(taskId: string): AgentInvolvement | null { /*...*/ }
171
+
172
+ // Route feedback to involved agents
173
+ async applyTaskFeedback(params: {
174
+ taskId: string;
175
+ feedback: string;
176
+ strategy?: 'all' | 'primary' | 'weighted';
177
+ }): Promise<void> { /*...*/ }
178
+
179
+ // Cleanup old entries
180
+ cleanupOldExecutions(maxAgeMs?: number): void { /*...*/ }
181
+ }
182
+ ```
183
+
184
+ ### Agent Config Parsing
185
+
186
+ In `parseAgentConfig(...)`:
187
+ - Telemetry (`tracer`, `meter`) is passed to `AxAI` options ✅ Already implemented
188
+ - If `agentConfigData.ace` is present, the agent's `initACE()` is called during creation
189
+ - If `compileOnStart` and a usable metric is available, run offline compile with `examples`
190
+
191
+ ### Minimal Usage
192
+
193
+ ```ts
194
+ const config: AxCrewConfig = {
195
+ crew: [
196
+ {
197
+ name: "writer",
198
+ description: "Writes articles",
199
+ signature: "topic:string -> article:string",
200
+ provider: "openai",
201
+ providerKeyName: "OPENAI_API_KEY",
202
+ ai: { model: "gpt-4o-mini" },
203
+ ace: {
204
+ enabled: true,
205
+ teacher: { provider: "openai", providerKeyName: "OPENAI_API_KEY", ai: { model: "gpt-4o" } },
206
+ options: { maxEpochs: 1, allowDynamicSections: true },
207
+ persistence: { playbookPath: "playbooks/writer.json", autoPersist: true },
208
+ metric: { primaryOutputField: "article" },
209
+ compileOnStart: false,
210
+ },
211
+ },
212
+ ],
213
+ };
214
+
215
+ // Initialize crew (telemetry is for observability, separate from ACE)
216
+ const crew = new AxCrew(config, AxCrewFunctions, {
217
+ telemetry: { tracer, meter } // Optional: for OpenTelemetry observability
218
+ });
219
+
220
+ await crew.addAgentsToCrew(["writer"]);
221
+ const writer = crew.agents?.get("writer");
222
+
223
+ // Manual per-agent ACE operations
224
+ await writer?.optimizeOffline();
225
+ const prediction = await writer?.forward({ topic: "Quantum" });
226
+ await writer?.applyOnlineUpdate({
227
+ example: { topic: "Quantum" },
228
+ prediction,
229
+ feedback: "Too verbose."
230
+ });
231
+
232
+ // Crew-level feedback routing (for multi-agent tasks)
233
+ // The forward() call returns a taskId that can be used for feedback
234
+ const result = await writer.forward({ topic: "AI Ethics" });
235
+ await crew.applyTaskFeedback({
236
+ taskId: result._taskId, // Returned by forward()
237
+ feedback: "More balanced perspective needed",
238
+ strategy: "all" // Route to all involved agents
239
+ });
240
+ ```
241
+
242
+ ### Benefits
243
+
244
+ - **Separation of Concerns**: Telemetry (observability) vs Execution Tracking (ACE feedback)
245
+ - **Zero Breaking Changes**: All ACE features are opt-in
246
+ - **Uses Real AxACE**: Imports actual `AxACE` class from `@ax-llm/ax`
247
+ - **Simple Feedback Routing**: In-memory tracking without OpenTelemetry dependency
248
+ - **Flexible**: Supports per-agent and crew-level feedback
249
+
250
+ ### Notes
251
+
252
+ - Persistence is optional; in-memory playbooks work fine. Save to file/DB only if you want reuse after restarts.
253
+ - Metric configuration is required for offline compile; online updates don't need a metric.
254
+ - Defaults are conservative: ACE is only active when `ace` config is present.
255
+ - Telemetry and ACE are independent features - you can use one without the other.
@@ -0,0 +1,32 @@
1
+ {
2
+ "sections": {
3
+ "Guidelines": [
4
+ {
5
+ "id": "guidel-9507499a",
6
+ "section": "Guidelines",
7
+ "content": "For returning customers who have spent $15000 over 8 years, exercise compassion and allow a refund.",
8
+ "helpfulCount": 1,
9
+ "harmfulCount": 0,
10
+ "createdAt": "2026-01-13T13:07:18.958Z",
11
+ "updatedAt": "2026-01-13T13:07:18.958Z"
12
+ },
13
+ {
14
+ "id": "guidel-8b3abea4",
15
+ "section": "Guidelines",
16
+ "content": "Always refund orders that have incurred shipping damage.",
17
+ "helpfulCount": 1,
18
+ "harmfulCount": 0,
19
+ "createdAt": "2026-01-17T10:00:30.736Z",
20
+ "updatedAt": "2026-01-17T10:00:30.736Z"
21
+ }
22
+ ]
23
+ },
24
+ "createdAt": null,
25
+ "updatedAt": "2026-01-17T10:00:30.736Z",
26
+ "stats": {
27
+ "bulletCount": 2,
28
+ "helpfulCount": 2,
29
+ "harmfulCount": 0,
30
+ "tokenEstimate": 39
31
+ }
32
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "version": 1,
3
+ "sections": {
4
+ "Guidelines": [
5
+ {
6
+ "id": "guidel-541925c9",
7
+ "section": "Guidelines",
8
+ "content": "Only show non-stop flights in the search results.",
9
+ "helpfulCount": 1,
10
+ "harmfulCount": 0,
11
+ "createdAt": "2025-12-30T03:44:39.741Z",
12
+ "updatedAt": "2025-12-30T03:44:39.741Z"
13
+ }
14
+ ]
15
+ },
16
+ "stats": {
17
+ "bulletCount": 1,
18
+ "helpfulCount": 1,
19
+ "harmfulCount": 0,
20
+ "tokenEstimate": 13
21
+ },
22
+ "updatedAt": "2025-12-30T03:44:39.743Z"
23
+ }