Powrot do bloga
26 lipca 2026
published

Semantic Kernel - po co go używać w aplikacjach .NET

Pierwsze wywołanie modelu AI z poziomu C# to piętnaście linijek z HttpClient. Działa, cieszysz się, wrzucasz na produkcję. Trzy tygodnie później masz klasę...

Okladka: Semantic Kernel - po co go używać w aplikacjach .NET

Pierwsze wywołanie modelu AI z poziomu C# to piętnaście linijek z HttpClient. Działa, cieszysz się, wrzucasz na produkcję. Trzy tygodnie później masz klasę na 400 linii, w której siedzi historia rozmowy, retry, parsowanie odpowiedzi i dwa różne formaty API, bo klient chce testować model z Azure, a wcześniej wszystko stało na OpenAI.

Ten artykuł jest o tym, jak Semantic Kernel rozwiązuje ten bałagan - z konkretnym kodem, z moimi doświadczeniami po kilku miesiącach pracy z tym SDK i z uczciwą odpowiedzią na pytanie, kiedy naprawdę go nie potrzebujesz.

Czytanie: ~15 min

Dlaczego zwykłe wywołanie API przestaje wystarczać

Zapytanie do modelu językowego jest banalnie proste. Wysyłasz tekst, dostajesz tekst. Gdyby aplikacje AI kończyły się na tym jednym zapytaniu, żaden framework nie byłby potrzebny.

Problem zaczyna się przy drugim kroku. Model nie pamięta poprzedniej wiadomości, więc musisz sam trzymać historię rozmowy i doklejać ją do każdego wywołania. Musisz zdecydować, co zrobić, gdy historia przekroczy okno kontekstowe. Musisz obsłużyć sytuację, w której model powinien sięgnąć po dane z Twojej bazy zamiast zgadywać.

A potem przychodzi klient i mówi, że dane nie mogą lecieć do OpenAI, bo prawnicy się nie zgodzili, i całość ma stać na Azure OpenAI w regionie sweden-central. Albo lokalnie na Ollamie. Twoje piętnaście linijek trzeba przepisać.

Każdy, kto buduje aplikację z AI w .NET, pisze mniej więcej ten sam kod. Semantic Kernel to biblioteka Microsoftu, która ten kod już napisała za Ciebie.

Czym Semantic Kernel jest, a czym nie jest

Semantic Kernel nie jest modelem AI. To warstwa pośrednia między Twoją aplikacją a modelem - coś jak Entity Framework, tylko że zamiast baz danych abstrahuje dostawców LLM.

Dla nietechnicznych: wyobraź sobie uniwersalny adapter do gniazdek. Aplikacja mówi „potrzebuję odpowiedzi od AI", a framework dogaduje się z konkretnym modelem. Który to model? To szczegół konfiguracji, nie architektury.

Nazwa bierze się z centralnego obiektu - kernela. To kontener na usługi (połączenia do modeli) i funkcje (kod, który model może wywołać). Rejestrujesz w nim, co masz do dyspozycji, a potem prosisz go o wykonanie zadania.

SDK jest dostępny w C#, Pythonie i Javie, ale to w .NET jest najbardziej dopracowany. Nie dziwne, skoro powstał w Microsofcie i cały ekosystem Azure jest pod niego podpięty.

Pierwszy kernel w dziesięć minut

Zanim wejdę głębiej, postawmy coś działającego. Potrzebujesz .NET 8 albo nowszego i klucza API do dowolnego modelu.

dotnet new console -n MojaPierwszaAI
cd MojaPierwszaAI
dotnet add package Microsoft.SemanticKernel

Najprostsza wersja - pytanie do modelu i odpowiedź w konsoli:

using Microsoft.SemanticKernel;

var builder = Kernel.CreateBuilder();

// Konfiguracja połączenia z modelem
builder.AddAzureOpenAIChatCompletion(
    deploymentName: "gpt-4o-mini",
    endpoint: "https://twoj-zasob.openai.azure.com/",
    apiKey: Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!);

var kernel = builder.Build();

// Zwykłe zapytanie
var odpowiedz = await kernel.InvokePromptAsync(
    "Wyjaśnij w dwóch zdaniach czym jest leasing operacyjny.");

Console.WriteLine(odpowiedz);

