解説前の注意事項

大嶋勇樹氏:というところで本題のLangChainに入っていこうと思いますが、LangChainに入門する前に(お話しすることとして)、一概にLangChainを使うべきと主張する意図はないです。

LangChainは日々大きなアップデートが入るライブラリになっていて、破壊的変更が入る可能性は低くないと思います。なので、ここからの入門はあくまでLangChainの紹介・説明で、使うべき、使わないべきといった意見とは別とさせてください。使う、使わないは各自というか、組織でしっかり判断するのがいいかなと思っています。

そもそもLangChainは何に使えるのか

ということで、LangChainの紹介から入っていこうと思います。先ほど文脈要約させてChatGPTに言わせましたが、LangChainはLLMを使ったアプリケーション開発のフレームワークです。GitHubで公開されているオープンソースで、MITライセンスで使えるようになっています。

実装としてはPythonとJavaScript、TypeScriptの3つがあります。そして、こういう機械学習やそっち系のライブラリ(では)あるあるなんですが、JavaScriptやTypeScriptよりも、Pythonのほうがやはり開発は活発です。今日の勉強会でもPythonのサンプルコードを見ていきます。

今日の時点でPythonの実装はバージョン0.0.144ということで、バージョンの付け方がすごいです。パッチバージョンがどんどん上がっていくような、破壊的変更とかがぜんぜんあり得るという意思を感じるようなバージョンの付け方で、すごいなというところですね。0.0.144が145、146と、もう本当に毎日のように上がっている感じですね。

LangChainがそもそもなにに使えるのかから入っていこうと思います。ユースケースは公式ドキュメントでもけっこう紹介されています。

簡単に言うと、例えば要約アプリみたいなのを作るとか、チャットボットを作るとか、ドキュメントにQ&Aできるボットを作るとかいろいろあると思いますが、そういうもの(に対して)全体的に使えます。LangChainはLLMを使ったアプリケーション開発で、すごく幅広く使えるようなものになっています。本当にいろいろな例で使えます。

LangChainの6つのモジュール

何に使えるかは機能を見ていくのがいいかと思います。LangChainを学ぶ時は、モジュールごとに理解していくことになります。モジュールとかコンポーネントとか……。公式ドキュメントでも表現は揺れているんですが、モジュールごとに「なんだこれは?」というものを理解していきます。

そして、LangChainのモジュールは大きく6個あります。Models、Prompts、Chains、Indexes、Memory、Agents、この6個です。これを順に見ていこうと思うんですが、まずは基本となるModels、Prompts、Chainsの3つから見ていこうと思います。

LangChainのモジュール「Models」

まずModelsです。これは想像がつくかもしれませんが、LangChainで使う機械学習モデルのことを指します。このモデルは3種類あります。1つはLLMs、GPT-3.5のtext-davinci-003みたいな大規模言語モデルもあれば、Chat ModelsというOpenAIのChat APIのためのモジュールもあれば、Text Embedding Modelsというテキストをベクトル化するモデルもあります。

基本的にLLM、いわゆる大規模言語モデルはLangChainのモデルの中でもLLMsというものに含まれるんですが、OpenAIのChatのAPIはAPIの形式が少し特殊なので、それ用のChat Modelsという特別なモデルがあったりもします。

プラス、後でもまた登場するんですが、テキストをベクトル化するモデルもあります。ここで押さえておきたいポイントとしては、簡単に言えば、LLMをLangChain流で使えるのがLangChainのModelsというやつです。いわゆるラッパーですね。

使い方は資料にも載っていますが、せっかくなのでコードを動かしていきます。(画面を示して)Pythonでこんなコードを書いています。これだけですね。これだけで動きます。

poetry run python learning_langchain/1.models.pyとして動かします。

応答には少し時間がかかるのでその間に内容を見ていくと、モデルの名前をtext-davinci-003と指定して、temperatureとかそういうパラメーターを指定して、LLMに対して「GPTとして自己紹介してください。日本語で2文程度でお願いします」というプロンプトを与えます。

すると応答が返ってきて、それをprintして表示しています。「私はGPTです。自然言語処理を用いて、自然な文章を生成することができます」と返ってきた感じですね。

