Skip to content

Document retrieval in chat

When a user attaches documents to a conversation, the assistant can answer from their content rather than from the model's own knowledge. Retrieval is not automatic: it runs only when the conversation has documents to search and only when the user's message needs them.

When retrieval runs

Both conditions must hold.

The conversation has at least one usable document. ChatService.list_conversation_attachments selects documents joined to the conversation whose status is READY and that are not deleted. A document still being ingested, one whose ingestion failed, and one the user has deleted are all invisible here, so none of them can trigger retrieval. With no such document the turn skips retrieval without asking anything.

The message needs those documents. RAGSearchClassifier.requires_search puts the message to a model together with each document's section headings, and answers yes or no. "Explain mitochondria" against a document that has such a section is a yes; "make that punchier", a short confirmation, or a question unrelated to the attached material is a no. A declined search is logged with the reason the model gave and the conversation UUID; the client is told retrieval was skipped, not why.

An empty message with documents attached does not retrieve. A document sent with no words is a valid turn — the scope classifier treats it as one — but there is no question to search for.

What gets searched

Exactly the documents from the first condition. The route turns them into drive_file blocks and the stream processor reads those blocks back out, so the set the classifier judged is the set retrieved from. There is no second source of document ids.

When the decision fails

A classifier failure raises RAGSearchError, and the turn ends with an error the user sees and that is persisted to the conversation. Retrieval is never guessed at: answering from the model's own knowledge while the user believes their document was consulted is worse than an error.

This is the opposite of how scope is handled — MessageScopeClassifier fails open, letting a turn through when it cannot judge it, because the chat model's own system prompt is a second line of defence there and there is none here.