Explainable AI (XAI) narratives: turn technical XAI outputs into clear natural-language explanations.
Project description
xains
xains generates explainable AI (XAI) Narratives - hence the name. It turns technical XAI outputs, e.g. SHAP or LIME attributions and counterfactuals, into clear natural-language narrative that makes the explanation of the model's decision understandable to a broad audience.
Installation
xains is intended to work with Python 3.11 and above.
Installation can be done via pip :
pip install xains
Or via uv :
uv add xains
Quickstart
Imagine a classifier flagged this applicant as a likely default. The raw feature importances (for example from SHAP) are: {debt_to_income: +0.37, salary: -0.21, age: -0.12}. Accurate, but is that understandable to a broad audience? xains turns it into a narrative.
xains needs three things: a schema (what the features and target mean), a request (this instance plus its importances), and an explainer (which model verbalizes it).
import xains
import xains.prompts
schema = xains.DatasetSchema(
modality=xains.Modality.TABULAR,
name="credit_risk",
description="Predicts 24-month default on personal loans.",
target=xains.TargetSchema(
name="default",
description="Whether the applicant defaulted.",
classes={0: "Repaid", 1: "Defaulted"},
),
features=[
xains.FeatureSchema(name="age", dtype="numeric", unit="years",
description="Applicant age at application."),
xains.FeatureSchema(name="salary", dtype="numeric", unit="EUR",
description="Annual gross salary."),
xains.FeatureSchema(name="debt_to_income", dtype="numeric",
description="Debt-to-income ratio."),
],
)
request = xains.TabularExplanationRequest(
features={"age": 29, "salary": 52000, "debt_to_income": 0.41},
prediction=xains.Prediction(predicted_class=1, probabilities={0: 0.2, 1: 0.8}),
contributions=[
xains.TabularContribution(name="debt_to_income", value=0.41, importance=0.37),
xains.TabularContribution(name="salary", value=52000, importance=-0.21),
xains.TabularContribution(name="age", value=29, importance=-0.12),
],
)
llm = xains.AnthropicProvider(model="claude-haiku-4-5", max_tokens=512)
explainer = xains.Explainer(
schema=schema,
generator=xains.LLMNarrativeGenerator(
prompt_template=xains.prompts.FeatureImportanceTabularPromptTemplate(),
llm=llm,
),
config=xains.ExplanationConfig(
mode="feature_importance", audience="end_user",
max_length_words=40, extract_narrative=True,
),
judge_llm=llm, # required when extract_narrative=True
)
result = explainer.explain(request)
print(result.text)
The resulting XAIN (XAI narrative):
Your profile indicates elevated default risk. A debt-to-income ratio of 0.41
substantially increases this concern, signaling that your debt obligations
consume a meaningful portion of earnings. Although your salary of EUR 52,000
and relatively young age of 29 provide some protective factors that work
against default, they ultimately prove insufficient to offset the debt burden
weighing on your financial stability.
Scoring the narrative
A narrative is only useful if it is faithful to the attributions and reads well. xains scores both. grade_extraction checks the claims the narrative makes against the input attributions - sign, value, and rank fidelity, coverage, and hallucination count:
grades = xains.grade_extraction(
extraction=result.narrative_extraction,
request=request,
schema=schema,
narrative_text=result.text,
k=5,
)
print(xains.render_grades(extraction=grades))
Verbalization fidelity
sign_faithfulness ↑: 1.0
value_faithfulness ↑: 1.0
rank_correlation ↑: 1.0
coverage ↑: 1.0
hallucination_count ↓: 0
Arrows mark the desired direction for each metric (↑ higher is better, ↓ lower is better). render_grades also accepts a narrativity= argument and emits a second Narrativity section.
grade_narrativity scores how well the text reads as a narrative, using the metrics from Cedro & Martens 2026. It needs a perplexity provider (any OpenAI-compatible endpoint that returns logprobs):
from xains.metrics import OpenAICompatibleEchoProvider
ppl = OpenAICompatibleEchoProvider(
base_url="https://api.together.xyz/v1",
model="meta-llama/Meta-Llama-3-8B-Instruct-Lite",
api_key_env_var="TOGETHER_API_KEY",
)
narrativity = xains.grade_narrativity(result.text, ppl)
print(xains.render_grades(narrativity=narrativity, scored_only=True))
Narrativity
csr ↑: 0.27
dcpr ↓: 1.3
ccpr ↓: 203.14
cecpr ↓: 29252.68
fdr ↑: 0.29
ttcpr ↓: 2.29
vcpr ↓: 50.79
These are the seven Cedro & Martens 2026 narrativity metrics. scored_only=True hides the nine auxiliary primitives (ppl_ordered, ttr, n_sentences, ...) which grade_narrativity also captures for paper replication; omit the flag to see them.
End-to-end notebook
For the full pipeline (load German Credit, train a RandomForest, compute SHAP, generate the narrative, extract structured claims, and score on faithfulness and narrativity), see the tutorial in notebooks/01_quickstart.ipynb.
API keys
The remote LLM providers need API keys (set them in your environment or a .env file; see .env.example).
Choosing a model
Any LLMProvider drops into xains.LLMNarrativeGenerator(llm=...) - pick the provider for the model you want:
import xains
# Anthropic (reads ANTHROPIC_API_KEY)
llm = xains.AnthropicProvider(model="claude-haiku-4-5", max_tokens=512)
# OpenAI (reads OPENAI_API_KEY)
llm = xains.OpenAIProvider(model="gpt-4o-mini", max_tokens=512)
# OpenRouter - Llama, and many others (reads OPENROUTER_API_KEY)
llm = xains.OpenRouterProvider(model="meta-llama/llama-3.3-70b-instruct", max_tokens=512)
# Any OpenAI-compatible endpoint (Together, Groq, vLLM, ...) - set base_url + the env var to read
llm = xains.OpenAICompatibleProvider(
base_url="https://api.together.xyz/v1",
api_key_env_var="TOGETHER_API_KEY",
model="meta-llama/Llama-3.3-70B-Instruct-Turbo",
max_tokens=512,
)
License
MIT - see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xains-0.1.1.tar.gz.
File metadata
- Download URL: xains-0.1.1.tar.gz
- Upload date:
- Size: 43.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3175bb71818ae263b00c1b9e890c011fd0ed8f6ab91b530dd9da5ec4775122f4
|
|
| MD5 |
498e2100905f18a6425fe0a6cca547d3
|
|
| BLAKE2b-256 |
f7e7d3cedd5c0b70ac9d6e0cf4b000560de8e18b2c9f6737e126cda80a658671
|
File details
Details for the file xains-0.1.1-py3-none-any.whl.
File metadata
- Download URL: xains-0.1.1-py3-none-any.whl
- Upload date:
- Size: 53.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a312cc66feecd28ee483663b0f49de22464425d9d92d8e07a2f60428e00119c
|
|
| MD5 |
31c1239b513dc4ccff9b23efa37782a6
|
|
| BLAKE2b-256 |
1631c987ae2112e8604ef89ae9b6f67ea8d1034e7ae99370ec9df366377edf1c
|