@amitdeshmukh/ax-crew 6.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.
- package/.claude/settings.local.json +7 -0
- package/CHANGELOG.md +35 -0
- package/README.md +240 -0
- package/dist/agents/ace.d.ts +134 -0
- package/dist/agents/ace.js +477 -0
- package/dist/agents/agentConfig.d.ts +3 -2
- package/dist/agents/agentConfig.js +6 -2
- package/dist/agents/index.d.ts +86 -2
- package/dist/agents/index.js +364 -6
- package/dist/index.d.ts +3 -3
- package/dist/types.d.ts +52 -1
- package/examples/README.md +46 -8
- package/examples/ace-customer-support.ts +480 -0
- package/examples/ace-flight-finder.ts +329 -0
- package/examples/telemetry-demo.ts +165 -0
- package/package.json +3 -2
- package/plan.md +255 -0
- package/playbooks/customer-support.json +32 -0
- package/playbooks/flight-assistant.json +23 -0
- package/src/agents/ace.ts +594 -0
- package/src/agents/agentConfig.ts +8 -2
- package/src/agents/index.ts +416 -8
- package/src/index.ts +14 -2
- package/src/types.ts +67 -1
- package/tests/telemetry.test.ts +81 -0
package/dist/agents/index.js
CHANGED
|
@@ -10,6 +10,13 @@ class StatefulAxAgent extends AxAgent {
|
|
|
10
10
|
agentName;
|
|
11
11
|
costTracker;
|
|
12
12
|
lastRecordedCostUSD = 0;
|
|
13
|
+
debugEnabled = false;
|
|
14
|
+
// ACE-related optional state
|
|
15
|
+
aceConfig;
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
aceOptimizer;
|
|
18
|
+
acePlaybook;
|
|
19
|
+
aceBaseInstruction; // Original description before playbook injection
|
|
13
20
|
isAxAIService(obj) {
|
|
14
21
|
return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
|
|
15
22
|
}
|
|
@@ -17,7 +24,7 @@ class StatefulAxAgent extends AxAgent {
|
|
|
17
24
|
return !!obj && typeof obj === 'object' && ('defaults' in obj || 'modelInfo' in obj);
|
|
18
25
|
}
|
|
19
26
|
constructor(ai, options, state) {
|
|
20
|
-
const { examples, ...restOptions } = options;
|
|
27
|
+
const { examples, debug, ...restOptions } = options;
|
|
21
28
|
const formattedOptions = {
|
|
22
29
|
...restOptions,
|
|
23
30
|
functions: restOptions.functions?.map((fn) => typeof fn === "function" ? fn() : fn),
|
|
@@ -26,6 +33,7 @@ class StatefulAxAgent extends AxAgent {
|
|
|
26
33
|
this.state = state;
|
|
27
34
|
this.axai = ai;
|
|
28
35
|
this.agentName = options.name;
|
|
36
|
+
this.debugEnabled = debug ?? false;
|
|
29
37
|
// Set examples if provided
|
|
30
38
|
if (examples && examples.length > 0) {
|
|
31
39
|
super.setExamples(examples);
|
|
@@ -37,8 +45,34 @@ class StatefulAxAgent extends AxAgent {
|
|
|
37
45
|
const start = performance.now();
|
|
38
46
|
const crewId = this.state?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
39
47
|
const labels = { crewId, agent: this.agentName };
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
const input = this.isAxAIService(first) ? second : first;
|
|
49
|
+
const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
50
|
+
// Track execution context in crew for ACE feedback routing
|
|
51
|
+
const crewInstance = this.state?.crew;
|
|
52
|
+
if (crewInstance) {
|
|
53
|
+
if (this.isAxAIService(first)) {
|
|
54
|
+
// For sub-agent calls, track under parent task ID
|
|
55
|
+
const parentTaskId = this.state?.currentTaskId;
|
|
56
|
+
if (parentTaskId) {
|
|
57
|
+
crewInstance.trackAgentExecution(parentTaskId, this.agentName, input);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Root-level call - start new execution tracking
|
|
62
|
+
crewInstance.trackAgentExecution(taskId, this.agentName, input);
|
|
63
|
+
this.state.currentTaskId = taskId;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Before forward: compose instruction with current playbook (mirrors AxACE.compile behavior)
|
|
67
|
+
// This ensures the agent uses the latest playbook context for this call
|
|
68
|
+
if (this.debugEnabled) {
|
|
69
|
+
console.log(`[ACE Debug] forward() called, aceConfig=${!!this.aceConfig}`);
|
|
70
|
+
}
|
|
71
|
+
if (this.aceConfig) {
|
|
72
|
+
await this.composeInstructionWithPlaybook();
|
|
73
|
+
}
|
|
74
|
+
// Execute the forward call
|
|
75
|
+
// Note: OpenTelemetry spans are automatically created by AxAI (configured via AxCrewOptions.telemetry)
|
|
42
76
|
if (this.isAxAIService(first)) {
|
|
43
77
|
// Sub-agent case (called with AI service)
|
|
44
78
|
result = await super.forward(this.axai, second, third);
|
|
@@ -83,6 +117,24 @@ class StatefulAxAgent extends AxAgent {
|
|
|
83
117
|
}
|
|
84
118
|
catch { }
|
|
85
119
|
}
|
|
120
|
+
// Record result in crew execution history for ACE feedback routing
|
|
121
|
+
if (crewInstance) {
|
|
122
|
+
if (this.isAxAIService(first)) {
|
|
123
|
+
// For sub-agent calls, record under parent task ID
|
|
124
|
+
const parentTaskId = this.state?.currentTaskId;
|
|
125
|
+
if (parentTaskId) {
|
|
126
|
+
crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Root-level result - include taskId for feedback routing
|
|
131
|
+
crewInstance.recordAgentResult(taskId, this.agentName, result);
|
|
132
|
+
// Clean up current task ID
|
|
133
|
+
delete this.state.currentTaskId;
|
|
134
|
+
// Attach taskId to result for feedback routing convenience
|
|
135
|
+
result._taskId = taskId;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
86
138
|
return result;
|
|
87
139
|
}
|
|
88
140
|
// Implementation
|
|
@@ -174,6 +226,196 @@ class StatefulAxAgent extends AxAgent {
|
|
|
174
226
|
const crewId = this.state?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
175
227
|
MetricsRegistry.reset({ crewId, agent: this.agentName });
|
|
176
228
|
}
|
|
229
|
+
// =============
|
|
230
|
+
// ACE API - Agentic Context Engineering for online learning
|
|
231
|
+
// Reference: https://axllm.dev/ace/
|
|
232
|
+
// =============
|
|
233
|
+
/**
|
|
234
|
+
* Initialize ACE (Agentic Context Engineering) for this agent.
|
|
235
|
+
* Builds the optimizer and loads any initial playbook from persistence.
|
|
236
|
+
* Sets up the optimizer for online-only mode if compileOnStart is false.
|
|
237
|
+
*/
|
|
238
|
+
async initACE(ace) {
|
|
239
|
+
this.aceConfig = ace;
|
|
240
|
+
if (!ace)
|
|
241
|
+
return;
|
|
242
|
+
try {
|
|
243
|
+
// Capture base instruction BEFORE any playbook injection (mirrors AxACE.extractProgramInstruction)
|
|
244
|
+
this.aceBaseInstruction = this.getSignature().getDescription() || '';
|
|
245
|
+
const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
246
|
+
// Build optimizer with agent's AI as student
|
|
247
|
+
this.aceOptimizer = buildACEOptimizer(this.axai, ace);
|
|
248
|
+
// For online-only mode (no offline compile), we need to set the program
|
|
249
|
+
// reference so applyOnlineUpdate can work. AxACE requires compile() to
|
|
250
|
+
// set the program, but we can set it directly for online-only use.
|
|
251
|
+
if (!ace.compileOnStart) {
|
|
252
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
253
|
+
this.aceOptimizer.program = this;
|
|
254
|
+
}
|
|
255
|
+
// Load initial playbook or create empty one
|
|
256
|
+
const initial = await loadInitialPlaybook(ace.persistence);
|
|
257
|
+
this.applyPlaybook(initial ?? createEmptyPlaybook());
|
|
258
|
+
if (this.debugEnabled) {
|
|
259
|
+
console.log(`[ACE Debug] Initialized for ${this.agentName}, base instruction: ${this.aceBaseInstruction?.slice(0, 50)}...`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
console.warn(`Failed to initialize ACE for agent ${this.agentName}:`, error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Run offline ACE compilation with examples and metric.
|
|
268
|
+
* Compiles the playbook based on training examples.
|
|
269
|
+
*/
|
|
270
|
+
async optimizeOffline(params) {
|
|
271
|
+
if (!this.aceConfig || !this.aceOptimizer)
|
|
272
|
+
return;
|
|
273
|
+
try {
|
|
274
|
+
const { runOfflineCompile, resolveMetric } = await import('./ace.js');
|
|
275
|
+
const registry = this.__functionsRegistry;
|
|
276
|
+
const metric = params?.metric || resolveMetric(this.aceConfig.metric, registry || {});
|
|
277
|
+
const examples = params?.examples || [];
|
|
278
|
+
if (!metric || examples.length === 0) {
|
|
279
|
+
console.warn(`ACE offline compile skipped for ${this.agentName}: missing metric or examples`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const result = await runOfflineCompile({
|
|
283
|
+
program: this,
|
|
284
|
+
optimizer: this.aceOptimizer,
|
|
285
|
+
metric,
|
|
286
|
+
examples,
|
|
287
|
+
persistence: this.aceConfig.persistence
|
|
288
|
+
});
|
|
289
|
+
// Apply optimized playbook if compilation succeeded
|
|
290
|
+
if (result?.artifact?.playbook) {
|
|
291
|
+
await this.applyPlaybook(result.artifact.playbook);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
console.warn(`ACE offline compile failed for ${this.agentName}:`, error);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Apply online ACE update based on user feedback.
|
|
300
|
+
*
|
|
301
|
+
* For preference-based feedback (e.g., "only show flights between 9am-12pm"),
|
|
302
|
+
* we use our own feedback analyzer that preserves specificity.
|
|
303
|
+
*
|
|
304
|
+
* Note: AxACE's built-in curator is designed for error correction (severity mismatches)
|
|
305
|
+
* and tends to over-abstract preference feedback into generic guidelines.
|
|
306
|
+
* We bypass it and directly use our feedback analyzer for better results.
|
|
307
|
+
*/
|
|
308
|
+
async applyOnlineUpdate(params) {
|
|
309
|
+
if (!this.aceConfig)
|
|
310
|
+
return;
|
|
311
|
+
if (!params.feedback?.trim())
|
|
312
|
+
return; // Nothing to do without feedback
|
|
313
|
+
try {
|
|
314
|
+
const { persistPlaybook, addFeedbackToPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
315
|
+
// Get or create playbook
|
|
316
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
317
|
+
let playbook = this.acePlaybook ?? this.aceOptimizer?.playbook;
|
|
318
|
+
if (!playbook) {
|
|
319
|
+
playbook = createEmptyPlaybook();
|
|
320
|
+
}
|
|
321
|
+
if (this.debugEnabled) {
|
|
322
|
+
console.log(`[ACE Debug] Adding feedback to playbook: "${params.feedback}"`);
|
|
323
|
+
}
|
|
324
|
+
// Use teacher AI (or student AI as fallback) for smart categorization
|
|
325
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
326
|
+
const teacherAI = this.aceOptimizer?.teacherAI;
|
|
327
|
+
const aiForAnalysis = teacherAI ?? this.axai;
|
|
328
|
+
// Directly add feedback to playbook using our analyzer (preserves specificity)
|
|
329
|
+
await addFeedbackToPlaybook(playbook, params.feedback, aiForAnalysis, this.debugEnabled);
|
|
330
|
+
// Store updated playbook (injection happens in next forward() call)
|
|
331
|
+
this.applyPlaybook(playbook);
|
|
332
|
+
// Sync with optimizer if available
|
|
333
|
+
if (this.aceOptimizer) {
|
|
334
|
+
this.aceOptimizer.playbook = playbook;
|
|
335
|
+
}
|
|
336
|
+
// Persist if auto-persist enabled
|
|
337
|
+
if (this.aceConfig.persistence?.autoPersist) {
|
|
338
|
+
await persistPlaybook(playbook, this.aceConfig.persistence);
|
|
339
|
+
}
|
|
340
|
+
if (this.debugEnabled) {
|
|
341
|
+
console.log(`[ACE Debug] Playbook updated, sections: ${Object.keys(playbook.sections || {}).join(', ')}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
console.warn(`ACE online update failed for ${this.agentName}:`, error);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Get the current ACE playbook for this agent.
|
|
350
|
+
*/
|
|
351
|
+
getPlaybook() {
|
|
352
|
+
return this.acePlaybook;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Apply an ACE playbook to this agent.
|
|
356
|
+
* Stores the playbook for use in next forward() call.
|
|
357
|
+
* Note: Playbook is composed into instruction BEFORE each forward(), mirroring AxACE.compile behavior.
|
|
358
|
+
*/
|
|
359
|
+
applyPlaybook(pb) {
|
|
360
|
+
this.acePlaybook = pb;
|
|
361
|
+
// Also update optimizer's internal playbook if possible
|
|
362
|
+
try {
|
|
363
|
+
this.aceOptimizer.playbook = pb;
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// Ignore - optimizer may not be initialized yet
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Compose instruction with current playbook and set on agent.
|
|
371
|
+
* This mirrors what AxACE does internally before each forward() during compile().
|
|
372
|
+
* Should be called BEFORE forward() to ensure playbook is in the prompt.
|
|
373
|
+
*/
|
|
374
|
+
async composeInstructionWithPlaybook() {
|
|
375
|
+
const playbook = this.acePlaybook ?? this.aceOptimizer?.playbook;
|
|
376
|
+
if (this.debugEnabled) {
|
|
377
|
+
console.log(`[ACE Debug] composeInstructionWithPlaybook called`);
|
|
378
|
+
console.log(`[ACE Debug] playbook exists: ${!!playbook}, sections: ${playbook ? Object.keys(playbook.sections || {}).length : 0}`);
|
|
379
|
+
console.log(`[ACE Debug] baseInstruction: "${this.aceBaseInstruction?.slice(0, 50)}..."`);
|
|
380
|
+
}
|
|
381
|
+
if (!playbook)
|
|
382
|
+
return;
|
|
383
|
+
try {
|
|
384
|
+
const { renderPlaybook } = await import('./ace.js');
|
|
385
|
+
const rendered = renderPlaybook(playbook);
|
|
386
|
+
if (this.debugEnabled) {
|
|
387
|
+
console.log(`[ACE Debug] rendered playbook (${rendered.length} chars): ${rendered.slice(0, 100)}...`);
|
|
388
|
+
}
|
|
389
|
+
if (!rendered)
|
|
390
|
+
return;
|
|
391
|
+
// Compose: base instruction + playbook (just like AxACE.composeInstruction)
|
|
392
|
+
const baseInstruction = this.aceBaseInstruction || '';
|
|
393
|
+
const combinedInstruction = [baseInstruction.trim(), '', rendered]
|
|
394
|
+
.filter((part) => part.trim().length > 0)
|
|
395
|
+
.join('\n\n');
|
|
396
|
+
if (this.debugEnabled) {
|
|
397
|
+
console.log(`[ACE Debug] combinedInstruction (${combinedInstruction.length} chars)`);
|
|
398
|
+
}
|
|
399
|
+
if (combinedInstruction.length >= 20) {
|
|
400
|
+
// Call setDescription on the internal program (like AxACE does)
|
|
401
|
+
// AxAgent.setDescription() only updates the signature, but we need
|
|
402
|
+
// to update the program's description which is used for the system prompt
|
|
403
|
+
const program = this.program;
|
|
404
|
+
if (program?.setDescription) {
|
|
405
|
+
program.setDescription(combinedInstruction);
|
|
406
|
+
}
|
|
407
|
+
// Also update via AxAgent's setDescription for consistency
|
|
408
|
+
this.setDescription(combinedInstruction);
|
|
409
|
+
if (this.debugEnabled) {
|
|
410
|
+
console.log(`[ACE Debug] setDescription called successfully`);
|
|
411
|
+
console.log(`[ACE Debug] Verifying - signature desc length: ${this.getSignature().getDescription()?.length}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
console.warn('[ACE Debug] Failed to compose instruction:', error);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
177
419
|
}
|
|
178
420
|
/**
|
|
179
421
|
* AxCrew orchestrates a set of Ax agents that share state,
|
|
@@ -196,17 +438,21 @@ class StatefulAxAgent extends AxAgent {
|
|
|
196
438
|
*/
|
|
197
439
|
class AxCrew {
|
|
198
440
|
crewConfig;
|
|
441
|
+
options;
|
|
199
442
|
functionsRegistry = {};
|
|
200
443
|
crewId;
|
|
201
444
|
agents;
|
|
202
445
|
state;
|
|
446
|
+
// Execution history for ACE feedback routing
|
|
447
|
+
executionHistory = new Map();
|
|
203
448
|
/**
|
|
204
449
|
* Creates an instance of AxCrew.
|
|
205
450
|
* @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
|
|
206
451
|
* @param {FunctionRegistryType} [functionsRegistry={}] - The registry of functions to use in the crew.
|
|
452
|
+
* @param {AxCrewOptions} [options] - Optional settings for the crew (e.g., telemetry).
|
|
207
453
|
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
208
454
|
*/
|
|
209
|
-
constructor(crewConfig, functionsRegistry = {}, crewId = uuidv4()) {
|
|
455
|
+
constructor(crewConfig, functionsRegistry = {}, options, crewId = uuidv4()) {
|
|
210
456
|
// Basic validation of crew configuration
|
|
211
457
|
if (!crewConfig || typeof crewConfig !== 'object' || !('crew' in crewConfig)) {
|
|
212
458
|
throw new Error('Invalid crew configuration');
|
|
@@ -220,6 +466,7 @@ class AxCrew {
|
|
|
220
466
|
this.crewConfig = crewConfig;
|
|
221
467
|
this.functionsRegistry = functionsRegistry;
|
|
222
468
|
this.crewId = crewId;
|
|
469
|
+
this.options = options;
|
|
223
470
|
this.agents = new Map();
|
|
224
471
|
this.state = createState(crewId);
|
|
225
472
|
// Make crewId discoverable to metrics
|
|
@@ -233,7 +480,7 @@ class AxCrew {
|
|
|
233
480
|
*/
|
|
234
481
|
createAgent = async (agentName) => {
|
|
235
482
|
try {
|
|
236
|
-
const agentConfig = await parseAgentConfig(agentName, this.crewConfig, this.functionsRegistry, this.state);
|
|
483
|
+
const agentConfig = await parseAgentConfig(agentName, this.crewConfig, this.functionsRegistry, this.state, this.options);
|
|
237
484
|
// Destructure with type assertion
|
|
238
485
|
const { ai, name, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
|
|
239
486
|
// Get subagents for the AI agent
|
|
@@ -267,6 +514,8 @@ class AxCrew {
|
|
|
267
514
|
}
|
|
268
515
|
}
|
|
269
516
|
// Create an instance of StatefulAxAgent
|
|
517
|
+
// Set crew reference in state for execution tracking (ACE feedback routing)
|
|
518
|
+
const agentState = { ...this.state, crew: this };
|
|
270
519
|
const agent = new StatefulAxAgent(ai, {
|
|
271
520
|
name,
|
|
272
521
|
description,
|
|
@@ -275,8 +524,24 @@ class AxCrew {
|
|
|
275
524
|
functions: uniqueFunctions,
|
|
276
525
|
agents: uniqueSubAgents,
|
|
277
526
|
examples,
|
|
278
|
-
|
|
527
|
+
debug: agentConfig.debug,
|
|
528
|
+
}, agentState);
|
|
279
529
|
agent.costTracker = tracker;
|
|
530
|
+
agent.__functionsRegistry = this.functionsRegistry;
|
|
531
|
+
// Initialize ACE if configured
|
|
532
|
+
try {
|
|
533
|
+
const crewAgent = parseCrewConfig(this.crewConfig).crew.find(a => a.name === name);
|
|
534
|
+
const ace = crewAgent?.ace;
|
|
535
|
+
if (ace) {
|
|
536
|
+
await agent.initACE?.(ace);
|
|
537
|
+
if (ace.compileOnStart) {
|
|
538
|
+
const { resolveMetric } = await import('./ace.js');
|
|
539
|
+
const metric = resolveMetric(ace.metric, this.functionsRegistry);
|
|
540
|
+
await agent.optimizeOffline?.({ metric, examples });
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
catch { }
|
|
280
545
|
return agent;
|
|
281
546
|
}
|
|
282
547
|
catch (error) {
|
|
@@ -394,11 +659,104 @@ class AxCrew {
|
|
|
394
659
|
throw error;
|
|
395
660
|
}
|
|
396
661
|
}
|
|
662
|
+
/**
|
|
663
|
+
* Track agent execution for ACE feedback routing
|
|
664
|
+
*/
|
|
665
|
+
trackAgentExecution(taskId, agentName, input) {
|
|
666
|
+
if (!this.executionHistory.has(taskId)) {
|
|
667
|
+
this.executionHistory.set(taskId, {
|
|
668
|
+
taskId,
|
|
669
|
+
rootAgent: agentName,
|
|
670
|
+
involvedAgents: new Set([agentName]),
|
|
671
|
+
taskInput: input,
|
|
672
|
+
agentResults: new Map(),
|
|
673
|
+
startTime: Date.now()
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
// Add to involved agents if not already present
|
|
678
|
+
const context = this.executionHistory.get(taskId);
|
|
679
|
+
context.involvedAgents.add(agentName);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Record agent result for ACE feedback routing
|
|
684
|
+
*/
|
|
685
|
+
recordAgentResult(taskId, agentName, result) {
|
|
686
|
+
const context = this.executionHistory.get(taskId);
|
|
687
|
+
if (context) {
|
|
688
|
+
context.agentResults.set(agentName, result);
|
|
689
|
+
context.endTime = Date.now();
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get agent involvement for a task (used for ACE feedback routing)
|
|
694
|
+
*/
|
|
695
|
+
getTaskAgentInvolvement(taskId) {
|
|
696
|
+
const context = this.executionHistory.get(taskId);
|
|
697
|
+
if (!context)
|
|
698
|
+
return null;
|
|
699
|
+
return {
|
|
700
|
+
rootAgent: context.rootAgent,
|
|
701
|
+
involvedAgents: Array.from(context.involvedAgents),
|
|
702
|
+
taskInput: context.taskInput,
|
|
703
|
+
agentResults: context.agentResults,
|
|
704
|
+
duration: context.endTime ? context.endTime - context.startTime : undefined
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Apply feedback to agents involved in a task for ACE online learning
|
|
709
|
+
*/
|
|
710
|
+
async applyTaskFeedback(params) {
|
|
711
|
+
const involvement = this.getTaskAgentInvolvement(params.taskId);
|
|
712
|
+
if (!involvement) {
|
|
713
|
+
console.warn(`No execution history found for task ${params.taskId}`);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
const { involvedAgents, taskInput, agentResults } = involvement;
|
|
717
|
+
const strategy = params.strategy || 'all';
|
|
718
|
+
// Determine which agents to update based on strategy
|
|
719
|
+
let agentsToUpdate = [];
|
|
720
|
+
if (strategy === 'primary') {
|
|
721
|
+
agentsToUpdate = [involvement.rootAgent];
|
|
722
|
+
}
|
|
723
|
+
else if (strategy === 'all' || strategy === 'weighted') {
|
|
724
|
+
agentsToUpdate = involvedAgents;
|
|
725
|
+
}
|
|
726
|
+
// Apply feedback to each involved agent
|
|
727
|
+
for (const agentName of agentsToUpdate) {
|
|
728
|
+
const agent = this.agents?.get(agentName);
|
|
729
|
+
if (agent && typeof agent.applyOnlineUpdate === 'function') {
|
|
730
|
+
try {
|
|
731
|
+
await agent.applyOnlineUpdate({
|
|
732
|
+
example: taskInput,
|
|
733
|
+
prediction: agentResults.get(agentName),
|
|
734
|
+
feedback: params.feedback
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
catch (error) {
|
|
738
|
+
console.warn(`Failed to apply ACE feedback to agent ${agentName}:`, error);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Clean up old execution history (call periodically to prevent memory leaks)
|
|
745
|
+
*/
|
|
746
|
+
cleanupOldExecutions(maxAgeMs = 3600000) {
|
|
747
|
+
const cutoffTime = Date.now() - maxAgeMs;
|
|
748
|
+
for (const [taskId, context] of this.executionHistory) {
|
|
749
|
+
if (context.startTime < cutoffTime) {
|
|
750
|
+
this.executionHistory.delete(taskId);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
397
754
|
/**
|
|
398
755
|
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
399
756
|
*/
|
|
400
757
|
destroy() {
|
|
401
758
|
this.agents = null;
|
|
759
|
+
this.executionHistory.clear();
|
|
402
760
|
this.state.reset();
|
|
403
761
|
}
|
|
404
762
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AxCrew } from './agents/index.js';
|
|
2
2
|
import { AxCrewFunctions } from './functions/index.js';
|
|
3
|
-
import type { AxCrewConfig, AgentConfig } from './types.js';
|
|
4
|
-
import type { UsageCost, AggregatedMetrics, AggregatedCosts, StateInstance, FunctionRegistryType } from './types.js';
|
|
3
|
+
import type { AxCrewConfig, AxCrewOptions, AgentConfig } from './types.js';
|
|
4
|
+
import type { UsageCost, AggregatedMetrics, AggregatedCosts, StateInstance, FunctionRegistryType, ACEConfig, ACETeacherConfig, ACEPersistenceConfig, ACEOptionsConfig, ACEMetricConfig } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Metrics types and helpers for request counts, token usage, and estimated cost.
|
|
7
7
|
*
|
|
@@ -29,4 +29,4 @@ export {
|
|
|
29
29
|
/** See class JSDoc on the `AxCrew` implementation. */
|
|
30
30
|
_AxCrew as AxCrew,
|
|
31
31
|
/** Built-in function registry; see file docs in `src/functions/index.ts`. */
|
|
32
|
-
_AxCrewFunctions as AxCrewFunctions, FunctionRegistryType, type AggregatedMetrics, type AggregatedCosts, type AgentConfig, type AxCrewConfig, type StateInstance, type UsageCost, };
|
|
32
|
+
_AxCrewFunctions as AxCrewFunctions, FunctionRegistryType, type AggregatedMetrics, type AggregatedCosts, type AgentConfig, type AxCrewConfig, type AxCrewOptions, type StateInstance, type UsageCost, type ACEConfig, type ACETeacherConfig, type ACEPersistenceConfig, type ACEOptionsConfig, type ACEMetricConfig, };
|
package/dist/types.d.ts
CHANGED
|
@@ -127,6 +127,40 @@ interface MCPStreamableHTTPTransportConfig {
|
|
|
127
127
|
* @property {MCPStdioTransportConfig | MCPHTTPSSETransportConfig | MCPStreambleHTTPTransportConfig} config - The config for the MCP server. Config can be either stdio, http-sse, or streamable http transport.
|
|
128
128
|
*/
|
|
129
129
|
type MCPTransportConfig = MCPStdioTransportConfig | MCPHTTPSSETransportConfig | MCPStreamableHTTPTransportConfig;
|
|
130
|
+
interface ACETeacherConfig {
|
|
131
|
+
provider?: Provider;
|
|
132
|
+
providerKeyName?: string;
|
|
133
|
+
apiURL?: string;
|
|
134
|
+
ai?: AxModelConfig & {
|
|
135
|
+
model: string;
|
|
136
|
+
};
|
|
137
|
+
providerArgs?: Record<string, unknown>;
|
|
138
|
+
}
|
|
139
|
+
interface ACEPersistenceConfig {
|
|
140
|
+
playbookPath?: string;
|
|
141
|
+
initialPlaybook?: Record<string, any>;
|
|
142
|
+
autoPersist?: boolean;
|
|
143
|
+
onPersist?: (pb: any) => Promise<void> | void;
|
|
144
|
+
onLoad?: () => Promise<any> | any;
|
|
145
|
+
}
|
|
146
|
+
interface ACEOptionsConfig {
|
|
147
|
+
maxEpochs?: number;
|
|
148
|
+
allowDynamicSections?: boolean;
|
|
149
|
+
tokenBudget?: number;
|
|
150
|
+
reflectorPrompt?: string;
|
|
151
|
+
curatorPrompt?: string;
|
|
152
|
+
}
|
|
153
|
+
interface ACEMetricConfig {
|
|
154
|
+
metricFnName?: string;
|
|
155
|
+
primaryOutputField?: string;
|
|
156
|
+
}
|
|
157
|
+
interface ACEConfig {
|
|
158
|
+
teacher?: ACETeacherConfig;
|
|
159
|
+
persistence?: ACEPersistenceConfig;
|
|
160
|
+
options?: ACEOptionsConfig;
|
|
161
|
+
metric?: ACEMetricConfig;
|
|
162
|
+
compileOnStart?: boolean;
|
|
163
|
+
}
|
|
130
164
|
/**
|
|
131
165
|
* The configuration for an agent.
|
|
132
166
|
*
|
|
@@ -175,6 +209,8 @@ interface AgentConfig {
|
|
|
175
209
|
agents?: string[];
|
|
176
210
|
examples?: Array<Record<string, any>>;
|
|
177
211
|
mcpServers?: Record<string, MCPTransportConfig>;
|
|
212
|
+
/** Optional AxACE configuration to enable optimization for this agent */
|
|
213
|
+
ace?: ACEConfig;
|
|
178
214
|
}
|
|
179
215
|
/**
|
|
180
216
|
* The configuration object for an AxCrew instance.
|
|
@@ -216,4 +252,19 @@ interface AgentConfig {
|
|
|
216
252
|
interface AxCrewConfig {
|
|
217
253
|
crew: AgentConfig[];
|
|
218
254
|
}
|
|
219
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Options for the AxCrew instance, specifically allowing optional OpenTelemetry injection.
|
|
257
|
+
*
|
|
258
|
+
* @property {Object} [telemetry] - Telemetry configuration.
|
|
259
|
+
* @property {any} [telemetry.tracer] - OpenTelemetry Tracer instance.
|
|
260
|
+
* @property {any} [telemetry.meter] - OpenTelemetry Meter instance.
|
|
261
|
+
*/
|
|
262
|
+
interface AxCrewOptions {
|
|
263
|
+
/** Enable debug logging for ACE and other internal operations */
|
|
264
|
+
debug?: boolean;
|
|
265
|
+
telemetry?: {
|
|
266
|
+
tracer?: any;
|
|
267
|
+
meter?: any;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
export { type AgentConfig, type AxCrewConfig, type AxCrewOptions, type AggregatedMetrics, type StateInstance, type FunctionRegistryType, type MCPStdioTransportConfig, type MCPHTTPSSETransportConfig, type MCPStreamableHTTPTransportConfig, type MCPTransportConfig, type ModelUsage, type ModelInfo, type UsageCost, type AggregatedCosts, type ACEConfig, type ACEMetricConfig, type ACEOptionsConfig, type ACEPersistenceConfig, type ACETeacherConfig };
|
package/examples/README.md
CHANGED
|
@@ -122,18 +122,56 @@ Key features:
|
|
|
122
122
|
- Integration with external APIs via MCP servers
|
|
123
123
|
- Cost tracking across agents
|
|
124
124
|
|
|
125
|
-
### 5.
|
|
126
|
-
[`
|
|
125
|
+
### 5. ACE Flight Assistant (Feedback Learning)
|
|
126
|
+
[`ace-feedback-routing.ts`](./ace-feedback-routing.ts)
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
|
|
128
|
+
Demonstrates ACE (Agentic Context Engineering) learning from user feedback:
|
|
129
|
+
- A flight booking assistant using Google Flights MCP server
|
|
130
|
+
- Real-time playbook updates based on user feedback
|
|
131
|
+
- Persistence of learned preferences across sessions
|
|
132
|
+
- Interactive CLI for flight queries
|
|
133
|
+
|
|
134
|
+
**Required Setup:**
|
|
135
|
+
```bash
|
|
136
|
+
# Clone the Google Flights MCP server
|
|
137
|
+
git clone https://github.com/opspawn/Google-Flights-MCP-Server.git
|
|
138
|
+
cd Google-Flights-MCP-Server
|
|
139
|
+
python -m venv .venv && source .venv/bin/activate
|
|
140
|
+
pip install -r requirements.txt && playwright install
|
|
141
|
+
|
|
142
|
+
# Set environment variable
|
|
143
|
+
export GOOGLE_FLIGHTS_SERVER_PATH=/path/to/server.py
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Key features:
|
|
147
|
+
- ACE feedback loop integration
|
|
148
|
+
- Playbook persistence to JSON
|
|
149
|
+
- Learning from natural language feedback
|
|
150
|
+
|
|
151
|
+
### 6. ACE Customer Support (Edge Case Learning)
|
|
152
|
+
[`ace-customer-support.ts`](./ace-customer-support.ts)
|
|
153
|
+
|
|
154
|
+
Shows how ACE learns to handle edge cases beyond standard policies:
|
|
155
|
+
- A customer support agent with predefined company policies
|
|
156
|
+
- Learning exceptions from supervisor feedback (e.g., "loyal customers get extended returns")
|
|
157
|
+
- Applying learned exceptions to similar future cases
|
|
158
|
+
- Interactive ticket handling demo
|
|
134
159
|
|
|
135
160
|
**Required Dependencies:** None beyond core package
|
|
136
161
|
|
|
162
|
+
```typescript
|
|
163
|
+
// Example feedback that teaches the agent:
|
|
164
|
+
// "For loyal customers (5+ years), extend return window to 60 days"
|
|
165
|
+
// "Medical emergencies justify extended deadlines"
|
|
166
|
+
// "Defective products override 'final sale' policy"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Key features:
|
|
170
|
+
- Edge case learning from feedback
|
|
171
|
+
- Policy exception handling
|
|
172
|
+
- Playbook persistence showing learned rules
|
|
173
|
+
- Sample tickets demonstrating various scenarios
|
|
174
|
+
|
|
137
175
|
## Running the Examples
|
|
138
176
|
|
|
139
177
|
1. Clone the repository:
|