OpenAI .NET API Kullanımı: Yapay Zeka Gücünü .NET Projelerinize Taşıyın

Kardel Rüveyda ÇETİN
16 min readJun 21, 2024

--

Yapay zeka dünyasında yeni bir çağın kapılarını aralayan OpenAI, geliştirdiği API’larla programcılara benzersiz bir yaratıcılık ve akıl imkanı sunuyor. Bu devrimsel teknolojinin kapılarını .NET geliştiricileri için açan resmi OpenAI .NET kütüphanesi, yapay zeka gücünü .NET projelerine taşımanın en güvenilir yolunu sunuyor. Bu kütüphane, OpenAI’nın sunduğu geniş yapay zeka yeteneklerini, .NET ortamındaki projelerde kolayca kullanılabilir hale getiriyor.

Makale kapsamında, OpenAI API için resmi .NET kütüphanesinin temel özelliklerini ve kullanımını göstereceğiz. Umarım okuyan herkes için faydalı bir kaynak olur. :)

Konu Başlıkları

  • Birtakım Prosedürler
  • Yeni Bir .NET Console Projesi Oluşturma İşlemleri
  • Open AI Paketini Projeye Dahil Edelim
  • OpenAI .NET Client Kütüphanesini Kullanarak Sohbet Tamamlamaları
  • OpenAIClient sınıfını kullanma
  • Sohbet Tamamlama İşlemlerinde Streaming
  • Sohbet Tanımlamalarında Fonksiyon ve Tools Kullanımı
  • OpenAI ile Nasıl Görseller Oluşturulur?
  • Sohbet Asistanlarını RAG (Bilgi ile Zenginleştirilmiş Üretim) ile Kullanma
  • Kaynakça

Birtakım Prosedürler

OpenAI REST API’yi çağırmak için bir API anahtarına ihtiyacınız olacak. Bir API anahtarı edinmek için aşağıdaki adımları izleyebilirsiniz.

1- Yeni Bir OpenAI Hesabı Oluşturun veya Giriş Yapın: İlk olarak, OpenAI’nin resmi web sitesine gidin. Eğer zaten bir hesabınız yoksa, yeni bir hesap oluşturun. Hesabınız varsa giriş yapın.

2- API Anahtarı Sayfasına Gidin: Hesabınıza giriş yaptıktan sonra, OpenAI kontrol paneline (dashboard) gidin. Burada, genellikle kullanıcı profilinizin altında veya menüde “API Keys” veya benzeri bir seçenek bulabilirsiniz.

3- Yeni Bir Gizli Anahtar Oluşturun: API anahtarları sayfasında, “Create new secret key” butonuna tıklayın. Bu işlem, yeni bir API anahtarı oluşturacaktır. Bu anahtara bir ad verebilirsiniz, böylece hangi anahtarın hangi projeye ait olduğunu kolayca takip edebilirsiniz.

4- API Anahtarınızı Güvenli Bir Yerde Saklayın: API anahtarınız oluşturulduktan sonra, bu anahtarı güvenli bir yerde sakladığınızdan emin olun. Anahtarınızı kimseyle paylaşmayın ve herkese açık ortamlarda (örneğin, açık kaynak kod depolarında) yayınlamaktan kaçının. API anahtarınız, sizin adınıza API çağrıları yapmaya yetkilidir, bu yüzden gizli tutulması önemlidir. ( Github’a pushlarken aman dikkat, bunlarla ilgili yazı serim içerisinde şu makaleye bakmanızı öneririm.)

Yeni Bir .NET Console Projesi Oluşturma İşlemleri ( Biliyorsunuz zaten ama gene de geçmek istemedim. :) )

dotnet new console -n MyOpenAIProject

Bu komut, “MyOpenAIProject” adında bir yeni proje klasörü oluşturur ve içinde bir konsol uygulaması başlatır.

Open AI Paketini Projeye Dahil Edelim

Projeye OpenAI paketini eklemek için aşağıdaki komutu çalıştırmak gerekiyor.

dotnet add package OpenAI --prerelease

Makalede yer alan kod örnekleri, en yeni .NET sürümü olan .NET 8 kullanılarak yazılmıştır. Ancak OpenAI .NET kütüphanesinin .NET Standard 2.0 uyumlu tüm uygulamalarla çalışmaktadır. Makaledeki bazı kod örnekleri, C# dilinin yeni sürümlerinde bulunan özellikleri kullanabilir. Örneğin, C# 10 veya C# 11 gibi sürümlerden gelen yeni sözdizimi veya dil özellikleri. Bu özellikler, daha eski C# sürümlerinde mevcut olmayabilir. Bu nedenle, okuyucuların kodu kendi projelerinde kullanmadan önce hangi dil sürümünü kullandıklarını ve bu özelliklerin mevcut olup olmadığını kontrol etmeleri önemlidir.Eğer daha eski bir .NET sürümünü veya daha eski bir C# sürümünü kullanıyorsanız, yeni dil özelliklerini kullanmaktan kaçınarak ve kodu daha uyumlu hale getirerek bu örnekleri kendi projenizde çalıştırabilirsiniz.

OpenAI .NET Client Kütüphanesini Kullanarak Sohbet Tamamlamaları

