Skip to main content

Overview

A RAG session owns one corpus. You ingest documents into it, and it chunks, embeds, and indexes them. Asking a question retrieves the closest chunks and generates an answer grounded in them. Everything runs on the device.

Dependencies

RAG ships inside the core SDK, so there is no separate RAG artifact. You do need a backend that provides embeddings: on Android that is runanywhere-onnx, which is what the MiniLM embedding models run on.
rag.open() loads rac_backend_onnx itself on first use, so its embedding operations are registered before the session exists. You do not load it by hand.

Basic usage

open() downloads and loads both models before returning the session, so there is no separate prepare step. Passing a null llmModel opens a retrieval-only session, where search() works and query() throws. Sessions are independent. Two sessions over different corpora can be open at the same time.

rag.open()

ModelRef(id) points at a registered model. It throws SDKException when either model cannot be loaded.

RagSession

close() releases the native session and is safe to call twice. clear() drops every indexed document but keeps the session open. Every verb throws SDKException once the session is closed.
Close the session when you are done with it. It holds an embedding model, an LLM, and a vector index resident at the same time.

Ingestion

RagDocument(text, metadata) takes plain text and a typed Map<String, String>, not a JSON string. RagDocument.file(path) reads a UTF-8 text file and throws when it is missing or unreadable. The metadata rides along onto every chunk and comes back on each Match.

Retrieval without generation

Match carries text, score (cosine similarity), and metadata. topK defaults to the session’s configured value.

Querying

RagResult carries answer, sources: List<Match>, and the same metrics block as GenerationResult: inputTokens, outputTokens, timeToFirstTokenMs, tokensPerSecond, requestId, and model. The answer is generated with the same LlmOptions the llm namespace uses, so systemPrompt, reasoning, and the sampling knobs all apply. See generate().

Streaming

RagEvent has three arms: Retrieved(matches) with the chunks that will ground the answer, then Token(text, kind), then Completed(result). A failure is thrown into the collector rather than delivered as an event. Cancelling the collector cancels that one native request. Concurrent collectors and unary queries on the same session serialize instead of killing each other.

Statistics

RagConfig

Chunking and retrieval knobs are fixed for the life of the session. Defaults come from the rac_default annotations in idl/rag.proto.
Leave similarityThreshold null unless you have measured that a floor helps with your embedding model. MiniLM-class sentence embeddings rarely exceed a cosine similarity of about 0.5 even for genuinely relevant text, and chunking lowers each chunk’s score further, so a positive floor filters out real matches and the answer model reports that it has no information. topK bounds the result count instead. Chunk size and overlap apply at ingestion, so changing them means opening a new session and re-ingesting. Smaller chunks retrieve more precisely; larger ones carry more surrounding context. RagConfig.DEFAULT_TOP_K, DEFAULT_CHUNK_SIZE, and DEFAULT_CHUNK_OVERLAP expose the three numeric defaults.

ViewModel

Closing the session outlives the ViewModel scope, which is why the teardown runs outside viewModelScope. Cancelling the scope first would cancel the close too.

Embeddings on their own

The embeddings namespace exposes the same embedding model directly, for your own similarity search or clustering.
embed(texts, options: EmbedOptions? = null) returns one Embedding per input in input order, each carrying its index and a FloatArray vector. EmbedOptions has normalize (NormalizeMode.L2 by default, or NONE) and pooling (PoolingMode.MEAN by default, or CLS or LAST). It throws when no embedding model is loaded.

Reranking

rerank scores documents against a query with a cross-encoder, which is more accurate than the cosine similarity retrieval uses and much slower. It needs a rerank model loaded.
RankedResult carries index, pointing back at the input list, and relevanceScore. Results are sorted best first. topN is optional and null returns everything. Set RagConfig(rerank = true) to have a session rerank its retrieved chunks before generating, instead of calling this yourself.

Errors

Branch on category, not on message text. queryStream throws into the collector rather than emitting an error event. See Error handling.

LLM generation

Text generation API

LoRA adapters

Adapt model behavior

Configuration

Register and download models

Best practices

Memory and lifecycle