@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.
- package/CHANGELOG.md +17 -0
- package/README.md +104 -0
- package/dist/agents/ace.d.ts +134 -0
- package/dist/agents/ace.js +477 -0
- package/dist/agents/agentConfig.d.ts +1 -0
- package/dist/agents/agentConfig.js +1 -0
- package/dist/agents/index.d.ts +83 -1
- package/dist/agents/index.js +359 -4
- package/dist/index.d.ts +3 -3
- package/dist/types.d.ts +39 -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 +0 -1
- package/package.json +1 -1
- 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 +1 -0
- package/src/agents/index.ts +408 -6
- package/src/index.ts +14 -2
- package/src/types.ts +52 -1
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,
|
|
@@ -201,6 +443,8 @@ class AxCrew {
|
|
|
201
443
|
crewId;
|
|
202
444
|
agents;
|
|
203
445
|
state;
|
|
446
|
+
// Execution history for ACE feedback routing
|
|
447
|
+
executionHistory = new Map();
|
|
204
448
|
/**
|
|
205
449
|
* Creates an instance of AxCrew.
|
|
206
450
|
* @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
|
|
@@ -270,6 +514,8 @@ class AxCrew {
|
|
|
270
514
|
}
|
|
271
515
|
}
|
|
272
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 };
|
|
273
519
|
const agent = new StatefulAxAgent(ai, {
|
|
274
520
|
name,
|
|
275
521
|
description,
|
|
@@ -278,8 +524,24 @@ class AxCrew {
|
|
|
278
524
|
functions: uniqueFunctions,
|
|
279
525
|
agents: uniqueSubAgents,
|
|
280
526
|
examples,
|
|
281
|
-
|
|
527
|
+
debug: agentConfig.debug,
|
|
528
|
+
}, agentState);
|
|
282
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 { }
|
|
283
545
|
return agent;
|
|
284
546
|
}
|
|
285
547
|
catch (error) {
|
|
@@ -397,11 +659,104 @@ class AxCrew {
|
|
|
397
659
|
throw error;
|
|
398
660
|
}
|
|
399
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
|
+
}
|
|
400
754
|
/**
|
|
401
755
|
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
402
756
|
*/
|
|
403
757
|
destroy() {
|
|
404
758
|
this.agents = null;
|
|
759
|
+
this.executionHistory.clear();
|
|
405
760
|
this.state.reset();
|
|
406
761
|
}
|
|
407
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.
|
|
@@ -224,9 +260,11 @@ interface AxCrewConfig {
|
|
|
224
260
|
* @property {any} [telemetry.meter] - OpenTelemetry Meter instance.
|
|
225
261
|
*/
|
|
226
262
|
interface AxCrewOptions {
|
|
263
|
+
/** Enable debug logging for ACE and other internal operations */
|
|
264
|
+
debug?: boolean;
|
|
227
265
|
telemetry?: {
|
|
228
266
|
tracer?: any;
|
|
229
267
|
meter?: any;
|
|
230
268
|
};
|
|
231
269
|
}
|
|
232
|
-
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 };
|
|
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:
|