using System;
using System.Threading.Tasks;
using OpenAI.Chat;

namespace MyOpenAIProject.Examples
{
public class ChatExample
{
public static async Task RunAsync()
{
// API anahtarını ConfigReader sınıfından alma işlemi
string apiKey = ConfigReader.ReadApiKeyFromConfig();

// API anahtarı alınamazsa işlemi sonlandır
if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("API key not found in config.json");
return;
}

// OpenAI ChatClient oluşturun
ChatClient client = new(model: "gpt-4", apiKey);

// Sohbet tamamlama işlemini gerçekleştirin
ChatCompletion completion = await client.CompleteChatAsync("Say 'this is a test.'");

// Sonucu ekrana yazdırın
Console.WriteLine($"[ASSISTANT]: {completion}");
}
}
}

Sohbet tamamlama, bir yapay zeka modelinin belirli bir metin girdisine dayanarak bir metin çıktısı oluşturması işlemidir. Genellikle doğal dil işleme alanında kullanılan bir tekniktir. Örneğin, bir sohbet botuyla etkileşimde bulunurken, siz bir mesaj yazdığınızda, botun sizin mesajınızı anlaması ve uygun bir yanıt üretmesi için sohbet tamamlama işlemi gerçekleştirilir. Bu işlem, botun yapay zeka modeli tarafından gerçekleştirilir ve genellikle önceki metin verileri üzerinde eğitilerek geliştirilir. ChatClient üzerinde CompleteChatAsync metodunu çağırarak bir sohbet tamamlama işlemi gerçekleştirilebilir.

Bu metod, belirtilen metni içeren bir sohbet başlatır ve bu metni tamamlayarak bir yanıt üretir. await anahtar kelimesi, bu metodun asenkron olarak çalıştığını ve tamamlanması için beklenmesi gerektiğini belirtir. Yani, metot tamamlanana kadar bekler ve sonra işlem devam eder.

// OpenAI ChatClient oluşturun
ChatClient client = new(model: "gpt-4", apiKey);

// Sohbet tamamlama işlemini gerçekleştirin
ChatCompletion completion = await client.CompleteChatAsync("Say 'this is a test.'");
        public static void Run()
{
// API anahtarını ConfigReader sınıfından alma işlemi
string apiKey = ConfigReader.ReadApiKeyFromConfig();

// API anahtarı alınamazsa işlemi sonlandır
if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("API key not found in config.json");
return;
}

// OpenAI ChatClient oluşturun
ChatClient client = new(model: "gpt-4", apiKey);

// Sohbet tamamlama işlemini gerçekleştirin
ChatCompletion completion = client.CompleteChat("Say 'this is a test.'");

// Sonucu ekrana yazdırın
Console.WriteLine($"[ASSISTANT]: {completion}");
Console.ReadLine();
}

Async ve async olmayan (senkron) metotlar arasındaki temel fark, async metotların işlemleri arka planda (asenkron olarak) gerçekleştirebilmesidir. Bu sayede, bir metot başka bir işlemi beklerken diğer işlemler devam edebilir ve uygulama daha duyarlı hale gelir. Senkron metotlar ise işlemleri sırayla ve aynı thread üzerinde gerçekleştirir, bu nedenle bir işlem bitmeden diğerine geçemezler. Async metotlar, genellikle ağ erişimi, disk okuma/yazma gibi gecikmeli işlemler veya UI gibi kullanıcı etkileşimli işlemler için tercih edilir. Bu durumlarda, işlem tamamlanana kadar beklemek yerine diğer işlemlere izin vermek ve uygulamanın daha hızlı ve daha duyarlı olmasını sağlamak önemlidir. Senkron metotlar ise genellikle basit, hızlı ve aynı anda birden fazla işlemin yapılmasına gerek olmayan durumlarda kullanılır. Örneğin, bir dosyayı okuma işlemi gibi. Async ve await kullanımı, kodun daha okunabilir ve yönetilebilir olmasını sağlar.

Ancak, gereksiz yere async-await kullanımı performansı olumsuz etkileyebilir ve kod karmaşıklığına yol açabilir. Bu nedenle, işlem yapısına göre doğru kullanımın yapılması önemlidir.

ChatCompletion completion = client.CompleteChat("Say 'this is a test.'");
ChatCompletion completion = await client.CompleteChatAsync("Say 'this is a test.'");

Senkron bir metot olan CompleteChat, çağrıldığında işlemi başlatır ve tamamlanana kadar bekler. Bu süre boyunca, diğer işlemler askıya alınır ve program, CompleteChat metodunun tamamlanmasını bekler.

Öte yandan, CompleteChatAsync metodu asenkron bir şekilde çalışır. Bu, metot çağrıldığında işlem başlatılır, ancak tamamlanması için bekleme yapılmaz. Bunun yerine, metot hemen geri döner ve işlem arka planda devam eder. Bu sayede, diğer işlemler programın çalışmasını engellemez ve program daha hızlı ve daha duyarlı hale gelir.

CompleteChatAsync ağ erişimi, disk okuma/yazma gibi gecikmeli işlemler veya kullanıcı etkileşimli işlemler gibi durumlarda tercih edilir. Bu durumlarda, işlemlerin arka planda gerçekleştirilmesi ve diğer işlemlerin devam etmesine izin verilmesi önemlidir.

