@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/src/agents/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
AxCrewConfig,
|
|
18
18
|
AxCrewOptions,
|
|
19
19
|
MCPTransportConfig,
|
|
20
|
+
ACEConfig,
|
|
20
21
|
} from "../types.js";
|
|
21
22
|
|
|
22
23
|
import { createState } from "../state/index.js";
|
|
@@ -48,6 +49,13 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
48
49
|
private agentName: string;
|
|
49
50
|
private costTracker?: any;
|
|
50
51
|
private lastRecordedCostUSD: number = 0;
|
|
52
|
+
private debugEnabled: boolean = false;
|
|
53
|
+
// ACE-related optional state
|
|
54
|
+
private aceConfig?: ACEConfig;
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
private aceOptimizer?: any;
|
|
57
|
+
private acePlaybook?: any;
|
|
58
|
+
private aceBaseInstruction?: string; // Original description before playbook injection
|
|
51
59
|
private isAxAIService(obj: any): obj is AxAI {
|
|
52
60
|
return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
|
|
53
61
|
}
|
|
@@ -66,10 +74,11 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
66
74
|
functions?: (AxFunction | (() => AxFunction))[] | undefined;
|
|
67
75
|
examples?: Array<Record<string, any>> | undefined;
|
|
68
76
|
mcpServers?: Record<string, MCPTransportConfig> | undefined;
|
|
77
|
+
debug?: boolean;
|
|
69
78
|
}>,
|
|
70
79
|
state: StateInstance
|
|
71
80
|
) {
|
|
72
|
-
const { examples, ...restOptions } = options;
|
|
81
|
+
const { examples, debug, ...restOptions } = options;
|
|
73
82
|
const formattedOptions = {
|
|
74
83
|
...restOptions,
|
|
75
84
|
functions: restOptions.functions?.map((fn) =>
|
|
@@ -80,6 +89,7 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
80
89
|
this.state = state;
|
|
81
90
|
this.axai = ai;
|
|
82
91
|
this.agentName = options.name;
|
|
92
|
+
this.debugEnabled = debug ?? false;
|
|
83
93
|
|
|
84
94
|
// Set examples if provided
|
|
85
95
|
if (examples && examples.length > 0) {
|
|
@@ -98,13 +108,40 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
98
108
|
third?: Readonly<AxProgramForwardOptions<any>>
|
|
99
109
|
): Promise<Record<string, any>> {
|
|
100
110
|
let result;
|
|
101
|
-
|
|
111
|
+
|
|
102
112
|
const start = performance.now();
|
|
103
113
|
const crewId = (this.state as any)?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
104
114
|
const labels = { crewId, agent: this.agentName } as any;
|
|
115
|
+
const input = this.isAxAIService(first) ? second : first;
|
|
116
|
+
const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
117
|
+
|
|
118
|
+
// Track execution context in crew for ACE feedback routing
|
|
119
|
+
const crewInstance = (this.state as any)?.crew as AxCrew;
|
|
120
|
+
if (crewInstance) {
|
|
121
|
+
if (this.isAxAIService(first)) {
|
|
122
|
+
// For sub-agent calls, track under parent task ID
|
|
123
|
+
const parentTaskId = (this.state as any)?.currentTaskId;
|
|
124
|
+
if (parentTaskId) {
|
|
125
|
+
crewInstance.trackAgentExecution(parentTaskId, this.agentName, input);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
// Root-level call - start new execution tracking
|
|
129
|
+
crewInstance.trackAgentExecution(taskId, this.agentName, input);
|
|
130
|
+
(this.state as any).currentTaskId = taskId;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
105
133
|
|
|
106
|
-
//
|
|
107
|
-
// This ensures
|
|
134
|
+
// Before forward: compose instruction with current playbook (mirrors AxACE.compile behavior)
|
|
135
|
+
// This ensures the agent uses the latest playbook context for this call
|
|
136
|
+
if (this.debugEnabled) {
|
|
137
|
+
console.log(`[ACE Debug] forward() called, aceConfig=${!!this.aceConfig}`);
|
|
138
|
+
}
|
|
139
|
+
if (this.aceConfig) {
|
|
140
|
+
await this.composeInstructionWithPlaybook();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Execute the forward call
|
|
144
|
+
// Note: OpenTelemetry spans are automatically created by AxAI (configured via AxCrewOptions.telemetry)
|
|
108
145
|
if (this.isAxAIService(first)) {
|
|
109
146
|
// Sub-agent case (called with AI service)
|
|
110
147
|
result = await super.forward(this.axai, second as Record<string, any>, third);
|
|
@@ -152,6 +189,24 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
152
189
|
} catch {}
|
|
153
190
|
}
|
|
154
191
|
|
|
192
|
+
// Record result in crew execution history for ACE feedback routing
|
|
193
|
+
if (crewInstance) {
|
|
194
|
+
if (this.isAxAIService(first)) {
|
|
195
|
+
// For sub-agent calls, record under parent task ID
|
|
196
|
+
const parentTaskId = (this.state as any)?.currentTaskId;
|
|
197
|
+
if (parentTaskId) {
|
|
198
|
+
crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
// Root-level result - include taskId for feedback routing
|
|
202
|
+
crewInstance.recordAgentResult(taskId, this.agentName, result);
|
|
203
|
+
// Clean up current task ID
|
|
204
|
+
delete (this.state as any).currentTaskId;
|
|
205
|
+
// Attach taskId to result for feedback routing convenience
|
|
206
|
+
result._taskId = taskId;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
155
210
|
return result;
|
|
156
211
|
}
|
|
157
212
|
|
|
@@ -258,6 +313,217 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
258
313
|
MetricsRegistry.reset({ crewId, agent: this.agentName } as any);
|
|
259
314
|
}
|
|
260
315
|
|
|
316
|
+
// =============
|
|
317
|
+
// ACE API - Agentic Context Engineering for online learning
|
|
318
|
+
// Reference: https://axllm.dev/ace/
|
|
319
|
+
// =============
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Initialize ACE (Agentic Context Engineering) for this agent.
|
|
323
|
+
* Builds the optimizer and loads any initial playbook from persistence.
|
|
324
|
+
* Sets up the optimizer for online-only mode if compileOnStart is false.
|
|
325
|
+
*/
|
|
326
|
+
async initACE(ace?: ACEConfig): Promise<void> {
|
|
327
|
+
this.aceConfig = ace;
|
|
328
|
+
if (!ace) return;
|
|
329
|
+
try {
|
|
330
|
+
// Capture base instruction BEFORE any playbook injection (mirrors AxACE.extractProgramInstruction)
|
|
331
|
+
this.aceBaseInstruction = this.getSignature().getDescription() || '';
|
|
332
|
+
|
|
333
|
+
const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
334
|
+
// Build optimizer with agent's AI as student
|
|
335
|
+
this.aceOptimizer = buildACEOptimizer(this.axai, ace);
|
|
336
|
+
|
|
337
|
+
// For online-only mode (no offline compile), we need to set the program
|
|
338
|
+
// reference so applyOnlineUpdate can work. AxACE requires compile() to
|
|
339
|
+
// set the program, but we can set it directly for online-only use.
|
|
340
|
+
if (!ace.compileOnStart) {
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
+
(this.aceOptimizer as any).program = this;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Load initial playbook or create empty one
|
|
346
|
+
const initial = await loadInitialPlaybook(ace.persistence);
|
|
347
|
+
this.applyPlaybook(initial ?? createEmptyPlaybook());
|
|
348
|
+
|
|
349
|
+
if (this.debugEnabled) {
|
|
350
|
+
console.log(`[ACE Debug] Initialized for ${this.agentName}, base instruction: ${this.aceBaseInstruction?.slice(0, 50)}...`);
|
|
351
|
+
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.warn(`Failed to initialize ACE for agent ${this.agentName}:`, error);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Run offline ACE compilation with examples and metric.
|
|
359
|
+
* Compiles the playbook based on training examples.
|
|
360
|
+
*/
|
|
361
|
+
async optimizeOffline(params?: { metric?: any; examples?: any[] }): Promise<void> {
|
|
362
|
+
if (!this.aceConfig || !this.aceOptimizer) return;
|
|
363
|
+
try {
|
|
364
|
+
const { runOfflineCompile, resolveMetric } = await import('./ace.js');
|
|
365
|
+
const registry = (this as any).__functionsRegistry as FunctionRegistryType | undefined;
|
|
366
|
+
const metric = params?.metric || resolveMetric(this.aceConfig.metric, registry || {} as any);
|
|
367
|
+
const examples = params?.examples || [];
|
|
368
|
+
|
|
369
|
+
if (!metric || examples.length === 0) {
|
|
370
|
+
console.warn(`ACE offline compile skipped for ${this.agentName}: missing metric or examples`);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const result = await runOfflineCompile({
|
|
375
|
+
program: this,
|
|
376
|
+
optimizer: this.aceOptimizer,
|
|
377
|
+
metric,
|
|
378
|
+
examples,
|
|
379
|
+
persistence: this.aceConfig.persistence
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Apply optimized playbook if compilation succeeded
|
|
383
|
+
if (result?.artifact?.playbook) {
|
|
384
|
+
await this.applyPlaybook(result.artifact.playbook);
|
|
385
|
+
}
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.warn(`ACE offline compile failed for ${this.agentName}:`, error);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Apply online ACE update based on user feedback.
|
|
393
|
+
*
|
|
394
|
+
* For preference-based feedback (e.g., "only show flights between 9am-12pm"),
|
|
395
|
+
* we use our own feedback analyzer that preserves specificity.
|
|
396
|
+
*
|
|
397
|
+
* Note: AxACE's built-in curator is designed for error correction (severity mismatches)
|
|
398
|
+
* and tends to over-abstract preference feedback into generic guidelines.
|
|
399
|
+
* We bypass it and directly use our feedback analyzer for better results.
|
|
400
|
+
*/
|
|
401
|
+
async applyOnlineUpdate(params: { example: any; prediction: any; feedback?: string }): Promise<void> {
|
|
402
|
+
if (!this.aceConfig) return;
|
|
403
|
+
if (!params.feedback?.trim()) return; // Nothing to do without feedback
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const { persistPlaybook, addFeedbackToPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
407
|
+
|
|
408
|
+
// Get or create playbook
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
410
|
+
let playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
|
|
411
|
+
if (!playbook) {
|
|
412
|
+
playbook = createEmptyPlaybook();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (this.debugEnabled) {
|
|
416
|
+
console.log(`[ACE Debug] Adding feedback to playbook: "${params.feedback}"`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Use teacher AI (or student AI as fallback) for smart categorization
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
421
|
+
const teacherAI = (this.aceOptimizer as any)?.teacherAI;
|
|
422
|
+
const aiForAnalysis = teacherAI ?? this.axai;
|
|
423
|
+
|
|
424
|
+
// Directly add feedback to playbook using our analyzer (preserves specificity)
|
|
425
|
+
await addFeedbackToPlaybook(playbook, params.feedback, aiForAnalysis, this.debugEnabled);
|
|
426
|
+
|
|
427
|
+
// Store updated playbook (injection happens in next forward() call)
|
|
428
|
+
this.applyPlaybook(playbook);
|
|
429
|
+
|
|
430
|
+
// Sync with optimizer if available
|
|
431
|
+
if (this.aceOptimizer) {
|
|
432
|
+
(this.aceOptimizer as any).playbook = playbook;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Persist if auto-persist enabled
|
|
436
|
+
if (this.aceConfig.persistence?.autoPersist) {
|
|
437
|
+
await persistPlaybook(playbook, this.aceConfig.persistence);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (this.debugEnabled) {
|
|
441
|
+
console.log(`[ACE Debug] Playbook updated, sections: ${Object.keys(playbook.sections || {}).join(', ')}`);
|
|
442
|
+
}
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.warn(`ACE online update failed for ${this.agentName}:`, error);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Get the current ACE playbook for this agent.
|
|
450
|
+
*/
|
|
451
|
+
getPlaybook(): any | undefined {
|
|
452
|
+
return this.acePlaybook;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Apply an ACE playbook to this agent.
|
|
457
|
+
* Stores the playbook for use in next forward() call.
|
|
458
|
+
* Note: Playbook is composed into instruction BEFORE each forward(), mirroring AxACE.compile behavior.
|
|
459
|
+
*/
|
|
460
|
+
applyPlaybook(pb: any): void {
|
|
461
|
+
this.acePlaybook = pb;
|
|
462
|
+
|
|
463
|
+
// Also update optimizer's internal playbook if possible
|
|
464
|
+
try {
|
|
465
|
+
(this.aceOptimizer as any).playbook = pb;
|
|
466
|
+
} catch {
|
|
467
|
+
// Ignore - optimizer may not be initialized yet
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Compose instruction with current playbook and set on agent.
|
|
473
|
+
* This mirrors what AxACE does internally before each forward() during compile().
|
|
474
|
+
* Should be called BEFORE forward() to ensure playbook is in the prompt.
|
|
475
|
+
*/
|
|
476
|
+
private async composeInstructionWithPlaybook(): Promise<void> {
|
|
477
|
+
const playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
|
|
478
|
+
|
|
479
|
+
if (this.debugEnabled) {
|
|
480
|
+
console.log(`[ACE Debug] composeInstructionWithPlaybook called`);
|
|
481
|
+
console.log(`[ACE Debug] playbook exists: ${!!playbook}, sections: ${playbook ? Object.keys(playbook.sections || {}).length : 0}`);
|
|
482
|
+
console.log(`[ACE Debug] baseInstruction: "${this.aceBaseInstruction?.slice(0, 50)}..."`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!playbook) return;
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
const { renderPlaybook } = await import('./ace.js');
|
|
489
|
+
const rendered = renderPlaybook(playbook);
|
|
490
|
+
|
|
491
|
+
if (this.debugEnabled) {
|
|
492
|
+
console.log(`[ACE Debug] rendered playbook (${rendered.length} chars): ${rendered.slice(0, 100)}...`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (!rendered) return;
|
|
496
|
+
|
|
497
|
+
// Compose: base instruction + playbook (just like AxACE.composeInstruction)
|
|
498
|
+
const baseInstruction = this.aceBaseInstruction || '';
|
|
499
|
+
const combinedInstruction = [baseInstruction.trim(), '', rendered]
|
|
500
|
+
.filter((part) => part.trim().length > 0)
|
|
501
|
+
.join('\n\n');
|
|
502
|
+
|
|
503
|
+
if (this.debugEnabled) {
|
|
504
|
+
console.log(`[ACE Debug] combinedInstruction (${combinedInstruction.length} chars)`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (combinedInstruction.length >= 20) {
|
|
508
|
+
// Call setDescription on the internal program (like AxACE does)
|
|
509
|
+
// AxAgent.setDescription() only updates the signature, but we need
|
|
510
|
+
// to update the program's description which is used for the system prompt
|
|
511
|
+
const program = (this as any).program;
|
|
512
|
+
if (program?.setDescription) {
|
|
513
|
+
program.setDescription(combinedInstruction);
|
|
514
|
+
}
|
|
515
|
+
// Also update via AxAgent's setDescription for consistency
|
|
516
|
+
this.setDescription(combinedInstruction);
|
|
517
|
+
|
|
518
|
+
if (this.debugEnabled) {
|
|
519
|
+
console.log(`[ACE Debug] setDescription called successfully`);
|
|
520
|
+
console.log(`[ACE Debug] Verifying - signature desc length: ${this.getSignature().getDescription()?.length}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.warn('[ACE Debug] Failed to compose instruction:', error);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
261
527
|
}
|
|
262
528
|
|
|
263
529
|
/**
|
|
@@ -286,6 +552,16 @@ class AxCrew {
|
|
|
286
552
|
crewId: string;
|
|
287
553
|
agents: Map<string, StatefulAxAgent> | null;
|
|
288
554
|
state: StateInstance;
|
|
555
|
+
// Execution history for ACE feedback routing
|
|
556
|
+
private executionHistory: Map<string, {
|
|
557
|
+
taskId: string;
|
|
558
|
+
rootAgent: string;
|
|
559
|
+
involvedAgents: Set<string>;
|
|
560
|
+
taskInput: any;
|
|
561
|
+
agentResults: Map<string, any>;
|
|
562
|
+
startTime: number;
|
|
563
|
+
endTime?: number;
|
|
564
|
+
}> = new Map();
|
|
289
565
|
|
|
290
566
|
/**
|
|
291
567
|
* Creates an instance of AxCrew.
|
|
@@ -376,6 +652,8 @@ class AxCrew {
|
|
|
376
652
|
}
|
|
377
653
|
|
|
378
654
|
// Create an instance of StatefulAxAgent
|
|
655
|
+
// Set crew reference in state for execution tracking (ACE feedback routing)
|
|
656
|
+
const agentState = { ...this.state, crew: this };
|
|
379
657
|
const agent = new StatefulAxAgent(
|
|
380
658
|
ai,
|
|
381
659
|
{
|
|
@@ -386,10 +664,26 @@ class AxCrew {
|
|
|
386
664
|
functions: uniqueFunctions,
|
|
387
665
|
agents: uniqueSubAgents,
|
|
388
666
|
examples,
|
|
667
|
+
debug: (agentConfig as any).debug,
|
|
389
668
|
},
|
|
390
|
-
|
|
669
|
+
agentState as StateInstance
|
|
391
670
|
);
|
|
392
671
|
(agent as any).costTracker = tracker;
|
|
672
|
+
(agent as any).__functionsRegistry = this.functionsRegistry;
|
|
673
|
+
|
|
674
|
+
// Initialize ACE if configured
|
|
675
|
+
try {
|
|
676
|
+
const crewAgent = parseCrewConfig(this.crewConfig).crew.find(a => a.name === name) as any;
|
|
677
|
+
const ace: ACEConfig | undefined = crewAgent?.ace;
|
|
678
|
+
if (ace) {
|
|
679
|
+
await (agent as any).initACE?.(ace);
|
|
680
|
+
if (ace.compileOnStart) {
|
|
681
|
+
const { resolveMetric } = await import('./ace.js');
|
|
682
|
+
const metric = resolveMetric(ace.metric, this.functionsRegistry);
|
|
683
|
+
await (agent as any).optimizeOffline?.({ metric, examples });
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
} catch {}
|
|
393
687
|
|
|
394
688
|
return agent;
|
|
395
689
|
} catch (error) {
|
|
@@ -521,11 +815,119 @@ class AxCrew {
|
|
|
521
815
|
}
|
|
522
816
|
}
|
|
523
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Track agent execution for ACE feedback routing
|
|
820
|
+
*/
|
|
821
|
+
trackAgentExecution(taskId: string, agentName: string, input: any): void {
|
|
822
|
+
if (!this.executionHistory.has(taskId)) {
|
|
823
|
+
this.executionHistory.set(taskId, {
|
|
824
|
+
taskId,
|
|
825
|
+
rootAgent: agentName,
|
|
826
|
+
involvedAgents: new Set([agentName]),
|
|
827
|
+
taskInput: input,
|
|
828
|
+
agentResults: new Map(),
|
|
829
|
+
startTime: Date.now()
|
|
830
|
+
});
|
|
831
|
+
} else {
|
|
832
|
+
// Add to involved agents if not already present
|
|
833
|
+
const context = this.executionHistory.get(taskId)!;
|
|
834
|
+
context.involvedAgents.add(agentName);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Record agent result for ACE feedback routing
|
|
840
|
+
*/
|
|
841
|
+
recordAgentResult(taskId: string, agentName: string, result: any): void {
|
|
842
|
+
const context = this.executionHistory.get(taskId);
|
|
843
|
+
if (context) {
|
|
844
|
+
context.agentResults.set(agentName, result);
|
|
845
|
+
context.endTime = Date.now();
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Get agent involvement for a task (used for ACE feedback routing)
|
|
851
|
+
*/
|
|
852
|
+
getTaskAgentInvolvement(taskId: string): {
|
|
853
|
+
rootAgent: string;
|
|
854
|
+
involvedAgents: string[];
|
|
855
|
+
taskInput: any;
|
|
856
|
+
agentResults: Map<string, any>;
|
|
857
|
+
duration?: number;
|
|
858
|
+
} | null {
|
|
859
|
+
const context = this.executionHistory.get(taskId);
|
|
860
|
+
if (!context) return null;
|
|
861
|
+
|
|
862
|
+
return {
|
|
863
|
+
rootAgent: context.rootAgent,
|
|
864
|
+
involvedAgents: Array.from(context.involvedAgents),
|
|
865
|
+
taskInput: context.taskInput,
|
|
866
|
+
agentResults: context.agentResults,
|
|
867
|
+
duration: context.endTime ? context.endTime - context.startTime : undefined
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Apply feedback to agents involved in a task for ACE online learning
|
|
873
|
+
*/
|
|
874
|
+
async applyTaskFeedback(params: {
|
|
875
|
+
taskId: string;
|
|
876
|
+
feedback: string;
|
|
877
|
+
strategy?: 'all' | 'primary' | 'weighted';
|
|
878
|
+
}): Promise<void> {
|
|
879
|
+
const involvement = this.getTaskAgentInvolvement(params.taskId);
|
|
880
|
+
if (!involvement) {
|
|
881
|
+
console.warn(`No execution history found for task ${params.taskId}`);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const { involvedAgents, taskInput, agentResults } = involvement;
|
|
886
|
+
const strategy = params.strategy || 'all';
|
|
887
|
+
|
|
888
|
+
// Determine which agents to update based on strategy
|
|
889
|
+
let agentsToUpdate: string[] = [];
|
|
890
|
+
if (strategy === 'primary') {
|
|
891
|
+
agentsToUpdate = [involvement.rootAgent];
|
|
892
|
+
} else if (strategy === 'all' || strategy === 'weighted') {
|
|
893
|
+
agentsToUpdate = involvedAgents;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Apply feedback to each involved agent
|
|
897
|
+
for (const agentName of agentsToUpdate) {
|
|
898
|
+
const agent = this.agents?.get(agentName);
|
|
899
|
+
if (agent && typeof (agent as any).applyOnlineUpdate === 'function') {
|
|
900
|
+
try {
|
|
901
|
+
await (agent as any).applyOnlineUpdate({
|
|
902
|
+
example: taskInput,
|
|
903
|
+
prediction: agentResults.get(agentName),
|
|
904
|
+
feedback: params.feedback
|
|
905
|
+
});
|
|
906
|
+
} catch (error) {
|
|
907
|
+
console.warn(`Failed to apply ACE feedback to agent ${agentName}:`, error);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Clean up old execution history (call periodically to prevent memory leaks)
|
|
915
|
+
*/
|
|
916
|
+
cleanupOldExecutions(maxAgeMs: number = 3600000): void { // Default 1 hour
|
|
917
|
+
const cutoffTime = Date.now() - maxAgeMs;
|
|
918
|
+
for (const [taskId, context] of this.executionHistory) {
|
|
919
|
+
if (context.startTime < cutoffTime) {
|
|
920
|
+
this.executionHistory.delete(taskId);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
524
925
|
/**
|
|
525
926
|
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
526
927
|
*/
|
|
527
928
|
destroy() {
|
|
528
929
|
this.agents = null;
|
|
930
|
+
this.executionHistory.clear();
|
|
529
931
|
this.state.reset();
|
|
530
932
|
}
|
|
531
933
|
|
|
@@ -565,4 +967,4 @@ class AxCrew {
|
|
|
565
967
|
}
|
|
566
968
|
|
|
567
969
|
export { AxCrew };
|
|
568
|
-
export type { StatefulAxAgent };
|
|
970
|
+
export type { StatefulAxAgent };
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { AxCrew } from './agents/index.js';
|
|
2
2
|
import { AxCrewFunctions } from './functions/index.js';
|
|
3
|
-
import type { AxCrewConfig, AgentConfig } from './types.js';
|
|
3
|
+
import type { AxCrewConfig, AxCrewOptions, AgentConfig } from './types.js';
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
UsageCost,
|
|
7
7
|
AggregatedMetrics,
|
|
8
8
|
AggregatedCosts,
|
|
9
9
|
StateInstance,
|
|
10
|
-
FunctionRegistryType
|
|
10
|
+
FunctionRegistryType,
|
|
11
|
+
ACEConfig,
|
|
12
|
+
ACETeacherConfig,
|
|
13
|
+
ACEPersistenceConfig,
|
|
14
|
+
ACEOptionsConfig,
|
|
15
|
+
ACEMetricConfig,
|
|
11
16
|
} from './types.js';
|
|
12
17
|
/**
|
|
13
18
|
* Metrics types and helpers for request counts, token usage, and estimated cost.
|
|
@@ -46,6 +51,13 @@ export {
|
|
|
46
51
|
type AggregatedCosts,
|
|
47
52
|
type AgentConfig,
|
|
48
53
|
type AxCrewConfig,
|
|
54
|
+
type AxCrewOptions,
|
|
49
55
|
type StateInstance,
|
|
50
56
|
type UsageCost,
|
|
57
|
+
// ACE type exports
|
|
58
|
+
type ACEConfig,
|
|
59
|
+
type ACETeacherConfig,
|
|
60
|
+
type ACEPersistenceConfig,
|
|
61
|
+
type ACEOptionsConfig,
|
|
62
|
+
type ACEMetricConfig,
|
|
51
63
|
};
|
package/src/types.ts
CHANGED
|
@@ -147,6 +147,47 @@ interface MCPStreamableHTTPTransportConfig {
|
|
|
147
147
|
*/
|
|
148
148
|
type MCPTransportConfig = MCPStdioTransportConfig | MCPHTTPSSETransportConfig | MCPStreamableHTTPTransportConfig
|
|
149
149
|
|
|
150
|
+
// ========================
|
|
151
|
+
// ACE integration types
|
|
152
|
+
// ========================
|
|
153
|
+
|
|
154
|
+
interface ACETeacherConfig {
|
|
155
|
+
provider?: Provider;
|
|
156
|
+
providerKeyName?: string;
|
|
157
|
+
apiURL?: string;
|
|
158
|
+
ai?: AxModelConfig & { model: string };
|
|
159
|
+
providerArgs?: Record<string, unknown>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface ACEPersistenceConfig {
|
|
163
|
+
playbookPath?: string;
|
|
164
|
+
initialPlaybook?: Record<string, any>;
|
|
165
|
+
autoPersist?: boolean;
|
|
166
|
+
onPersist?: (pb: any) => Promise<void> | void;
|
|
167
|
+
onLoad?: () => Promise<any> | any;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
interface ACEOptionsConfig {
|
|
171
|
+
maxEpochs?: number;
|
|
172
|
+
allowDynamicSections?: boolean;
|
|
173
|
+
tokenBudget?: number;
|
|
174
|
+
reflectorPrompt?: string;
|
|
175
|
+
curatorPrompt?: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface ACEMetricConfig {
|
|
179
|
+
metricFnName?: string;
|
|
180
|
+
primaryOutputField?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface ACEConfig {
|
|
184
|
+
teacher?: ACETeacherConfig;
|
|
185
|
+
persistence?: ACEPersistenceConfig;
|
|
186
|
+
options?: ACEOptionsConfig;
|
|
187
|
+
metric?: ACEMetricConfig;
|
|
188
|
+
compileOnStart?: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
150
191
|
/**
|
|
151
192
|
* The configuration for an agent.
|
|
152
193
|
*
|
|
@@ -193,6 +234,8 @@ interface AgentConfig {
|
|
|
193
234
|
agents?: string[];
|
|
194
235
|
examples?: Array<Record<string, any>>;
|
|
195
236
|
mcpServers?: Record<string, MCPTransportConfig>;
|
|
237
|
+
/** Optional AxACE configuration to enable optimization for this agent */
|
|
238
|
+
ace?: ACEConfig;
|
|
196
239
|
}
|
|
197
240
|
|
|
198
241
|
/**
|
|
@@ -244,6 +287,8 @@ interface AxCrewConfig {
|
|
|
244
287
|
* @property {any} [telemetry.meter] - OpenTelemetry Meter instance.
|
|
245
288
|
*/
|
|
246
289
|
interface AxCrewOptions {
|
|
290
|
+
/** Enable debug logging for ACE and other internal operations */
|
|
291
|
+
debug?: boolean;
|
|
247
292
|
telemetry?: {
|
|
248
293
|
tracer?: any;
|
|
249
294
|
meter?: any;
|
|
@@ -264,5 +309,11 @@ export {
|
|
|
264
309
|
type ModelUsage,
|
|
265
310
|
type ModelInfo,
|
|
266
311
|
type UsageCost,
|
|
267
|
-
type AggregatedCosts
|
|
312
|
+
type AggregatedCosts,
|
|
313
|
+
// ACE exports
|
|
314
|
+
type ACEConfig,
|
|
315
|
+
type ACEMetricConfig,
|
|
316
|
+
type ACEOptionsConfig,
|
|
317
|
+
type ACEPersistenceConfig,
|
|
318
|
+
type ACETeacherConfig
|
|
268
319
|
}
|