spine-framework-cortex 0.2.21 → 0.2.22
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/functions/custom_case_analysis.ts +224 -439
- package/functions/custom_kb-embeddings.ts +129 -22
- package/functions/custom_support-redaction.ts +115 -0
- package/functions/custom_support-solution.ts +104 -0
- package/manifest.json +9 -5
- package/package.json +1 -1
- package/pages/support/RedactionReview.tsx +36 -18
- package/pages/support/TicketDetailPage.tsx +127 -129
- package/seed/ai-agents.json +98 -0
- package/seed/pipelines.json +29 -0
- package/seed/prompt-configs.json +84 -0
- package/LICENSE.md +0 -193
- package/README.md +0 -46
- package/functions/custom_cortex-handler.ts +0 -35
- package/functions/custom_kb-chunker-test.ts +0 -364
- package/functions/custom_kb-ingestion.ts +0 -447
- package/functions/custom_tag_management.ts +0 -314
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
2
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
3
3
|
import { apiFetch } from '@core/lib/api'
|
|
4
4
|
import { Button } from '@core/components/ui/button'
|
|
@@ -7,7 +7,8 @@ import { Badge } from '@core/components/ui/badge'
|
|
|
7
7
|
import { Skeleton } from '@core/components/ui/skeleton'
|
|
8
8
|
import { ScrollArea } from '@core/components/ui/scroll-area'
|
|
9
9
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@core/components/ui/tabs'
|
|
10
|
-
import {
|
|
10
|
+
import { Textarea } from '@core/components/ui/textarea'
|
|
11
|
+
import { ArrowLeft, Send, Lock, Globe, Bot, CheckCircle, AlertCircle, FileText, Building2, CreditCard, Package, BookOpen, Eye, EyeOff, Brain, Clock, TrendingUp, Tag as TagIcon, Sparkles } from 'lucide-react'
|
|
11
12
|
import { useAuth } from '@core/contexts/AuthContext'
|
|
12
13
|
|
|
13
14
|
interface Ticket {
|
|
@@ -367,145 +368,143 @@ function CaseDataPanel({ ticket }: { ticket: Ticket | null }) {
|
|
|
367
368
|
)
|
|
368
369
|
}
|
|
369
370
|
|
|
370
|
-
// AI
|
|
371
|
-
|
|
372
|
-
if (!ticket?.data?.aim_confidence_threshold && !ticket?.data?.aim_confidence_at_response && !ticket?.data?.aim_escalation_reason) {
|
|
373
|
-
return (
|
|
374
|
-
<div className="h-full flex flex-col">
|
|
375
|
-
<div className="px-4 py-2 border-b border-border bg-purple-50">
|
|
376
|
-
<p className="text-xs font-medium uppercase tracking-wide text-purple-700">AI Analysis</p>
|
|
377
|
-
</div>
|
|
378
|
-
<div className="flex-1 p-4 flex items-center justify-center text-sm text-muted-foreground">
|
|
379
|
-
No AI metadata available for this ticket.
|
|
380
|
-
</div>
|
|
381
|
-
</div>
|
|
382
|
-
)
|
|
383
|
-
}
|
|
371
|
+
// Solution AI Panel — internal AI collaborator for support agents
|
|
372
|
+
interface AIMessage { role: 'user' | 'ai'; content: string }
|
|
384
373
|
|
|
385
|
-
|
|
386
|
-
const
|
|
374
|
+
function SolutionAIPanel({ ticket, onGenerateKB }: { ticket: Ticket | null; onGenerateKB?: () => void }) {
|
|
375
|
+
const [messages, setMessages] = useState<AIMessage[]>([])
|
|
376
|
+
const [input, setInput] = useState('')
|
|
377
|
+
const [sending, setSending] = useState(false)
|
|
378
|
+
const [threadId, setThreadId] = useState<string | null>(null)
|
|
379
|
+
const bottomRef = useRef<HTMLDivElement>(null)
|
|
380
|
+
const ai = ticket?.data
|
|
381
|
+
const isResolved = ticket?.status === 'resolved' || ticket?.status === 'closed'
|
|
382
|
+
|
|
383
|
+
// Auto-scroll to bottom when new messages arrive
|
|
384
|
+
useEffect(() => {
|
|
385
|
+
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
|
|
386
|
+
}, [messages])
|
|
387
|
+
|
|
388
|
+
const sendMessage = async () => {
|
|
389
|
+
if (!input.trim() || !ticket?.id || sending) return
|
|
390
|
+
const userText = input.trim()
|
|
391
|
+
setInput('')
|
|
392
|
+
setMessages(prev => [...prev, { role: 'user', content: userText }])
|
|
393
|
+
setSending(true)
|
|
394
|
+
try {
|
|
395
|
+
const isFirst = !threadId
|
|
396
|
+
const res = await apiFetch(
|
|
397
|
+
`/.netlify/functions/custom_support-solution?action=${isFirst ? 'start' : 'message'}`,
|
|
398
|
+
{
|
|
399
|
+
method: 'POST',
|
|
400
|
+
headers: { 'Content-Type': 'application/json' },
|
|
401
|
+
body: JSON.stringify(isFirst
|
|
402
|
+
? { ticket_id: ticket.id, message: userText }
|
|
403
|
+
: { thread_id: threadId, message: userText }
|
|
404
|
+
),
|
|
405
|
+
}
|
|
406
|
+
).then(r => r.json())
|
|
407
|
+
|
|
408
|
+
if (res.threadId && !threadId) setThreadId(res.threadId)
|
|
409
|
+
if (res.content) setMessages(prev => [...prev, { role: 'ai', content: res.content }])
|
|
410
|
+
} catch (err) {
|
|
411
|
+
setMessages(prev => [...prev, { role: 'ai', content: 'Error contacting Solution AI. Please try again.' }])
|
|
412
|
+
} finally {
|
|
413
|
+
setSending(false)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
387
416
|
|
|
388
417
|
return (
|
|
389
418
|
<div className="h-full flex flex-col">
|
|
390
|
-
|
|
391
|
-
|
|
419
|
+
{/* Header */}
|
|
420
|
+
<div className="px-4 py-2 border-b border-border bg-purple-50 shrink-0">
|
|
421
|
+
<div className="flex items-center justify-between">
|
|
422
|
+
<div className="flex items-center gap-1.5">
|
|
423
|
+
<Sparkles className="h-3.5 w-3.5 text-purple-600" />
|
|
424
|
+
<p className="text-xs font-medium uppercase tracking-wide text-purple-700">Solution AI</p>
|
|
425
|
+
</div>
|
|
426
|
+
<p className="text-xs text-purple-500">Internal only</p>
|
|
427
|
+
</div>
|
|
392
428
|
</div>
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
429
|
+
|
|
430
|
+
{/* Triage metadata strip */}
|
|
431
|
+
{(ai?.aim_confidence_at_response || ai?.aim_escalation_reason) && (
|
|
432
|
+
<div className="px-3 py-1.5 border-b border-border bg-muted/40 shrink-0 flex items-center gap-3 text-xs text-muted-foreground">
|
|
396
433
|
{ai.aim_confidence_at_response && (
|
|
397
|
-
<
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
<span className={`font-mono font-medium ${ai.aim_confidence_at_response >= 0.75 ? 'text-green-600' : 'text-amber-600'}`}>
|
|
401
|
-
{Math.round(ai.aim_confidence_at_response * 100)}%
|
|
402
|
-
</span>
|
|
403
|
-
</div>
|
|
404
|
-
{ai.aim_confidence_threshold && (
|
|
405
|
-
<p className="text-xs text-muted-foreground">Threshold: {Math.round(ai.aim_confidence_threshold * 100)}%</p>
|
|
406
|
-
)}
|
|
407
|
-
</div>
|
|
434
|
+
<span className={ai.aim_confidence_at_response >= 0.75 ? 'text-green-600' : 'text-amber-600'}>
|
|
435
|
+
Triage confidence: {Math.round(ai.aim_confidence_at_response * 100)}%
|
|
436
|
+
</span>
|
|
408
437
|
)}
|
|
409
|
-
|
|
410
438
|
{ai.aim_escalation_reason && ai.aim_escalation_reason !== 'none' && (
|
|
411
|
-
<
|
|
412
|
-
<AlertCircle className="h-
|
|
413
|
-
|
|
414
|
-
<p className="font-medium text-amber-700">Escalated</p>
|
|
415
|
-
<p className="text-amber-600 text-xs">{ai.aim_escalation_reason.replace('_', ' ')}</p>
|
|
416
|
-
</div>
|
|
417
|
-
</div>
|
|
418
|
-
)}
|
|
419
|
-
|
|
420
|
-
{/* Problem Statement */}
|
|
421
|
-
{ai.aim_problem_statement && (
|
|
422
|
-
<div className="space-y-1">
|
|
423
|
-
<p className="text-xs font-medium text-muted-foreground uppercase">Problem</p>
|
|
424
|
-
<p className="text-sm bg-muted p-2 rounded">{ai.aim_problem_statement}</p>
|
|
425
|
-
</div>
|
|
439
|
+
<span className="text-amber-600 flex items-center gap-1">
|
|
440
|
+
<AlertCircle className="h-3 w-3" /> Escalated
|
|
441
|
+
</span>
|
|
426
442
|
)}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
443
|
+
</div>
|
|
444
|
+
)}
|
|
445
|
+
|
|
446
|
+
{/* Chat history */}
|
|
447
|
+
<ScrollArea className="flex-1 px-3 py-2">
|
|
448
|
+
{messages.length === 0 ? (
|
|
449
|
+
<div className="flex flex-col items-center justify-center h-32 text-center text-muted-foreground text-xs gap-2">
|
|
450
|
+
<Bot className="h-6 w-6" />
|
|
451
|
+
<p>Ask Solution AI for help with this case.</p>
|
|
452
|
+
<p className="text-xs opacity-70">Searches KB · recalls similar cases · drafts replies</p>
|
|
453
|
+
</div>
|
|
454
|
+
) : (
|
|
455
|
+
<div className="space-y-2">
|
|
456
|
+
{messages.map((msg, i) => (
|
|
457
|
+
<div key={i} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
|
458
|
+
<div className={`max-w-[90%] rounded-lg px-3 py-2 text-xs whitespace-pre-wrap ${
|
|
459
|
+
msg.role === 'user'
|
|
460
|
+
? 'bg-primary text-primary-foreground'
|
|
461
|
+
: 'bg-muted text-foreground'
|
|
462
|
+
}`}>
|
|
463
|
+
{msg.content}
|
|
464
|
+
</div>
|
|
444
465
|
</div>
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
<>
|
|
451
|
-
<Separator />
|
|
452
|
-
<div className="space-y-3">
|
|
453
|
-
<p className="text-xs font-medium text-muted-foreground uppercase">Post-mortem</p>
|
|
454
|
-
|
|
455
|
-
{postmortem?.root_cause_category && (
|
|
456
|
-
<p className="text-sm">
|
|
457
|
-
<span className="text-muted-foreground">Root cause:</span>{' '}
|
|
458
|
-
{postmortem.root_cause_category}
|
|
459
|
-
</p>
|
|
460
|
-
)}
|
|
461
|
-
|
|
462
|
-
{postmortem?.resolution_time_minutes && (
|
|
463
|
-
<p className="text-sm">
|
|
464
|
-
<span className="text-muted-foreground">Resolution time:</span>{' '}
|
|
465
|
-
{Math.round(postmortem.resolution_time_minutes)} min
|
|
466
|
-
</p>
|
|
467
|
-
)}
|
|
468
|
-
|
|
469
|
-
{postmortem?.customer_satisfaction !== undefined && (
|
|
470
|
-
<div className="flex items-center gap-2">
|
|
471
|
-
<span className="text-sm text-muted-foreground">Satisfaction:</span>
|
|
472
|
-
<div className="flex">
|
|
473
|
-
{[1, 2, 3, 4, 5].map((star) => (
|
|
474
|
-
<span
|
|
475
|
-
key={star}
|
|
476
|
-
className={`text-lg ${star <= (postmortem.customer_satisfaction || 0) ? 'text-amber-400' : 'text-gray-300'}`}
|
|
477
|
-
>
|
|
478
|
-
★
|
|
479
|
-
</span>
|
|
480
|
-
))}
|
|
481
|
-
</div>
|
|
482
|
-
</div>
|
|
483
|
-
)}
|
|
484
|
-
|
|
485
|
-
{/* KB Generation */}
|
|
486
|
-
<div className="pt-2">
|
|
487
|
-
{postmortem?.kb_generated ? (
|
|
488
|
-
<div className="flex items-center gap-2 text-green-600 text-sm">
|
|
489
|
-
<CheckCircle className="h-4 w-4" />
|
|
490
|
-
KB article created
|
|
491
|
-
</div>
|
|
492
|
-
) : (
|
|
493
|
-
<Button
|
|
494
|
-
variant="outline"
|
|
495
|
-
size="sm"
|
|
496
|
-
className="w-full gap-1 text-xs"
|
|
497
|
-
onClick={onGenerateKB}
|
|
498
|
-
>
|
|
499
|
-
<BookOpen className="h-3 w-3" />
|
|
500
|
-
Generate KB Article
|
|
501
|
-
</Button>
|
|
502
|
-
)}
|
|
466
|
+
))}
|
|
467
|
+
{sending && (
|
|
468
|
+
<div className="flex justify-start">
|
|
469
|
+
<div className="bg-muted rounded-lg px-3 py-2 text-xs text-muted-foreground animate-pulse">
|
|
470
|
+
Thinking…
|
|
503
471
|
</div>
|
|
504
472
|
</div>
|
|
505
|
-
|
|
473
|
+
)}
|
|
474
|
+
<div ref={bottomRef} />
|
|
475
|
+
</div>
|
|
476
|
+
)}
|
|
477
|
+
</ScrollArea>
|
|
478
|
+
|
|
479
|
+
{/* KB generation (resolved only) */}
|
|
480
|
+
{isResolved && (
|
|
481
|
+
<div className="px-3 py-2 border-t border-border shrink-0">
|
|
482
|
+
{ai?.kb_proposed_kb_id ? (
|
|
483
|
+
<div className="flex items-center gap-2 text-green-600 text-xs">
|
|
484
|
+
<CheckCircle className="h-3.5 w-3.5" /> KB article created
|
|
485
|
+
</div>
|
|
486
|
+
) : (
|
|
487
|
+
<Button variant="outline" size="sm" className="w-full gap-1 text-xs" onClick={onGenerateKB}>
|
|
488
|
+
<BookOpen className="h-3 w-3" /> Generate KB Article
|
|
489
|
+
</Button>
|
|
506
490
|
)}
|
|
507
491
|
</div>
|
|
508
|
-
|
|
492
|
+
)}
|
|
493
|
+
|
|
494
|
+
{/* Input */}
|
|
495
|
+
<div className="px-3 py-2 border-t border-border shrink-0 flex gap-2">
|
|
496
|
+
<Textarea
|
|
497
|
+
value={input}
|
|
498
|
+
onChange={e => setInput(e.target.value)}
|
|
499
|
+
onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage() } }}
|
|
500
|
+
placeholder="Ask Solution AI…"
|
|
501
|
+
className="flex-1 text-xs resize-none min-h-[36px] max-h-[100px]"
|
|
502
|
+
rows={1}
|
|
503
|
+
/>
|
|
504
|
+
<Button size="sm" onClick={sendMessage} disabled={!input.trim() || sending} className="shrink-0">
|
|
505
|
+
<Send className="h-3.5 w-3.5" />
|
|
506
|
+
</Button>
|
|
507
|
+
</div>
|
|
509
508
|
</div>
|
|
510
509
|
)
|
|
511
510
|
}
|
|
@@ -838,7 +837,6 @@ export default function TicketDetailPage() {
|
|
|
838
837
|
}
|
|
839
838
|
|
|
840
839
|
const handleGenerateKB = () => {
|
|
841
|
-
// Trigger KB generation flow - would navigate to redaction review
|
|
842
840
|
navigate(`/cortex/support/${id}/kb-review`)
|
|
843
841
|
}
|
|
844
842
|
|
|
@@ -910,7 +908,7 @@ export default function TicketDetailPage() {
|
|
|
910
908
|
</TabsList>
|
|
911
909
|
</Tabs>
|
|
912
910
|
{activeSidePanel === 'case' && <CaseDataPanel ticket={ticket} />}
|
|
913
|
-
{activeSidePanel === 'ai' && <
|
|
911
|
+
{activeSidePanel === 'ai' && <SolutionAIPanel ticket={ticket} onGenerateKB={handleGenerateKB} />}
|
|
914
912
|
{activeSidePanel === 'analysis' && <CaseAnalysisPanel ticket={ticket} analysisLoading={analysisLoading} />}
|
|
915
913
|
</div>
|
|
916
914
|
</div>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "Support Triage Agent",
|
|
4
|
+
"description": "First-contact AI for inbound support tickets. Searches KB, reasons over prior cases, responds with confidence scoring. Hands off to Solution AI or human when confidence is low.",
|
|
5
|
+
"agent_type": "support",
|
|
6
|
+
"model_config": {
|
|
7
|
+
"model": "gpt-4o",
|
|
8
|
+
"temperature": 0.4,
|
|
9
|
+
"max_tokens": 1024
|
|
10
|
+
},
|
|
11
|
+
"system_prompt": "You are a helpful and empathetic support agent for a software platform. Your goal is to resolve customer issues accurately and efficiently on the first response.\n\nGuidelines:\n- Be concise and clear. Customers want answers, not essays.\n- If you find a relevant KB article or prior case, reference it.\n- If you are not confident (below 0.75), say so honestly and let the customer know a human will follow up.\n- Never fabricate feature behaviour or make promises about the product.\n- Tone: professional but warm. Match the customer's level of formality.",
|
|
12
|
+
"tools": [],
|
|
13
|
+
"capabilities": ["rag_search", "confidence_scoring", "escalation"],
|
|
14
|
+
"constraints": {
|
|
15
|
+
"max_context_tokens": 8000,
|
|
16
|
+
"never_reveal": ["internal_notes", "agent_ids", "prompt_config_ids"]
|
|
17
|
+
},
|
|
18
|
+
"metadata": {
|
|
19
|
+
"default_prompt_config_slug": "support_triage",
|
|
20
|
+
"owned_by": "cortex"
|
|
21
|
+
},
|
|
22
|
+
"ownership": "platform",
|
|
23
|
+
"is_system": true,
|
|
24
|
+
"is_active": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "Solution AI Agent",
|
|
28
|
+
"description": "Internal AI collaborator for support agents working a case. Searches KB, queries prior resolved cases, suggests diagnostic steps and response drafts. Never visible to customers.",
|
|
29
|
+
"agent_type": "support",
|
|
30
|
+
"model_config": {
|
|
31
|
+
"model": "gpt-4o",
|
|
32
|
+
"temperature": 0.5,
|
|
33
|
+
"max_tokens": 2048
|
|
34
|
+
},
|
|
35
|
+
"system_prompt": "You are an expert technical support advisor assisting a human support agent working a customer case. Your audience is the support agent, not the customer.\n\nYou can:\n- Search the knowledge base and surface relevant articles\n- Recall similar resolved cases and how they were fixed\n- Suggest diagnostic steps based on the symptoms described\n- Draft a proposed customer-facing response for the agent to review\n- Flag automation potential if this issue could be auto-resolved in future\n\nBe direct and practical. The agent needs actionable guidance, not hedged generalities.\nFormat your output clearly: use headers if helpful, bullet points for steps.",
|
|
36
|
+
"tools": [],
|
|
37
|
+
"capabilities": ["rag_search", "case_history_search", "response_drafting"],
|
|
38
|
+
"constraints": {
|
|
39
|
+
"visibility": "internal_only",
|
|
40
|
+
"max_context_tokens": 12000
|
|
41
|
+
},
|
|
42
|
+
"metadata": {
|
|
43
|
+
"default_prompt_config_slug": "solution_ai",
|
|
44
|
+
"owned_by": "cortex"
|
|
45
|
+
},
|
|
46
|
+
"ownership": "platform",
|
|
47
|
+
"is_system": true,
|
|
48
|
+
"is_active": true
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "Case Resolution Analysis Agent",
|
|
52
|
+
"description": "Post-resolution analyst. Given a closed ticket and its full conversation (including internal AI collaboration thread), produces a structured postmortem: root cause, diagnostic path, solution, automation potential, and KB candidacy flag.",
|
|
53
|
+
"agent_type": "analysis",
|
|
54
|
+
"model_config": {
|
|
55
|
+
"model": "gpt-4o",
|
|
56
|
+
"temperature": 0.2,
|
|
57
|
+
"max_tokens": 2048
|
|
58
|
+
},
|
|
59
|
+
"system_prompt": "You are a case analysis specialist. When given a resolved support ticket — including the customer conversation, the internal support-agent collaboration thread, and any escalation notes — you produce a structured postmortem.\n\nYour output must be a JSON object with these exact fields:\n- reported_issue: what the customer described (their words)\n- true_problem: the actual root cause (may differ significantly from how it was reported)\n- diagnostic_steps: ordered array of steps taken to identify the root cause\n- solution_steps: ordered array of steps taken to resolve it\n- final_solution: one-sentence concise answer\n- customer_temperature: 'positive' | 'neutral' | 'frustrated' | 'escalated'\n- time_to_resolution: estimated minutes from first message to resolution\n- escalation_required: boolean\n- back_and_forth_count: integer — number of customer messages\n- sentiment_progression: array of sentiment labels across the conversation\n- automation_potential: 'high' | 'medium' | 'low' — could this be auto-resolved next time?\n- kb_candidate: boolean — would a KB article have prevented this ticket?\n- suggested_tags: array of tag objects { slug, name, purpose, category, applicable_to }\n- confidence_score: 0.0-1.0\n- analysis_summary: 2-3 sentence plain English summary for the ticket record",
|
|
60
|
+
"tools": [],
|
|
61
|
+
"capabilities": ["structured_output", "sentiment_analysis", "case_classification"],
|
|
62
|
+
"constraints": {
|
|
63
|
+
"output_format": "json",
|
|
64
|
+
"max_context_tokens": 16000
|
|
65
|
+
},
|
|
66
|
+
"metadata": {
|
|
67
|
+
"default_prompt_config_slug": "case_analysis_prompt",
|
|
68
|
+
"owned_by": "cortex"
|
|
69
|
+
},
|
|
70
|
+
"ownership": "platform",
|
|
71
|
+
"is_system": true,
|
|
72
|
+
"is_active": true
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "KB Redaction Agent",
|
|
76
|
+
"description": "Prepares resolved cases for publication as KB articles. Identifies PII, account-specific details, internal references, and confidential data. Produces redaction suggestions and a generalised, customer-facing rewrite.",
|
|
77
|
+
"agent_type": "content",
|
|
78
|
+
"model_config": {
|
|
79
|
+
"model": "gpt-4o",
|
|
80
|
+
"temperature": 0.1,
|
|
81
|
+
"max_tokens": 3000
|
|
82
|
+
},
|
|
83
|
+
"system_prompt": "You are a knowledge base editorial agent. You receive the raw text of a resolved support case and must prepare it for public publication as a KB article.\n\nYour tasks:\n1. IDENTIFY all text that should be redacted before publishing. Categories:\n - pii: names, emails, phone numbers, addresses, usernames\n - account_specific: account names, account IDs, organisation-specific data\n - confidential: internal pricing, unreleased features, internal process details\n - internal_reference: ticket IDs, internal tool names, employee names\n\n2. GENERALISE the solution: rewrite the content to be universally applicable, removing all account-specific context while preserving the diagnostic and solution logic.\n\nOutput must be a JSON object:\n{\n \"original_content\": \"<the input text unchanged>\",\n \"redacted_content\": \"<fully redacted and generalised version>\",\n \"suggestions\": [\n {\n \"id\": \"<uuid>\",\n \"start_index\": <int>,\n \"end_index\": <int>,\n \"original_text\": \"<exact text to redact>\",\n \"redacted_text\": \"<replacement text, e.g. [Account Name]>\",\n \"sensitivity_level\": \"high|medium|low\",\n \"reasoning\": \"<one sentence why>\",\n \"category\": \"pii|confidential|account_specific|internal_reference\"\n }\n ],\n \"confidence_score\": 0.0-1.0,\n \"processing_metadata\": { \"model_used\": \"<model>\", \"temperature\": 0.1, \"tokens_consumed\": <int> }\n}",
|
|
84
|
+
"tools": [],
|
|
85
|
+
"capabilities": ["structured_output", "redaction", "content_generalisation"],
|
|
86
|
+
"constraints": {
|
|
87
|
+
"output_format": "json",
|
|
88
|
+
"max_context_tokens": 12000
|
|
89
|
+
},
|
|
90
|
+
"metadata": {
|
|
91
|
+
"default_prompt_config_slug": "kb_generator",
|
|
92
|
+
"owned_by": "cortex"
|
|
93
|
+
},
|
|
94
|
+
"ownership": "platform",
|
|
95
|
+
"is_system": true,
|
|
96
|
+
"is_active": true
|
|
97
|
+
}
|
|
98
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "support_escalation",
|
|
4
|
+
"description": "Fired by agent-runner when triage confidence falls below threshold. Marks the thread as pending human review and notifies the support team.",
|
|
5
|
+
"trigger_type": "event",
|
|
6
|
+
"config": {
|
|
7
|
+
"event": "agent.inference.low_confidence",
|
|
8
|
+
"scope": "account"
|
|
9
|
+
},
|
|
10
|
+
"stages": [
|
|
11
|
+
{
|
|
12
|
+
"stage_type": "send_notification",
|
|
13
|
+
"config": {
|
|
14
|
+
"message": "A support ticket has been escalated to human review — confidence too low for AI response.",
|
|
15
|
+
"entity_type": "threads",
|
|
16
|
+
"entity_id_field": "thread_id",
|
|
17
|
+
"recipients": ["role:support_admin", "role:support"]
|
|
18
|
+
},
|
|
19
|
+
"continue_on_error": true
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"metadata": {
|
|
23
|
+
"note": "Thread escalation_status is already set to 'pending' by agent-runner before this pipeline fires. Ticket status is updated by the calling function (custom_support-triage) from agentMsg.data.escalated."
|
|
24
|
+
},
|
|
25
|
+
"ownership": "platform",
|
|
26
|
+
"is_system": true,
|
|
27
|
+
"is_active": true
|
|
28
|
+
}
|
|
29
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "Support Triage",
|
|
4
|
+
"slug": "support_triage",
|
|
5
|
+
"context_template": "A customer has submitted the following support request:\n\n{{user_message}}\n\nReview the knowledge base articles and prior case history provided in your context. Respond helpfully and accurately. If you are not confident in your answer (below 0.75), set escalate=true.",
|
|
6
|
+
"is_multi_turn": true,
|
|
7
|
+
"max_history_messages": 20,
|
|
8
|
+
"confidence_threshold": 0.75,
|
|
9
|
+
"escalation_action": "pipeline",
|
|
10
|
+
"escalation_target": "support_escalation",
|
|
11
|
+
"output_mode": "json",
|
|
12
|
+
"knowledge_sources": [],
|
|
13
|
+
"available_tools": [],
|
|
14
|
+
"metadata": {
|
|
15
|
+
"knowledge_scope": ["kb_article", "resolved_case"],
|
|
16
|
+
"note": "knowledge_sources is resolved at runtime to the requesting account's KB + platform KB. Empty array = full scope search."
|
|
17
|
+
},
|
|
18
|
+
"ownership": "platform",
|
|
19
|
+
"is_system": true,
|
|
20
|
+
"is_active": true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "Solution AI",
|
|
24
|
+
"slug": "solution_ai",
|
|
25
|
+
"context_template": "A support agent is working on the following case and needs your assistance:\n\nTicket: {{ticket_title}}\nCustomer issue: {{ticket_description}}\n\nConversation so far:\n{{conversation_history}}\n\nAgent question: {{user_message}}\n\nProvide practical, actionable guidance. If you can draft a suggested customer-facing response, include it under 'Suggested Response:'.",
|
|
26
|
+
"is_multi_turn": true,
|
|
27
|
+
"max_history_messages": 30,
|
|
28
|
+
"confidence_threshold": 0.0,
|
|
29
|
+
"escalation_action": null,
|
|
30
|
+
"escalation_target": null,
|
|
31
|
+
"output_mode": "text",
|
|
32
|
+
"knowledge_sources": [],
|
|
33
|
+
"available_tools": [],
|
|
34
|
+
"metadata": {
|
|
35
|
+
"visibility": "internal",
|
|
36
|
+
"knowledge_scope": ["kb_article", "resolved_case"],
|
|
37
|
+
"note": "No escalation — this agent assists human agents. confidence_threshold=0 disables auto-escalation."
|
|
38
|
+
},
|
|
39
|
+
"ownership": "platform",
|
|
40
|
+
"is_system": true,
|
|
41
|
+
"is_active": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "Case Analysis Prompt",
|
|
45
|
+
"slug": "case_analysis_prompt",
|
|
46
|
+
"context_template": "Analyze this resolved support ticket:\n\nTicket: {{ticket_title}}\nDescription: {{ticket_description}}\nCreated: {{created_at}}\nResolved: {{resolved_at}}\nStatus: {{status}}\nPriority: {{priority}}\n\nCustomer Conversation:\n{{conversation_history}}\n\nInternal Support Thread:\n{{internal_thread}}\n\nProduce a complete structured postmortem in the JSON format specified in your system prompt.",
|
|
47
|
+
"is_multi_turn": false,
|
|
48
|
+
"max_history_messages": 0,
|
|
49
|
+
"confidence_threshold": 0.0,
|
|
50
|
+
"escalation_action": null,
|
|
51
|
+
"escalation_target": null,
|
|
52
|
+
"output_mode": "json",
|
|
53
|
+
"knowledge_sources": [],
|
|
54
|
+
"available_tools": [],
|
|
55
|
+
"metadata": {
|
|
56
|
+
"requires_resolved_ticket": true,
|
|
57
|
+
"includes_internal_thread": true
|
|
58
|
+
},
|
|
59
|
+
"ownership": "platform",
|
|
60
|
+
"is_system": true,
|
|
61
|
+
"is_active": true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "KB Generator",
|
|
65
|
+
"slug": "kb_generator",
|
|
66
|
+
"context_template": "Prepare the following resolved case content for KB publication:\n\n{{content}}\n\nIdentify all redactable text and produce a generalised, customer-facing version suitable for publishing as a public knowledge base article.",
|
|
67
|
+
"is_multi_turn": false,
|
|
68
|
+
"max_history_messages": 0,
|
|
69
|
+
"confidence_threshold": 0.0,
|
|
70
|
+
"escalation_action": null,
|
|
71
|
+
"escalation_target": null,
|
|
72
|
+
"output_mode": "json",
|
|
73
|
+
"requires_review": true,
|
|
74
|
+
"knowledge_sources": [],
|
|
75
|
+
"available_tools": [],
|
|
76
|
+
"metadata": {
|
|
77
|
+
"redaction_categories": ["pii", "confidential", "account_specific", "internal_reference"],
|
|
78
|
+
"auto_accept_sensitivity": ["high"]
|
|
79
|
+
},
|
|
80
|
+
"ownership": "platform",
|
|
81
|
+
"is_system": true,
|
|
82
|
+
"is_active": true
|
|
83
|
+
}
|
|
84
|
+
]
|