Why AI Agents Change Their Answers
Listen to the summary
Uses a voice available on your device
Audio options
On this page 5 sections
Related concepts 2 concepts
Key Takeaways
- Expanding the retrieval corpus introduces answer churn, where agents change their output despite identical core configurations.
- Natural Questions testing showed a 6.44 percentage point increase in normalized-exact excess churn and 10.25 percentage points for semantic excess churn.
- TriviaQA results demonstrated that churn occurs even when exact-match accuracy moves in the opposite direction.
- Answer changes are not inherently negative or indicative of regressions, as higher accuracy sometimes accompanies higher churn.
Summary & Methodology Analysis
The researchers employed a Snapshot Compatibility Audit to evaluate behavioral stability when systems access larger data volumes. By locking the prompt, model identifier, retrieval policy, evidence depth, and generation controls, they isolated the effects of corpus expansion from other variables. The study utilized deepseek-v4-flash with tools disabled in independent singleton sessions, establishing a noise floor by drawing two independent generator responses per question. This allows for measuring excess churn by subtracting internal generation stochasticity from the disagreement observed between different corpus states, specifically analyzing changes across a FineWeb prefix expanded from one to seven shards.
Interactive System Flowchart
Illustrative Implementation
A short sketch of the paper's core idea, not the authors' own code.
# Illustrative sketch (not from the paper)
import random, itertools, math
import torch
# 1. Fixed snapshot configuration (prompt, model id, retrieval policy, etc.)
PROMPT = "Answer the question using retrieved evidence."
MODEL_ID = "deepseek-v4-flash"
RETRIEVAL_POLICY = "top_k=5"
EVIDENCE_DEPTH = 2
# 2. Simulated function to get two independent generator responses per question
def generate_responses(question, corpus_version):
# In practice call the LLM with fixed seed per call to get stochasticity
return [f"resp_{question}_{corpus_version}_{i}" for i in (0, 1)]
# 3. Compute normalized‑exact agreement (binary match) and blinded semantic agreement (placeholder)
def exact_agreement(a, b):
return int(a == b)
def semantic_agreement(a, b):
# Placeholder for blinded human/ML judgment; returns 0/1
return random.choice([0, 1])
# 4. Snapshot Compatibility Audit
def audit_snapshot(questions, corpus_versions):
results = []
for q in questions:
# draw responses for each corpus version
resp = {v: generate_responses(q, v) for v in corpus_versions}
# within‑snapshot disagreement (noise floor)
within = [1 - exact_agreement(r1, r2) for r1, r2 in resp[corpus_versions[0]]]
# cross‑snapshot disagreement
cross = []
for v1, v2 in itertools.combinations(corpus_versions, 2):
for r1 in resp[v1]:
for r2 in resp[v2]:
cross.append(1 - exact_agreement(r1, r2))
# excess churn = cross - mean(within)
excess = sum(cross) / len(cross) - sum(within) / len(within)
results.append(excess)
return results
# 5. Bootstrap confidence intervals using PyTorch
def bootstrap_ci(data, n_boot=1000, alpha=0.05):
tensor = torch.tensor(data, dtype=torch.float32)
boots = []
for _ in range(n_boot):
idx = torch.randint(0, len(tensor), (len(tensor),))
boots.append(tensor[idx].mean().item())
lower = torch.quantile(torch.tensor(boots), alpha/2).item()
upper = torch.quantile(torch.tensor(boots), 1-alpha/2).item()
return lower, upper
# Example usage (illustrative)
questions = ["Q1", "Q2", "Q3"]
corpus_versions = ["v1", "v2"]
excess_churn = audit_snapshot(questions, corpus_versions)
ci = bootstrap_ci(excess_churn)
print("Excess churn:", excess_churn)
print("95% CI:", ci)// Illustrative sketch (not from the paper)
const crypto = require('crypto');
// 1. Fixed snapshot configuration
const PROMPT = "Answer the question using retrieved evidence.";
const MODEL_ID = "deepseek-v4-flash";
const RETRIEVAL_POLICY = "top_k=5";
const EVIDENCE_DEPTH = 2;
// 2. Simulated generator returning two independent responses per call
function generateResponses(question, corpusVersion) {
// In practice invoke the LLM with deterministic seed per call
return [
`resp_${question}_${corpusVersion}_0`,
`resp_${question}_${corpusVersion}_1`
];
}
// 3. Agreement metrics (exact and blinded semantic placeholder)
function exactAgreement(a, b) { return a === b ? 1 : 0; }
function semanticAgreement(a, b) { // random placeholder for blinded judgment
return Math.random() < 0.5 ? 1 : 0;
}
// 4. Snapshot Compatibility Audit
function auditSnapshot(questions, corpusVersions) {
const results = [];
for (const q of questions) {
const resp = {};
for (const v of corpusVersions) {
resp[v] = generateResponses(q, v);
}
// within‑snapshot disagreement (noise floor)
const within = resp[corpusVersions[0]].map((r, i) => 1 - exactAgreement(r, resp[corpusVersions[0]][i]));
// cross‑snapshot disagreement
const cross = [];
for (let i = 0; i < corpusVersions.length; i++) {
for (let j = i + 1; j < corpusVersions.length; j++) {
for (const r1 of resp[corpusVersions[i]]) {
for (const r2 of resp[corpusVersions[j]]) {
cross.push(1 - exactAgreement(r1, r2));
}
}
}
}
const mean = arr => arr.reduce((a, b) => a + b, 0) / arr.length;
const excess = mean(cross) - mean(within);
results.push(excess);
}
return results;
}
// 5. Bootstrap confidence interval (simple implementation)
function bootstrapCI(data, nBoot = 1000, alpha = 0.05) {
const boots = [];
for (let b = 0; b < nBoot; b++) {
const sample = [];
for (let i = 0; i < data.length; i++) {
const idx = Math.floor(Math.random() * data.length);
sample.push(data[idx]);
}
const mean = sample.reduce((a, c) => a + c, 0) / sample.length;
boots.push(mean);
}
boots.sort((a, b) => a - b);
const lower = boots[Math.floor((alpha / 2) * nBoot)];
const upper = boots[Math.floor((1 - alpha / 2) * nBoot)];
return [lower, upper];
}
// Example usage (illustrative)
const questions = ['Q1', 'Q2', 'Q3'];
const corpusVersions = ['v1', 'v2'];
const excessChurn = auditSnapshot(questions, corpusVersions);
const ci = bootstrapCI(excessChurn);
console.log('Excess churn:', excessChurn);
console.log('95% CI:', ci);
Cross-Examination & FAQs
A deeper dive clarifying mechanics, constraints, and baseline evaluations.
Q1. What is answer churn in AI agents?
Answer churn refers to a phenomenon where an agent provides different answers to the same question after a change in its retrieval corpus, despite using the same model and prompt settings.
Q2. Does more data always improve accuracy?
No. The study shows that answer changes do not automatically equate to regressions or improvements, and changes in accuracy metrics like exact-match scores do not follow a uniform pattern.
Q3. Is this a universal law for all AI systems?
The paper does not establish a universal scaling law or a general prevalence of churn, noting that further research across different retrievers and time-based index refreshes is required.
Q4. Which datasets were used in the audit?
The study utilized Natural Questions and TriviaQA datasets.
Q5. What specific model configuration was used?
The authors used deepseek-v4-flash with tools turned off in independent singleton sessions.
Q6. How was churn measured quantitatively?
The study measured normalized-exact excess churn and blinded-semantic excess churn by comparing pairs of responses across different corpus scales.
Q7. What were the results for Natural Questions?
The Natural Questions study observed 6.44 percentage points of normalized-exact excess churn and 10.25 percentage points of semantic excess churn, with an exact-match accuracy change of -1.50 points.
Q8. Did the study find any case where accuracy increased with churn?
Yes. A post-hoc replication using a second generator configuration found 8.75 percentage points of semantic excess churn while exact-match accuracy rose by 3.00 percentage points.
Q9. Does the paper define a fixed scaling curve for corpus expansion?
No. The authors explicitly state they did not establish a monotone or universal law assigning a specific churn rate to larger corpora.