Why this usecase?
In SaaS support, triage is the highest-leverage intervention point. Get it wrong and you have P1 production outages sitting in Tier 1 queues while engineers handle password resets. Get it right and you compress MTTR (mean time to resolve), protect SLAs, and reduce escalation noise dramatically.
Traditional approaches – Flow-based routing rules, static assignment criteria — break the moment a case arrives that doesn’t fit the template. A customer writes: “Our revenue dashboard shows $0 for all accounts since this morning.” Is that a UI rendering bug? A data sync failure? A permissions problem? A genuine data loss incident? A rule engine can’t distinguish them. An LLM can.
But here is the architect’s tension: you cannot let an LLM freely route cases in a production support queue. Misrouting a P1 to Tier 1 is an SLA breach. So the design question is never “should we automate triage?” — it’s “which triage decisions are safe to automate, and which need a human checkpoint?”
The Autonomy Problem – And the HSAD Answer
The HSAD (Human-Supervised Agentforce Design) framework is built on one core principle: autonomy level is a function of impact × confidence. The agent should not have a uniform posture — it should reason contextually about when to act and when to defer.
For escalation triage, the matrix looks like this:
| Condition | Agent Action | Status | Why |
|---|---|---|---|
| Sensitive keywords in case | Skip LLM, flag immediately | Hard Guardrail | Data loss, security, breach = mandatory human review regardless |
| P1 + confidence ≥ 85% | Auto-route to Tier 3 | Auto-Routed | High impact, high certainty — delay is worse than rare misroute risk |
| P1 + confidence < 85% | Flag, suggest Tier 3 | Flagged | Never auto-route P1 with doubt — SLA breach risk too high |
| P2/P3 + confidence ≥ 75% | Auto-route to correct tier | Auto-Routed | Lower stakes, acceptable automation zone |
| P2/P3 + confidence < 75% | Route, but flag for review | Flagged | Don’t stall the case — mark for review without blocking |
| P4 (any confidence) | Always auto-route to Tier 1 | Auto-Routed | Low stakes, high volume — human review defeats the purpose |
Data Model — Why Separate AI Fields
This is the question I get most often: “Why not just write the AI’s severity call to the Case Priority field?” Here’s the architectural answer.
The Case Priority field represents the support team’s official severity call. The AI’s classification is an independent assessment. These must coexist separately — because the delta between them is your feedback loop. Without separate fields you can’t measure model accuracy, you can’t audit routing decisions, and you can’t learn from human overrides.
| Field API Name | Type | Purpose |
|---|---|---|
AI_Severity_Class__c | Picklist (P1–P4) | Agent’s classification output — independent of human Priority field |
AI_Confidence_Score__c | Percent | Model confidence — the variable that drives your entire routing matrix |
AI_Routing_Rationale__c | Long Text Area | Immutable agent testimony — answers “why did it route here?” at any time |
AI_Triage_Status__c | Picklist | Pipeline state: Pending → Auto-Routed → Flagged → Human Reviewed |
AI_Ambiguity_Reason__c | Text Area | Why the agent was uncertain — the reviewer’s briefing note |
Suggested_Tier__c | Picklist | Agent’s suggested routing — human confirms or overrides with one click |
The Prompt Template — Designing for Ambiguity
The classification prompt is the most important artifact in this entire build. Get it wrong and your entire Autonomy Decision Matrix is running on bad input. The two most common mistakes I see: definitions that are too clean (the LLM always finds a bucket), and no instructions that tell the model when to be uncertain.
Here’s the template I use in Einstein Prompt Builder, with the ambiguity design deliberately baked in:
You are an expert support triage analyst for a B2B SaaS company.Analyze the support case below and classify its severity.CASE SUBJECT: {!CaseSubject}CASE DESCRIPTION: {!CaseDescription}ACCOUNT NAME: {!AccountName}ACCOUNT TIER: {!AccountTier}SEVERITY DEFINITIONS:P1 - System down, data loss, security incident, revenue impact, or affects multiple users / entire accountP2 - Major feature broken, workaround exists, limited scopeP3 - Minor issue, cosmetic bug, low-frequency pathP4 - Question, how-to, feature request, general feedbackHARD RULES:- If description contains "data loss", "security", "breach", or "compliance" — severity must be P1, confidence must be 95+- Account Tier = Enterprise amplifies severity if description is borderline between two levelsCONFIDENCE SCORING — READ CAREFULLY:Score confidence LOWER when any of these are true:- Description is vague, one sentence, or missing key details- Subject and description suggest different severity levels- Impact scope is unclear (one user? all users? unknown?)- Customer uses emotional language without technical evidence- Case could reasonably be classified as two different severitiesScore confidence HIGHER only when ALL of these are true:- Impact scope is clearly stated- Technical symptoms are specific and unambiguous- Severity bucket has no overlap with adjacent levelsRespond ONLY in this exact JSON format with no other text:{ "severity": "P1|P2|P3|P4", "confidence": <integer 0-100>, "rationale": "<one sentence explaining classification>", "ambiguity_reason": "<one sentence if confidence < 80, else null>"}
Agent Script – Where the Autonomy Matrix Becomes Real
Agent Script is Salesforce’s answer to the reliability problem with instruction-only agents. It lets you define exactly where the LLM reasons and where deterministic logic takes over — giving you hybrid agents that are both flexible and predictable.
For triage, the separation of responsibilities is clear: the LLM handles classification from unstructured text. Agent Script handles routing logic, guardrails, and error handling. This is not a design preference — it is the correct architectural boundary.
// ============================================================// ESCALATION TRIAGE AGENT SCRIPT// HSAD Gates: Hard guardrail (keywords) + Pre-routing (confidence)// ============================================================// STEP 1: Fetch enriched case context// Always first — downstream actions depend on this outputconst caseContext = await actions.Get_Case_Context({ caseId: context.recordId});// Null guard — failed data fetch should never silently route a caseif (!caseContext || !caseContext.caseDescription) { await actions.Update_Case_And_Route({ caseId: context.recordId, triageStatus: "Flagged for Review", ambiguityReason: "Could not retrieve case details for classification", suggestedTier: "Human_Review_AI_Flagged" }); return;}// STEP 2: Hard guardrail — check BEFORE calling the LLM// Why before: Don't waste an Einstein API call on cases// that must always go to human review regardlessconst desc = caseContext.caseDescription.toLowerCase();const subj = caseContext.caseSubject?.toLowerCase() || "";const sensitiveKeywords = [ "data loss", "security", "breach", "compliance", "gdpr", "hack", "exposed"];const hasSensitiveKeyword = sensitiveKeywords.some(k => desc.includes(k) || subj.includes(k));if (hasSensitiveKeyword) { await actions.Update_Case_And_Route({ caseId: context.recordId, aiSeverity: "P1", aiConfidence: 99, rationale: "Sensitive keyword — mandatory human review", triageStatus: "Flagged for Review", ambiguityReason: "Hard guardrail triggered", suggestedTier: "Tier_3_Engineering_Escalation" }); return; // Done. Human reviews this.}// STEP 3: LLM Classification via Prompt Templateconst classificationResult = await actions.Classify_Case_Severity({ CaseSubject: caseContext.caseSubject, CaseDescription: caseContext.caseDescription, AccountName: caseContext.accountName, AccountTier: caseContext.accountTier});// Parse JSON output — if parse fails, flag immediately// Never route on malformed classification datalet classification;try { classification = JSON.parse(classificationResult.result);} catch (e) { await actions.Update_Case_And_Route({ caseId: context.recordId, triageStatus: "Flagged for Review", ambiguityReason: "Prompt template returned malformed output" }); return;}const { severity, confidence, rationale, ambiguity_reason } = classification;// STEP 4: Autonomy Decision Matrix// Principle: (impact of wrong decision) × (confidence) = autonomy levellet triageStatus, suggestedTier;if (severity === "P1" && confidence >= 85) { triageStatus = "Auto-Routed"; suggestedTier = "Tier_3_Engineering_Escalation";} else if (severity === "P1" && confidence < 85) { // Never auto-route P1 with doubt — SLA breach risk too high triageStatus = "Flagged for Review"; suggestedTier = "Tier_3_Engineering_Escalation";} else if (severity === "P2" && confidence >= 75) { triageStatus = "Auto-Routed"; suggestedTier = "Tier_2_Technical_Support";} else if (severity === "P2" && confidence < 75) { triageStatus = "Flagged for Review"; suggestedTier = "Tier_2_Technical_Support";} else if (severity === "P3" && confidence >= 75) { triageStatus = "Auto-Routed"; suggestedTier = "Tier_1_General_Support";} else if (severity === "P4") { // P4 = always auto-route — low stakes, high volume triageStatus = "Auto-Routed"; suggestedTier = "Tier_1_General_Support";} else { // Catch-all: when in doubt, always defer to human triageStatus = "Flagged for Review"; suggestedTier = "Human_Review_AI_Flagged";}// STEP 5: Write decision back to record — every case, always// Audit trail is non-negotiable in a supervised systemawait actions.Update_Case_And_Route({ caseId: context.recordId, aiSeverity: severity, aiConfidence: confidence, rationale: rationale, triageStatus: triageStatus, ambiguityReason: ambiguity_reason || null, suggestedTier: suggestedTier});
The most important design decision in this entire build isn’t the prompt template, the Agent Script, or the trigger. It’s the Autonomy Decision Matrix – and specifically, the act of making it explicit.
The confidence score is a designed output, not a natural one. The LLM defaults to certainty. If your prompt doesn’t explicitly define when uncertainty is the correct response, your human review gate will never fire. You’ll have built a supervised system that never actually routes anything to supervision.
Let me know your thoughts on this, what framework you think of while designing the Agentforce?






Leave a Reply