Yerel Yapay Zekâ Agent Geliştirmeye Başlangıç - 5
Semantic Kernel ile Etkileşimli LLM Playground Oluşturmak
Önceki bölümlerde Semantic Kernel ile çalışan ürün asistanımızı MVC tabanlı
bir web arayüzüne taşıdık. Kullanıcı artık tarayıcıdan soru sorabiliyor,
ProductsPlugin veritabanındaki ürünleri sorgulayabiliyor ve konuşma geçmişi
PostgreSQL'de saklanıyor.
Bu bölümde chat ekranını küçük bir LLM laboratuvarına dönüştüreceğiz. Kullanıcı yalnızca soru sormayacak; modelin üretim ayarlarını değiştirerek aynı soruya verilen cevapların nasıl farklılaştığını da gözlemleyebilecek.
Ekrana şu seçenekleri ekleyeceğiz:
- Temperature
- Top P
- Top K
- Maksimum cevap token sayısı
- Dengeli, yaratıcı ve odaklı hazır profiller
- Ollama'da yüklü modeller arasından seçim
- Streaming cevap
- Cevap beklenirken animasyon
- Cevap balonunda kullanılan ayarların gösterilmesi
Bu bölümün amacı yeni bir ürün özelliği eklemekten çok, LLM parametrelerinin çıktıyı nasıl etkilediğini anlaşılır ve denenebilir hale getirmektir.
Neden Sadece Streaming Yetmiyor?
Bir cevabın parça parça ekrana gelmesi kullanıcı deneyimini iyileştirir. Ancak streaming, modelin hangi kelimeleri seçeceğini açıklamaz; yalnızca üretilen metnin aktarılma biçimini değiştirir.
Bir kullanıcı şu iki soruyu ayırt edemeyebilir:
Model daha yaratıcı cevap verdi.
Model cevabı daha erken göstermeye başladı.
Bu nedenle arayüze model üretim ayarlarını da koyuyoruz. Böylece kullanıcı aynı soruyu farklı profillerle gönderip sonucu karşılaştırabiliyor.
Örnek:
Soru: UMAI Bilişim için kısa bir ürün tanıtımı yaz.
Temperature: 0.2
Sonuç: Daha tutarlı ve kontrollü bir cevap
Temperature: 1.2
Sonuç: Daha değişken ve yaratıcı bir cevap
Bu fark her modelde aynı şiddette görülmeyebilir. Modelin büyüklüğü, eğitimi, quantization seviyesi ve kullanılan connector da sonucu etkiler. Ama ayarların gerçekten modele gönderilmesi, deneyi anlamlı hale getirir.
Hangi Ayarları Açıyoruz?
Bu projede yerel Ollama connector'ının doğrudan desteklediği dört ayarı kullanıyoruz.
Temperature
Temperature modelin olası token'lar arasında ne kadar farklı seçim yapmasına izin verileceğini etkiler. Düşük değerler daha kontrollü, yüksek değerler daha değişken cevaplar üretme eğilimindedir.
Bu projedeki aralık: 0.0 - 1.5
Top P
Top P, modelin seçim yapacağı olası token kümesini olasılık dağılımına göre sınırlar. Daha düşük değerler daha dar ve odaklı bir seçim kümesi oluşturur.
Bu projedeki aralık: 0.1 - 1.0
Top K
Top K, modelin sonraki token için değerlendireceği en olası adayların sayısını belirler. Daha düşük değerler daha muhafazakâr, yüksek değerler daha çeşitli seçimler oluşturabilir.
Bu projedeki aralık: 1 - 100
NumPredict
Ollama connector'ındaki NumPredict, üretilecek maksimum çıktı token sayısını
belirler. Bu bir kalite ayarı değil, aynı zamanda cevabın üst sınırıdır.
Bu projedeki aralık: 64 - 2048
Thinking Neden Eklenmedi?
Bazı modeller “thinking” veya benzer akıl yürütme seçenekleri sunabilir. Ancak bu ayar tüm LLM sağlayıcılarının ortak bir Semantic Kernel ayarı değildir. Kullanılan model, Ollama sürümü ve connector sürümüne göre adı ve davranışı değişebilir.
Bu nedenle mevcut uygulamaya işlevsiz bir Thinking checkbox'ı koymadık.
Bir ayar arayüzde görünüyorsa gerçekten modele gönderilmesi gerekir.
İleride belirli bir model ve Ollama API sözleşmesi için model-özel ayarlar
eklenebilir.
Ayar Modelini Tanımlamak
Üretim ayarlarını controller parametrelerine dağınık halde bırakmak yerine tek bir record içinde topluyoruz:
public sealed record ChatGenerationOptions(
double Temperature = 0.7,
double TopP = 0.9,
int TopK = 40,
int MaxTokens = 512)
{
public ChatGenerationOptions Normalize() => new(
Math.Clamp(Temperature, 0, 1.5),
Math.Clamp(TopP, 0.1, 1),
Math.Clamp(TopK, 1, 100),
Math.Clamp(MaxTokens, 64, 2048));
}
Buradaki Normalize metodu yalnızca UI doğrulamasına güvenmememizi sağlar.
İstemci doğrudan HTTP isteği gönderse bile sunucu kabul edilebilir sınırlar
içinde çalışır.
Semantic Kernel Ayarlarına Dönüştürmek
ChatService, uygulamaya ait seçenekleri Ollama connector'ının beklediği
OllamaPromptExecutionSettings nesnesine çevirir:
private static OllamaPromptExecutionSettings CreateExecutionSettings(
ChatGenerationOptions? options)
{
var normalized = (options ?? new ChatGenerationOptions()).Normalize();
return new OllamaPromptExecutionSettings
{
Temperature = (float)normalized.Temperature,
TopP = (float)normalized.TopP,
TopK = normalized.TopK,
NumPredict = normalized.MaxTokens,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
}
Burada önemli nokta, ayarların yalnızca ekranda gösterilmemesidir. Değerler gerçekten Semantic Kernel çağrısına girer:
await _completionService.GetStreamingChatMessageContentsAsync(
history,
kernel: _kernel,
executionSettings: CreateExecutionSettings(options),
cancellationToken: cancellationToken);
FunctionChoiceBehavior.Auto() ayarı korunur. Yani model, ürün sorusu için
gerekli gördüğünde ProductsPlugin fonksiyonlarını çağırmaya devam eder.
Streaming ile Birlikte Kullanmak
Streaming metodunun artık seçenekleri de alması yeterlidir:
public async IAsyncEnumerable<string> StreamAsync(
Guid sessionId,
string question,
ChatGenerationOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
Modelden gelen parçalar tarayıcıya aktarılırken cevap yine StringBuilder
içinde biriktirilir. Böylece kullanıcı cevabı anlık görür, tamamlanan cevap da
PostgreSQL'e tek assistant mesajı olarak kaydedilir.
Controller ve Arayüz
Form değerleri action parametrelerine bind ediliyor:
public async Task Stream(
Guid sessionId,
string question,
double temperature = 0.7,
double topP = 0.9,
int topK = 40,
int maxTokens = 512,
CancellationToken cancellationToken = default)
Ardından tek bir ayar nesnesi oluşturup servise iletiyoruz:
var options = new ChatGenerationOptions(
temperature, topP, topK, maxTokens);
await foreach (var chunk in _chatService.StreamAsync(
sessionId, question, options, cancellationToken))
{
await Response.WriteAsync(chunk, cancellationToken);
await Response.Body.FlushAsync(cancellationToken);
}
Razor ekranında her ayar için range input kullanıyoruz:
<label>Temperature <output id="temperature-value">0.7</output>
<input id="temperature" name="temperature"
type="range" min="0" max="1.5" step="0.1" value="0.7" />
</label>
name değeri controller parametresiyle aynı olduğu için FormData bu değeri
otomatik olarak POST gövdesine ekler.
Kullanıcıya üç hazır profil de sunuyoruz:
const profiles = {
balanced: { temperature: "0.7", topP: "0.9", topK: "40", maxTokens: "512" },
creative: { temperature: "1.2", topP: "0.95", topK: "80", maxTokens: "768" },
focused: { temperature: "0.2", topP: "0.6", topK: "20", maxTokens: "384" }
};
Hazır profiller bilimsel olarak “doğru” ayarlar değildir. Öğretici kullanım için başlangıç noktalarıdır. Kullanıcı daha sonra slider'ları tek tek değiştirerek kendi deneyini yapabilir.
Ollama Modellerini Listelemek
Sabit bir model adını arayüzde yazmak yerine Ollama'nın model listesini
/api/tags endpoint'inden alıyoruz. Bu endpoint, makinenin Ollama üzerinde
yüklü modellerini döndürür.
public async Task<IReadOnlyList<string>> GetModelsAsync(
CancellationToken cancellationToken = default)
{
var result = await _httpClient.GetFromJsonAsync<OllamaTagsResponse>(
"/api/tags", cancellationToken);
return result?.Models
.Select(model => model.Name)
.Where(name => !string.IsNullOrWhiteSpace(name))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(name => name)
.ToArray() ?? [];
}
Controller bu listeyi ViewModel'e koyar. Razor tarafında kullanıcıya bir
select kontrolü sunulur:
<select id="model" name="model" form="chat-form">
@foreach (var availableModel in Model.Models)
{
<option value="@availableModel">@availableModel</option>
}
</select>
Model adı sadece UI'dan alınarak kullanılmaz. Sunucu, seçilen modelin Ollama listesinde gerçekten bulunduğunu tekrar kontrol eder:
var model = await _modelCatalog.ResolveModelAsync(
options?.Model, cancellationToken);
Bu kontrol yanlış veya yüklenmemiş bir model adıyla çağrı yapılmasını önler.
Seçilen model daha sonra dinamik Ollama chat completion service'e aktarılır:
return new OllamaChatCompletionService(
model, new Uri(endpoint), _loggerFactory);
Bu nedenle model seçimi yalnızca ekranda değişen bir etiket değildir; gerçek model çağrısı seçilen model ile yapılır.
Bu örnekte model seçimi Local Ollama provider'ına aittir. OpenAI veya başka bir provider için aynı UI doğrudan kullanılamaz; o provider'ın model listeleme API'si ayrıca uygulanmalıdır.
Cevap Beklerken Animasyon Göstermek
Streaming cevabının ilk parçası gelene kadar kullanıcıya isteğin işlendiğini göstermek için üç noktalı bir animasyon ekliyoruz:
<div class="typing-indicator" aria-label="Cevap hazırlanıyor">
<i></i><i></i><i></i>
</div>
CSS ile noktaların sırayla hareket etmesini sağlıyoruz:
.typing-indicator i {
animation: typing 1s infinite ease-in-out;
}
İlk streaming parçası geldiğinde animasyonu gizliyoruz:
if (!done) {
typingIndicator.hidden = true;
answerElement.textContent += decoder.decode(value, { stream: true });
}
İstek hata verirse animasyon yine kapatılır ve kullanıcıya bağlantı mesajı gösterilir. Böylece butonun cevap gelene kadar kilitli olmasıyla birlikte kullanıcı hangi aşamada olduğumuzu anlayabilir.
Cevapta Kullanılan Ayarları Göstermek
Streaming cevabı oluştururken arayüzde küçük bir bilgi satırı ekliyoruz:
settings.textContent = `Ayarlar: sıcaklık ${temperature.value}, ` +
`Top P ${topP.value}, Top K ${topK.value}, ` +
`maksimum ${maxTokens.value} token`;
Bu bilgi veritabanına assistant mesajının parçası olarak kaydedilmiyor. Sadece o anki deneyin okunabilir olması için arayüzde gösteriliyor.
Aynı Soruyu Tekrar Denemek
Playground'un en faydalı kullanım şekli aynı soruyu değiştirmeden tekrar göndermektir:
Soru: En ucuz ürününüzü kısa ve ikna edici biçimde tanıt.
1. Odaklı profil
2. Yaratıcı profil
3. Temperature 0.7, Top P 0.9, Top K 40
Ürün fiyatı ve stok gibi veritabanından gelen bilgilerde yalnızca yaratıcılığı artırmak her zaman istenen bir şey değildir. Bu tür sorular için düşük temperature ve kontrollü bir profil daha uygun olabilir.
Tanıtım metni gibi yaratıcı görevlerde ise daha yüksek temperature farklı üsluplar görmemizi sağlayabilir.
Tool Calling ile Deney Yaparken Dikkat
Bu uygulamada model iki farklı şey yapabilir:
- Doğrudan doğal dil cevabı üretebilir.
- Ürün bilgisi gerekiyorsa
ProductsPluginfonksiyonunu çağırabilir.
Temperature gibi ayarlar özellikle doğal dil üretiminde daha görünür hale gelir. Ancak ürün fiyatı veya stok sonucu bir plugin'den geldiğinde modelin bu gerçek veriyi değiştirmemesi gerekir.
Playground ayarları bir model deneyi sunar; veritabanından gelen gerçek değerleri rastgeleleştirmez.
Thinking ve Model-Özel Ayarlar İçin Gelecek Adım
Thinking desteği eklemek istersek önce modelin ve Ollama API'sinin desteklediği alanı doğrulamak gerekir. İleride ayarları provider'a göre ayırabiliriz:
Ortak ayarlar : temperature, top_p, max_tokens
Ollama ayarları : top_k, model-specific options
OpenAI ayarları : provider-specific options
Thinking ayarları : yalnızca destekleyen modeller
Bu ayrım, çoklu provider desteğini korumak açısından önemlidir.
Test Senaryoları
- Dengeli profille bir ürün sorusu sormak.
- Aynı soruyu yaratıcı profille tekrar sormak.
- Aynı soruyu odaklı profille tekrar sormak.
- Cevap balonunda kullanılan ayarların görünmesini kontrol etmek.
- Uzun cevapta streaming aktarımını gözlemlemek.
- Maksimum token değerini düşürerek cevabın kısaldığını görmek.
- Sayfa yenilendiğinde tamamlanmış cevabın geçmişte tek mesaj olarak durduğunu doğrulamak.
Derleme kontrolü:
dotnet build MSSemantic.slnx --no-restore
Bu komut C# ve Razor kodunun derlendiğini gösterir. Model parametrelerinin gerçek çıktıya etkisi için Ollama, PostgreSQL ve tarayıcı üzerinden ayrıca canlı test yapılması gerekir.
Sonuç
Bu bölümde MVC chat ekranını etkileşimli bir LLM Playground'a dönüştürdük:
- UI ayarları controller üzerinden servise taşındı.
ChatGenerationOptionsdeğerleri güvenli aralıklara çekti.- Ollama connector'ına
Temperature,TopP,TopKveNumPredictgönderildi. - Streaming cevap akışı korundu.
- Hazır profillerle aynı soru farklı davranışlarla denenebilir hale geldi.
- Kullanılan ayarlar cevap balonunda görünür oldu.
- Veritabanı şeması ve migration yapısı değiştirilmedi.
Artık uygulama yalnızca çalışan bir chat örneği değil; Semantic Kernel ile model parametrelerini gözlemlemek için kullanılabilecek küçük bir eğitim laboratuvarıdır.
Dejar un Comentario
Tu comentario se publicara tras la aprobacion del administrador.