Agenci AI

Wprowadzenie

Wiesz już czym jest sztuczna inteligencja i na czym opiera się jej działanie. Dobór odpowiedniego narzędzia to połowa sukcesu! Do utworzenia kucharza w Twojej restauracji najlepiej nadaje się model językowy: prześlesz mu zdjęcie, wygeneruje Ci przepis. Zaproponuję Ci lepsze rozwiązanie, powiązane z LLM-ami – Agenci AI. 

Skoro mamy narzędzie, z którym “rozmawiamy” zwykłym językiem, sprawmy, aby mogło planować działanie, podejmować decyzje i wykonywać czynności. To właśnie nazywamy agentem. Nie musisz ręcznie przesyłać zdjęcia jednym zapytaniem, przesyłać przepisu i dodatkowo poprawiać błędy, możesz stworzyć agenta, który przetworzy zdjęcie, wygeneruje przepis, wypisze listę składników i dodatkowo weryfikuje swoje działanie. Jest to najwyższy poziom automatyzacji w tworzeniu oprogramowania.

Jak Wyglądają Agenci AI od Kuchni?

Chcesz stworzyć własnego, ale nie wiesz jak? Przed etapem programowania, musisz zrozumieć podstawowe pojęcia wykorzystywane przy tworzeniu agentów. Są to kluczowe elementy, z których każdy agent jest zbudowany:

Prompt: Co Agent Ma Wykonać?

Mówiąc krótko – jest to instrukcja lub polecenie dla sztucznej inteligencji.  

Bardzo dobrze przygotowany prompt to połowa sukcesu, ale źle sformułowane pytanie może stworzyć więcej szkody niż pożytku.  AI musi wiedzieć, jaki cel osiągnąć, wynik musi być jasno zdefiniowany! Dobry prompt jasno określa:

  • Cel: co model ma wykonać
  • Zakres: w jakiej dziedzinie agent wykonuje zadanie
  • Ograniczenia: czego agent nie może zrobić, czego powinien unikać 
  • Zasady jakości: agent musi wiedzieć jak wygląda dobry wynik

Przy tworzeniu agentów, trzeba też wspomnieć o różnych typach promptów. Najważniejszy jest System Prompt, którego agent bezwzględnie przestrzega. Kolejnym poziomem jest User Prompt, który jak nazwa wskazuje, odpowiada za polecenie od użytkownika. Te 2 typy nie muszą się wzajemnie wykluczać, System Prompt definiuje globalne zachowanie, określone reakcje, działania, blokady i schemat odpowiedzi, a User Prompt zwykle określa zadanie do wykonania. Najpierw napisz prompt samemu, następnie wstaw go do LLM-a. Modele językowe bardzo dobrze przygotowują prompty, co pozwoli Tobie ujednolicić je oraz poprawić ich jakość. Zgłaszaj modelowi problemy, które napotkasz podczas tworzenia agenta, aby mógł usprawnić prompt. 

Po przesłaniu danych do AI, tekst zamieniany jest na tokeny. Token to podstawowa jednostka tekstu, któremu odpowiada jakaś liczba, ponieważ komputer lepiej rozumie liczby niż słowa. Korzystając z LLM-ów, płacisz za przesłane tokeny. Działa to też w drugą stronę, model zwraca je tą samą drogą. Im więcej tokenów, tym powstanie lepszy tekst, ale więcej zapłacimy. Nie bój się jednak, że przekroczysz limit: dobry model przetworzy nawet 32 tysiące słów. Kolejny raz powtórzę – SI nie rozumie słów, tylko dobrze przewiduje na ich podstawie.

Dobry prompt będzie długi, z wieloma przykładami, ograniczeniami, czasami nawet schematami. Pamiętaj! Musisz precyzyjnie określić rolę i zakres odpowiedzialności. Podczas  tworzenia książki kucharskiej, kluczowe jest aby zaznaczyć agentowi kontekst: „Jesteś kucharzem w ekskluzywnej restauracji” albo “Zajmujesz się przygotowaniem włoskiego jedzenia”.  Bez tego model operuje w próżni, przez co odpowiedź może być nie trafiona. 

Dobry prompt pozwala zapobiec też zjawisku “halucynacji”. Jeżeli model otrzyma dobre przykłady, odpowiedni zakres i ograniczenia, zredukujemy jego możliwość do wytwarzania elementów nie powiązanych z naszym zapytaniem.

Przykład złego promptu: 

“Napisz przepis na makaron z kurczakiem.” 

