@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
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACE Feedback Loop Demo - Customer Support Agent
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates ACE learning from human feedback.
|
|
5
|
+
*
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
7
|
+
* THE CHALLENGE: 3 Strict Policies vs Real-World Edge Cases
|
|
8
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
*
|
|
10
|
+
* The agent knows only 3 simple rules:
|
|
11
|
+
* 1. Returns within 30 days only
|
|
12
|
+
* 2. Refunds to original payment method only
|
|
13
|
+
* 3. Sale items: no returns, no refunds
|
|
14
|
+
*
|
|
15
|
+
* But every scenario presented violates at least one policy!
|
|
16
|
+
* Watch how the agent handles these impossible situations,
|
|
17
|
+
* then teach it when exceptions should apply.
|
|
18
|
+
*
|
|
19
|
+
* ACE learns your feedback and applies it to similar future cases.
|
|
20
|
+
*
|
|
21
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
22
|
+
*
|
|
23
|
+
* Usage: npx tsx examples/ace-customer-support.ts
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { AxCrew } from "../dist/index.js";
|
|
27
|
+
import { AxCrewFunctions } from "../dist/functions/index.js";
|
|
28
|
+
import type { AxCrewConfig } from "../dist/types.js";
|
|
29
|
+
import type { Provider } from "../dist/types.js";
|
|
30
|
+
import * as readline from "readline";
|
|
31
|
+
import dotenv from "dotenv";
|
|
32
|
+
dotenv.config();
|
|
33
|
+
|
|
34
|
+
// --- 1. Configuration ---
|
|
35
|
+
|
|
36
|
+
const STANDARD_POLICIES = [
|
|
37
|
+
"Returns: Only accepted within 30 days of delivery",
|
|
38
|
+
"Refunds: Must go to the original payment method used",
|
|
39
|
+
"Sale Items: Final sale - absolutely no returns or refunds"
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const crewConfig: AxCrewConfig = {
|
|
43
|
+
crew: [
|
|
44
|
+
{
|
|
45
|
+
name: "SupportAgent",
|
|
46
|
+
description: `You are a customer support agent for TechMart, an e-commerce electronics retailer. Strictly follow the company policies. No exceptions.`,
|
|
47
|
+
signature: "ticket:string, standardPolicies:string[] -> politeSupportResponse:string, decision:string, policyApplied:string",
|
|
48
|
+
provider: "google-gemini" as Provider,
|
|
49
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
50
|
+
ai: {
|
|
51
|
+
model: "gemini-flash-latest",
|
|
52
|
+
temperature: 0.7,
|
|
53
|
+
},
|
|
54
|
+
options: {
|
|
55
|
+
debug: false,
|
|
56
|
+
stream: false
|
|
57
|
+
},
|
|
58
|
+
// Enable ACE for learning from edge case feedback
|
|
59
|
+
ace: {
|
|
60
|
+
teacher: {
|
|
61
|
+
provider: "google-gemini" as Provider,
|
|
62
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
63
|
+
ai: { model: "gemini-flash-latest" }
|
|
64
|
+
},
|
|
65
|
+
options: {
|
|
66
|
+
maxEpochs: 1,
|
|
67
|
+
allowDynamicSections: true
|
|
68
|
+
},
|
|
69
|
+
persistence: {
|
|
70
|
+
playbookPath: "playbooks/customer-support.json",
|
|
71
|
+
autoPersist: true
|
|
72
|
+
},
|
|
73
|
+
metric: { primaryOutputField: "politeSupportResponse" },
|
|
74
|
+
compileOnStart: false,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// --- 2. Sample Support Tickets ---
|
|
81
|
+
|
|
82
|
+
// Each ticket violates at least one of the 3 policies - perfect for teaching exceptions
|
|
83
|
+
const SAMPLE_TICKETS = [
|
|
84
|
+
{
|
|
85
|
+
id: "T-001",
|
|
86
|
+
category: "🚫 Violates: 30-day return policy",
|
|
87
|
+
ticket: `Customer: Sarah Mitchell (8-year Gold Member, $15,000+ purchases)
|
|
88
|
+
Purchase: Laptop, 45 days ago (unopened, still sealed)
|
|
89
|
+
Request: Full refund
|
|
90
|
+
Reason: Was hospitalized for 3 weeks after purchase`
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "T-002",
|
|
94
|
+
category: "🚫 Violates: Original payment method",
|
|
95
|
+
ticket: `Customer: James Chen
|
|
96
|
+
Purchase: Headphones, returned within 30 days
|
|
97
|
+
Request: Refund to a DIFFERENT card (not the original)
|
|
98
|
+
Reason: Original card was stolen. Has police report.`
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "T-003",
|
|
102
|
+
category: "🚫 Violates: No refunds on sale items",
|
|
103
|
+
ticket: `Customer: Maria Garcia (12-year Platinum Member)
|
|
104
|
+
Purchase: TV during Black Friday sale
|
|
105
|
+
Request: Full refund
|
|
106
|
+
Reason: Dead on arrival - won't power on. Factory defect.`
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "T-004",
|
|
110
|
+
category: "🚫 Violates: 30-day return policy",
|
|
111
|
+
ticket: `Customer: David Park
|
|
112
|
+
Purchase: Monitor, 35 days ago
|
|
113
|
+
Request: Replacement
|
|
114
|
+
Reason: Arrived damaged (shipping damage). Photos confirm.
|
|
115
|
+
Couldn't report earlier due to family funeral.`
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "T-005",
|
|
119
|
+
category: "🚫 Violates: No refunds on sale items",
|
|
120
|
+
ticket: `Customer: Emily Watson
|
|
121
|
+
Purchase: Earbuds (bought on clearance sale)
|
|
122
|
+
Request: Store credit
|
|
123
|
+
Reason: Allergic reaction to ear tips (medical issue).
|
|
124
|
+
Item unused, original packaging.`
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: "T-006",
|
|
128
|
+
category: "🚫 Violates: 30-day return policy",
|
|
129
|
+
ticket: `Customer: Robert Taylor (First-time customer)
|
|
130
|
+
Purchase: Gaming keyboard, 60 days ago
|
|
131
|
+
Request: Refund or replacement
|
|
132
|
+
Reason: Keys started failing after 2 weeks of normal use.
|
|
133
|
+
Product defect, documented with video.`
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "T-007",
|
|
137
|
+
category: "🚫 Violates: Original payment method",
|
|
138
|
+
ticket: `Customer: Lisa Anderson (5-year Gold Member)
|
|
139
|
+
Purchase: Smart watch, wants refund
|
|
140
|
+
Request: Refund to PayPal (paid with credit card)
|
|
141
|
+
Reason: Bank closed her credit card account.
|
|
142
|
+
Card no longer exists.`
|
|
143
|
+
}
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
// --- 3. CLI Helper Functions ---
|
|
147
|
+
|
|
148
|
+
const rl = readline.createInterface({
|
|
149
|
+
input: process.stdin,
|
|
150
|
+
output: process.stdout
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const prompt = (question: string): Promise<string> => {
|
|
154
|
+
return new Promise((resolve) => {
|
|
155
|
+
rl.question(question, (answer) => {
|
|
156
|
+
resolve(answer.trim());
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const displayPlaybook = (playbook: any, agentName: string) => {
|
|
162
|
+
console.log(`\n📘 ACE Playbook for ${agentName}:`);
|
|
163
|
+
console.log("─".repeat(70));
|
|
164
|
+
|
|
165
|
+
if (!playbook) {
|
|
166
|
+
console.log(" (No learned exceptions yet - standard policies apply)");
|
|
167
|
+
console.log("─".repeat(70));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (playbook.sections) {
|
|
172
|
+
for (const [sectionName, bullets] of Object.entries(playbook.sections)) {
|
|
173
|
+
console.log(`\n 📂 ${sectionName}:`);
|
|
174
|
+
if (Array.isArray(bullets)) {
|
|
175
|
+
bullets.forEach((bullet: any, i: number) => {
|
|
176
|
+
const content = typeof bullet === 'string' ? bullet : bullet.content || JSON.stringify(bullet);
|
|
177
|
+
// Wrap long lines
|
|
178
|
+
const wrapped = content.length > 60
|
|
179
|
+
? content.match(/.{1,60}(\s|$)/g)?.join('\n ') || content
|
|
180
|
+
: content;
|
|
181
|
+
console.log(` ${i + 1}. ${wrapped}`);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
console.log(" " + JSON.stringify(playbook, null, 2).replace(/\n/g, "\n "));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (playbook.updatedAt) {
|
|
190
|
+
console.log(`\n 🕐 Last updated: ${new Date(playbook.updatedAt).toLocaleString()}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log("─".repeat(70));
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const displayTicket = (ticket: typeof SAMPLE_TICKETS[0]) => {
|
|
197
|
+
console.log(`\n┌${"─".repeat(68)}┐`);
|
|
198
|
+
console.log(`│ 🎫 Ticket ${ticket.id.padEnd(56)}│`);
|
|
199
|
+
console.log(`│ Category: ${ticket.category.padEnd(55)}│`);
|
|
200
|
+
console.log(`├${"─".repeat(68)}┤`);
|
|
201
|
+
|
|
202
|
+
// Split ticket into lines that fit
|
|
203
|
+
const lines = ticket.ticket.split('\n');
|
|
204
|
+
lines.forEach(line => {
|
|
205
|
+
const trimmed = line.trim();
|
|
206
|
+
if (trimmed.length <= 66) {
|
|
207
|
+
console.log(`│ ${trimmed.padEnd(66)}│`);
|
|
208
|
+
} else {
|
|
209
|
+
// Wrap long lines
|
|
210
|
+
const words = trimmed.split(' ');
|
|
211
|
+
let currentLine = '';
|
|
212
|
+
words.forEach(word => {
|
|
213
|
+
if ((currentLine + ' ' + word).trim().length <= 66) {
|
|
214
|
+
currentLine = (currentLine + ' ' + word).trim();
|
|
215
|
+
} else {
|
|
216
|
+
if (currentLine) console.log(`│ ${currentLine.padEnd(66)}│`);
|
|
217
|
+
currentLine = word;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
if (currentLine) console.log(`│ ${currentLine.padEnd(66)}│`);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
console.log(`└${"─".repeat(68)}┘`);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const displayResponse = (result: any) => {
|
|
228
|
+
console.log(`\n╔${"═".repeat(68)}╗`);
|
|
229
|
+
console.log(`║ 💬 Agent Response${" ".repeat(50)}║`);
|
|
230
|
+
console.log(`╠${"═".repeat(68)}╣`);
|
|
231
|
+
|
|
232
|
+
// Display decision
|
|
233
|
+
console.log(`║ 📋 Decision: ${(result.decision || "N/A").substring(0, 52).padEnd(53)}║`);
|
|
234
|
+
console.log(`║ 📖 Policy: ${(result.policyApplied || "N/A").substring(0, 54).padEnd(55)}║`);
|
|
235
|
+
console.log(`╠${"═".repeat(68)}╣`);
|
|
236
|
+
|
|
237
|
+
// Display response (wrapped)
|
|
238
|
+
const responseLines = (result.politeSupportResponse || "").split('\n');
|
|
239
|
+
responseLines.forEach((line: string) => {
|
|
240
|
+
const trimmed = line.trim();
|
|
241
|
+
if (trimmed.length === 0) {
|
|
242
|
+
console.log(`║ ${" ".repeat(66)}║`);
|
|
243
|
+
} else if (trimmed.length <= 66) {
|
|
244
|
+
console.log(`║ ${trimmed.padEnd(66)}║`);
|
|
245
|
+
} else {
|
|
246
|
+
const words = trimmed.split(' ');
|
|
247
|
+
let currentLine = '';
|
|
248
|
+
words.forEach(word => {
|
|
249
|
+
if ((currentLine + ' ' + word).trim().length <= 66) {
|
|
250
|
+
currentLine = (currentLine + ' ' + word).trim();
|
|
251
|
+
} else {
|
|
252
|
+
if (currentLine) console.log(`║ ${currentLine.padEnd(66)}║`);
|
|
253
|
+
currentLine = word;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
if (currentLine) console.log(`║ ${currentLine.padEnd(66)}║`);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
console.log(`╚${"═".repeat(68)}╝`);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const displayHelp = () => {
|
|
264
|
+
console.log("\n📋 Commands:");
|
|
265
|
+
console.log(" • [1-7] - Select a sample ticket by number");
|
|
266
|
+
console.log(" • custom - Enter a custom support ticket");
|
|
267
|
+
console.log(" • playbook - View learned exceptions");
|
|
268
|
+
console.log(" • policies - Show the 3 strict rules");
|
|
269
|
+
console.log(" • help - Show this help message");
|
|
270
|
+
console.log(" • quit - Exit the demo");
|
|
271
|
+
|
|
272
|
+
console.log("\n📝 Example feedback to teach exceptions:");
|
|
273
|
+
console.log(" • \"Medical emergencies extend the 30-day window to 60 days\"");
|
|
274
|
+
console.log(" • \"Allow alternate payment if original method is closed/stolen\"");
|
|
275
|
+
console.log(" • \"Defective products get refunded regardless of sale status\"");
|
|
276
|
+
console.log(" • \"Loyal customers (5+ years) get extended return windows\"");
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const displayPolicies = () => {
|
|
280
|
+
console.log("\n📜 The Only 3 Rules the Agent Knows:");
|
|
281
|
+
console.log("═".repeat(70));
|
|
282
|
+
STANDARD_POLICIES.forEach((policy, i) => console.log(` ${i + 1}. ${policy}`));
|
|
283
|
+
console.log("═".repeat(70));
|
|
284
|
+
console.log("\n⚠️ Every sample ticket VIOLATES at least one of these rules!");
|
|
285
|
+
console.log(" Watch how the agent decides, then teach it when to make exceptions.");
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// --- 4. Main Interactive Loop ---
|
|
289
|
+
|
|
290
|
+
async function main() {
|
|
291
|
+
console.log("\n🎧 ACE Customer Support Demo: 3 Rules vs Reality");
|
|
292
|
+
console.log("═".repeat(70));
|
|
293
|
+
console.log("The agent knows only 3 strict policies. Every ticket violates one.");
|
|
294
|
+
console.log("Watch it struggle, then teach it when exceptions should apply.\n");
|
|
295
|
+
|
|
296
|
+
// Initialize AxCrew
|
|
297
|
+
const crew = new AxCrew(crewConfig, AxCrewFunctions);
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
console.log("⏳ Initializing Support Agent...");
|
|
301
|
+
await crew.addAgentsToCrew(["SupportAgent"]);
|
|
302
|
+
const agent = crew.agents!.get("SupportAgent")!;
|
|
303
|
+
|
|
304
|
+
console.log("✅ Support Agent ready with ACE enabled\n");
|
|
305
|
+
|
|
306
|
+
// Show initial playbook (if loaded from persistence)
|
|
307
|
+
const initialPlaybook = (agent as any).getPlaybook?.();
|
|
308
|
+
displayPlaybook(initialPlaybook, "SupportAgent");
|
|
309
|
+
|
|
310
|
+
displayHelp();
|
|
311
|
+
|
|
312
|
+
let continueLoop = true;
|
|
313
|
+
let ticketCount = 0;
|
|
314
|
+
let feedbackCount = 0;
|
|
315
|
+
|
|
316
|
+
while (continueLoop) {
|
|
317
|
+
console.log(`\n${"═".repeat(70)}`);
|
|
318
|
+
console.log(`🎧 Support Session #${ticketCount + 1}`);
|
|
319
|
+
console.log("═".repeat(70));
|
|
320
|
+
|
|
321
|
+
// List available tickets
|
|
322
|
+
console.log("\n📋 Sample Tickets:");
|
|
323
|
+
SAMPLE_TICKETS.forEach((t, i) => {
|
|
324
|
+
console.log(` [${i + 1}] ${t.id}: ${t.category}`);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const choice = await prompt("\n🔍 Select ticket [1-7], 'custom', 'playbook', 'policies', 'help', or 'quit': ");
|
|
328
|
+
|
|
329
|
+
if (choice.toLowerCase() === 'quit' || choice.toLowerCase() === 'exit' || choice === '') {
|
|
330
|
+
continueLoop = false;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (choice.toLowerCase() === 'help') {
|
|
335
|
+
displayHelp();
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (choice.toLowerCase() === 'playbook') {
|
|
340
|
+
const currentPlaybook = (agent as any).getPlaybook?.();
|
|
341
|
+
displayPlaybook(currentPlaybook, "SupportAgent");
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (choice.toLowerCase() === 'policies') {
|
|
346
|
+
displayPolicies();
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
let ticketText: string;
|
|
351
|
+
let ticketDisplay: typeof SAMPLE_TICKETS[0] | null = null;
|
|
352
|
+
|
|
353
|
+
if (choice.toLowerCase() === 'custom') {
|
|
354
|
+
console.log("\n📝 Enter custom ticket details (press Enter twice when done):");
|
|
355
|
+
let lines: string[] = [];
|
|
356
|
+
let line = await prompt(" ");
|
|
357
|
+
while (line !== '') {
|
|
358
|
+
lines.push(line);
|
|
359
|
+
line = await prompt(" ");
|
|
360
|
+
}
|
|
361
|
+
ticketText = lines.join('\n');
|
|
362
|
+
if (!ticketText.trim()) {
|
|
363
|
+
console.log("❌ Empty ticket, please try again.");
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
const ticketNum = parseInt(choice);
|
|
368
|
+
if (isNaN(ticketNum) || ticketNum < 1 || ticketNum > SAMPLE_TICKETS.length) {
|
|
369
|
+
console.log("❌ Invalid selection. Enter 1-7, 'custom', 'playbook', 'policies', 'help', or 'quit'.");
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
ticketDisplay = SAMPLE_TICKETS[ticketNum - 1];
|
|
373
|
+
ticketText = ticketDisplay.ticket;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
ticketCount++;
|
|
377
|
+
|
|
378
|
+
// Display the ticket
|
|
379
|
+
if (ticketDisplay) {
|
|
380
|
+
displayTicket(ticketDisplay);
|
|
381
|
+
} else {
|
|
382
|
+
console.log("\n📝 Custom Ticket:");
|
|
383
|
+
console.log("─".repeat(70));
|
|
384
|
+
console.log(ticketText);
|
|
385
|
+
console.log("─".repeat(70));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.log("\n⏳ Agent processing ticket...\n");
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
// Execute the query
|
|
392
|
+
const result = await agent.forward({ ticket: ticketText, standardPolicies: STANDARD_POLICIES });
|
|
393
|
+
const taskId = (result as any)._taskId;
|
|
394
|
+
|
|
395
|
+
displayResponse(result);
|
|
396
|
+
|
|
397
|
+
// Get feedback from user
|
|
398
|
+
console.log("\n💬 Supervisor Feedback Loop");
|
|
399
|
+
console.log("─".repeat(70));
|
|
400
|
+
console.log("Was this response correct? If not, provide feedback to teach the agent");
|
|
401
|
+
console.log("how to handle this type of edge case in the future.\n");
|
|
402
|
+
|
|
403
|
+
const feedback = await prompt("📝 Feedback (or Enter to approve): ");
|
|
404
|
+
|
|
405
|
+
if (feedback.toLowerCase() === 'quit' || feedback.toLowerCase() === 'exit') {
|
|
406
|
+
continueLoop = false;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (feedback && feedback.length > 0) {
|
|
411
|
+
console.log("\n⏳ Teaching agent new exception handling...");
|
|
412
|
+
|
|
413
|
+
// Apply feedback via ACE
|
|
414
|
+
if (taskId) {
|
|
415
|
+
await crew.applyTaskFeedback({
|
|
416
|
+
taskId,
|
|
417
|
+
feedback,
|
|
418
|
+
strategy: "all"
|
|
419
|
+
});
|
|
420
|
+
} else {
|
|
421
|
+
await (agent as any).applyOnlineUpdate?.({
|
|
422
|
+
example: { ticket: ticketText },
|
|
423
|
+
prediction: result,
|
|
424
|
+
feedback
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
feedbackCount++;
|
|
429
|
+
console.log("✅ Agent learned from feedback!\n");
|
|
430
|
+
|
|
431
|
+
// Display updated playbook
|
|
432
|
+
const updatedPlaybook = (agent as any).getPlaybook?.();
|
|
433
|
+
displayPlaybook(updatedPlaybook, "SupportAgent");
|
|
434
|
+
|
|
435
|
+
console.log("\n💡 The agent will now apply this learning to similar situations!");
|
|
436
|
+
console.log(" Try another ticket to see the agent use this new knowledge.");
|
|
437
|
+
} else {
|
|
438
|
+
console.log("\n✅ Response approved - no changes needed.");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
} catch (error: any) {
|
|
442
|
+
console.error(`\n❌ Error: ${error.message}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Final summary
|
|
447
|
+
console.log("\n" + "═".repeat(70));
|
|
448
|
+
console.log("📊 Session Summary");
|
|
449
|
+
console.log("═".repeat(70));
|
|
450
|
+
console.log(` Tickets handled: ${ticketCount}`);
|
|
451
|
+
console.log(` Feedback provided: ${feedbackCount}`);
|
|
452
|
+
|
|
453
|
+
const finalPlaybook = (agent as any).getPlaybook?.();
|
|
454
|
+
if (finalPlaybook?.sections) {
|
|
455
|
+
const bulletCount = Object.values(finalPlaybook.sections)
|
|
456
|
+
.reduce((acc: number, bullets: any) => acc + (Array.isArray(bullets) ? bullets.length : 0), 0);
|
|
457
|
+
console.log(` Learned exceptions: ${bulletCount}`);
|
|
458
|
+
}
|
|
459
|
+
console.log(` Saved to: playbooks/customer-support.json`);
|
|
460
|
+
|
|
461
|
+
console.log("\n🎧 Thanks for training the Support Agent!");
|
|
462
|
+
console.log(" Learned exceptions are saved for future sessions.\n");
|
|
463
|
+
|
|
464
|
+
} catch (error: any) {
|
|
465
|
+
console.error("\n❌ Error:", error.message);
|
|
466
|
+
console.log("\nTroubleshooting:");
|
|
467
|
+
console.log("• Ensure GEMINI_API_KEY (or OPENAI_API_KEY) is set");
|
|
468
|
+
} finally {
|
|
469
|
+
crew.cleanupOldExecutions(60000);
|
|
470
|
+
crew.destroy();
|
|
471
|
+
rl.close();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// --- 5. Run ---
|
|
476
|
+
|
|
477
|
+
main().catch((error) => {
|
|
478
|
+
console.error("Fatal error:", error);
|
|
479
|
+
process.exit(1);
|
|
480
|
+
});
|