OpenAIClient sınıfını kullanma

OpenAI.NET kütüphanesini kullanırken, OpenAIClient sınıfını kullanarak farklı özellik alanı istemcileriyle çalışabilirsiniz. Bu sınıf, birden fazla istemci örneğiyle çalışmanız gerektiğinde bazı kolaylıklar sunar.

Öncelikle, OpenAIClient sınıfından bir örnek oluşturarak tüm istemcilerin kimlik doğrulamasında kullanılacak API anahtarını belirtmeniz gerekmektedir. Bu şekilde, oluşturduğunuz tüm istemciler aynı kimlik doğrulama bilgilerini kullanacaktır.

OpenAIClient client = new OpenAIClient(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

Ardından, örneğin bir AudioClient oluşturmak için GetAudioClient metodunu kullanabilirsiniz. Bu metod, oluşturulacak AudioClient'ın kullanacağı OpenAI modelini parametre olarak alır.

AudioClient, OpenAI API'sını kullanarak metin tabanlı içeriği ses dosyasına dönüştürmek için kullanılan bir istemcidir. Özellikle metin tabanlı çıktıların kullanıcılar tarafından sesli olarak duyulmasını sağlamak için tasarlanmıştır. Bu sayede, metin tabanlı içeriğin sesli olarak çalınması veya sesli yanıtlar oluşturulması gibi senaryolarda kullanılabilir.

Örneğin, AudioClient kullanarak bir metni ses dosyasına dönüştürebilir ve bu ses dosyasını çeşitli uygulamalarda kullanabilirsiniz. Sesli kitap uygulamaları, metin tabanlı içerik okuyucuları veya sesli asistanlar gibi uygulamalar bu türden bir işlevselliği kullanabilirler.

AudioClient'ın sunduğu özellikler arasında metni ses dosyasına dönüştürme, ses dosyasının çeşitli formatlarda kaydedilmesi ve ses dosyasının çeşitli ayarlarla oluşturulması gibi işlevler bulunabilir. Bu sayede, kullanıcılar metin tabanlı içerikleri daha erişilebilir ve kullanıcı dostu hale getirebilirler.

AudioClient whisperClient = client.GetAudioClient("tts-1");

OpenAI’nın sağladığı metinden seslendirme (text-to-speech) özelliğini kullanarak bir metni ses dosyasına dönüştürebilme imkanımız bulunmaktadır.

AudioClient client = new AudioClient("tts-1", ConfigReader.ReadApiKeyFromConfig());

string input = "Euro 2024, Avrupa Futbol Şampiyonası'nın 2024 yılında Almanya'da düzenlenecek olan 17. turnuvasıdır. "
+ "Turnuva, 20 Haziran ile 14 Temmuz 2024 tarihleri arasında gerçekleşecektir. Bu turnuva, Almanya'nın ikinci kez ev sahipliği yapacağı "
+ "Avrupa Futbol Şampiyonası olacaktır. İlk kez 1988 yılında ev sahipliği yapan Almanya, 36 yıl aradan sonra yeniden "
+ "bu organizasyona ev sahipliği yapacak.";

BinaryData speech = client.GenerateSpeechFromText(input, GeneratedSpeechVoice.Alloy);

using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.mp3");
speech.ToStream().CopyTo(stream);

Yukarıdaki örnekte ilk olarak, AudioClient sınıfından bir örnek oluşturuluyor. Bu örnek, metinden seslendirme yapmak için gerekli olan API anahtarını ve hedef seslendirme modelini tanımlıyor. API anahtarı, OpenAI API'ına erişim sağlamak için kullanılıyor. Daha sonra, seslendirilmesi istenen metin bir değişkene atanıyor. Bu örnekte, “Euro 2024, Avrupa Futbol Şampiyonası’nın 2024 yılında Almanya’da düzenlenecek olan 17. turnuvasıdır…” gibi bir metin kullanılıyor. Bu metin, Euro 2024 turnuvası ile ilgili bilgiler içeriyor. Sonraki adımda, client.GenerateSpeechFromText metoduyla metin ses dosyasına dönüştürülüyor. Bu metod, metni ses formatına dönüştürmek için OpenAI API'sını kullanır ve dönüştürülmüş ses verisini BinaryData tipinde bir nesne olarak döndürür. Son olarak, elde edilen ses verisi bir dosyaya yazılıyor. File.OpenWrite metoduyla yeni bir MP3 dosyası oluşturuluyor ve ses verisi bu dosyaya yazılıyor. Bu sayede OpenAI’nın metinden seslendirme özelliğini kullanarak belirli bir metni ses dosyasına dönüştürmek için basit bir yöntem sunulur. Bu şekilde, metin tabanlı içerikleri seslendirerek kullanıcıların sesli olarak erişmelerine olanak tanır.

Sohbet Tamamlama İşlemlerinde Streaming

OpenAI’nın sohbet tamamlama (chat completion) hizmeti, kullanıcıların verilen bir metin veya konuşma parçası üzerinden doğal dil işleme (NLP) tekniklerini kullanarak anlamlı ve tutarlı yanıtlar üretmelerini sağlar. Bu hizmet, insanlarla etkileşim halinde olan uygulamalarda, sanal asistanlarda, müşteri hizmetlerinde ve diğer alanlarda kullanılabilir.

Geleneksel olarak, bir sohbet tamamlaması istendiğinde, sunucu tamamlamayı tamamlayıp tüm yanıtı tek bir yanıtta geri gönderir. Bu durumda, özellikle uzun ve karmaşık tamamlamalarda, kullanıcıların yanıtı alması birkaç saniye sürebilir. Bu bekleme süresini azaltmak ve kullanıcı deneyimini iyileştirmek için, OpenAI REST API, tamamlanma süreci devam ederken kısmi sonuçları akış halinde geri gönderebilir. Böylece, tamamlamanın başlangıcını işlemeye başlayabilir ve tamamlanma tamamlandığında daha hızlı yanıt alabilirsiniz.

Örneğin, bir müşteri hizmetleri botu, kullanıcının sorusunu anında yanıtlayabilmesi için akışlı sohbet tamamlamasını kullanabilir. Kullanıcı bir soru sorduğunda, bot bu soruyu OpenAI’ya gönderir ve OpenAI, tamamlama süreci devam ederken botu kısmi yanıtlarla günceller. Böylece, bot, tam yanıtı beklemek yerine kısmi yanıtları kullanarak hızlı bir şekilde yanıt verebilir.Akışlı sohbet tamamlaması, uzun ve karmaşık metinler üzerinde çalışırken özellikle faydalı olabilir. Kullanıcıların uzun metinler yazmasına gerek kalmadan, botları daha verimli bir şekilde yanıt verebilir ve etkileşimleri daha akıcı hale getirebilir. Ayrıca, akışlı tamamlama, gerçek zamanlı etkileşimlerde daha hızlı ve daha dinamik bir deneyim sunabilir.

 // OpenAI ChatClient oluşturun
ChatClient client = new(model: "gpt-4", apiKey);
// Sohbet tamamlama işlemini gerçekleştirin
ResultCollection<StreamingChatCompletionUpdate> updates= client.CompleteChatStreaming("Say 'this is a test.'");
// Sonucu ekrana yazdırın
Console.WriteLine($"[ASSISTANT]:");

foreach (StreamingChatCompletionUpdate update in updates)
{
foreach (ChatMessageContentPart updatePart in update.ContentUpdate)
{
Console.Write(updatePart);
}
}
Console.ReadLine();

Yukarıdaki örnek OpenAI’nın akışlı sohbet tamamlama (streaming chat completion) özelliğini kullanarak bir sohbet asistanı oluşturmayı gösteriyor. Akışlı sohbet tamamlama, tamamlanan metni beklemek yerine metin parçalarını akış halinde almanızı sağlar, böylece kullanıcıya daha hızlı ve daha dinamik bir yanıt verme imkanı sunar. Bu özellik, gerçek zamanlı sohbet uygulamalarında kullanıcı deneyimini geliştirmek için idealdir. Kod örneği, öncelikle API anahtarını alır, ardından ChatClient sınıfını kullanarak bir sohbet başlatır ve akışlı olarak yanıtları alır. Son olarak, gelen yanıtları ekrana yazdırır. Bu sayede, kullanıcıya hızlı ve dinamik bir yanıt sunulurken, tam metni beklemeye gerek kalmaz, böylece daha hızlı bir sohbet deneyimi sağlanır.

           AsyncResultCollection<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreamingAsync("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
await foreach (StreamingChatCompletionUpdate update in updates)
{
foreach (ChatMessageContentPart updatePart in update.ContentUpdate)
{
Console.Write(updatePart.Text);
}
}
Console.ReadLine();

async kullanarak asenkron işlemler, özellikle uzun süren veya dış kaynaklara bağımlı işlemlerde programın daha duyarlı ve verimli olmasını sağlar, bu nedenle CompleteChatStreamingAsync gibi uzun süren işlemlerde tercih edilebilir.

Streaming sayesinde uzun sürecek işlemlerde, sunucudan tam yanıt gelene kadar beklemek yerine, kısmi sonuçları alıp işlemeye olanak tanır. Yani, örneğin bir sohbet botu senaryosunda, kullanıcının girdiği metne yanıt vermek için sunucuya bir istek gönderdiğinizde, sunucu hala yanıtı üretmekteyken size parça parça veri gönderebilir ve siz de bu verileri anlık olarak işleyebilirsiniz. Bu şekilde, tam yanıt gelene kadar beklemek zorunda kalmazsınız ve kullanıcıya daha hızlı geri dönüşler sağlayabilirsiniz.

Sohbet Tanımlamalarında Fonksiyon ve Tools Kullanımı

Örneğin, kullanıcı hava durumu hakkında bir soru sorabilir. Bu durumda, model kullanıcının konumunu almalı ve bu konuma göre hava durumunu sorgulamalıdır. Ancak, modelin bu bilgilere ihtiyacı olduğunu belirlemesi için bir araç çağrısı yapması gerekebilir. Bu nedenle, bu örnekte GetCurrentLocation ve GetCurrentWeather gibi işlevler araçlar olarak tanımlanmış ve modelin ihtiyaç duyduğu bilgileri sağlamak için gerektiğinde çağrılmıştır. Bu sayede, modelin daha karmaşık soruları yanıtlamasına olanak tanınır ve daha zengin bir sohbet deneyimi sağlanır.

 private static string GetCurrentLocation()
{
// Kullanıcının mevcut konumunu almak için burada konum API'sını çağırın.
return "İstanbul";
}

GetCurrentLocation, kullanıcının mevcut konumunu almak için kullanılır. Şu an için sabit bir değer olan “İstanbul” geri döndürür. Gerçek uygulamalarda konum bilgisini kullanıcı cihazının konum hizmeti API’lerinden alabilirsiniz. Bir önceki yazımda OpenAIWeatherMap’i kullanmıştım.

private static string GetCurrentWeather(string location, string unit = "celsius")
{
// Verilen konum için hava durumunu sorgulamak için burada hava durumu API'sını çağırın.
return $"30 {unit}";
}

GetCurrentWeather verilen bir konum için hava durumunu almak için kullanılır. Varsayılan olarak sıcaklık birimi “celsius” olarak ayarlanmıştır.

 private static readonly ChatTool getCurrentLocationTool = ChatTool.CreateFunctionTool(
functionName: nameof(GetCurrentLocation),
functionDescription: "Kullanıcının mevcut konumunu alır"
);

getCurrentLocationTool adında bir ChatTool örneği oluşturuyor. CreateFunctionTool yöntemi kullanılarak bu örnek, GetCurrentLocation adındaki bir işlevi temsil etmek üzere tanımlanıyor. İşlev, kullanıcının mevcut konumunu alır. Bu ChatTool, OpenAI Chat API'si aracılığıyla modelin ihtiyaç duyduğu ek bilgileri almak için kullanılabilir. Örneğin, model bir konum bilgisine ihtiyaç duyarsa, bu araç çağrılabilir ve işlev sonucunda kullanıcının mevcut konumu elde edilebilir. Bu tür araçlar, modelin daha karmaşık soruları yanıtlamasına ve daha zengin bir sohbet deneyimi sağlamasına olanak tanır.

    private static readonly ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
functionName: nameof(GetCurrentWeather),
functionDescription: "Verilen konum için mevcut hava durumunu alır",
functionParameters: BinaryData.FromString(@"
{
""type"": ""object"",
""properties"": {
""location"": {
""type"": ""string"",
""description"": ""Şehir ve eyalet, örn. İstanbul, TR""
},
""unit"": {
""type"": ""string"",
""enum"": [ ""celsius"", ""fahrenheit"" ],
""description"": ""Kullanılacak sıcaklık birimi. Belirtilen konumdan bu çıkarılır.""
}
},
""required"": [ ""location"" ]
}
")
);

GetCurrentWeather adındaki bir işlevi temsil etmek üzere tanımlanıyor. İşlev, verilen bir konum için mevcut hava durumunu alır. functionParameters parametresi, bu işlevin alabileceği parametreleri ve bu parametrelerin türlerini ve açıklamalarını belirtir. Örneğin, location parametresi bir şehir ve eyaleti temsil ederken, unit parametresi kullanılacak sıcaklık birimini belirtir (örneğin, Celsius veya Fahrenheit).

    public static void Run()
{
Console.WriteLine("Sohbet başlatılıyor...");

List<ChatMessage> messages = new()
{
new UserChatMessage("Bugün hava nasıl?")
};

ChatCompletionOptions options = new()
{
Tools = { getCurrentLocationTool, getCurrentWeatherTool }
};

// API anahtarını ConfigReader sınıfından alma işlemi
Console.WriteLine("API anahtarı okunuyor...");
string apiKey = ConfigReader.ReadApiKeyFromConfig();

// API anahtarı alınamazsa işlemi sonlandır
if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("Hata: API anahtarı bulunamadı.");
return;
}

// OpenAI ChatClient oluşturun
Console.WriteLine("ChatClient oluşturuluyor...");
ChatClient client = new(model: "gpt-4", apiKey);

Console.WriteLine("Chat tamamlama başlatılıyor...");

ChatCompletion chatCompletion = client.CompleteChat(messages, options);

switch (chatCompletion.FinishReason)
{
case ChatFinishReason.Stop:
{
Console.WriteLine("Sohbet tamamlandı. Sonuçlar:");
messages.Add(new AssistantChatMessage(chatCompletion));
break;
}

case ChatFinishReason.ToolCalls:
{
Console.WriteLine("Tool çağrıları yapılıyor...");
messages.Add(new AssistantChatMessage(chatCompletion));

foreach (ChatToolCall toolCall in chatCompletion.ToolCalls)
{
switch (toolCall.FunctionName)
{
case nameof(GetCurrentLocation):
{
Console.WriteLine("Konum alınıyor...");
string toolResult = GetCurrentLocation();
messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
break;
}

case nameof(GetCurrentWeather):
{
Console.WriteLine("Hava durumu sorgulanıyor...");
using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments);
bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);

if (!hasLocation)
{
throw new ArgumentNullException(nameof(location), "Konum argümanı gereklidir.");
}

string toolResult = hasUnit
? GetCurrentWeather(location.GetString(), unit.GetString())
: GetCurrentWeather(location.GetString());
messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
break;
}

default:
{
throw new NotImplementedException();
}
}
}
break;
}

case ChatFinishReason.Length:
throw new NotImplementedException("MaxTokens parametresi veya token limiti nedeniyle tamamlanmayan model çıktısı.");

case ChatFinishReason.ContentFilter:
throw new NotImplementedException("İçerik filtresi bayrağı nedeniyle atlanan içerik.");

case ChatFinishReason.FunctionCall:
throw new NotImplementedException("Tool çağrılarına göre kullanımdan kaldırıldı.");

default:
throw new NotImplementedException(chatCompletion.FinishReason.ToString());
}

