Skip to main content
Wildo.ai Coming soon

AI models and retrieval

Find answers in your application’s knowledge

Retrieve attributable passages from selected resource fields and documents using the caller’s access context.

Selected source records contribute attributed passages to an answer.

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 adapterDeployment prerequisiteWhat to check before querying
PostgreSQLThe vector extension, including for a currently lexical-only corpusStartup capability probe and framework-managed chunk table/indexes
MongoDBSearch-capable deployment with $search and $vectorSearch, and permission to manage search indexesStartup probe and queryability of the framework-managed search indexes; an ordinary database connection is insufficient
HTTP API or introspectionNo owned persistent rows for derived chunksCorpus 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

ChoiceMeaningConsequence for the application
resourceTypesNarrows eligible resource types; does not grant accessA requested type can still appear in refusedResourceTypes
limitNumber of chunk hits, clamped to the engine maximumSeveral hits can form one returned row; do not expect that many documents
expandRelationshipsOne-hop related-record reads, off by defaultEnable deliberately when related context adds value; it adds reads and context
maxContextCharactersBudget for assembled contextLater 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 resultAppropriate interpretation
Healthy search, embeddings not configuredSupported wording-based search; an embeddings provider is optional
Embedding failed, search healthyWording-based search completed, but semantic matching was unavailable for this call
Search degradedSome requested search arms or stores were unavailable; explain the reduced coverage
Healthy search, empty rowsNo usable context was returned; inspect access refusals and dropped-row counts before concluding nothing matched
Search failed or not runDo not claim the knowledge base contains no answer; explain that the search was unavailable or not performed
Refused resource types or dropped rowsInspect 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 reachedSome context was omitted; use the truncation report and the model’s own input budget
Logged search-store failureThe 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.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.