⛓️

OpenAI APIとLangChainを使って、任意のテキストを読み込んで回答するチャットボットを作る

2023/05/03に公開

はじめに

ChatGPTすごいですよね。
これをAPI経由で使うことができるのが、OpenAI APIです。
API経由で使うことで、自前のサービスに組み込んだり、外部のデータや機能と連携させることができるようになります。

まずは、外部のデータ連携をしたいと思いますので、テキストからデータを取得し、それについて答えるシンプルなBotを作ってみます。
データローダーとしては、wikiやslackなど色々なものが読み込めますが、今回はシンプルにテキストを読み込む場合の実装を行います。

今回はLangChainを使ってみます。
LLamaIndexを使った方法も公開したいと思います。

実装

環境設定

chromadbのインストールに環境変数が必要だったので、先に設定します。

import os
os.environ["OPENAI_API_KEY"] = "Your API Key"
# Chromadbのインストールに必要だった
os.environ["HNSWLIB_NO_NATIVE"] = "1" 

パッケージインストール

# パッケージのインストール
!pip install langchain
!pip install openai
!pip install chromadb

モデル指定

text-davinci-003を使った記事も多いですが、性能同等でコストが1/10のgpt-3.5-turboがでているので、こちらを指定します。[1]

from langchain.llms import OpenAI
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0, max_tokens=500)

データ連携

テキストローダー

チェンソーマンのwikiからデンジの説明部分をテキストにして読み込んでいます。[2]
データロードされた値を、短いチャンクに分割しています。
短すぎると、意味が繋がらなくなる。長いと

from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
loader = TextLoader('./source/denji.txt')
data = loader.load()
# chunk_sizeが4096を超えるとエラーになる
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=0)
texts = text_splitter.split_documents(data)

Embeddings

Embeddingsは、テキストを意味検索するためのベクトルに変換するものです。
こちらをChromaDBというデータベースに登録します。
この処理にも費用がかかるので、データが多い場合は、Embeddingしたデータを永続化する必要があります。
OpenAI以外にもHuggingFaceなどいくつかのサービスでEmbeddingを行うことができます。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
embeddings = OpenAIEmbeddings()
db = Chroma.from_documents(texts, embeddings)

Retriever

データベースをRetrieverにします。
Retrieverとは、データベースを検索することができる機能を固めたコンポーネントです。
get_relevant_documentsなどのメソッドがあります。

retriever = db.as_retriever()

Chain作成

RetrievalQAに、Retrieverを渡すと、QAができるチャットbotができます。

from langchain.chains import RetrievalQA
chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)

実行

query = "チェンソーの悪魔とはなんですか?"
chain.run(query)

出力

チェンソーの悪魔は、主人公デンジが変身する姿であり、頭部全体がチェンソーのような形状になり、両腕からもガイドバーとソーチェンが生える。胸のスターターロープを引っ張れば何度でも蘇ることのできる不死身の存在だが、エネルギー源である血液が足りなくなると肉体の再生ができずに仮死状態に陥るという弱点も存在する。また、悪魔を食べると、その名前を持つ存在をこの世から消し去ってしまうという能力を持っている。

参考

https://python.langchain.com/en/latest/use_cases/question_answering.html
https://blog.langchain.dev/retrieval/

脚注
  1. もっと安く早い回答を求めるならtext-babbage-001、text-ada-001という選択もあります。
    Babbage
    Babbage は、単純な分類などの簡単なタスクを実行できます。 また、ドキュメントが検索クエリとどの程度一致しているかを示すセマンティック検索のランク付けにも対応できます。
    用途: 中程度の分類、セマンティック検索分類
    Ada
    Ada は通常、最速のモデルであり、テキストの解析、住所変更、過度のニュアンスを必要としない特定の種類の分類などのタスクを実行できます。 Ada のパフォーマンスは、多くの場合、より多くのコンテキストを提供することで改善できます。
    用途: テキストの解析、単純な分類、住所変更、キーワード
    https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/concepts/models ↩︎

  2. VectorstoreIndexCreatorを使った方法も公式で紹介されているのですが、以下のエラーがでました。
    NotEnoughElementsException: Number of requested results 4 cannot be greater than number of elements in index 1
    chunk_sizeを小さくしないといけないというissueを見つけたので、VectorstoreIndexCreatorを使わず、chunck_sizeを小さくする処理をしています。
    https://github.com/hwchase17/langchain/issues/1793 ↩︎

Discussion