RAGDefining indexes
RAG

Defining indexes

Register named knowledge indexes with sources, embedding models, and vector stores.

A named index is the application's definition of a searchable knowledge collection. Keep its name stable so ingestion jobs and queries refer to the same collection.

Install the host and provider dependencies

For this OpenAI agent example, add the RAG, agent, and hosting packages:

.NET CLI
dotnet add package Runiq.AI.Rag
dotnet add package Runiq.AI.Agents
dotnet add package Runiq.AI.Core

The typed embedding helpers in Runiq.AI.Agents.Providers.OpenAI are included in the Runiq.AI.Agents package.

Supply OpenAI:ApiKey with .NET user-secrets, the OpenAI__ApiKey environment variable, or your host's secret provider. Do not put real credentials in source-controlled appsettings.json or appsettings.Development.json files.

Register the index and its agent

This startup example selects Markdown company policies and a disposable in-memory store, then explicitly connects an agent to that index. Create the document directory and supply the API key before starting the host:

Program.cs
using Runiq.AI.Agents.Providers.OpenAI;
using Runiq.AI.Agents;
using Runiq.AI.Agents.Configuration;
using Runiq.AI.Core;
using Runiq.AI.Rag.DependencyInjection;
using Runiq.AI.Rag.Models.Retrieval;

var builder = WebApplication.CreateBuilder(args);
var apiKey = builder.Configuration["OpenAI:ApiKey"]
    ?? throw new InvalidOperationException("OpenAI:ApiKey is required.");
const string indexName = "company-policies";

builder.Services.AddRuniqRag(rag =>
{
    rag.AddIndex(indexName, index => index
        .UseDirectory(
            Path.Combine(AppContext.BaseDirectory, "documents", "company-policies"),
            "*.md",
            recursive: true)
        .UseOpenAiEmbeddingModel(OpenAiEmbeddingModels.TextEmbedding3Small)
        .UseInMemoryVectorStore()
        .ConfigureIngestion(ingestion => ingestion.OnStartup()));
});

var agent = new Agent(
        id: "company-policy-assistant",
        name: "Company Policy Assistant",
        instructions: "Answer company policy questions from the retrieved documents. Cite sources and state when evidence is missing.",
        model: "openai/gpt-4.1-mini",
        apiKey: apiKey)
    .UseRag(rag =>
    {
        rag.IndexName = indexName;
        rag.Mode = RagExecutionMode.Required;
        rag.RetrievalMode = RagRetrievalMode.Hybrid;
        rag.NoContextBehavior = RagNoContextBehavior.ReturnNotFound;
        rag.Acceptance.MinimumRelevance = 0.55;
        rag.Acceptance.CandidateCount = 20;
        rag.Acceptance.MaximumAcceptedResults = 6;
    });

builder.Services.AddRuniqServer(options => options.AddAgent(agent));

var app = builder.Build();
app.Run();

There are three separate registrations here: AddIndex defines the collection, .UseRag(...) connects the agent to it, and AddRuniqServer(...) registers the agent runtime. Defining the index alone never enables agent retrieval.

Keep this order: the current OpenAI host integration inspects already registered indexes when AddRuniqServer(...) adds RAG-enabled OpenAI agents. It registers the matching OpenAI embedding client using the agent's API key. UseOpenAiEmbeddingModel(...) alone only selects the model reference; it does not perform that client registration. For custom providers or retrieval-only hosts, register an IEmbeddingClient through AddRagEmbeddingClient(...) explicitly.

OnStartup rebuilds this disposable index on every process start and blocks host startup while ingestion runs. It is appropriate here for a small local collection; choose a persistent store and a deliberate ingestion strategy for production.

Hybrid needs both semantic and lexical retrieval support. The acceptance settings control candidate evaluation in the agent runtime; the example threshold should be tuned with representative policy questions. Required with ReturnNotFound skips model invocation when successful retrieval leaves no accepted context.

The example resolves documents from AppContext.BaseDirectory, independent of the process working directory. Keep the corpus under documents/company-policies in the project and copy it to build and publish output. Add this item to the application's project file:

XML
<ItemGroup>
  <None Update="documents/company-policies/**/*.md"
        CopyToOutputDirectory="PreserveNewest"
        CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>

Understand the registration

SettingResponsibility
AddIndex("company-policies", ...)Give ingestion and retrieval a shared logical name.
UseDirectory(...)Describe which files discovery should read when ingestion runs.
UseOpenAiEmbeddingModel(...)Select the typed provider/model reference.
UseInMemoryVectorStore()Select disposable local storage.
ConfigureIngestion(...)Decide when ingestion starts automatically, if at all.

Registration does not scan the directory, call the embedding service, ingest documents, or open a database connection. It stores the configuration for later runtime execution.

Indexes cannot be created from Dashboard > RAG Management. Register them in application startup code; RAG Management shows those registrations and lets you inspect or start/cancel ingestion.

Configure chunk boundaries

The index builder can override chunk size and overlap. These values are character counts:

Snippet
index.ConfigureChunking(maxChunkLength: 1200, chunkOverlap: 150);

This is an example configuration to evaluate with your documents. Test whether retrieved chunks contain enough surrounding text to answer real questions before choosing production values.

Select a persistent store

For PostgreSQL, replace the index's in-memory selection with:

Snippet
index.UsePostgreSqlVectorStore();

Import Runiq.AI.Rag.PostgreSql.DependencyInjection and register the provider as shown in PostgreSQL. A store selection identifies the provider; the provider registration supplies its connection configuration.

Continue with Ingestion to make the collection searchable.

Reference: RAG package guide.

On this page