Generowanie treści za pomocą modeli AI

Sercem generatywnej AI są modele AI. Dwa najbardziej znane przykłady modeli generatywnych to duże modele językowe (LLM) i modele generowania obrazów. Te modele przyjmują dane wejściowe, tzw. prompt (najczęściej tekst, obraz lub ich kombinację), a na ich podstawie generują tekst, obraz, a nawet dźwięk lub film.

Wyniki działania tych modeli mogą być zaskakująco przekonujące: duże modele językowe generują tekst, który wygląda tak, jakby został napisany przez człowieka, a modele do generowania obrazów mogą tworzyć obrazy bardzo zbliżone do prawdziwych zdjęć lub dzieł sztuki stworzonych przez ludzi.

Modele LLM sprawdzają się też w przypadku zadań innych niż proste generowanie tekstu:

  • pisanie programów komputerowych;
  • planowanie podzadań, które są wymagane do wykonania większego zadania;
  • porządkowanie nieuporządkowanych danych;
  • interpretowanie i wyodrębnianie danych z korpusu tekstowego;
  • Wykonywanie automatycznych czynności na podstawie ich tekstu opisowego.

Dostępnych jest wiele modeli od różnych dostawców. Każdy model ma swoje mocne i słabe strony. Jeden model może się świetnie sprawdzać w jednym zadaniu, ale gorzej w innym. Aplikacje korzystające z generatywnej AI często mogą korzystać z kilku różnych modeli w zależności od wykonywanego zadania.

Jako deweloper aplikacji zwykle nie wchodzisz w bezpośrednią interakcję z modelami generatywnej AI, ale korzystasz z usług dostępnych jako interfejsy API internetowe. Chociaż te usługi często mają podobne funkcje, wszystkie udostępniają je za pomocą różnych i niezgodnych ze sobą interfejsów API. Jeśli chcesz korzystać z wielu usług modelu, musisz używać ich zastrzeżonych pakietów SDK, które mogą być ze sobą niekompatybilne. Jeśli chcesz przejść z jednego modelu na najnowszy i najbardziej wydajny, konieczne może być ponowne tworzenie integracji.

Genkit rozwiązuje to wyzwanie, udostępniając jeden interfejs, który abstrahuje szczegóły dostępu do dowolnej usługi modelu generatywnej AI. Dostępne są już też różne wstępnie zaimplementowane rozwiązania. Tworzenie aplikacji opartej na AI za pomocą Genkit upraszcza proces tworzenia pierwszego wywołania generatywnej AI i ułatwia łączenie wielu modeli lub zastępowanie jednego modelu innym w miarę pojawiania się nowych modeli.

Zanim zaczniesz

Jeśli chcesz uruchomić przykłady kodu na tej stronie, najpierw wykonaj czynności opisane w przewodniku dla początkujących. We wszystkich przykładach zakładamy, że masz już zainstalowaną bibliotekę Genkit jako zależność w projekcie.

Modele obsługiwane przez Genkit

Genkit został zaprojektowany tak, aby był na tyle elastyczny, aby można było używać go z dowolną usługą generatywnej AI. Jej podstawowe biblioteki definiują wspólny interfejs do pracy z modelami, a wtyczki modela definiują szczegóły implementacji do pracy z konkretnym modelem i jego interfejsem API.

Zespół Genkit obsługuje wtyczki do pracy z modelami udostępnianymi przez Vertex AI, Google Generative AI i Ollama:

Wczytywanie i konfigurowanie wtyczek modeli

Zanim zaczniesz generować treści za pomocą Genkit, musisz załadować i skonfigurować wtyczkę modelu. Jeśli korzystasz z tego przewodnika po zapoznaniu się z poprzednim, masz już to za sobą. W przeciwnym razie zapoznaj się z poradnikiem Pierwsze kroki lub dokumentacją poszczególnych wtyczek i wykonaj podane w nich instrukcje.

Funkcja genkit.Generate()

W Genkit głównym interfejsem, za pomocą którego możesz wchodzić w interakcje z modelami generatywnej AI, jest funkcja genkit.Generate().

Najprostsze wywołanie funkcji genkit.Generate() określa model, którego chcesz użyć, oraz prompt tekstowy:

