Skip to content
3 min read · 599 words

Retrievable

Primitives covers the eight-primitive overview.

Where a Memory is something the agent recalls from a previous conversation, a Retrievable is something the agent pulled in from somewhere else for this turn. A chunk of a policy document. A snippet from a knowledge base. A web result. A passage out of a customer's uploaded PDF. It is the primitive that retrieval-augmented generation actually retrieves — the typed container the rest of the ADK needs in order to reason about what just got pulled in, where it came from, and how much weight it should carry.

The required field is Retrievable.trustTier, and it is the single most opinionated thing in the entire primitives set. The threat model for retrieved content depends entirely on where it came from: a signed internal policy document is in a different universe from a scraped web page, which is in a different universe from a stranger's uploaded resume. The executor that ultimately renders the record cannot guess which is which, the model has no way to tell the difference once they are all text on the screen, and the consequences of getting this wrong are how prompt injection through retrieval ends up working. Your retrieval middleware is the only party that knows the provenance, so your retrieval middleware is the only party trusted to declare it. The three values — 'first-party', 'third-party-public', 'third-party-private' — name the only distinctions that load-bear at render time; see RAG tiering for how an executor should treat each one.

The optional fields exist so you, future-you, and the model can all judge the record after the fact: source (the URL, path, or KB id), kind ('policy', 'web-page', 'pdf', whatever your retrieval pipeline emits), and score (relevance or similarity in [0, 1] if your retriever produced one). The ADK does not require them; an executor that wants to surface provenance to the model uses them.

Inline rendering and automatic spooling

Retrievable.inline controls rendering once content is backed by a SpooledArtifact: false (the resolved default) renders a compact artifact handle, while true materializes the content in the prompt. It does not control storage. contentString() always fully materializes the content for direct programmatic consumers regardless of this flag. estimateTokens() does not: for non-inline, hinted spooled content it deliberately returns the compact handle's token count (matching what actually renders), falling back to a full-content estimate only when hints are absent or inline is true; see budgets.

Storage-time auto-spooling wraps plain static string content when it enters through storeRetrievable or mutateRetrievable on either context type, through fetchRetrievables(), or through the preload normalization performed by DispatchRunner. A record added from scratch with ctx.turnRetrievables.add() is explicitly not covered if it never passed through fetch or store. Dynamic Tokenizable content is also never auto-spooled: this is a pre-existing limitation, and it continues to use its no-context fallback when rendered. A record with inline: true is also skipped by auto-spool: the handle machinery it exists for only applies once inline === false, and wrapping inline content in a SpooledArtifact would only make it more expensive to measure (its budget/thrift token estimate degrades from a synchronous Tokenizable count to an async reader read) for no rendering benefit.

A converter's explicit spool hook wins over auto-spooling. inline remains an independent rendering choice: a record spooled through an explicit spool hook can still render inline.

No 'unknown'. No auto-classification. No safe default.

Declaring the tier is the entry fee for handing content to the executor at all. If your middleware does not know the provenance of what it returns, that is the bug worth fixing.