﻿
{"id":27588,"date":"2025-03-01T13:38:29","date_gmt":"2025-03-01T13:38:29","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=27588"},"modified":"2025-03-01T13:38:29","modified_gmt":"2025-03-01T13:38:29","slug":"semantic-kernel-signalr-ile-birlikte-canli-ai-yanitlarini-gosteren-kendi-chat-uygulamamizi-yazalim","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/semantic-kernel-signalr-ile-birlikte-canli-ai-yanitlarini-gosteren-kendi-chat-uygulamamizi-yazalim\/","title":{"rendered":"Semantic Kernel &#038; SignalR \u0130le Birlikte Canl\u0131 AI Yan\u0131tlar\u0131n\u0131 G\u00f6steren Kendi Chat Uygulamam\u0131z\u0131 Yazal\u0131m"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>Malumunuz, g\u00fcn\u00fcm\u00fcz, anl\u0131k olarak yaz\u0131l\u0131msal ilkelerin s\u0131n\u0131rlar\u0131n\u0131 a\u015fan e\u015fsiz devrimlerin s\u00f6z konusu oldu\u011fu ilgin\u00e7 bir tarihsel d\u00f6neme \u015fahitlik etmektedir. Art\u0131k yaz\u0131l\u0131mlar, salt algoritmalara dayal\u0131 ve \u00f6nceden tarif edilebilir i\u015fleyi\u015f mant\u0131klar\u0131ndan ziyade kompleks bir altyap\u0131ya ve \u015fuurumsu bir davran\u0131\u015fa sahip yapay zek\u00e2lardan meydana gelmekte ya da hi\u00e7 yoktan ucundan k\u0131y\u0131s\u0131ndan yapay zeka deste\u011fiyle varl\u0131klar\u0131na, d\u00fcnden ciddi farkla bu g\u00fcn devam ederek geleneksel d\u00f6nemlere nazaran radikal bir evrimle\u015fme s\u00fcrecinde ilerlemektedirler. Evet, bu bir d\u00f6n\u00fc\u015f\u00fcmd\u00fcr. Belki d\u0131\u015fardan bak\u0131nca zahiren gibi g\u00f6z\u00fckebilir ama esas\u0131nda olay\u0131n kimyas\u0131 d\u00f6n\u00fc\u015fmekte ve d\u00f6n\u00fc\u015f\u00fcrken de ister istemez bir\u00e7ok team\u00fcl ile birlikte al\u0131\u015fkanl\u0131klar da de\u011fi\u015fmektedir. Bu d\u00f6n\u00fc\u015f\u00fcm; sadece yaz\u0131l\u0131mlar\u0131n gereksinimlerini de\u011fi\u015ftirmekle kalmamakta, bir yandan da biz geli\u015ftiricilerin problem \u00e7\u00f6zme bi\u00e7imlerine de dokunu\u015f yaparak, \u015fekillendirmektedir.<\/p>\n<p>Art\u0131k bir yaz\u0131l\u0131m geli\u015ftiricisi i\u00e7in mesele yaln\u0131zca belirli algoritmalar\u0131 tasarlamak ve in\u015fa etmekten ibaret de\u011fildir. Ayn\u0131 zamanda bu algoritma s\u00fcre\u00e7lerindeki verileri y\u00f6netecek, i\u015fleyecek ve \u00f6zellikle karar s\u00fcre\u00e7lerini y\u00f6nlendirecek yapay zek\u00e2 modellerini e\u011fitmek ve dinamik sistemler in\u015fa etmek gibi \u00e7ok boyutlu bir s\u00fcrecin zanaatkar\u0131 olmas\u0131 ve klasik tekniklerin \u00f6tesine ge\u00e7mesi gerekmektedir. Evet, \u015fahsen benim bizlerden beklentim budur. Bu ge\u00e7i\u015f, yak\u0131nda ortalama bir yaz\u0131l\u0131mda olmazsa olmaz denebilecek yapay zek\u00e2 sohbet uygulamalar\u0131n\u0131n niteliksel olarak yaz\u0131l\u0131mlara dahil edilmesiyle kendini mutlak g\u00f6sterecektir. Kullan\u0131c\u0131 deneyimini iyile\u015ftirmek i\u00e7in AI modelleriyle olan etkile\u015fim sonucunda cevaplar\u0131n &#8216;canl\u0131&#8217; olarak ekrana yans\u0131t\u0131lmas\u0131 b\u00fcy\u00fck fark yaratacak ve bir\u00e7ok yaz\u0131l\u0131mc\u0131dan beklenen beceri olarak kar\u015f\u0131m\u0131za \u00e7\u0131kacakt\u0131r. Bizler bu i\u00e7eri\u011fimizde bu fark\u0131 .NET geli\u015ftiricileri olarak nas\u0131l kendi uygulamalar\u0131m\u0131za katabilece\u011fimizi inceleyecek ve canl\u0131 AI yan\u0131tlar\u0131n\u0131 web uygulamas\u0131nda yay\u0131nlayan kendi chat uygulamam\u0131z\u0131n nas\u0131l geli\u015ftirilece\u011fine odaklanaca\u011f\u0131z.<\/p>\n<p>\u0130\u00e7eri\u011fimiz s\u00fcrecinde <em>OpenRouter<\/em> \u00fczerinden <a href=\"https:\/\/openrouter.ai\/google\/gemini-2.0-pro-exp-02-05:free\" target=\"_blank\">Google: Gemini Pro 2.0 Experimental (free)<\/a> modelini kullan\u0131yor olaca\u011f\u0131z.<\/p>\n<blockquote><p><em>OpenRouter, farkl\u0131 yapay zek\u00e2 modellerine tek bir API \u00fczerinden eri\u015fim sa\u011flayan ve b\u00f6ylece \u00f6zellikle yapay zek\u00e2 destekli uygulamalar geli\u015ftirenler i\u00e7in b\u00fcy\u00fck kolayl\u0131klar sunan bir y\u00f6nlendirme platformudur. Bu platform sayesinde OpenAI, Anthropic, Mistral, Google Gemini, Meta vs. gibi farkl\u0131 LLM provider&#8217;lar\u0131n\u0131 tek bir API \u00e7a\u011fr\u0131s\u0131yla kullanabilir ve olas\u0131 model de\u011fi\u015ftirme ihtiya\u00e7lar\u0131na kar\u015f\u0131n kodu ba\u015ftan yazmaktansa OpenRouter API parametresine k\u00fc\u00e7\u00fck bir dokunu\u015fta bulunarak an\u0131nda istenilen modele ge\u00e7i\u015f yap\u0131labilir. Ayr\u0131ca daha pahal\u0131 bir model yerine benzer i\u015flev g\u00f6ren uygun fiyatl\u0131 alternatifleri de deneyerek maliyet optimizasyonu yap\u0131labilece\u011fi gibi, bir yandan da farkl\u0131 modellerin g\u00fc\u00e7l\u00fc ve zay\u0131f y\u00f6nlerini merkezi bir \u015fekilde test ederek projenizin gereksinimlerine uygun olan\u0131 se\u00e7ip performansta art\u0131\u015f ile birlikte esneklikte sa\u011flanabilmektedir.<\/p>\n<p>OpenRouter&#8217;\u0131n sunmu\u015f oldu\u011fu merkezi LLM hizmeti sayesinde kullan\u0131lan AI modelinde bir \u00e7\u00f6kme durumu meydana gelir ya da kullan\u0131lamamazl\u0131k s\u00f6z konusu olursa direkt platform \u00fczerinden alternatif bir modelle h\u0131zl\u0131ca i\u015flevselli\u011fe devam edebilir ve b\u00f6ylece servis s\u00fcreklili\u011fini de olabildi\u011fince garanti alt\u0131na alabilirsiniz. Evet, t\u00fcm bunlar tek bir API anahtar\u0131 ile ger\u00e7ekle\u015ftirilmektedir.<\/p>\n<p>\u00d6zetle; OpenRouter, yapay zek\u00e2 modellerinin uygulama kapsam\u0131nda olduk\u00e7a az maliyetle kolayca y\u00f6netilmesine ve sistem s\u00fcreklili\u011finin sa\u011flanmas\u0131na imkan veren olduk\u00e7a de\u011ferli bir platformdur.<br \/>\n<\/em><\/p><\/blockquote>\n<h3>Semantic Kernel \u0130le SignalR Uygulamas\u0131<\/h3>\n<p>Evet, art\u0131k \u00f6rnek \u00e7al\u0131\u015fmam\u0131za ge\u00e7ebiliriz&#8230; Bunun i\u00e7in a\u015fa\u011f\u0131daki her bir basama\u011f\u0131n ad\u0131m ad\u0131m takip edilmesi yeterli olacakt\u0131r. O halde hadi ba\u015flayal\u0131m&#8230;<\/p>\n<ul style=\"font-size:15px;\">\n<li><strong style=\"font-size:16px;\">Ad\u0131m 1<\/strong> <em style=\"font-size:12px;color:green;\">(Semantic Kernel K\u00fct\u00fcphanesinin Kurulmas\u0131 ve Temel Yap\u0131land\u0131rman\u0131n Sa\u011flanmas\u0131) &#8211; <span style=\"color:purple;\">Backend<\/span><\/em><br \/>\n\u0130lk olarak bir Asp.NET Core projesi olu\u015ftural\u0131m ve i\u00e7erisine <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.SemanticKernel.Connectors.OpenAI\" target=\"_blank\">Microsoft.SemanticKernel.Connectors.OpenAI<\/a> k\u00fct\u00fcphanesini kural\u0131m.<\/p>\n<div style=\"text-align:center;color:red;line-height:0.2\"><code>Install-Package Microsoft.SemanticKernel.Connectors.OpenAI<\/code><\/div>\n<p>Ve akabinde a\u015fa\u011f\u0131daki temel yap\u0131land\u0131rmalarda bulunarak uygulama boyunca kullanaca\u011f\u0131m\u0131z ana zemini olu\u015ftural\u0131m;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nusing Microsoft.SemanticKernel;\r\nusing OpenAI;\r\nusing System.ClientModel;\r\n\r\nvar builder = WebApplication.CreateBuilder(args);\r\n\r\nbuilder.Services.AddOpenAIChatCompletion(\r\n    modelId: &quot;google\/gemini-2.0-pro-exp-02-05:free&quot;,\r\n    openAIClient: new OpenAIClient(\r\n        credential: new ApiKeyCredential(&quot;sk-or-v1-0959a8d8aed****d06e78ff68ceaa&quot;),\r\n        options: new OpenAIClientOptions\r\n        {\r\n            Endpoint = new Uri(&quot;https:\/\/openrouter.ai\/api\/v1&quot;)\r\n        })\r\n    );\r\n\r\nbuilder.Services.AddCors(options =&gt;\r\n    options.AddDefaultPolicy(policy =&gt; policy.AllowAnyMethod()\r\n                                       .AllowAnyHeader()\r\n                                       .AllowCredentials()\r\n                                       .SetIsOriginAllowed(s =&gt; true)));\r\n\r\nbuilder.Services.AddSignalR();\r\n\r\nvar app = builder.Build();\r\napp.UseCors();\r\n\r\napp.MapGet(&quot;\/&quot;, () =&gt; &quot;Hello World!&quot;);\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<p>\u015eimdi burada yapt\u0131\u011f\u0131m\u0131z \u00e7al\u0131\u015fmalar\u0131 ele almam\u0131z gerekirse e\u011fer; g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere <em>OpenRouter<\/em> \u00fczerinden bir AI modeline ba\u011flan\u0131yorken <code>Microsoft.SemanticKernel.Connectors.OpenAI<\/code> k\u00fct\u00fcphanesini kullanmam\u0131z yeterlidir. Sanki Open AI kullan\u0131yormu\u015f gibi <code>AddOpenAIChatCompletion<\/code> arac\u0131l\u0131\u011f\u0131yla temel yap\u0131land\u0131rmada bulunmam\u0131z ve kullan\u0131lacak <em>model id<\/em> ve <em>credential<\/em> bilgilerini vermemiz gerekmektedir. Bizler yukar\u0131daki sat\u0131rlarda bahsetti\u011fimiz \u00fczere <em>Google: Gemini Pro 2.0 Experimental (free)<\/em> modelini kullanaca\u011f\u0131m\u0131zdan dolay\u0131 bu modelin id&#8217;sini ilgili parametreye belirtmi\u015f bulunuyoruz. <strong><em>model id&#8217;yi nereden bulacaz la hoca?<\/em><\/strong> derseniz bunun i\u00e7in <em>OpenRouter<\/em> \u00fczerinden ilgili modelin detay\u0131na girip a\u015fa\u011f\u0131daki g\u00f6rselde oldu\u011fu gibi vurgulanan alandan <em>model id<\/em> de\u011ferini almam\u0131z yeterli olacakt\u0131r;<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.png\" alt=\"Semantic Kernel &amp; SignalR \u0130le Birlikte Canl\u0131 AI Yan\u0131tlar\u0131n\u0131 G\u00f6steren Kendi Chat Uygulamam\u0131z\u0131 Yazal\u0131m\" width=\"1209\" height=\"584\" class=\"aligncenter size-full wp-image-27597\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.png 1209w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-300x145.png 300w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1024x495.png 1024w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-768x371.png 768w\" sizes=\"auto, (max-width: 1209px) 100vw, 1209px\" \/><\/a><em><strong>Ee peki <em>credential<\/em>&#8216;da kullan\u0131lan key&#8217;i nereden bulaca\u011f\u0131z?<\/strong><\/em> diye sorarsan\u0131z, bu de\u011feri de yine yukar\u0131daki g\u00f6rselde tee sa\u011f tarafta API sekmesini g\u00f6r\u00fcyorsunuz&#8230; \u0130\u015fte bu sekmeye geldi\u011finizde &#8216;Create API key&#8217; butonunu g\u00f6receksiniz. \u0130\u015fte buradan olu\u015fturup, elde edebilirsiniz&#8230;<\/p>\n<p>Bunlar\u0131n d\u0131\u015f\u0131nda yap\u0131lan di\u011fer yap\u0131land\u0131rmalara g\u00f6z atarsak e\u011fer; malum default CORS politikalar\u0131n\u0131 ayarlam\u0131\u015f bulunuyoruz. \u00c7\u00fcnk\u00fc g\u00fcn\u00fcn sonunda AI modelle yap\u0131lan haberle\u015fme neticesinde elde edilen cevaplar\u0131 bir web uygulamas\u0131 \u00fczerinde SignalR ile anl\u0131k olarak yay\u0131nlayaca\u011f\u0131z. O y\u00fczden, bu a\u00e7\u0131dan ilgili politikalar\u0131n bu duruma elveri\u015fli olmas\u0131 gerekmektedir. Bir yandan da <code>AddSignalR<\/code> servisini ekleyerek uygulamaya SignalR altyap\u0131s\u0131n\u0131 dahil etmi\u015f bulunuyoruz.<\/p>\n<p>Evet, art\u0131k hasbel kader ilerlemeye haz\u0131r\u0131z diyebiliriz \ud83d\ude42\n<\/li>\n<li><strong style=\"font-size:16px;\">Ad\u0131m 2<\/strong> <em style=\"font-size:12px;color:green;\">(SignalR AIHub&#8217;\u0131n\u0131n Olu\u015fturulmas\u0131 ve Temel Yap\u0131land\u0131rmalar\u0131n Sa\u011flanmas\u0131) &#8211; <span style=\"color:purple;\">Backend<\/span><\/em><br \/>\n\u015eimdi de uygulamada kullanaca\u011f\u0131m\u0131z SignalR altyap\u0131s\u0131n\u0131 in\u015fa edece\u011fiz. Bunun i\u00e7in \u00f6ncelikle a\u015fa\u011f\u0131daki gibi bir Hub s\u0131n\u0131f\u0131 tasarlamam\u0131z gerekmektedir;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class AIHub : Hub\r\n    {\r\n\r\n    }\r\n<\/pre>\n<\/div>\n<p>Bu s\u0131n\u0131f, \u00e7al\u0131\u015fmam\u0131z\u0131n devam\u0131nda AI modelinden \u00fcretilecek olan anl\u0131k cevab\u0131 par\u00e7a par\u00e7a mesaj olarak client&#8217;a g\u00f6nderecektir.<\/p>\n<p>Velhas\u0131l, olu\u015fturulan bu Hub&#8217;\u0131 a\u015fa\u011f\u0131daki gibi uygulamaya dahil ederek, bir endpoint verelim;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\nvar app = builder.Build();\r\napp.UseCors();\r\n\r\napp.MapGet(&quot;\/&quot;, () =&gt; &quot;Hello World!&quot;);\r\n\r\napp.MapHub&lt;AIHub&gt;(&quot;ai-hub&quot;);\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong style=\"font-size:16px;\">Ad\u0131m 3<\/strong> <em style=\"font-size:12px;color:green;\">(Modelden \u00dcretilen Cevab\u0131 Client&#8217;a Server-to-Client Modeliyle Stream Edecek Servisi Olu\u015fturma) &#8211; <span style=\"color:purple;\">Backend<\/span><\/em><br \/>\n\u015eimdi de, AI modelinde kullan\u0131c\u0131dan gelen prompt&#8217;a kar\u015f\u0131l\u0131k \u00fcretilecek cevab\u0131 client&#8217;a par\u00e7a par\u00e7a g\u00f6nderelim. Bunun i\u00e7in a\u015fa\u011f\u0131daki gibi bir servis olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class AIService(IHubContext&lt;AIHub&gt; hubContext, IChatCompletionService chatCompletionService)\r\n    {\r\n        public async Task GetMessageStreamAsync(string prompt, string connectionId, CancellationToken? cancellationToken = default!)\r\n        {\r\n            await foreach (var response in chatCompletionService.GetStreamingChatMessageContentsAsync(prompt))\r\n            {\r\n                cancellationToken?.ThrowIfCancellationRequested();\r\n\r\n                await hubContext.Clients.Client(connectionId).SendAsync(&quot;ReceiveMessage&quot;, response.ToString());\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere yukar\u0131da, prompt AI modele g\u00f6nderilmekte ve modelden stream bir \u015fekilde gelen mesaj elde edilerek, bir yandan da client&#8217;a gelen her par\u00e7a ad\u0131m ad\u0131m g\u00f6nderilmektedir. Tabi burada teknik olarak <a href=\"https:\/\/www.gencayyildiz.com\/blog\/asp-net-core-signalr-serisi-7-signalr-server-ihubcontextt-interfacei-ile-hub-disi-ileti-gonderme\/\" target=\"_blank\">IHubContext<T> Interface\u2019i \u0130le Hub D\u0131\u015f\u0131 \u0130leti G\u00f6nderme<\/a> tekni\u011fini kullanarak bir \u00f6nceki ad\u0131mda olu\u015fturdu\u011fumuz Hub&#8217;\u0131 kulland\u0131\u011f\u0131m\u0131z\u0131 da s\u00f6ylemekte fayda g\u00f6rmekteyim. Ayr\u0131ca dikkat ederseniz, \u00fcretilecek cevap sadece client&#8217;tan gelecek olan <em>connection id<\/em> bilgisine kar\u015f\u0131l\u0131k client&#8217;a(yani yine o client&#8217;a) g\u00f6nderilmektedir.<\/p>\n<p>Velhas\u0131l, olu\u015fturulan bu servisi kullanabilmek i\u00e7in IoC Container&#8217;a a\u015fa\u011f\u0131daki gibi singleton olarak ekleyelim;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\nbuilder.Services.AddSingleton&lt;AIService&gt;();\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong style=\"font-size:16px;\">Ad\u0131m 4<\/strong> <em style=\"font-size:12px;color:green;\">(Chat Endpoint&#8217;ini Olu\u015fturma) &#8211; <span style=\"color:purple;\">Backend<\/span><\/em><br \/>\nEvet, backend a\u00e7\u0131s\u0131ndan yapmam\u0131z gereken son hamle ise chat endpoint&#8217;inin olu\u015fturulmas\u0131d\u0131r. Bunun i\u00e7in a\u015fa\u011f\u0131daki gibi bir \u00e7al\u0131\u015fma yapmam\u0131z yeterli olacakt\u0131r;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\napp.MapPost(&quot;\/chat&quot;, async (AIService aiService, ChatRequestVM chatRequest, CancellationToken cancellationToken)\r\n    =&gt; await aiService.GetMessageStreamAsync(chatRequest.Prompt, chatRequest.ConnectionId, cancellationToken));\r\n<\/pre>\n<\/div>\n<p>Burada kulland\u0131\u011f\u0131m\u0131z viewmodel&#8217;\u0131n i\u00e7eri\u011fi ise a\u015fa\u011f\u0131daki gibidir;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public record ChatRequestVM(string Prompt, string ConnectionId)\r\n    {\r\n\r\n    }\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong style=\"font-size:16px;\">Ad\u0131m 5<\/strong> <em style=\"font-size:12px;color:green;\">(UI Ortam\u0131n\u0131n Haz\u0131rlanmas\u0131 ve <code>@microsoft\/signalr<\/code> K\u00fct\u00fcphanesinin \u0130ndirilip, Entegre Edilmesi) &#8211; <span style=\"color:purple;\">Client<\/span><\/em><br \/>\nEvet, art\u0131k frontend k\u0131sm\u0131n\u0131 geli\u015ftirmeye odaklanabiliriz. Art\u0131k backend AI modelle haberle\u015febilmekte ve kullan\u0131c\u0131 taraf\u0131ndan g\u00f6nderilen prompt e\u015fli\u011finde \u00fcretilecek cevab\u0131 bizlere ad\u0131m ad\u0131m g\u00f6nderebilmektedir. (\u0130n\u015fallah \ud83d\ude02) Art\u0131k bizler bir UI \u00e7al\u0131\u015fmas\u0131 ile gelen bu verileri SignalR e\u015fli\u011finde realtime&#8217;da t\u00fcketecek ve anl\u0131k chat \u00e7al\u0131\u015fmas\u0131n\u0131 g\u00f6rsel olarak ger\u00e7ekle\u015ftirece\u011fiz. Bunun i\u00e7in solution dizinine UI ad\u0131nda bir klas\u00f6r ekleyerek i\u00e7erisinde \u00f6nce <code>npm init<\/code> komutuyla bir proje altyap\u0131s\u0131 olu\u015ftural\u0131m, ard\u0131ndan da <code style=\"color:red;\">npm install @microsoft\/signalr<\/code> talimat\u0131yla SignalR&#8217;\u0131n k\u00fct\u00fcphanesini y\u00fckleyelim.<\/p>\n<p>Devam\u0131nda ise ilgili klas\u00f6r i\u00e7erisine <code>index.html<\/code> ve <code>app.js<\/code> isimli dosyalar\u0131 olu\u015fturup, ekleyelim ve <code>index.html<\/code> i\u00e7eri\u011fini a\u015fa\u011f\u0131daki gibi tasarlayal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html lang=&quot;tr&quot;&gt;\r\n\r\n&lt;head&gt;\r\n    &lt;meta charset=&quot;UTF-8&quot;&gt;\r\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;\r\n    &lt;title&gt;Custom Chat&lt;\/title&gt;\r\n    &lt;link href=&quot;style.css&quot; rel=&quot;stylesheet&quot; \/&gt;\r\n&lt;\/head&gt;\r\n\r\n&lt;body&gt;\r\n    &lt;div class=&quot;chat-container&quot;&gt;\r\n        &lt;div class=&quot;chat-header&quot;&gt;Custom ChatGPT &lt;span id=&quot;connectionId&quot;&gt;&lt;\/span&gt;&lt;\/div&gt;\r\n        &lt;div class=&quot;chat-box&quot; id=&quot;chat-box&quot;&gt;&lt;\/div&gt;\r\n        &lt;div class=&quot;chat-input&quot;&gt;\r\n            &lt;input type=&quot;text&quot; id=&quot;user-input&quot; placeholder=&quot;Mesaj\u0131n\u0131z\u0131 yaz\u0131n...&quot;&gt;\r\n            &lt;button onclick=&quot;sendPrompt()&quot;&gt;G\u00f6nder&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;script src=&quot;node_modules\/@microsoft\/signalr\/dist\/browser\/signalr.min.js&quot;&gt;&lt;\/script&gt;\r\n    &lt;script src=&quot;app.js&quot;&gt;&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n\r\n&lt;\/html&gt;\r\n<\/pre>\n<\/div>\n<p><a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1.png\" alt=\"Semantic Kernel &amp; SignalR \u0130le Birlikte Canl\u0131 AI Yan\u0131tlar\u0131n\u0131 G\u00f6steren Kendi Chat Uygulamam\u0131z\u0131 Yazal\u0131m\" width=\"811\" height=\"761\" class=\"aligncenter size-full wp-image-27599\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1.png 811w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1-300x282.png 300w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/02\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1-768x721.png 768w\" sizes=\"auto, (max-width: 811px) 100vw, 811px\" \/><\/a>Burada <strong><em>20<\/em><\/strong> ve <strong><em>21.<\/em><\/strong> sat\u0131rlarda g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere hem <code>signalr.min.js<\/code> hem de <code>app.js<\/code> dosyalar\u0131 ilgili html dosyas\u0131na eklenmi\u015f vaziyettedir. Art\u0131k bizler t\u00fcm client i\u015flemleri i\u00e7in <code>app.js<\/code>&#8216;e odaklan\u0131p, i\u015flemlerimizi y\u00fcr\u00fctebiliriz.\n<\/li>\n<li><strong style=\"font-size:16px;\">Ad\u0131m 6<\/strong> <em style=\"font-size:12px;color:green;\">(AI Modelinden Gelen Mesajlar\u0131n Client&#8217;ta Toparlan\u0131p, G\u00f6sterilmesi) &#8211; <span style=\"color:purple;\">Client<\/span><\/em><br \/>\n\u015eimdi de, backend&#8217;den gelecek olan mesaj par\u00e7alar\u0131n\u0131 tek tek toparlayal\u0131m ve chat g\u00f6r\u00fcn\u00fcm\u00fc verecek \u015fekilde JavaScript&#8217;le a\u015fa\u011f\u0131daki \u00e7al\u0131\u015fmay\u0131 ger\u00e7ekle\u015ftirelim;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar hubConnection = new signalR.HubConnectionBuilder()\r\n    .withUrl(&quot;https:\/\/localhost:7118\/ai-hub&quot;)\r\n    .build();\r\n\r\nhubConnection.start()\r\n    .then(() =&gt;\r\n        document.getElementById(&quot;connectionId&quot;).innerHTML = `(${hubConnection.connectionId})`)\r\n    .catch(err =&gt;\r\n        console.error(err.toString()));\r\n\r\nhubConnection.on(&quot;ReceiveMessage&quot;, responseMessage =&gt; {\r\n    const aiMessage = `${responseMessage}`;\r\n    chatBox.innerHTML += aiMessage;\r\n    chatBox.scrollTop = chatBox.scrollHeight;\r\n});\r\n\r\nconst input = document.getElementById(&quot;user-input&quot;);\r\nconst chatBox = document.getElementById(&quot;chat-box&quot;);\r\n\r\nfunction sendPrompt() {\r\n    if (input.value.trim() === &quot;&quot;) return;\r\n\r\n    const userMessage = `&lt;div&gt;&lt;strong&gt;Sen:&lt;\/strong&gt; ${input.value}&lt;\/div&gt;`;\r\n    chatBox.innerHTML += userMessage;\r\n    chatBox.scrollTop = chatBox.scrollHeight;\r\n\r\n    fetch(&quot;https:\/\/localhost:7118\/chat&quot;, {\r\n        method: &quot;POST&quot;,\r\n        headers: {\r\n            &quot;Content-Type&quot;: &quot;application\/json&quot;\r\n        },\r\n        body: JSON.stringify({ prompt: input.value, connectionId: hubConnection.connectionId })\r\n    })\r\n        .then(response =&gt; response.json())\r\n        .then(data =&gt; console.log(&quot;Success : &quot;, data))\r\n        .catch(error =&gt; console.error(&quot;Error&quot;, error));\r\n    input.value = &quot;&quot;;\r\n}\r\n<\/pre>\n<\/div>\n<p>Burada dikkat ederseniz jQuery&#8217;ye vs. bula\u015fmaks\u0131z\u0131n salt JavaScript ile olduk\u00e7a sade bir SignalR client \u00e7al\u0131\u015fmas\u0131 ger\u00e7ekle\u015ftirmi\u015f bulunuyoruz. \u015eimdi yap\u0131lanlar\u0131 incelersek e\u011fer; ilk olarak, client&#8217;lar\u0131 birbirlerinden ay\u0131rt edebilmek i\u00e7in <strong><em>7.<\/em><\/strong> sat\u0131rda oldu\u011fu gibi Hub&#8217;a ba\u011fland\u0131klar\u0131 an <em>connection id<\/em> bilgileri al\u0131nmakta ve bir yandan da ekranda uygun bir yere yazd\u0131r\u0131lmaktad\u0131r. <strong><em>11<\/em><\/strong> ile <strong><em>15.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda ise backend&#8217;den gelecek olan AI verileri chat b\u00f6l\u00fcm\u00fcne eklenerek, g\u00f6rsel olarak sohbet havas\u0131 kat\u0131lmaktad\u0131r. <code>sendPrompt<\/code> fonksiyonun da ise kullan\u0131c\u0131dan al\u0131nan prompt \u00f6nceki ad\u0131mlarda olu\u015fturdu\u011fumuz API&#8217;ye g\u00f6nderilmekte ve AI modelinin cevap \u00fcretip stream edece\u011fi s\u00fcre\u00e7 b\u00f6ylece ba\u015flat\u0131lmaktad\u0131r.\n<\/li>\n<li><strong style=\"font-size:16px;\">Ad\u0131m 7<\/strong> <em style=\"font-size:12px;color:green;\">(Test)<\/em><br \/>\nEvet, t\u00fcm ad\u0131mlardaki i\u015flevleri harfiyen yapt\u0131\u011f\u0131m\u0131za g\u00f6re art\u0131k teste ge\u00e7ebiliriz. Bunun i\u00e7in API uygulamas\u0131n\u0131 derleyip, \u00e7al\u0131\u015ft\u0131ral\u0131m ve ard\u0131ndan olu\u015fturdu\u011fumuz client \u00fczerinden a\u015fa\u011f\u0131daki ekran g\u00f6r\u00fcnt\u00fcs\u00fcnde oldu\u011fu gibi bir prompt g\u00f6nderip, neticeyi g\u00f6zlemleyelim.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.gif\" alt=\"Semantic Kernel &amp; SignalR \u0130le Birlikte Canl\u0131 AI Yan\u0131tlar\u0131n\u0131 G\u00f6steren Kendi Chat Uygulamam\u0131z\u0131 Yazal\u0131m\" width=\"800\" height=\"395\" class=\"aligncenter size-full wp-image-27601\" \/><\/a>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere Semantic Kernel sayesinde uygulamam\u0131za entegre etti\u011fimiz bir AI model&#8217;le yapt\u0131\u011f\u0131m\u0131z ileti\u015fim sayesinde \u00f6zelle\u015ftrilmi\u015f bir chat yap\u0131s\u0131 in\u015fa etmi\u015f olduk \ud83d\ude42 Tamam, tasar\u0131m biraz daha \u00f6zenli olabilirdi belki ama bu konuda beni bilenler a\u00e7\u0131s\u0131ndan \u015f\u00fck\u00fcrl\u00fck bi g\u00f6rselimiz de yok de\u011fil diyebiliriz. \ud83d\ude42\n<\/li>\n<\/ul>\n<p>Art\u0131k in\u015fa etti\u011fimiz bu temel \u00fczerine Semantic Kernel&#8217;\u0131n yeteneklerinden faydalanarak belli ba\u015fl\u0131 geli\u015ftirmelerde bulunabiliriz. \u015eimdi gelin bunla ilgili bir ka\u00e7 dokunu\u015fta bulunal\u0131m&#8230;<\/p>\n<h3>Chat&#8217;e History Object \u0130le Memory \u00d6zelli\u011fi Ekleme<\/h3>\n<p>AI modelleriyle ger\u00e7ekle\u015ftirilmi\u015f bir chat uygulamas\u0131n\u0131n olmazsa olmaz\u0131 konunun ba\u011flam\u0131n\u0131n hat\u0131rlanmas\u0131d\u0131r. Evet, hat\u0131rlarsan\u0131z e\u011fer <a href=\"https:\/\/www.gencayyildiz.com\/blog\/semantic-kernel-nedir-deepseek-r1-esliginde-net-acisindan-derinlemesine-degerlendirelim\/\" target=\"_blank\">Semantic Kernel Nedir? (DeepSeek R1 E\u015fli\u011finde .NET A\u00e7\u0131s\u0131ndan Derinlemesine De\u011ferlendirelim)<\/a> ba\u015fl\u0131kl\u0131 makalemizde History object&#8217;i ile context&#8217;i chat s\u00fcrecinde memory&#8217;de tutabilece\u011fimizi ve b\u00f6ylece \u00f6nceki mesajlar\u0131nda AI modeli taraf\u0131ndan hat\u0131rlan\u0131p, ileti\u015fim s\u00fcrecine dahil edilebilece\u011fini ifade etmi\u015ftik. A\u015fa\u011f\u0131daki \u00e7al\u0131\u015fmada yukar\u0131da geli\u015ftirdi\u011fimiz altyap\u0131ya history niteli\u011fi kazand\u0131rmakta ve b\u00f6ylece chat s\u00fcrecinde ba\u011flam haf\u0131zaya at\u0131larak \u00f6nceki prompt&#8217;lar ve cevaplar\u0131 o anki s\u00fcrece dahil edilmektedir;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class HistoryService\r\n    {\r\n        private static readonly Dictionary&lt;string, ChatHistory&gt; _chatHistories = new();\r\n        public static ChatHistory GetChatHistory(string connectionId)\r\n        {\r\n            ChatHistory? chatHistory = null;\r\n            if (_chatHistories.TryGetValue(connectionId, out chatHistory))\r\n                return chatHistory;\r\n            else\r\n            {\r\n                chatHistory = new();\r\n                _chatHistories.Add(connectionId, chatHistory);\r\n            }\r\n            return chatHistory;\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>\u0130lk olarak g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere farkl\u0131 client&#8217;lar\u0131 birbirlerinden ay\u0131rt edebilmek i\u00e7in yukar\u0131daki <code>HistoryService<\/code> s\u0131n\u0131f\u0131n\u0131 tasarl\u0131yoruz. Dikkat ederseniz, bir <code>Dictionary<\/code> i\u00e7erisinde Hub ile ba\u011flant\u0131 kurmu\u015f olan t\u00fcm client&#8217;lar\u0131n <em>connection id<\/em> de\u011ferlerine kar\u015f\u0131l\u0131k bir <code>ChatHistory<\/code> nesnesi tutuyoruz. <code>GetChatHistory<\/code> metodu sayesinde de, art\u0131k hangi <em>connection id<\/em>&#8216;ye uygun History nesnesi laz\u0131msa onu elde ediyoruz.<\/p>\n<p>Devam\u0131nda ise <code>AIService<\/code>&#8216;i a\u015fa\u011f\u0131daki gibi bu History yap\u0131lanmas\u0131yla tekrardan restore edersek e\u011fer;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class AIService(IHubContext&lt;AIHub&gt; hubContext, IChatCompletionService chatCompletionService)\r\n    {\r\n        public async Task GetMessageStreamAsync(string prompt, string connectionId, CancellationToken? cancellationToken = default!)\r\n        {\r\n            var history = HistoryService.GetChatHistory(connectionId);\r\n\r\n            history.AddUserMessage(prompt);\r\n            string responseContent = &quot;&quot;;\r\n            await foreach (var response in chatCompletionService.GetStreamingChatMessageContentsAsync(history))\r\n            {\r\n                cancellationToken?.ThrowIfCancellationRequested();\r\n\r\n                await hubContext.Clients.Client(connectionId).SendAsync(&quot;ReceiveMessage&quot;, response.ToString());\r\n                responseContent += response.ToString();\r\n            }\r\n            history.AddAssistantMessage(responseContent);\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Evet, art\u0131k bu \u00e7al\u0131\u015fma neticesinde chat s\u00fcrecinde client&#8217;lar aras\u0131ndaki fark\u0131 g\u00f6zeterek konunun ba\u011flam\u0131n\u0131 AI modele hat\u0131rlatabilmekteyiz.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.png\" alt=\"Semantic Kernel &amp; SignalR \u0130le Birlikte Canl\u0131 AI Yan\u0131tlar\u0131n\u0131 G\u00f6steren Kendi Chat Uygulamam\u0131z\u0131 Yazal\u0131m\" width=\"807\" height=\"760\" class=\"aligncenter size-full wp-image-27602\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim.png 807w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-300x283.png 300w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-768x723.png 768w\" sizes=\"auto, (max-width: 807px) 100vw, 807px\" \/><\/a><\/p>\n<h3>Plugin \u0130le Chat&#8217;e \u00d6zelle\u015ftirilmi\u015f Davran\u0131\u015f Ekleme<\/h3>\n<p>Semantic Kernel&#8217;\u0131n en b\u00fcy\u00fck \u00f6zelliklerinden birisi plugin olu\u015fturarak AI modellerinin davran\u0131\u015flar\u0131n\u0131 uygulamam\u0131z\u0131n gereksinimlerine g\u00f6re \u00f6zelle\u015ftirebilmemizi sa\u011flamas\u0131d\u0131r. Bunu ilgili <a href=\"https:\/\/www.gencayyildiz.com\/blog\/semantic-kernel-nedir-deepseek-r1-esliginde-net-acisindan-derinlemesine-degerlendirelim\/\" target=\"_blank\">Semantic Kernel<\/a> makalemizde \u00f6zellikle vurgulad\u0131\u011f\u0131m\u0131z\u0131 an\u0131msayacaks\u0131n\u0131z. Olu\u015fturdu\u011fumuz plugin&#8217;ler sayesinde AI modele gelen prompt&#8217;lara kar\u015f\u0131n \u00fcretilecek cevaplarda otomatik bir \u015fekilde bu eklentileri devreye sokarak hem AI modelin kapsam\u0131n\u0131 belirleyebiliyor, hem de verece\u011fi cevab\u0131 manip\u00fcle edebilme \u015fans\u0131 elde ediyoruz. Yeter ki, bu \u00f6zelli\u011fi kullan\u0131lan AI modeli destekliyor olsun. Evet, bizim bu i\u00e7eri\u011fimizde kulland\u0131\u011f\u0131m\u0131z <a href=\"https:\/\/openrouter.ai\/google\/gemini-2.0-pro-exp-02-05:free\" target=\"_blank\">Google: Gemini Pro 2.0 Experimental (free)<\/a> AI modelimiz prompt i\u00e7eri\u011fine uygun olarak otomatik plugin devreye sokabilme deste\u011fine sahiptir. \u015eimdi gelin buna \u00f6zel bir plugin olu\u015ftural\u0131m ve ard\u0131ndan AI model&#8217;i otomatik bir \u015fekilde plugin&#8217;i kullanacak \u015fekilde yap\u0131land\u0131ral\u0131m;<\/p>\n<p>\u00d6ncelikle plugin&#8217;i a\u015fa\u011f\u0131daki gibi tasarlayal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class CalculatorPlugin\r\n    {\r\n        &#x5B;KernelFunction(&quot;add&quot;)]\r\n        &#x5B;Description(&quot;\u0130ki say\u0131sal de\u011fer \u00fczerinde toplama i\u015flemi ger\u00e7ekle\u015ftirir.&quot;)]\r\n        &#x5B;return: Description(&quot;Toplam de\u011feri d\u00f6nd\u00fcr\u00fcr.&quot;)]\r\n        public int Add(int number1, int number2)\r\n            =&gt; number1 + number2;\r\n    }\r\n<\/pre>\n<\/div>\n<p>Evet, basit bir toplama i\u015flemi. \u00d6rnek verebilmek i\u00e7in gidip tulumbadan su \u00e7ekmemi beklemiyordunuz herhalde \ud83d\ude42<\/p>\n<p>\u015eimdi bu plugin&#8217;i Semantic Kernel arac\u0131l\u0131\u011f\u0131yla uygulamaya dahil edelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\nbuilder.Services\r\n    .AddKernel()\r\n    .AddOpenAIChatCompletion(\r\n        modelId: &quot;google\/gemini-2.0-pro-exp-02-05:free&quot;,\r\n        openAIClient: new OpenAIClient(\r\n            credential: new ApiKeyCredential(&quot;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&quot;),\r\n            options: new OpenAIClientOptions\r\n            {\r\n                Endpoint = new Uri(&quot;https:\/\/openrouter.ai\/api\/v1&quot;)\r\n            })\r\n    )\r\n    .Plugins.AddFromType&lt;CalculatorPlugin&gt;();\r\n<\/pre>\n<\/div>\n<p>Burada ufak ama g\u00f6zden ka\u00e7abilecek bir noktaya dikkatinizi \u00e7ekmek istiyorum. Olu\u015fturulan plugin&#8217;i sisteme dahil edebilmek i\u00e7in <code>.Plugins.AddFromType&lt;CalculatorPlugin&gt;()<\/code> kodunu yazmam\u0131z gerekmektedir. Ee bunu da yazabilmek i\u00e7in <code>AddOpenAIChatCompletion<\/code> metodunu <code>AddKernel()<\/code> metodu \u00fczerinden yap\u0131land\u0131rmam\u0131z gerekmektedir. Aksi taktirde <code>Plugins<\/code> property&#8217;sine eri\u015fim s\u00f6z konusu olmayacakt\u0131r.<\/p>\n<p>Uygulamaya plugin&#8217;i ekledikten sonra <code>AIService<\/code>&#8216;de a\u015fa\u011f\u0131daki \u00e7al\u0131\u015fmay\u0131 ger\u00e7ekle\u015ftirerek chat s\u00fcrecine de eklentiyi dahil edelim ve prompt i\u00e7eri\u011fine g\u00f6re otomatik tetiklenebilece\u011finin yap\u0131land\u0131rmas\u0131n\u0131 ger\u00e7ekle\u015ftirelim;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class AIService(IHubContext&lt;AIHub&gt; hubContext, IChatCompletionService chatCompletionService, Kernel kernel)\r\n    {\r\n        public async Task GetMessageStreamAsync(string prompt, string connectionId, CancellationToken? cancellationToken = default!)\r\n        {\r\n            OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()\r\n            {\r\n                FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()\r\n            };\r\n\r\n            var history = HistoryService.GetChatHistory(connectionId);\r\n\r\n            history.AddUserMessage(prompt);\r\n            string responseContent = &quot;&quot;;\r\n            await foreach (var response in chatCompletionService.GetStreamingChatMessageContentsAsync(history, executionSettings: openAIPromptExecutionSettings, kernel: kernel))\r\n            {\r\n                cancellationToken?.ThrowIfCancellationRequested();\r\n\r\n                await hubContext.Clients.Client(connectionId).SendAsync(&quot;ReceiveMessage&quot;, response.ToString());\r\n                responseContent += response.ToString();\r\n            }\r\n            history.AddAssistantMessage(responseContent);\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada da dikkat edilirse e\u011fer <code>OpenAIPromptExecutionSettings<\/code> referans\u0131 \u00fczerinden eklentilerin otomatik \u00e7a\u011fr\u0131labilece\u011fi yap\u0131land\u0131r\u0131lmakta ve bu, <code>GetStreamingChatMessageContentsAsync<\/code> metodunun <em>executionSettings<\/em> parametresine verilerek AI modelinin davran\u0131\u015f\u0131 \u015fekillendirilmektedir. Ayr\u0131ca burada yine k\u00fc\u00e7\u00fck bir detay var ki, o da, uygulamada kullan\u0131lan <code>Kernel<\/code> referans\u0131 dependency injection ile IoC container&#8217;dan talep edilmekte ve yine ayn\u0131 metodun <em>kernel<\/em> parametresine verilmektedir. Aksi taktirde bu \u00e7al\u0131\u015fma i\u00e7in t\u00fcrl\u00fc hatalar s\u00f6z konusu olabilmektedir.<\/p>\n<p>\u015eimdi yapt\u0131\u011f\u0131m\u0131z bu \u00e7al\u0131\u015fmay\u0131 derleyip teste tabi tutarsak a\u015fa\u011f\u0131daki ekran g\u00f6r\u00fcnt\u00fcs\u00fcnde oldu\u011fu gibi prompt&#8217;un mahiyetine g\u00f6re ilgili eklentinin \u00e7a\u011fr\u0131ld\u0131\u011f\u0131n\u0131 g\u00f6zlemlemi\u015f olaca\u011f\u0131z;<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/03\/Semantic-Kernel-SignalR-Ile-Birlikte-Canli-AI-Yanitlarini-Gosteren-Kendi-Chat-Uygulamamizi-Yazalim-1.gif\" alt=\"Semantic Kernel &amp; SignalR \u0130le Birlikte Canl\u0131 AI Yan\u0131tlar\u0131n\u0131 G\u00f6steren Kendi Chat Uygulamam\u0131z\u0131 Yazal\u0131m\" width=\"800\" height=\"499\" class=\"aligncenter size-full wp-image-27604\" \/><\/a>Dikkat ederseniz ilk prompt normal nazik bi soruyken, ikincisi ise yar\u0131 say\u0131sal yar\u0131 metinsel bir toplama sorusudur. \u0130\u015fte bu soruda AI modeli taraf\u0131ndan eklentimiz devreye sokulup, \u00e7al\u0131\u015ft\u0131r\u0131lmaktad\u0131r. Bunu da break point&#8217;in tetiklenmesinden anlamaktay\u0131z.<\/p>\n<p>Ee bu kadar k\u00e2fi diyelim \ud83d\ude42<\/p>\n<p>Evet, g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere Asp.NET Core&#8217;da geli\u015ftirdi\u011fimiz bir Web API arac\u0131l\u0131\u011f\u0131yla Semantic Kernel \u00fczerinden rahatl\u0131kla <em>Google: Gemini Pro 2.0 Experimental (free)<\/em> AI modeliyle entegrasyon ger\u00e7ekle\u015ftirmi\u015f ve bir yandan da kullan\u0131c\u0131dan gelen prompt&#8217;lara kar\u015f\u0131 \u00fcretilen cevaplar\u0131n stream&#8217;ini t\u00fcketerek SignalR e\u015fli\u011finde bu mesajlar\u0131 bir UI uygulamas\u0131na aktararak do\u011fal bir chat havas\u0131 olu\u015fturmu\u015f bulunuyoruz. T\u00fcm bu s\u00fcre\u00e7te history nesnesiyle chat&#8217;in ba\u011flam\u0131n\u0131 client&#8217;a \u00f6zel memory&#8217;e alm\u0131\u015f ve bir yandan da custom plugin olu\u015fturarak prompt&#8217;un mahiyetine g\u00f6re otomatik devreye girecek \u015fekilde uygulamam\u0131z\u0131n gereksinimlerini g\u00f6zeterek manip\u00fclasyonlar ve kapsam s\u0131n\u0131rlamas\u0131 ger\u00e7ekle\u015ftirmi\u015f bulunuyoruz. <\/p>\n<p>\u0130lgilenenlerin faydalanmas\u0131 dile\u011fiyle&#8230;<br \/>\nSonraki yaz\u0131lar\u0131mda g\u00f6r\u00fc\u015fmek \u00fczere&#8230;<br \/>\n\u0130yi \u00e7al\u0131\u015fmalar&#8230;<\/p>\n<p>Not : \u00d6rnek \u00e7al\u0131\u015fmaya a\u015fa\u011f\u0131daki github adresinden eri\u015febilirsiniz.<br \/>\n<a href=\"https:\/\/github.com\/gncyyldz\/SemanticKernel.SignalR.Streaming.Handler.Example\" target=\"_blank\">https:\/\/github.com\/gncyyldz\/SemanticKernel.SignalR.Streaming.Handler.Example<\/a><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>Merhaba, Malumunuz, g\u00fcn\u00fcm\u00fcz, anl\u0131k olarak yaz\u0131l\u0131msal ilkelerin s\u0131n\u0131rlar\u0131n\u0131 a\u015fan e\u015fsiz devrimlerin s\u00f6z konusu oldu\u011fu ilgin\u00e7 bir tarihsel d\u00f6neme \u015fahitlik etmektedir. Art\u0131k yaz\u0131l\u0131mlar, salt algoritmalara dayal\u0131 ve \u00f6nceden tarif edilebilir i\u015fleyi\u015f mant\u0131klar\u0131ndan ziyade kompleks bir&#46;&#46;&#46;<!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":1,"featured_media":27602,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5206,2739,5222,5220],"tags":[5238,5221,5239,5240,5241,5229,5242,639],"class_list":["post-27588","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-net-core","category-llm","category-yapay-zeka-ai","tag-openrouter","tag-semantic-kernel","tag-semantic-kernel-signalr","tag-semantic-kernel-signalr-canli-ai","tag-semantic-kernel-canli-ai-yanitlari","tag-semantic-kernel-history","tag-semantic-kernel-plugin","tag-signalr"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/27588","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/comments?post=27588"}],"version-history":[{"count":9,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/27588\/revisions"}],"predecessor-version":[{"id":27606,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/27588\/revisions\/27606"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/27602"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=27588"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=27588"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=27588"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}