import (
    "context"
    "log"

    "github.com/firebase/genkit/go/ai"
    "github.com/firebase/genkit/go/genkit"
    "github.com/firebase/genkit/go/plugins/googlegenai"
)

func main() {
    ctx := context.Background()

    g, err := genkit.Init(ctx,
        genkit.WithPlugins(&googlegenai.GoogleAI{}),
        genkit.WithDefaultModel("googleai/gemini-2.0-flash"),
    )
    if err != nil {
        log.Fatal(err)
    }

    resp, err := genkit.Generate(ctx, g,
        ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    )
    if err != nil {
        log.Fatal(err)
    }

    log.Println(resp.Text())
}

Po wykonaniu tego krótkiego przykładu zostanie wydrukowana pewna ilość informacji debugowania, a następnie dane zwrócone przez wywołanie genkit.Generate(), które zwykle będą zawierać tekst Markdown, jak w tym przykładzie:

## The Blackheart's Bounty

**A hearty stew of slow-cooked beef, spiced with rum and molasses, served in a
hollowed-out cannonball with a side of crusty bread and a dollop of tangy
pineapple salsa.**

**Description:** This dish is a tribute to the hearty meals enjoyed by pirates
on the high seas. The beef is tender and flavorful, infused with the warm spices
of rum and molasses. The pineapple salsa adds a touch of sweetness and acidity,
balancing the richness of the stew. The cannonball serving vessel adds a fun and
thematic touch, making this dish a perfect choice for any pirate-themed
adventure.

Uruchom ponownie skrypt, aby uzyskać inny wynik.

Poprzedni przykład kodu wysłał żądanie generowania do domyślnego modelu, który został określony podczas konfigurowania instancji Genkit.

Możesz też określić model w pojedynczym wywołaniu funkcji genkit.Generate():

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-pro"),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)

Identyfikator ciągu znaków modelu ma postać providerid/modelid, gdzie identyfikator dostawcy (w tym przypadku googleai) identyfikuje wtyczkę, a identyfikator modelu to identyfikator ciągu znaków specyficzny dla wtyczki dla konkretnej wersji modelu.

Te przykłady pokazują też ważną kwestię: gdy używasz funkcji genkit.Generate() do wywoływania modeli generatywnej AI, zmiana modelu, którego chcesz użyć, polega na przekazaniu innej wartości parametrowi model. Korzystając z pakietu genkit.Generate() zamiast pakietu SDK do natywnych modeli, zyskujesz elastyczność, która ułatwi Ci używanie w aplikacji różnych modeli i ich zmianę w przyszłości.

Do tej pory pokazaliśmy tylko przykłady najprostszych wywołań genkit.Generate(). genkit.Generate() udostępnia też interfejs do bardziej zaawansowanych interakcji z modelami generatywnymi, co opiszemy w następnych sekcjach.

Prompty systemowe

Niektóre modele obsługują prompt systemowy, który zawiera instrukcje dotyczące tego, jak model ma odpowiadać na wiadomości od użytkownika. Za pomocą prompta systemowego możesz określić cechy, takie jak persona, którą model ma przyjąć, ton odpowiedzi i format odpowiedzi.

Jeśli model, którego używasz, obsługuje prompty systemowe, możesz użyć opcji WithSystem():

resp, err := genkit.Generate(ctx, g,
    ai.WithSystem("You are a food industry marketing consultant."),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)

W przypadku modeli, które nie obsługują promptów systemowych, WithSystem() symuluje je, modyfikując żądanie tak, aby wyglądało jak prompt systemowy.

Parametry modelu

Funkcja genkit.Generate() przyjmuje opcję WithConfig(), za pomocą której możesz określić opcjonalne ustawienia kontrolujące sposób generowania treści przez model:

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    ai.WithConfig(&googlegenai.GeminiConfig{
        MaxOutputTokens: 500,
        StopSequences:   ["<end>", "<fin>"],
        Temperature:     0.5,
        TopP:            0.4,
        TopK:            50,
    }),
)

Dokładne parametry, które są obsługiwane, zależą od modelu i od API. Parametry w poprzednim przykładzie są jednak wspólne dla niemal każdego modelu. Oto wyjaśnienie tych parametrów:

