Slide 1

Slide 1 text

LangChain4jを使った生成AIシ ステム設計パターン 〜 Javaで構築する最新アーキテクチャ 経営企画本部 AI推進室 鏡味秀行 1

Slide 2

Slide 2 text

自己紹介 鏡味(かがみ) 秀行 (51歳) 現在: 自社の生成AIの普及展開 Java歴: Hotjava(‘95)~SI案 件15年ほど JHipsterで昨年JJUG登壇 日曜プログラミング、生成AI、 緑黄色社会、ドラム 2

Slide 3

Slide 3 text

本日のテーマ 生成AIを使ったシステムのアーキテクチャを学ぶ LangChain4jを通じてJava言語による生成AIシステムの 構築方法を理解 生成AIシステムの設計パターンを習得し今後の開発に活か せる 3

Slide 4

Slide 4 text

アジェンダ LangChain4jとLLMアーキテクチャ (15分) LLM抽象化/ Structured Outputs/ Function Calling RAG (Retrieval-Augmented Generation) (15分) Naive RAG/ Advanced RAG AIエージェント (10分) まとめとQ&A (5分) 4

Slide 5

Slide 5 text

軽めのデモ あなたはLangChain4jで作られたエージェントです。 これからJJUG(ジェイジャグ)の会場でLangChain4jのセ ッションをします。 会場にいる人に自己紹介と現在時刻を伝えてください。 あなたが時刻を知っていることは珍しいので、そのこと も伝えてください。 最後に短くご挨拶をしてください。 “ “ 5

Slide 6

Slide 6 text

デモ環境 10/18にOpenAIから出たAudio generation(従来のテキ スト用のAPIに音声も付く)をLangChain4jへQuickHack LangChain4jエージェントにて、時刻取りつつ挨拶 VSCodeでrapaio-jupyter-kernel実行 Java用のJupyter Kernel JShell実行、repo, dependency を記述可能 6

Slide 7

Slide 7 text

会場の方へヒアリング システム開発をしていてLLMフレームワークを組み込んで いる方 それをJavaで開発されている方 7

Slide 8

Slide 8 text

LangChain4j Java用のLLMフレームワーク Python/JavaScriptの LangChainに触発 広範囲な機能をアクティブに 多様なモデルやストア対応 様々なツールの提供 SpringやQuarkusとの統合 8

Slide 9

Slide 9 text

その他のJavaのフレームワーク Spring AI Springファースト TestContainers周りの充実 Semantic Kernel MSのPlatformのサポート C#, Python → Javaポーティング 9

Slide 10

Slide 10 text

LLM FWのアーキテクチャ 紹介するもの: LangChain4jの他 Spring AI, Semantic Kernel for Java など他でもサポートしている機能 ( 以降は一番メジャーなOpenAI社の機能名で説明) 1. LLMモデルなどのAPI抽象化 2. 構造データ出力 (Structured Ouputs ) 3. 関数呼び出し(Function Calling) 10

Slide 11

Slide 11 text

1. APIの抽象化 対応モデル: OpenAI, Azure OpenAI, Anthropic, Gemini, Ollama, etc... 以下 ️ 以外は共通 ChatLanguageModel model = OpenAiChatModel.builder() // ️ プロバイダー .apiKey("api key") // ️ APIキー .modelName(GPT_4_O_MINI) // ️ LLMモデル .build(); String answer = model.generate("「こんにちは」と言ってください"); System.out.println(answer); // → こんにちは 11

Slide 12

Slide 12 text

