Foundry Local ve C# ile Canlı Konuşma-Metin Dönüşümü
Yerel yapay zeka denilince akla önce sohbet uygulamaları gelir. Oysa Foundry Local yalnızca küçük dil modellerini çalıştırmak için değil, konuşma tanıma gibi özelleşmiş iş yükleri için de kullanılabilen bir yerel çalışma zamanı. Bruno Capuano imzalı.NET Blog yazısı bu senaryoyu somutlaştıran küçük bir C# konsol uygulamasını adım adım anlatıyor: mikrofondan gelen sesi gerçek zamanlı olarak, tamamen cihazda transkribe eden bir uygulama.
Bu yazıda söz konusu örneğin mimarisini, kullandığı API’leri ve yerel konuşma-metin dönüşümünü.NET tarafında pratik kılan noktaları özetliyorum.
Uygulamanın genel akışı
Örnek uygulama.NET 10 hedefliyor ve Windows’a özgü bir senaryo üzerine kurulu. Microsoft.AI.Foundry.Local.WinML, Windows ML üzerinde çalışıyor; ses yakalama tarafında ise NAudio.WaveInEvent Windows ses API’lerini kullanıyor. Yani örnek yalnızca Windows’ta koşuyor.
Uygulamanın işleyişi sade:
- Foundry Local başlatılır ve katalogdan bir konuşma modeli çözümlenir.
- Model indirilir ve belleğe yüklenir.
- Canlı bir transkripsiyon oturumu oluşturulur.
- Mikrofon sesi 16 kHz, 16 bit, tek kanal PCM formatında yakalanır.
- Ses akışı modele beslenirken ara ve nihai sonuçlar ekrana yazdırılır.
Kullanılan model, Foundry Local kataloğundaki nemotron-speech-streaming-en-0.6b; İngilizce akış tabanlı bir ASR modeli.
Soyutlama nerede biter, sağlayıcıya özel API nerede başlar?
Yazının en dikkat çekici mimari mesajı burada. Sohbet senaryolarında Microsoft.Extensions.AI paketindeki IChatClient soyutlaması, farklı sağlayıcılar arasında geçiş yapabilen taşınabilir kod yazmayı mümkün kılıyor. Ancak canlı ses akışı için kullanılan AudioClient, Microsoft.Extensions.AI soyutlamasının bir parçası değil; doğrudan yerel Microsoft.AI.Foundry.Local SDK’sına ait.
Sebebi net: canlı transkripsiyon oturumları, ham PCM akışı ve ara sonuçlar Foundry Local’a özgü yetenekler. Yazının önerdiği pratik şu: soyutlama senaryoya uyduğunda Microsoft.Extensions.AI kullanın, özelleşmiş bir sağlayıcı yeteneğine ihtiyaç duyduğunuzda sağlayıcı SDK’sına doğrudan başvurun. İki yaklaşım birbirini tamamlıyor.
Modeli Foundry Local’a yönetteyin
Örneğin en rahatlatıcı yani, model yaşam döngüsünün büyük ölçüde Foundry Local tarafından üstlenilmesi. Uygulama modeli takma adıyla (alias) istiyor; uygun varyantı seçmek, donanıma göre execution provider’ları indirmek, dosyaları önbelleğe almak ve çıkarım için yüklemek Foundry Local’ın işi.
using Microsoft.AI.Foundry.Local;
using Microsoft.Extensions.Logging.Abstractions;
using NAudio.Wave;
using System.Threading.Channels;
var config = new Configuration
{
AppName = "dotnet-local-ai-live-transcription",
LogLevel = LogLevel.Information
};
await FoundryLocalManager.CreateAsync(config, NullLogger.Instance);
using var manager = FoundryLocalManager.Instance;
await manager.DownloadAndRegisterEpsAsync();
var catalog = await manager.GetCatalogAsync();
var model = await catalog.GetModelAsync(
"nemotron-speech-streaming-en-0.6b")
? throw new InvalidOperationException("Speech model not found.");
await model.DownloadAsync(progress =>
Console.Write($"\rDownloading model: {progress:F2}%"));
await model.LoadAsync();
İlk çalıştırmada model ve gerekli execution provider’lar indiriliyor. Sonraki çalıştırmalarda önbellekten yüklendiği için ek indirmeye gerek kalmıyor. Yazıda vurgulanan bir nokta da şu: modeli Hugging Face gibi kaynaklardan ayrı ayrı indirmek yerine katalog üzerinden model.DownloadAsync() ile almak öneriliyor, çünkü katalog metadata’sı ve dosya yerleşimi Foundry Local’a ait.
Canlı transkripsiyon oturumu oluşturmak
Model yüklendikten sonra AudioClient üzerinden akış oturumu açılıyor:
var audioClient = await model.GetAudioClientAsync();
using var session = audioClient.CreateLiveTranscriptionSession();
session.Settings.SampleRate = 16000;
session.Settings.Channels = 1;
session.Settings.Language = "en";
await session.StartAsync();
Bu, tamamlanmış bir ses dosyasını toplu şekilde transkribe eden bir API değil. Oturum açık kalıyor ve kullanıcı konuştukça ham PCM verisi kabul ediyor.
Sesin akıtılması: backpressure neden önemli?
NAudio ile mikrofon yakalama işlemi senkron bir geri çağırma modeliyle çalışıyor; buna karşın session.AppendAsync() asenkron. İki modelin uyumlu şekilde birlikte çalışabilmesi için örnek, sınırlı kapasiteli bir Channel<byte[]> kullanıyor. Kanal dolduğunda en eski parçalar düşürülüyor; böylece sınırsız sayıda “fire-and-forget” işinin oluşmasının önüne geçiliyor.
var audioChannel = Channel.CreateBounded<byte[]>(new BoundedChannelOptions(50)
{
FullMode = BoundedChannelFullMode.DropOldest
});
var appendTask = Task.Run(async () =>
{
await foreach (var chunk in audioChannel.Reader.ReadAllAsync())
{
await session.AppendAsync(chunk);
}
});
Ara ve nihai sonuçları okumak
Transkripsiyon sonuçları asenkron bir akıştan geliyor. Yazının vurguladığı önemli detay: bu akışı, AppendAsync()‘i çağıran görevden farklı bir görevden okumak gerekiyor. Yoksa sonuçları okumak ses yakalamayı bloklayabilir.
await foreach (var result in session.GetStream())
{
var text = result.Content?[0]?.Text;
if (result.IsFinal)
{
Console.WriteLine();
Console.WriteLine($"[FINAL] {text}");
}
else if (!string.IsNullOrEmpty(text))
{
Console.Write(text);
}
}
Ara sonuçlar kullanıcı konuşurken anında görüntülenebiliyor. Model bir söylemi tamamladığında ise nihai sonucu yayınlıyor. Bu yapı canlı altyazı, toplantı notu, sesle kontrol edilen masaüstü uygulamaları veya sınırlı bağlantıya sahip uç senaryolar için doğrudan uygun.
Kaynak temizliği ve yaşam döngüsü
Örnekte kaynak temizliği bir istisna durumunda da çalışacak biçimde koruma altına alınmış. Sadeleştirilmiş yaşam döngüsü şöyle:
await model.LoadAsync();
try
{
using var session = audioClient.CreateLiveTranscriptionSession();
await session.StartAsync();
try
{
using var waveIn = new WaveInEvent();
// Mikrofon sesini yakala ve akıt.
}
finally
{
await session.StopAsync();
}
}
finally
{
await model.UnloadAsync();
}
Uygulama düzgün kapanış için Enter tuşunu kullanıyor. Ctrl+C desteği isteyen bir komut satırı uygulamasında Console.CancelKeyPress yakalanmalı, aktif iş iptal edilmeli ve aynı finally bloğunun tamamlanması sağlanmalı. Örnek ayrıca RemoveFromCacheAsync() ile indirilen modelin önbellekten açıkça kaldırılabildiğini de gösteriyor; normal senaryoda modeli önbellekte tutmak yeniden indirmeyi önlüyor.
Yerelde konuşma-metin: ne zaman anlamlı?
Yazının altını çizdiği pratik kazanımlar şunlar:
- Mikrofon sesi cihazda kalıyor.
- Bulut AI kaynağı veya API anahtarı gerekmiyor.
- İlk model indirmesinden sonra çıkarım için ağ turuna ihtiyaç yok.
- Akış modeli, Foundry Local’ın seçtiği CPU varyantı üzerinden çalışabiliyor.
Bunlar olurken uygulama tarafı hala tanıdık C# desenlerinden yararlanıyor: async/await, asenkron akışlar, kanallar ve çeşitli SDK istemcileri. Yerel çıkarım her senaryonun cevabı değil; büyük modeller, merkezi yönetim ve elastik ölçek gibi ihtiyaçlar için bulut hala önemli. Ama gizlilik hassasiyeti yüksek ses verisi, çevrimdışı deneyimler ve cihaz üzerinde çalışması gereken uygulamalar için yerel seçenek somut bir değer sunuyor.
Kaynaklar ve İleri Okuma
- Orijinal yazı: Beyond Chat: live Speech-to-Text with Foundry Local and C# (Bruno Capuano,.NET Blog)
- Tam çalışan C# örneği (GitHub)
- Foundry Local ile canlı ses transkripsiyonu (Microsoft Learn)
- Foundry Local dokümantasyonu
- Önceki yazı: GPT-OSS, C# ve Ollama
- .NET & AI Community Standup
- Generative AI for Beginners.NET
- Foundry Local 1.1: Mikrofondan Canlı Transkripsiyon Geldi
- Foundry Local GA Oldu: Bulut Olmadan Yerel AI
- Foundry Local ile Uçta Yapay Zekâ: Bulut Dışı Hızın Gerçek Yüzü







Yorum gönder