Parametry kontrolujące długość danych wyjściowych

MaxOutputTokens

Duże modele językowe działają na jednostkach zwanych tokenami. Token zwykle, ale niekoniecznie, jest mapowany na określoną sekwencję znaków. Gdy przekazujesz prompt do modelu, jednym z pierwszych kroków jest znakowanie promptu w postaci sekwencji tokenów. Następnie LLM generuje sekwencję tokenów na podstawie pofragmentowanego wejścia. Na koniec sekwencja tokenów jest ponownie konwertowana na tekst, który jest wyjściem.

Parametr maksymalna liczba tokenów wyjściowych określa maksymalną liczbę tokenów generowanych za pomocą LLM. Każdy model może używać innego tokenizera, ale dobrą zasadą jest uznawanie, że jedno słowo angielskie składa się z 2–4 tokenów.

Jak już wspomnieliśmy, niektóre tokeny mogą nie być mapowane na sekwencje znaków. Przykładem może być token wskazujący koniec sekwencji: gdy LLM wygeneruje ten token, przestaje generować kolejne. Dlatego może się zdarzyć, że LLM wygeneruje mniej tokenów niż maksymalną liczbę, ponieważ wygenerował token „stop”.

StopSequences

Ten parametr umożliwia ustawienie tokenów lub sekwencji tokenów, które po wygenerowaniu wskazują koniec danych wyjściowych LLM. Prawidłowe wartości, których należy tu używać, zależą od sposobu trenowania modelu i zwykle są ustawiane przez wtyczkę modelu. Jeśli jednak model wygenerował inną sekwencję znaków stop, możesz ją tutaj podać.

Pamiętaj, że określasz ciągi znaków, a nie tokeny. W większości przypadków podajesz sekwencję znaków, którą model tokenizera mapuje na pojedynczy token.

Parametry, które kontrolują „kreatywność”

Parametry temperatura, górny pgórny k określają, jak „kreatywny” ma być model. W tej sekcji znajdziesz krótkie wyjaśnienia znaczenia tych parametrów, ale ważniejsze jest to, że służą one do dostosowywania charakteru danych wyjściowych LLM. Ich optymalne wartości zależą od Twoich celów i preferencji i najprawdopodobniej można je znaleźć tylko przez eksperymentowanie.

Temperatura

LLM to maszyny do przewidywania tokenów. W przypadku danej sekwencji tokenów (np. prompta) LLM przewiduje dla każdego tokena w swoim słowniku prawdopodobieństwo, że token będzie następny w sekwencji. Temperatura to współczynnik skalowania, przez który dzieli się te prognozy przed ich znormalizowaniem do prawdopodobieństwa w zakresie od 0 do 1.

Niska wartość temperatury (w zakresie od 0,0 do 1,0) zwiększa różnicę w prawdopodobieństwo między tokenami, przez co model będzie jeszcze mniej skłonny do wygenerowania tokenu, który został już oceniony jako mało prawdopodobny. Jest on często postrzegany jako mniej kreatywny. Chociaż 0,0 nie jest technicznie prawidłową wartością, wiele modeli traktuje ją jako wskazanie, że model powinien działać deterministycznie i uwzględniać tylko jeden najbardziej prawdopodobny token.

Wysokie wartości temperatury (czyli większe niż 1,0) powodują kompresję różnic w prawdopodobieństwoach między tokenami, co zwiększa prawdopodobieństwo wygenerowania przez model tokenów, które wcześniej zostały ocenione jako mało prawdopodobne. Jest ono często postrzegane jako bardziej kreatywne. Niektóre interfejsy API modeli narzucają maksymalną temperaturę, często 2,0.

TopP

Top-p to wartość z zakresu od 0,0 do 1,0, która określa liczbę możliwych tokenów, które model ma wziąć pod uwagę, przez określenie skumulowanego prawdopodobieństwa tokenów. Na przykład wartość 1,0 oznacza, że należy wziąć pod uwagę wszystkie możliwe tokeny (ale nadal uwzględnić prawdopodobieństwo każdego z nich). Wartość 0,4 oznacza uwzględnianie tylko najbardziej prawdopodobnych tokenów, których prawdopodobieństwa dają w sumie 0,4, oraz wykluczanie pozostałych tokenów.