ストリーミング(流れるような文字) StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder()...; model.generate("皆さんへ挨拶を一言で", new StreamingResponseHandler() { // コールバック @Override public void onNext(String token) { System.out.print(token + "|"); // |皆|さん|、|こんにちは|!| } @Override public void onComplete(Response response) { } @Override public void onError(Throwable error) { } }); 12

Slide 13

Slide 13 text

2. Structured Outputs 天気 本⽇10/28の 天気は 曇りです。 10/28 曇り Structured Outputs 13

Slide 14

Slide 14 text

2. Structured Outputs 非構造化データ から 構造データ を抽出 LangChain4jでは Structured Data Extraction と呼 ばれる 天気 本⽇10/28の 天気は 曇りです。 10/28 曇り Structured Outputs 14

Slide 15

Slide 15 text

取りたい構造データの宣言 record WeatherInfo(LocalDate date, String weather) {}; 処理のインターフェースを宣言 interface WeatherForecast { // AiServices @SystemMessage("天気の情報を取得") WeatherInfo getWeatherInfo(String text); } 15

Slide 16

Slide 16 text

LLMモデルの作成 ChatLanguageModel strictModel = OpenAiChatModel.builder() .responseFormat("json_schema") // JSONスキーマ出力を強制する .strictJsonSchema(true) // JSONスキーマ出力を強制する .build(); インスタンス化して実行 WeatherForecast forecast = AiServices.create(WeatherForecast.class, strictModel); WeatherInfo info = forecast.getWeatherInfo("明日(2024/10/28)の天気は曇りです。"); // 実行 System.out.println(info); // WeatherInfo[date=2024-10-27, weather=曇り] 16

Slide 17

Slide 17 text

2. Structured Outputs 欲しいJavaの型をJSON Schemaに変換して送信 LLMにスキーマを認識してもらえてエラー率が減る 部品として小回りがきく Pythonでも Instructor, Outlines が好みの方も Pydantic(データのバリデーションや型ヒントの提 供)とJSON Schemaとの連携 ハルシネーションは起こしてしまうので対策は必須 17

Slide 18

Slide 18 text

AiServices (LangChain4j 固有名称) 18

Slide 19

Slide 19 text

Structured Outputs と AiServices Weather String 天気 10/28 曇り Weather AiService Code ⽂字から天気情報 を 抽出するよ "本⽇10/28の天 気は曇りです" 19

Slide 20

Slide 20 text

AiServices による通常の対話 こんにちは。 なにかお⼿伝いできますか︖ String String こんにちは︕ Human Assistant AiService 質問に 答えるよ 20

Slide 21

Slide 21 text

AiServices LangChain4jの高レベルの抽象化API 概念的にはエージェント(あとの方でお話) 基本機能: Structured Outputs 機能を持つ対話型API 通常の対話 String型 → String型 JPAのO/Rマッピングに似ている 機能のインジェクションで、エージェント能力強化 21

Slide 22

Slide 22 text

3. Function Calling 14:30です String String ⽇本は今何時ですか︖ Human Timekeeper AiService LocalDateTime String 14:30 Clock #getCurrentTime (String zoneId) 時刻来たかぁ… 関数に頼ろう Asia/Tokyo 22

Slide 23

Slide 23 text

3. Function Calling LLMに用意された関数を実行するかを判断してもらう LLMが考えるのは 関数名 と 引数 のみ。実行はLLMの呼び 出し側で行う。 呼び出され側で行う Assistant API とは別モノ LangChain4jでは Tools と呼ばれる @Tool がついたメソッドを呼び出す 23

Slide 24

Slide 24 text

AiSerivices (処理のインターフェース) 宣言 interface TimeKeeper { @SystemMessage("現在の時刻を何時何分の形式で答えてください") String ask(String question); } Function (Tool) の宣言 class Clock { // Function群 (Tools) @Tool // 呼び出したいFunction (Tool) public LocalDateTime getCurrentTime(String timeZoneId) { return LocalDateTime.now(ZoneId.of(timeZoneId)); } } 24

Slide 25

Slide 25 text

インスタンス化 TimeKeeper timeKeeper = AiServices.builder(TimeKeeper.class) .chatLanguageModel(model) .tools(new Clock()) // 関数(ツール)群の注入 .build(); 実行 String answer = timeKeeper.ask("今何時?"); System.out.println(answer); // 例: "現在の時刻は16時58分です" 25

Slide 26

Slide 26 text

Function Calling の使用場面 LLMに足りない機能を補う用途に使う 複雑な計算や統計分析、時制や地理を含む話題など LLMに制御を委ねてよい場合に使う 制御を自前でしたければ Structured Outputs を活用 LLMの処理結果を構造データで返してもらう データを見て制御を行う 26

Slide 27

Slide 27 text

⾃分は知らないので 外部に問い合わせよう。 結果から回答⽂は作ろう (専⾨的な回答) (専⾨的な質問) Human LLMに無い 専⾨的な 情報 問い合わせ 専⾨情報 AiService RAG 27

Slide 28

Slide 28 text

RAG (Retrieval-Augmented Generation) 専門知識など、LLMが知らない情報を外部より能力強化 多くは検索エンジンと組み合わせ 自然言語と相性のよいベクター検索が人気 外部情報その他: ネットの情報, RDB, GraphDB 様々な手法が日々考案(裏を返せば期待と結果のギャップ が大きい) 28

Slide 29

Slide 29 text

(専⾨的な回答) (専⾨的な質問) Human Vector DB Document RAG Pipline Query embedding Chunked Txt Vec[12,-34,... Vec[11,-33,... Chunked Txt Naive RAG (Naive: 普通の) 29

Slide 30

Slide 30 text

Indexing ( RAG Pipiline ) (引用: https://docs.langchain4j.dev) 30

Slide 31

Slide 31 text

Indexing ( RAG Pipiline ) テキストを裁断してテキストチャンクに parsing, splitting, chunking テキストチャンクの意味をベクトル化 embedding DBに保存 indexing (専⾨的な回答) (専⾨的な質問) Human Vector DB Document RAG Pipline Query embedding Chunked Txt Vec[12,-34,... Vec[11,-33,... Chunked Txt 31

Slide 32

Slide 32 text

従来のETL(Extract/Transform/Load)ほぼ同様の概念 LLMシステムでは、より非構造データ・自然言語を対象と する傾向が強まる LlamaIndex の RAG Pipiline より データの取得(Load the data) データの変換(Transform the data) データ索引格納(Index and store the data) 32

Slide 33

Slide 33 text

// parsing: RAGで使うドキュメントを読む DocumentParser documentParser = new TextDocumentParser(); Document document = FileSystemDocumentLoader.loadDocument(toPath(documentPath), documentParser); // splitting, chunking: ドキュメントを300byteごとにチャンク分割 DocumentSplitter splitter = DocumentSplitters.recursive(300, 0); List segments = splitter.split(document); 33

Slide 34

Slide 34 text

// embedding: チャンクの数分、float[385]のembeddingを作成 EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel(); List embeddings = embeddingModel.embedAll(segments).content(); // indexing: インメモリストアに保存 EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); embeddingStore.addAll(embeddings, segments); 34

Slide 35

Slide 35 text

ローダー・パーサーのバリエーション DocumentLoader: Amazon S3, Azure Blob Storage, Google Cloud Storage, File System, URL,etc... DocumentParser: Text, Apache Tika (MSOffice(POI) / PDF(PDFBox)) Tikaがサポートするフォーマット https://tika.apache.org/3.0.0-BETA2/formats.html 構造はシンプルなので独自実装も十分可能 35

Slide 36

Slide 36 text

Querying (引用: https://docs.langchain4j.dev) 36

Slide 37

Slide 37 text

Querying 質問の意味をベクトルで数値化する embedding 類似の意味を持つ情報を取り出す retrive 抽出した情報をLLMが整理して回答する generation (専⾨的な回答) (専⾨的な質問) Human Vector DB Document RAG Pipline Query embedding Chunked Txt Vec[12,-34,... Vec[11,-33,... Chunked Txt 37

Slide 38

Slide 38 text

Injection Content Retriever Human VectorDB Query embedding AiService AiServiceでRAG 38

Slide 39

Slide 39 text

// コンテンツ取得担当(Retriever)を用意する ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) // ストアの注入 .embeddingModel(embeddingModel) // モデルの注入 .build(); 39

Slide 40

Slide 40 text

// 質問回答アシスタントを用意する(自前で定義) public interface Assistant { String answer(String query); // 質問したら回答 } // アシスタントのプロキシを作成 Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(chatLanguageModel) // 回答作成用モデル .contentRetriever(contentRetriever) // Retrieverの注入 .build(); // Retrieve-Argumented Generation: 質問するとドキュメントを検索して回答 String agentAnswer = assistant.answer(userQuery); 40

Slide 41

Slide 41 text

Retrieval Argmentor Injection Content Retriever Human AiService Query Transformer Content Aggregator Query Router Content Injector Advanced RAG 41

Slide 42

Slide 42 text

LangChain4jにおけるサポート RetrievalAugmentor: RAGプロセス全体を管理・制御 QueryTransformer : ユーザークエリを最適化 QueryRouter: 複数のRetrieverにクエリを振り分け ContentRetriever: ソースから情報取得 ContentAggregator: 取得したコンテンツを集約整理 ContentInjector: 取得したコンテンツをLLMのプロンプ トに挿入 42

Slide 43

Slide 43 text

QueryTransformer (専⾨的な回答) (専⾨的な質問) Human VectorDB Query embedding Vec[11,-33,... Query Transformer (ヒットしやすい 質問) 43

Slide 44

Slide 44 text

QueryTransformer ベクター検索でヒットさせるため、質問を回答に近づける 質問と回答が意味的に近い「わけではない」 例:「東京の観光スポットは?」→ 「東京で最も訪れる価 値のある観光地」「東京における観光客に人気の名所」 「東京都内の有名な見どころ」 44

Slide 45

Slide 45 text

ContentRetriever: Text-to-SQL SQL Content Retriever Human RDB AiService ⽇本の都市を… Text to SQL ”SELECT city FROM ... [東京, 神奈川,…] 東京や神奈川が... 45

Slide 46

Slide 46 text

Text-to-SQL ContentRetriever の一つ 自然言語からSQLを生成してRDBへ問い合わせ LangChain4jではSqlDatabaseContentRetrieverで試 験実装 自然言語クエリの解釈、SQLの生成と実行、エラー処理 から結果の整形して再実行 46

Slide 47

Slide 47 text

Text-to-SQLの様々な手法 自然言語→LLMが固定されたSQLを選択 自然言語→SQLテンプレート+LLM抽出パラメータ 自然言語→LLMがクエリの自動生成 スキーマや制約条件をLLMに与えて生成 その他、処理時間計測して最適化する例: Architectural Patterns for Text-to-SQL: Leveraging LLMs for Enhanced BigQuery Interactions 47

Slide 48

Slide 48 text

セマンティックレイヤー 概念的には「ビジネスドメインの意味を もたせる層」「人間とデータの仲介」 Text-to-SQLにおける「自然言語」と 「SQLやデータ」 LangChain4j では AiService 48

Slide 49

Slide 49 text

AIエージェント 49

Slide 50

Slide 50 text

生成AIのハイプ・サイクル 引用: Gartner、「生成AIのハイプ・サイクル:2024年」を 発表 より 50

Slide 51

Slide 51 text

AIエージェントとはなにか ユーザの代理として自律的(プロアクティブ)に実行する 実行計画を自分で立てる 自ら実行する 反省し学習し適応する 51

Slide 52

Slide 52 text

AIエージェントはなぜ必要? 複雑で動的な問題の自律的解決 ビジネス環境の不確実性が高く、全てを事前に予測困難 状況に応じて臨機応変にスケーラブルに対応 できることはAIに委譲し、限られた人的リソースは創造的 で価値の高いことに使いたい 52

Slide 53

Slide 53 text

Agentic Design Patterns https://www.deeplearning.ai/ (Andrew Ng先生)より リフレクション: LLMの自己評価、性質の異なるLLMによ る相互評価、改善し再実行 ツールの利用: 外部ツールやAPIの活用(Function Calling やRAG) 計画と実行: 目標設定とタスク分割 マルチエージェント: エージェント同士の協力でタスク実 行 53

Slide 54

Slide 54 text

START END Coding Test Code Test Comp? 計画と実行 LangChainのブログ'Planning for Agents'より 「PDCAプロセスをイチから考えて」 のLLMへの丸投げは今は辛み 人間がより具体化する必要 ドメイン固有の認知アーキテクチャ 状況把握と指示→状態遷移マシン ブログでは LangGraph を推奨 54

Slide 55

Slide 55 text

マルチエージェント DeepLearning.AIのAgentic Design Patterns Part 5, Multi-Agent Collaborationより 一つのLLMよりも、多様な意見を持つLLMに議論させたほ うが良い推論ができる LLMは人間と同じで、長く煩雑で雑多な指示が苦手 役割ごとに細分化する 例)システムプロンプトで「あなたは○○のエキスパー トです」を複数立てる 55

Slide 56

Slide 56 text

LangChain4jでAgent こいつです→AiServices 自律的に判断して行動 外部情報取得 Function Calling, RAG 今後よりエージェント対応していくと予想 計画と実行のための状態遷移マシン 発展途上だが LangGraph4j 56

Slide 57

Slide 57 text

LangChain4j + LangGraph4j Judge! Pros Cons START END Debate! 57

Slide 58

Slide 58 text

アーキテクチャスタイル LLMをツールとして使うか、エージェント として使うか 制御を手前か奥か Weather String 天気 10/28 曇り Weather AiService Code ⽂字から天気情報 を 抽出するよ "本⽇10/28の天 気は曇りです" Retrieval Argmentor Injection Content Retriever Human AiService Query Transformer Content Aggregator Query Router Content Injector 58

Slide 59

Slide 59 text

本日説明しきれなかったところ システム評価と改善 LLMの出力品質を評価し最適化を行うフィードバックル ープの構築 Javaではスタンダートなものは無く、しばらくはPython などのツールを使っていくことに 59

Slide 60

Slide 60 text

Java+LLMについての思い 舞台は整っている: 環境は整備され学習コストも低い JavaがあればJavaに: エンタープライズ系、既存システム のエンハンス 溜めたスキル活かす: 皆さまがこれまで習得したスキルが むしろ大事、そのままLLMシステムに活かせる LangChain4j: オススメですが、あくまで一つの代表例。 他のフレームワークも触っていただければ ️ 60

Slide 61

Slide 61 text

Spring Boot + LangChain4j 手軽にSpring BootでLangChain4jを試したかったら 拙作の JHipster LLM も触ってみてください https://github.com/hide212131/generator-jhipster-llm 以下のコマンドで実行環境ができます。 jhipster-llm generate-sample sample --llm-framework langchain4j 61

Slide 62

Slide 62 text

Q&A 62

Slide 63

Slide 63 text

ご清聴ありがとうございました! 63