Agent bez problemu wykona przedstawione mu zadanie, ale efekt będzie bardzo ogólny. Otrzymany przepis będzie poprawny stylistycznie, ale mało użyteczny w praktyce. Nie ma tutaj informacji o ograniczeniach, strukturze wyjściowej oraz kontekstu – model nie wie, czy jest kucharzem, kelnerem, czy pilotem samolotu. 

Przykład dobrego promptu:

“Wygeneruj przepis spełniający poniższe warunki:

  1. Danie: makaron z kurczakiem
  2. Liczba porcji: 2
  3. Maksymalny koszt składników: 30 zł (rynek PL)
  4. Czas przygotowania: ≤ 30 minut
  5. Styl: kuchnia śródziemnomorska
  6. Każdy składnik podaj w gramach lub ml (bez „szczypty”)
  7. Podaj makroskładniki na porcję (kalorie, białko, tłuszcz, węglowodany)
  8. Zwróć wynik w postaci listy kroków

Tak sformułowane polecenie precyzuje zakres zadania, wprowadza ograniczenia i strukturę odpowiedzi. Model wie co ma wykonać, ale też w jakich ramach może się poruszać, jak ma wyglądać rezultat. Różnica między dobrym i złym promptem nie odnosi się do działania, ale do kontroli rezultatu. Dobry prompt zredukuje przypadkowość i zwiększy użyteczność odpowiedzi.

State: Pamięć Krótkotrwała

Pomiędzy wierzchołkami trzeba przekazywać dane. Musisz utworzyć schemat, według którego wierzchołki komunikują się ze sobą, kiedy node zakończy swoje działanie, musi przekazać uzyskane dane do kolejnego. Możesz przechowywać wiele różnych stanów, w zależności od potrzeb. Musisz tylko pamiętać, aby było to spójne. 

Jest to niestety “pamięć krótkotrwała”, ponieważ istnieje tylko w jednym wykonaniu agenta. Zapytasz budowanego agenta o przepis, na podstawie poprzedniego zdjęcia, co się stanie? Agent odpowie Ci, że on nie ma żadnego zdjęcia. 

W poniższym przykładzie przedstawiłem bardzo prosty State, w którym agent ma 2 tryby działania oraz informację o tym, że przesłany został obraz.

