Getting Started
This page walks through a complete end-to-end setup: installing packages, wiring up DI, ingesting a document, and running your first question-answer call. All code assumes .NET 10 and the Microsoft.Extensions.AI ecosystem.
1. Install packages
dotnet add package Rag.NET
dotnet add package Rag.NET.VectorStores.PgVector # or Rag.NET.VectorStores.Qdrant / Rag.NET.VectorStores.AzureAISearch
dotnet add package Rag.NET.Parsers.Pdf # add as many format parsers as you need
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.AI.OpenAI # or your provider's Microsoft.Extensions.AI integration
Rag.NET itself depends only on the Microsoft.Extensions.AI abstractions — the concrete ServiceCollection, AddChatClient/AddEmbeddingGenerator, and a provider client (here, OpenAI's) come from these three packages and need adding explicitly.
Not sure which packages your scenario needs — or whether you need more than these? Choosing packages walks through the decisions and what arrives transitively.
2. Register AI services
Rag.NET consumes two standard Microsoft.Extensions.AI abstractions. Register them anywhere in your service configuration — order does not matter, because Rag.NET resolves them from the container when the pipeline is built, not when it is registered:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.AI;
using OpenAI;
var services = new ServiceCollection();
// Example using the OpenAI provider (Microsoft.Extensions.AI.OpenAI)
services.AddChatClient(
new OpenAIClient("sk-...").GetChatClient("gpt-4o").AsIChatClient());
services.AddEmbeddingGenerator(
new OpenAIClient("sk-...").GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator());
Any provider that implements IChatClient and IEmbeddingGenerator<string, Embedding<float>> works — Ollama, Azure OpenAI, and others are drop-in replacements. For Microsoft Foundry — the cloud endpoint, Foundry Local, or the non-OpenAI catalogue models — this step is written out in full in Microsoft Foundry.
3. Configure Rag.NET
using Rag.NET.DependencyInjection;
using Rag.NET.PgVector;
using Rag.NET.Parsers.Pdf;
services.AddRagNet(rag => rag
.UsePgVector("Host=localhost;Database=ragdb;Username=postgres;Password=secret",
vectorDimensions: 1536)
.AddPdfParser()
.AddParser<MyCustomParser>()); // optional — add any IDocumentParser
AddRagNet registers:
IRagPipeline(the main entry point)IChunkingStrategydefaulting toRecursiveChunkingStrategy- Built-in Text and Markdown parsers (always available in
Rag.NETcore)
The configure delegate gives you a RagBuilder for fluent additional configuration. All settings are optional — sensible defaults are applied.
4. Build the service provider
var provider = services.BuildServiceProvider();
var pipeline = provider.GetRequiredService<IRagPipeline>();
5. Ingest a document
IngestAsync parses, chunks, embeds, and stores a document in one call:
using Rag.NET.Models;
var metadata = new DocumentMetadata
{
DocumentId = new DocumentId("report-2024-q4"), // your stable identifier — used for updates/deletes
FileName = "report.pdf",
ContentType = "application/pdf",
Tags = new Dictionary<string, MetadataValue>
{
["department"] = "finance",
["year"] = "2024",
},
};
using var stream = File.OpenRead("report.pdf");
var result = await pipeline.IngestAsync(stream, metadata);
if (result.IsSuccess)
Console.WriteLine($"Stored {result.Value.ChunksStored} chunks for {result.Value.DocumentId}");
else
Console.WriteLine($"Ingestion failed: {result.Error}");
The ContentType value drives parser selection. Omitting it defaults to text/plain. Tags are propagated into every chunk's Metadata dictionary and can be used for metadata filtering at query time.
Re-ingesting a document
To replace an existing document without accumulating stale chunks, set Overwrite = true:
using Rag.NET.Models.Options;
await pipeline.IngestAsync(stream, metadata,
options: new IngestionOptions { Overwrite = true });
This deletes all previously stored chunks for metadata.DocumentId before storing the new ones.
6. Ask a question
using Rag.NET.Models.Options;
var response = await pipeline.AskAsync("What are the key findings in the Q4 report?");
Console.WriteLine(response.Answer);
foreach (var source in response.Sources)
Console.WriteLine($" [{source.Score:F2}] {source.Chunk.Text[..80]}...");
AskAsync embeds the query, retrieves the top-K most relevant chunks, builds a grounded prompt, calls IChatClient, and returns a RagResponse containing both the answer text and the source chunks used.
Streaming responses
For a better interactive experience use AskStreamingAsync, which yields text deltas as the model produces them:
await foreach (var update in pipeline.AskStreamingAsync("Summarize the report"))
{
if (update.Sources is { Count: > 0 })
Console.WriteLine($"[Found {update.Sources.Count} source(s)]");
if (update.TextDelta is not null)
Console.Write(update.TextDelta);
}
The first RagStreamingUpdate always contains Sources and a null TextDelta. Subsequent updates contain only TextDelta.
7. Retrieve without chat
If you want to drive your own LLM call or just inspect the retrieved passages:
var results = await pipeline.RetrieveAsync("key findings", new RetrievalOptions
{
TopK = 10,
MinScore = 0.6,
});
if (results.IsSuccess)
foreach (var r in results.Value)
Console.WriteLine($"[{r.Score:F2}] {r.Chunk.Text}");
8. Delete a document
await pipeline.DeleteAsync("report-2024-q4");
Removes all stored chunks associated with the given document ID from both the vector store and the in-memory BM25 index.
Optional extension packages
The core Rag.NET package includes RecursiveChunkingStrategy and ChatAnswerEngine out of the box. Install additional packages for more advanced capabilities:
Semantic chunking
dotnet add package Rag.NET.Chunking
services.AddRagNet(rag => rag.UseSemanticChunking());
Token-aware chunking
dotnet add package Rag.NET.Chunking
services.AddRagNet(rag => rag.UseTokenAwareChunking());
C# semantic chunking
dotnet add package Rag.NET.Chunking.CSharp
services.AddRagNet(rag => rag.UseCSharpChunking());
Uses Roslyn to split C# source files at real AST boundaries — each class, method, property, interface etc. becomes its own chunk with structured metadata (csharp.kind, csharp.namespace, csharp.name, etc.).
HyDE query expansion
dotnet add package Rag.NET.QueryTechniques
services.AddRagNet(rag => rag.UseHyde());
MapReduce answer engine
dotnet add package Rag.NET.AnswerEngines
services.AddRagNet(rag => rag.UseMapReduceAnswerEngine());
Persistent cross-session memory
dotnet add package Rag.NET.Memory
services.AddRagNet(rag => rag
.UseConversationMemory(configure: mem => mem.UsePersistentMemory()));
Next steps
- Architecture — understand how the pipeline works internally
- Chunking — choose the right strategy for your content type
- Retrieval — enable hybrid search, metadata filtering, and score thresholds
- Post-Retrieval — improve answer quality with reordering and redundancy filtering
- Observability — add logging, tracing, and resilience