Strona główna / Artykuły

Tworzenie agentów AI na bazie istniejących usług .NET oraz interfejsów API

Zespoły C# mogą udostępniać istniejące usługi .NET jako narzędzia sterowane przez agenty AI poprzez wywoływanie funkcji, z zachowaniem niezbędnych zasad kontekstu, bezpieczeństwa i obserwowalności.

Wydawca: REACTAPP.TOP9 min czytania2060 słów

Źródła: AI Agents Are Changing C# Development: 10 Things Every .NET Developer Should Know

Większość zespołów .NET już posiada kluczowe elementy agenta AI: usługi typowane, API HTTP, mechanizmy autoryzacji, bazy danych oraz zadania w tle. Nowością jest model językowy, który potrafi wybrać, które z tych funkcji uruchomić i w jakiej kolejności, aby osiągnąć cel opisany przez użytkownika prostym językiem. Ten przewodnik przedstawia koncepcje, których potrzebuje programista C#, aby połączyć agenta z istniejącą aplikacją, nie tracąc kontroli nad tym, co faktycznie robi aplikacja.

Klasyczny chatbot realizuje jedną rundę komunikacji: użytkownik zadaje pytanie, a model odpowiada tekstem. Agent natomiast działa w celu osiągnięcia określonego rezultatu. Analizuje, czego wymaga ten cel, wykorzystuje narzędzia do pozyskiwania informacji lub podejmowania działań, sprawdza wyniki i dopiero wtedy zwraca rezultat.

Rozważmy użytkownika, który chce wiedzieć, ile zamówień nadeszło dzisiaj. Chatbot może jedynie zgadywać lub wyjaśniać, jak można to sprawdzić. Agent wyposażony we właściwe narzędzia może uzyskać dane o zamówieniach, przeprowadzić zapytanie dotyczące dzisiejszego dnia, przeanalizować liczby, napisać krótki podsumowanie i dostarczyć je w formie raportu. Różnica praktyczna jest prosta: chatboty generują odpowiedzi, a agenci wykonywają prace.

Narzędzia przekształcają model w coś przydatnego

Sam model językowy nie ma pojęcia o tym, jak działa Twoja aplikacja. Nie może czytać tabeli klientów ani składać zamówień. Narzędzia stanowią most – to nazwane operacje, o które model może prosić. Typowy zestaw narzędzi dla backendu handlowego może wyglądać w ten sposób:

GetCustomer()
CreateOrder()
SearchProducts()
GetWeather()
SendEmail()
QueryDatabase()

Model decyduje, kiedy jest potrzebny któryś z tych elementów; twój kod określa, w jaki sposób ma on działać. W C# narzędzie nie musi być niczym egzotycznym – może to być zwykła metoda asynchroniczna w istniejącym już serwisie, na przykład taka, która ładuje klienta według ID za pomocą serwisu wstrzykiwanego:

public async Task<Customer> GetCustomer(int id)
{
    return await customerService.GetByIdAsync(id);
}

Gdy metoda ta zostanie zarejestrowana jako narzędzie, agent może korzystać z tej funkcjonalności, podczas gdy implementacja pozostaje wewnątrz standardowej warstwy serwisów, objęta tą samą iniekcją zależności, testami i logowaniem co reszta kodu. To właśnie w tym miejscu spotykają się codzienne rozwijanie aplikacji z rozwojem sztucznej inteligencji.

Jak w praktyce wygląda wywoływanie funkcji

Wywoływanie funkcji to mechanizm leżący u podstaw narzędzi. Model sam nic nie wykonuje – wysyła strukturyzowaną prośbę o wywołanie funkcji z określonymi argumentami, a twoja aplikacja dokonuje tego wywołania.

Załóżmy, że użytkownik pyta o stan zamówienia 12345. Wewnętrznie model dochodzi do wniosku, że brakuje mu informacji:

I need order information.

Dlatego prosi aplikację hostującą o wywołanie funkcji, której argumentem jest numer zamówienia:

GetOrderStatus(12345)

Twój kod realizuje tę prośbę, przekazuje wynik z powrotem modelowi, a ten przekształca surowe dane w czytelną odpowiedź. Dla programisty C# powinno to wydawać się znajome: publikujesz niewielką część swojej aplikacji, podobnie jak publiczna API, której użytkownikiem jest model. Jasne nazwy, precyzyjne typy parametrów i dobrze napisane opisy znacznie zwiększają szanse na prawidłowe wywołania.