@dataclass
class State(MessagesState):
  mode: Literal["analyse_image”, “extract_recipe”]
  has_image: Boolean

Node: Sposób Działania Agenta

Programowo, agenci działają według grafu. Wierzchołki (node) zawierają działanie agenta w konkretnym kroku. Można tutaj wywołać model językowy, zapisać konwersację do pliku, dokonać obliczeń – ograniczają Cię możliwości komputera. Musisz zapamiętać, że w wierzchołku agent coś wykonuje.

W przykładzie przedstawiłem prosty wierzchołek, w którym agent odwołuje się do modelu językowego. System Prompt definiuje jego zachowanie.

def example_node(state: State):

    message = state["messages"][-1]

    SYSTEM_PROMPT = "You are a helpful chef assistant. Answer user questions”

    system = SystemMessage(SYSTEM_PROMPT)
    response = ai_model.invoke([system, message])
    return {"messages": [response]}

Edge: Logika Działania Agenta

Drugim elementem grafu są krawędzie (edge). Ich zadaniem jest zapewnienie przejść pomiędzy wierzchołkami. Mogą być proste, ale też warunkowe. W warunkowym przejściu, agent ma wiele ścieżek, z których (zwykle) wybierze jedną, na podstawie jakiegoś kryterium. Na przykładzie pomocnika gotowania, agent może wybrać ścieżkę analizującą zdjęcie, ponieważ dostał zdjęcie, albo przygotować przepis, jeżeli zobaczy tekst. Pamiętaj! Ważne jest, aby graf był prosty, dodatkowe komplikacje w grafie źle wpływają na działanie agenta.

O to prosta krawędź, która pomoże Ci zapoznać się z tym mechanizmem. Agent sprawdza, czy otrzymał zdjęcie i wybierany jest odpowiedni Node.

def edge_router(state: State) -> Literal["analyse_image”, “extract_recipe”]:
    if state[“has_image”]:
        return “analyse_image”
    return “extract_recipe”

Memory, Czyli Jak Agent Ma Zapamiętać Użytkownika

Musimy więc zapewnić pamięć długotrwałą, która pozwoli zapamiętać konwersacje. Jest na to wiele sposobów, najprostszy pozwala grupować wywołania przez użytkowników. Kolejnymi, które warto wyróżnić to baza danych, gdzie przechowuje się poszczególne konwersacje, ale też proste pliki tekstowe, w których może zostać zapisane podsumowanie konwersacji. 

Ważne jest aby poukładać dane tak, aby rozróżnić konwersacje, użytkowników i pamiętać o tym aby przedstawić te dane agentowi. Potrzebne jest narzędzie, dzięki któremu model np. zapamięta, że użytkownik jest uczulony na orzechy i będzie je odradzał użytkownikowi. 

Korzystając z LangGraph, mamy do dyspozycji obiekt MemorySaver(), który zachowa stan agenta pomiędzy wywołaniami. Historia zostaje zapisana w osobnych wątkach, co pozwala korzystać wielu użytkownikom z agenta jednocześnie. 

Klasycznym podejściem jest zapisanie historii w bazie danych. Utworzenie tabel wymaga więcej pracy od programisty, ale daje więcej kontroli nad przekazywanymi danymi. Po każdym kroku, musimy zapisać stan w bazie danych, a przy kolejnych wykonaniach pobierać je.

Unikalnym podejściem, które stosuje się przy pracy z agentami jest RAG (Retrieval Augmented Generation). Nie jest to klasyczna baza danych, ale system wyszukiwania wiedzy. Model przeszukuje dodatkowe materiały, dopiero potem generuje odpowiedź. Jako programista, masz możliwość kontroli jakie dokumenty są przekazywane do modelu.

Tools: Podstawa Autonomiczności

Ostatnim ważnym pojęciem są narzędzia, wykorzystywane przez agentów. Są to po prostu funkcje z opisem, które agent może wykorzystać. Agent przeanalizuje jakie dane są potrzebne, wywnioskuje z opisu co funkcja robi i może ją samodzielnie wykonać. Idąc dalej, funkcja zwraca wynik, który agent podsumowuje. Taka funkcja pozwala agentowi realnie wpłynąć na otoczenie. Jakie to ma znaczenie dla Ciebie?
Agent samodzielnie może podjąć decyzję o analizie zdjęcia albo wygenerowaniu składników. Otrzymał zdjęcie – uruchom narzędzie do jego analizy. Użytkownik chce zapisać przepis – uruchom narzędzie do zapisu do pliku. Utwórz takie narzędzia, aby były maksymalnie proste i wykonywały jedną czynność. 

W poprzednim rozdziale, MVP zakładało analizowanie zdjęć z lodówki i tworzenie z nich przepisów. W poniższym przykładzie, kucharz, który otrzyma to narzędzie, będzie mógł to zadanie zrealizować.

@tool 
def tool_image(query: str, image_url: str) -> str:
    """Parses image with LLM.
    """

    SYSTEM_PROMPT = "You are a chef, when user uploads image derive it's recipe"

    user_message = query
    content = [
        {"type": "text", "text": user_message},
        {"type": "image_url", "image_url": {"url": image_url}},
    ]
    messages = [SystemMessage(SYSTEM_PROMPT), HumanMessage(content=content)]

    response = llm.invoke(messages)
    return response.content

Narzędzia Wykorzystywane do Tworzenia Agentów

Chcesz już zaprogramować agenta? Jednym z najlepszych narzędzi do tego jest LangGraph. Jest to otwarto źródłowe rozwiązanie do tworzenia agentów AI. Masz gotowy zestaw narzędzi do tworzenia wierzchołków, krawędzi, narzędzi. Możesz wykorzystać gotowe struktury danych dla stanów i pamięci, albo rozbudować je i dostosować do swoich potrzeb. LangGraph oferuje swoje biblioteki w języku Python i TypeScript/JavaScript.

Warto wspomnieć o LangChain – rozwiązaniu również stosowanym do tworzenia agentów. LangChain buduje agentów, którzy pracują krok po kroku, w przeciwieństwie do LangGraph, nie ma tutaj możliwości tworzenia warunków, logiki lub pętli. LangChain jest prostszy w wykorzystaniu, ale ma mniejsze możliwości, świetnie nadaje się do tworzenia prostych, nieskomplikowanych agentów.

Co Wyróżnia LangGraph?

Tworząc agenta w tym narzędziu, budujemy jego proces decyzyjny poprzez graf. Klasyczne podejście: Prompt -> LLM -> Wynik nie pozwala agentowi samemu podejmować decyzje, wymaga stworzenia pętli działania i nie mamy kontroli nad działaniem agenta. Dzięki podejściu grafowym, możemy wprowadzić warunki, zwiększa odporność na błędy i agent ma większą autonomiczność.

Chain: Najprostszy Agent

Najprostszy możliwy agent, są to węzły połączone sekwencyjnie – wynik jednego węzła jest wejściem dla kolejnego. Każdy node ma wykonać jedno zadanie. Jest to po prostu ciąg wywołań, przykładowy agent może: 

  • Przeanalizować obraz z daniem
  • Utworzyć przepis z wyciągniętych składników
  • Zapisać dane w pliku
  • Przesłać je na serwer
  • Streścić wszystko użytkownikowi

Działa to, jako jeden ciąg wywołań, zawsze w tej samej kolejności. Jeżeli błąd zajdzie w jednym ogniwie, reszta łańcucha się wysypie. Agent typu Chain to najlepszy punkt startowy dla Twojego agenta.

Router: Agent Sam Podejmuje Decyzję

Wzorzec, w którym jeden wierzchołek pełni rolę dyspozytora. Otrzymuje dane na wejściu i musi zdecydować, do którego węzła przekazać zadanie do wyspecjalizowanych węzłów. Tworzą się niezależne gałęzie w grafie, czasami z różnymi odpowiedziami. Nie ma tutaj zapętlenia, jeden w węzeł wybiera liniową ścieżkę.

Na przykładzie agenta zarządzającego przepisami, możemy wyróżnić 3 ścieżki:

  • Przeanalizuj zdjęcie
  • Utwórz przepis
  • Opisz składniki z przepisu

Na wejściu, router może otrzymać zdjęcie, składniki albo przepis. Na tej podstawie, zdecyduje, gdzie przekierować: 

  • otrzyma zdjęcie, znaczy że musi zostać przeanalizowane.
  • otrzyma przepis, jego zadaniem jest wypisać składniki
  • otrzyma składniki, musi stworzyć przepis

SubGraph: Agent w Agencie

Podejście podobne do funkcji czy obiektów z klasycznego programowania. Tworzymy izolowany graf, który z zewnątrz wygląda jak jeden, pojedynczy węzeł. Główny graf nie wie nic o jego wewnętrznej strukturze, wywołuje go i oczekuje na jego zakończenie. To rozwiązanie pozwala nam łatwo je podmieniać, używać w różnych miejscach, czy testować niezależnie przed wykorzystaniem w grafie głównym. 

Odnosząc to do kuchni, SubGraph może być jej sekcją, np. cukiernią, której główny kucharz zleca wykonanie ciasta. Kucharza nie interesuje, jak wewnątrz funkcjonuje cukiernia, tylko czy upiecze deser.

ReAct: Pierwszy Krok do Niezależności

Agent analizuje przedstawioną mu sytuację i podejmuje odpowiednie działanie (np. uruchamiając odpowiednie narzędzie). Na podstawie swojego działania, ponownie je analizuje i wykonuje, do momentu gdy uznał problem za rozwiązany. Zwykle, temu rodzajowi agentów przypisujemy dobrze opisane narzędzia np.:

  • Zmień składniki na przepis
  • Wyciągnij składniki ze zdjęcia

Jest to pierwszy krok do pełnej automatyzacji, autonomiczny kucharz nie potrzebuje algorytmu do działania, a sam eksperymentuje.

Podsumowanie

Dobór odpowiednich modeli i tworzenie agentów pozwoli unowocześnić aplikację. Wiesz, które rozwiązania są odpowiednie, a których lepiej unikać na początku działania aplikacji. Twoja książka już nie tylko przedstawia przepisy, ale dzięki agentowi może je aktywnie tworzyć. 

Wiesz już z jakiego narzędzia powinieneś skorzystać podczas tworzenia agenta. Rozumiesz podstawowe zagadnienia z nimi powiązane, musisz tylko umiejętnie układać klocki. W kolejnym rozdziale przejdziemy do ulepszenia wyglądu Twojego MVP. Kiedy stworzone przez Ciebie rozwiązanie działa i spełnia założenia, musi być jeszcze estetyczna, dostosowana do różnych użytkowników i urządzeń i wygodna w użyciu. 

Tworząc jednak piękną salę (stronę internetową), nie możesz zapomnieć o kucharzach (agentach AI) i kuchni (serwerze). Kolejny rozdział naprowadzi Cię na to, jak powinna funkcjonować restauracja jako całość (aplikacja internetowa).

Materiały

Materiały do samodzielnej nauki związane z tym tematem:

  1. Najlepsze praktyki dotyczące bezpieczeństwa kluczy API
  2. Jak mogę zabezpieczyć moje konta OpenAI?
  3. Jak działają agenci AI (logika działania agentów AI)
  4. Różnica między LangChain a LangGraph
  5. Przykłady dobrych promptów
  6. Jak pisać dobre prompty
  7. Dokumentacja LangChain
  8. Podstawy LangGraph
  9. Samouczek: korzystanie z LangSmith
  10. Przewodnik po tworzeniu agenta AI
  11. Kompletny kurs LangGraph dla początkujących – złożeni agenci AI w Pythonie
  12. Czym są agenci AI?