Key points
- UQLM is an open source Python package for scoring the reliability of a language model response at the moment it is generated, with no ground truth answer required.
- It combines four families of scoring technique in one library, black box consistency checks, white box token probability methods, LLM as a judge panels, and tunable ensembles of all three.
- Every scorer outputs a confidence value between zero and one, and the package plugs directly into any LangChain compatible chat model, which covers OpenAI, Anthropic, Google, Cohere, AWS Bedrock, and locally hosted models like Llama and Mistral.
- The ensemble approach can be tuned against a small set of graded examples, letting a team optimize the weighting for their own accuracy target rather than relying on defaults.
- The work was published in the Journal of Machine Learning Research and the code is public on GitHub under a permissive license.
The problem this actually solves
Ask a large language model a factual question and it will almost always answer in the same fluent, self assured tone whether it knows the answer or is guessing. Researchers call the guessing case a hallucination, a generated statement that is false or misleading but reads as if it were fact. The paper’s authors, led by Dylan Bouchard at CVS Health, frame the stakes plainly. In healthcare, legal, and financial settings, a hallucinated answer that looks exactly like a correct one is far more dangerous than an answer that visibly hedges.
The standard way to catch these errors is to grade a model’s output against a human written ground truth answer, the approach used by tools such as OpenAI’s Evals and G Eval. That works well during a pre deployment test cycle, when you already have a labeled dataset sitting next to your prompts. It falls apart the moment a real user sends a live question that nobody wrote a reference answer for in advance, which is most of the time a production system is actually running.
So the interesting engineering problem is not grading against a known answer. It is estimating, at the instant a model produces a response and with nothing else to compare it to, how much that response should be trusted. That is the definition of generation time hallucination detection, and it is the specific gap UQLM was built to fill.
Where the existing tools fall short
The paper is refreshingly specific about what already exists and where each approach runs out of road. Source comparison toolkits such as Ragas, Arize Phoenix, and DeepEval check whether a generated answer is consistent with the input prompt or retrieved context. That catches a model contradicting its own source material, but it will happily approve an answer that just parrots the prompt’s phrasing without being factually correct, since agreement with the prompt is not the same thing as agreement with reality.
Internet grounded checkers like FacTool go the other direction and verify claims against a live search. That adds real latency to every response and introduces a new failure mode, since the search results themselves can be wrong or out of date, which defeats the purpose of trying to reduce uncertainty.
Then there is the uncertainty quantification literature itself, which is large and mathematically rich but has mostly stayed inside research code. SelfCheckGPT popularized the idea of sampling a model multiple times and checking whether the answers agree with each other, but its toolkit only covers a narrow slice of the available techniques and does not tie generation and scoring together in one workflow. LM Polygraph, built on the Hugging Face ecosystem, is a genuinely strong collection of UQ methods, and the UQLM authors credit it as an important step, while noting that it still asks more of a non specialist practitioner than a small team building a product feature usually has time for.
UQLM’s pitch is that a team should not have to choose between a narrow, easy tool and a comprehensive, hard to adopt one. It packages a wide range of established UQ techniques from the academic literature behind a small, consistent API, and it fuses response generation with scoring so a developer calls one method and gets both the answer and its confidence score back together.
How UQLM is put together
The library organizes its scorers into four families, and the design choice that ties them together is worth noting before the details. Every class in the package, whether it belongs to the black box, white box, judge, or ensemble family, is built by handing it a LangChain chat model object and calling a generate and score method. That single method both produces the model’s response and computes its confidence score, which is what the authors mean when they say the package integrates generation with evaluation rather than treating them as two separate steps bolted together afterward.
Black box scoring
Black box methods do not look inside the model at all. They exploit the fact that language models are stochastic, meaning the same prompt sent twice can produce two different answers. UQLM generates an original response plus several additional candidate responses to the same prompt, then measures how much those responses agree with each other using a specified metric. The package supports several ways of measuring that agreement, including discrete semantic entropy, counting the number of distinct semantic clusters among the responses, non contradiction probability, entailment probability, BERTScore similarity, an exact match rate, and cosine similarity between response embeddings.
The intuition is straightforward once you see it. If a model is asked the same factual question five times and gives five different answers, that disagreement is itself a signal the model does not actually know the answer, regardless of how confident any single response sounds. The tradeoff is cost. Generating multiple responses to the same prompt means multiple calls to the underlying model, so black box scoring is compatible with any chat model but adds both latency and spend.
White box scoring
White box methods go the opposite direction and reach directly into the token probabilities the model produces while generating, which means they only work with APIs that actually expose log probabilities. The package’s single generation methods, including minimum token probability, length normalized token probability, sequence probability, likelihood margin, and top K token entropy measures, cost nothing extra since they are computed from a response you were already generating anyway. A second set of sampling based white box methods, including semantic entropy, semantic density, Monte Carlo predictive entropy, and a method called CoCoA, trade that speed advantage for typically stronger signal by drawing on multiple samples the same way the black box family does.
One additional white box technique worth calling out is the P(True) method, which asks the model a follow up question about whether its own prior answer was correct and treats the probability it assigns to yes as a further confidence signal. It needs one extra generation per response, a modest cost for what is effectively asking the model to self assess.
LLM as a judge
The third family sidesteps token probabilities entirely and instead asks one or more separate language models to grade a question and answer pair directly, using the LLMPanel class. A developer can register several judge models at once and choose from four scoring templates, a simple binary correct or incorrect scale, a three point scale that allows for an uncertain middle option, a continuous scale from zero to one, and a five point Likert scale. Custom instructions can be layered in through an additional context argument, which is useful when a team wants judges to apply domain specific correctness criteria rather than generic ones. Running several judges at once and looking at their average, minimum, and median gives a more stable read than trusting any single judge’s opinion.
Ensembles
The fourth and arguably most practical piece is the UQEnsemble class, which combines scorers from any of the previous three families into one weighted score. Out of the box, with no configuration, it defaults to a combination of exact match, non contradiction probability, and a self judge approach drawn from earlier work by Chen and Mueller. A team that wants better performance for their own use case can instead specify which scorers to include and then call a tune method, handing it a set of prompts paired with ideal answers. UQLM grades the model’s responses against that answer key, either with a grader function the team supplies or by falling back to the same LLM acting as its own grader, and then runs an optimization routine that solves for the scorer weights that best separate correct answers from incorrect ones.
That optimization can target different objectives depending on what a team cares about. A threshold agnostic objective like ROC AUC is useful when the downstream decision about where to draw the line between trusted and flagged is still open. A threshold dependent objective like F1 score is the better fit once a team has already committed to a specific cutoff, for example flagging anything scoring below 0.4 for human review. Once the weights are solved, they get stored on the ensemble object and reused for every future call, so the tuning step is something a team runs once against a small labeled set and then leaves alone.
The most useful thing about treating confidence as a tunable weighted score, rather than a fixed rule, is that a team gets to decide what kind of mistake they would rather make. Editorial observation, aitrendblend
What it actually costs to run each family
The paper includes a compatibility table in its appendix that is genuinely more useful for a planning conversation than any benchmark number would be, because it tells you upfront what each approach demands from your infrastructure before you write a line of code.
| Scorer family | Needs token log probabilities | Model compatibility |
|---|---|---|
| Black box UQ | No | Works with any chat model, since it only needs multiple generated responses to compare |
| White box UQ | Yes | Limited to chat models whose API actually exposes log probabilities |
| LLM as a Judge | No | Works with any chat model, since judging is itself just another generation task |
| UQ Ensemble | Depends | Determined by whichever individual scorers are included in the ensemble |
That log probability requirement is a more practical constraint than it might sound. Plenty of hosted APIs either do not return token probabilities at all or gate them behind specific request parameters, so a team building on top of a provider that does not expose them is effectively locked out of the entire white box family and needs to lean on black box consistency checks or judge panels instead. UQLM does not hide that limitation, it documents it directly and points developers to LangChain’s own guidance on which chat model wrappers support requesting log probabilities.
What this changes for teams shipping LLM products
The broader implication here is less about any single scoring technique and more about what it means for uncertainty quantification to finally show up as a well packaged software dependency rather than a research paper’s supplementary code. A method like semantic entropy has existed in the literature since Farquhar and colleagues published it in Nature in 2024, but a team without a research background historically had to translate that paper into working code themselves. UQLM’s contribution is mostly one of accessibility, taking a body of techniques scattered across a dozen papers and putting them behind one consistent interface that plugs into infrastructure teams already use.
That accessibility argument matters more in smaller organizations than large ones. A well resourced lab can afford to build internal tooling around any one of these techniques. A three person startup shipping a customer support bot cannot, and is far more likely to either skip confidence scoring entirely or bolt on something shallow like a fixed keyword filter. Lowering the engineering cost of a real uncertainty estimate changes the calculus for exactly that smaller team.
It is also worth being honest about what this kind of scoring does not do. A confidence score is a signal, not a guarantee. A high score means the model was internally consistent and its own probability estimates were strong, which correlates with correctness but is not the same thing as correctness. Teams that treat a UQLM score as a hard pass or fail gate without any human review in the loop for high stakes decisions are trading one kind of blind trust for another, just with better math underneath it.
Honest limitations
A few constraints are worth flagging plainly rather than glossing over. The paper itself is short, four pages of core content plus appendices, and it functions as a software announcement rather than a full empirical study. Detailed experiment results and head to head comparisons against the scorer families are pushed to a separate companion paper the same authors published, so a reader wanting hard numbers on which scorer performs best under which conditions needs to go read that second paper rather than expecting them here.
The tuning workflow also has a chicken and egg quality to it. Getting a well calibrated ensemble requires a set of prompts with known correct answers, which is exactly the kind of labeled data that is scarce for the live, unpredictable queries a production system actually receives. Teams will likely end up tuning against a curated historical sample and hoping it generalizes to new traffic, which is a reasonable approach but not a guarantee.
Finally, all four authors are employed by CVS Health and hold equity in the company, a conflict of interest the paper discloses directly. That does not undermine the technical content, which draws on peer reviewed methods and is independently verifiable through the public GitHub repository, but it is relevant context for readers evaluating the source.
A working reimplementation of the ensemble idea
To make the ensemble concept concrete, the code below is an original, simplified reimplementation of the core idea described in the paper, a weighted combination of a black box style consistency scorer and a white box style token probability scorer, tuned against a small labeled set. It is written for learning purposes and is not the UQLM library’s actual source code, which is publicly available at the GitHub link below if you want the real implementation.
# Educational reimplementation of the UQLM ensemble concept. # Not the actual UQLM source, written to illustrate the approach described in the paper. import torch import torch.nn as nn import torch.optim as optim class ConfidenceEnsemble(nn.Module): """ Combines a black box style consistency score and a white box style token probability score into one weighted confidence value, then lets the weights be tuned against labeled examples. """ def __init__(self, num_scorers=2): super().__init__() # start with equal weights, softmax keeps them positive and summing to one self.raw_weights = nn.Parameter(torch.zeros(num_scorers)) def forward(self, scorer_outputs): # scorer_outputs shape is (batch, num_scorers), each value already in [0, 1] weights = torch.softmax(self.raw_weights, dim=0) confidence = (scorer_outputs * weights).sum(dim=-1) return confidence def black_box_consistency_score(candidate_embeddings): """ Simple stand in for a black box scorer. Given several response embeddings for the same prompt, returns the average pairwise cosine similarity as a rough consistency estimate. """ normed = nn.functional.normalize(candidate_embeddings, dim=-1) sims = normed @ normed.transpose(-1, -2) n = sims.shape[-1] mask = ~torch.eye(n, dtype=torch.bool, device=sims.device) return sims[..., mask].view(sims.shape[0], n, n - 1).mean(dim=(-2, -1)) def white_box_min_probability_score(token_log_probs): """ Stand in for a white box scorer, the minimum token probability across the generated sequence, which is highly sensitive to a single weak or unlikely token anywhere in the response. """ return token_log_probs.exp().min(dim=-1).values def train_ensemble(model, scorer_outputs, labels, epochs=200, lr=0.05): """ Tunes the ensemble weights against labeled examples, playing the role of UQLM's tune method. Labels are 1.0 for a correct response and 0.0 for an incorrect one, gathered from a small answer key. """ optimizer = optim.Adam(model.parameters(), lr=lr) loss_fn = nn.BCELoss() for epoch in range(epochs): optimizer.zero_grad() predictions = model(scorer_outputs) loss = loss_fn(predictions, labels) loss.backward() optimizer.step() return model def evaluate(model, scorer_outputs, labels, threshold=0.5): """Reports simple accuracy at a chosen confidence threshold.""" with torch.no_grad(): predictions = model(scorer_outputs) predicted_labels = (predictions > threshold).float() accuracy = (predicted_labels == labels).float().mean().item() return accuracy if __name__ == "__main__": # Smoke test on dummy data, standing in for a real batch of prompts torch.manual_seed(0) batch_size = 64 num_candidates = 5 embedding_dim = 16 seq_len = 12 candidate_embeddings = torch.randn(batch_size, num_candidates, embedding_dim) token_log_probs = torch.log(torch.rand(batch_size, seq_len).clamp(min=1e-4)) black_box_scores = black_box_consistency_score(candidate_embeddings) white_box_scores = white_box_min_probability_score(token_log_probs) scorer_outputs = torch.stack([black_box_scores, white_box_scores], dim=-1) dummy_labels = (scorer_outputs.mean(dim=-1) > 0.5).float() ensemble = ConfidenceEnsemble(num_scorers=2) ensemble = train_ensemble(ensemble, scorer_outputs, dummy_labels) acc = evaluate(ensemble, scorer_outputs, dummy_labels) learned_weights = torch.softmax(ensemble.raw_weights, dim=0).detach() print(f"Smoke test accuracy on dummy data: {acc:.3f}") print(f"Learned scorer weights: {learned_weights.tolist()}")
Conclusion
UQLM’s core achievement is not any single scoring technique, most of which were already published elsewhere. It is the packaging decision to put black box consistency checks, white box token probability methods, LLM judge panels, and tunable ensembles behind one small, consistent interface, and to tie response generation and confidence scoring together into a single method call instead of two separate systems a team has to wire up themselves.
That packaging choice represents a real conceptual shift in how uncertainty quantification gets adopted. Research code that demonstrates a technique works is a different artifact from production code a small team can drop into an existing LangChain pipeline on a Tuesday afternoon. UQLM sits firmly in the second category, and that is precisely the gap the authors identify in their survey of prior tools, where methods like LM Polygraph offer real depth but ask more setup effort than many teams can spare.
The transferability of this approach reaches beyond the chatbot use case the paper leans on for its motivating example. Any pipeline where a language model produces a claim that downstream code or a human will act on, a summarization tool, an automated report generator, a code review assistant, faces the same fundamental problem of not knowing when to trust a fluent looking output. A confidence scoring layer that plugs into any LangChain compatible model is portable across all of those situations with essentially no rework.
The honest limitations are still worth carrying forward. This is a young library announced in a short paper, its ensemble tuning depends on labeled data that will often be scarce, and a high confidence score is correlated with correctness rather than a guarantee of it. Teams adopting UQLM should treat it as a triage tool that directs human review toward the riskiest outputs, not as a replacement for that review on decisions where being wrong actually costs something.
Where this goes next probably depends less on new scoring math and more on integration depth, tighter hooks into observability platforms, broader coverage of hosted APIs that still do not expose log probabilities, and real world calibration studies showing how these scores hold up outside the lab. For now, UQLM’s real contribution is making a genuinely useful piece of research infrastructure something a working engineer can install and use before lunch.
Frequently asked questions
What does UQLM actually stand for
UQLM stands for Uncertainty Quantification for Language Models. It is the name of both the research paper and the open source Python package it introduces.
Does UQLM need a ground truth answer to work
No, and that is its main selling point. UQLM estimates a confidence score at generation time using the model’s own outputs and token probabilities, so it does not require a pre written correct answer for the specific question being asked, though tuning the ensemble weights does require a small labeled set.
Which language models work with UQLM
Any chat model wrapped by LangChain’s chat model interface, which covers commercial providers such as OpenAI, Anthropic, Google, Cohere, and AWS Bedrock, plus locally hosted open source models like Llama, Mistral, and Qwen run through tools such as Ollama.
Why do some scoring methods not work with every model
The white box scoring family reads token probabilities directly from the model’s output, and some hosted APIs do not expose those probabilities through their interface. Black box scoring and LLM as a judge scoring do not have this restriction since they only need generated text, not internal probabilities.
Is UQLM free and open source
Yes. The paper is published under a CC BY 4.0 license and the code is publicly available on GitHub under CVS Health’s organization account.
Can UQLM guarantee a response is correct
No. It produces a confidence score that correlates with correctness based on internal consistency and probability signals, which is useful for prioritizing human review, but it is not a factual verification system and can be wrong.
This analysis is based on the published paper and an independent evaluation of its claims.