このModelsはLangChainのすごく重要な要素ではあるんですが、これを使うだけだと、直接OpenAIのAPIを使うのと、大きな違いはないと言えばないです。できること自体はLLMを呼び出せるだけです。このModelsだけではそうなんですが、続きでだんだんLangChainができることがいろいろ見えてきます。

LangChainのモジュール「Prompts」

次はPromptsというモジュールです。LangChainのPromptsというモジュールですが、これはモデルへの入力を組み立てるモジュールです。プロンプトを組み立てるようなモジュールということですね。

大きくはPrompt TemplatesやChat Prompt TemplatesやExmaple Selectors、Output Parsersなど、いろいろな要素がありますが、ここでは一番わかりやすいPrompt Templatesを見ていこうと思います。わかりやすいというよりは、一番基本になっている要素という感じですね。このPrompt Templatesというプロンプトの中の要素を見ていこうと思います。

これはなにかというと、その名のとおり、プロンプトをテンプレート化できるものになっています。

先ほど「カレー」と入力されたら、それをプログラムに埋めて使いたいみたいなことを例として紹介しましたが、このための要素みたいな感じですね。「プログラムのテンプレートを用意しておいてプログラムに埋める」みたいなことができるのが、Prompt Templatesです。

(画面を示して)実際どうやって使うかも見てみようと思うんですが、こんな感じですね。

ソースコードがあって、これを実行すると、こんなふうに表示されるわけです。

なにをしているかというと、まず「template =」というところで、Pythonの文字列を複数行書ける書き方で「次のコマンドの概要を説明してください。コマンド: {command}」と書いて、テンプレートを埋める場所に穴を開けておきます。

LangChainのPromptTemplateを使って、「input_variables、入力の変数名はcommandですよ。そしてtemplateはこの文字列ですよ」ということを書いてあげます。

そしてpromptに対してformatというメソッドを使って、「commandは『echo』」みたいに書いてあげる。その結果を表示すると(埋める場所が)「echo」と埋まっています。これだけと言えばこれだけなんですが、このようにプロンプトにパラメーターを埋められるのがPrompt Templatesです。

Prompt Templatesはこんなふうにプログラムで文字列の一部を置き換えているだけです。Prompt Templatesの中でこの置き換えのためにLLMのAPIを呼び出すことはしていなくて、LangChainのPython実装の内部で、echoに置き換えているだけですね。

これだけだったら別に「自分でそういうコードを書けばいいじゃん」ぐらいの機能かもしれませんが、実はこのPromptsの機能の中には、ほかにもプロンプトの長さを考慮しつつ、Few-shotプロンプティングのための例をいくつか埋め込むというものもあります。

例えば、ユーザーの入力が長かったら例を少なくしないとプロンプトの量があふれちゃうので、そういう調整をしながら埋め込むとか、出力をJSONとかで取り出しやすい形式のプロンプトをいい感じで生成して埋め込むとか、そんな機能もあります。そのあたりまで考えると、けっこう便利な機能だったりしますね。

LangChainのモジュール「Chains」

Models、Templatesに続いて、Chainsという要素を見ていきます。このLangChainのChainも、こういう(使い方の)ところから(名前の由来が)来ているのかもしれませんが、このChainsというのは、モジュール、ModelsやTemplatesやChainsを連結するものになっています。

先ほどTemplatesを見てみましたが、Templatesはテンプレートを埋めるだけじゃ意味がなくて、それをOpenAIのAPIに渡したいわけですね。埋めたテンプレートを基にAPIを呼び出して応答を得るという一連の流れをやりたい。そういうことができるのがChainsです。

これも実装を見て動かしてみようと思います。「verbose = True」をいったん消して、poetry run python learning……。このプログラムを動かしてみます。

中でコードがどうなっているかを見ると、最初に「llm = OpenAI……」、なんとかということでモデルを用意して、次にプロンプトを用意しました。テンプレートを用意して、テンプレートのどこが穴埋めするところかみたいなものを指定したプロンプトを用意しました。

そして、chainを作ります。このchainに対して、「run("echo")」みたいなことをすると、echoがプロンプトと置き換わったものが出来上がります。

