Check cited sources
A document assistant answers from retrieved documents and names its sources. But a model can cite a source id that looks right and was never retrieved, or does not exist at all: one kind of hallucination. A cheap first check catches this. Every id the answer cites must be among the ids that were retrieved for this request. Passing the check does not prove the source supports the claim; it only proves the source was retrieved.
Learn it
Words to know
- citation
- A reference from an answer to the source document it is based on, usually by id.
- grounding
- Tying each claim in an answer to a source the model was given, so that the claim can be checked.
- hallucination
- Something a model states with confidence that is not in its sources or is not true, such as an invented source id.
How it works
- Put the retrieved ids in a set, so that looking one up is fast.
- Go through the citations in order.
- A citation that is not in the set is unknown. Report it once, the first time it appears.
Worked example
Input citations = ["a", "x", "x", "y"] retrievedIds = ["a", "b"] Output ["x", "y"]
| Citation | Was it retrieved? | Reported before? | Action |
|---|---|---|---|
| a | yes | does not matter | nothing |
| x | no | no | report x |
| x | no | yes | skip |
| y | no | no | report y |
| citations | a | x | x | y |
|---|
- retrieved
- not retrieved
The answer cites two ids that were never retrieved. x is cited twice and reported once, and the order of first appearance is kept.
Why it matters
The first portfolio project returns source ids with every answer. This check is its first line of defence. Grounding, which is checking that the source really supports the claim, comes after it.
Your turn
Find the citations in an answer that do not point at any retrieved document.
citations- the source ids the answer cites, in order; an id can appear more than once
retrievedIds- the ids of the documents that were retrieved for this request
- Returns
- The cited ids that were not retrieved, each one once, in the order they first appear. If every citation is known, or there are none, an empty list.
What JavaScript gives you
new Set(list)- a collection that holds each item once
set.has(x)- whether x is in the set
set.add(x)- puts x in the set
What Python gives you
set(list)- a collection that holds each item once
x in s- whether x is in the set
s s.add(x)- puts x in the set
s
Example 1
Input citations = ["a", "x", "x", "y"] retrievedIds = ["a", "b"] Output ["x", "y"]
As in the worked example: x and y were never retrieved, and x is listed once.
Example 2
Input citations = ["a"] retrievedIds = ["a"] Output []
The only citation was retrieved, so nothing is reported. An empty list does not prove the answer is right.
Example 3
Input citations = ["b", "a", "b"] retrievedIds = [] Output ["b", "a"]
Nothing was retrieved, so every citation is unknown. b is listed once, and before a, because it appeared first.
The checks also try
- no citations at all
- unknown ids out of alphabetical order
- repeats on both sides
- an id that differs only by a capital letter
- many unknown ids with longer names
- an id that is also a built-in name
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1: Where to start
You need two memories: the ids that were retrieved, and the unknown ids you have already reported.
Hint 2: The key step
Walk the citations in order. An id goes into the result only if it was not retrieved and you have not added it before.
Hint 3: The outline
Make a set
knownfromretrievedIds, an empty setreported, and an empty listunknown. For each citation: if it is not inknownand not inreported, add it tounknownand toreported. Returnunknown.
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
Step by step
- The ids that were retrieved for this request. A set answers whether an id is in it with one fast check, however many documents were retrieved.
- An id that was not retrieved and not reported yet. The second set remembers what is already in the result, so an id cited three times is still reported once.
- Report it, in the order it first appeared. Walking the citations in order and adding at the end keeps first-appearance order, so the output is the same on every run.
The same numbered steps are the comments in the solution.
JavaScript
function solve(citations, retrievedIds) {
// 1. The ids that were retrieved for this request
const known = new Set(retrievedIds);
const reported = new Set();
const unknown = [];
for (const cited of citations) {
// 2. An id that was not retrieved and not reported yet
if (!known.has(cited) && !reported.has(cited)) {
// 3. Report it, in the order it first appeared
unknown.push(cited);
reported.add(cited);
}
}
return unknown;
}Python
def solve(citations, retrievedIds):
# 1. The ids that were retrieved for this request
known = set(retrievedIds)
reported = set()
unknown = []
for cited in citations:
# 2. An id that was not retrieved and not reported yet
if cited not in known and cited not in reported:
# 3. Report it, in the order it first appeared
unknown.append(cited)
reported.add(cited)
return unknown
Watch out
- An empty result is not a pass mark for the answer. It says every cited id was retrieved, not that any cited document supports what the answer claims.
- Keep the order of first appearance. Sorting the result changes that order.
- In JavaScript: Use a
Set, not a plain object:known["constructor"]is found on an empty object. ASetalso keeps the order items were added in, so[...new Set(citations)]drops repeats and keeps first appearance. - In Python: In Python
set(citations) - set(retrievedIds)finds the right ids, but a set has no fixed order. Walk the list instead.
Check yourself
What does this check prove, and what does it not prove?
It proves every cited id was retrieved for this request. It does not prove the cited text supports the claim; that needs a separate check.
Why is each unknown id reported only once?
The result is a list of problems to fix. Repeating an id adds no information.