TopK

Top-K to wartość całkowita, która również kontroluje liczbę możliwych tokenów, które model ma wziąć pod uwagę, ale tym razem poprzez jawne określenie maksymalnej liczby tokenów. Podanie wartości 1 oznacza, że model powinien zachowywać się deterministycznie.

Eksperymentowanie z parametrami modelu

Korzystając z interfejsu dla programistów, możesz eksperymentować z wpływem tych parametrów na dane wyjściowe generowane przez różne kombinacje modeli i promptów. Uruchom interfejs programisty za pomocą polecenia genkit start. Automatycznie wczyta on wszystkie modele zdefiniowane przez wtyczki skonfigurowane w projekcie. Możesz szybko wypróbowywać różne prompty i wartości konfiguracji bez konieczności wielokrotnego wprowadzania tych zmian w kodzie.

Parowanie modelu z konfiguracją

Ponieważ każdy dostawca lub nawet konkretny model może mieć własny schemat konfiguracji lub wymagać określonych ustawień, ustawienie oddzielnych opcji za pomocą parametrów WithModelName() i WithConfig() może być obarczone błędami, ponieważ te ostatnie nie są silnie powiązane z pierwszymi.

Aby sparować model z konfiguracją, możesz utworzyć odwołanie do modelu, które możesz przekazać do wywołania generate:

model := googlegenai.GoogleAIModelRef("gemini-2.0-flash", &googlegenai.GeminiConfig{
    MaxOutputTokens: 500,
    StopSequences:   ["<end>", "<fin>"],
    Temperature:     0.5,
    TopP:            0.4,
    TopK:            50,
})

resp, err := genkit.Generate(ctx, g,
    ai.WithModel(model),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)
if err != nil {
    log.Fatal(err)
}

Konstruktor odwołania do modelu zapewni, że zostanie podany prawidłowy typ konfiguracji, co może zmniejszyć liczbę niezgodności.

Dane wyjściowe uporządkowane

Gdy używasz generatywnej AI jako komponentu w aplikacji, często chcesz uzyskać dane w innym formacie niż zwykły tekst. Nawet jeśli generujesz tylko treści do wyświetlenia użytkownikowi, możesz skorzystać z wyjścia ustrukturyzowanego, aby prezentować je w bardziej atrakcyjny sposób. W przypadku bardziej zaawansowanych zastosowań generatywnej AI, takich jak programowe wykorzystanie danych wyjściowych modelu lub podawanie danych wyjściowych jednego modelu innemu, konieczne jest uporządkowanie danych wyjściowych.

W Genkit możesz poprosić o uporządkowane dane wyjściowe z modelu, podając typ danych wyjściowych w wywołaniu funkcji genkit.Generate():

type MenuItem struct {
    Name        string   `json:"name"`
    Description string   `json:"description"`
    Calories    int      `json:"calories"`
    Allergens   []string `json:"allergens"`
}

resp, err := genkit.Generate(ctx, g,
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    ai.WithOutputType(MenuItem{}),
)
if err != nil {
  log.Fatal(err) // One possible error is that the response does not conform to the type.
}

Typy danych wyjściowych modelu są określane jako schemat JSON za pomocą pakietu invopop/jsonschema. Umożliwia to sprawdzanie typów w czasie wykonywania, co pozwala zniwelować różnice między statyczną typizacją Go a nieprzewidywalnymi wynikami modeli generatywnej AI. System ten umożliwia pisanie kodu, który może polegać na tym, że wywołanie generate zawsze zwróci dane wyjściowe zgodne z Twoimi typami Go.

Gdy określisz typ wyjściowy w parametrye genkit.Generate(), Genkit wykona kilka czynności w tle:

  • uzupełnia prompt o dodatkowe wskazówki dotyczące wybranego formatu wyjściowego. Ma to też ten efekt uboczny, że modelowi podajesz, jakie treści chcesz wygenerować (np. nie tylko sugerujesz pozycję menu, ale też generujesz opis, listę alergenów itp.).
  • Sprawdza, czy dane wyjściowe są zgodne ze schematem.
  • Przekształca dane wyjściowe modelu w typ Go.

