1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632 | // ============================================================================
// customer-support/main.scaicore
// ============================================================================
@core CustomerSupport {
version = "1.0.0"
description = "Handles customer inquiries across channels"
// Per-customer instance: each customer gets isolated state
instance = :entity(key = "customer_id") {
idle_timeout = 30m
max_concurrent = 1 // one conversation at a time per customer
max_active_instances = 10000
overflow = :deactivate_lru
}
@plugins {
crm = company/salesforce@1.0
knowledge = scailabs/knowledge-base@1.0
billing = company/billing-system@2.0
scaisend = scailabs/scaisend@1.0
escalation = company/agent-routing@1.0
}
@llm {
primary = {
model = "scailabs/poolnoodle-omni"
temperature = 0.5
role = "general"
}
fast = {
model = "scailabs/poolnoodle-mini"
temperature = 0.3
role = "classification"
}
}
@memory {
// Per-customer memory (exclusive to this customer's instance)
interaction_history: array[InteractionRecord]
preferences: CustomerPreferences
escalation_count: int
satisfaction_trend: array[float]
active_conversation: ConversationState | null
}
// Read-only data — same for all instances, immutable at runtime
@reference {
product_knowledge: array[KnowledgeEntry]
resolution_templates: map[string, Template]
escalation_rules: array[EscalationRule]
faq: map[string, FAQEntry]
}
@config {
@param sentiment_escalation_threshold: float = -0.7 @hot_reload @runtime_configurable {
description = "Sentiment threshold for automatic escalation"
validation = value >= -1.0 && value <= 0.0
}
@param max_auto_responses: int = 10 @hot_reload {
description = "Maximum automated responses before escalating"
}
@param escalation_keywords: array[string] = ["legal", "lawyer", "sue"] @hot_reload @runtime_configurable {
description = "Keywords that trigger immediate escalation"
}
}
@constraints {
never = [
"promise refund > €100 without approval",
"share customer data with other customers",
"admit legal liability",
"make commitments about unannounced features",
"diagnose issues as 'bug' without verification"
]
always = [
"log all interactions to CRM",
"identify customer before discussing account details",
"respect GDPR data access/deletion requests"
]
prefer = [
"resolve without escalation when possible",
"de-escalation over defensiveness",
"teach customer self-service when appropriate"
]
}
identity = {
name = "Alex"
personality = """
Friendly, professional, solution-oriented.
Acknowledges frustration without being defensive.
Concise but thorough. Doesn't over-apologize.
"""
languages = ["nl", "en", "de"]
}
@triggers {
@webhook chat_message {
flow = handle_message
path = "/webhooks/chat"
}
@webhook email_message {
flow = handle_message
path = "/webhooks/email"
}
@api submit_ticket {
flow = handle_message
}
}
// Subscribe to events from other Cores
@on billing_update from core://billing-system {
flow = handle_billing_update
}
@conversation_policy {
routing = {
match_strategy = "flexible"
match_on = [sender, session_id, topic_similarity]
similarity_threshold = 0.8
no_match = "new_conversation"
}
context = {
always = [customer.profile, customer.tier, customer.recent_tickets(30d)]
if_relevant = [customer.billing_status, customer.product_usage]
on_demand = [customer.full_history]
max_history_turns = 50
}
timeout = {
idle = 30m
absolute = 4h
idle_warning = 25m
idle_message = "Are you still there? I'm happy to continue helping."
}
channels = [chat, email, api, internal]
}
}
// ============================================================================
// Types
// ============================================================================
@types {
type Intent = enum[
question, complaint, request, feedback,
cancellation, billing_inquiry, bug_report,
feature_request, other
]
type Urgency = enum[low, medium, high, critical]
type ConversationMode = enum[standard, empathetic, de_escalation]
type Understanding = {
primary_intent: Intent,
secondary_intents: array[Intent]?,
sentiment: float,
urgency: Urgency,
entities: {
product: string?,
order_id: string?,
feature: string?,
amount: money?,
date_reference: date?
},
language: string
}
type HandlerResult = {
status: enum[resolved, partial, pending, escalated],
approach: string,
follow_up: string?
}
type InteractionRecord = {
timestamp: datetime,
intent: Intent,
sentiment: float,
resolution_status: string,
approach: string
}
type ResolutionPattern = {
intent: Intent,
context_signals: map[string, string],
successful_approach: string,
confirmed: bool
}
type KnowledgeEntry = {
topic: string,
content: string,
source: "kb" | "learned",
confidence: float
}
type EscalationOutcome = {
reason: string,
human_resolution: string,
could_automate: bool,
learned_pattern: string?
}
}
// ============================================================================
// Main Interaction Flow
// ============================================================================
@flow handle_message(customer_id: string, incoming: Message): HandlerResult {
@budget {
max_duration = 30s
on_exceeded = "warn"
}
// Establish context — customer_id already routed us to the right instance
@rigid {
customer = crm.get_profile(customer_id)
conversation = conversation_policy.route(incoming, customer)
conversation.append(role = "customer", content = incoming)
}
// Understand intent
understanding = @flexible {
goal = "Understand what the customer needs"
llm = fast
input = incoming
context = {
history: conversation.history,
profile: customer.profile,
previous_interactions: memory.interaction_history.last(5)
}
output = Understanding
on_failure = {
low_confidence = retry(max = 1, with = "focus on the primary intent")
parse_error = retry(max = 2)
}
}
@debug {
log.info("Intent: ${understanding.primary_intent}, Sentiment: ${understanding.sentiment}")
}
// Sentiment-driven mode adjustment
@rigid {
conversation.mode = match understanding.sentiment {
s if s < -0.5 => :de_escalation
s if s < 0.0 => :empathetic
_ => :standard
}
if understanding.sentiment < config.sentiment_escalation_threshold {
crm.flag_ticket(
id = conversation.ticket_id,
flag = "frustrated_customer",
notify = "support_lead"
)
}
// Check for escalation keywords
has_escalation_keyword = config.escalation_keywords.any(
kw => incoming.content.lower().contains(kw)
)
if has_escalation_keyword {
return @call escalate_to_human(
conversation, customer, "Escalation keyword detected"
)
}
// Check response count limit
agent_responses = conversation.messages.filter(m => m.role == "agent").length
if agent_responses >= config.max_auto_responses {
return @call escalate_to_human(
conversation, customer, "Maximum automated responses reached"
)
}
}
// Route to appropriate handler
handler_result = match understanding.primary_intent {
:question => @call handle_question(understanding, customer, conversation)
:complaint => @call handle_complaint(understanding, customer, conversation)
:billing_inquiry => @call handle_billing(understanding, customer, conversation)
:cancellation => @call handle_cancellation(understanding, customer, conversation)
:bug_report => @call handle_bug_report(understanding, customer, conversation)
_ => @call handle_general(understanding, customer, conversation)
}
// Generate and send response
response = @flexible {
goal = "Formulate response to customer"
input = { handler_result: handler_result, understanding: understanding }
context = conversation.history
identity = core.identity
guidance = match conversation.mode {
:de_escalation => """
Acknowledge frustration explicitly.
Lead with what you CAN do.
Offer escalation to human proactively.
"""
:empathetic => """
Acknowledge the inconvenience.
Be warm but stay solution-focused.
"""
:standard => """
Friendly and efficient.
Get to the solution quickly.
"""
}
output = {
message: string,
suggested_actions: array[string]?,
internal_notes: string?,
follow_up_needed: bool
}
constraints = {
inherit = true
}
}
// Deliver response
@rigid {
conversation.append(role = "agent", content = response.message)
deliver(
channel = conversation.channel,
message = response.message,
actions = response.suggested_actions
)
crm.log_interaction({
ticket_id = conversation.ticket_id,
customer_id = customer.id,
intent = understanding.primary_intent,
sentiment = understanding.sentiment,
resolution_status = handler_result.status,
internal_notes = response.internal_notes
})
}
// Record interaction in this customer's memory
@rigid {
memory.interaction_history.add({
intent = understanding.primary_intent,
sentiment = understanding.sentiment,
resolution_status = handler_result.status,
timestamp = now()
})
memory.satisfaction_trend.add(understanding.sentiment)
}
// Emit event for resolved interactions (knowledge Core can learn from this)
if handler_result.status == :resolved {
emit interaction_resolved {
customer_id = customer.id
intent = understanding.primary_intent
context_signals = understanding.entities
approach = handler_result.approach
resolved_at = now()
}
}
return handler_result
}
// ============================================================================
// Intent Handlers
// ============================================================================
@flow handle_question(understanding: Understanding, customer: Customer, conversation: Conversation): HandlerResult {
// Search knowledge base and reference data in parallel
@parallel {
kb_results = knowledge.search(
query = understanding.entities,
context = customer.product_tier,
limit = 5
)
// Reference data: read-only, same for all instances, fast local reads
faq_matches = reference.faq.search(
query = understanding.entities,
limit = 3
)
// Instance memory: this customer's past interactions
past_interactions = memory.interaction_history.search(
query = "question ${understanding.entities}",
limit = 3
)
}
answer = @flexible {
goal = "Answer the customer's question"
input = {
understanding: understanding,
kb_results: kb_results,
faq_matches: faq_matches,
customer_history: past_interactions
}
output = {
answered: bool,
answer: string?,
follow_up_question: string?,
kb_article_ids: array[string]?
}
constraints = {
prefer = ["cite specific KB articles", "link to self-service if applicable"]
never = ["guess if KB has no answer"]
}
on_failure = {
low_confidence = retry(max = 1)
}
}
if answer.answered {
return { status = :resolved, approach = "kb_answer" }
} else if answer.follow_up_question != null {
return { status = :partial, approach = "clarification_needed", follow_up = answer.follow_up_question }
} else {
return @call escalate_to_human(
conversation, customer, "Could not find answer in knowledge base"
)
}
}
@flow handle_complaint(understanding: Understanding, customer: Customer, conversation: Conversation): HandlerResult {
// Gather context
@parallel {
recent_orders = crm.orders(customer_id = customer.id, limit = 5)
recent_tickets = crm.tickets(customer_id = customer.id, limit = 5)
customer_value = crm.lifetime_value(customer.id)
}
// Assess the complaint
assessment = @flexible {
goal = "Understand the root cause and determine appropriate remedy"
input = { understanding: understanding, orders: recent_orders, tickets: recent_tickets }
output = {
root_cause: string,
our_fault: bool,
suggested_remedy: enum[apology, discount, refund, replacement, escalate, investigation],
remedy_value: money?,
confidence: float
}
}
// Apply business rules with guardrails
remedy = @guarded {
goal = "Determine appropriate compensation"
input = { assessment: assessment, customer_value: customer_value }
output = {
approved: bool,
remedy_type: string,
remedy_value: money?,
message: string
}
validate = {
output.remedy_value == null || output.remedy_value <= 100.00
}
guidance = """
High-value customers (>€5000 LTV) may warrant more generous remedies.
Balance cost against retention value.
Never exceed €100 without human approval.
"""
on_validation_failure = {
// Over €100 — send to human
@checkpoint {
type = "approval"
assignee = "support_lead"
present = {
customer = customer,
complaint = assessment,
proposed_remedy = assessment.remedy_value
}
}
}
}
return {
status = remedy.approved ? :resolved : :pending,
approach = "complaint_${assessment.suggested_remedy}"
}
}
@flow handle_billing(understanding: Understanding, customer: Customer, conversation: Conversation): HandlerResult {
// Delegate to billing specialist Core (with instance routing)
@try {
billing_response = @core_call {
target = core://billing-specialist
instance_key = customer.billing_account_id
version = "^2.0"
input = {
customer_id = customer.id,
inquiry = understanding,
conversation_context = conversation.summary
}
timeout = 30s
}
return {
status = billing_response.status,
approach = "billing_specialist_delegation"
}
} catch (TimeoutError e) {
// Fallback to local handling
local_response = @flexible {
goal = "Address billing question with available information"
input = understanding
context = billing.customer_summary(customer.id)
output = { answer: string, resolved: bool }
constraints = {
never = ["make changes to billing without verification"]
}
}
return {
status = local_response.resolved ? :resolved : :partial,
approach = "billing_local_fallback"
}
}
}
// ============================================================================
// Escalation
// ============================================================================
@internal
@flow escalate_to_human(conversation: Conversation, customer: Customer, reason: string): HandlerResult {
// Prepare handoff summary
summary = @flexible {
goal = "Summarize conversation for human agent handoff"
llm = fast
input = conversation.history
output = {
customer_issue: string,
attempted_resolutions: array[string],
customer_sentiment: string,
recommended_approach: string
}
}
@rigid {
agent = escalation.find_agent(
skills = [understanding.primary_intent],
urgency = understanding.urgency,
language = conversation.language
)
escalation.handoff(
agent = agent,
conversation = conversation,
summary = summary
)
// Track escalations in this customer's instance memory
memory.escalation_count = memory.escalation_count + 1
}
// Warm handoff message
handoff_msg = @flexible {
goal = "Write a warm handoff message for the customer"
llm = fast
input = { agent_name: agent.name, reason: reason }
output = { message: string }
guidance = """
Let customer know they're being connected to a specialist.
Reassure them their issue is being prioritized.
Give realistic wait time if queue exists.
"""
}
@rigid {
deliver(
channel = conversation.channel,
message = handoff_msg.message
)
}
// Notify other systems about the escalation
emit customer_escalated {
customer_id = customer.id
reason = reason
escalation_count = memory.escalation_count
sentiment = conversation.sentiment_trend
agent = agent.name
escalated_at = now()
}
return { status = :escalated, approach = "human_escalation: ${reason}" }
}
// ============================================================================
// Event Handlers
// ============================================================================
// Handles billing_update events from the billing-system Core
// This flow is triggered by the @on subscription in the @core block
@internal
@flow handle_billing_update(event: BillingUpdateEvent): void {
@rigid {
// Update this customer's memory with billing context
memory.interaction_history.add({
intent = :billing_update,
sentiment = 0.0,
resolution_status = :informational,
timestamp = now(),
notes = "Billing update: ${event.update_type}"
})
}
}
|