Why I don't ship AI without citations
A client once asked me, watching a demo of AI generation: "how do I know this is true?" Good question, and the only one that matters when you put an LLM in the hands of a salesperson about to send it to a prospect. Fluent, wrong text, signed with the client's company name, isn't a feature. It's a liability.
The problem demos don't show
Generative AI demos are impressive because they show the best case: coherent, well-turned text that sounds informed. What they don't show is the case where the model claims a company "just raised funding" when nothing of the sort exists in the retrieved data. In B2B, that kind of error goes out under the sender's name and spends their credibility.
The problem isn't that LLMs get things wrong. It's that they get things wrong with exactly the same assurance as when they're right. Nothing in the output distinguishes a verified claim from a plausible hallucination.
And this isn't a teething problem the next generation of models will fix. In Why Language Models Hallucinate, Adam Kalai and his co-authors make an uncomfortable argument: hallucination is the rational product of how we train and evaluate models. Most benchmarks score an answer right or wrong and never award credit for "I don't know." Faced with an uncertain question, a model that takes a shot scores points on average; a model that abstains scores none. Like a student sitting a multiple-choice exam with no penalty for wrong answers, it is statistically correct to guess. So we spent years optimizing systems that guess rather than systems that hesitate. The confidence isn't a surface bug, it's what we rewarded.
The practical consequence is simple. If the model's confidence isn't a reliability signal, you have to manufacture that signal somewhere else, outside the model.
What I built in Project A
Project A generates outreach sequences: emails and messages aimed at decision-makers, built from real signals (a LinkedIn post, a company news item, a role change). The architecture I put in place treats generation not as a black box but as a three-stage pipeline.
First, generation itself: the model drafts the message using the sources retrieved for that prospect. Then an attribution pass: every sentence in the generated text is checked against the available sources to see whether it can be tied back to one. Finally, display: sourced sentences carry a clickable footnote pointing to the real source, unsourced sentences are visually flagged, and a counter shows something like "1/2 sentences sourced" so the user sees the message's reliability at a glance.
Picking the sentence as the unit of attribution isn't arbitrary. It's the same granularity Anthropic's Citations API settles on, chunking source documents into sentences before passing them to the model, then returning the exact passages backing each claim. The reason sits between two bounds: below the sentence, you're attributing fragments of syntax that assert nothing verifiable; above it, at the paragraph, a single invented line is enough to contaminate a whole block stamped "sourced." The sentence is the smallest fragment that still carries a complete claim.
Here's a simplified version of the data shape behind this logic:
type SourceRef = {
url: string
label: string
}
type GroundedSentence = {
text: string
source?: SourceRef
}
function verify(sentences: GroundedSentence[]) {
const sourced = sentences.filter((s) => s.source).length
return { sourced, total: sentences.length }
}
When the user clicks "Regenerate (grounded)", it isn't the same generation with a different seed. It's a constrained generation: the prompt is rewritten to only allow claims directly backed by retrieved sources, even if that means a shorter, more cautious message that is fully verifiable.
The trade-offs, without hiding them
This system has a cost. The attribution pass adds latency: you can't just stream the model's text straight to the screen anymore, you have to verify before showing each sentence's status.
More importantly there's a trade-off, and it has a name in the literature. The ALCE benchmark (Gao et al., EMNLP 2023), which evaluates how well LLMs cite their sources, splits it into two distinct metrics: citation recall, the share of produced sentences actually supported by the passages cited, and citation precision, the share of citations that genuinely support the sentence they're attached to. Both are measured with a natural language inference model that tests whether the cited passage truly entails the sentence, rather than settling for lexical overlap.
Where you set the dial between them is a product decision, not an implementation detail. Attribution that's too strict flags correct sentences as unsourced: frustrating. Attribution that's too lenient lets approximations through: dangerous. I chose to lean strict, favouring precision on the "sourced" label at the cost of recall. The reasoning is asymmetric: a correct sentence wrongly flagged costs the user ten seconds of re-reading, while a false sentence stamped "sourced" costs trust in the entire system. Better a message that admits it's incomplete than one that sounds confident and is wrong.
And there's the case where nothing is sourceable at all: no recent signal, no news, no post. In that case the system doesn't force a creative generation to fill the gap. It says so, and falls back to a more generic message, presented as such rather than disguised as personalization.
What it means for a client
A salesperson sending an AI-generated message is putting their reputation and their company's on the line. Being able to see, at a glance, what's proven and what isn't turns AI from an anxiety-inducing text generator into a working tool whose limits are understood.
It's also a matter of legal safety, and that isn't a theoretical worry. In 2024, British Columbia's Civil Resolution Tribunal ordered Air Canada to compensate a passenger whose chatbot had described a bereavement fare policy that didn't exist (Moffatt v. Air Canada, 2024 BCCRT 149). The airline argued the chatbot was a separate legal entity, responsible for its own statements. The tribunal dismissed that flatly: a chatbot may have an interactive component, but it is still part of Air Canada's website, and a company answers for all the information on it, whether it comes from a static page or a conversational agent. The transposition is immediate: a false claim about a prospect, a figure, an event, isn't asserted by the model. It's asserted by whoever sends it.
Traceability isn't a nice-to-have, then, it's a condition for adoption: sales teams only keep using an AI tool long-term if they can trust it without re-reading every line each time.
That's the conviction behind how I integrate AI into the products I build: generation is only useful if it's verifiable. If you have a product where AI needs to produce text that represents your company or your clients, this is exactly the kind of guardrail I build in from the architecture up.