Aby uzyskać ustrukturyzowany wynik po pomyślnym wywołaniu metody generate, wywołaj Output() w odpowiedzi modelu z pustą wartością typu:

var item MenuItem
if err := resp.Output(&item); err != nil {
    log.Fatalf(err)
}

log.Printf("%s (%d calories, %d allergens): %s\n",
    item.Name, item.Calories, len(item.Allergens), item.Description)

Możesz też użyć genkit.GenerateData(), aby skrócić wywołanie:

item, resp, err := genkit.GenerateData[MenuItem](ctx, g,
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)
if err != nil {
  log.Fatal(err)
}

log.Printf("%s (%d calories, %d allergens): %s\n",
    item.Name, item.Calories, len(item.Allergens), item.Description)

Ta funkcja wymaga parametru typu wyjścia, ale przed zwróceniem wartości automatycznie ustawia opcję WithOutputType() i wywołuje funkcję resp.Output().

Obsługa błędów

W poprzednim przykładzie wywołanie funkcji genkit.Generate() może spowodować błąd. Jeden z możliwych błędów może wystąpić, gdy model nie wygeneruje danych wyjściowych zgodnych ze schematem. Najlepsza strategia postępowania w przypadku takich błędów zależy od konkretnego przypadku użycia, ale poniżej znajdziesz kilka ogólnych wskazówek:

  • Wypróbuj inny model. Aby dane uporządkowane były generowane prawidłowo, model musi być w stanie generować dane w formacie JSON. Najpotężniejsze modele LLM, takie jak Gemini, są wystarczająco wszechstronne, aby to zrobić. Mniejsze modele, takie jak niektóre modele lokalne, których używasz z Ollama, mogą natomiast nie być w stanie niezawodnie generować danych wyjściowych w formie ustrukturyzowanej, chyba że zostały specjalnie wytrenowane do tego celu.

  • Uprość schemat. Sieci LLM mogą mieć problemy z generowaniem złożonych lub głęboko zagnieżdżonych typów. Jeśli nie możesz niezawodnie generować uporządkowanych danych, spróbuj użyć przejrzystych nazw, mniejszej liczby pól lub spłaszczonej struktury.

  • Ponowne nawiązanie połączenia genkit.Generate(). Jeśli wybrany model tylko rzadko nie generuje zgodnego z standardem wyjścia, możesz traktować ten błąd jak błąd sieci i ponownie wysłać żądanie, stosując pewien rodzaj strategii stopniowego wycofywania.

Streaming

Podczas generowania dużych ilości tekstu możesz ułatwić użytkownikom korzystanie z usługi, prezentując wyniki w miarę ich generowania (przesyłając je strumieniowo). Znajomy przykład strumieniowego przesyłania danych można zobaczyć w większości aplikacji do czatu z wykorzystaniem LLM: użytkownicy mogą czytać odpowiedzi modelu na ich wiadomości w miarę ich generowania, co zwiększa postrzeganą szybkość reakcji aplikacji i wzmacnia iluzję czatu z inteligentnym rozmówcą.

W Genkit możesz przesyłać dane wyjściowe w strumieniu za pomocą opcji WithStreaming():

resp, err := genkit.Generate(ctx, g,
    ai.WithPrompt("Suggest a complete menu for a pirate themed restaurant."),
    ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
        // Do something with the chunk...
        log.Println(chunk.Text())
        return nil
    }),
)
if err != nil {
    log.Fatal(err)
}

log.Println(resp.Text())

Dane multimodalne

W przykładach, które do tej pory widzisz, jako prompty modelu użyto ciągów tekstowych. Chociaż jest to najczęstszy sposób uruchamiania modeli generatywnej AI, wiele z nich może też przyjmować inne media jako prompty. Prompty multimedialne są najczęściej używane w połączeniu z promptami tekstowymi, które instruują model do wykonania jakiejś operacji na mediach, np. do dodania podpisu do obrazu lub transkrypcji nagrania audio.

Możliwość akceptowania danych wejściowych w formie multimediów oraz typy multimediów, których możesz używać, zależą całkowicie od modelu i jego interfejsu API. Na przykład seria modeli Gemini 2.0 może akceptować obrazy, filmy i dźwięk jako prompty.

Aby przekazać prompt multimedialny do modelu, który go obsługuje, zamiast przekazywać do funkcji genkit.Generate() prosty prompt tekstowy, prześlij tablicę zawierającą część multimedialną i tekstową. W tym przykładzie obraz jest określany za pomocą publicznie dostępnego adresu URL HTTPS.

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithMessages(
        NewUserMessage(
            NewMediaPart("image/jpeg", "https://example.com/photo.jpg"),
            NewTextPart("Compose a poem about this image."),
        ),
    ),
)

Możesz też przesłać dane multimedialne bezpośrednio, kodując je jako adres URL danych. Przykład:

image, err := ioutil.ReadFile("photo.jpg")
if err != nil {
    log.Fatal(err)
}

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithMessages(
        NewUserMessage(
            NewMediaPart("image/jpeg", "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(image)),
            NewTextPart("Compose a poem about this image."),
        ),
    ),
)

Wszystkie modele, które obsługują dane wejściowe, obsługują zarówno adresy URL danych, jak i adresy URL HTTPS. Niektóre wtyczki do modeli umożliwiają obsługę innych źródeł multimediów. Na przykład wtyczka Vertex AI umożliwia też używanie adresów URL Cloud Storage (gs://).

Dalsze kroki

Więcej informacji o Genkit

  • Jako deweloper aplikacji możesz wpływać na wyniki modeli generatywnej AI za pomocą promptów. Aby dowiedzieć się, jak Genkit pomaga tworzyć skuteczne prompty i nimi zarządzać w Twojej bazie kodu, przeczytaj artykuł Zarządzanie promptami za pomocą Dotprompt.
  • Chociaż genkit.Generate() jest jądrem każdej aplikacji korzystającej z generatywnej AI, aplikacje używane w praktyce zwykle wymagają dodatkowej pracy przed wywołaniem modelu generatywnej AI i po jego wywołaniu. W związku z tym Genkit wprowadza pojęcie przepływów, które są zdefiniowane jak funkcje, ale zawierają dodatkowe funkcje, takie jak możliwość monitorowania i uproszczone wdrażanie. Więcej informacji znajdziesz w artykule Definiowanie przepływów pracy dotyczących AI.

Zaawansowane korzystanie z modeli LLM

Aplikacja może stosować techniki, które pozwolą jej jeszcze lepiej korzystać z LLM.

  • Jedną z możliwości rozszerzenia możliwości LLM jest wyświetlenie użytkownikowi listy sposobów, w jaki LLM może poprosić o dodatkowe informacje lub poprosić użytkownika o wykonanie jakiegoś działania. Jest to tzw. wywoływanie narzędzia lub wywoływanie funkcji. Modele, które zostały przeszkolone do obsługi tej funkcji, mogą odpowiadać na prompty specjalnymi odpowiedziami, które wskazują aplikacji wywołującej, że powinna wykonać określone działanie i przesłać wynik z powrotem do LLM wraz z pierwotnym promptem. Genkit zawiera funkcje biblioteki, które automatyzują generowanie promptów i elementy pętli wywołania–odpowiedzi w ramach implementacji wywołania narzędzia. Aby dowiedzieć się więcej, zapoznaj się z artykułem Wywoływanie narzędzia.
  • Generowanie rozszerzone przez wyszukiwanie w zapisanych informacjach (RAG) to technika służąca do wprowadzania informacji dotyczących danej domeny do danych wyjściowych modelu. Polega to na wstawianiu odpowiednich informacji do promptu przed przekazaniem go do modelu językowego. Pełne wdrożenie RAG wymaga połączenia kilku technologii: modeli generowania wektorów dystrybucyjnych, baz danych wektorów i dużych modeli językowych. Zapoznaj się z artykułem Generowanie rozszerzone przez wyszukiwanie w zapisanych informacjach (RAG), aby dowiedzieć się, jak Genkit upraszcza proces koordynowania tych różnych elementów.