Ponowne wykorzystanie istniejących API

Wdrożenie agentów nie wymaga przepisywania backendu. Jeśli twoja aplikacja już udostępnia punkty końcowe, mogą one stać się narzędziami agenta. Usługa może już publikować funkcję wyszukiwania klientów:

GET /api/customers/{id}

Oprócz punktów końcowych do odczytu zamówienia, tworzenia zamówień oraz wyświetlania produktów (poniższy przykład łączy te trzy ścieżki w jednej linii; należy je traktować jako oddzielne punkty końcowe):

GET /api/orders/{id}POST /api/ordersGET /api/products

Gdy to już jest gotowe, prośba o znalezienie klienta, który złożył najwięcej zamówień w tym miesiącu, staje się sekwencją, którą agent może samodzielnie zaplanować: pobrać dane klienta, uzyskać informacje o zamówieniach, porównać liczby i poinformować o zwycięzcy. Agent jest po prostu kolejnym klientem korzystającym z funkcjonalności, które już stworzyłeś i zabezpieczyłeś.

Kontekst: daj modelowi to, czego potrzebuje, a nie wszystko, co masz

Załóżmy półgodzinne spotkanie z agentem, podczas którego omawiane są Twoja aplikacja, jej schemat bazy danych, zasady kodowania zespołu, reguły biznesowe, otwarte błędy oraz decyzje podjęte wcześniej w trakcie rozmowy. Aby agent mógł być przydatny, musi śledzić wszystkie te istotne elementy. Dlatego w projektowaniu agentów ciągle pojawiają się kwestie takie jak historia rozmów, zarządzanie kontekstem, pamięć, wyszukiwanie informacji, embeddingi oraz bazy danych wektorowych.

Istnieje pułapka, której łatwo nie zauważyć: większa ilość kontekstu nie oznacza automatycznie lepszych rezultatów. Każdy dodatkowy element kosztuje pieniądze i powoduje opóźnienia, a nieistotne dane osłabiają sygnał, przez co odpowiedzi stają się mniej wiarygodne. Dobry projekt agenta polega na dostarczaniu właściwego kontekstu w momencie, gdy jest on potrzebny – na przykład poprzez pobieranie tylko tych dokumentów, które pasują do bieżącego kroku, zamiast wklejania całej bazy wiedzy do każdego żądania. Aby lepiej zrozumieć różnice pomiędzy tymi mechanizmami pamięci, zapoznaj się z naszym wyjaśnieniem dotyczącym pamięci AI, embeddingów oraz RAG.

Zezwalanie agentom na bezpieczne wyszukiwanie danych

Dostęp do danych sprawia, że agenty stają się szczególnie atrakcyjne dla firm używających .NET. Użytkownik biznesowy może chcieć wiedzieć, które produkty wykazały największy spadek sprzedaży w zeszłym miesiącu. Zamiast tworzyć dedykowany raport na każde takie zapytanie, system oparty na agentach może je zinterpretować, określić, jakie dane są istotne, przeprowadzić zapytanie, przeanalizować wyniki i wyjaśnić, co odkrył.

Ostrzeżenie, które towarzyszy temu rozwiązaniu, jest bezwzględne: nigdy nie udzielaj modelowi nieograniczonego dostępu do swojej bazy danych. Pozwalanie mu na generowanie i wykonywanie dowolnych zapytań SQL może prowadzić do destruktywnych operacji, wycieków danych między różnymi użytkownikami oraz zapytań blokujących tabele. Zamiast tego należy używać wąsko określonych operacji stworzonych specjalnie do tego celu, z walidowanymi parametrami oraz własnymi kontrolami uprawnień:

GetSalesReport()
GetProductSales()
GetCustomerOrders()

Każda z tych metod może zapewnić bezpieczeństwo na poziomie wierszy, ograniczyć rozmiar wyników oraz używać połączenia tylko do odczytu, oferując nieco mniej elastyczności w zamian za dużą przewidywalność.

Agenty nie zastępują architektury

