1. Wprowadzenie
Last Updated: 2024-02-05
Czym jest generatywna AI
Generatywna AI lub generatywna sztuczna inteligencja to wykorzystywanie AI do tworzenia nowych treści, takich jak tekst, obrazy, muzyka, dźwięki i filmy.
Generatywna AI jest oparta na modelach podstawowych (dużych modelach AI), które mogą wykonywać wiele zadań i działać od razu po wyjęciu z pudełka, m.in. podsumowywać, odpowiadać na pytania i klasyfikować. Modele podstawowe wymagają minimalnego trenowania i można je dostosowywać do konkretnych przypadków użycia przy użyciu bardzo małej ilości danych przykładowych.
Jak działa generatywna AI?
Generatywna AI działa na podstawie modelu uczenia maszynowego, który rozpoznaje wzorce i zależności w zbiorze danych zawierającym treści utworzone przez ludzi. Następnie wykorzystuje wyuczone wzorce do generowania nowych treści.
Najczęstszym sposobem trenowania modelu generatywnej AI jest uczenie nadzorowane – model otrzymuje zestaw treści utworzonych przez człowieka i odpowiadające im etykiety. Następnie uczy się generować treści podobne do tych utworzonych przez człowieka i oznaczonych tymi samymi etykietami.
Jakie są typowe zastosowania generatywnej AI?
Generatywna AI przetwarza ogromne ilości treści, tworząc statystyki i odpowiedzi w formie tekstu, obrazów i przyjaznych dla użytkownika formatów. Generatywna AI może służyć do:
- Ulepszanie interakcji z klientami dzięki ulepszonym funkcjom czatu i wyszukiwania
- Przeglądanie ogromnych ilości nieuporządkowanych danych za pomocą interfejsów konwersacyjnych i podsumowań
- pomagać w powtarzalnych zadaniach, takich jak odpowiadanie na zapytania ofertowe, lokalizowanie treści marketingowych w 5 językach i sprawdzanie zgodności umów z klientami z przepisami;
Jakie oferty generatywnej AI są dostępne w Google Cloud?
Dzięki Vertex AI możesz wchodzić w interakcje z modelami podstawowymi, dostosowywać je i osadzać w aplikacjach – nie musisz mieć przy tym specjalistycznej wiedzy z zakresu uczenia maszynowego. Uzyskaj dostęp do modeli podstawowych w Model Garden, dostrajaj modele za pomocą prostego interfejsu w Generative AI Studio lub używaj modeli w notatniku do obsługi danych.
Vertex AI Search and Conversation to najszybszy sposób na tworzenie wyszukiwarek i chatbotów opartych na generatywnej AI.
Duet AI to oparta na AI usługa wspomagająca dostępna w Google Cloud i IDE pomagająca wydajniej i szybciej pracować.
Na czym skupiają się te ćwiczenia?
Te warsztaty skupiają się na dużym modelu językowym PaLM 2, który jest hostowany w usłudze Google Cloud Vertex AI obejmującej wszystkie produkty i usługi związane z uczeniem maszynowym.
Do interakcji z interfejsem PaLM API będziesz używać języka Java w połączeniu z orkiestratorem platformy LLM LangChain4J. Poznasz różne konkretne przykłady wykorzystania LLM do odpowiadania na pytania, generowania pomysłów, wyodrębniania jednostek i treści strukturalnych oraz tworzenia podsumowań.
Chcę się dowiedzieć więcej o platformie LangChain4J.
Platforma LangChain4J to biblioteka open source do integrowania dużych modeli językowych z aplikacjami w języku Java. Umożliwia ona koordynowanie różnych komponentów, takich jak sam LLM, ale także inne narzędzia, np. bazy danych wektorowych (do wyszukiwania semantycznego), moduły do wczytywania i dzielenia dokumentów (do analizowania dokumentów i uczenia się na ich podstawie), parsery danych wyjściowych i inne.

Czego się nauczysz
- Jak skonfigurować projekt Java do korzystania z PaLM i LangChain4J
- Jak wyodrębniać przydatne informacje z nieuporządkowanych treści (wyodrębnianie jednostek lub słów kluczowych, dane wyjściowe w formacie JSON)
- Jak rozpocząć rozmowę z użytkownikami
- Jak używać modelu czatu do zadawania pytań dotyczących własnej dokumentacji
Czego potrzebujesz
- Znajomość języka programowania Java
- projekt Google Cloud,
- przeglądarka, np. Chrome lub Firefox;
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.



- Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Zawsze możesz ją zaktualizować.
- Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić po ustawieniu. Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie musisz się nim przejmować. W większości ćwiczeń z programowania musisz odwoływać się do identyfikatora projektu (zwykle oznaczanego jako
PROJECT_ID). Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować inny losowy identyfikator. Możesz też spróbować własnej nazwy i sprawdzić, czy jest dostępna. Po tym kroku nie można go zmienić i pozostaje on taki przez cały czas trwania projektu. - Warto wiedzieć, że istnieje też trzecia wartość, numer projektu, której używają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
- Następnie musisz włączyć płatności w konsoli Cloud, aby korzystać z zasobów i interfejsów API Google Cloud. Wykonanie tego laboratorium nie będzie kosztować dużo, a może nawet nic. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.
Uruchamianie Cloud Shell
Z Google Cloud można korzystać zdalnie na laptopie, ale w tym module użyjemy Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.
Aktywowanie Cloud Shell
- W konsoli Cloud kliknij Aktywuj Cloud Shell
.

Jeśli uruchamiasz Cloud Shell po raz pierwszy, zobaczysz ekran pośredni z opisem tego środowiska. Jeśli pojawił się ekran pośredni, kliknij Dalej.

Uzyskanie dostępu do środowiska Cloud Shell i połączenie się z nim powinno zająć tylko kilka chwil.

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Większość zadań w tym module, a być może wszystkie, możesz wykonać w przeglądarce.
Po połączeniu z Cloud Shell zobaczysz, że uwierzytelnianie zostało już przeprowadzone, a projekt jest już ustawiony na Twój identyfikator projektu.
- Aby potwierdzić, że uwierzytelnianie zostało przeprowadzone, uruchom w Cloud Shell to polecenie:
gcloud auth list
Wynik polecenia
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- Aby potwierdzić, że polecenie gcloud zna Twój projekt, uruchom w Cloud Shell to polecenie:
gcloud config list project
Wynik polecenia
[core] project = <PROJECT_ID>
Jeśli nie, możesz go ustawić za pomocą tego polecenia:
gcloud config set project <PROJECT_ID>
Wynik polecenia
Updated property [core/project].
3. Przygotowywanie środowiska programistycznego
W tym ćwiczeniu programistycznym do tworzenia programów w Javie będziesz używać terminala i edytora kodu Cloud Shell.
Włączanie interfejsów API Vertex AI
- W konsoli Google Cloud upewnij się, że nazwa projektu jest wyświetlana u góry konsoli Google Cloud. Jeśli nie, kliknij Wybierz projekt, aby otworzyć selektor projektów, i wybierz odpowiedni projekt.
- Jeśli nie jesteś w sekcji Vertex AI w konsoli Google Cloud, wykonaj te czynności:
- W polu Wyszukaj wpisz Vertex AI, a następnie naciśnij Enter.
- W wynikach wyszukiwania kliknij Vertex AI. Pojawi się panel Vertex AI.
- W panelu Vertex AI kliknij Włącz wszystkie zalecane interfejsy API.
Włączy to kilka interfejsów API, ale najważniejszy w tym samouczku jest interfejs aiplatform.googleapis.com, który możesz też włączyć w wierszu poleceń w terminalu Cloud Shell, uruchamiając to polecenie:
$ gcloud services enable aiplatform.googleapis.com
Tworzenie struktury projektu za pomocą Gradle
Do tworzenia przykładowych kodów w języku Java będziesz używać narzędzia do kompilacji Gradle i Javy w wersji 17. Aby skonfigurować projekt za pomocą Gradle, w terminalu Cloud Shell utwórz katalog (w tym przypadku palm-workshop) i uruchom w nim polecenie gradle init:
$ mkdir palm-workshop $ cd palm-workshop $ gradle init Select type of project to generate: 1: basic 2: application 3: library 4: Gradle plugin Enter selection (default: basic) [1..4] 2 Select implementation language: 1: C++ 2: Groovy 3: Java 4: Kotlin 5: Scala 6: Swift Enter selection (default: Java) [1..6] 3 Split functionality across multiple subprojects?: 1: no - only one application project 2: yes - application and library projects Enter selection (default: no - only one application project) [1..2] 1 Select build script DSL: 1: Groovy 2: Kotlin Enter selection (default: Groovy) [1..2] 1 Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] Select test framework: 1: JUnit 4 2: TestNG 3: Spock 4: JUnit Jupiter Enter selection (default: JUnit Jupiter) [1..4] 4 Project name (default: palm-workshop): Source package (default: palm.workshop): > Task :init Get more help with your project: https://docs.gradle.org/7.4/samples/sample_building_java_applications.html BUILD SUCCESSFUL in 51s 2 actionable tasks: 2 executed
Utworzysz aplikację (opcja 2) w języku Java (opcja 3) bez użycia podprojektów (opcja 1) z syntaktyką Groovy w pliku kompilacji (opcja 1), bez użycia nowych funkcji kompilacji (opcja 0), generując testy za pomocą JUnit Jupiter (opcja 4). Jako nazwę projektu możesz użyć palm-workshop, a jako pakiet źródłowy – palm.workshop.
Struktura projektu będzie wyglądać tak:
├── gradle
│ └── ...
├── gradlew
├── gradlew.bat
├── settings.gradle
└── app
├── build.gradle
└── src
├── main
│ └── java
│ └── palm
│ └── workshop
│ └── App.java
└── test
└── ...
Zaktualizujmy plik app/build.gradle, aby dodać potrzebne zależności. Możesz usunąć zależność guava, jeśli jest obecna, i zastąpić ją zależnościami projektu LangChain4J oraz biblioteki logowania, aby uniknąć irytujących komunikatów o braku rejestratora:
dependencies {
// Use JUnit Jupiter for testing.
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
// Logging library
implementation 'org.slf4j:slf4j-jdk14:2.0.9'
// This dependency is used by the application.
implementation 'dev.langchain4j:langchain4j-vertex-ai:0.24.0'
implementation 'dev.langchain4j:langchain4j:0.24.0'
}
LangChain4J ma 2 zależności:
- jeden w projekcie podstawowym,
- i jeden dla dedykowanego modułu Vertex AI.
Aby używać Javy 17 do kompilowania i uruchamiania programów, dodaj ten blok poniżej bloku plugins {}:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
Kolejna zmiana do wprowadzenia: zaktualizuj blok application w app/build.gradle, aby umożliwić użytkownikom zastąpienie głównej klasy do uruchomienia w wierszu poleceń podczas wywoływania narzędzia do kompilacji:
application {
mainClass = providers.systemProperty('javaMainClass')
.orElse('palm.workshop.App')
}
Aby sprawdzić, czy plik kompilacji jest gotowy do uruchomienia aplikacji, możesz uruchomić domyślną klasę główną, która wyświetla prosty komunikat Hello World!:
$ ./gradlew run -DjavaMainClass=palm.workshop.App > Task :app:run Hello World! BUILD SUCCESSFUL in 3s 2 actionable tasks: 2 executed
Możesz już programować za pomocą dużego modelu językowego PaLM, korzystając z projektu LangChain4J.
Oto jak powinien teraz wyglądać pełny plik kompilacji app/build.gradle:
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
java {
toolchain {
// Ensure we compile and run on Java 17
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Use JUnit Jupiter for testing.
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
// This dependency is used by the application.
implementation 'dev.langchain4j:langchain4j-vertex-ai:0.24.0'
implementation 'dev.langchain4j:langchain4j:0.24.0'
implementation 'org.slf4j:slf4j-jdk14:2.0.9'
}
application {
mainClass = providers.systemProperty('javaMainClass').orElse('palm.workshop.App')
}
tasks.named('test') {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
4. Wykonywanie pierwszego wywołania modelu czatu PaLM
Teraz, gdy projekt jest już prawidłowo skonfigurowany, możesz wywołać interfejs PaLM API.
Utwórz w katalogu app/src/main/java/palm/workshop nową klasę o nazwie ChatPrompts.java (obok domyślnej klasy App.java) i wpisz ten kod:
package palm.workshop;
import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.chain.ConversationalChain;
public class ChatPrompts {
public static void main(String[] args) {
VertexAiChatModel model = VertexAiChatModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("YOUR_PROJECT_ID")
.location("us-central1")
.publisher("google")
.modelName("chat-bison@001")
.maxOutputTokens(400)
.maxRetries(3)
.build();
ConversationalChain chain = ConversationalChain.builder()
.chatLanguageModel(model)
.build();
String message = "What are large language models?";
String answer = chain.execute(message);
System.out.println(answer);
System.out.println("---------------------------");
message = "What can you do with them?";
answer = chain.execute(message);
System.out.println(answer);
System.out.println("---------------------------");
message = "Can you name some of them?";
answer = chain.execute(message);
System.out.println(answer);
}
}
W tym pierwszym przykładzie musisz zaimportować klasę VertexAiChatModel i ConversationalChain LangChain4J, aby ułatwić obsługę wieloetapowych rozmów.
Następnie w metodzie main skonfigurujesz model językowy czatu za pomocą narzędzia do tworzenia VertexAiChatModel, aby określić:
- punkt końcowy,
- projektu,
- region,
- wydawca,
- i nazwę modelu (
chat-bison@001).
Model językowy jest już gotowy, więc możesz przygotować ConversationalChain. Jest to abstrakcja wyższego poziomu oferowana przez LangChain4J, która umożliwia konfigurowanie różnych komponentów do obsługi rozmowy, takich jak sam model językowy do czatu, ale potencjalnie także innych komponentów do obsługi historii rozmowy na czacie lub do podłączania innych narzędzi, takich jak wyszukiwarki, które pobierają informacje z baz danych wektorowych. Ale bez obaw, wrócimy do tego później w tym laboratorium.
Następnie przeprowadzisz wieloetapową rozmowę z modelem czatu, aby zadać kilka powiązanych ze sobą pytań. Najpierw zastanawiasz się nad LLM, potem pytasz, co możesz z nimi zrobić i jakie są ich przykłady. Zwróć uwagę, że nie musisz się powtarzać. Model LLM wie, że w kontekście tej rozmowy „them” oznacza modele LLM.
Aby przeprowadzić rozmowę wieloetapową, wystarczy wywołać metodę execute() w łańcuchu. Spowoduje to dodanie jej do kontekstu rozmowy, a model czatu wygeneruje odpowiedź i doda ją do historii czatu.
Aby uruchomić tę klasę, wpisz w terminalu Cloud Shell to polecenie:
./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts
Powinny pojawić się dane wyjściowe podobne do tych:
$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts Starting a Gradle Daemon, 2 incompatible and 2 stopped Daemons could not be reused, use --status for details > Task :app:run Large language models (LLMs) are artificial neural networks that are trained on massive datasets of text and code. They are designed to understand and generate human language, and they can be used for a variety of tasks, such as machine translation, question answering, and text summarization. --------------------------- LLMs can be used for a variety of tasks, such as: * Machine translation: LLMs can be used to translate text from one language to another. * Question answering: LLMs can be used to answer questions posed in natural language. * Text summarization: LLMs can be used to summarize text into a shorter, more concise form. * Code generation: LLMs can be used to generate code, such as Python or Java code. * Creative writing: LLMs can be used to generate creative text, such as poems, stories, and scripts. LLMs are still under development, but they have the potential to revolutionize a wide range of industries. For example, LLMs could be used to improve customer service, create more personalized marketing campaigns, and develop new products and services. --------------------------- Some of the most well-known LLMs include: * GPT-3: Developed by OpenAI, GPT-3 is a large language model that can generate text, translate languages, write different kinds of creative content, and answer your questions in an informative way. * LaMDA: Developed by Google, LaMDA is a large language model that can chat with you in an open-ended way, answering your questions, telling stories, and providing different kinds of creative content. * PaLM 2: Developed by Google, PaLM 2 is a large language model that can perform a wide range of tasks, including machine translation, question answering, and text summarization. * T5: Developed by Google, T5 is a large language model that can be used for a variety of tasks, including text summarization, question answering, and code generation. These are just a few examples of the many LLMs that are currently being developed. As LLMs continue to improve, they are likely to play an increasingly important role in our lives. BUILD SUCCESSFUL in 25s 2 actionable tasks: 2 executed
PaLM odpowiedział na 3 powiązane pytania.
Narzędzie do tworzenia VertexAIChatModel umożliwia zdefiniowanie parametrów opcjonalnych, które mają już wartości domyślne, które możesz zastąpić. Oto przykłady:
.temperature(0.2)– określa, jak kreatywna ma być odpowiedź (0 – mało kreatywna i często bardziej oparta na faktach, 1 – bardziej kreatywna)..maxOutputTokens(50)– w tym przykładzie poproszono o 400 tokenów (3 tokeny to w przybliżeniu 4 słowa), w zależności od tego, jak długa ma być wygenerowana odpowiedź..topK(20)– losowe wybranie słowa z maksymalnej liczby prawdopodobnych słów do dokończenia tekstu (od 1 do 40)..topP(0.95)– aby wybrać możliwe słowa, których łączne prawdopodobieństwo wynosi tę liczbę zmiennoprzecinkową (od 0 do 1)..maxRetries(3)– jeśli przekraczasz limit żądań na określony czas, możesz na przykład poprosić model o 3-krotne ponowienie wywołania.
5. Przydatny czatbot z osobowością.
W poprzedniej sekcji od razu zaczęliśmy zadawać pytania chatbotowi LLM bez podawania mu żadnego konkretnego kontekstu. Możesz jednak wyspecjalizować takiego chatbota, aby stał się ekspertem w określonym zadaniu lub w określonej dziedzinie.
Jak to zrobić? Poprzez wprowadzenie: wyjaśnij modelowi LLM, jakie jest zadanie, podaj kontekst, być może podaj kilka przykładów tego, co ma zrobić, jaką ma przyjąć osobowość, w jakim formacie chcesz otrzymywać odpowiedzi i ewentualnie jaki ma być ton, jeśli chcesz, aby chatbot zachowywał się w określony sposób.
W tym artykule o tworzeniu promptów znajdziesz ilustrację, która dobrze obrazuje to podejście:

https://medium.com/@eldatero/master-the-perfect-chatgpt-prompt-formula-c776adae8f19
Aby to zilustrować, zaczerpnijmy inspiracji z witryny prompts.chat, która zawiera wiele świetnych i ciekawych pomysłów na dostosowane chatboty, które mogą pełnić funkcje:
- tłumacz emotikonów – do tłumaczenia wiadomości użytkowników na emotikony;
- ulepszacza promptów, który pomoże Ci tworzyć lepsze prompty;
- recenzent czasopisma – aby pomagać w recenzowaniu artykułów naukowych;
- osobistego stylisty – aby uzyskać sugestie dotyczące stylu ubioru;
Jest jeden przykład, jak przekształcić czatbota LLM w gracza w szachy. Wprowadźmy to w życie.
Zaktualizuj klasę ChatPrompts w ten sposób:
package palm.workshop;
import dev.langchain4j.chain.ConversationalChain;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
public class ChatPrompts {
public static void main(String[] args) {
VertexAiChatModel model = VertexAiChatModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("YOUR_PROJECT_ID")
.location("us-central1")
.publisher("google")
.modelName("chat-bison@001")
.maxOutputTokens(7)
.maxRetries(3)
.build();
InMemoryChatMemoryStore chatMemoryStore = new InMemoryChatMemoryStore();
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryStore(chatMemoryStore)
.maxMessages(200)
.build();
chatMemory.add(SystemMessage.from("""
You're an expert chess player with a high ELO ranking.
Use the PGN chess notation to reply with the best next possible move.
"""
));
ConversationalChain chain = ConversationalChain.builder()
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();
String pgn = "";
String[] whiteMoves = { "Nf3", "c4", "Nc3", "e3", "Dc2", "Cd5"};
for (int i = 0; i < whiteMoves.length; i++) {
pgn += " " + (i+1) + ". " + whiteMoves[i];
System.out.println("Playing " + whiteMoves[i]);
pgn = chain.execute(pgn);
System.out.println(pgn);
}
}
}
Przyjrzyjmy się temu bliżej:
- Aby zarządzać pamięcią czatu, musisz dodać kilka nowych importów.
- Tworzysz instancję modelu czatu, ale z niewielką liczbą maksymalnych tokenów (w tym przypadku 7), ponieważ chcemy wygenerować tylko następny ruch, a nie całą rozprawę o szachach.
- Następnie utworzysz magazyn pamięci czatu, aby zapisywać rozmowy na czacie.
- Tworzysz rzeczywistą pamięć czatu w oknie, aby zachować ostatnie ruchy.
- W pamięci czatu dodajesz wiadomość „systemową”, która instruuje model czatu, kim ma być (np. ekspertem w grze w szachy). Wiadomość „system” dodaje kontekst, a wiadomości „użytkownik” i „AI” to właściwa dyskusja.
- Tworzysz łańcuch konwersacyjny, który łączy pamięć i model czatu.
- Następnie mamy listę ruchów dla białych, po której iterujemy. Łańcuch jest wykonywany z kolejnym ruchem białych, a model czatu odpowiada kolejnym najlepszym ruchem.
Po uruchomieniu tej klasy z tymi ruchami powinny się wyświetlić te dane wyjściowe:
$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts Starting a Gradle Daemon (subsequent builds will be faster) > Task :app:run Playing Nf3 1... e5 Playing c4 2... Nc6 Playing Nc3 3... Nf6 Playing e3 4... Bb4 Playing Dc2 5... O-O Playing Cd5 6... exd5
Wow! PaLM wie, jak grać w szachy? Nie do końca, ale podczas trenowania model musiał widzieć komentarze do partii szachowych, a nawet pliki PGN (Portable Game Notation) z rozegranych wcześniej partii. Ten czatbot prawdopodobnie nie wygra z AlphaZero (AI, która pokonuje najlepszych graczy w go, shogi i szachy), a rozmowa może się w pewnym momencie wykoleić, ponieważ model nie będzie pamiętać rzeczywistego stanu gry.
Modele czatu są bardzo zaawansowane i mogą tworzyć bogate interakcje z użytkownikami oraz obsługiwać różne zadania kontekstowe. W następnej sekcji przyjrzymy się przydatnemu zadaniu: wyodrębnianiu uporządkowanych danych z tekstu.
6. Wyodrębnianie informacji z nieustrukturyzowanego tekstu
W poprzedniej sekcji utworzyliśmy rozmowy między użytkownikiem a modelem językowym do czatu. Dzięki LangChain4J możesz też używać modelu czatu do wyodrębniania uporządkowanych informacji z nieuporządkowanego tekstu.
Załóżmy, że chcesz wyodrębnić imię i wiek osoby na podstawie jej biografii lub opisu. Możesz poinstruować duży model językowy, aby generował struktury danych JSON za pomocą sprytnie zmodyfikowanego prompta (jest to powszechnie nazywane „inżynierią promptów”).
Zaktualizuj klasę ChatPrompts w ten sposób:
package palm.workshop;
import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
public class ChatPrompts {
static class Person {
String name;
int age;
}
interface PersonExtractor {
@UserMessage("""
Extract the name and age of the person described below.
Return a JSON document with a "name" and an "age" property, \
following this structure: {"name": "John Doe", "age": 34}
Return only JSON, without any markdown markup surrounding it.
Here is the document describing the person:
---
{{it}}
---
JSON:
""")
Person extractPerson(String text);
}
public static void main(String[] args) {
VertexAiChatModel model = VertexAiChatModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("YOUR_PROJECT_ID")
.location("us-central1")
.publisher("google")
.modelName("chat-bison@001")
.maxOutputTokens(300)
.build();
PersonExtractor extractor = AiServices.create(PersonExtractor.class, model);
Person person = extractor.extractPerson("""
Anna is a 23 year old artist based in Brooklyn, New York. She was born and
raised in the suburbs of Chicago, where she developed a love for art at a
young age. She attended the School of the Art Institute of Chicago, where
she studied painting and drawing. After graduating, she moved to New York
City to pursue her art career. Anna's work is inspired by her personal
experiences and observations of the world around her. She often uses bright
colors and bold lines to create vibrant and energetic paintings. Her work
has been exhibited in galleries and museums in New York City and Chicago.
"""
);
System.out.println(person.name);
System.out.println(person.age);
}
}
Przyjrzyjmy się poszczególnym etapom w tym pliku:
PersonKlasa jest zdefiniowana tak, aby reprezentować szczegóły opisujące osobę (jej imię i wiek).- Interfejs
PersonExtractorjest tworzony za pomocą metody, która na podstawie nieuporządkowanego ciągu tekstowego zwraca utworzoną instancjęPerson. extractPerson()jest opatrzony adnotacją@UserMessage, która wiąże z nim prompta. Jest to prompt, którego model użyje do wyodrębnienia informacji i zwrócenia szczegółów w postaci dokumentu JSON, który zostanie przeanalizowany i zdeserializowany do instancjiPerson.
Przyjrzyjmy się teraz zawartości metody main():
- Model czatu jest tworzony.
- Obiekt
PersonExtractorjest tworzony dzięki klasieAiServicesLangChain4J. - Następnie możesz po prostu zadzwonić pod numer
Person person = extractor.extractPerson(...), aby wyodrębnić szczegóły osoby z nieustrukturyzowanego tekstu i otrzymać instancjęPersonz imieniem i nazwiskiem oraz wiekiem.
Teraz uruchom tę klasę za pomocą tego polecenia:
$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts > Task :app:run Anna 23
Tak. To Anna, ma 23 lata.
Szczególnie interesujące w tym podejściu AiServices jest to, że pracujesz z obiektami o ściśle określonym typie. Nie wchodzisz w bezpośrednią interakcję z dużym modelem językowym czatu. Zamiast tego pracujesz z konkretnymi klasami, np. klasą Person, która reprezentuje wyodrębnione dane osobowe, i masz klasę PersonExtractor z metodą extractPerson(), która zwraca instancję klasy Person. Pojęcie LLM jest tu abstrakcyjne, a jako programista Java pracujesz tylko ze zwykłymi klasami i obiektami.
7. Generowanie rozszerzone przez wyszukiwanie w zapisanych informacjach: czatowanie z dokumentami
Wróćmy do rozmów. Tym razem możesz zadawać pytania dotyczące dokumentów. Utworzysz chatbota, który będzie w stanie pobierać odpowiednie informacje z bazy danych wyciągów z Twoich dokumentów. Te informacje będą używane przez model do „uzasadniania” odpowiedzi, zamiast generować odpowiedzi na podstawie danych treningowych. Ten wzorzec jest nazywany RAG, czyli generowaniem wspomaganym wyszukiwaniem.
Generowanie wspomagane wyszukiwaniem obejmuje 2 etapy:
- Faza wczytywania – dokumenty są wczytywane i dzielone na mniejsze części, a ich reprezentacja wektorowa („wektor dystrybucyjny”) jest przechowywana w „bazie danych wektorowych”, która umożliwia wyszukiwanie semantyczne.

- Faza zapytania – użytkownicy mogą teraz zadawać chatbotowi pytania dotyczące dokumentacji. Pytanie zostanie również przekształcone w wektor i porównane ze wszystkimi innymi wektorami w bazie danych. Najbardziej podobne wektory są zwykle powiązane semantycznie i są zwracane przez wektorową bazę danych. Następnie model LLM otrzymuje kontekst rozmowy, fragmenty tekstu odpowiadające wektorom zwróconym przez bazę danych, i jest proszony o uzasadnienie swojej odpowiedzi na podstawie tych fragmentów.

Przygotowywanie dokumentów
W tym nowym demo będziesz zadawać pytania dotyczące architektury sieci neuronowej „transformer”, opracowanej przez Google, która jest obecnie wykorzystywana we wszystkich nowoczesnych dużych modelach językowych.
Aby pobrać z internetu plik PDF z artykułem naukowym opisującym tę architekturę („Attention is all you need”), użyj polecenia wget:
wget -O attention-is-all-you-need.pdf \
https://proceedings.neurips.cc/paper_files/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf
Wdrażanie łańcucha pobierania informacji w formie rozmowy
Krok po kroku omówimy, jak zbudować dwuetapowe podejście, najpierw z przetwarzaniem dokumentu, a potem z czasem zapytania, gdy użytkownicy zadają pytania dotyczące dokumentu.
Przetwarzanie dokumentów
Pierwszym krokiem fazy wczytywania dokumentu jest znalezienie pliku PDF, pobranie go i przygotowanie PdfParser do jego odczytania:
PdfDocumentParser pdfParser = new PdfDocumentParser();
Document document = pdfParser.parse(
new FileInputStream(new File("/home/YOUR_USER_NAME/palm-workshop/attention-is-all-you-need.pdf")));
Zamiast tworzyć zwykły model językowy do czatu, najpierw utworzysz instancję modelu „embedding”. Jest to konkretny model i punkt końcowy, których zadaniem jest tworzenie wektorowych reprezentacji fragmentów tekstu (słów, zdań, a nawet akapitów).
VertexAiEmbeddingModel embeddingModel = VertexAiEmbeddingModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("YOUR_PROJECT_ID")
.location("us-central1")
.publisher("google")
.modelName("textembedding-gecko@001")
.maxRetries(3)
.build();
Następnie potrzebujesz kilku klas, które będą ze sobą współpracować, aby:
- Wczytaj i podziel dokument PDF na części.
- Utwórz wektory dystrybucyjne dla wszystkich tych fragmentów.
InMemoryEmbeddingStore<TextSegment> embeddingStore =
new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
.documentSplitter(DocumentSplitters.recursive(500, 100))
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
storeIngestor.ingest(document);
EmbeddingStoreRetriever retriever = EmbeddingStoreRetriever.from(embeddingStore, embeddingModel);
Tworzona jest instancja InMemoryEmbeddingStore, bazy danych wektorowych w pamięci, do przechowywania wektorów dystrybucyjnych.
Dokument jest dzielony na fragmenty dzięki klasie DocumentSplitters. Podzieli on tekst pliku PDF na fragmenty po 500 znaków, z nakładaniem się 100 znaków (z następnym fragmentem, aby uniknąć dzielenia słów lub zdań na części).
„Ingestor” sklepu łączy rozdzielacz dokumentów, model wektorów dystrybucyjnych do obliczania wektorów i bazę danych wektorów w pamięci. Następnie metoda ingest() zajmie się przetwarzaniem.
Pierwszy etap dobiegł końca. Dokument został przekształcony w bloki tekstu z powiązanymi wektorami dystrybucyjnymi i zapisany w bazie danych wektorowych.
Zadawanie pytań
Czas zadać pytania. Aby rozpocząć rozmowę, możesz utworzyć zwykły model czatu:
VertexAiChatModel model = VertexAiChatModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("YOUR_PROJECT_ID")
.location("us-central1")
.publisher("google")
.modelName("chat-bison@001")
.maxOutputTokens(1000)
.build();
Potrzebna będzie też klasa „retriever”, która połączy bazę danych wektorów (w zmiennej embeddingStore) z modelem wektorów dystrybucyjnych. Jego zadaniem jest wysyłanie zapytań do bazy danych wektorowych przez obliczanie wektora dystrybucyjnego dla zapytania użytkownika w celu znalezienia podobnych wektorów w bazie danych:
EmbeddingStoreRetriever retriever =
EmbeddingStoreRetriever.from(embeddingStore, embeddingModel);
W tym momencie możesz utworzyć instancję klasy ConversationalRetrievalChain (to tylko inna nazwa wzorca Retrieval Augmented Generation):
ConversationalRetrievalChain rag = ConversationalRetrievalChain.builder()
.chatLanguageModel(model)
.retriever(retriever)
.promptTemplate(PromptTemplate.from("""
Answer to the following query the best as you can: {{question}}
Base your answer on the information provided below:
{{information}}
"""
))
.build();
Ten „łańcuch” łączy:
- Model języka czatu skonfigurowany wcześniej.
- Wyszukiwarka porównuje wektor dystrybucyjny zapytania z wektorami w bazie danych.
- Szablon promptu wyraźnie wskazuje, że model czatu powinien odpowiadać na podstawie podanych informacji (czyli odpowiednich fragmentów dokumentacji, których wektor dystrybucyjny jest podobny do wektora pytania użytkownika).
Teraz możesz w końcu zadać pytania.
String result = rag.execute("What neural network architecture can be used for language models?");
System.out.println(result);
System.out.println("------------");
result = rag.execute("What are the different components of a transformer neural network?");
System.out.println(result);
System.out.println("------------");
result = rag.execute("What is attention in large language models?");
System.out.println(result);
System.out.println("------------");
result = rag.execute("What is the name of the process that transforms text into vectors?");
System.out.println(result);
Uruchom program za pomocą:
$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts
W danych wyjściowych powinna pojawić się odpowiedź na Twoje pytania:
The Transformer is a neural network architecture that can be used for language models. It is based solely on attention mechanisms, dispensing with recurrence and convolutions. The Transformer has been shown to outperform recurrent neural networks and convolutional neural networks on a variety of language modeling tasks. ------------ The Transformer is a neural network architecture that can be used for language models. It is based solely on attention mechanisms, dispensing with recurrence and convolutions. The Transformer has been shown to outperform recurrent neural networks and convolutional neural networks on a variety of language modeling tasks. The Transformer consists of an encoder and a decoder. The encoder is responsible for encoding the input sequence into a fixed-length vector representation. The decoder is responsible for decoding the output sequence from the input sequence. The decoder uses the attention mechanism to attend to different parts of the input sequence when generating the output sequence. ------------ Attention is a mechanism that allows a neural network to focus on specific parts of an input sequence. In the context of large language models, attention is used to allow the model to focus on specific words or phrases in a sentence when generating output. This allows the model to generate more relevant and informative output. ------------ The process of transforming text into vectors is called word embedding. Word embedding is a technique that represents words as vectors in a high-dimensional space. The vectors are typically learned from a large corpus of text, and they capture the semantic and syntactic relationships between words. Word embedding has been shown to be effective for a variety of natural language processing tasks, such as machine translation, question answering, and sentiment analysis.
Kompleksowe rozwiązanie
Aby ułatwić kopiowanie i wklejanie, podajemy pełną treść klasy ChatPrompts:
package palm.workshop;
import dev.langchain4j.chain.ConversationalRetrievalChain;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.parser.PdfDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.model.vertexai.VertexAiEmbeddingModel;
import dev.langchain4j.retriever.EmbeddingStoreRetriever;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class ChatPrompts {
public static void main(String[] args) throws IOException {
PdfDocumentParser pdfParser = new PdfDocumentParser();
Document document = pdfParser.parse(new FileInputStream(new File("/ABSOLUTE_PATH/attention-is-all-you-need.pdf")));
VertexAiEmbeddingModel embeddingModel = VertexAiEmbeddingModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("YOUR_PROJECT_ID")
.location("us-central1")
.publisher("google")
.modelName("textembedding-gecko@001")
.maxRetries(3)
.build();
InMemoryEmbeddingStore<TextSegment> embeddingStore =
new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
.documentSplitter(DocumentSplitters.recursive(500, 100))
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
storeIngestor.ingest(document);
EmbeddingStoreRetriever retriever = EmbeddingStoreRetriever.from(embeddingStore, embeddingModel);
VertexAiChatModel model = VertexAiChatModel.builder()
.endpoint("us-central1-aiplatform.googleapis.com:443")
.project("genai-java-demos")
.location("us-central1")
.publisher("google")
.modelName("chat-bison@001")
.maxOutputTokens(1000)
.build();
ConversationalRetrievalChain rag = ConversationalRetrievalChain.builder()
.chatLanguageModel(model)
.retriever(retriever)
.promptTemplate(PromptTemplate.from("""
Answer to the following query the best as you can: {{question}}
Base your answer on the information provided below:
{{information}}
"""
))
.build();
String result = rag.execute("What neural network architecture can be used for language models?");
System.out.println(result);
System.out.println("------------");
result = rag.execute("What are the different components of a transformer neural network?");
System.out.println(result);
System.out.println("------------");
result = rag.execute("What is attention in large language models?");
System.out.println(result);
System.out.println("------------");
result = rag.execute("What is the name of the process that transforms text into vectors?");
System.out.println(result);
}
}
8. Gratulacje
Gratulacje! Udało Ci się utworzyć pierwszą aplikację do czatu z generatywną AI w języku Java przy użyciu LangChain4J i interfejsu PaLM API. Po drodze odkryłeś, że duże modele językowe do czatu są dość zaawansowane i potrafią wykonywać różne zadania, takie jak odpowiadanie na pytania (nawet na podstawie własnej dokumentacji), wyodrębnianie danych, a nawet gra w szachy.
Co dalej?
Aby dowiedzieć się więcej o PaLM w Javie, zapoznaj się z tymi ćwiczeniami:
Więcej informacji
- Typowe przypadki użycia generatywnej AI
- Materiały szkoleniowe dotyczące generatywnej AI
- Interakcja z modelem PaLM za pomocą Generative AI Studio
- Odpowiedzialna AI