Why can the same prompt return different answers, and how should the UI handle that?
Advertisement
🧩 Scenario
Concept Walkthrough
Where the variation comes from
- Sampling. The model picks each next token by sampling from a probability distribution. One different early token sends the rest of the answer down a different path, like a branch in a recursive function.
- Temperature is not a lock. Lower temperature reduces variation, but Anthropic's API reference states that even temperature 0.0 is not fully deterministic. Some newer models also only accept the default temperature.
- Hidden context changes. The model sees more than the user's text: system prompt, chat history, retrieved documents, tool results, today's date. If any of those differ, the answer can differ.
- Model changes. Providers release new versions. If you call a moving alias, the model behind it can change. Pin the exact model ID where your provider offers one, and record it.
So for a UI engineer, the right mental model is: an LLM call is not a pure function. Same input does not guarantee same output.
What the UI should do
Treat generated text like user-generated content: once created, it is a record.
- Generate once, store, then render the stored copy. For a job post summary, generate it on publish or on first request, save it, and serve it from the database. Refreshing should never trigger a new generation.
- Store what the user saw. Chat messages, shared links and exports must point to stored output, not re-run prompts.
- Make variation a feature where it helps. In chat, offer "Regenerate" and keep earlier versions so users can switch back.
- Validate what the app depends on. If the UI needs a category, a score or JSON, validate it in code; never depend on exact wording.
- Record metadata. Model ID, prompt version and timestamp make it possible to explain later why an answer looks the way it does.
Key Code Explained
import { useState } from 'react';
type Version = {
id: string;
text: string;
model: string; // exact model ID used
promptVersion: string; // e.g. 'summary-v3'
createdAt: string;
};
type AssistantMessage = { id: string; versions: Version[]; activeIndex: number };
export function AssistantBubble({
message,
onRegenerate,
}: {
message: AssistantMessage;
onRegenerate: (messageId: string) => Promise<void>; // server appends a new Version
}) {
const [index, setIndex] = useState(message.activeIndex);
const [busy, setBusy] = useState(false);
const total = message.versions.length;
const current = message.versions[index];
async function regenerate() {
setBusy(true);
try {
await onRegenerate(message.id);
setIndex(total); // jump to the new version once it is added
} finally {
setBusy(false);
}
}
return (
<div>
<p>{current.text}</p>
<div aria-label='Response versions'>
<button disabled={index === 0} onClick={() => setIndex(index - 1)}>Previous</button>
<span>Response {index + 1} of {total}</span>
<button disabled={index === total - 1} onClick={() => setIndex(index + 1)}>Next</button>
<button disabled={busy} onClick={regenerate}>Regenerate</button>
</div>
</div>
);
}
Each assistant message holds an array of versions instead of a single string. Regenerate appends rather than overwrites, so nothing the user saw disappears. The model and promptVersion fields are what you need when someone asks "why did the bot say this last week?". In a real app you would also persist activeIndex so shared links show the version that was selected.
Tradeoffs
| Approach | Consistency | Cost | Best for |
|---|---|---|---|
| Call the model on every view | None | Highest | Nothing user-facing |
| Generate once and store | Full, per record | Lowest | Summaries, descriptions, emails |
| Store versions plus regenerate | User controlled | Grows with regenerations | Chat, writing tools |
| Lower temperature only | Partial | No change | Small improvement, not a guarantee |
| Validate structured output | Contract enforced | Small extra logic | Anything the UI branches on |
What Interviewers Actually Check
- Whether you can name causes beyond "randomness" (context, model versions)
- Whether you know temperature 0 is not deterministic
- Whether you persist output instead of regenerating on every view
- Whether regenerate keeps history instead of overwriting
- Whether you separate UI tests (mocked model) from quality checks (evals)
Follow-Up Questions
- When would you invalidate a stored summary and generate a new one?
- How would you show users that a response came from an older model or prompt version?
- How would you let users give feedback on one specific version?
- How do shared chat links work if the conversation continues after sharing?
- Which parts of an AI feature should never depend on model output at all?
Common Candidate Mistakes
- Blaming only temperature and ignoring prompt, context and model changes
- Generating in a
useEffecton page load, so every refresh costs money and changes content - Replacing the old answer on regenerate
- Parsing free text with string matching and breaking when wording shifts
- Writing exact-match tests against live model output
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you name four sources of variation in LLM output?
- Can you explain why an LLM call is not a pure function?
- Can you design a message shape with versions and metadata?
- Can you explain when to cache versus when to regenerate?
- Can you describe how to test UI code that calls a model?
Summary
The same prompt can return different answers because models sample tokens, temperature 0 is still not fully deterministic, hidden context like history or retrieved data changes, and models get updated. Treat an LLM call as a non-pure function and treat its output as a record. Generate once and store for content that should stay stable, store exactly what users saw for chats and shares, offer regenerate with saved versions where variety helps, and validate any output the app logic depends on. Record the model ID and prompt version with each output so you can explain results later.
If I set temperature to 0, will answers stop changing?
They will vary less, but not stop. Anthropic's API reference says results are not fully deterministic even at temperature 0.0, and several newer models do not let you change temperature at all.
Should I cache LLM responses to make them consistent?
For features where the same input should show the same result (for example an AI summary on a job post), yes: generate once, store it, and serve the stored copy. For a chat, store each message as it was shown so history and shares never change after the fact.
How do I write tests if output is not repeatable?
Test your UI and parsing with a mocked model that returns fixed responses, and test the model's quality separately with evals that check properties (valid JSON, required fields, tone) instead of exact strings. Both topics come later in this series.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement