Did the data say that, or did the AI?
Why does GLAMLI now split every explanation into ground truth and AI interpretation?
I’m working on GLAMLI for my Master’s in AI dissertation. GLAMLI is a web-based machine learning application built around WEKA. Users can upload a raw CSV dataset, and GLAMLI guides them through preparing the data, choosing what to predict, training and comparing models.
A big part of that experience is LLM-generated copy. The app describes your columns, narrates your model results, and generally tries to be a helpful guide.
One of the LLM-generated texts in GLAMLI is the column description. What I noticed is that the LLM mixes facts it gathered with what it theorized. Here’s a concrete example. I uploaded bank-full.csv, the UCI Bank Marketing dataset.
Just the CSV, nothing else. The app described the marital column like this:
The ‘marital’ column shows the marital status of each person, such as married, single, or divorced. Marital status can affect financial priorities and decisions, making it a useful factor in understanding customer behavior.
Look at what I actually uploaded as a CSV:
Inspecting the first sentence of the description, you can say that it is true. The column really does contain married, single, and divorced. But the second sentence? Marital status can affect financial priorities and decisions, making it a useful factor in understanding customer behavior. That’s nowhere in the CSV! The LLM made it up or learned it from its training dataset.
For the marital column, this feels harmless. Most of what the LLM stated is common knowledge. But what happens if you’re working in an area where you’re not familiar?
Let’s take, for example, this dataset that I test GLAMLI with: kc1, a NASA software-defect dataset full of code metrics.
The LLM tells you ‘i’ likely represents Halstead intelligence? The values of i are numbers from 0-103. Could you tell whether that came from your data or from the model’s training data, or it’s just a hallucination?
LLMs’ added theory on the column description has value. It gives you a starting point to do additional verification. In the case of i in the kc1 dataset, you can dig deeper into what Halstead Intelligence is.
That said, is there a way apps like GLAMLI can help you distinguish between facts and what’s made up by LLMs? How do you let a user distinguish between what comes from the data and what the LLM theorized?
Here’s the walkthrough of this post:
The research: PaperTrail
While searching for UX patterns I could apply to GLAMLI to solve the distinction problem, I stumbled upon PaperTrail: A Claim-Evidence Interface for Grounding Provenance in LLM-based Scholarly Q&A (Martin-Boyle et al., CHI ‘26). It’s the closest thing to the problem I described on GLAMLI.
What the PaperTrail paper built:
A Q&A interface for scholars that breaks both the LLM’s answer and the source papers down into small, atomic claims, then matches them against each other.
The reader sees which answer claims are supported or unsupported by the papers
The reader sees paper claims the LLM left out with a “Claim Coverage” bar summarising the coverage.
The architecture is a three-stage pipeline:
1. Claims are extracted from the papers offline.
When the LLM produces an answer, a separate extraction model breaks that answer into claims and evidence spans. Keeping the answerer and extractor separate allows PaperTrail to analyze outputs from different LLMs.
The system first retrieves source claims relevant to the question. An LLM then decides which answer claims and source claims are semantically equivalent. Similarity scores are also used to flag potentially unsupported evidence.
The study they conducted was with 26 participants. PaperTrail significantly lowered people’s trust in the LLM, meaning they questioned the answers the LLM generated. Which I think is a good thing since people are more vigilant. The problem is it did not change the participants’ behavior.
The takeaway I carried into GLAMLI is that there should be a separation between verified claims and unverified ones.
Adapting it to GLAMLI
I adapted PaperTrail’s claim-evidence separation to GLAMLI. But I had one big advantage over their setting. PaperTrail has to estimate whether a sentence is supported, because its ground truth is unstructured text in PDFs.
In GLAMLI, the ground truth is based on model metrics coming straight out of WEKA (the Java machine-learning toolkit GLAMLI runs on) when it evaluates a model.
I don’t have to guess whether a number is supported. I can compute it, and refuse to let the LLM edit or stray from the number.
That principle shows up in two places in the architecture, at different strengths:
The column lane (top) is what produced the marital description. When you upload a CSV, WEKA profiles it based on: attribute names, types, per-column stats, class distribution.
The verified facts are handed to the LLM. The LLM then writes a narrative for every column in one structured call, and the output contract (a Pydantic schema the model’s response must parse into) forces it to split each narrative into two halves:
class ColumnNarrative(BaseModel):
column: str
grounded: str | None = None
grounded_source: Literal[”dictionary”, “values”] | None = None
theorized: strIn the prompt to the LLM, there is a rule that I think stops the hallucination: “never move an inference into grounded, and when in doubt, leave it null”. A Python validation pass then drops any narrative for a column that doesn’t actually exist in the schema.
Both the AI theory and grounded explanation of the column are still LLM-written, but the model has to declare which is which. The LLM has to say where the grounded half came from (the data dictionary or the column’s values), and an empty grounded block is preferred over a padded one.
The model card lane (bottom) is where GLAMLI explains a trained model. Every numeric fact (baseline comparison, cross-validation spread, confusion matrix, residuals) is computed in Python from the persisted evaluation results before the LLM is called.
Those computed answers are pasted into the prompt as text the model must restate. And then, after generation, the same Python values overwrite the LLM’s output verbatim:
# Overwrite every deterministic field with the Python-built value verbatim, so
# a model that “helpfully” reworded a number (or claimed a status it shouldn’t
# have) can never actually change what’s shown - defence in depth on top of
# the prompt instruction.
update: dict = dict(computed)
update[”confusion_matrix”] = matrix
update[”class_labels”] = labels
card = card.model_copy(update=update)See the last line. It says that whatever the LLM wrote for those fields is simply discarded.
The result
This is how it looks in GLAMLI today:
The two kinds of content are now visibly separated. In the column modal, the grounded half sits at the top under a label that names its source (“From your data dictionary” or “From this column’s values”), then comes the column’s actual distribution chart, and only after the chart comes the block labeled “AI interpretation”.
I also added a little pill on the interpretation block that quotes the interpretation into the chat if you want to ask the AI more about it.
Is the problem solved? Not fully. The theorized half can still be wrong; it’s just honestly labeled now.
Separating “this is your data” from “this is what the AI thinks about your data” turned out to be one of those changes that’s a relatively small change on the back end but psychologically is a huge step.
Now I can say that the users could stop asking “did the data say that, or did the AI?”, because the user interface already answered it.








