Share
Embed
LangChain for Java - Java on Azure Day 2024
Overview to leverage LangChain for Java (LangChain4j) to create AI-powered applications (in Japanese).
This presentation contains only basic information about LangChain4j.
Akihiro Nishikawa
June 05, 2024
Transcript
50 slides
-
Build LLM-Powered Applications with LangChain4j NISHIKAWA, Akihiro (@logico_jp) Cloud Solution
Architect Microsoft Japan -
LangChain4j LangChain for Java JavaアプリケーションからAI/LLMの機能を利用するためのライブラリ LangChain4j | LangChain4j
PythonベースのLangChainをJavaにポーティング 本家ではなく、コミュニティが主導して移植 -
特長 統一したAPI 様々なベンダーのLLMを透過的に使えるように(抽象度は2段階) LLMを活用するアプリケーションで必要な機能を幅広く提供 プロンプトテンプレート、メモリ管理、出力解析...
エージェント、 RAG... 開発フレームワークやランタイムとの統合 もちろん、統合されていない開発フレームワークでも利用可能 -
低レベルAPI 高レベルAPI モジュール Chains AI Services Basics RAG Language Model
Document Loader Prompt Template Document Splitter Output Parser Embedding Models Memory Embedding Store -
多種多様なLLM、機能をサポート Comparison Table of all supported Language Models |
LangChain4j langchain4j/langchain4j: Java version of LangChain (github.com) -
開発フレームワークとの統合 Quarkus / Spring Boot Quarkus Quarkus LangChain4j quarkus-langchain4j-azure-openai extension
quarkiverse/quarkus-langchain4j: Quarkus Langchain4j extension (github.com) Spring Boot LangChain4j Spring Boot Starter langchain4j/langchain4j-spring: LangChain4j integration with Spring (github.com) Azure OpenAI Service langchain4j-azure-open-ai-spring-boot-starter Azure AI Search langchain4j-azure-ai-search-spring-boot-starter -
依存関係 2024/06/04現在の最新は0.31.0 必須の依存関係 共通部分: langchain4j LLM固有部分:
langchain4j-azure-open-ai 必要に応じて他の依存関係を取り込む langchain4j-azure-ai-search langchain4j-open-ai など -
共通 依存関係 <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>0.31.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-azure-open-ai</artifactId>
<version>0.31.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>0.31.0</version> </dependency> LLM固有 必要に 応じて追加 -
①モデルの構築 JSONで出力するChatLanguageModelの例 ChatLanguageModel chatLanguageModel = AzureOpenAiChatModel.builder() .endpoint(AZURE_OPENAI_ENDPOINT) .apiKey(AZURE_OPENAI_APIKEY) .deploymentName(DEPLOYMENT_MODEL_NAME) .temperature(0.0)
// Temperature .responseFormat( new ChatCompletionsJsonResponseFormat()) // ResponseFormat .logRequestsAndResponses(true) // ログ出力の有無 .build(); ChatLanguageModel, StreamingLanguageModel, EmbeddingModel, ImageModel, StreamingChatLanguageModel など -
②プロンプトの設定 System Prompt 低レベルAPI 高レベルAPI SystemMessage.from( "あなたはフランスについて詳しい人です"); @SystemMessage( "あなたは天真爛漫なアシスタントです。") String
chat(String userMessage); User Prompt 低レベルAPI 高レベルAPI UserMessage.from( "誰がモナリザの微笑を描きましたか?"); @SystemMessage( "あなたは{{characteristic}}なアシスタントです") String chat( @UserMessage String userMessage, @V("characteristic") String characteristic); -
③リクエスト発行・レスポンスの収集 ChatとImageの例 // 生成結果を取得 (テキスト) Response<AiMessage> response1 = chatLanguageModel.generate(systemMessage, userMessage1);
var answer1 = response1.content().text(); // イメージを生成 Response<Image> image1 = imageModel.generate(userMessage2); // 出力されたイメージ格納先 (Blob storage) のURLを取得 var answer2 = image1.content().url().toString(); -
③リクエスト発行・レスポンスの収集 Streamモード時は、StreamingChatLanguageModelを利用 streamingChatLanguageModel.generate( systemMessage, userMessage, new StreamingResponseHandler<AiMessage>() { @Override public
void onNext(String s) {...} @Override public void onComplete(Response<AiMessage> response) { // LLMによる生成結果 System.out.println(response.content().text()); } @Override public void onError(Throwable throwable) {...} }); ... -
③リクエスト発行・レスポンスの収集 a) 過去のやりとりを可変長引数やListで都度generateメソッドに渡す var systemMessage = SystemMessage.from("あなたはフランスについて詳しい人です。"); var userMessage1 =
UserMessage.from("誰がモナリザの微笑を描きましたか?"); Response<AiMessage> aiMessage1 = chatLanguageModel.generate(systemMessage, userMessage1); var userMessage2 = UserMessage.from("どこでその絵を鑑賞できますか?"); Response<AiMessage> aiMessage2 = chatLanguageModel .generate(systemMessage, userMessage1, aiMessage1, userMessage2); -
③リクエスト発行・レスポンスの収集 b) 過去のやりとりをConversationChainにアタッチしたChatMemoryに保持 var systemMessage = SystemMessage.from("あなたはフランスについて詳しい人です。"); var chatMemory =
MessageWindowChatMemory.withMaxMessages(20); chatMemory.add(systemMessage); var chain = ConversationalChain.builder() .chatLanguageModel(chatLanguageModel).chatMemory(chatMemory).build(); var answer1 = chain.execute("誰がモナリザの微笑を描きましたか?"); var answer2 = chain.execute("どこでその絵を鑑賞できますか?"); // 履歴はList形式で格納されている chatMemory.messages().forEach(f-> System.out.printf( "type[%s] text[%s]¥n", f.type().name(), f.text())); -
シンプルなChat 京都出身の親友という体で会話 interface Kyoto { @SystemMessage(""" あなたは私の京都出身の親友です。 京都人らしく、心温かくも少々いけずな返答をしてください。 """) String
chat(String message); } var question = "こんにちは。どこかにお出かけですか?"; Kyoto friend = AiServices.create(Kyoto.class, chatLanguageModel); var answer = friend.chat(question); -
イメージからデータを抽出する Response FormatはJSON var systemMessage = SystemMessage.from( "画像のアイテムを読み取り、JSON形式で出力してください。"); var userMessage
= UserMessage.from(ImageContent.from(URL)); Response<AiMessage> response = chatLanguageModel .generate(systemMessage, userMessage); var answer = response.content().text(); // Jacksonを利用してJSONオブジェクトに変換 ObjectMapper objectMapper = new ObjectMapper(); JsonNode json = objectMapper.readTree(answer); ... -
イメージから撮影場所を推定する ResponseFormatはテキスト (default) var systemMessage = SystemMessage.from(""" 画像から撮影場所を見つけ出し、 日本語で回答してください。 """);
var userMessage = UserMessage.from( ImageContent.from(URL)); Response<AiMessage> response = chatLanguageModel.generate( systemMessage, userMessage); -
イメージの生成 Chatの場合とほぼ同じ ImageModel imageModel = AzureOpenAiImageModel.builder() .endpoint(AZURE_OPENAI_ENDPOINT) .apiKey(AZURE_OPENAI_APIKEY) .deploymentName(DEPLOYMENT_MODEL_NAME) .maxRetries(5)
.build(); var question = "夏の終わりの夕暮れ時に、海辺をドライブする様子をアメリカンポップアート風に描いてください。"; Response<Image> image1 = imageModel.generate(question); var url = image1.content().url().toString(); -
Tools (Function calling) Toolとして利用可能なメソッドを定義 class RateCalculator { @Tool("基準通貨に基づいた為替レートを取得します。") String calculator(String
a, String b) { String result = null; String url = "https://..."; try (Client client = ClientBuilder.newClient()) { result = client.target(url) .request(MediaType.APPLICATION_JSON_TYPE) .get(String.class); } catch (Exception e) { throw new RuntimeException(e); } return result; } } -
Tools (Function calling) AiServicesから利用できるようにInterfaceを定義 interface Assistant { String rate(String userMessage);
} Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(chatLanguageModel) .tools(new RateCalculator()) .chatMemory(MessageWindowChatMemory.withMaxMessages(10)) .build(); var ans = assistant.rate("USDを基準にしたJPYの為替レートを教えてください。"); -
AI Servicesの利用 文章中の情報をもとに、POJOを返す interface PersonExtractor { @UserMessage("次の文章から人の情報を抽出してください。: {{it}}") Person extractPersonFrom(@V("it")
String text); } // ResponseFormat形式はJSON PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, chatLanguageModel); return personExtractor.extractPersonFrom("..."); -
Embedding Embedding Model テキストをベクトルデータに変換 AzureOpenAiEmbeddingModel Embedding Store ベクトルデータの保存先 Azure AI
Search Azure CosmosDB Mongo vCore Azure CosmosDB (NoSQL) その他PostgreSQL、Redis、Cassandraなど Embedding Models | LangChain4j Comparison table of all supported Embedding Stores | LangChain4j -
RAG (Retrieval-Augmented Generation) Easy RAG LangChain4jがよしなにやってくれる カスタマイズしたRAGに比べると、精度は落ちる
RAG APIを使ってカスタマイズ (Naïve RAG) Advanced RAG Deconstructing RAG (langchain.dev) [2312.10997] Retrieval-Augmented Generation for Large Language Models: A Survey (arxiv.org) -
User message + Contents Advanced RAG Foundation for advanced RAG
by langchain4j · Pull Request #538 · langchain4j/langchain4j (github.com) 2 Vector store RDB or Graph DB 3 4 ContentRetriever-A ContentRetriever-B 1 QueryTransformer ContentInjector QueryRouter 5 ContentAggregator 6 Query User message -
RAGでの考慮事項 プロトタイピング時 In-process embeddingモデルとIn-process embeddingストアを使って手軽に確認 期待した精度が出ないときは
ユースケースや文書コーパスにあわせて、分割、Embeddingパラメータを最適化 ドキュメント分割前にドキュメントを整備しておく 分割したドキュメント(セグメント)にコンテキストを付加する Advanced RAG 良好な結果が得られるが、レイテンシが大きい -
Embedding & Query ModelとStoreのクライアント EmbeddingModel embeddingModel = AzureOpenAiEmbeddingModel.builder() .endpoint(AZURE_OPENAI_ENDPOINT) .apiKey(AZURE_OPENAI_APIKEY)
.deploymentName(DEPLOYMENT_MODEL_NAME) .tokenizer(new OpenAiTokenizer( OpenAiEmbeddingModelName.TEXT_EMBEDDING_3_SMALL)) .build(); EmbeddingStore<TextSegment> embeddingStore = AzureAiSearchEmbeddingStore.builder() .endpoint(AZURE_AISEARCH_ENDPOINT) .apiKey(AZURE_AISEARCH_KEY) .dimensions(1536) .build(); -
Embedding & Query ドキュメントの取り込み・解析 // HTMLからテキストを抽出し、分割してEmbeddingを生成し、Embedding Storeに格納 EmbeddingStore<TextSegment> embeddingStore; EmbeddingModel
embeddingModel; String url = "https://...."; DocumentParser documentParser = new TextDocumentParser(); HtmlTextExtractor htmlTextExtractor = new HtmlTextExtractor(); Document document = htmlTextExtractor.transform( UrlDocumentLoader.load(url, documentParser)); DocumentSplitter splitter = DocumentSplitters.recursive(600,30); List<TextSegment> segments = splitter.split(document); List<Embedding> embeddings = embeddingModel.embedAll(segments).content(); embeddingStore.addAll(embeddings, segments); -
Embedding & Query RAG (プロンプトとクエリ) interface Assistant { @SystemMessage(“”“ あなたはJava言語のエキスパートです。
Instructions: - Java SE 22言語仕様に基づき回答ください。 - 回答は日本語でお願いします。 - わからない場合は、"わからない" と答えてください。 - 参考文献のURLを提示してください。 """) Result<String> answer(String query); } Assistant createAssistant() { ContentRetriever contentRetriever = EmbeddingStoreContentRetriever .builder() .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .maxResults(3).minScore(0.6).build(); ChatMemory chatMemory = MessageWindowChatMemory .withMaxMessages(10); return AiServices .builder(Assistant.class) .chatLanguageModel(chatLanguageModel) .contentRetriever(contentRetriever) .chatMemory(chatMemory).build(); } // プロンプトの受け渡しと回答の取得 String query="..."; Assistant assistant = createAssistant(); return assistant.answer(query).content(); -
認証・認可・RBAC API Key でも TokenCredential でも利用可能 DefaultAzureCredential Class |
Microsoft Learn // API Keyを利用する場合 AzureOpenAiChatModel.builder() .endpoint(AZURE_OPENAI_ENDPOINT) .apiKey(AZURE_OPENAI_APIKEY) .deploymentName(DEPLOYMENT_MODEL_NAME) .temperature(1.0) .build(); // TokenCredentialを利用する場合 // この例ではPlatformのSystem assigned // managed identityを利用する前提 AzureOpenAiChatModel.builder() .endpoint(AZURE_OPENAI_ENDPOINT) .tokenCredential( new DefaultAzureCredentialBuilder() .build()) .deploymentName(DEPLOYMENT_MODEL_NAME) .temperature(1.0) .build(); -
Azure API Managementが Azure OpenAI Serviceの前段にある場合 LangChain4jが期待しているものに合わせる 基本的にLangChainが期待しているものと同じ
API認証 APIリクエスト: Frontend (Inbound section) でリクエストを認証 (validate-jwtポリシー) バックエンド: API ManagementのManaged Identityで認証(Authorizationヘッダーを上書き) LangChain4j API Management エンドポイント /openai で終わる 任意 API Key api-key 任意 デフォルトはocp-apim-subscription-key -
複数のインスタンスに負荷分散して、TPMを稼いでいる場合 ロードバランサー Application Gateway Front Door (API Management) (L7のみ利用可) エンドポイント
/openai で終わること (API Managementと同じ) モデルデプロイ名 利用するAzure OpenAI Service全インスタンスで名称を統一 API認証 Microsoft Entra ID認証 -
絶賛開発進行中 0.31で GPT-4o が追加 今後、Tokenizer (jtokkit) がアップデートされる予定
GPT-4o でアップデートされたTokenizerに追随 Feature requestsなどはIssuesをチェック Issues · langchain4j/langchain4j (github.com) -
リソース ドキュメント LangChain4j | LangChain4j Overview (LangChain4j
:: Aggregator 0.31.0-SNAPSHOT API) リポジトリ langchain4j/langchain4j: Java version of LangChain (github.com)