Chapter 8: Confidence and Confidence-Gated Routing
Learning Objectives
Explain how confidence is derived from the shape of a probability distribution rather than from the model's opinion of itself
Apply the three-tier pattern: act automatically, flag for review, escalate to a human
Set confidence thresholds that scale with the risk of the action being taken
Plot confidence against accuracy on real historical tickets and pick thresholds from the resulting table
Pre-Quiz: What Confidence Measures
Two incidents both return choice = "dc_fabric", but Incident A reports confidence ~0.93 and Incident B reports confidence ~0.38. What does this difference indicate?
Incident A's probability distribution is peaked on dc_fabric while Incident B's is flat across several options, even though both picked the same top option
Incident B used a different, less capable model than Incident A
Incident A's probabilities don't sum to 1.0 while Incident B's do
The choice field is unreliable and should be ignored whenever confidence is reported
Why does the chapter recommend inspecting the full probabilities field rather than confidence alone when confidence is low?
Because probabilities reveal how the model is uncertain — e.g., a near two-way tie can be routed to a combined queue, while a genuine four-way scatter usually needs a human
Because the confidence field is only available on Score answers, not Choice answers
Because probabilities are always more accurate than the confidence statistic
Because TypeSafe recomputes confidence incorrectly and probabilities are the corrected version
A ticket owner question consistently splits confidence roughly 50/50 between network_core and dc_fabric across many different incidents. What does the chapter say this pattern most likely indicates?
A criteria defect — the two options' descriptions probably overlap and need clearer what/not_for/examples boundaries
The incidents are genuinely ambiguous and no fix is possible
The model needs to be replaced with a higher-parameter version
The severity Score question is miscalibrated and should be removed
What is the key difference between this chapter's confidence ladder and Chapter 7's Noul-probability ladder?
Confidence measures how concentrated a Choice/Score answer's distribution is, while Noul probability measures the likelihood that a statement is true — different quantities on the same 0-to-1 scale
They are the same measurement, just renamed in different chapters for clarity
Noul probability only applies to destructive actions, while confidence only applies to read-only actions
Confidence is always higher than Noul probability for the same action
In the SEC filings example, uncertain predictions forced into specific categories were 40% accurate, while the same predictions reported one level up the hierarchy (as divisions) were 70% accurate. What lesson does the chapter draw from this?
Hierarchical fallback — degrading a low-confidence answer to a coarser but more reliable label nearly doubles accuracy on exactly the cases the system would otherwise get wrong
Confidence scores should never be trusted below 0.9
The model should always be asked to reconsider with a second validation call when confidence is low
SEC filings are a poor use case for TypeSafe and should be routed entirely to humans
What Confidence Measures
Key Points
Confidence is a derived statistic that collapses a full probability distribution into one number from 0 to 1 — it is not the model's self-assessment in prose.
A peaked distribution (mass concentrated on one option) yields high confidence; a flat distribution (mass spread across options) yields low confidence, even when choice is identical.
Reading only choice hides the difference between a near-certain answer and a weak guess barely above random chance.
The full probabilities array reveals how the model is uncertain: a two-way near-tie is actionable (route to a combined queue or add a hierarchical fallback), while a genuine four-way scatter usually needs a human.
Persistently low confidence on a question is usually a defect report about your criteria (overlapping definitions) or your state (thin context) — and it is a different quantity entirely from Chapter 7's Noul-probability ladder.
TypeSafe's confidence works like a five-minute utilization average: it collapses many possible values into one summary figure, and is therefore useful but lossy. Every Choice and Score answer already carries a full probability distribution, and confidence is a statistic computed from it — a property of numbers the model already produced, not a heuristic bolted on afterward.
Peaked Versus Flat Distributions
The shape that matters is concentration. A flat shape, with probability spread across several options, means low confidence. A single peak on one option means high confidence. Two answers to the same NOC triage question illustrate the point:
Team option
Incident A: peaked
Incident B: flat
dc_fabric
0.91
0.34
network_core
0.05
0.29
wan_transport
0.03
0.22
wireless
0.01
0.15
Selected choice
dc_fabric
dc_fabric
Reported confidence
~0.93 (high)
~0.38 (low)
Both answers return the same choice, because choice is simply the highest-probability option. If your code reads only choice, the two incidents are indistinguishable — but Incident A is a fabric event that could hardly be anything else, while Incident B is a 34% guess in a four-way race where random guessing scores 25%.
Same selected label, same field value, entirely different events. The left one is a fabric alert that could hardly be anything else. The right one is a 34% guess in a four-way race where blind guessing scores 25%.
Visual animation — coming soon
Confidence as a Summary; Probabilities as the Full Picture
Confidence is the five-minute average; probabilities is the per-sample detail. Consider two low-confidence answers that both report roughly 0.4:
...versus a genuinely four-way split at 0.30 / 0.26 / 0.23 / 0.21. The first is a two-horse race: the model has firmly ruled out wireless and transport and cannot separate fabric from core — actionable, route to a combined queue. The second is noise, and the right move is a human.
This is the hierarchical fallback idea: across 60 SEC filings with a threshold at 0.9, confident predictions were 90% accurate, uncertain predictions forced to be specific were 40% accurate, and those same uncertain predictions reported one level up as divisions were 70% accurate. Degrading gracefully nearly doubled the accuracy of exactly the cases the system would otherwise get wrong.
Low Confidence as a Signal the Question or State May Be Wrong
Persistent low confidence deserves the same suspicion network engineers apply to an impossible interface counter — check whether you polled the right OID before blaming the device. Three causes account for most of it:
Overlapping criteria. If two options' descriptions both say "switching infrastructure," the model has no basis for separating them. Use structured criteria (what, not_for, examples) to draw the boundary explicitly.
Thin state. A syslog line with no peer address, device role, or site tag genuinely does not identify an owning team — the low confidence is correct, and the fix is enriching state before you ask.
A question that is not atomic. "Which team should own this and is it urgent?" is two questions; the symptom is broad, persistently mediocre confidence across many incidents rather than a hard split between two options.
One clarification worth holding onto for the rest of the chapter and the next: this chapter's confidence ladder is not the same thing as Chapter 7's Noul-probability ladder. Noul probability scores the likelihood that a statement is true; confidence scores how concentrated a Choice or Score answer's distribution is. They are different quantities that happen to share the same 0-to-1 range, so a given Noul probability value and a confidence value of the same magnitude are not interchangeable — the two ladders are tuned independently. Where the action class is the same, the two tables agree by design: read-only enrichment clears at 0.50 on both, routing and assignment at 0.60, and anything destructive at 0.95 plus corroboration.
Key Takeaway: Confidence is a statistic that collapses the shape of an answer's probability distribution into a single number from 0 to 1 — peaked means high, flat means low — computed from probabilities the model already produced, not from self-assessment. Route on confidence, but inspect probabilities when you need to know how the model is uncertain, because a two-way split and a four-way split call for different fallbacks. Persistently low confidence on a question is a defect report about your criteria or your state, not about the model.
Post-Quiz: What Confidence Measures
Two incidents both return choice = "dc_fabric", but Incident A reports confidence ~0.93 and Incident B reports confidence ~0.38. What does this difference indicate?
Incident A's probability distribution is peaked on dc_fabric while Incident B's is flat across several options, even though both picked the same top option
Incident B used a different, less capable model than Incident A
Incident A's probabilities don't sum to 1.0 while Incident B's do
The choice field is unreliable and should be ignored whenever confidence is reported
Why does the chapter recommend inspecting the full probabilities field rather than confidence alone when confidence is low?
Because probabilities reveal how the model is uncertain — e.g., a near two-way tie can be routed to a combined queue, while a genuine four-way scatter usually needs a human
Because the confidence field is only available on Score answers, not Choice answers
Because probabilities are always more accurate than the confidence statistic
Because TypeSafe recomputes confidence incorrectly and probabilities are the corrected version
A ticket owner question consistently splits confidence roughly 50/50 between network_core and dc_fabric across many different incidents. What does the chapter say this pattern most likely indicates?
A criteria defect — the two options' descriptions probably overlap and need clearer what/not_for/examples boundaries
The incidents are genuinely ambiguous and no fix is possible
The model needs to be replaced with a higher-parameter version
The severity Score question is miscalibrated and should be removed
What is the key difference between this chapter's confidence ladder and Chapter 7's Noul-probability ladder?
Confidence measures how concentrated a Choice/Score answer's distribution is, while Noul probability measures the likelihood that a statement is true — different quantities on the same 0-to-1 scale
They are the same measurement, just renamed in different chapters for clarity
Noul probability only applies to destructive actions, while confidence only applies to read-only actions
Confidence is always higher than Noul probability for the same action
In the SEC filings example, uncertain predictions forced into specific categories were 40% accurate, while the same predictions reported one level up the hierarchy (as divisions) were 70% accurate. What lesson does the chapter draw from this?
Hierarchical fallback — degrading a low-confidence answer to a coarser but more reliable label nearly doubles accuracy on exactly the cases the system would otherwise get wrong
Confidence scores should never be trusted below 0.9
The model should always be asked to reconsider with a second validation call when confidence is low
SEC filings are a poor use case for TypeSafe and should be routed entirely to humans
Pre-Quiz: Thresholds, Tiers, and Risk
The confidence.md and confidence-routing.md pages give slightly different numbers for the three-tier bands (e.g., high above 0.9 vs above 0.85). What point does the chapter make about this discrepancy?
The bands are illustrative starting points, not fixed settings — organizations should start conservative and adjust thresholds based on their own measured performance
One of the two documentation pages is outdated and should be ignored
The discrepancy proves TypeSafe's confidence scores are not reproducible
0.85 applies to Choice answers and 0.9 applies to Score answers
In the medium confidence tier, why does the chapter say auto-assigning an incident to a team can proceed automatically across the whole 0.60–0.85 band, while auto-resolving (closing) an incident should not?
Assignment is cheaply and quickly reversible (a human just reassigns it), while auto-resolution is effectively irreversible because a wrongly closed incident has no natural detection path
Assignment never affects customer-visible fields, while resolution always does
ServiceNow's API technically prevents PATCHing the state field below 0.85 confidence
Auto-resolution is cheaper to compute than assignment, so it warrants more caution
According to the risk-scaled threshold ladder, which starting threshold is correctly matched to its action class?
Assign/route an incident to a team: 0.60
State change/notify (e.g., paging on-call): 0.60
Read-only enrichment such as tagging: 0.85
Auto-resolve (close) an incident: 0.60
Why does the chapter argue that auto-resolving an incident deserves a stricter confidence gate (0.95+) than pushing a config change (0.90+), even though a config change can affect live production traffic?
A bad config change tends to announce itself through alarms, but a wrongly closed incident looks handled and may go unnoticed for hours — it has no natural detection path
Config changes are always reviewed by a human before being pushed, so they need a lower bar
Auto-resolution requires more API calls than a config change, increasing the chance of error
ServiceNow does not log auto-resolution actions, so the extra confidence compensates for the missing audit trail
A team builds a confidence-vs-accuracy bucket table from historical tickets and finds a 13-point accuracy drop between the 0.75–0.84 bucket (84% accurate) and the 0.60–0.74 bucket (71% accurate) — the steepest drop in the table. What does the chapter say to do with this finding?
Treat the cliff as a natural threshold location — a floor placed just above it captures most available coverage while avoiding the bucket where accuracy degrades fast
Ignore it, because cliffs in reliability diagrams are statistical noise and not actionable
Immediately lower all thresholds to 0.60 to capture more automation coverage
Conclude the model is fundamentally uncalibrated and abandon confidence-based routing
The Three-Tier Threshold Pattern
Key Points
The pattern separates the model's answer (what) from your automation's willingness to act (whether): high confidence acts automatically, medium confidence acts cautiously or asks for confirmation, low confidence hands off to a human.
TypeSafe's own documentation gives two slightly different band examples (0.85 vs 0.9 high; 0.5 vs 0.6 floor) — the discrepancy itself is the lesson: these are illustrative starting points, tuned to your domain.
The medium tier should split by the stakes of the action, not treat the whole band the same: reversible, low-cost actions can proceed automatically, while higher-stakes actions request confirmation.
Below the floor threshold, "route to a human" is a formal pattern called selective prediction — and the review queue's capacity is the binding constraint on how low that floor can go.
Thresholds must be runtime-adjustable, not compiled constants, so the escalation bar can rise when the human queue is drowning during a major incident.
The pattern fits in one sentence: "The answer tells you what; confidence tells you whether to act." Confidence-gated routing uses the confidence score as a decision filter alongside the answer, so the same choice value leads to different behavior depending on how strongly the model held it — the QoS-classification analogy: a marking doesn't change what a packet contains, only what the network is willing to do with it.
Tier
confidence.md guidance
confidence-routing.md guidance
What your automation does
High
Above 0.9: act automatically for high-stakes decisions
Act, but leave an audit trail; or act only on the low-risk subset
Low
Below 0.5: route to humans; genuine uncertainty
Below 0.6 (the floor): route to human support agents
Hand off to a person or a deterministic fallback
These are starting points, not settings — "the correct threshold values depend on your domain and the performance of the model for your use case." The two pages disagreeing slightly is itself the lesson: illustrative bands you replace with measured ones.
Figure 8.2: Three-tier confidence-gated routing
flowchart TD
A["Model produces confidence score"] --> B{"Confidence tier"}
B -->|"High: above 0.85 to 0.9"| C["Act automatically"]
B -->|"Medium: 0.5 to 0.9 or 0.6 to 0.85"| D["Act cautiously or request confirmation"]
B -->|"Low: below 0.5 or 0.6"| E["Route to a human"]
C --> F["No human involvement"]
D --> G["Audit trail or confirmation step"]
E --> H["Human review or coarser fallback"]
High Confidence: Act Without Human Involvement
The high tier is for answers where the probability mass sits on one option and the action is worth automating. In the NOC service, an access point that deauthenticates and re-associates inside a known maintenance window — a pattern closed as noise hundreds of times — should not consume a human minute at 0.97 confidence. Two rules keep this tier safe: write the confidence value and question name onto every automated action for auditability, and sample it — a random 2% of auto-actioned tickets into a weekly human review queue is how you learn a 0.97 answer stopped being 97% accurate.
Medium Confidence: Proceed with Caution or Flag
The middle tier is where most real traffic lands. Between 0.6 and 0.85, lower-risk actions such as checking an account balance proceed automatically, while higher-stakes actions request confirmation rather than acting independently. In incident terms, assigning to a team is the balance check — cheap to get wrong, trivially reversible — while auto-resolving is the transfer approval, where a wrong call closes a real outage silently. So the medium tier performs the reversible action and attaches a review flag: a work note carrying the confidence value, the runner-up option, and a marker asking a human to confirm.
Low Confidence: Route to a Human or a Fallback System
Below the floor, the model is telling you it does not know. "Route to a human" has a formal name — selective prediction, where a system may abstain rather than predict, with explicit risk control so the review queue is not overwhelmed. The binding constraint is capacity: if 30% of alerts fall below the floor and the night shift is two people, you have built a queue nobody drains, not a safety net. As the human queue lengthens, the confidence threshold for automation must decrease, meaning the bar for escalation must rise — a counter-cyclical rule, best expressed as a runtime value rather than a constant compiled into the triage service.
Key Takeaway: The three-tier pattern separates what the model answered from whether you should act on it: act automatically at high confidence, act reversibly with an audit flag in the middle, and hand off to a human or a coarser fallback below the floor. The documented bands — roughly above 0.85 or 0.9, a 0.5-to-0.9 or 0.6-to-0.85 middle, and a 0.5 or 0.6 floor — are explicitly starting points that depend on your domain and your model's performance. Size the low tier to the humans who must actually drain it, and make the thresholds runtime-adjustable so you can raise the escalation bar when the queue is drowning.
Scaling Thresholds to Risk
One threshold for a whole service is the most common mistake in this pattern. Different actions within the same system should be gated at different levels depending on the consequences of getting it wrong — matching thresholds to consequences prevents both over-caution (rejecting valid high-confidence requests) and dangerous under-caution (acting on uncertain high-risk decisions). The mental model is a change management process: a show command needs no approval, a VLAN description edit needs a peer, and a BGP policy change needs a CAB review.
Action class
Example
Blast radius if wrong
Reversibility
Suggested starting threshold
Read-only / enrich
Tag an incident, add a work note, populate a dashboard
None — a wrong label a human overrides
Instant, free
0.50
Assign / route
PATCH assignment_group on a ServiceNow incident
Minutes of the wrong team's attention
Reassign, seconds
0.60
State change / notify
Move to In Progress; page the on-call engineer
Wakes a person; consumes an escalation
Cannot un-page; apologize
0.85
Config change
Push a QoS or ACL change to an IOS-XE or EOS device
Can affect live traffic
Rollback window, minutes to hours
0.90+
Destructive / terminal
Auto-resolve an incident; suppress an alert class
A real outage closed silently, unnoticed for hours
Effectively none
0.95+
The last row surprises people: auto-resolving a ticket feels gentler than pushing a config change, so teams gate it loosely — but it deserves the strictest gate because it is the only action with no natural detection path. A bad config change announces itself through alarms; a wrongly closed incident announces itself when a customer calls three hours later.
Figure 8.3: Risk-scaled threshold ladder
flowchart LR
A["Read-only or enrich: 0.50"] --> B["Assign or route: 0.60"]
B --> C["State change or notify: 0.85"]
C --> D["Config change: 0.90 or higher"]
D --> E["Destructive or terminal: 0.95 or higher"]
Start Conservative, Measure, Then Loosen
Shadow mode, two weeks. Ask the questions on every incident and log answer and confidence, but take no action.
Read-only tier only. Turn on tagging and enrichment at 0.50. Nothing can break.
Assignment at a deliberately high bar. Enable auto-assignment at 0.90, and measure the reassignment rate.
Loosen in steps, measuring each. Drop to 0.85, then 0.80, then 0.75, and stop when the reassignment rate crosses what your team tolerates.
Auto-resolution last, on one narrow class. Enable it for a single well-understood noise pattern at 0.95, with 100% audit sampling for the first month.
Plotting Confidence Against Accuracy on Historical Tickets
Everything above depends on one table you can build from tickets you already closed — a reliability diagram: partition predictions into roughly ten equal-frequency bins, plot each bin's average predicted probability against the accuracy actually observed, and compare against the ideal diagonal where confidence equals accuracy.
Confidence bucket
Incidents
Model agreed with final owner
Cumulative coverage above bucket floor
0.95 – 1.00
812
98%
30%
0.90 – 0.94
430
95%
46%
0.85 – 0.89
361
91%
60%
0.75 – 0.84
402
84%
74%
0.60 – 0.74
355
71%
87%
0.50 – 0.59
188
58%
94%
Below 0.50
152
41%
100%
Read three things off it. The model is roughly calibrated — accuracy tracks confidence bucket by bucket. Pick each threshold by naming the accuracy you require: if auto-resolution must be 98% correct, the table says 0.95 and you automate 30% of volume. And find the cliff — the steepest drop in the table (13 points, between the 0.75–0.84 and 0.60–0.74 buckets) is a natural threshold location, since just above it you capture most available coverage and just below it accuracy degrades fast. Rebuild the table monthly — it is your drift detector.
Figure 8.4: The threshold-tuning loop
flowchart TD
A["Label historical tickets"] --> B["Run questions against ticket state"]
B --> C["Bucket answers by confidence"]
C --> D["Measure accuracy per bucket"]
D --> E["Set thresholds from required accuracy"]
E --> F["Deploy thresholds"]
F --> G["Monitor for drift"]
G --> A
Visual animation — coming soon
Key Takeaway: Different actions in the same service need different thresholds, scaled to blast radius and reversibility — roughly 0.50 for read-only enrichment, 0.60 for assignment, 0.85 for state changes and paging, and 0.95 or higher for auto-resolution, which is terminal and therefore the strictest gate despite feeling gentle. Start above those numbers, run in shadow mode to collect labels, and loosen one step at a time while watching the reassignment rate. Pick the final values from a confidence-versus-accuracy bucket table built on your own closed tickets, and rebuild it monthly to catch drift.
Post-Quiz: Thresholds, Tiers, and Risk
The confidence.md and confidence-routing.md pages give slightly different numbers for the three-tier bands (e.g., high above 0.9 vs above 0.85). What point does the chapter make about this discrepancy?
The bands are illustrative starting points, not fixed settings — organizations should start conservative and adjust thresholds based on their own measured performance
One of the two documentation pages is outdated and should be ignored
The discrepancy proves TypeSafe's confidence scores are not reproducible
0.85 applies to Choice answers and 0.9 applies to Score answers
In the medium confidence tier, why does the chapter say auto-assigning an incident to a team can proceed automatically across the whole 0.60–0.85 band, while auto-resolving (closing) an incident should not?
Assignment is cheaply and quickly reversible (a human just reassigns it), while auto-resolution is effectively irreversible because a wrongly closed incident has no natural detection path
Assignment never affects customer-visible fields, while resolution always does
ServiceNow's API technically prevents PATCHing the state field below 0.85 confidence
Auto-resolution is cheaper to compute than assignment, so it warrants more caution
According to the risk-scaled threshold ladder, which starting threshold is correctly matched to its action class?
Assign/route an incident to a team: 0.60
State change/notify (e.g., paging on-call): 0.60
Read-only enrichment such as tagging: 0.85
Auto-resolve (close) an incident: 0.60
Why does the chapter argue that auto-resolving an incident deserves a stricter confidence gate (0.95+) than pushing a config change (0.90+), even though a config change can affect live production traffic?
A bad config change tends to announce itself through alarms, but a wrongly closed incident looks handled and may go unnoticed for hours — it has no natural detection path
Config changes are always reviewed by a human before being pushed, so they need a lower bar
Auto-resolution requires more API calls than a config change, increasing the chance of error
ServiceNow does not log auto-resolution actions, so the extra confidence compensates for the missing audit trail
A team builds a confidence-vs-accuracy bucket table from historical tickets and finds a 13-point accuracy drop between the 0.75–0.84 bucket (84% accurate) and the 0.60–0.74 bucket (71% accurate) — the steepest drop in the table. What does the chapter say to do with this finding?
Treat the cliff as a natural threshold location — a floor placed just above it captures most available coverage while avoiding the bucket where accuracy degrades fast
Ignore it, because cliffs in reliability diagrams are statistical noise and not actionable
Immediately lower all thresholds to 0.60 to capture more automation coverage
Conclude the model is fundamentally uncalibrated and abandon confidence-based routing
Pre-Quiz: Worked Example — Auto-Close, Assign, or Page
In the triage() function, why does one client.system_one(...) call ask both a Choice (owner) and a Score (severity) question together rather than making two separate calls?
Asking multiple questions in a single request costs minimal additional tokens, and the two answers together decide both "where" to route and "how loudly" to escalate
The TypeSafe SDK does not support making more than one API call per incident
ServiceNow's Table API requires exactly one PATCH per TypeSafe request
Combining questions increases the confidence score reported for each answer
Why does the chapter recommend storing routing targets like fallback_group and each entry in team_groups as ServiceNow sys_ids rather than display names?
A sys_id is the stable reference that keeps working even if someone renames the group later, whereas a display name would break
ServiceNow's PATCH endpoint only accepts sys_ids, never display names, for any field
Sys_ids are required to compute the TypeSafe confidence score correctly
Display names cannot be stored in a Python dictionary
In assign(), under what condition does the function add a "[REVIEW]" flag to the work note asking a human to confirm the assignment?
When confidence is at or above the 0.60 assignment floor but still below 0.85
Whenever the incident is routed to the known_noise option
Whenever confidence is at or above 0.95
Only when the model's top two probabilities are exactly tied
An incident's owner confidence comes back at 0.45 (below the assignment floor), and its severity Score is 4.2 (above page_min_severity). According to escalate(), what happens to the incident?
It is PATCHed to the fallback group with a work note carrying the model's best guess and distribution, AND an on-call page is fired because severity is high
It is discarded with no action, since confidence is too low to act on at all
It is auto-resolved as known noise regardless of the owner choice, since severity is high
It is assigned directly to the model's best-guess team without any review flag
Why does the chapter describe assigning an unclear incident to "Network Operations" (the parent group) in escalate() as a hierarchical fallback rather than a failure to act?
It mirrors the technique of reporting a coarser, more reliable label instead of forcing a narrow guess or rejecting the answer outright, and it keeps the model's shortlist attached to the ticket
Network Operations is the team with the highest confidence score among all four options
It is a temporary bug that the chapter recommends fixing in a future version
It only happens when the TypeSafe API call fails entirely
Worked Example: Auto-Close, Assign, or Page
Key Points
One client.system_one call can ask multiple questions at once (a Choice for owner, a Score for severity) at minimal extra cost, giving both "where" and "how loudly" from a single request.
All thresholds, ServiceNow state values, and routing targets live in one CONFIG dictionary so the escalation bar can be raised during a major incident as a config edit, not a code change.
High confidence (≥ 0.95) on the known_noise choice triggers PATCH to state=6 (Resolved) with close_code and close_notes recording the confidence and runner-up option.
Medium confidence (≥ 0.60) on a real team PATCHes assignment_group and adds a review-flag work note when confidence is still below 0.85, asking a human to confirm.
Below the 0.60 floor, the incident is never dropped — it is assigned to a fallback group (a hierarchical fallback) and, if severity is high, an on-call page is fired, carrying the model's best guess and full distribution along with it.
team_groups and fallback_group now hold ServiceNow sys_ids, not display names, because a sys_id survives someone renaming the group.
One client.system_one(...) call asks two questions about an incoming ServiceNow incident: a Choice for the owning team — with a fifth option for known transient noise — and a Score for severity. The confidence values then select one of three actions against the ServiceNow Table API. Incidents are updated with PATCH /api/now/table/incident/[sys_id]; PATCH updates only the fields you send, unlike PUT which replaces the whole record. The state field is numeric — 1 = New, 2 = In Progress, 3 = On Hold, 6 = Resolved, 7 = Closed.
Every tunable value lives in one dictionary at the top, so raising the escalation bar during a major incident is a config edit rather than a code change:
CONFIG = {
# TypeSafe
"model": "jev-latest",
# ServiceNow
"sn_instance": "https://acme.service-now.com",
"sn_incident_table": "/api/now/table/incident",
"sn_state_in_progress": "2",
"sn_state_resolved": "6",
"sn_close_code": "Solved (Permanently)",
# Confidence thresholds -- STARTING POINTS from the TypeSafe docs,
# replaced by values read off our own confidence-vs-accuracy table.
"auto_resolve_min_confidence": 0.95, # terminal action: strictest gate
"assign_min_confidence": 0.60, # reversible action: middle tier
"review_flag_max_confidence": 0.85, # below this, flag for human review
# Severity gate for paging on low-confidence answers
"page_min_severity": 3.0, # index into the severity legend
# Routing targets. Values are sys_ids from sys_user_group in YOUR
# instance -- a group's display name also resolves, but sys_id is
# the reference that survives someone renaming the group.
"noise_option": "known_noise",
"fallback_group": "<sys_id of Network Operations>",
"team_groups": {
"dc_fabric": "<sys_id of DC-Fabric>",
"network_core": "<sys_id of Network-Core>",
"wan_transport": "<sys_id of WAN-Transport>",
"wireless": "<sys_id of Wireless-NOC>",
},
}
def triage(incident: dict) -> dict:
response = client.system_one(state=state, questions=QUESTIONS, model=CONFIG["model"])
owner = response.answers["owner"]
severity = response.answers["severity"]
sys_id = incident["sys_id"]
# Tier 1: high confidence on known noise -> auto-resolve
if owner.choice == CONFIG["noise_option"] and owner.confidence >= CONFIG["auto_resolve_min_confidence"]:
return auto_resolve(sys_id, owner)
# Tier 2: medium confidence on a real team -> assign, flag if borderline
if owner.choice != CONFIG["noise_option"] and owner.confidence >= CONFIG["assign_min_confidence"]:
return assign(sys_id, owner)
# Tier 3: below the floor -> page or park for a human
return escalate(sys_id, owner, severity)
ServiceNow Incidents: Auto-Resolve Known Noise at High Confidence
This is the strictest gate in the service, and it fires only when the model both picks known_noiseand is peaked on it. The close_notes record the confidence and the runner-up, so a reviewer auditing a wrongly closed ticket sees exactly how close the call was. A 403 on the PATCH is not a transient failure to retry — it will fail forever until someone grants the integration user the role.
Assign to a Team at Medium Confidence with a Review Flag
The assignment is the reversible action, so it proceeds automatically across the whole medium band. What changes inside the band is the review flag: between 0.60 and 0.85 the work note asks a human to confirm; above 0.85 it does not. Writing the top three probabilities into the note makes graceful degradation visible — on a near-tie between two teams, the receiving engineer sees it immediately and knows where to bounce it. work_notes is the internal activity log, and u_ai_confidence is a custom field so the monthly bucket table is a database query rather than a log-parsing exercise.
Page On-Call at Low Confidence for High-Severity Guesses
Notice what the low tier does not do: throw the answer away. It assigns to Network Operations, the parent group above the four specific teams — a hierarchical fallback, reporting a coarser level rather than forcing a narrow category — and the best guess plus the full distribution travel with the ticket. The severity gate keeps paging humane: low confidence on a low-severity event is a queue item, while low confidence on something scored major or critical is a page, because an unclassified potential outage is exactly when a human must look now.
Figure 8.5: Auto-close, assign, or page worked example
sequenceDiagram
participant Incident as ServiceNow Incident
participant Triage as Triage Service
participant TypeSafe as TypeSafe Model
participant Table as ServiceNow Table API
participant OnCall as On-Call Engineer
Incident->>Triage: New incident created
Triage->>TypeSafe: Ask owner and severity questions
TypeSafe-->>Triage: Choice, Score, confidence
alt High confidence known noise
Triage->>Table: PATCH state to Resolved with close_code
else Medium confidence real team
Triage->>Table: PATCH assignment_group with work_notes
else Low confidence high severity
Triage->>Table: PATCH assignment_group to fallback group
Triage->>OnCall: Page with confidence and distribution
end
Visual animation — coming soon
Key Takeaway: A single client.system_one call carrying a Choice for ownership and a Score for severity drives three different ServiceNow actions through one confidence ladder: PATCH state to 6 with close_code and close_notes for known noise above 0.95, PATCH assignment_group with a confidence-bearing work_notes entry above 0.60, and a coarse fallback assignment plus an on-call page below the floor when severity is high. Keeping every threshold, state value, and group name in one configuration dictionary makes the escalation bar adjustable at runtime. Below the floor the answer is still written to the ticket as a shortlist — low confidence degrades the action, not the information.
Post-Quiz: Worked Example — Auto-Close, Assign, or Page
In the triage() function, why does one client.system_one(...) call ask both a Choice (owner) and a Score (severity) question together rather than making two separate calls?
Asking multiple questions in a single request costs minimal additional tokens, and the two answers together decide both "where" to route and "how loudly" to escalate
The TypeSafe SDK does not support making more than one API call per incident
ServiceNow's Table API requires exactly one PATCH per TypeSafe request
Combining questions increases the confidence score reported for each answer
Why does the chapter recommend storing routing targets like fallback_group and each entry in team_groups as ServiceNow sys_ids rather than display names?
A sys_id is the stable reference that keeps working even if someone renames the group later, whereas a display name would break
ServiceNow's PATCH endpoint only accepts sys_ids, never display names, for any field
Sys_ids are required to compute the TypeSafe confidence score correctly
Display names cannot be stored in a Python dictionary
In assign(), under what condition does the function add a "[REVIEW]" flag to the work note asking a human to confirm the assignment?
When confidence is at or above the 0.60 assignment floor but still below 0.85
Whenever the incident is routed to the known_noise option
Whenever confidence is at or above 0.95
Only when the model's top two probabilities are exactly tied
An incident's owner confidence comes back at 0.45 (below the assignment floor), and its severity Score is 4.2 (above page_min_severity). According to escalate(), what happens to the incident?
It is PATCHed to the fallback group with a work note carrying the model's best guess and distribution, AND an on-call page is fired because severity is high
It is discarded with no action, since confidence is too low to act on at all
It is auto-resolved as known noise regardless of the owner choice, since severity is high
It is assigned directly to the model's best-guess team without any review flag
Why does the chapter describe assigning an unclear incident to "Network Operations" (the parent group) in escalate() as a hierarchical fallback rather than a failure to act?
It mirrors the technique of reporting a coarser, more reliable label instead of forcing a narrow guess or rejecting the answer outright, and it keeps the model's shortlist attached to the ticket
Network Operations is the team with the highest confidence score among all four options
It is a temporary bug that the chapter recommends fixing in a future version
It only happens when the TypeSafe API call fails entirely