Natural Language Explanations
The "Why this tool?" card inside the Sankey view on a trace detail page translates SHAP feature-importance scores into a plain-English paragraph. If you have an LLM provider key configured, the paragraph is generated by a small judge model; otherwise a deterministic template fallback keeps the surface useful.
Why a second surface (SHAP isn't enough)?
SHAP scores are dense: feature: query_specificity · weight: 0.41
tells a data scientist a lot and a product manager nothing. The NL
explanation is a two-sentence synthesis designed to onboard
non-technical reviewers without hiding the underlying numbers — the
SHAP bars stay visible below the card.
How it renders
On /traces/[id] → Sankey tab, after the ablation has run:
┌────────────────────────────────────────────────────────┐
│ ✨ Why this tool? [ Regenerate ] │
│ Natural-language summary of the SHAP attributions │
│ for web_search │
│ │
│ The agent selected `web_search` primarily because │
│ `query_specificity` contributed 41% of the decision │
│ weight. Secondary factors were `recency_filter` and │
│ `source_quality`. │
│ │
│ Powered by gpt-4o-mini. │
└────────────────────────────────────────────────────────┘
Footer text shows either "Powered by judge_model" or "Template
fallback (no provider key)". The Regenerate button re-runs the
call without touching the underlying SHAP data.
Caller pattern
The endpoint is stateless — no span_id parameter. The frontend
first loads SHAP attributions for the selected span (via the existing
ablation hooks), then passes those attributions in:
useAblationShap(spanId)→ returns{ features[], tool_predicted, … }- Map
features→[{ phrase, score }] useNlExplanation()mutate with{ agent_action: tool_predicted, attributions, prompt, judge_model }- Render the returned
{ explanation, source }paragraph
This way, any surface that has attributions (counterfactual panels, assistant tiles, regression reports) can re-use the same NLG route without duplicating SHAP fetches.
Endpoint
POST /api/v1/xai/explain-attributions
// Request
{
"agent_action": "web_search",
"attributions": [
{ "phrase": "query_specificity", "score": 0.41 },
{ "phrase": "recency_filter", "score": 0.22 },
{ "phrase": "source_quality", "score": 0.15 }
],
"prompt": "latest AI safety papers 2026",
"judge_model": "gpt-4o-mini"
}
// Response
{
"explanation": "The agent selected `web_search` primarily because query_specificity contributed 41% of the decision weight…",
"source": "llm"
}source is "llm" when a provider key is configured
(AUDITTRAIL_NLG_OPENAI_KEY / AUDITTRAIL_NLG_ANTHROPIC_KEY),
otherwise "template".
User-scoped (bearer or sk-at-*). Rate limited at 20/minute per
client because it calls an external LLM.
UI mount point
The <NlExplanationCard /> component mounts at the top of the
InlineSankeyView panel on /traces/[id], above the SHAP / Ablation /
Tool-Probabilities grid. It also renders when the standalone
/sankey?traceId=… page has SHAP data available.
Shipped in: prod-v2.6.4 — backend nlg.py was in place since
V1.3; the UI card lands this sprint.