前回はPDFドキュメントをvector index化して自然言語で検索するアプリを検討してみました。
今回は単にドキュメントを検索するだけでなく、ChatGPTのように自然言語による会話で回答してくれるアプリを作ってみたいと思います。
PDF文献について自然言語で質疑応答させる
LangChainにはまさにこのようなユースケースに利用できるChainが例として掲載されています。
Retrieval Question/Answering — 🦜🔗 LangChain 0.0.123
基本はこちらの例を利用してやってみます。
環境の準備
Google Colabで必要なパッケージをインストールします。
|
|
続いてOpenAIのAPI keyをセットします。
|
|
ドキュメントの準備
今回もPDFドキュメントとして、azu (azu) · GitHubさんがCC BY NNで公開されているJavascript Promiseの薄い本を利用させていただきます。
ここまではほぼ前回と同じです。
|
|
アプリの準備とテスト
まず必要なものをインポートします。
|
|
あとは例に従ってプロンプトを構築していきます。
公式サイトの例では Chroma
を利用していましたが、ここでは前回と同じくFAISSをRetrieverとして利用します。
|
|
構築したChainに質問してみます。
|
|
Promiseとは、非同期処理を抽象化したオブジェクトとそれを操作する仕組みのことを指します。 JavaScriptにおけるPromiseは、ES6の仕様で定義されたAPIであり、Promiseコンストラクタ関数であるPromiseからインスタンスとなるpromiseオブジェクトを作成して利用します。 Promiseは、コールバックや即時関数を置き換え、コードの可読性や再利用性を向上させることができます。 また、Promiseを利用することで、非同期処理を簡単にパターン化することができます。
内容はよさそうです。ソースのドキュメントに照らしてみます。
Chapter.1に以下のような記述がありますが、他の説明についてはそれっぽい記述はないためOpenAIのAPIによって生成されていそうです。
Promiseは非同期処理を抽象化したオブジェクトとそれを操作する仕組みの事をいいます。
様々な質疑を試してみる
ドキュメントに書かれている内容について聞いてみる
|
|
Promise.all()を使用すると、渡された複数のPromiseオブジェクトを並列に実行し、すべてのPromiseがFulfilledまたはRejectedになるまで待機し、その後次の処理を実行します。 次の処理には、Promise.all()に渡した順序でresolveされた値の配列が渡されます。 これにより、複数の非同期処理を並行実行し、そのすべての結果を同時に受信できます。
内容は正しそうですが、これといった記述は見つけられませんでした。
|
|
Promises/A+とは、Promiseのthenについて取り決めたコミュニティベースの仕様で、ES6 Promisesの前身となったものです。 Promiseのライブラリを探す際には、Promises/A+互換という言葉がよく使われます。 Promises/A+互換と書かれている場合は、多くの場合Promise.allやcatchなどと同様の機能が実装されていることが期待されます。 しかし、Promises/A+はPromise#thenについてのみの仕様となっているため、他の機能は実装されていても名前が異なる場合があります。
こちらについては以下の内容がありました。
p.116の記述。
ES6 Promisesの前身となったコミュニティベースの仕様で あり、ES6 Promisesとは多くの部分が共通している。
p.60の記述。
Promises/A+互換と書かれていた場合はについての動作は互換性があり、多くの場合はそれに加えてPromise.allや等と同様の機能が実装されています。 しかし、Promises/A+はPromise#thenについてのみの仕様となっているため、他の機能は実装されていても名前が異なる場合があります。
かなり内容的な一致が見られました。
書かれていない内容を聞いてみる
|
|
コード中に"hoge"という変数や関数などの定義はありませんので、何を返すかはわかりません。ただ存在しないものを返すわけではありません。
ちょっと質問がいじわるすぎたかもしれませんが、わからないという回答は出力されました。
|
|
Async Functionを使うと、非同期処理をシンプルに扱えるようになります。 Async FunctionはPromiseを返しますが、内部でawaitを使ってプログラムの流れを待機させることができるため、コールバック関数を使う場合よりも可読性が高く、コードの書き方が簡単になります。 また、Async Function自身が非同期処理であるため、thenやcatchを使う必要がなく、より直感的なコードを書くことができます。
自分が利用している版はかなり古いため、Async Functionについての記述はありません。 しかし、ちょっと類似した内容として以下のような説明をしている部分があります。
|
|
asyncFunction という関数 は promiseオブジェクトを返していて、 そのpromise オブジェクトに対して then でresolveした時のコールバックを、 catch でエ ラーとなった場合のコールバックを設定しています。
しかしこれらは参照されず、async/awaitに対する返答が出力されたようです。
サンプルコードを出してもらう
|
|
Markdown形式でサンプルコードを提示してくれました。
|
|
これはp.42に掲載されている「Promise.race」セクションのサンプルコードそのままでした。
ドキュメントのページを聞いてみる
|
|
Promise.raceの説明は、JavaScript Promiseの本の43ページに書かれています。
p.42から43にかけて「Promise.race」セクションが記述されているので正しいです。
|
|
JavaScript Promiseの本のコンテキストによって、Thenableについて言及しているページは複数あります。以下のページでThenableについて解説しています。
- 第4章のPromise.resolveとThenable
- 第18ページの「JavaScript Promiseの本」の説明
- 第70ページのThenableを利用することで、中間的な実装をする方法
以上のページで解説されています。
Chapter.4 Advanced以下に「Promise.resolveとThenable」という節があります。
p.18はPromise.resolveについての説明をしている箇所ですが、以下のような記述がちょうどあるところです。
ThenableについてはPromiseを使ったライブラリを書くとき等には知っておくべきで すが、 通常の利用だとそこまで使う機会がないものかもしれません。
p.70はChapter.4 Advanced以下「Promise.resolveとThenable」セクションの一節の指摘です。
指摘としては全部間違ってはいないようです。「Promise.resolveとThenable」の節を出力できているのは悪くない精度だと感じます。
回答のソースを求めて確認する
回答を見ていると、ChatGPTと会話をしているのと変わらないくらいの自然さで、参照している文献の内容について回答を得られていると感じました。 ただ必ずしも文献を当たって回答しているだけというわけではないようなところもあり、本当に文献から引かれているかわからないケースも見られました。
このようなケースでは、どこを参照して回答を提示したのかを質問することで妥当性の検証もできます。 ChatGPTでは既に存在しないWebページを提示してきたり、関係が薄いものを提示することがありますが、 LangChainを使ったアプリでは参照文献を提示しているため、このような質問で正確性を担保することもできそうです。
例えば、先程のAsync Functionについて確認してみます。
|
|
このテキスト中には、Async Functionについての説明は見当たりません。
と記載がないことが確認できます。
今回のアプリでは会話のコンテキストを維持していないため、指示語による過去の会話内容を示しての質問はできないですが、 LangChainの機能として会話履歴を維持させることも可能なようです。
まとめ
LangChainを使って、特定のPDF文献をソースとして自然言語による会話で質疑応答ができるアプリを試作してみました。
文献をコンテキストとして共有していることで、文献を参照しての回答を指示するような質問や、ソースを要求することで情報の正当性も確認しやすくなったように感じます。
LangChainを利用することで、当初の目的であった効率的な学習という意味では、ChatGPTのような汎用言語モデルを直接使うよりもかなり使い易いアプリケーションを構築できそうです。
先日Github NextにおいてMDNなどの文献に対する質疑が可能な「Github Copilot for Doc」が発表されましたが、現在は特定のDocに固定されているようです。
LangChainであれば、様々な文献やWebサイトをマッシュアップした自分だけのCopilot for Docを作成することができます。 機能的にも十分な出力が期待できそうです。
これを拡張していけば、例えば会話の開始前にドキュメントを渡してコンテキストを固定する、というようなアプリケーションも作成できそうです。