Console.WriteLine("Sohbet tamamlandı. Sonuçlar konsola yazdırılıyor...");
foreach (ChatMessage message in messages)
{
if (message is UserChatMessage userMessage)
{
Console.WriteLine($"[USER]: {userMessage.Content[0]}");
}
else if (message is ToolChatMessage toolMessage)
{
Console.WriteLine($"[TOOL]: {toolMessage.Content[0]}");
}
else
{
Console.WriteLine("[UNKNOWN MESSAGE]");
}
}

}
  • Bu metod, asıl işlemlerin gerçekleştiği ana metodudur. İlk olarak, sohbet için mesajlar içeren bir liste oluşturulur. Daha sonra, ChatCompletionOptions sınıfı kullanılarak getCurrentLocationTool ve getCurrentWeatherTool araçları belirlenir.
  • client.CompleteChat(messages, options) metoduyla chat tamamlama işlemi gerçekleştirilir. Bu işlem sonucunda bir ChatCompletion nesnesi elde edilir.
  • Switch-case yapısıyla ChatCompletion’ın tamamlanma nedenine göre işlem yapılır. Örneğin, eğer tamamlanma nedeni ChatFinishReason.Stop ise, asistanın yanıtını sohbet geçmişine eklemek için messages listesine yeni bir AssistantChatMessage eklenir. Eğer tamamlanma nedeni ChatFinishReason.ToolCalls ise, modelin bir veya daha fazla aracı çağırması gerektiği anlaşılır ve bu araçlar sırayla çağrılır.
  • Diğer durumlar için (örneğin, uzunluk sınırı aşıldığında veya içerik filtresi uygulandığında) NotImplementedException fırlatılır, çünkü bu örnek uygulama bu durumları ele almamaktadır.