Powszechnym błędem jest przekonanie, że dodanie modelu pozwala zaniedbać podstawy. Jest zupełnie inaczej. Injekcja zależności, projekt SOLID, logowanie, testowanie, autoryzacja, upoważnianie, obsługa błędów, możliwość obserwacji oraz uporządkowanie warstw stają się jeszcze ważniejsze, gdy to komponent niedeterministyczny decyduje o tym, co wywołać.

Korzystnym modelem myślowym jest rozdzielenie ról: agent może zaproponować działanie, ale to twoja aplikacja decyduje, czy to działanie jest dozwolone. Krótko mówiąc, AI decyduje, a twoja aplikacja zarządza. Sprawdzania autoryzacji powinny znajdować się w implementacjach narzędzi i usługach za nimi, a nie w instrukcjach, ponieważ instrukcje to jedynie sugestia dla modelu, a nie obowiązująca zasada.

Ryzyka bezpieczeństwa rosną wraz z każdym narzędziem

Każde narzędzie, które udostępniasz, zwiększa ryzyko wystąpienia błędów. Agent może być w stanie wykonywać takie działania:

Read customer data
Send emails
Create orders
Delete records
Call external APIs

Następują trzy pytania. Co się dzieje, gdy model po prostu popełnia błąd, na przykład usuwając niewłaściwy rekord? Co się dzieje, gdy złośliwy użytkownik próbuje nakłonić agenta do niewłaściwego użycia narzędzia? I co się dzieje, gdy zewnętrzny treść, którą agent odczytuje, np. e-mail lub strona internetowa, zawiera ukryte instrukcje? Ten ostatni scenariusz, zwykle nazywany pośrednią iniekcją promptu, to rzeczywała i dobrze udokumentowana klasa ataków, a nie eksperyment myślowy.

