RAG (Retrieval-Augmented Generation), LLM'lerin kendi eğitim verisi dışındaki güncel veya özel bilgilerle yanıt üretmesini sağlayan mimari bir kalıptır. .NET, OpenAI embeddings ve Qdrant vektör veritabanıyla tam bir RAG pipeline kurabilirsiniz.
RAG Nedir ve Neden Gerekli?
GPT-4o gibi modeller bilgi kesme tarihinden sonraki verileri bilmez ve şirketinizin iç dokümanlarına erişemez. RAG bu boşluğu şu adımlarla doldurur: dokümanları parçalara böl, her parçayı vektöre dönüştür, vektör veritabanına kaydet; soru geldiğinde soruyu da vektöre çevir, benzer parçaları bul ve bunları model bağlamına ekle.
Kullanılan Paketler
// dotnet add package Microsoft.Extensions.AI
// dotnet add package Microsoft.Extensions.AI.OpenAI
// dotnet add package Qdrant.Client
Doküman Chunking Stratejisi
public static IEnumerable<string> MetniParcala(
string metin,
int maxKelime = 200,
int cakisma = 30)
{
var kelimeler = metin.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var parca = new List<string>();
int sayac = 0;
foreach (var kelime in kelimeler)
{
parca.Add(kelime);
sayac++;
if (sayac >= maxKelime)
{
yield return string.Join(" ", parca);
// Örtüşme: bağlamı korumak için son 'cakisma' kelimeyi tut
parca = parca.Skip(maxKelime - cakisma).ToList();
sayac = parca.Count;
}
}
if (parca.Count > 0)
yield return string.Join(" ", parca);
}
Qdrant: Vektör Veritabanı Kurulumu
using Qdrant.Client;
using Qdrant.Client.Grpc;
var qdrant = new QdrantClient("localhost", 6334);
// Koleksiyon oluştur
// text-embedding-3-small modeli 1536 boyutlu vektör üretir
await qdrant.CreateCollectionAsync("belgeler",
new VectorParams
{
Size = 1536,
Distance = Distance.Cosine
});
// Docker ile Qdrant başlatmak için:
// docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
Belge İndeksleme Pipeline'ı
public class BelgeIndexleyici(
IEmbeddingGenerator<string, Embedding<float>> embedder,
QdrantClient qdrant)
{
public async Task IndexleAsync(
string belgeId,
string icerik,
string kaynak,
CancellationToken ct = default)
{
var parcalar = MetniParcala(icerik, maxKelime: 200, cakisma: 30).ToList();
var noktalar = new List<PointStruct>();
for (int i = 0; i < parcalar.Count; i++)
{
var vektor = await embedder.GenerateEmbeddingVectorAsync(
parcalar[i], cancellationToken: ct);
noktalar.Add(new PointStruct
{
Id = new PointId { Uuid = Guid.NewGuid().ToString() },
Vectors = vektor.ToArray(),
Payload =
{
["belge_id"] = belgeId,
["parca_index"] = i,
["icerik"] = parcalar[i],
["kaynak"] = kaynak,
["tarih"] = DateTime.UtcNow.ToString("O")
}
});
// Büyük belgeler için toplu upsert (100'er parça)
if (noktalar.Count == 100)
{
await qdrant.UpsertAsync("belgeler", noktalar, cancellationToken: ct);
noktalar.Clear();
}
}
if (noktalar.Count > 0)
await qdrant.UpsertAsync("belgeler", noktalar, cancellationToken: ct);
Console.WriteLine($"{parcalar.Count} parça indexlendi: {belgeId}");
}
}
Tam RAG Pipeline
public class RagServisi(
IEmbeddingGenerator<string, Embedding<float>> embedder,
QdrantClient qdrant,
IChatClient chatClient)
{
public async Task<string> SoruCevapla(string soru, CancellationToken ct = default)
{
// 1. Soruyu vektöre çevir
var sorguVektoru = await embedder.GenerateEmbeddingVectorAsync(
soru, cancellationToken: ct);
// 2. En benzer parçaları bul
var sonuclar = await qdrant.SearchAsync(
collectionName: "belgeler",
vector: sorguVektoru.ToArray(),
limit: 5,
scoreThreshold: 0.70f,
cancellationToken: ct);
if (!sonuclar.Any())
return "Sorunuzla ilgili bilgi bulunamadı.";
// 3. Bağlamı hazırla
var baglamlar = sonuclar
.OrderByDescending(r => r.Score)
.Select(r => r.Payload["icerik"].StringValue);
var baglamMetni = string.Join("\n\n---\n\n", baglamlar);
// 4. LLM'e gönder
var mesajlar = new List<ChatMessage>
{
new(ChatRole.System, """
Sen bir teknik dokümantasyon asistanısın.
YALNIZCA aşağıdaki bağlam bilgisine dayanarak yanıt ver.
Bağlamda olmayan bilgiler için "Bu konuda bilgim yok." de.
Kısa ve net yanıtlar ver.
"""),
new(ChatRole.User, $"Bağlam:\n{baglamMetni}\n\nSoru: {soru}")
};
var yanit = await chatClient.CompleteAsync(mesajlar, cancellationToken: ct);
return yanit.Message.Text ?? "Yanıt alınamadı.";
}
}
DI Kaydı
// Program.cs
builder.Services.AddSingleton(new QdrantClient("localhost", 6334));
builder.Services
.AddOpenAIClient(opts => opts.ApiKey = config["OpenAI:ApiKey"]!)
.AddChatClient("gpt-4o");
builder.Services
.AddOpenAIClient()
.AddEmbeddingGenerator<string, Embedding<float>>("text-embedding-3-small");
builder.Services.AddScoped<BelgeIndexleyici>();
builder.Services.AddScoped<RagServisi>();
RAG, LLM'lerin halüsinasyon üretme riskini minimize ederken güncel ve özel verilerle çalışmasını sağlar. Doğru chunk boyutu, yeterli örtüşme ve iyi bir sistem prompt'u bir RAG sisteminin kalitesini belirleyen üç kritik faktördür.