OpenAI ile Nasıl Görseller Oluşturulur?

OpenAI’nin görsel oluşturma yetenekleri sayesinde tasarımcılar artık fikirleri kolayca prototipleyebilirler. Bu makale, iç mekan tasarım projeleri için ilham verici görseller oluşturmanın basit ancak güçlü bir yolunu açıklar.

Başlamak için OpenAI.Images ad alanını içe aktarın ve istediğiniz modeli ve API anahtarınızı belirterek bir ImageClient örneği oluşturdum ve dall-e-3 modelini kullandım.

ImageClient client = new(model: "dall-e-3", apiKey);

Oluşturmak isteğiniz görseli doğru bir şekilde canlandırabilecek bir prompt oluşturun. Bu prompt, oluşturulacak görselin temelini oluşturur. En iyi sonuçlar için tasarım konseptinizin özünü yakalayan özel ayrıntılar ekleyin. Malum bu aralar Euro 2024 heyecanı var. (Türkiyemize başarılar❤) :) Ben promptumu ona göre hazırladım.

    
string prompt = "2024 yılında Almanya'da düzenlenecek olan Euro 2024 için Türkiye millî futbol takımını temsil eden etkileyici bir tanıtım görseli oluşturun. Türkiye'nin futbol tutkusunu, millî takımın coşkusunu ve zafer için verdiği mücadeleyi yansıtan bir görüntü hayal edin. Tribünlerde coşkuyla destek veren taraftarlar, sahadaki mücadele dolu anlar ve millî takımın başarısını simgeleyen sembollerle dolu bir kompozisyon oluşturun. Görüntü, güçlü bir duygu uyandırmalı ve Türkiye'nin Euro 2024'teki başarısına olan inancı ve heyecanı yansıtmalı.";