Obrony to głównie metody, które już znasz, stosowane celowo:

  • Autoryzacja i uwierzytelnianie przy każdym wywołaniu narzędzia, skoncentrowane na końcowym użytkowniku, a nie na agencie.
  • Walidacja danych wejściowych wszystkich argumentów dostarczanych przez model.
  • Jasne uprawnienia dla poszczególnych narzędzi, dzięki czemu agent obsługi nie może uzyskać dostępu do funkcji rozliczeniowych.
  • Limity szybkości, aby zapobiec niekontrolowanym pętlom i nadużyciom.
  • Zapisywanie każdego połączenia oraz jego parametrów.
  • Autorizacja przez człowieka przed podjęciem działań wrażliwych lub nieodwracalnych.
  • W przypadku działań o dużym wpływie, takich jak zwroty pieniędzy, usuwanie danych lub wysyłanie wiadomości do klientów, krok potwierdzenia, w którym osoba zatwierdza dane działanie, jest często najtańszym sposobem zapewnienia bezpieczeństwa.

    Obserwowalność w przypadku nieliniowych przepływów żądań

    Konwencjonalna obsługa żądań jest łatwa do śledzenia, ponieważ przebiega w linii prostej:

    Request → Service → Database → Response
    

    Proces realizacji żądania może przechodzić tam i z powrotem pomiędzy modelem, kilkoma narzędziami oraz różnymi backendami, zanim zostanie wydana odpowiedź:

    User
     ↓
    AI Model
     ↓
    Tool
     ↓
    API
     ↓
    Database
     ↓
    AI Model
     ↓
    Another Tool
     ↓
    Final Response
    

    Taka struktura jest znacznie trudniejsza do debugowania. Gdy coś idzie nie tak, należy odpowiedzieć na pytania dotyczące narzędzi, które zostały wywołane, dlaczego model je wybrał, jak długo trwało każde wywołanie, co się nie udało, ile razy model musiał zostać wywołany podczas działania oraz jaki był koszt całej operacji. Każde uruchomienie należy traktować jako ślad z oznaczeniami wywołań modelu i narzędzi, z zapisem argumentów oraz liczby tokenów obok czasu trwania; w przeciwnym razie błędna odpowiedź jest niemal niemożliwa do odtworzenia.

    Dlaczego programiści C# mają przewagę

    Programiści biegle posługujący się C# i .NET nie zaczynają od zera. Interfejsy i klasy, API HTTP, kontenery DI, kod asynchroniczny, bazy danych, mechanizmy autoryzacji, usługi w tle oraz implementacje w chmurze to dokładnie elementy, z których składają się systemy agentowe. Pozostaje krótka lista nowych koncepcji, które budują się na sobie mniej więcej w takiej kolejności:

    LLMs
     ↓
    Prompting
     ↓
    Function Calling
     ↓
    Tools
     ↓
    RAG
     ↓
    Agents
     ↓
    Multi-Agent Systems
    

    Nie ma potrzeby przyswajania tego wszystkiego od razu; zacznij od małych kroków.

    Architektura referencyjna

    Minimalny projekt umieszcza agenta w centrum, a kilka narzędzi łączy się z twoimi usługami, bazą danych oraz zewnętrznym API do wyszukiwania; wyniki wracają następnie przez agenta do użytkownika:

    User
                   ↓
              AI Agent
                   ↓
           ┌───────┼────────┐
           ↓       ↓        ↓
        API Tool DB Tool  Search Tool
           ↓       ↓        ↓
         .NET    Database  External API
           └───────┼────────┘
                   ↓
              Agent Result
                   ↓
                 User
    

    Główną zaletą takiego układu jest to, że agent nigdy nie ma bezpośredniego dostępu do twoich systemów. Każda ścieżka przechodzi przez narzędzie należące do twojej aplikacji .NET, dzięki czemu kontrola dostępu, walidacja i logowanie znajdują się w jednym miejscu, którego obsługiwanie już znasz.

    Praktyczna ścieżka nauki

    Dla programisty C#, który chce przejść na prace związane z sztuczną inteligencją, rozsądny plan postępu wygląda następująco:

    • Zrozumienie modeli językowych LLM. Dowiedz się, jak zachowują się duże modele językowe, w czym są dobre i gdzie zawodzą, np. poprzez pewne, ale błędne odpowiedzi.
  • Zintegruj API modelu. Wywołaj model z aplikacji .NET i obsłuż odpowiedzi, błędy oraz przestoje.
  • Nauka wywoływania funkcji. Pozwól modelowi wysyłać żądania do funkcji w twojej aplikacji i odbierać wyniki.
  • Nauka RAG. Pobieraj treści z własnych dokumentów i danych, aby odpowiedzi opierały się na faktach, na których model nigdy nie był szkoleniowy.
  • Zbuduj małego agenta. Daj mu dwa lub trzy narzędzia i obserwuj, jak planuje działania przy ich użyciu.
  • Zabezpiecz i instrumentuj go. Dodaj uprawnienia, walidację oraz śledzenie przed tym, jak ktokolwiek będzie polegał na agencie.
  • Stwórz coś rzeczywistego. Przejdź od tutoriali do narzędzia, którego faktycznie używają ludzie.
  • Agent obsługi klienta to dobry pierwszy rzeczywisty projekt, ponieważ angażuje wszystkie aspekty. Może wyszukać informacje o klientie, sprawdzić zamówienie, sprawdzić status dostawy, otworzyć ticket i odpowiedzieć:

    Search customer
     ↓
    Check order
     ↓
    Check delivery status
     ↓
    Create support ticket
     ↓
    Respond to customer
    

    Każdy krok odpowiada określonemu narzędziu, a te z efektami ubocznymi są oczywistymi kandydatami na reguły zatwierdzania.

    Główne wnioski

    • Agent to system oprogramowania: model połączony z narzędziami, danymi, logiką aplikacji, zabezpieczeniami i monitorowaniem, a nie zamiennik żadnego z nich.
    • Ujawniaj możliwości poprzez specyficzne, zweryfikowane narzędzia; nigdy nie udzielaj bezpośredniego dostępu do bazy danych ani nieograniczonego dostępu przez API.
    • Zachowuj uprawnienia w kodzie, tak aby aplikacja kontrolowała to, co proponuje agent.
    • Dostosowuj kontekst do każdego kroku, zamiast go maksymalizować.
    • Śledź każdy proces od początku do końca, aby błędy i koszty były zrozumiałe.
  • Istniejące umiejętności w .NET można od razu wykorzystać; realistycznym rezultatem nie jest zastąpienie programistów, lecz znaczne podniesienie produktywności tych, którzy tworzą oprogramowanie z wykorzystaniem AI.