How to Stop a Model Picking the Wrong Fact From a Long Prompt
> Engineer's view. This is a search ranking bug. Your site search returns the page that matches the query's words best, even when that page is out of date. A model reading a long prompt does the same thing. A passage that echoes your question can beat the true one. Every extra chunk you send adds rivals.
>
> TL;DR. A model reading a long prompt favors the passage that best matches your question, true or not. Long prompts hurt mostly because they carry more look-alikes, so keep those out and ask questions only the right passage answers.
Explain Like I'm 5
You lost a blue umbrella, so you go to the lost-and-found desk. You say, "I lost a blue umbrella."
The clerk looks in the box and hands you the blue umbrella that best fits your words.
If the box holds one blue umbrella, you get yours. If it holds five, you may get someone else's.
A bigger box full of shoes does not confuse the clerk much. More blue umbrellas do.
So you do two things. You tell the clerk what only yours has: "the one with a broken handle." And
you ask them to set aside the umbrellas that clearly belong to other people.
The Problem
You have shipped this bug before, in a system with no model in it.
A customer types "refund window" into your help center. Search returns last year's refund page,
not the current one. Both pages match the words. The old one matches a little better, because it
says "refund window" twice. Nobody notices until a support ticket quotes a 14-day rule you
dropped in March.
A long prompt has the same bug inside it. Your agent's context holds tool results, retrieved
chunks and old turns. The model has to find the one passage that answers the question. It leans
toward the passage that best matches the question's wording, and it can be fooled by a passage
that only looks right.
Chroma tested 18 models this way in its report on what it calls context rot. Accuracy fell as
input grew, even on easy tasks. Adding one near-miss passage hurt, and four hurt more. On a memory
benchmark, every model scored much higher on a 300-token prompt than on the same task in 113,000
tokens.
I tried to break the length part on a current small model. Length alone did nothing to one easy
fact, out to 110,000 tokens. Four look-alikes cost 4 answers in 10, at 4,000 tokens and at 55,000.
The fix: keep look-alikes out of the prompt, ask questions that name what only the true passage
has, and test your own task with a length and look-alike sweep.
The Fix: Keep Look-Alikes Out and Ask What Only the Answer Has
Isn't the model just running out of room?
No. All of these failures happen far below the context limit, which is the most text a model can
accept. In Chroma's tests, accuracy fell fastest when the answer was worded unlike the question.
A strong match survives a long prompt. A weak match gets lost in it.
My test used gpt-4.1-mini at temperature 0, with this site's old articles as filler. The planted
fact was one sentence: the author's old manager taught them to "write the rollback plan before
writing the migration." With no rivals, it was found in every trial at every length.
Why does a look-alike win?
Then I added four look-alikes, which Chroma calls distractors. Each one echoed the question:
"the most useful thing a former boss taught me was to write the tests first." The question asked
for the most useful thing a former boss taught the author.
!The model answers with the passage that best matches the question
The model returned a look-alike in 4 of 10 trials, at both lengths. Length did not make it worse.
More text matters because more text brings in more look-alikes.
What actually fixes it?
!Found the true fact, 10 trials each, gpt-4.1-mini
Asking about what only the true passage had fixed it: 10 of 10, with the look-alikes still there.
Tagging each passage with its source helped only a little. Removing the look-alikes fixes it by
definition. Ten trials is a small sample, so treat these as directions, not rates.
What This Means for You
When this matters. You put more than one candidate answer into a prompt. That covers a RAG
pipeline that returns ten chunks, an agent that keeps every tool result, and a memory store that
holds old and new versions of a fact. It matters most when documents get revised, because an old
version is the perfect look-alike. It matters little for a single short document.
How it affects you. Your evals probably test the clean case. A test with one right document in
the prompt passes. In production, the prompt also holds the superseded page and a forwarded email
that disagrees. The model then quotes the wrong one some of the time, with full confidence. Adding
more context to "help" makes this worse, because it adds rivals.
What to do about it. First, with no setup: take one real prompt your app sent today and count
the passages that could answer its question. If more than one could, you have the bug.
Next, change how you assemble context. Keep only the current version of each document. Drop
sources outside the question's scope. Drop chunks that repeat each other. Then make the question
name what only the right passage has, such as a date, an ID or an author.
Then add the sweep from ## Implementing It to your eval suite. It tells you how many answers
look-alikes cost on your own task. Passing a clean test only shows the model can read.
If you need a refresher on why a model rereads all of this every turn, see
How Agent Context Gets Re-Read Every Turn.
Implementing It
The change.
Three parts change: the code that assembles context, the code that writes the question, and your
eval suite.
Part 1, the context assembler. This runs after retrieval and before the prompt is built. Filter
on metadata first, because it is exact and cheap. Then filter on similarity, which is fuzzy:
def assemble_context(chunks, scope, budget_tokens):
newest = {}
for c in chunks:
if c["source"] not in scope: # a forwarded email is not the policy
continue
if c["doc_id"] not in newest or c["version"] > newest[c["doc_id"]]["version"]:
newest[c["doc_id"]] = c # only the current version survives
kept, used = [], 0
for c in newest.values():
if any(overlap(c["text"], k) > NEAR_DUP for k in kept):
continue # a second copy adds a rival, not information
cost = len(c["text"]) // 4
if used + cost > budget_tokens:
break
kept.append(c["text"])
used += cost
return kept
This needs doc_id, version and source on every chunk in your index. If your index lacks them,
add them at ingest time. That is the real change, and the code above is the easy part. In
code_example.py, six retrieved chunks hold five refund rules. Only the current one survives.
Part 2, the question. Rewrite the user's question before it goes into the prompt. Add the
attribute that only the right passage carries:
# before
q = "What is the refund window?"
# after: name the scope the true passage has, and the look-alikes do not
q = f"What is the refund window in the {policy.name} policy, version {policy.version}?"
If your code cannot tell which policy the user means, ask the user. Do not let the model guess.
Part 3, the eval. Add one sweep per task. It plants the true passage in filler at several
lengths, with and without look-alikes, and calls your real model:
LENGTHS = [500, 4000, 16000, 64000]
for with_la in (False, True):
for tokens in LENGTHS:
prompt = build_prompt(needle, lookalikes if with_la else [], tokens, depth, seed=i)
hits += answer_word in ask(prompt, question).lower()
Write the look-alikes by hand from real near-misses: the old version, a similar product, another
person's note. Random filler does not test this. Chroma's harness is MIT-licensed in
chroma-core/context-rot if you want its needle and distractor setup.
How you know it worked.
Read the sweep as two rows. The clean row should stay flat across lengths. If it falls, your
answers are worded too unlike your questions, so fix retrieval first. The look-alike row should
come close to the clean row once the assembler and the specific question are in place. Log one
production number too: chunks dropped per request by the assembler. If it is always zero, the
metadata filter is not running.
Run at least 20 trials per cell. With 10, a one-answer swing looks like a finding. The reason is
in How to Tell a Real Prompt Improvement From a Lucky Eval Run.
When Filtering Context Is the Wrong Tool
When the look-alikes are the point. A question like "how did the refund window change?" needs the
old version and the new one. Filtering out the old version makes the answer impossible. Label each
version with its date instead, and ask for a comparison.
When the right passage is not known ahead of time. Metadata filters need a scope. An open question
over a pile of email has none, so the filter can only drop exact copies.
When the near-duplicate threshold is too aggressive. Two chunks can share most words and differ in
the one number that matters, such as 14 days versus 30. A high threshold keeps both. A low one can
drop the true one. Check what the filter removed on a sample before you trust it.
My test used one small model, one fact and ten trials per cell. Larger models may resist
look-alikes better, and harder questions may show the length effect Chroma measured.
Three questions before you change your pipeline:
- How many passages in today's prompts could answer the same question?
- Does every chunk in your index carry a document ID, a version and a source?
- Does your eval include the old version of the page, or only the right one?
Glossary
- context rot — Chroma's name for model accuracy falling as more text is added to the prompt.
- look-alikes — passages that resemble the answer or echo the question but are wrong.
- context limit — the most text, in tokens, that a model can accept in one request.
- distractors — the research term for look-alikes planted in a test prompt.
- context assembler — the code that chooses which retrieved chunks go into the prompt.
- sweep — one test run across several prompt lengths, with and without look-alikes.
Further reading
- Context Rot: How Increasing Input Tokens Impacts LLM Performance — Chroma technical report (Kelly Hong, Anton Troynikov, Jeff Huber)
- chroma-core/context-rot — Chroma, MIT license
- Effective context engineering for AI agents — Anthropic Engineering
---
Originally published at theaicommit.com, a daily AI engineering lab for software engineers. The original has an interactive version you can play with, and the full runnable code is on GitHub.
Chroma discovery should start with collection data, not a search-engine count
Chroma discovery should start with collection data, not a search-engine count
Chroma is a vector database used for retrieval over embeddings, documents, and metadata. A self-hosted Chroma server can contain internal documents, customer records, model outputs, and security-sensitive context. Exposing the server or its API is therefore a data-boundary issue, even when no vulnerability is known.
A ZoomEye count-only measurement on October 31, 2026 at 06:44 Beijing time used page 1, page size 1, and sub_type=all. app="Chroma" returned 0 matches and title="Chroma" returned 1,215. The title count can include documentation and unrelated web pages. It does not show that 1,215 vector databases are reachable or that their collections are readable.
Use the counts with the right question
The app query is a narrow product-fingerprint question. A zero result means that the exact signature did not appear in the selected scope, not that no Chroma deployment exists. A reverse proxy, custom health response, or nonstandard deployment can change discovery evidence.
The title query is a broader discovery signal. A site may present a customized interface while the title still names the product. Alternatively, the title may belong to a project page, a tutorial, or an unrelated product name. Resolve the candidate through an owned asset record and a safe public response check. Do not query an unowned collection or attempt to enumerate embeddings.
Review server authentication
Chroma documentation describes authentication for server and client mode. The supported methods include basic authentication and a static API token, with the documentation marking these mechanisms as alpha. The exact server version and deployment configuration determine which method is available and how it is enabled.
For an owned deployment, record whether authentication is enabled, where the credential is stored, how it is rotated, and which clients use it. Put the server behind TLS and a network policy that limits access to the applications that need it. Do not expose the server directly to the internet when a controlled gateway can provide authentication, logging, and source restrictions.
A static token can become a shared master credential. Separate development and production tokens where the deployment permits it, and remove tokens for retired jobs. Test a request without credentials, a request with a revoked token, and a request from an unapproved source. Confirm that the gateway and the Chroma server agree on the expected authentication path.
Protect collection data and metadata
A collection can reveal more than the embeddings. Documents, metadata filters, collection names, and query results may expose business information or personal data. Review who can list databases and collections, add or delete records, and query across them. If the application supplies tenant identifiers, verify that every query is scoped server-side and cannot be changed by a client parameter.
Back up the Chroma data and the configuration that identifies its storage. Protect backups as sensitive data, not as opaque model artifacts. Test a restore with an isolated client and verify collection counts, query behavior, and authentication. Rotate credentials after a restore from an untrusted copy.
Check the embedding source. A document may contain secrets or personal data that the original application would not expose through its normal interface. Data retention and deletion policies should cover the vector store as well as the source content.
Turn discovery into evidence
A practical inventory records the hostname, owner, environment, Chroma version, authentication method, network path, exposed collections, client applications, backup owner, and last restore test. Add the exact ZoomEye query, count, time, and scope that led to the review.
Use the count to find candidates. Use the owned configuration and a controlled query to determine what data is reachable. A public page that mentions Chroma is not enough to claim that an API or collection is exposed.
Conclusion
Chroma's 0 app and 1,215 title matches describe discovery evidence, not confirmed data exposure. The useful review connects server authentication, network restrictions, collection authorization, backups, deletion, and query logging. Those controls determine whether a vector database is a controlled internal service or an unowned copy of sensitive business data.
References
- Chroma documentation
- Chroma architecture overview
- Chroma authentication usage guide
- ZoomEye: app="Chroma"
- ZoomEye: title="Chroma"
Mentions
How to Stop a Model Picking the Wrong Fact From a Long Prompt > Engineer's view. This is a search ranking bug. Your site search returns the page that matches the query's words best, even when that page is out of date. A model reading a long prompt does the same thing. A passage that echoes your question can beat the true one. Every extra chunk you send adds rivals. > > TL;DR. A model reading a long prompt favors the passage that best matches your question, true or not. Long prompts hurt mostly because they carry more look-alikes, so keep those out and ask questions only the right passage answers. Explain Like I'm 5 You lost a blue umbrella, so you go to the lost-and-found desk. You say, "I lost a blue umbrella." The clerk looks in the box and hands you the blue umbrella that best fits your words. If the box holds one blue umbrella, you get yours. If it holds five, you may get someone else's. A bigger box full of shoes does not confuse the clerk much. More blue umbrellas do. So you do two things. You tell the clerk what only yours has: "the one with a broken handle." And you ask them to set aside the umbrellas that clearly belong to other people. The Problem You have shipped this bug before, in a system with no model in it. A customer types "refund window" into your help center. Search returns last year's refund page, not the current one. Both pages match the words. The old one matches a little better, because it says "refund window" twice. Nobody notices until a support ticket quotes a 14-day rule you dropped in March. A long prompt has the same bug inside it. Your agent's context holds tool results, retrieved chunks and old turns. The model has to find the one passage that answers the question. It leans toward the passage that best matches the question's wording, and it can be fooled by a passage that only looks right. Chroma tested 18 models this way in its report on what it calls context rot. Accuracy fell as input grew, even on easy tasks. Adding one near-miss passage hurt, and four hurt more. On a memory benchmark, every model scored much higher on a 300-token prompt than on the same task in 113,000 tokens. I tried to break the length part on a current small model. Length alone did nothing to one easy fact, out to 110,000 tokens. Four look-alikes cost 4 answers in 10, at 4,000 tokens and at 55,000. The fix: keep look-alikes out of the prompt, ask questions that name what only the true passage has, and test your own task with a length and look-alike sweep. The Fix: Keep Look-Alikes Out and Ask What Only the Answer Has Isn't the model just running out of room? No. All of these failures happen far below the context limit, which is the most text a model can accept. In Chroma's tests, accuracy fell fastest when the answer was worded unlike the question. A strong match survives a long prompt. A weak match gets lost in it. My test used gpt-4.1-mini at temperature 0, with this site's old articles as filler. The planted fact was one sentence: the author's old manager taught them to "write the rollback plan before writing the migration." With no rivals, it was found in every trial at every length. Why does a look-alike win? Then I added four look-alikes, which Chroma calls distractors. Each one echoed the question: "the most useful thing a former boss taught me was to write the tests first." The question asked for the most useful thing a former boss taught the author. !The model answers with the passage that best matches the question The model returned a look-alike in 4 of 10 trials, at both lengths. Length did not make it worse. More text matters because more text brings in more look-alikes. What actually fixes it? !Found the true fact, 10 trials each, gpt-4.1-mini Asking about what only the true passage had fixed it: 10 of 10, with the look-alikes still there. Tagging each passage with its source helped only a little. Removing the look-alikes fixes it by definition. Ten trials is a small sample, so treat these as directions, not rates. What This Means for You When this matters. You put more than one candidate answer into a prompt. That covers a RAG pipeline that returns ten chunks, an agent that keeps every tool result, and a memory store that holds old and new versions of a fact. It matters most when documents get revised, because an old version is the perfect look-alike. It matters little for a single short document. How it affects you. Your evals probably test the clean case. A test with one right document in the prompt passes. In production, the prompt also holds the superseded page and a forwarded email that disagrees. The model then quotes the wrong one some of the time, with full confidence. Adding more context to "help" makes this worse, because it adds rivals. What to do about it. First, with no setup: take one real prompt your app sent today and count the passages that could answer its question. If more than one could, you have the bug. Next, change how you assemble context. Keep only the current version of each document. Drop sources outside the question's scope. Drop chunks that repeat each other. Then make the question name what only the right passage has, such as a date, an ID or an author. Then add the sweep from ## Implementing It to your eval suite. It tells you how many answers look-alikes cost on your own task. Passing a clean test only shows the model can read. If you need a refresher on why a model rereads all of this every turn, see How Agent Context Gets Re-Read Every Turn. Implementing It The change. Three parts change: the code that assembles context, the code that writes the question, and your eval suite. Part 1, the context assembler. This runs after retrieval and before the prompt is built. Filter on metadata first, because it is exact and cheap. Then filter on similarity, which is fuzzy: def assemble_context(chunks, scope, budget_tokens): newest = {} for c in chunks: if c["source"] not in scope: # a forwarded email is not the policy continue if c["doc_id"] not in newest or c["version"] > newest[c["doc_id"]]["version"]: newest[c["doc_id"]] = c # only the current version survives kept, used = [], 0 for c in newest.values(): if any(overlap(c["text"], k) > NEAR_DUP for k in kept): continue # a second copy adds a rival, not information cost = len(c["text"]) // 4 if used + cost > budget_tokens: break kept.append(c["text"]) used += cost return kept This needs doc_id, version and source on every chunk in your index. If your index lacks them, add them at ingest time. That is the real change, and the code above is the easy part. In code_example.py, six retrieved chunks hold five refund rules. Only the current one survives. Part 2, the question. Rewrite the user's question before it goes into the prompt. Add the attribute that only the right passage carries: # before q = "What is the refund window?" # after: name the scope the true passage has, and the look-alikes do not q = f"What is the refund window in the {policy.name} policy, version {policy.version}?" If your code cannot tell which policy the user means, ask the user. Do not let the model guess. Part 3, the eval. Add one sweep per task. It plants the true passage in filler at several lengths, with and without look-alikes, and calls your real model: LENGTHS = [500, 4000, 16000, 64000] for with_la in (False, True): for tokens in LENGTHS: prompt = build_prompt(needle, lookalikes if with_la else [], tokens, depth, seed=i) hits += answer_word in ask(prompt, question).lower() Write the look-alikes by hand from real near-misses: the old version, a similar product, another person's note. Random filler does not test this. Chroma's harness is MIT-licensed in chroma-core/context-rot if you want its needle and distractor setup. How you know it worked. Read the sweep as two rows. The clean row should stay flat across lengths. If it falls, your answers are worded too unlike your questions, so fix retrieval first. The look-alike row should come close to the clean row once the assembler and the specific question are in place. Log one production number too: chunks dropped per request by the assembler. If it is always zero, the metadata filter is not running. Run at least 20 trials per cell. With 10, a one-answer swing looks like a finding. The reason is in How to Tell a Real Prompt Improvement From a Lucky Eval Run. When Filtering Context Is the Wrong Tool When the look-alikes are the point. A question like "how did the refund window change?" needs the old version and the new one. Filtering out the old version makes the answer impossible. Label each version with its date instead, and ask for a comparison. When the right passage is not known ahead of time. Metadata filters need a scope. An open question over a pile of email has none, so the filter can only drop exact copies. When the near-duplicate threshold is too aggressive. Two chunks can share most words and differ in the one number that matters, such as 14 days versus 30. A high threshold keeps both. A low one can drop the true one. Check what the filter removed on a sample before you trust it. My test used one small model, one fact and ten trials per cell. Larger models may resist look-alikes better, and harder questions may show the length effect Chroma measured. Three questions before you change your pipeline: - How many passages in today's prompts could answer the same question? - Does every chunk in your index carry a document ID, a version and a source? - Does your eval include the old version of the page, or only the right one? Glossary - context rot — Chroma's name for model accuracy falling as more text is added to the prompt. - look-alikes — passages that resemble the answer or echo the question but are wrong. - context limit — the most text, in tokens, that a model can accept in one request. - distractors — the research term for look-alikes planted in a test prompt. - context assembler — the code that chooses which retrieved chunks go into the prompt. - sweep — one test run across several prompt lengths, with and without look-alikes. Further reading - Context Rot: How Increasing Input Tokens Impacts LLM Performance — Chroma technical report (Kelly Hong, Anton Troynikov, Jeff Huber) - chroma-core/context-rot — Chroma, MIT license - Effective context engineering for AI agents — Anthropic Engineering --- Originally published at theaicommit.com, a daily AI engineering lab for software engineers. The original has an interactive version you can play with, and the full runnable code is on GitHub.
Chroma discovery should start with collection data, not a search-engine count Chroma discovery should start with collection data, not a search-engine count Chroma is a vector database used for retrieval over embeddings, documents, and metadata. A self-hosted Chroma server can contain internal documents, customer records, model outputs, and security-sensitive context. Exposing the server or its API is therefore a data-boundary issue, even when no vulnerability is known. A ZoomEye count-only measurement on October 31, 2026 at 06:44 Beijing time used page 1, page size 1, and sub_type=all. app="Chroma" returned 0 matches and title="Chroma" returned 1,215. The title count can include documentation and unrelated web pages. It does not show that 1,215 vector databases are reachable or that their collections are readable. Use the counts with the right question The app query is a narrow product-fingerprint question. A zero result means that the exact signature did not appear in the selected scope, not that no Chroma deployment exists. A reverse proxy, custom health response, or nonstandard deployment can change discovery evidence. The title query is a broader discovery signal. A site may present a customized interface while the title still names the product. Alternatively, the title may belong to a project page, a tutorial, or an unrelated product name. Resolve the candidate through an owned asset record and a safe public response check. Do not query an unowned collection or attempt to enumerate embeddings. Review server authentication Chroma documentation describes authentication for server and client mode. The supported methods include basic authentication and a static API token, with the documentation marking these mechanisms as alpha. The exact server version and deployment configuration determine which method is available and how it is enabled. For an owned deployment, record whether authentication is enabled, where the credential is stored, how it is rotated, and which clients use it. Put the server behind TLS and a network policy that limits access to the applications that need it. Do not expose the server directly to the internet when a controlled gateway can provide authentication, logging, and source restrictions. A static token can become a shared master credential. Separate development and production tokens where the deployment permits it, and remove tokens for retired jobs. Test a request without credentials, a request with a revoked token, and a request from an unapproved source. Confirm that the gateway and the Chroma server agree on the expected authentication path. Protect collection data and metadata A collection can reveal more than the embeddings. Documents, metadata filters, collection names, and query results may expose business information or personal data. Review who can list databases and collections, add or delete records, and query across them. If the application supplies tenant identifiers, verify that every query is scoped server-side and cannot be changed by a client parameter. Back up the Chroma data and the configuration that identifies its storage. Protect backups as sensitive data, not as opaque model artifacts. Test a restore with an isolated client and verify collection counts, query behavior, and authentication. Rotate credentials after a restore from an untrusted copy. Check the embedding source. A document may contain secrets or personal data that the original application would not expose through its normal interface. Data retention and deletion policies should cover the vector store as well as the source content. Turn discovery into evidence A practical inventory records the hostname, owner, environment, Chroma version, authentication method, network path, exposed collections, client applications, backup owner, and last restore test. Add the exact ZoomEye query, count, time, and scope that led to the review. Use the count to find candidates. Use the owned configuration and a controlled query to determine what data is reachable. A public page that mentions Chroma is not enough to claim that an API or collection is exposed. Conclusion Chroma's 0 app and 1,215 title matches describe discovery evidence, not confirmed data exposure. The useful review connects server authentication, network restrictions, collection authorization, backups, deletion, and query logging. Those controls determine whether a vector database is a controlled internal service or an unowned copy of sensitive business data. References - Chroma documentation - Chroma architecture overview - Chroma authentication usage guide - ZoomEye: app="Chroma" - ZoomEye: title="Chroma"
Chroma 已有29607颗星,Apache-2.0 开源。给 AI 做资料检索,可以存文档和来源标签,再按相似度找内容、筛来源。 官方示例默认建的是内存数据库。想把资料留到下次启动,还得另配持久化,别把演示代码当长期知识库。 chroma-core/chroma
Chroma MCP 能让 AI 存资料、按语义查回来,适合给项目接一个可搜索的笔记库。Apache-2.0 开源。 官方默认用内存模式,重启不会保留资料。想下次还能查,得选持久化模式、指定存盘目录,别只把服务启动了就当记住了。 chroma-core/chroma-mcp
3. Chroma Chroma is a vector database designed for AI applications. It helps you store and search information called “embeddings”. This becomes useful when you want an AI application to find relevant information from your own documents or data. chroma-core/chroma
context-1-data-gen ⭐ 441 A pipeline for generating multi-hop search tasks, for developers building search systems. 🔗 chroma-core/context-1-data-gen #opensource t.co
Well, I got there too late. I just found a correctness bug in Chroma's wal3 this morning, and was working on an integration test to prove what my TLA+ spec found, and on reading the fork, I see that just 2 days ago, the bug got fixed! github.com/chroma-core/chroma/commit/f36d9bba588e…