Görsel oluşturma sürecini ihtiyaçlarınıza göre ayarlayın. ImageGenerationOptions sınıfının bir örneğini oluşturun ve Quality, Size ve Style özelliklerini istediğiniz gibi ayarlayın. İsteğe bağlı olarak, ResponseFormat özelliğini GeneratedImageFormat.Bytes olarak ayarlayarak görüntüyü ikili veri olarak alabilirsiniz.

            ImageGenerationOptions options = new()
{
Quality = GeneratedImageQuality.High,
Size = GeneratedImageSize.W1792xH1024,
Style = GeneratedImageStyle.Vivid,
ResponseFormat = GeneratedImageFormat.Bytes
};

ImageClient'ın GenerateImage yöntemini çağırarak görseli oluşturun ve uygulamanın oluşturduğu görsel ile tasarım konseptinizi yansıtan etkileyici bir görsel elde edebilirsiniz.

GeneratedImage image = client.GenerateImage(prompt, options);
BinaryData bytes = image.ImageBytes;

Oluşturulan görseli yerel depolamaya kaydederek tasarım konseptinizin özünü koruyun. Vee taa daaa “Ölürüm Türkiye’m ☪” :)

Sohbet Asistanlarını RAG (Bilgi ile Zenginleştirilmiş Üretim) ile Kullanma

