Upgrade to Pro — share decks privately, control downloads, hide ads and more …

脱ブラックボックス化!LLMと一緒に使われるLangChainやLlamaIndexを徹底解説

 脱ブラックボックス化!LLMと一緒に使われるLangChainやLlamaIndexを徹底解説

nakamura.shogo

July 11, 2023
Tweet

More Decks by nakamura.shogo

Other Decks in Programming

Transcript

  1. 2 自己紹介 中村祥吾 (ハラショー / nokomoro3) データアナリティクス事業本部 インテグレーション部 機械学習チーム いまのお仕事:

    機械学習の分析・環境構築プロジェクト+技術調査 機械学習を使用した自社サービス開発 https://linktr.ee/nokomoro3
  2. 4 OpenAI APIの話 OpenAI APIとは OpenAI社が提供するAPI OpenAI社が開発した言語モデル「GPT」や 書き起こし「Whisper」などをHTTPのREST APIとして利用可能 API

    KEYを使用すれば従量課金で 自社プロダクトやサービスにモデルの機能を統合可能 手順はとても簡単 ・Googleアカウントなどでサインアップ ・API KEYを生成すれば利用可能 https://platform.openai.com/signup
  3. 5 OpenAI APIの話 GPT関連のリリース変遷 2020年05月28日:GPT-3が発表 2020年06月11日:GPT-3のAPIが公開 2022年03月15日:GPT-3.5が発表 2022年09月21日:Whisperが発表 2022年11月30日:ChatGPT(Webサービス)がリリース 2023年02月13日:ChatGPT

    Plusのリリース 2023年03月01日:ChatGPT(GPT-3.5-turbo)とWhisperのAPIが公開 2023年03月14日:GPT-4のリリース 2023年03月24日:ChatGPT Pluginsの登場 2023年06月13日:GPT-3.5-turboとGPT-4のAPIアップデート
  4. 6 OpenAI APIの話 curlを使ったリクエスト例 パラメータとしては"model"と"messages"が必須 "messages"は"role"と"content"のペアのリスト "role"には、"system"、"user"、"assistant"を指定可能 curl https://api.openai.com/v1/chat/completions ¥

    -H "Content-Type: application/json" ¥ -H "Authorization: Bearer $OPENAI_API_KEY" ¥ -d '{ "model": "gpt-3.5-turbo", "messages": [ {"role": "system", "content": "Pythonのエキスパートとして質問に答えてください。"}, {"role": "user", "content": "Pythonの環境構築方法について教えてください。"}] }'
  5. 7 OpenAI APIの話 モジュールを使ったリクエスト例 PythonまたはNode.jsから"openai"モジュールを利用可能 import openai # 直接指定するか、環境変数OPENAI_API_KEYに入れておけばで自動で読み込み可能 #

    openai.api_key = "APIキーを記載" model_name = "gpt-3.5-turbo" question = "Pythonの環境構築方法について教えてください。" response = openai.ChatCompletion.create( model=model_name, messages=[ {"role": "system", "content": "Pythonのエキスパートとして質問に答えてください。"}, {"role": "user", "content": question}, ], )
  6. 8 OpenAI APIの話 レスポンス例 choicesに回答の選択肢がリストとして取得(通常は一つ) choicesのmessageはrequestと同じ形式 usageに消費したトークンが得られ、これが従量課金の指標 <OpenAIObject chat.completion id=chatcmpl-7XSExEiVgwifgqNtXZk15zatEZNPb

    at 0x153ccf823f0> JSON: { "id": "chatcmpl-7XSExEiVgwifgqNtXZk15zatEZNPb", "object": "chat.completion", "created": 1688207735, "model": "gpt-3.5-turbo-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Python¥u306e¥u74b0¥u5883¥u69cb..." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 53, "completion_tokens": 509, "total_tokens": 562 } } 1. Pythonをダウンロードする:Python公式ウェブサイト (https://www.python.org/downloads/) から、最新のPython バージョンをダウンロードしましょう。Windowsの場合はインストーラを、macOSやLinuxの場合はパッケージマネージャ を使用することもできます。 2. インストーラを実行する:Windowsの場合はダウンロードしたインストーラを実行し、指示に従ってPythonをインス トールします。macOSやLinuxの場合は、ターミナルでインストールコマンドを実行することでインストールできます。 3. 環境変数の設定:Pythonコマンドをターミナルから実行できるように、環境変数の設定を行います。Windowsの場合 は、インストール時に「Add Python to PATH」オプションを選択するか、環境変数に手動でPythonのインストールパス を追加します。macOSやLinuxの場合は、ターミナルの設定ファイル (例えば、.bashrcや.profile) にPythonのインス トールパスを追加します。 4. 正常にインストールされたか確認する:ターミナルを開き、`python --version`または`python3 --version`コマン ドを実行して、Pythonのバージョンが表示されればインストールは成功しています。 上記の手順に従って環境構築を行えば、Pythonを使用する準備が整います。また、特定のパッケージやツールを使いた い場合は、`pip`を使って必要なパッケージをインストールすることも忘れずに行いましょう。
  7. 9 OpenAI APIの話 APIリファレンス https://platform.openai.com/docs/api-reference/chat/create Parameter Type Required Default Range

    説明 model string Required - - "gpt-3.5-turbo"や"gpt-4"などを指定 messages array Required - - そこまでのチャット履歴やシステム設定を指定 functions array Optional - - Function calling機能 function_call string Optional - - Function calling機能 temperature number Optional 1 0.0~2.0 高いほどランダム、低いほど決定論的に動作するパラメータ(top_pとの併用は非推奨) top_p number Optional 1 0.0~1.0 上位p%以上の確率を持つトークンのみを採用する(temperatureとの併用は非推奨) n integer Optional 1 - 各入力メッセージに対していくつのチャット補完選択肢を生成するか stream boolean Optional false - 有効とすると結果を差分で得ることができる stop string or array Optional null - 特定のトークンが出現したら強制的に終了させることができる max_tokens integer Optional inf - 出力トークン数の最大数(トークン制限は入力+出力なので注意) presence_penalty number Optional 0 -2.0~2.0 トークンが出現済みかどうかでペナルティを課す定数 frequency_penalty number Optional 0 -2.0~2.0 トークンの頻度に基づいてペナルティを課す定数 logit_bias map Optional null - 指定したトークンが出現する確率を変更するバイアス user string Optional - - エンドユーザーを表す一意の識別子で、監視と不正利用の検出に使用可能 必須のパラメータ
  8. 10 OpenAI APIの話 APIリファレンス https://platform.openai.com/docs/api-reference/chat/create Parameter Type Required Default Range

    説明 model string Required - - "gpt-3.5-turbo"や"gpt-4"などを指定 messages array Required - - そこまでのチャット履歴やシステム設定を指定 functions array Optional - - Function calling機能 function_call string Optional - - Function calling機能 temperature number Optional 1 0.0~2.0 高いほどランダム、低いほど決定論的に動作するパラメータ(top_pとの併用は非推奨) top_p number Optional 1 0.0~1.0 上位p%以上の確率を持つトークンのみを採用する(temperatureとの併用は非推奨) n integer Optional 1 - 各入力メッセージに対していくつのチャット補完選択肢を生成するか stream boolean Optional false - 有効とすると結果を差分で得ることができる stop string or array Optional null - 特定のトークンが出現したら強制的に終了させることができる max_tokens integer Optional inf - 出力トークン数の最大数(トークン制限は入力+出力なので注意) presence_penalty number Optional 0 -2.0~2.0 トークンが出現済みかどうかでペナルティを課す定数 frequency_penalty number Optional 0 -2.0~2.0 トークンの頻度に基づいてペナルティを課す定数 logit_bias map Optional null - 指定したトークンが出現する確率を変更するバイアス user string Optional - - エンドユーザーを表す一意の識別子で、監視と不正利用の検出に使用可能 0613版で追加された機能(後述)
  9. 11 OpenAI APIの話 APIリファレンス https://platform.openai.com/docs/api-reference/chat/create Parameter Type Required Default Range

    説明 model string Required - - "gpt-3.5-turbo"や"gpt-4"などを指定 messages array Required - - そこまでのチャット履歴やシステム設定を指定 functions array Optional - - Function calling機能 function_call string Optional - - Function calling機能 temperature number Optional 1 0.0~2.0 高いほどランダム、低いほど決定論的に動作するパラメータ(top_pとの併用は非推奨) top_p number Optional 1 0.0~1.0 上位p%以上の確率を持つトークンのみを採用する(temperatureとの併用は非推奨) n integer Optional 1 - 各入力メッセージに対していくつのチャット補完選択肢を生成するか stream boolean Optional false - 有効とすると結果を差分で得ることができる stop string or array Optional null - 特定のトークンが出現したら強制的に終了させることができる max_tokens integer Optional inf - 出力トークン数の最大数(トークン制限は入力+出力なので注意) presence_penalty number Optional 0 -2.0~2.0 トークンが出現済みかどうかでペナルティを課す定数 frequency_penalty number Optional 0 -2.0~2.0 トークンの頻度に基づいてペナルティを課す定数 logit_bias map Optional null - 指定したトークンが出現する確率を変更するバイアス user string Optional - - エンドユーザーを表す一意の識別子で、監視と不正利用の検出に使用可能 ランダム性や選択肢を設定可能 temperature=0.0とするサンプルも多い
  10. 12 OpenAI APIの話 APIリファレンス https://platform.openai.com/docs/api-reference/chat/create Parameter Type Required Default Range

    説明 model string Required - - "gpt-3.5-turbo"や"gpt-4"などを指定 messages array Required - - そこまでのチャット履歴やシステム設定を指定 functions array Optional - - Function calling機能 function_call string Optional - - Function calling機能 temperature number Optional 1 0.0~2.0 高いほどランダム、低いほど決定論的に動作するパラメータ(top_pとの併用は非推奨) top_p number Optional 1 0.0~1.0 上位p%以上の確率を持つトークンのみを採用する(temperatureとの併用は非推奨) n integer Optional 1 - 各入力メッセージに対していくつのチャット補完選択肢を生成するか stream boolean Optional false - 有効とすると結果を差分で得ることができる stop string or array Optional null - 特定のトークンが出現したら強制的に終了させることができる max_tokens integer Optional inf - 出力トークン数の最大数(トークン制限は入力+出力なので注意) presence_penalty number Optional 0 -2.0~2.0 トークンが出現済みかどうかでペナルティを課す定数 frequency_penalty number Optional 0 -2.0~2.0 トークンの頻度に基づいてペナルティを課す定数 logit_bias map Optional null - 指定したトークンが出現する確率を変更するバイアス user string Optional - - エンドユーザーを表す一意の識別子で、監視と不正利用の検出に使用可能 トークン数制限
  11. 13 OpenAI APIの話 トークンとは? ・大規模言語モデルが系列の要素と見なす単位 ・gpt-3.5-turboなどの場合、日本語で約1トークン1文字程度(平均) ・あくまで平均なので単語単位のトークンや1文字以下のトークンも存在 import tiktoken from

    tiktoken.core import Encoding encoding: Encoding = tiktoken.encoding_for_model("gpt-3.5-turbo") # encoding: Encoding = tiktoken.get_encoding("cl100k_base") # 同じtokenizerが得られる tokens = encoding.encode("Pythonの環境構築方法について教えてください。") [encoding.decode_bytes([t]) for t in tokens] token 31380 16144 163 240 108 43244 225 162 100 233 83898 231 token (bytes) b'Python' b'¥xe3¥x81¥xae' b'¥xe7' b'¥x92' b'¥xb0' b'¥xe5¥xa2' b'¥x83' b'¥xe6' b'¥xa7' b'¥x8b' b'¥xe7¥xaf' b'¥x89' 表記 Python の 環 境 構 築 41007 20230 59739 16995 38144 8067 247 ... b'¥xe6¥x96¥xb9¥xe6¥xb3¥x95' b'¥xe3¥x81¥xab' b'¥xe3¥x81¥xa4' b'¥xe3¥x81¥x84' b'¥xe3¥x81¥xa6' b'¥xe6¥x95' b'¥x99' ... 方法 に つ い て 教 ...
  12. 14 OpenAI APIの話 トークン制限について ・最大トークン数に制限 ・モデルよって異なる値(4k~32kトークン) ・入力と出力の合計トークンが制限の対象単位 ・入力が長すぎると出力が短く生成(あるいはエラー) ・トークン制限への対応 ・入力はtiktokenでざっくり見積もり可能

    ・出力はmax_tokensパラメータで指定可能 モデル 最大トークン数 用途 gpt-3.5-turbo 4k(4096) ブログ記事、コラム、短い報告書など gpt-4 8k(8192) 長めのブログ記事やレポートなど gpt-3.5-turbo-16k 16k(16384) 長編エッセイ、詳細なリサーチレポート、解説記事など gpt-4-32k 32k(32768) 詳細な技術解説、学術論文、長編レポートなど
  13. 15 OpenAI APIの話 入力・出力って何のこと? ・OpenAI APIはステートレスで、GPTは次の単語を予測しているのみ ・会話の流れに沿うには、過去すべての履歴が入力に必要 ・「すべての履歴」と「トークン制限」の両方のケアが必要 import openai

    model_name = "gpt-3.5-turbo" question = "今日はお肉を使った料理を作りたいです。いくつかメニュー案を考えてください。" messages = [ {"role": "system", "content": "あなたは家庭料理に関するアドバイザーです。"}, {"role": "user", "content": question}, ] response = openai.ChatCompletion.create( model=model_name, messages=messages ) システム設定 最初の質問 messages.append(response["choices"][0]["message"]) messages.append({"role": "user", "content": "ローストビーフの作り方の詳細を教えてください。"}) response = openai.ChatCompletion.create( model=model_name, messages=messages ) 最初の回答 システム設定 最初の質問 最初の回答 次の質問 次の回答 入力:messages 出力:レスポンス 入力:messages 出力:レスポンス
  14. 16 OpenAI APIの話 補足:ChatGPT(Webアプリ)の比較 ・OpenAI APIの制限は多いものの 「入力データの扱い」や「従量課金」の面でメリットが多い Open AI API

    ChatGPT(Webアプリ) 入力データの扱い 使用されない (30日間保存後削除) 使用される チャット履歴の管理 ユーザー側で自作が必要 アプリ側で仕組みが提供 (実現方法は不明) URL参照やWeb検索等 参照できない (シンプルに次の文章を予 測するのみ、それらしい結 果はURLから推測) pluginでカバー可能 料金 従量課金 無料(機能制限あり) or 定額(plus)
  15. 17 OpenAI APIの話 OpenAI APIの課題の整理 ・チャット履歴管理 ・トークン制限 ・長い文章要約や長い文脈依存は対応困難 ・プロンプトの管理 ・Web検索などの外部ツールとの連携

    ・最近はFunction callingで解決策が提示 ・事前学習データの限界 ・学習データは2021年9月までのもの ・それ以降に関する情報の回答は困難 ・プライベートデータに基づく回答も困難 これらを解決する フレームワーク・サービス LangChain LlamaIndex Semantic Kernel Guidance Add your data (Azure OpenAI) Prompt Flow (Azure Machine Learning)
  16. 18 OpenAI APIの話 補足:Function callingとは ・0613のアップデート時に追加された機能 ・APIが外部機能を使うかどうかをユーザのクエリから自動で判断 ・functionsで外部機能をAPIに通知 ・ユーザのクエリとfunctionsの内容 でfunctionを使うか判断

    ・使う場合はAPIのレスポンスから functionの名前と引数が取得 functions=[ { "name": "search_menu", "description": "指定した食材のキーワードでメニューを検索", "parameters": { "type": "object", "properties": { "ingredient": {"type": "string", "description": "食材"}, }, "required": ["ingredient"], }, }, { "name": "search_recipe", "description": "指定したメニューについてレシピを検索", "parameters": { "type": "object", "properties": { "menu": {"type": "string", "description": "メニュー名"}, }, "required": ["menu"], }, } ]
  17. 19 OpenAI APIの話 補足:Function callingとは ・0613のアップデート時に追加された機能 ・APIが外部機能を使うかどうかをユーザのクエリから自動で判断 { "role": "user",

    "content": "今日はお肉を使った料理を作りたいです。¥ いくつかメニュー案を考えて作り方を教えてください。" } { "role": "assistant", "content": null, "function_call": { "name": "search_menu", "arguments": "{¥n ¥"ingredient¥": ¥"¥u304a¥u8089¥"¥n}" } } システム設定 最初の質問 最初の回答
  18. 21 OpenAI APIの話 補足:Function callingとは ・0613のアップデート時に追加された機能 ・APIが外部機能を使うかどうかをユーザのクエリから自動で判断 { "role": "function",

    "name": "search_menu", "content": "ローストビーフ" } { "role": "assistant", "content": null, "function_call": { "name": "search_recipe", "arguments": "{¥n ¥"menu¥": ¥"¥u30ed¥u30fc¥u30b9¥u30c8¥u30d3¥u30fc¥u30d5¥"¥n}" } } システム設定 最初の質問 最初の回答 関数の実行結果 次の回答
  19. 22 OpenAI APIの話 補足:Function callingとは ・0613のアップデート時に追加された機能 ・APIが外部機能を使うかどうかをユーザのクエリから自動で判断 ユーザ側で"search_recipe" を実行して結果を得る "1.

    オーブンを200℃に予熱します。¥n 2. 牛肉を室温に戻します。..." システム設定 最初の質問 最初の回答 関数の実行結果 次の回答
  20. 23 OpenAI APIの話 補足:Function callingとは ・0613のアップデート時に追加された機能 ・APIが外部機能を使うかどうかをユーザのクエリから自動で判断 { "role": "function",

    "name": "search_recipe", "content": "..." } { "role": "assistant", "content": "<最終的な回答>" } システム設定 最初の質問 最初の回答 関数の実行結果 次の回答 関数の実行結果 最終的な回答
  21. 25 OpenAI APIの話(再掲) OpenAI APIの課題の整理 ・チャット履歴管理 ・トークン制限 ・長い文章要約や長い文脈依存は対応困難 ・プロンプトの管理 ・Web検索などの外部ツールとの連携

    ・最近はFunction callingで解決策が提示 ・事前学習データの限界 ・学習データは2021年9月までのもの ・それ以降に関する情報の回答は困難 ・プライベートデータに基づく回答も困難 これらを解決する フレームワーク・サービス LangChain LlamaIndex Semantic Kernel Guidance Add your data (Azure OpenAI) Prompt Flow (Azure Machine Learning)
  22. 27 LangChainの概要 LangChainとは ・チャットのアプリケーションを実現する様々なパーツと広範な仕組みを提供 ・事実上のデファクトスタンダード(AWSやAzureとの組み合わせ) ・モジュール Agent(Chain) : ええ感じに自動判断 Chain

    : 基本部品 Model IO : 最小部品 PromptTemplate LLM OutputParser Data Connection : 外部ソース Memory : 記憶の仕組み DocumentLoader Embeddings VectorStore Tool : 外部ツール ConversationBufferMemory ConversationSummaryMemory ... LLMChain SequentialChain SQLDatabaseChain RetrievalQA ConversationChain ZERO_SHOT_REACT_DESCRIPTION CONVERSATIONAL_REACT_DESCRIPTION OPENAI_FUNCTIONS SerpAPI LLMMathChain
  23. 28 LangChainの概要 最小部品を使ったシンプルなサンプル from langchain import OpenAI llm = OpenAI(temperature=0)

    result = llm("オススメのお肉の料理を3つ教えて。") print(result) 1. 焼肉 2. ローストビーフ 3. ハンバーグ
  24. 29 LangChainの概要 Chatモデルを使う ・GPT-3.5-turboやGPT-4の場合はこちらを使う必要がある from langchain.chat_models import ChatOpenAI from langchain.schema

    import ( AIMessage, HumanMessage, SystemMessage ) llm = ChatOpenAI(model="gpt-3.5-turbo") result = llm([ HumanMessage(content="オススメのお肉の料理を3つ教えて。") ]) print(result.content) 1. ステーキ:ジューシーで柔らかいお肉を楽しむなら、ステー キはおすすめです。牛肉や豚肉など、お好みのお肉を選んで焼き 上げ、塩や胡椒でシンプルに味付けするのが定番です。ステーキ ソースやマッシュルームソースなどのソースを添えても美味しい です。 2. ローストビーフ:ジューシーなビーフの塊をオーブンでじっ くり焼き上げたローストビーフは、特別な日のメインディッシュ として人気です。肉の外側がカリッと焼け、内側は柔らかくピン ク色に仕上げるのがポイントです。ハーブやスパイスで風味を付 けたり、ホースラディッシュソースやマスタードソースなどと一 緒に食べるのがおすすめです。 3. カルビ焼き:韓国料理の代表的なお肉料理であるカルビ焼き は、牛肉のバラ肉を甘辛いタレで焼いたものです。お肉が柔らか くてジューシーなので、食べ応えがあります。一緒に焼く野菜や キムチと一緒に巻いて食べるのが一般的ですが、ご飯の上に盛り 付けても美味しいです。
  25. 30 LangChainの概要 言語モデルとPromptTemplateをセットにする ・LLMChainを使うことでセットにできる from langchain.llms import OpenAI from langchain

    import PromptTemplate, LLMChain template = """オススメの{食材}の料理を3つ教えて。""" prompt = PromptTemplate(template=template, input_variables=["食材"]) llm = OpenAI(temperature=0) llm_chain = LLMChain(prompt=prompt, llm=llm) result = llm_chain.run("お肉") print(result)
  26. 31 LangChainの概要 チャットモデルとPromptTemplateをセットにする ・LLMChainに加え、PromptTemplateの構成がポイント from langchain.chat_models import ChatOpenAI from langchain.prompts.chat

    import ( ChatPromptTemplate, HumanMessagePromptTemplate, ) from langchain import LLMChain llm = ChatOpenAI(temperature=0) prompt = ChatPromptTemplate.from_messages( [HumanMessagePromptTemplate.from_template("""オススメの{食材}の料理を{個数}個教えて。""")] ) llm_chain = LLMChain(llm=llm, prompt=prompt) result = llm_chain.run(食材="お肉", 個数="5") print(result)
  27. 32 LangChainの概要 複数のLLMChainを組み合わせる ・SequentialChainで接続し、1番目のChainの出力を2番目に渡せる from langchain.chat_models import ChatOpenAI from langchain.prompts.chat

    import ( ChatPromptTemplate, HumanMessagePromptTemplate, ) from langchain import LLMChain from langchain.chains import SequentialChain llm = ChatOpenAI(temperature=0) prompt = ChatPromptTemplate.from_messages( [HumanMessagePromptTemplate.from_template("""オススメの{食材1}と{食材2}の料理のメニューを考えて名前を1つ教えて。""")] ) llm_chain1 = LLMChain(llm=llm, prompt=prompt, output_key="メニュー") prompt = ChatPromptTemplate.from_messages( [HumanMessagePromptTemplate.from_template("""{メニュー}の作り方を教えて。""")] ) llm_chain2 = LLMChain(llm=llm, prompt=prompt, output_key="手順") sequential_chain = SequentialChain( chains=[llm_chain1, llm_chain2], input_variables=["食材1", "食材2"], output_variables=["メニュー", "手順"], verbose=True) result = sequential_chain({"食材1": "お肉", "食材2": "トマト"}) print(result)
  28. 33 LangChainの概要 その他のChain ・LLMRouterChain : 分岐を作ることのできるChain ・SQLDatabaseChain : SQLにクエリを投げて結果を元に回答するChain ・ConversationChain

    : Memoryと連携して履歴を保持しながら会話するChain ・RetrievalQA : 外部ソースの情報を元に回答するChain
  29. 34 LangChainの概要 RouterChain ・destinations_strを元にrouter_promptでrouteを判断 from langchain.chains.router import MultiPromptChain from langchain.chains.llm

    import LLMChain from langchain.prompts import PromptTemplate from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE llm = ChatOpenAI(temperature=0) prompt = ChatPromptTemplate.from_messages( [HumanMessagePromptTemplate.from_template("""{input}の作り方を教えて""")] ) llm_chain1 = LLMChain(llm=llm, prompt=prompt) prompt = ChatPromptTemplate.from_messages( [HumanMessagePromptTemplate.from_template("""{input}の作り方を教えて""")] ) llm_chain2 = LLMChain(llm=llm, prompt=prompt) destination_chains = { "お肉": llm_chain1, "お魚": llm_chain2 } destinations = [ "お肉: お肉の料理用のChainです", "お魚: お魚の料理用のChainです", ] destinations_str = "¥n".join(destinations) router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str) router_prompt = PromptTemplate( template=router_template, input_variables=["input"], output_parser=RouterOutputParser(), ) router_chain = LLMRouterChain.from_llm(llm, router_prompt) chain = MultiPromptChain( router_chain=router_chain, destination_chains=destination_chains, default_chain=llm_chain1, verbose=True, ) result = chain.run(input="ブリの照り焼き") print(result)
  30. 35 LangChainの概要 SQLDatabaseChain ・DBのメタデータからクエリを自動で作成する from langchain.chat_models import ChatOpenAI from langchain.prompts.chat

    import ( ChatPromptTemplate, HumanMessagePromptTemplate, ) from langchain import LLMChain from langchain import SQLDatabase, SQLDatabaseChain sql_url = "sqlite:///sqlite-sakila-sample-database/sqlite-sakila.db" tables = ["customer", "store", "staff"] sql_database = SQLDatabase.from_uri( sql_url, include_tables=tables) llm = ChatOpenAI(temperature=0) template = ¥ """ Given an input question. Question: {input} First create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer. Only use the following tables: {table_info} """ prompt = ChatPromptTemplate.from_messages( [HumanMessagePromptTemplate.from_template(template)] ) llm_chain = LLMChain(llm=llm, prompt=prompt) db_chain = SQLDatabaseChain( llm_chain=llm_chain, database=sql_database, return_intermediate_steps=True) result = db_chain("姓が「MA」で始まる顧客情報を3名分取得し、その名前を回答してください。") print(result["result"])
  31. 36 LangChainの概要 ConversationChain ・Memoryに会話履歴の保持が可能 from langchain.chat_models import ChatOpenAI from langchain.prompts.chat

    import ( ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder, ) from langchain.chains import ConversationChain from langchain.memory import ConversationBufferMemory llm = ChatOpenAI(temperature=0) memory = ConversationBufferMemory(return_messages=True) prompt = ChatPromptTemplate.from_messages([ MessagesPlaceholder(variable_name="history"), HumanMessagePromptTemplate.from_template("""{input}""") ]) llm_chain = ConversationChain(llm=llm, prompt=prompt, memory=memory) result = llm_chain.run("オススメのお肉料理を一つ教えて。") print(result)
  32. 37 LangChainの概要 Memoryの種類 ・様々な種類のMemoryがある ConversationBufferMemory すべての会話履歴を使用するメモリ ConversationBufferWindowMemory 最新K回の会話履歴を使用するメモリ ConversationTokenBufferMemory 最新Kトークンの会話履歴を使用するメモリ

    ConversationSummaryMemory 会話履歴の要約を使用するメモリ ConversationSummaryBufferMemory 最新Kトークンの会話履歴の要約を使用するメモリ ConversationEntityMemory 会話内のエンティティ情報を記憶して必要に応じて使用するメモリ ConversationKGMemory ナレッジグラフの情報を使用するメモリ VectorStoreRetrieverMemory 会話履歴をベクトルデータベースに保存して上位K個の類似情報を使用するメモリ
  33. 39 LangChainの概要 RetrievalQA ・外部ソースをインデックス化して検索して回答 from langchain.document_loaders import TextLoader from langchain.text_splitter

    import CharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.chains import RetrievalQA from langchain.chat_models import ChatOpenAI loader = TextLoader('./input/developers-io-in-las-vegas-2022.txt', encoding='utf8') documents = loader.load() text_splitter = CharacterTextSplitter(separator="。", chunk_size=600, chunk_overlap=0) texts = text_splitter.split_documents(documents) embeddings = OpenAIEmbeddings() vector_store = Chroma.from_documents(texts, embeddings) retriever = vector_store.as_retriever() chain = RetrievalQA.from_chain_type(llm=ChatOpenAI(), chain_type="stuff", retriever=retriever) query = "re:Inventではどのようなアップデートがありましたか?" result = chain.run(query) print(result)
  34. 41 LangChainの概要 RetrievalQAを例に ・内部の動きが良く分からん… import langchain langchain.debug = True [chain/start]

    [1:chain:RetrievalQA] Entering Chain run with input: { "query": "re:Inventではどのようなアップデートがありましたか?" } [chain/start] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain] Entering Chain run with input: [inputs] [chain/start] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain] Entering Chain run with input: { "question": "re:Inventではどのようなアップデートがありましたか?", "context": "¥"はい、みなさんこんばんは。 クラスメソッドがですね、ラスベガスからリインベントの様子をお届けしま す。 ... } [llm/start] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain > 5:llm:ChatOpenAI] Entering LLM run with input: { "prompts": [ "System: Use the following pieces of context to answer the users question. ¥nIf you don't know the answer, just say that you don't know, don't try to make up an answer.¥n----------------¥n¥"はい、みなさんこんばんは。 ] } [llm/end] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain > 5:llm:ChatOpenAI] [1.15s] Exiting LLM run with output: { "generations": [ [ { ...
  35. 44 LangChainの概要 RetrievalQAを例に ・カスタマイズの例 : contextの数をデフォルトから変更 from langchain.document_loaders import TextLoader

    from langchain.text_splitter import CharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.chains import RetrievalQA from langchain.chat_models import ChatOpenAI loader = TextLoader('./input/developers-io-in-las-vegas-2022.txt', encoding='utf8') documents = loader.load() text_splitter = CharacterTextSplitter(separator="。", chunk_size=600, chunk_overlap=0) texts = text_splitter.split_documents(documents) embeddings = OpenAIEmbeddings() vector_store = Chroma.from_documents(texts, embeddings) retriever = vector_store.as_retriever() retriever.search_kwargs = {"k": 1} chain = RetrievalQA.from_chain_type(llm=ChatOpenAI(), chain_type="stuff", retriever=retriever) query = "re:Inventではどのようなアップデートがありましたか?" result = chain.run(query) print(result)
  36. 45 LangChainの概要 RetrievalQAを例に ・カスタマイズの例 : プロンプトを日本語に from langchain.document_loaders import TextLoader

    from langchain.text_splitter import CharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.chains import RetrievalQA from langchain.chat_models import ChatOpenAI loader = TextLoader('./input/developers-io-in-las-vegas-2022.txt', encoding='utf8') documents = loader.load() text_splitter = CharacterTextSplitter(separator="。", chunk_size=600, chunk_overlap=0) texts = text_splitter.split_documents(documents) embeddings = OpenAIEmbeddings() vector_store = Chroma.from_documents(texts, embeddings) retriever = vector_store.as_retriever() prompt_template = """以下の文脈を利用して、最後の質問に答えなさい。答えがわからない場合は、答えを作ろうとせず、わからないと答えましょう。 {context} Question: {question} Helpful Answer:""" prompt = PromptTemplate( template=prompt_template, input_variables=["context", "question"] ) chain = RetrievalQA.from_chain_type(llm=ChatOpenAI(), chain_type="stuff", retriever=retriever, chain_type_kwargs={"prompt": prompt}) query = "re:Inventではどのようなアップデートがありましたか?" result = chain.run(query) print(result)
  37. 46 LangChainの概要 AgentとTools ・AgentはToolsを自動で選択、会話履歴も保持可能 from langchain.agents import load_tools from langchain.chains.conversation.memory

    import ConversationBufferMemory from langchain.agents import initialize_agent from langchain.agents import AgentType from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(temperature=0) # メモリの作成 memory = ConversationBufferMemory( memory_key="chat_history", return_messages=True) # ツールの準備 tools = load_tools( tool_names=["serpapi", "llm-math"], llm=llm) # エージェントの作成 agent = initialize_agent( agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, llm=llm, tools=tools, memory=memory, verbose=True) result = agent.run("今日の東京の天気をWeb検索してください。") print(result) Input: ユーザのクエリ Thought: AgentはToolを使うべきか判断 Action: 使用するToolと引数を判断 Observation: ツールの出力結果を確認 を繰り返してThoughtで終了と判断するまで 繰り返す Googleの論文「ReAct」で提案されたため ReActフレームワークと呼ばれる
  38. 47 LangChainの概要 AgentTypeについて ZERO_SHOT_REACT_DESCRIPTION ツールの説明に基づいて使用するツールを決定するエージェント CONVERSATIONAL_REACT_DESCRIPTION 会話履歴を記憶し、ReActフレームワークを使用してツールを決定する エージェント CHAT_ZERO_SHOT_REACT_DESCRIPTION ChatOpenAIモデル用のZERO_SHOT_REACT_DESCRIPTION

    CHAT_CONVERSATIONAL_REACT_DESCRIPTION ChatOpenAIモデル用のCHAT_CONVERSATIONAL_REACT_DESCRIPTION REACT_DOCSTORE ReActフレームワークを使用してドキュメントストアと対話するエー ジェント SELF_ASK_WITH_SEARCH Intermediate Answerというツールを使用するエージェントで、「self ask with search」の論文に相当 STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION OPENAI_FUNCTIONS 単純にOpenAIのFunction callingを使用する機能。 OPENAI_MULTI_FUNCTIONS OpenAIのFunction callingを使用する機能だが間にtool_selectionを挟 んで問い合わせ回数を削減している
  39. 48 LangChainの概要 LangChainとは ・チャットのアプリケーションを実現する様々なパーツと広範な仕組みを提供 ・事実上のデファクトスタンダード(AWSやAzureとの組み合わせ) ・モジュール Agent(Chain) : ええ感じに自動判断 Chain

    : 基本部品 Model IO : 最小部品 PromptTemplate LLM OutputParser Data Connection : 外部ソース Memory : 記憶の仕組み DocumentLoader Embeddings VectorStore Tool : 外部ツール ConversationBufferMemory ConversationSummaryMemory ... LLMChain SequentialChain SQLDatabaseChain RetrievalQA ConversationChain ZERO_SHOT_REACT_DESCRIPTION CONVERSATIONAL_REACT_DESCRIPTION OPENAI_FUNCTIONS SerpAPI LLMMathChain
  40. 50 LlamaIndexの概要と仕組み LangChainとLlamaIndexの違い ・LangChainのモジュール Agent(Chain) : ええ感じに自動判断 Chain : 基本部品

    Model IO : 最小部品 PromptTemplate LLM OutputParser Data Connection : 外部ソース Memory : 記憶の仕組み DocumentLoader Embeddings VectorStore Tool : 外部ツール ConversationBufferMemory ConversationSummaryMemory ... LLMChain SequentialChain SQLDatabaseChain RetrievalQA ConversationChain ZERO_SHOT_REACT_DESCRIPTION CONVERSATIONAL_REACT_DESCRIPTION OPENAI_FUNCTIONS SerpAPI LLMMathChain LlamaIndexはここに特化
  41. ChatEngine QueryEngine LlamaIndexとは ・事前学習済みのLLMをプライベートなデータで拡張することに特化 ・多様なインデックス構造やレスポンスモードが存在 ・モジュール 51 LlamaIndexの概要と仕組み Storage Context

    Document Store Vector Store Index Store Service Context Node Parser Embeddings LLMPredictor PromptHelper CallbackManager LlamaLogger Index Retriever Response Synthesizer RetrieverQueryEngine PandasQueryEngine SimpleChatEngine NLSQLTableQueryEngine ListIndex VectorIndex TreeIndex
  42. 53 LlamaIndexの概要と仕組み ユースケース例を実現するサンプル ・もっともシンプルな場合は非常に短いコードで実現可能 from llama_index import SimpleDirectoryReader from llama_index

    import VectorStoreIndex documents = SimpleDirectoryReader("./input").load_data() vector_index = VectorStoreIndex.from_documents(documents) query_engine = vector_index.as_query_engine() response = query_engine.query("機械学習に関するアップデートについて箇条書きで教えてください。")
  43. 54 LlamaIndexの概要と仕組み シンプルな例の中身 ・インデックス構築時 データソース プレーン テキスト 適切な サイズに インデックス

    構築 Node Parser Embeddings LLMPredictor PromptHelper Data Connectors (LlamaHub) Index ListIndex VectorIndex TreeIndex LlamaLogger CallbackManager
  44. 55 LlamaIndexの概要と仕組み シンプルな例の中身 ・クエリ実行時 インデックス (構築済み) Retriever 抽出した テキスト 再度適切な

    サイズに Embeddings PromptHelper LLM レスポンス LLMPredictor Response Synthesizer 最終結果 LlamaLogger CallbackManager
  45. 57 LlamaIndexの概要と仕組み ノウハウ:CallbackManagerを有効化する ・各ブロックでの入出力をデバッグ可能 from llama_index import SimpleDirectoryReader from llama_index

    import VectorStoreIndex from llama_index import ServiceContext from llama_index.callbacks import CallbackManager, LlamaDebugHandler, CBEventType documents = SimpleDirectoryReader("./input").load_data() llama_debug_handler = LlamaDebugHandler() callback_manager = CallbackManager([llama_debug_handler]) service_context = ServiceContext.from_defaults(callback_manager=callback_manager) list_index = VectorStoreIndex.from_documents(documents, service_context=service_context) query_engine = list_index.as_query_engine() response = query_engine.query("機械学習に関するアップデートについて箇条書きで教えてください。") llama_debug_handler.get_event_pairs(CBEventType.LLM) CBEventType.CHUNKING : テキスト分割処理の前後 CBEventType.NODE_PARSING : NodeParserの前後 CBEventType.EMBEDDING : 埋め込みベクトル作成処理の前後 CBEventType.LLM : LLM呼び出しの前後 CBEventType.QUERY : クエリの開始と終了 CBEventType.RETRIEVE : ノード抽出の前後 CBEventType.SYNTHESIZE : レスポンス合成の前後 CBEventType.TREE : サマリー処理の前後最終結果
  46. 58 LlamaIndexの概要と仕組み ノウハウ:チャンク分割をカスタムする ・区切り文字やチャンクサイズ、オーバーラップ度合を設定可能 from llama_index import SimpleDirectoryReader from llama_index

    import VectorStoreIndex from llama_index import ServiceContext from llama_index.node_parser import SimpleNodeParser from llama_index.langchain_helpers.text_splitter import TokenTextSplitter from llama_index.constants import DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE import tiktoken documents = SimpleDirectoryReader("./input").load_data() text_splitter = TokenTextSplitter(separator=" ", chunk_size=DEFAULT_CHUNK_SIZE , chunk_overlap=DEFAULT_CHUNK_OVERLAP , tokenizer=tiktoken.get_encoding("gpt2").encode) node_parser = SimpleNodeParser(text_splitter=text_splitter) service_context = ServiceContext.from_defaults( node_parser=node_parser ) list_index = VectorStoreIndex.from_documents(documents, service_context=service_context) query_engine = list_index.as_query_engine() response = query_engine.query("機械学習に関するアップデートについて箇条書きで教えてください。") separator : 区切り文字。ここの指定文字がチャンク分割 の切れ目になるようチャンクを作ります。 chunk_size : チャンクサイズ。このチャンクサイズ以下 となるようチャンクを作ります。 chunk_overlap : チャンクのオーバーラップ。後ろのチャ ンクに前のチャンクの末尾を付け加えることで、チャンク 分割による文脈の断裂を緩和します。 tokenizer : tokenizerのencode処理。チャンクサイズを トークン数でカウントするために使用されます。
  47. 59 LlamaIndexの概要と仕組み ノウハウ:LLMをChatGPT相当にする ・LangChainのChatOpenAIをLLMPredictorでラップする from llama_index import SimpleDirectoryReader from llama_index

    import VectorStoreIndex from llama_index import ServiceContext from llama_index import LLMPredictor from langchain.chat_models import ChatOpenAI documents = SimpleDirectoryReader("./input").load_data() llm = ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo") llm_predictor = LLMPredictor(llm=llm) service_context = ServiceContext.from_defaults( llm_predictor=llm_predictor) list_index = VectorStoreIndex.from_documents(documents, service_context=service_context) query_engine = list_index.as_query_engine() response = query_engine.query("機械学習に関するアップデートについて箇条書きで教えてください。") 指定しない場合、text-davinci-003となるため注意
  48. 60 LlamaIndexの概要と仕組み ノウハウ:プロンプトを日本語化する ・ServiceContextは使わず、as_query_engineの引数にテンプレートを渡す from llama_index import SimpleDirectoryReader from llama_index

    import VectorStoreIndex documents = SimpleDirectoryReader("./input").load_data() vector_index = VectorStoreIndex.from_documents(documents) text_qa_prompt_template = ( "コンテキスト情報は以下です。¥n" "---------------------¥n" "{context_str}" "¥n---------------------¥n" "コンテキスト情報を考慮したうえで、以下の質問に答えなさい。¥n" "{query_str}¥n" ) refine_prompt_template = ( "元の質問はこちらです。: {query_str}¥n" "既に得られている回答はこちらです。: {existing_answer}¥n" "我々は必要な場合のみ、以下の新しいコンテキスト情報で既に得られている回答を洗練させる機会があります。¥n" "------------¥n" "{context_msg}¥n" "------------¥n" "新しいコンテキストを踏まえて、元の答えをより良いものに改良してください。¥n" "新しいコンテキストが有用でない場合は、元の答えを返してください。" ) query_engine = vector_index.as_query_engine(text_qa_prompt_template=text_qa_prompt_template, refine_prompt_template=refine_prompt_template) response = query_engine.query("機械学習に関するアップデートについて箇条書きで教えてください。") (注意) TreeIndexなどインデックス構築にLLMを使用する場合、 その時のプロンプトはインデックス作成時に引数で 渡す必要がある 元の英語プロンプトを参考にしている https://github.com/jerryjliu/llama_index/blob/main/ llama_index/prompts/default_prompts.py
  49. 61 LlamaIndexの概要と仕組み ノウハウ:ベクトル計算にHugging Faceを使う ・sentence_transformersを使いHugging Faceからモデルを取得 from llama_index import SimpleDirectoryReader

    from llama_index import VectorStoreIndex from langchain.embeddings.huggingface import HuggingFaceEmbeddings from llama_index import LangchainEmbedding, ServiceContext documents = SimpleDirectoryReader("./input").load_data() embed_model = LangchainEmbedding(HuggingFaceEmbeddings( model_name="oshizo/sbert-jsnli-luke-japanese-base-lite" )) service_context = ServiceContext.from_defaults( embed_model=embed_model) vector_index = VectorStoreIndex.from_documents(documents, service_context=service_context) query_engine = vector_index.as_query_engine() response = query_engine.query("機械学習に関するアップデートについて箇条書きで教えてください。") (注意) 言語モデル側のモデルの差し替え方法は調査中
  50. 62 LlamaIndexの概要と仕組み インデックス種類の違い ・厳密には後述のRetriever Modeで異なるがざっくりは以下 ListIndex VectorIndex TreeIndex ・基本的には全ノード使用 ・先頭から順にResponse

    Synthesisで 処理される ・キーワードはベクトル類似度で絞る ことも可能 ・ベクトル類似度で上位のノードを 取得する ・ノード数が一定以下の場合は 親ノードは作成されない ・親ノードは子ノードの要約となって おり、要約生成にLLMを使用する ・ノード選択は多くの種類が準備
  51. 63 LlamaIndexの概要と仕組み Retriever Mode List Index ListRetrieverMode.DEFAULT すべてのノードを抽出 List Index

    ListRetrieverMode.EMBEDDING 埋め込みベクトルを使って抽出 Vector Index 一意 埋め込みベクトルを使って抽出 Tree Index TreeRetrieverMode.SELECT_LEAF プロンプトを使ってLeafノードを探索して抽出 Tree Index TreeRetrieverMode.SELECT_LEAF_EMBEDDING 埋め込みベクトルを使ってLeafノードを探索して抽出 Tree Index TreeRetrieverMode.ALL_LEAF 全てのLeafノードを使いクエリ固有のツリーを構築して応答 Tree Index TreeRetrieverMode.ROOT ルートノードのみを使って応答 Table Index KeywordTableRetrieverMode.DEFAULT GPTを使ってクエリのキーワード抽出を行う Table Index KeywordTableRetrieverMode.SIMPLE 正規表現を使ってクエリのキーワード抽出を行う Table Index KeywordTableRetrieverMode.RAKE RAKEキーワード抽出器を使ってクエリのキーワード抽出を行 う
  52. 64 LlamaIndexの概要と仕組み Response Mode Refine Tree Summarize ResponseMode.REFINE 各ノードを順次調べて答えを作成しながら洗練 させていく。

    ResponseMode.COMPACT 実際はCompactAndRefineであり、ほぼRefineと 同じだが、チャンクをLLMのコンテキスト長を最 大限利用するようにrepackするためより高速。 ResponseMode.TREE_SUMMARIZE 抽出されたノードを使いサマライズを繰り返す ことでチャンクを圧縮した後にクエリを行う。 バージョンにより多少動作が変わる。 ResponseMode.SIMPLE_SUMMARIZE ノードを単純に結合してひとつのチャンクとし てクエリする。LLMコンテキスト長を超えるとエ ラーとなる。 ResponseMode.GENERATION ノード情報を使わず単にLLMに問い合わせる。 ResponseMode.ACCUMULATE 各ノードに対するLLMの結果を計算して結果を連 結する。 ResponseMode.COMPACT_ACCUMULATE ACCUMULATEとほぼ同じだが、チャンクをLLMのコ ンテキスト長を最大限利用するようにrepackす るためより高速。
  53. 66 その他のライブラリ Microsoft製のライブラリやサービスなど ・Semantic Kernel : LangChainなど同様、PromptTemplate、ベクトル化、 Chain of Thoughtなどが可能だが、C#の方がメインと

    いうイメージ ・Guidance : 穴埋めをしてくるようなライブラリ 穴埋めのため出力が予測しやすい形で入手できる ・Add your data : Azure OpenAI Studioで使用可能な外部ソースを簡単に 連携して使用できる仕組み。 Cognitive Searchなどとシームレスに連携。 ・Prompt Flow : Azure Machine Learningに追加された機能で、LLMが絡む 複雑な開発フローを対話型で実行することが可能
  54. 69