Tyle. Trzy linijki konfiguracji, jedna linijka wywołania. Ale prawdziwa wartość zaczyna się, gdy chcesz sterować tym, co model dostaje i co potrafi zrobić.

Prompty w Semantic Kernel są szablonami ze zmiennymi:

var funkcja = kernel.CreateFunctionFromPrompt(
    """
    Jesteś ekspertem od {{$dziedzina}}.
    Odpowiadaj po polsku, maksymalnie w pięciu zdaniach.

    Pytanie: {{$pytanie}}
    """);

var wynik = await kernel.InvokeAsync(funkcja, new KernelArguments
{
    ["dziedzina"] = "rachunkowości",
    ["pytanie"] = "Kiedy środek trwały można zamortyzować jednorazowo?"
});

Wygląda niepozornie, ale przy złożonych promptach z pięcioma zmiennymi i przykładami few-shot ten porządek ratuje życie. Sklejanie stringów w takim scenariuszu kończy się źle.

Zmiana modelu bez przepisywania aplikacji

To jest dla mnie główny powód, dla którego sięgam po Semantic Kernel. Model podmieniam w konfiguracji, reszta kodu zostaje.

Na kursie, który przerobiłem w lipcu, korzystałem z trzech źródeł modeli: Azure AI Foundry, Hugging Face i GitHub Models. Za każdym razem zmieniałem kilka linijek w rejestracji usługi. Logika aplikacji, pluginy, prompty - wszystko zostawało bez zmian.

// Azure OpenAI
builder.AddAzureOpenAIChatCompletion("gpt-4o-mini", endpoint, apiKey);

// OpenAI bezpośrednio
builder.AddOpenAIChatCompletion("gpt-4o-mini", apiKey);

// Lokalny model przez Ollamę
builder.AddOllamaChatCompletion("llama3.2", new Uri("http://localhost:11434"));

W praktyce wygląda to jeszcze lepiej, bo wybór modelu wrzucasz do appsettings.json i przełączasz się bez rekompilacji. Klient chce najpierw przetestować na tanim modelu, a produkcję postawić na mocniejszym? Zmiana jednej wartości w konfiguracji.

Ta abstrakcja ma też drugą stronę. Nie każdy dostawca wspiera te same funkcje - structured outputs, obsługa obrazów czy równoległe wywoływanie narzędzi różnią się między modelami. Semantic Kernel wygładza różnice, ale ich nie usuwa. Przy przesiadce z GPT-4o na lokalną Llamę część rzeczy po prostu przestanie działać i trzeba to sprawdzić testami.

Pluginy, czyli gdy model wywołuje Twój kod

Tu robi się ciekawie. Plugin to zwykła klasa C# z metodami oznaczonymi atrybutem. Model dostaje opis tych metod i sam decyduje, kiedy je wywołać.

using System.ComponentModel;
using Microsoft.SemanticKernel;

public class FakturyPlugin
{
    private readonly IFakturyRepository _repo;

    public FakturyPlugin(IFakturyRepository repo) => _repo = repo;