置き換わったものを、OpenAIのモデル、APIに対して投げて応答を得て、最後にそれを表示しているという感じですね。

ちなみに「langchain」は「verbose = True」みたいなことをすると、中の挙動をある程度見ることができるので、それでもう1度ちょっと実行してみます。今話したとおりですが、「Prompt after formatting:」ということで、フォーマットをかけたプロンプトが表示されます。

これをOpenAIのAPIに投げると、こんなふうに表示されます。その結果が最後にprintで表示される流れですね。

これがChainsの一番基本的というか、簡単な使い方です。TemplatesとModelsを組み合わせて、くっつけて(という)流れとして使います。

Chainsはいろいろ実は種類があるんですが……。あっ、SimpleSequentialChainですね。すみません、(スライドは)Simpleが抜けていますが、SimpleSequentialChainというものを使って、プロンプトエンジニアリングの例で見たCoT(Chain of Thought)をやって、プラスで要約するみたいなこともできたりします。

これも実際に実装を見ていこうと思いますが、時間がかかるので、実行しながら見ていこうと思います。

(画面を示して)これはなにをしているかというと、まず1つ目にPromptとChainを用意します。

そして、ざっくり見て2つ目のPromptとChainを用意します。

ChainをつないだChainにして実行しています。

もうちょっと見ていくと、まずモデルとしてはOpenAIのtext-davinci-003を準備しています。

1つ目のプロンプトでは「以下の質問に回答してください」。ここの質問はテンプレート部分ですね。そして「### 質問終了 ###」。そして「ステップバイステップで考えましょう」という、いわゆるCoTのプロンプトを用意しているわけですね。そしてモデルとプロンプトをつないだChain、「cot_chain」として用意しておきます。

さらに、2つ目のプロンプトとしてサマライズするプロンプトを用意しています。「入力を結論だけ一言に要約してください」「### 入力 ###」「### 入力終了 ###」。これは要約させるためのものですね。「summarize_prompt」「summarize_chain」みたいな感じで用意します。

そして、このCoTをステップバイステップで考えると、サマライズをつなぎます。

そして実行します。「私は市場に行って10個のリンゴを買いました。隣人に2つ、修理工に2つ渡しました。それから5つのリンゴを買って1つ食べました。残りは何個ですか?」と。

そして最後に返ってきた結果を表示しているんですが、ステップバイステップで考えて要約させて、最終的な結果としては「残り5個です」となりました。

(画面を示して)ちなみにこれ(の答え)は合ってないんですが、text-davinci-003は、これをやっても合っていないことを言うことがあります。

ポイントは中で2つのステップがあるということです。(画面を示して)verboseにして実行したので、中でやっていることはこんなふうに表示されます。

見てみると、まず1個目のプロンプトの穴埋めが始まります。1個目のプロンプトは「以下の質問に回答してください」「ステップバイステップで考えましょう」みたいなテンプレートだったわけですが、この質問が埋まった状態でプロンプトが出来上がります。

これをAPIに投げると、青い箇所、「ステップバイステップで考えましょう」と言った結果、ステップバイステップで考えた内容が返ってきます。ただ、実際にアプリケーションを作る時はこんな内容をユーザーに表示したいんじゃなくて、含まれる結論だけを表示したいようなこともあると思います。

そんな時に、結論を要約するプロンプトをテンプレートとして用意して、そこに埋め込んで要約させることができるわけですね。つまり、ここでもう1回LLMを呼び出しているわけです。

こういうことをしてあげると、最終的な結論だけを表示することができます。内部でLLMの呼び出しが2回かかっているので、もちろん料金はその分かかりますが、そういうこともできます。

これがSimpleSequentialChainというやつです。

内部の動きとかもちょっと(スライドに)出していますが。

ということで、ここまでLangChainのModels、Prompts、Chainsといった最も基礎的なモジュールを見てきました。ModelsはLangChainで使う機械学習モデル。Promptsはモデルへの入力を組み立てる。Chainsはモジュールを連結ということで見てきましたが、おもしろいのはここからです。

(次回につづく)