【Chat with your PDF】2024年にPDF文書を読み解く人にお勧めしたい、サーバーレスなChatGPT活用アプリを公開

# 更新履歴
- 2024-02-17: RAG機能を追加しました。ChatGPTにPDFのページを検索させ、その検索結果を参照させながらQAや要約などの任意の生成処理を行わせることができます。

# 数クリックで要約完了、後は好きに質問を

PDFファイルを与えるとテキストを抽出してくれ、その内容をChatGPTに要約させたり、好きに質問して読み解かせたりできるアプリを公開します（下図参照）。

最大の特徴は、アプリ用のサーバーを立てる必要がないことです。
**アプリを使うには1個のHTMLファイルをブラウザで開くだけ。そう、ただそれだけ。**（Apple風）
HTMLファイルの置き場所はデスクトップでもいいですし、どこかのサイトでも。
アプリを自分用に改造することもしやすいでしょう。

* 数クリックで、PDF論文を[落合フォーマット](https://www.google.com/search?q=%E8%90%BD%E5%90%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)に従って要約させた例:
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/d65f28ef-3366-4620-7602-e52445fcc6d2.png" width=600>

特徴:

1. PDFファイルからのテキスト抽出機能
1. 抽出したテキストを用いた対話型チャット機能
1. いわゆるRAG機能（ChatGPTにPDFのページを検索させ、その検索結果を参照させながらQAなどを行わせることができる）
1. OpenAI APIまたはAzure OpenAI Serviceをバックエンドとして利用可能
1. URLにチャット履歴を保存可能（デフォルトではオフ）
1. 1ファイルからなるシンプルさ。アプリサーバー不要。自分用に改造しやすい。[MITライセンス](https://licenses.opensource.jp/MIT/MIT.html)。
1. **ChatGPT APIにアクセスする以外は、全てブラウザのメモリ内で処理が行われる**（セキュアである）。


# 準備

それでは本アプリの使い方を説明していきます。
まずは準備です。次の2つを用意するだけです。

1. [Chromeブラウザ](https://www.google.com/intl/ja/chrome/)か[Edgeブラウザ](https://www.microsoft.com/ja-jp/edge)（アプリの動作確認済みブラウザ）
1. 次のどちらかのChatGPT APIサービス（有償）のAPIキー
    - [OpenAI APIキー](https://platform.openai.com/api-keys)（sk-...という形式のアレ）
    - [Azure OpenAI Service](https://azure.microsoft.com/ja-jp/products/ai-services/openai-service)のAPIキーとアクセス情報


# 使い方

## 1. アプリの起動

次のいずれかの方法でアプリを起動します。

1. 本アプリのHTMLファイルを下記からダウンロードしてローカルにおきChromeかEdgeで開く。
    * **本アプリのHTMLファイル**: https://github.com/sonoisa/chatpdf/blob/main/chatpdf.html の右上あたりにあるDownload raw fileボタンをクリックすればダウンロードできます。
2. または、同HTMLが置かれた下記のサイトをChromeかEdgeで開く。
    * https://huggingface.co/spaces/sonoisa/chatpdf 
      ※注意: huggingface spaces版ではチャット履歴保存機能は動きません。
    * https://sonoisa.github.io/chatpdf/

アプリの起動には40〜60秒かかりますので、しばしお待ちください。
（後で解説するように[Gradio Lite](https://www.gradio.app/guides/gradio-lite)の起動待ちです）
次の画面（設定画面）が表示されたら起動完了です。

<img alt="起動直後の画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/425d6578-23ab-5ae1-3cb4-105439291ac4.png" width=600 >



## 2. APIキー等の設定

まずAPIを利用するための設定を行います。
OpenAI APIを利用する場合は、APIキーの設定をすればあとはデフォルトのままで大丈夫です。


各設定項目の意味は下図の通りです。

<img alt="設定項目" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/41a61992-ab45-0fb3-f364-a32814d82ae5.png" width=600>

（設定項目のMax TokensとTemperatureの意味は[OpenAI APIの説明](https://platform.openai.com/docs/api-reference/chat/create)をご参照のこと）


APIキーの設定方法:
* OpenAI APIを利用する場合は、"API Key"の欄にAPIキーを入力するか、APIキーを記載したテキストファイル（例えばopenai_api_key.txt）を"API Key File"にドロップして下さい。
* Azure OpenAI Serviceを利用する場合は、"API Key"の他に、"Endpoint"と"Azure Deployment"と"Azure API Version"にお使いの設定を入力して下さい。

例えば、OpenAIを利用する場合、APIキーを設定すれば、次のような画面になっているはずです。

<img alt="APIキー設定" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/a8b29acc-edb0-7b0a-2929-bcccb86c8a22.png" width=600>

なお、APIキー以外の設定値はブラウザ（localStorage）に記憶されますので、次回起動時はAPIキーのみ設定するだけで済みます。



## 3. PDFを読み込み、要約など任意のチャット

アプリの一番上にある"Chat"をクリックすると下図のチャット画面に切り替わります。

<!-- <img alt="チャット画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/aed90e4c-be9b-63c3-0ca8-6d18308da2c7.png" width=600> -->

<img alt="チャット画面の解説" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/21a49921-4459-e13c-da6c-c279efa32e0e.png" width=600>


あとは直感で使えるでしょう。

まず、PDFからテキストを抽出します。
左上の"PDF"のエリアをクリックして、PDFファイルを選択すると下図のように"Context"欄に抽出されたテキストが表示されます。なぜかPDFファイルを直接ドロップする方法が機能しないので、エリアをクリックする方法を用いてください。
この抽出処理はブラウザのメモリ内で全て行われます（仕組みの解説は後述）。

<img alt="PDFからテキストを抽出" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/c833ae2c-1412-e160-f8f3-1a08cc77f2f3.png" width=600>

（PDFファイルの出典: [CodePlan: Repository-level Coding using LLMs and Planning](https://arxiv.org/pdf/2309.12499.pdf)）


試しに、この論文を[落合フォーマット](https://www.google.com/search?q=%E8%90%BD%E5%90%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)に従って要約させてみましょう。
画面の一番下にある"Examples"の1個目が落合フォーマットに従った要約用のプロンプトになっていますので、それをクリックすると（下図のように）自動的にメッセージ入力欄に入力されます。

**プロンプト中に"{context}"という文字列を書くことで、先ほどPDFから抽出したテキストがここに埋め込まれて処理が行われます。** プロンプトが読み書きしやすくなりますね。

<img alt="論文要約プロンプト" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/c7c8f56e-3208-574d-3580-082d5fa6af65.png" width=600>

:::note warn
注意
ユーザーからは見えませんが、プロンプトの"{context}"部分にはPDFファイルから抽出されたテキスト全文が埋め込まれて、ChatGPTに渡されて処理されます。つまりPDFファイルのテキストが長ければ長いほど処理するトークン数が増えるため、よりお金と処理時間がかかります。モデルがサポートするトークン数の上限を超える長さである場合はエラーが発生します。
:::


あとは"Submit"ボタンを押すだけです。
しばらく待つと次のような生成結果が得られます（APIキー等の設定を間違わなければ）。

<img alt="生成結果" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/1e6b2b68-839f-40ae-1edd-e40d6b69e04d.png" width=600>

要約がたった数クリックでできました。論文読解が捗りますね。

この要約に、更にテスト問題も追加で生成してもらったチャット履歴を下記リンク先で見ることができます（アプリの起動に20〜30秒かかりますが）。これはチャット履歴保存機能を使った履歴共有の例でもあります。

* [論文要約とテスト問題生成の例（チャット履歴）](https://sonoisa.github.io/chatpdf/?history=NrBEjYlQQX0Q3NDe5RrBkMn6gRBkIAMhTuUNByg7BkCgeg3L0DWowcNNBzBkAXjQCQtBVBkBiGQMwZlBohkACjQZQjAQhkGkjQVZt5ATBmT1AgZGRA6gz1A8gyADBmrJAQAwAdAHYqAxAAIocFQCoNowKABALg2ByTUBm2oE1TQBHGejYGeDQFnagWqiT5i4CM0wPiu90YAVfQBzzbEAAKMA8qNNAU9NAGnMwv0gg7EBpy0AEI0AIf6xsQBAVQFAMwDu3ASEAVgAGEuJAdW1ASH-jFQ0NAEYAOg1ASwZAZIZAKwYCUkAhBiZAaQY6jQAmFsAJRUAZCIJ4QBXrQE8GekAqRQZmQE6HQHsGQFV5IeV6gGYWqjpGVk5ebEBZxOFOzZ3hgBYW1uxACIZAMQZOwE7THlJASHNAJ1KfSYgAsGQCqUYBC%2BTEgH0GXb1IotHaAB2jCBt%2BnCNAA2FqAQmt4C5AAEM80Asgwkba7dQaAgkFSKUB0hnKADeAGMAPbKAAuAFMAB6cgC%2BtPpItpyk0AUCoAANKBmhpdLoOt18MCBoqVIBlBjwqo2qEAvQyAVYZABMMgDKGQDlDIAihkAFQyAZ4ZAB0MgGmGS2AK4ZbYAGhmwgG5bQCrSqhAJ5O8EAiwyAYYZAI0MgB%2BGe2Ab89JqhAJrpgBC3QDHkYAE8w4gDGGQCdDMaBoBIhmw1sAlwwRwA3DIBdhgjxuwgDv5QDG1qhAM0MEcAkwyATYYI1nAB4M2EA0eqAC5tABOJHEAugzYQBrcthAKtygA3LbCAX-jAAVKnVaTdbLvL9uNLsAFoqAZXlAEkMgGC9QAj3oAntXggAvfQBjaYBNBiYgH6GLOAeoYDxuty7S23ANcM2GbgHGGQBihg9J9nxBCQnEAeH1ADRlCRkEAPwZb3kTVaALUC9XfbdAFDFP9W0AGQZAA%2B3QAkuUAXPlAA1tOCmGhQBAhlQMxAHjIwBCK3CQAjhn-b80gAGU4gBZKoD0AF7dAFLjQB-eUAClcJGTQAvG3vTUQQ2QAHtUAY7TSCQkdNVIaIegwzdty-X8AOA90D0ACCjAG5XQB5VUAGQzwPoQBZeUAO39AFMGQBFBlQUD4HIsROmQQ1SwjQADhkAe4YI2fQAYBgAYTZAATbkAAUABsAENlEAWAYDyoODEKUVRlDGBVdCmGYFmWVYmDudVdBUKLYsSlK9UAEoZbWfF1AEOGW0H2-cjAHIDQAwJVTVBAEhNQAXs0AR31AH-0-rUEASwTAB39XtzMtb9ABaGF1AC6GALsEABtN6M6QARX0AfwZAAgVQB4hkAbQZ4EAL-UnKEBCkJQ%2BBXkAKIZUEw3c93gE7oSYQAsTUAVutADi07AbzScz%2BKOlIxFQbieIPMShH9JxcJbRz4EAP%2B0JEbW0M0AWoYcwPQBgAMAO0MYRvA9JneOS8NIYlspQvUYYPcSYXQQBZJWwS1AHWGQBbhgjQBBhhBWZAGB4iRXt0z8f2wDy0fvZ6AUCRNOmkwA1bwywBs%2BTEbKxUOArjjK9huB4K4bgqjVlBq%2BLkuUY3bh0j8cObFtsGInrCZJ9yX0AL7U-mwQBwY0ALk8rpu%2BgYcepgyYpuTSDFj8D0AADlAGqY7rsH6QA2p0AaUN4EAOB1JCpmnytU%2BR4OhIbhrwydHfgQA4OUACQYmAmg9%2BqhwBR-WEeBZsADQZ6CXYHr1Msz70AX4DAGylQAghlQbU-MCkLn2wQBaOUADgsD06W9hEAAl9C7FJ4CpeD5vl%2BQFgXBKFYTNi26utwFur1UNIxjSY0gitQqhBQAhM0AZNTE0dkG4oAT05AAFhyKo2ARj8GwG9fSHlABSDEISEdB4IwnkFkdCgBwSMAA6myM3qkHgbQCeqEpYvmwKgwAi2nUAPIAK8CfTQXvGzbAMMOxZg5jzfmpANiABUElIJxkCdAGIAYwZoRBDwv7BBSDz5Wz1JdQAaXYgk6EwQA%2BO6AHztQWItSrQjjoAF8CDz%2ByhowosTVnqjxWNCbqg8DyAC83QAd8aayQWKBEBVkSonRGfGKlsUrYEAAppgAZV1sqgH0iYeocEdugQAaJocwjBmQChpACFDM%2BY0PY0azVQBg7AgA1hm5iGV8gAYFVfvAYaW17yADIjQApq7YEAATWgAgzRXHhQAmMaACUGPMtBsrF2hrxbAs1AAq3p5CipwaKoEALBydZABdHp0TpeE3rwEACVRAj8kHlHtCPMt5AD3yoPbqeFehPWyoAKblAAx%2BjqNh%2BCCyACaoqy6NABnilDcR7jppiXPCCQAfT6ADK9A81A8zwEAIgqgAoOWoGdJgVNAD45oAUP1uFa1ytiAqeJCQkjJDsFxtUrbYEAEGWgBX-ReW8wAj7bUSotReAds9KS0MiBF8dEmKsXYtgQAlEpiVQFQ8y7BUBNRau1TqPN3RFgCv1eAgAi1PvAQA8hIqaAArjQAFpoIPugQ3qgA5xPgNAlyTBAAA5ltbAgAmBMAJbWeornW0AGAuyYDyYtQIAMB1ACm5jqbAXx3ndQPIAIuMmCfNIIAT31ADTXvwUgYhADhDO8eQoAAC60owDXE6PkQo9BUA6ueUMwAsVH3hotgbMxprIHgaCUCygAdeT4fQTAcgvWynlJqvCaNACGMSCe8kCCURiAkS58uLd6fFHtgPCgBqFXgk028AxAD4-2KfKmrsBUF4HhIq%2BAVESBWKcNYB4wH1KkLIBQ2sWh5vEveMKjK2odS6mRPqqZUryS2KpAYqAxx5ETNsXdHbcrbzCiNCa-Ut0atcRfGVQga2dHtYAJyUDyBGhFlVtp6VD2O7XNBaZklqrQ2nhaSYgPi2WYIAbjlOjWWPd%2BsU4Lu1XwPJCaNOLxJ0GhGAiB4soEvgQ%2B2sUAB2Odd6EWQiCPmVAT7-CEDwlnAYTSYTEdygADnI-C9x3jbL1MadCMdE66khpHKhPCnRAA6DNZWRTBsADEADVRgA4MwkDILNYoACcXG3HW12fstGZzbwkhOURn9ygk3aYvki1FmLsX4iJMSAgUrx38EnWphQPq-WgCyHG6y2BGInmTB0tIgB3RT3MnQAG1nICwoATu14CyRBPxNNGas0yjlC0RUebC3FqYKWgy5ajIeQ2E%2Butjbm2IXbWbeoGgAC0Ghb5RljAmFM6Y435kLCWCsVZawNkdownsA5hxjknLOBcy5WhlZbchVCGw3oO3wi7HpzA%2BkMUYp0G8eFW6ydApvXK%2BVMsUfcb2ng-bpiDuFsOsqQnXMifc-IKr9Q6uao2Mu5la6N2lyvQNADi0VrrQCgeQ6p1flRxjtufcn1vr-SBmTCGUNnqJ2TmnTOOc860yQsMWrGg8uEKrRjENjN4b0ERsjVG%2BOsa43xsTUmfdI4gmpsSAuGPco6wO9x5QeEF1MCXc1FdnUerXu3bu-d2BD2mYe1j17q6Bepj1I7bAI1y7YEABcJvV4BPobrXQYB5AaJngP1e8vBC7ajF2Dl082Wzv2wORLlgAh%2BQPMM%2BAgAoo0AIfGUNFet06IAcO9AACRgeAEgBw5z14jBL8ANmgpUNvRUF6xqTU3fJVAmqH30Cfa%2B99n7GAVbbRLurl6496n6PLxXc2JzI3V28T4DdABTyoAHwZW2N1TPeHmgBuhizIXTVBztT9QPIALASu6l0eumhugB15TlSspHTAK6tlVweRGLcbg9zJuZDD8AsNTbsRl3Q-75q-ZAwFMDUkIPvCg0wWD8Gd3fpzxoM-Vk8US3yxW90oF-dB4GPwR3ijJgYd7uZA8-j4DeApAJbG4cx-YbR344Q2624gigTYDCCwBuyizyrTTrbXhcCABgGQeFJE4JrMzioOCmzjptgKhuhnZmvjhuAnluhBfpVlVHsI9vVuGI1g-E-C-B-F-K2D-P-EAsoFUJzmJKItCNNmhIRonodtbCPAeDOIAKAMnQqcgAgzY4IQgr6AAeCYAA4MLGQhpGm%2B3aVGgQNGdGDGTGmhtBmOT24hkiMiciSiQ6aimi2iUMdCDCnYzCvMfMhixipiFi1iiCQhD0z0Zu%2BkuO6M94KGAIayIimhOUKgnGBU3avGEg-GtAgmLmk6%2B64mUmMmci8mymqm0692dB1WdW-igSwSYSloESUSsS8S2AiSeEHiTgiYwIck02EB2AO49A1uZEduHADcjuruCWeESkYgkgqA5k2ATyB4gAcvKZq-Lj7rLYrWS7ZFEaApLpKZIHg5J5IFJMAlLlIVKtHJIkKdBQEHiACAxvwDtnqCkjPlsTeIMYHorIACwaR0CxfS2oI8%2BxGwgeycMxB4gA20aSDzHh54HKBaZxHiHYB6Y9AGZGbEgmY0HZ6FEMElHrpBKtihLhKRIxJxI9jPSAD2QcgIkskpgusdkrkvkkUqUoANGp5irSPE7SHS2AgyQy1A5y94Pw2AtJ1SSREe5mJQOhkJKKeqWKNE9mJITmkqqRt2%2BREuWOtKZk7AIIUuLK3MbKHKqYwh2AUq2AiqKq6qYh7O2AOqIphqJqBAZqFq1qtqDqzqrqHqXq3q3qQAA)


## 4. RAG (Retrieval-Augmented Generation) 機能

RAG機能とは、PDFのページをキーワードで検索して、その検索結果を用いて質問に回答させたり、要約させたりできる機能です。（[RAGの解説](https://blogs.nvidia.co.jp/2023/11/17/what-is-retrieval-augmented-generation/)）

「ページを検索して」といったページ検索を促す表現と、検索キーワード（検索クエリ）を指定する文言をプロンプトに書けば、RAG機能（PDFのページ検索機能）が動きます。
検索キーワードは自分で明示的に与えることもできますし、ChatGPTに考えさせることもできます。

以下で説明する例はいずれもアプリ最下部にあるExamplesに含まれています。

### RAGによるQAの例

下図のようにプロンプトを書けば、質問文から検索キーワード（検索クエリ）を複数バリエーション考えさせ、ページを複数検索させて、検索結果を用いて回答させることができます。

Examplesの「QA (英語文書RAG)」をクリックすれば、このプロンプトが入力されます。
プロンプトは長々としていますが、都度変える必要があるのは「質問」と「検索クエリの言語」くらいでしょう。

<img alt="QA_RAG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/2feb11e7-0e2c-4050-5024-9aa8a2e1e1b0.png" width=600>

この例では検索キーワードとして "How was the proposed method evaluated for effectiveness?,In what ways was the effectiveness of the proposed method assessed?,What methods were used to assess the effectiveness of the proposed approach?" が自動生成されて、検索が行われ、ページ17と16、3がヒットしています。実際の回答に用いられたのはそのうちページ17と16です。

:::note warn
注意
ユーザーからは見えませんが、検索でヒットした全ページのコンテンツがChatGPTに渡されて処理されます。つまりページ数が多ければ多いほど処理するトークン数が増えて、お金がかかったり、トークン数の上限に到達してエラーが発生したりします。上記プロンプトの「検索するページ数」設定は増やしすぎないようにお気をつけください。
:::

### RAGによる要約や翻訳の例

本アプリのRAG機能はQAだけでなく、任意の生成に利用できます。

例えば、下図のようにプロンプトを書けば、要約や翻訳が行えます。
どちらもキーワードを明示的に指定していますが、QAのプロンプトの例のようにChatGPTに考えさせることも可能です。


検索したページの要約の例:
<img alt="要約_RAG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/eaacd19a-8f92-4e75-0373-c63254adfb92.png" width=600>


検索したページの翻訳の例:
<img alt="翻訳_RAG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/65ab385a-6153-9c08-af88-08656768988d.png" width=600>


### ページ番号を指定してページを参照する例

RAG機能を使っていると、検索で見つけたページの前後のページも参照したくなる場合があります。例えば続きのページを要約したくなるなど。
そういった用途のために、ページ番号を指定してページを参照できる機能もあります。

例えば、下図のようにページ番号を明示的に与えて参照させる表現を使うと、そのページを参照して生成処理を行なってくれます。
他にも、あるページを処理した次の発話で「その次のページも要約してください。」といった間接的にページ番号を与える指示を用いることもできます。

<img alt="要約_ページ指定" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/522d0065-e1d1-3974-14b7-dd5fb97b2e72.png" width=600>

:::note warn
注意
ページ検索の場合と同じ注意事項です。ユーザーからは見えませんが、与えられた全ページのコンテンツがChatGPTに渡されて処理されます。つまりページ数が多ければ多いほど処理するトークン数が増えて、お金がかかったり、トークン数の上限に到達してエラーが発生したりします。参照させるページ数は増やしすぎないようにお気をつけください。
:::


## おまけ: チャット履歴保存機能

設定画面で「URLにチャット履歴を保存する機能」をオンにすると、URLのクエリパラメータ"history"にチャット履歴データ（を圧縮したもの）が保存されるようになります。（注意：huggingface spaces版ではチャット履歴保存機能は動きません）

オンの時は画面をリロードしても前のチャットの続きを行えますし、そのURLを他の人に渡すことでチャット履歴を共有することもできます。

ただし、GitLab pagesなどの利用できるURLの文字数制限をしているサーバー上で本機能を用いると、チャットが長くなると414エラーなどのページ読み込みエラーが発生する場合がありますので、その場合はチャット履歴保存機能をオフにして下さい。

* チャット履歴の例：[論文要約とテスト問題生成結果](https://sonoisa.github.io/chatpdf/?history=NrBEjYlQQX0Q3NDe5RrBkMn6gRBkIAMhTuUNByg7BkCgeg3L0DWowcNNBzBkAXjQCQtBVBkBiGQMwZlBohkACjQZQjAQhkGkjQVZt5ATBmT1AgZGRA6gz1A8gyADBmrJAQAwAdAHYqAxAAIocFQCoNowKABALg2ByTUBm2oE1TQBHGejYGeDQFnagWqiT5i4CM0wPiu90YAVfQBzzbEAAKMA8qNNAU9NAGnMwv0gg7EBpy0AEI0AIf6xsQBAVQFAMwDu3ASEAVgAGEuJAdW1ASH-jFQ0NAEYAOg1ASwZAZIZAKwYCUkAhBiZAaQY6jQAmFsAJRUAZCIJ4QBXrQE8GekAqRQZmQE6HQHsGQFV5IeV6gGYWqjpGVk5ebEBZxOFOzZ3hgBYW1uxACIZAMQZOwE7THlJASHNAJ1KfSYgAsGQCqUYBC%2BTEgH0GXb1IotHaAB2jCBt%2BnCNAA2FqAQmt4C5AAEM80Asgwkba7dQaAgkFSKUB0hnKADeAGMAPbKAAuAFMAB6cgC%2BtPpItpyk0AUCoAANKBmhpdLoOt18MCBoqVIBlBjwqo2qEAvQyAVYZABMMgDKGQDlDIAihkAFQyAZ4ZAB0MgGmGS2AK4ZbYAGhmwgG5bQCrSqhAJ5O8EAiwyAYYZAI0MgB%2BGe2Ab89JqhAJrpgBC3QDHkYAE8w4gDGGQCdDMaBoBIhmw1sAlwwRwA3DIBdhgjxuwgDv5QDG1qhAM0MEcAkwyATYYI1nAB4M2EA0eqAC5tABOJHEAugzYQBrcthAKtygA3LbCAX-jAAVKnVaTdbLvL9uNLsAFoqAZXlAEkMgGC9QAj3oAntXggAvfQBjaYBNBiYgH6GLOAeoYDxuty7S23ANcM2GbgHGGQBihg9J9nxBCQnEAeH1ADRlCRkEAPwZb3kTVaALUC9XfbdAFDFP9W0AGQZAA%2B3QAkuUAXPlAA1tOCmGhQBAhlQMxAHjIwBCK3CQAjhn-b80gAGU4gBZKoD0AF7dAFLjQB-eUAClcJGTQAvG3vTUQQ2QAHtUAY7TSCQkdNVIaIegwzdty-X8AOA90D0ACCjAG5XQB5VUAGQzwPoQBZeUAO39AFMGQBFBlQUD4HIsROmQQ1SwjQADhkAe4YI2fQAYBgAYTZAATbkAAUABsAENlEAWAYDyoODEKUVRlDGBVdCmGYFmWVYmDudVdBUKLYsSlK9UAEoZbWfF1AEOGW0H2-cjAHIDQAwJVTVBAEhNQAXs0AR31AH-0-rUEASwTAB39XtzMtb9ABaGF1AC6GALsEABtN6M6QARX0AfwZAAgVQB4hkAbQZ4EAL-UnKEBCkJQ%2BBXkAKIZUEw3c93gE7oSYQAsTUAVutADi07AbzScz%2BKOlIxFQbieIPMShH9JxcJbRz4EAP%2B0JEbW0M0AWoYcwPQBgAMAO0MYRvA9JneOS8NIYlspQvUYYPcSYXQQBZJWwS1AHWGQBbhgjQBBhhBWZAGB4iRXt0z8f2wDy0fvZ6AUCRNOmkwA1bwywBs%2BTEbKxUOArjjK9huB4K4bgqjVlBq%2BLkuUY3bh0j8cObFtsGInrCZJ9yX0AL7U-mwQBwY0ALk8rpu%2BgYcepgyYpuTSDFj8D0AADlAGqY7rsH6QA2p0AaUN4EAOB1JCpmnytU%2BR4OhIbhrwydHfgQA4OUACQYmAmg9%2BqhwBR-WEeBZsADQZ6CXYHr1Msz70AX4DAGylQAghlQbU-MCkLn2wQBaOUADgsD06W9hEAAl9C7FJ4CpeD5vl%2BQFgXBKFYTNi26utwFur1UNIxjSY0gitQqhBQAhM0AZNTE0dkG4oAT05AAFhyKo2ARj8GwG9fSHlABSDEISEdB4IwnkFkdCgBwSMAA6myM3qkHgbQCeqEpYvmwKgwAi2nUAPIAK8CfTQXvGzbAMMOxZg5jzfmpANiABUElIJxkCdAGIAYwZoRBDwv7BBSDz5Wz1JdQAaXYgk6EwQA%2BO6AHztQWItSrQjjoAF8CDz%2ByhowosTVnqjxWNCbqg8DyAC83QAd8aayQWKBEBVkSonRGfGKlsUrYEAAppgAZV1sqgH0iYeocEdugQAaJocwjBmQChpACFDM%2BY0PY0azVQBg7AgA1hm5iGV8gAYFVfvAYaW17yADIjQApq7YEAATWgAgzRXHhQAmMaACUGPMtBsrF2hrxbAs1AAq3p5CipwaKoEALBydZABdHp0TpeE3rwEACVRAj8kHlHtCPMt5AD3yoPbqeFehPWyoAKblAAx%2BjqNh%2BCCyACaoqy6NABnilDcR7jppiXPCCQAfT6ADK9A81A8zwEAIgqgAoOWoGdJgVNAD45oAUP1uFa1ytiAqeJCQkjJDsFxtUrbYEAEGWgBX-ReW8wAj7bUSotReAds9KS0MiBF8dEmKsXYtgQAlEpiVQFQ8y7BUBNRau1TqPN3RFgCv1eAgAi1PvAQA8hIqaAArjQAFpoIPugQ3qgA5xPgNAlyTBAAA5ltbAgAmBMAJbWeornW0AGAuyYDyYtQIAMB1ACm5jqbAXx3ndQPIAIuMmCfNIIAT31ADTXvwUgYhADhDO8eQoAAC60owDXE6PkQo9BUA6ueUMwAsVH3hotgbMxprIHgaCUCygAdeT4fQTAcgvWynlJqvCaNACGMSCe8kCCURiAkS58uLd6fFHtgPCgBqFXgk028AxAD4-2KfKmrsBUF4HhIq%2BAVESBWKcNYB4wH1KkLIBQ2sWh5vEveMKjK2odS6mRPqqZUryS2KpAYqAxx5ETNsXdHbcrbzCiNCa-Ut0atcRfGVQga2dHtYAJyUDyBGhFlVtp6VD2O7XNBaZklqrQ2nhaSYgPi2WYIAbjlOjWWPd%2BsU4Lu1XwPJCaNOLxJ0GhGAiB4soEvgQ%2B2sUAB2Odd6EWQiCPmVAT7-CEDwlnAYTSYTEdygADnI-C9x3jbL1MadCMdE66khpHKhPCnRAA6DNZWRTBsADEADVRgA4MwkDILNYoACcXG3HW12fstGZzbwkhOURn9ygk3aYvki1FmLsX4iJMSAgUrx38EnWphQPq-WgCyHG6y2BGInmTB0tIgB3RT3MnQAG1nICwoATu14CyRBPxNNGas0yjlC0RUebC3FqYKWgy5ajIeQ2E%2Butjbm2IXbWbeoGgAC0Ghb5RljAmFM6Y435kLCWCsVZawNkdownsA5hxjknLOBcy5WhlZbchVCGw3oO3wi7HpzA%2BkMUYp0G8eFW6ydApvXK%2BVMsUfcb2ng-bpiDuFsOsqQnXMifc-IKr9Q6uao2Mu5la6N2lyvQNADi0VrrQCgeQ6p1flRxjtufcn1vr-SBmTCGUNnqJ2TmnTOOc860yQsMWrGg8uEKrRjENjN4b0ERsjVG%2BOsa43xsTUmfdI4gmpsSAuGPco6wO9x5QeEF1MCXc1FdnUerXu3bu-d2BD2mYe1j17q6Bepj1I7bAI1y7YEABcJvV4BPobrXQYB5AaJngP1e8vBC7ajF2Dl082Wzv2wORLlgAh%2BQPMM%2BAgAoo0AIfGUNFet06IAcO9AACRgeAEgBw5z14jBL8ANmgpUNvRUF6xqTU3fJVAmqH30Cfa%2B99n7GAVbbRLurl6496n6PLxXc2JzI3V28T4DdABTyoAHwZW2N1TPeHmgBuhizIXTVBztT9QPIALASu6l0eumhugB15TlSspHTAK6tlVweRGLcbg9zJuZDD8AsNTbsRl3Q-75q-ZAwFMDUkIPvCg0wWD8Gd3fpzxoM-Vk8US3yxW90oF-dB4GPwR3ijJgYd7uZA8-j4DeApAJbG4cx-YbR344Q2624gigTYDCCwBuyizyrTTrbXhcCABgGQeFJE4JrMzioOCmzjptgKhuhnZmvjhuAnluhBfpVlVHsI9vVuGI1g-E-C-B-F-K2D-P-EAsoFUJzmJKItCNNmhIRonodtbCPAeDOIAKAMnQqcgAgzY4IQgr6AAeCYAA4MLGQhpGm%2B3aVGgQNGdGDGTGmhtBmOT24hkiMiciSiQ6aimi2iUMdCDCnYzCvMfMhixipiFi1iiCQhD0z0Zu%2BkuO6M94KGAIayIimhOUKgnGBU3avGEg-GtAgmLmk6%2B64mUmMmci8mymqm0692dB1WdW-igSwSYSloESUSsS8S2AiSeEHiTgiYwIck02EB2AO49A1uZEduHADcjuruCWeESkYgkgqA5k2ATyB4gAcvKZq-Lj7rLYrWS7ZFEaApLpKZIHg5J5IFJMAlLlIVKtHJIkKdBQEHiACAxvwDtnqCkjPlsTeIMYHorIACwaR0CxfS2oI8%2BxGwgeycMxB4gA20aSDzHh54HKBaZxHiHYB6Y9AGZGbEgmY0HZ6FEMElHrpBKtihLhKRIxJxI9jPSAD2QcgIkskpgusdkrkvkkUqUoANGp5irSPE7SHS2AgyQy1A5y94Pw2AtJ1SSREe5mJQOhkJKKeqWKNE9mJITmkqqRt2%2BREuWOtKZk7AIIUuLK3MbKHKqYwh2AUq2AiqKq6qYh7O2AOqIphqJqBAZqFq1qtqDqzqrqHqXq3q3qQAA)

# 仕組みの解説

本アプリは[Gradio Lite](https://www.gradio.app/guides/gradio-lite)を用いて構築されています。
Gradio Liteとは、ブラウザ内で動作するアプリをPython言語（[Pyodide](https://pyodide.org/en/stable/)）を用いて開発できるフレームワークです。このフレームワークのおかげで、様々な既存のPythonライブラリ資産を活用でき、アプリサーバーを立てなくてもブラウザのメモリ内で高度な処理が行えます。


主な利用技術:
- [Gradio Lite](https://www.gradio.app/guides/gradio-lite): ユーザーインターフェイスの構築
- [Pyodide](https://pyodide.org/en/stable/): ブラウザ内で動作するPython環境
- [pdfminer.six](https://github.com/pdfminer/pdfminer.six): PDFファイルからテキストを抽出するために用いたライブラリ
- [Janome](https://github.com/mocobeta/janome): RAGの実装に用いた日本語形態素解析器
- [Rank-BM25](https://github.com/dorianbrown/rank_bm25): RAGの実装に用いた検索エンジン
- OpenAI APIやAzure OpenAI Service: ChatGPT APIとして利用

ロボットアイコン ![chatbot_icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26062/5607abdf-5f9b-e6c2-3ab1-9df76ee898b4.png) はDALL-Eを使って生成しました。

PDFのページ検索には、Okapi BM25アルゴリズムによるキーワード型検索を採用しています。ベクトル型検索はキーワード型検索よりも劣る結果になるケースをよく見かけますので、ひとまずベクトル型検索は不採用とし、キーワード型検索のみを採用しております。気が向いたらハイブリッド検索を実装するかもしれません。

検索対象のチャンク設計は「1チャンク = 1ページ」としました。この設計にした主な理由は、私の扱う資料ではまあまあ1ページごとに1つのトピックが分かれている傾向があるというのと、ページはユーザーにとっても理解と取り扱いがしやすい分割単位であることです。その他、ベクトル型検索を用いる場合はもっとチャンクサイズを小さくしないと埋め込みの精度が落ちるという問題がありますが、今回採用したキーワード型検索ではそういった問題は起こりにくいことも設計時の観点にありました。

現時点、キーワード型検索を行う際の形態素解析器として日本語用のみがある状態であり、英単語の取り扱いが十分とはいえません（単数形と複数形が別の形態素になっています）。英語のLemmatization等の正規化を行える形態素解析器も利用可能にすることで英語の検索精度を上げることができるはずです。


# まとめと次の予定

私が資料チェックのときに普段使いしているアプリを公開させていただきました。
皆様のお役立てば幸いです。

今でも最低限の機能は備えていると思っておりますが、実用性を高めるために下記のような機能を追加したり、お遊びに別アプリを作ってみようかなと思っております。

* ~~RAG的な機能の追加。文章が長すぎると見落とし等の間違いが発生しやすくなるため、精度向上の観点から（function callingを使い）PDFのページやチャンクなどを検索して処理対象を絞り込めるようにする。（[RAGの解説](https://blogs.nvidia.co.jp/2023/11/17/what-is-retrieval-augmented-generation/)）~~ （2024-02-17に実装済み）
* Advanced Data Analyticsのような機能の追加？技術的な面白さはあるものの、この仕組みで実装することの嬉しさは少ない？（autogenのインストールまでは確認済み）
* マルチモーダル対応？
* Gemini対応？


# 免責事項
本アプリは[MITライセンス](https://licenses.opensource.jp/MIT/MIT.html)に従い公開いたします。
著者は本アプリを開発するにあたって、その機能等について細心の注意を払っておりますが、バグがないことや、安全なものであるか等について保証をするものではなく、何らの責任を負うものではありません。本アプリのご利用により、万一、ご利用者様に何らかの不都合や損害が発生したとしても、著者や著者の所属組織は何らの責任を負うものではありません。