
Find answers in your application’s knowledge
Choose which resource fields contribute knowledge, then retrieve relevant passages from those records and attached documents. Results keep their source identity, so an assistant can explain where an answer came from.
Retrieval respects the caller’s resource access and tenancy. It supplies grounding material; your application decides how to present it or use it in a generated answer.
Example — Ask about a policy
A person asks what the organization’s policy says about expenses. The application retrieves passages from readable knowledge documents and hands them to an assistant with their document and field attribution.
For engineers
Declare source fields, then call retrieval from an authorized operation using the caller’s context.
Use .ragSource() on selected text fields and z_file({ ragSource: ... }) for file-backed text. Forward the operation’s executionContext unchanged to retrieve. Narrow with resourceTypes where the feature should search only a specific corpus. Inspect searchStatus, embeddingStatus, refusedResourceTypes and truncation; a failed search, refused corpus and healthy empty search are separate outcomes. Search execution errors remain in operational logs; the result exposes safe availability diagnostics. Scores rank one query’s results and are not universal confidence percentages. PostgreSQL and MongoDB stores exist, with adapter-specific search infrastructure requirements.
Prepare the store that owns the records
Chunk storage follows the resource’s persistence adapter. Do not introduce a second database solely because a field becomes a corpus source.
| Resource adapter | Deployment prerequisite | What to check before querying |
|---|---|---|
| PostgreSQL | The vector extension, including for a currently lexical-only corpus | Startup capability probe and framework-managed chunk table/indexes |
| MongoDB | Search-capable deployment with $search and $vectorSearch, and permission to manage search indexes | Startup probe and queryability of the framework-managed search indexes; an ordinary database connection is insufficient |
| HTTP API or introspection | No owned persistent rows for derived chunks | Corpus declarations are refused on these adapters |
MongoDB indexes build asynchronously. A ready lexical arm can serve without vectors, and an available vector arm can operate while lexical search is unavailable. If neither usable arm can run, their availability reports produce failed search status. If only one requested arm runs, search status is degraded. Execution exceptions are still caught and logged, preserving the turn with failed search status and empty context. When an aggregate aborts before returning reliable arm reports, those requested arms are marked unknown. None of these outcomes should be described as an empty knowledge base.
Connect the declaration to the query
The following declaration is adapted from Wonder Todos’ knowledge-document schema. The decorator initialization and imports belong in the shared schema module. Fields without ragSource remain outside the corpus.
import { z } from 'zod';
import { initZodDecorators, RAGChunkingStrategy } from '@wildo-ai/zod-decorators';
initZodDecorators(z);
const KnowledgeText = z.object({
title: z.string().min(1).max(200).ragSource(),
body: z.string().max(200_000).ragSource({
chunkingStrategy: RAGChunkingStrategy.MARKDOWN,
chunkSize: 1_200,
chunkOverlap: 150,
}).optional(),
internalReviewNotes: z.string().optional(),
});
Inside the registered ask custom-operation handler, the application resolves the retrieval service lazily from its container and forwards the caller’s existing context. This is the actual call shape; question, limit and maxContextCharacters come from the operation’s validated request. projectAnswer is the operation’s response mapper; it retains source identifiers and passages.
retrievalService ??= container.get<RagRetrievalBackendService>(
SAAS_SERVICE_TYPES.RagRetrievalService,
);
const result = await retrievalService.retrieve({
query: question,
executionContext,
resourceTypes: [TasksManager_ResourceType.KNOWLEDGE_DOCUMENTS],
expandRelationships: false,
...(limit !== undefined && { limit }),
...(maxContextCharacters !== undefined && { maxContextCharacters }),
});
return projectAnswer(question, result);
The surrounding operation owns its request/response DTOs and role declaration, and registers through createResourceCustomServiceImplementation. The assistant’s function tool calls that same operation through executeCustomOperation; it does not expose an unscoped retrieval endpoint or accept tenant identity from the model.
Size the context and keep its attribution
| Choice | Meaning | Consequence for the application |
|---|---|---|
resourceTypes | Narrows eligible resource types; does not grant access | A requested type can still appear in refusedResourceTypes |
limit | Number of chunk hits, clamped to the engine maximum | Several hits can form one returned row; do not expect that many documents |
expandRelationships | One-hop related-record reads, off by default | Enable deliberately when related context adds value; it adds reads and context |
maxContextCharacters | Budget for assembled context | Later rows can be omitted, but the best row is retained even if it alone exceeds the budget; this is not a hard model-token limit |
Inside the operation, consume the actual result instead of treating a passages array as a complete answer. This illustrative projection preserves the service’s attribution and diagnostics; keep operational refusal details in an appropriate authenticated response or diagnostics surface.
const sources = result.rows.map((row) => ({
resourceType: row.resourceType,
resourceId: row.resourceId,
header: row.header,
passages: row.passages,
}));
return {
context: result.rows.map((row) => row.contextText).join('\n\n'),
sources,
mode: result.mode,
searchStatus: result.searchStatus,
embeddingStatus: result.embeddingStatus,
searches: result.searches,
chunkHitCount: result.chunkHitCount,
contextCharacters: result.contextCharacters,
refusedResourceTypes: result.refusedResourceTypes,
truncation: result.truncation,
};
searchStatus distinguishes a healthy search from a degraded, failed or unattempted one. embeddingStatus separately distinguishes an intentionally absent provider from a failed query embedding. mode describes the requested search; searches reports actual arm outcomes for each admitted resource type. If an aggregate call aborts before returning those outcomes, requested arms are marked unknown rather than claiming they ran.
contextText retains the row/field framing already assembled by the engine. Keep the source IDs beside the generated answer so the interface can provide citations. The model still needs instructions about how to use that context and what to do when it cannot support an answer.
| Observed result | Appropriate interpretation |
|---|---|
| Healthy search, embeddings not configured | Supported wording-based search; an embeddings provider is optional |
| Embedding failed, search healthy | Wording-based search completed, but semantic matching was unavailable for this call |
| Search degraded | Some requested search arms or stores were unavailable; explain the reduced coverage |
| Healthy search, empty rows | No usable context was returned; inspect access refusals and dropped-row counts before concluding nothing matched |
| Search failed or not run | Do not claim the knowledge base contains no answer; explain that the search was unavailable or not performed |
| Refused resource types or dropped rows | Inspect the specific refusal/truncation reason: access and scope refusals differ from failed row reads; do not elevate the caller to hide a refusal |
| Context budget reached | Some context was omitted; use the truncation report and the model’s own input budget |
| Logged search-store failure | The service preserves turn availability and reports failed search explicitly; private database error details remain in operational logs |
For attached documents, use the local extraction declaration or explicitly configure provider-backed recognition. Source-field chunking, background ingestion and this query are separate stages of the same corpus.