LLM'ler deterministik değildir: aynı prompt farklı çıktılar üretebilir, beklenen JSON şemasını ihlal edebilir veya halüsinasyon yapabilir. Production kalitesinde bir AI uygulamasında çıktı doğrulama, yeniden deneme ve guardrail katmanları zorunludur.

Structured Outputs: JSON Schema ile Şema Zorlama

using OpenAI.Chat;
using System.Text.Json;

// JSON schema ile çıktı formatını zorla
var secenek = new ChatCompletionOptions
{
    ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
        jsonSchemaFormatName: "urun_analizi",
        jsonSchema: BinaryData.FromString("""
        {
            "type": "object",
            "properties": {
                "baslik":      { "type": "string"  },
                "puan":        { "type": "number", "minimum": 0, "maximum": 10 },
                "artilari":    { "type": "array", "items": { "type": "string" } },
                "eksileri":    { "type": "array", "items": { "type": "string" } },
                "tavsiye":     { "type": "boolean" }
            },
            "required": ["baslik","puan","artilari","eksileri","tavsiye"],
            "additionalProperties": false
        }
        """),
        jsonSchemaIsStrict: true)
};

var yanit = await client.CompleteChatAsync(
    $"Şu ürünü analiz et: {urunAdi}", secenek);
var json = yanit.Value.Content[0].Text;

JSON Doğrulama ve Güvenli Parsing

public static bool JsonGecerliMi(string json, out JsonElement root)
{
    try
    {
        root = JsonDocument.Parse(json).RootElement;

        // Alan kontrolü
        if (!root.TryGetProperty("puan", out var puan))
            return false;
        if (puan.GetDouble() < 0 || puan.GetDouble() > 10)
            return false;
        if (!root.TryGetProperty("artilari", out var artilari) ||
            artilari.GetArrayLength() == 0)
            return false;

        return true;
    }
    catch (JsonException)
    {
        root = default;
        return false;
    }
}

// Kullanım
var yanit = await client.CompleteChatAsync(prompt, secenek);
if (!JsonGecerliMi(yanit.Value.Content[0].Text, out var kok))
    throw new InvalidOperationException("Geçersiz AI çıktısı");

Yeniden Deneme: Geçersiz Çıktıda Farklı Yaklaşım

public async Task<T> GuvenliSorAsync<T>(
    IChatClient chat,
    string prompt,
    int maxDeneme = 3)
{
    var mesajlar = new List<ChatMessage>
    {
        new(ChatRole.System, $"""
            Yanıtını YALNIZCA şu JSON formatında ver:
            {typeof(T).Name} tipine uygun JSON.
            Açıklama veya markdown ekleme.
            """),
        new(ChatRole.User, prompt)
    };

    for (int deneme = 1; deneme <= maxDeneme; deneme++)
    {
        var yanit = await chat.CompleteAsync(mesajlar);
        var metinYanit = yanit.Message.Text ?? "";

        try
        {
            // JSON bloğunu temizle (```json ... ``` varsa)
            var temiz = TemizleJsonBlok(metinYanit);
            var nesne = JsonSerializer.Deserialize<T>(temiz,
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            if (nesne is not null) return nesne;
        }
        catch (JsonException) { }

        // Başarısız olursa modele geri bildirim ver
        mesajlar.Add(new ChatMessage(ChatRole.Assistant, metinYanit));
        mesajlar.Add(new ChatMessage(ChatRole.User,
            $"Yanıtın geçersiz JSON formatında. Lütfen yalnızca geçerli JSON döndür, başka hiçbir şey ekleme. Deneme {deneme}/{maxDeneme}."));

        if (deneme < maxDeneme)
            await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, deneme)));
    }

    throw new Exception($"{maxDeneme} denemeden sonra geçerli yanıt alınamadı.");
}

private static string TemizleJsonBlok(string metin)
{
    var baslangic = metin.IndexOf('{');
    var bitis     = metin.LastIndexOf('}');
    return baslangic >= 0 && bitis > baslangic
        ? metin[baslangic..(bitis + 1)]
        : metin;
}

Guardrails: Girdi ve Çıktı Moderasyonu

public class GuardrailChatClient(IChatClient inner, IChatClient moderator) : IChatClient
{
    public async Task<ChatCompletion> CompleteAsync(
        IList<ChatMessage> messages,
        ChatOptions? options = null,
        CancellationToken ct = default)
    {
        // 1. Girdi güvenlik kontrolü
        var sonMesaj = messages.LastOrDefault()?.Text ?? "";
        var girisKontrolu = await moderator.CompleteAsync(
            $"Bu mesaj zararlı, saldırgan veya uygunsuz içerik mi? Yanıt: EVET veya HAYIR.\nMesaj: {sonMesaj}", cancellationToken: ct);

        if (girisKontrolu.Message.Text?.Contains("EVET") == true)
            throw new ContentPolicyException("Mesaj güvenlik politikasına aykırı.");

        // 2. AI'dan yanıt al
        var yanit = await inner.CompleteAsync(messages, options, ct);

        // 3. Çıktı güvenlik kontrolü
        var cikisKontrolu = await moderator.CompleteAsync(
            $"Bu yanıt zararlı bilgi içeriyor mu? EVET veya HAYIR.\nYanıt: {yanit.Message.Text}", cancellationToken: ct);

        if (cikisKontrolu.Message.Text?.Contains("EVET") == true)
            return new ChatCompletion(new ChatMessage(ChatRole.Assistant,
                "Bu konuda yardımcı olamıyorum."));

        return yanit;
    }

    // ... diğer IChatClient metodları
    public void Dispose() => inner.Dispose();
    public object? GetService(Type type, object? key = null) => inner.GetService(type, key);
    public IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
        IList<ChatMessage> messages, ChatOptions? options = null, CancellationToken ct = default)
        => inner.CompleteStreamingAsync(messages, options, ct);
}

// DI kaydı
builder.Services
    .AddOpenAIClient(opts => opts.ApiKey = config["OpenAI:ApiKey"]!)
    .AddChatClient("gpt-4o")
    .Use((inner, services) =>
        new GuardrailChatClient(inner,
            services.GetRequiredService<OpenAIClient>().AsChatClient("gpt-4o-mini")));

LLM çıktısına güvenmek yerine doğrulamak, retry ve guardrail katmanları eklemek production AI uygulamasının temel prensipleridir. Structured outputs ile şema ihlallerini önleyin, güvenli parsing ile hataları yakalayın, guardrail middleware ile içerik politikasını uygulayın.