    [KernelFunction("pobierz_status_faktury")]
    [Description("Zwraca status płatności faktury na podstawie jej numeru.")]
    public async Task<string> PobierzStatusAsync(
        [Description("Numer faktury, np. FV/2026/07/123")] string numer)
    {
        var faktura = await _repo.ZnajdzAsync(numer);

        return faktura is null
            ? "Nie znaleziono faktury o takim numerze."
            : 
quot;Status: {faktura.Status}, termin płatności: {faktura.Termin:d}"; } }

Rejestracja i włączenie automatycznego wywoływania:

kernel.Plugins.AddFromType<FakturyPlugin>("Faktury");

var ustawienia = new AzureOpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var chat = kernel.GetRequiredService<IChatCompletionService>();
var historia = new ChatHistory("Jesteś asystentem działu księgowości.");
historia.AddUserMessage("Czy faktura FV/2026/07/123 została już zapłacona?");

var wynik = await chat.GetChatMessageContentAsync(historia, ustawienia, kernel);
Console.WriteLine(wynik);

Model widzi pytanie o fakturę, zauważa, że ma do dyspozycji funkcję pobierz_status_faktury, wywołuje ją z wyciągniętym z tekstu numerem i buduje odpowiedź na podstawie zwróconych danych. Ty nie piszesz ani jednego if.

Zwróć uwagę na atrybuty [Description]. To nie jest dokumentacja dla programisty - to jest dokumentacja dla modelu. Jeśli opiszesz funkcję niejasno, model będzie ją wywoływał w złych momentach albo wcale. Przy jednym projekcie zmieniłem tylko opis parametru i skuteczność wywołań wskoczyła z „czasem trafia" na „zawsze trafia".

Drugi haczyk: model potrafi wywołać funkcję z halucynowanymi argumentami. Jeśli plugin robi coś nieodwracalnego (wysyła maila, zmienia dane w bazie), zbuduj potwierdzenie po stronie aplikacji zamiast ufać modelowi.

MCP, czyli gotowe narzędzia bez pisania integracji

Model Context Protocol to standard podłączania zewnętrznych narzędzi do aplikacji AI. Semantic Kernel wspiera go natywnie - podpinasz serwer MCP i jego funkcje trafiają do kernela dokładnie tak, jakbyś napisał je sam.

Testowałem dwa scenariusze. Pierwszy: lokalny serwer MCP czytający pliki z dysku. Drugi: zewnętrzny serwer pobierający kursy akcji z giełdy przez Google Finance. W obu przypadkach nie napisałem ani linijki kodu integracji - tylko konfigurację połączenia.

To zmienia rachunek opłacalności. Zamiast pisać własnego klienta do każdego API, sprawdzasz najpierw, czy nie ma gotowego serwera MCP. Dla popularnych usług zwykle jest.

Uwaga bezpieczeństwa, bo to nie jest teoretyczne: podłączając zewnętrzny serwer MCP dajesz obcemu kodowi wpływ na to, co robi Twój agent. Prompt injection przez opis narzędzia to realny wektor ataku. Przy danych firmowych sprawdź, czyj to serwer i co dokładnie robi.

RAG, czyli odpowiedzi na podstawie firmowych dokumentów

Model zna to, na czym go wytrenowano. Nie zna Twojego regulaminu, Twoich procedur ani Twoich umów. RAG (Retrieval Augmented Generation) rozwiązuje to przez podanie odpowiednich fragmentów dokumentów jako kontekstu.

Mechanizm w skrócie:

  1. Dzielisz dokumenty na fragmenty
  2. Zamieniasz fragmenty na wektory (embeddingi) - liczbowe reprezentacje treści
  3. Zapisujesz je w bazie wektorowej
  4. Przy pytaniu wyszukujesz najbardziej pasujące fragmenty
  5. Doklejasz je do prompta i wysyłasz do modelu

Semantic Kernel ma warstwę VectorData z konektorami do Azure AI Search, Qdrant, Redis, Postgresa z pgvector i kilku innych. Interfejs jest wspólny, więc bazę zmieniasz podobnie jak model.

// Wyszukanie fragmentów pasujących do pytania
var wyniki = await kolekcja.SearchAsync(pytanie, top: 4);

var kontekst = string.Join("\n---\n",
    await wyniki.Results.Select(r => r.Record.Tresc).ToListAsync());

var odpowiedz = await kernel.InvokePromptAsync(
    """
    Odpowiadaj wyłącznie na podstawie poniższego kontekstu.
    Jeśli nie ma w nim odpowiedzi, powiedz to wprost.

    Kontekst:
    {{$kontekst}}

    Pytanie: {{$pytanie}}
    """,
    new KernelArguments { ["kontekst"] = kontekst, ["pytanie"] = pytanie });

Największą wartość RAG widać na dokumentach, których nikomu nie chce się czytać. Gruby regulamin, procedury, dokumentacja techniczna. Nagle to wszystko staje się przeszukiwalne w języku naturalnym, a pracownik dostaje odpowiedź w piętnaście sekund zamiast szukać przez pół godziny.

Zastrzeżenie, które powtarzam każdemu klientowi: RAG ogranicza halucynacje, ale ich nie eliminuje. Przy odpowiedziach mających konsekwencje prawne albo finansowe zawsze dokładaj odnośnik do źródła, żeby człowiek mógł zweryfikować.

Agenty i multimodalność - projekt, który mnie przekonał

Najciekawszy projekt, jaki zbudowałem na Semantic Kernel, oceniał zdjęcia z kamer ulicznych. Model dostawał obraz i odpowiadał na dwa pytania: czy na drodze jest korek i czy kamera w ogóle działa poprawnie.

Drugie pytanie okazało się ważniejsze od pierwszego. Kamera zabrudzona, zamarznięta albo obrócona w krzaki wygląda w systemie monitoringu tak samo jak sprawna - dopóki ktoś na nią nie spojrzy. Model wychwytywał to od razu.

Semantic Kernel obsługuje treści multimodalne przez ChatHistory:

var historia = new ChatHistory("""
    Oceniasz zdjęcia z kamer drogowych.
    Zwróć JSON: { "korek": bool, "kamera_sprawna": bool, "uzasadnienie": string }
    """);

historia.AddUserMessage(new ChatMessageContentItemCollection
{
    new TextContent("Oceń sytuację na tym zdjęciu."),
    new ImageContent(bajtyZdjecia, "image/jpeg")
});

var ocena = await chat.GetChatMessageContentAsync(historia, ustawienia, kernel);

Do tego dochodzi Agent Framework wbudowany w SDK - warstwa nad kernelem, która pozwala definiować agenta z własną instrukcją systemową, zestawem pluginów i wątkiem rozmowy. Kilku agentów można spiąć w grupę, w której każdy odpowiada za swój kawałek zadania.

Ważna rzecz o ekosystemie: Microsoft w październiku 2025 ogłosił Microsoft Agent Framework, który łączy Semantic Kernel z AutoGenem. Semantic Kernel dalej żyje i jest wspierany, ale jeśli zaczynasz nowy projekt mocno oparty na agentach, warto sprawdzić, gdzie Microsoft prowadzi ścieżkę migracji. Do prostszych zastosowań SK pozostaje dobrym wyborem.

Telemetria, bo debugowanie agenta bez logów to zgadywanka

Aplikacja z AI potrafi zwrócić bzdurę i nie zostawić śladu, dlaczego. Może prompt był zły. Może model wywołał złą funkcję. Może kontekst z RAG w ogóle nie trafił do zapytania.

Semantic Kernel loguje przez standardowy ILogger i wystawia dane w formacie OpenTelemetry. Podpinasz Aspire Dashboard i widzisz każde wywołanie modelu, każdą wywołaną funkcję, liczbę tokenów i czas odpowiedzi.

builder.Services.AddLogging(c => c
    .AddConsole()
    .SetMinimumLevel(LogLevel.Trace));

Odpalenie dashboardu to jedna komenda w Dockerze i przeglądarka na localhost:18888. Zajęło mi to dziesięć minut, a oszczędziło kilka wieczorów zgadywania, dlaczego agent wybiera złe narzędzie.

Przy zapytaniach po polsku warto patrzeć na tokeny osobno. Polski jest droższy tokenowo od angielskiego, przy dłuższych kontekstach potrafi to zaskoczyć na fakturze.

Kiedy Semantic Kernel się opłaca, a kiedy to przerost formy

Nie każdy projekt z AI potrzebuje frameworka. Sam kilka razy dodałem SK tam, gdzie wystarczyłby zwykły klient HTTP.

Sięgnij po Semantic Kernel, gdy:

  • Jesteś w .NET i chcesz zostać w .NET. To nie jest port z Pythona, tylko biblioteka napisana pod C# z dependency injection, IOptions i całą resztą wzorców, które już znasz
  • Model ma wywoływać Twój kod. Pluginy z automatycznym wywoływaniem funkcji to najmocniejsza część tego SDK
  • Planujesz zmieniać dostawcę modeli albo obsługiwać kilku naraz
  • Budujesz RAG na firmowych danych i siedzisz w Azure. Integracja z Azure AI Search działa bez tańca z bębnem
  • Potrzebujesz telemetrii i logów na poziomie, który przejdzie audyt

Odpuść, gdy:

  • Robisz jedno zapytanie do modelu i wyświetlasz odpowiedź. HttpClient wystarczy, a zależność mniej to zależność mniej
  • Potrzebujesz najnowszej funkcji konkretnego dostawcy w dniu premiery. Abstrakcja zawsze goni API, nigdy go nie wyprzedza
  • Piszesz jednorazowy skrypt. Framework do skryptu to armata na muchę
  • Twój zespół siedzi w Pythonie. Wersja pythonowa jest w porządku, ale ekosystem LangChain czy LlamaIndex ma tam więcej gotowych klocków

Sam wpadłem w pierwszą pułapkę przy prototypie dla klienta. Zainstalowałem SK, skonfigurowałem kernel, a cała funkcjonalność sprowadzała się do jednego prompta zwracającego tekst. Wywaliłem framework, zostały 22 linijki i było czytelniej. Przy kolejnym projekcie - agent z czterema pluginami i RAG na dokumentach - SK zaoszczędził mi kilka dni.

Semantic Kernel na tle alternatyw

Kilka opcji, z którymi miałem realnie do czynienia:

  • LangChain - większa społeczność, więcej integracji, ale dom to Python. Wersja .NET istnieje i jest daleko za oryginałem. Jeśli piszesz w C#, SK jest naturalniejszy
  • Czysty SDK (OpenAI, Anthropic) - pełna kontrola, zero abstrakcji, więcej kodu. Mój wybór przy prostych rzeczach
  • Microsoft.Extensions.AI - lżejsza warstwa od Microsoftu, wspólne interfejsy do modeli bez całej maszynerii pluginów i agentów. Dobry środek, jeśli potrzebujesz tylko abstrakcji nad dostawcą
  • Azure AI Foundry Agent Service - agenty jako usługa w chmurze zamiast biblioteki w Twojej aplikacji. Mniej kodu, mniej kontroli, rachunek za zużycie

Przy projektach w C# domyślnie zaczynam od Semantic Kernel, przy prototypach w Pythonie sięgam po LangChain, a gdy chodzi o jedno zapytanie - po zwykłe SDK.

Od czego zacząć - moja ścieżka

Jeśli chcesz przejść od czytania do robienia, kolejność, która u mnie zadziałała:

  1. Postaw konsolówkę z jednym promptem. Ten pierwszy przykład z artykułu. Pobaw się instrukcją systemową i temperaturą, zobacz jak zmienia się ton odpowiedzi
  2. Dodaj `ChatHistory`. Zrób z tego rozmowę, w której model pamięta poprzednie wiadomości
  3. Napisz pierwszy plugin. Jedna funkcja, coś prostego - pobranie kursu waluty, odczyt pliku, cokolwiek. Włącz FunctionChoiceBehavior.Auto() i zobacz, jak model sam ją wywołuje
  4. Zbuduj mini-RAG. Weź jeden PDF, potnij, wrzuć do bazy wektorowej, zadaj pytanie. To moment, w którym większość ludzi łapie, o co w tym wszystkim chodzi
  5. Podłącz telemetrię. Aspire Dashboard i spojrzenie na to, co naprawdę leci do modelu
  6. Przenieś to do ASP.NET. Kernel rejestrujesz w DI jak każdą inną usługę i masz API zamiast konsolówki

Nie próbuj ogarnąć całego SDK naraz. Ja przerobiłem siedem godzin kursu plus drugie tyle własnej pracy i dalej nie znam wszystkich zakamarków. Zacznij od problemu, który chcesz rozwiązać, i dobieraj klocki.

Zamiast zakończenia

Semantic Kernel nie robi niczego, czego nie dałoby się napisać samemu. Robi za to trzy rzeczy, których pisanie samemu jest stratą czasu: abstrahuje dostawcę modelu, spina Twój kod z decyzjami modelu przez pluginy i daje logi, które pozwalają zrozumieć, co się właściwie stało.

Nie jest idealny. API zmieniało się sporo między wersjami, dokumentacja bywa spóźniona względem kodu, a kierunek rozwoju w stronę Microsoft Agent Framework wprowadza pytanie o migrację. Ale jeśli siedzisz w .NET i chcesz zbudować coś więcej niż wrapper na jedno zapytanie - to najkrótsza droga.

Napisz prostą konsolówkę, dodaj jeden plugin i zobacz, jak model sam decyduje, żeby go wywołać. Ten moment, w którym po raz pierwszy widzisz w logach wywołanie własnej funkcji przez AI, jest zaskakująco satysfakcjonujący.

A jeśli masz w firmie proces, który wygląda na kandydata do takiego rozwiązania, i chcesz zobaczyć działający prototyp zamiast prezentacji - odezwij się przez mwozniczka.net/kontakt. Buduję PoC-e i MVP, które da się przetestować w realnej pracy.