📕

LLMと対話しながら読書できるオープンソース電子書籍リーダーの紹介

に公開

はじめに

以前から Kindle に LLM とのチャット機能をつけてほしいなあ、と思っていたのですがなかなか実装されないので、それっぽいものを自分で作ってみました。

https://github.com/shutootaki/bookwith/

大変ありがたいことに、Flow という EPUB リーダーの OSS がすでにあったので、今回はこれをフォークし、独自の機能を追加する形で開発しました。

サービス概要

LLM とのチャット機能を備えた電子書籍リーダーです。
主な機能は以下の通りです。

  • LLM とのチャット対話
  • 書籍内容のポッドキャスト生成
  • テキストのハイライト・メモ
  • 本の全文検索

レスポンシブデザインに対応しているので、PC だけでなくスマートフォンやタブレットでも快適に使用できます。

機能紹介

では、具体的な機能をいくつか紹介します。

LLM とのチャット対話

ユーザーが質問すると、現在開いている本の内容をもとに AI が回答を生成します。
技術書や専門書で難解な部分があっても、その場で LLM に質問することで理解を深められます。

https://youtu.be/n2jfN0KKQe8

チャット履歴は保存されるため、後から対話内容を振り返ることも可能です。

書籍内容のポッドキャスト生成

書籍のテキストを、AI が対話形式のポッドキャスト風の音声に変換します。
5~10 分ほどの音声を生成してくれるため、移動中やちょっとした作業をしている際に本の概要を把握したり、振り返ったりするのに便利です。

https://youtu.be/KC_WbInxwRo

テキストハイライト

フォーク元である Flow に実装されている機能です。テキストをハイライトし、その箇所にメモを紐づけることができます。

テキスト全文検索

こちらも Flow で実装されている機能です。書籍全体のテキストを対象に全文検索ができます。

技術周り

技術スタック

レイヤー 主な技術
フロントエンド Next.js / TypeScript / Tailwind CSS / shadcn/ui
バックエンド FastAPI / LangChain
データベース Supabase (PostgreSQL) / Weaviate
LLM Google / OpenAI
その他 Docker / GCS (エミュレータ: fsouza/fake-gcs-server)

EPUB ファイルの RAG 実装

本アプリでは LangChain を利用して RAG を実現しており、具体的な各ステップでは以下のコンポーネントを使用しています。

  1. EPUB 読み込み
    LangChain の UnstructuredEPubLoader (mode="elements") を使用し、EPUB ファイルを見出しや段落などの要素タイプをメタデータとして保持したオブジェクト群に変換します。

https://python.langchain.com/api_reference/community/document_loaders/langchain_community.document_loaders.epub.UnstructuredEPubLoader.html

  1. チャンク分割
    RecursiveCharacterTextSplitter を使用しています。
    段落  →  文  →  単語  →  文字という、より大きい単位から順にチャンクを試す再帰的アルゴリズムで、指定したチャンクサイズ以下になるまで分割します。こうすることで文脈的なまとまりをなるべく保ったまま、チャンクを生成できます。

https://python.langchain.com/docs/how_to/recursive_text_splitter/

  1. ベクトル化
    各チャンクを OpenAI の text-embedding-3-small でベクトル化します。

https://platform.openai.com/docs/models/text-embedding-3-small

  1. 保存
    ベクトルは Weaviate に保存し、チャット時に類似検索を実行します。

VectorDB の選定

VectorDB には Weaviate を採用しました。
本アプリの開発当時、LangChain との統合が可能な VectorDB の中で、唯一公式ドキュメントに multi-tenant 対応が明記されていたため Weaviate を選択しました。
しかし、現在は Milvus や Qdrant なども LangChain とのインテグレーションで同様の機能を提供しているため、必ずしも Weaviate である必要はなさそうです。

https://python.langchain.com/docs/integrations/vectorstores/

VectorDB の選定にあたっては、以下の記事が参考になりました。
https://benchmark.vectorview.ai/vectordbs.html

LLM のメモリ管理

LLM の API は基本的にステートレスです。そのため、文脈に沿った回答を生成させるには、開発者側で対話履歴を管理し、コンテキストとして API リクエストに含める必要があります。本アプリでは、この履歴管理をウィンドウバッファと要約を組み合わせて実現しています。

  • ウィンドウバッファ: 直近 5 件の会話履歴を常にコンテキストに含める
  • 要約: 会話が長くなった場合に備え、20 件ごとに過去の会話内容を LLM に要約させ、その要約を VectorDB に保存する

毎回のチャット時には、ユーザーの入力と関連性の高い過去の会話要約を VectorDB から検索し、それもコンテキストに加えることで、長期的な文脈を AI が理解できるようにしています。

さらに、ユーザーがハイライトした箇所も AI の記憶として活用します。
ハイライト箇所はすべてベクトル化して VectorDB に保存しておき、ユーザーからの質問があった際に、その質問文とベクトル類似度を計算します。類似度が高いハイライトが見つかれば、そのテキストもコンテキストに加えることで、ユーザーが過去に重要だと判断した本の内容を半受動的に想起できるようにしています。

アプリの起動方法

現在はローカル環境での動作を前提としています。
もしご興味があれば、以下の手順に従って Docker で起動してみてください。

https://github.com/shutootaki/bookwith/blob/main/docs/DEVELOPMENT_GUIDE_JA.md

おわりに

LLM を搭載した Epub リーダーの紹介でした。便利なツールですので、ぜひ使ってみてもらえると嬉しいです!
https://github.com/shutootaki/bookwith/

Discussion