Bu örnekte, Euro 2024'e katılan takımların maç sonuçlarını içeren bir JSON belgeniz var ve bu belgeyi analiz edebilecek ve soruları yanıtlayabilecek bir yardımcı oluşturmak ,istiyorum. Bakalım bunun için neler yapabiliriz :)

Konfigürasyon işlemleri

  static OpenAIClient CreateOpenAIClient()
{
// API anahtarını ConfigReader sınıfından alma işlemi
string apiKey = ConfigReader.ReadApiKeyFromConfig();
return new OpenAIClient(apiKey);
}

API anahtarını alır ve bu anahtarı kullanarak OpenAIClient nesnesi oluşturur. Bu şekilde, OpenAI hizmetlerine bağlanmak ve bu hizmetlerden yararlanmak için gereken istemciyi hazırlar.

static AssistantClient GetAssistantClient(OpenAIClient openAIClient)
{
return openAIClient.GetAssistantClient();
}

Yukarıda verilen kodda OpenAIClient nesnesini kullanarak bir AssistantClient nesnesi döndürür. GetAssistantClient metodu, openAIClient üzerinden AssistantClient'i alır ve geri döner. Bu işlem, AssistantClient'in oluşturulmasını ve OpenAI API'sine erişim için kullanılmasını sağlar.

Dosya İşlemlerini Gerçekleştirme

 static FileClient GetFileClient(OpenAIClient openAIClient)
{
return openAIClient.GetFileClient();
}

GetFİleClient, verilen OpenAIClient örneğinden bir FileClient örneği döndürüyor. Yani, OpenAIClient üzerinden dosya işlemleri yapabilmek için bir FileClient örneği alınmasını sağlıyor.

    static Stream LoadDocument()
{
return BinaryData.FromString("""
{
"description": "Bu belge, Euro 2024'e katılan takımların maç sonuçlarını içermektedir.",
"matches": [
{
"date": "2024-06-10",
"team1": "Türkiye",
"team2": "Gürcistan",
"score": "3-1"
},
{
"date": "2024-06-12",
"team1": "Almanya",
"team2": "Fransa",
"score": "1-1"
},
{
"date": "2024-06-15",
"team1": "İspanya",
"team2": "Portekiz",
"score": "3-2"
}
]
}
""").ToStream();
}

LoadDocument metotu ile JSON belgesini içeren bir Stream döndürüyor. Bu belge, Euro 2024'e katılan takımların maç sonuçlarını içeriyor. Bu belgeyi, daha sonra bu maç sonuçlarıyla ilgili sorguları yanıtlamak için kullanacağım.

static OpenAIFileInfo UploadDocument(FileClient fileClient, Stream document)
{
// Belgeyi OpenAI'ya yükleme
return fileClient.UploadFile(document, "euro_2024_matches.json", FileUploadPurpose.Assistants);
}

Yukarıdaki kod, Euro 2024'e katılan takımların maç sonuçlarını içeren bir belgeyi OpenAI’a yüklemek için kullanılıyor. fileClient.UploadFile metoduyla belge yükleniyor ve yükleme amacı olarak FileUploadPurpose.Assistants belirtiliyor, bu da belgenin daha sonra bir asistan tarafından kullanılacağını gösteriyor.

Asistanı Oluşturma

static Assistant CreateAssistant(AssistantClient assistantClient, OpenAIFileInfo file)
{
AssistantCreationOptions assistantOptions = new()
{
Name = "Örnek: Euro 2024 Maç Sonuçları RAG",
Instructions =
"Kullanıcı sorgularına dayalı olarak Euro 2024 maç sonuçlarını arayan bir yardımcısınız. Maç sonucu istendiğinde sana verilen dokümana göre cevap ver.",
Tools = { new FileSearchToolDefinition(), new CodeInterpreterToolDefinition() },
ToolResources = new() { FileSearch = new() { NewVectorStores = { new VectorStoreCreationHelper(new[] { file.Id }) } } }
};
return assistantClient.CreateAssistant("gpt-4o", assistantOptions);
}

AssistantCreationOptions sınıfı, oluşturulacak asistanın özelliklerini ve davranışlarını belirlemek için kullanılır. Bu örnekte, asistanın adı "Örnek: Euro 2024 Maç Sonuçları RAG" olarak belirlenmiştir. Instructions alanı, Yardımcının ne tür sorgulara yanıt vereceğini ve nasıl davranması gerektiğini açıklar. Tools alanı, asistanın kullanacağı araçları belirtir. Bu örnekte, asistan maç sonuçlarını aramak ve yorumlamak için iki araç kullanır: FileSearchToolDefinition ve CodeInterpreterToolDefinition. ToolResources alanı, asistanın kullanacağı kaynakları belirler. Bu örnekte, asistanın maç sonuçlarını içeren belgeye erişim sağlamak için bir FileSearch aracı ve bu belgeyi kullanarak sorulara cevap vermek için bir CodeInterpreter aracı tanımlanmıştır. Son olarak, assistantClient.CreateAssistant metodu kullanılarak asistan oluşturulur ve geri döndürülür. Bu asistan, Euro 2024 maç sonuçları hakkında bilgi vermek üzere tasarlanmıştır ve kullanıcıların sorduğu sorulara belge üzerinden yanıt verir.

Thread Meselesi :)

static ThreadRun CreateAndRunThread(AssistantClient assistantClient, Assistant assistant)
{
ThreadCreationOptions threadOptions = new()
{
InitialMessages = { "Türkiye'nin Euro 2024'teki ilk maçında kimle oynadı ve sonuç ne oldu?" }
};

return assistantClient.CreateThreadAndRun(assistant.Id, threadOptions);
}

Asistan ile iletişim kurmak için bir iş parçacığı oluşturma ve başlatma işlemi aşağıdaki kodla gerçekleşir. ThreadCreationOptions sınıfı, iş parçacığının başlatılması için gereken ayarları belirlemek için kullanılır. Bu örnekte, iş parçacığı başlatılırken asistana iletilmek üzere bir başlangıç mesajı belirlenmiştir. Ardından, assistantClient.CreateThreadAndRun metodu kullanılarak iş parçacığı oluşturulur ve çalıştırılır. Oluşturulan iş parçacığı, asistan ile belirtilen başlangıç mesajı üzerinden iletişime geçer ve asistanın yanıtını alır.

static ThreadRun PollThreadRunStatus(AssistantClient assistantClient, ThreadRun threadRun)
{
do
{
Thread.Sleep(TimeSpan.FromSeconds(1));
threadRun = assistantClient.GetRun(threadRun.ThreadId, threadRun.Id);
} while (!threadRun.Status.IsTerminal);

return threadRun;
}

Bu kodu bir iş parçacığının durumunu izlemek için kullandım. do-while döngüsü kullanarak iş parçacığının durumu IsTerminal özelliği ile kontrol edilir. IsTerminal, iş parçacığının sonlandırılıp sonlandırılmadığını belirten bir değerdir. İş parçacığı hala çalışıyorsa, metod bir saniye uyur ve daha sonra iş parçacığının güncel durumunu almak için assistantClient.GetRun metoduyla iş parçacığı yeniden alınır. Bu işlem, iş parçacığının sonlanıncaya kadar devam eder. Son olarak, iş parçacığı sonlandığında threadRun nesnesi döndürülür.

Sonuçların Konsola Yazdırılması

static void PrintThreadMessages(AssistantClient assistantClient, FileClient fileClient, ThreadRun threadRun)
{
PageableCollection<ThreadMessage> messages = assistantClient.GetMessages(threadRun.ThreadId, ListOrder.OldestFirst);

foreach (ThreadMessage message in messages)
{
Console.Write($"[{message.Role.ToString().ToUpper()}]: ");
foreach (MessageContent contentItem in message.Content)
{
if (!string.IsNullOrEmpty(contentItem.Text))
{
Console.WriteLine($"{contentItem.Text}");
}
}
Console.WriteLine();
}
}

İş parçacığı kimliği ve mesajların sırasını belirten bir sıralama parametresiyle birlikte assistantClient.GetMessages metodu kullanılarak mesajlar alınır. Daha sonra, her mesaj için mesajın rolünü ve içeriğini konsola yazdırmak için foreach döngüsü kullanılır. Eğer mesajın içeriğinde metin varsa, bu metin konsola yazdırılır. Sonuç olarak, iş parçacığındaki tüm mesajlar konsola yazdırılmış olur.

 public static void Main()
{
OpenAIClient openAIClient = CreateOpenAIClient();
AssistantClient assistantClient = GetAssistantClient(openAIClient);
FileClient fileClient = GetFileClient(openAIClient);

Stream document = LoadDocument();

// JSON belgesini vektör mağazasına yükleme
OpenAIFileInfo salesFile = UploadDocument(fileClient, document);

Assistant assistant = CreateAssistant(assistantClient, salesFile);
ThreadRun threadRun = CreateAndRunThread(assistantClient, assistant);
threadRun = PollThreadRunStatus(assistantClient, threadRun);
PrintThreadMessages(assistantClient, fileClient, threadRun);
}

Bu Main metodunda öncelikle bir OpenAIClient oluşturulur ve bu istemciyi kullanarak bir AssistantClient ve FileClient alınır. Daha sonra LoadDocument metodu kullanılarak bir belge yüklenir ve bu belge UploadDocument metoduyla OpenAI'ya yüklenir. Ardından bir yardımcı oluşturulur ve bir iş parçacığı başlatılır. İş parçacığı tamamlanana kadar işlem devam eder ve son olarak yardımcının verdiği mesajlar PrintThreadMessages metoduyla konsola yazdırılır. Bu sayede, Euro 2024 maç sonuçları hakkında sorular sormak ve yardımcının verdiği yanıtları görmek için bir uygulama geliştirilmiş olur.

Vee konsol çıktısı da aşağıdaki gibidir :)

--

--

Kardel Rüveyda ÇETİN

Expert Software Engineer @DogusTeknoloji | Microsoft MVP | Mathematical Engineer | Speaker | Blogger | Founder&Organizer @thecoderverse