LangChainのベーシックを全面解説する

NLP
LLMs
LangChain
Published

May 6, 2023

前書き

OpenAIのGPTのAPIを利用してアプリを作成するには、今まで一番使いやすいパッケージはLangChain🦜️🔗 だと思います。本文では、LangChainの基本的な使い方を優しく説明します。

LangChain

環境設定

まずは定番のpipからインストールすることです。

pip install langchain, openai

そのつぎに、OpenAIのAPIキーを取得して、環境変数に設定します。 APIはここから取得できます。

import os
os.environ["OPENAI_API_KEY"] = "..."

直接にAPIキーを書くのはセキュリティ上の問題があるので、スクリプトを共有する場合は(例えば本文)、APIキーを別ファイルに保存し、ファイルから読み込んだほうがよいです。

import os 
with open("../../.env", "r") as f: 
    os.environ.update(dict([line.strip().split("=") for line in f.readlines()]))

OpenAIのGPTモデル

LangChainの中にOpenAIのGPTモデルを使うラッパーがあります。現在使えるモデルはテキスト補完モデルとChatモデルの2種類あります。生成モデルの場合は以下のように使います。

from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
output = llm("日本の首都は?")
print(output.strip())
東京です。

また、Chatモデルを利用して対話を行うこともできます。

from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)

chat = ChatOpenAI(temperature=0)
output = chat([HumanMessage(content="文法を修正してください:I loves programming.")])
print(output.content)
I love programming.

各モデルの特性のまとめ

各モデルの値段や、最大トークン数、モデルサイズは以下の表にまとめました。

テキスト補完モデル

モデル名 値段(1k tokensごと) 最大トークン数 モデルサイズ(推測)
Davinci $0.0200 4,097 175B
Curie $0.0020 4,097 6.7B
Babbage $0.0005 4,097 1.3B
Ada $0.0004 4,097 350M
Chatモデル
モデル名 値段(**Prompt) 値段(**補完) 最大トークン数 モデルサイズ(推測)
gpt-3.5-turbo $0.002 $0.002 4,096 6.7B
gpt-4 $0.03 $0.06 8,192 6.7B
gpt-4-32k $0.06 $0.12 32,768 1.3B
Note

ここで注意することとしてはGPT4の値段です。インプットするテキストがprompt、生成したテキストはcompletionに分かれていて、promptの値段とcompletionの値段を足したものがGPT4の値段になります。

モデルの使い分け

モデルの使い分けについては、最も使われているのはChatモデルのgpt-3.5-turbogpt-4です。gpt-3.5-turboはモデルのサイズが小さいので、生成時間が短く、値段も安いです。一方、gpt-4は性能が良いので、性能を求める場合はgpt-4のほうが良いです。また、gpt-4の最大トークン数が8Kになっているので、生成するテキストの長さが長い場合もこちらを使うほうがいいです。

他のモデルはほとんど使われないので、必要に応じて詳細を見れば良いです。

Tokenの計算方法

Tokenの計算方法については、こちらで紹介したので、本文では割愛します。要するに、日本語千文字のドキュメントはおおよそ1,000トークンになり、それを処理するにはgpt-3.5-turboの場合は概算で0.59円、gpt-4の場合は概算で$9.7円かかります。

Prompt Template

LangChainのPrompt TemplateはPromptを簡単に作成するためのモジュールです。Example selector付きのPromptを作るにはとても役に立ちます。でもそれはよりアドバンス的なやり方なので、入門の段階では単純にPythonのf-stringとして使えれば良いです。

Promptのテンプレートを書いた後、それをPromptTemplateのインスタンスに渡して、PromptTemplateformatメソッドを呼び出すと、Promptが生成されます。

from langchain import PromptTemplate

template = "私は{fruit}が好きです。"
prompt_template = PromptTemplate.from_template(template)
print(prompt_template.format(fruit="りんご"))
print(prompt_template.format(fruit="みかん"))
私はりんごが好きです。
私はみかんが好きです。

VectorStore

ドキュメントを検索するためには、VectorStoreを作成する必要があります。VectorStoreはドキュメントのリストを受け取って、それをベクトルに変換して保存します。検索する際に、検索クエリをベクトルに変換して、ベクトルの類似度を計算して、類似度が高いドキュメントを返します。

FAISSについて

FAISSはMetaが開発した高速な類似性検索ライブラリです。Faissは、大量のベクトルデータを格納し、高速な検索を行うことができます。

from langchain.document_loaders import TextLoader
from langchain.indexes import VectorstoreIndexCreator
from langchain.vectorstores import FAISS
# create test data
with open("./test_data.txt", "w") as f:
    fruits = ["りんご", "みかん", "バナナ", "パイナップル", "ぶどう"]
    for fruit in fruits:
        f.write(f"私は{fruit}が好きです。\n")
        
# load test data
loader = TextLoader('./test_data.txt', encoding='utf8')

# query test data
index = VectorstoreIndexCreator(vectorstore_cls=FAISS).from_loaders([loader])
index.query("りんご")
' 私はりんごが好きです。'

Chain

ChainはLangChainの中心的な概念です。今まで紹介した複数の部品を組み合わせでChainを作ることができます。インプットが入力された後、Chainの内部で処理し、アウトプットを出す。

例えば、PromptTemplateとLLMをつなぐChainを作ることができます。PromptTemplateはPromptを生成するので、LLMのインプットになります。LLMはPromptを受け取って、それを補完して、アウトプットを生成します。こうしてPromptTemplateとLLMをつなぐChainを作ることができます。

flowchart LR
    Input([Input])-->PromptTemplate
    LLM-->Output([Output])
    subgraph Chain
    PromptTemplate-->formattedPrompt([Formatted Prompt])
    formattedPrompt-->LLM
    end
    style PromptTemplate stroke:#333,stroke-width:4px
    style LLM stroke:#333,stroke-width:4px

Chainのダイアグラムの例

from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
from langchain.chains import LLMChain
llm = OpenAI(temperature=0.9)
prompt = PromptTemplate.from_template("{country}の首都は何ですか?")
chain = LLMChain(llm=llm, prompt=prompt)

これで各国の首都は簡単に検索できるようになりました。

print(chain.run({"country": "日本"}).strip())
print(chain.run({"country": "アメリカ"}).strip())
東京です。
ワシントンD.C.

Agent

AgentはChainよりも高いレベルの概念です。Agentはツールを使うことができます。それにより、Agentは内部環境にとどまらず、外部環境ともやり取りできます。

一番シンプルの例としてはBingChatがあげられます。ユーザーのクエリーを受けた後、BingChatはインタネットから情報を検索し、それをサマリーして、ユーザーのクエリに答えます。

Agentの中身は複雑でドキュメントに書いていないので、今回は挙動だけ見せます。ここでBingChatに似ている機能を実現するAgentを作ります。このAgentはユーザーのクエリーを受け取って、それをインタネットで検索し、その答えを返すことができます。また、外部の電卓ツールを利用して計算もできます。

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0)
tools = load_tools(["serpapi", "llm-math"], llm=llm)
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("今日の気温は何度ですか?その2乗は何ですか?")


> Entering new AgentExecutor chain...
 I need to find out the temperature and then calculate its square.
Action: Search
Action Input: 今日の気温
Observation: ニューヨーク, NY, アメリカ合衆国 の天気. 4. 今日 · 1時間ごと · 10日間 · レーダー. 1時間ごとの天気-ニューヨーク, NY, アメリカ合衆国. 13:48 EDT時点 ...
Thought: I need to find the temperature from the search results
Action: Search
Action Input: 今日の気温 ニューヨーク
Observation: 16:00 · 体感温度16° · 風南東 8 km/h · 湿度47% · 紫外線指数2/10 · 雲量78% · 雨量0 cm ...
Thought: I now have the temperature, I need to calculate its square
Action: Calculator
Action Input: 16^2
Observation: Answer: 256
Thought: I now know the final answer
Final Answer: 今日の気温は16度で、その2乗は256です。

> Finished chain.
'今日の気温は16度で、その2乗は256です。'

「今日の気温は何度ですか?その2乗は何ですか?」のクエリーを投げた後、Agentのほうはまずやるべきことを決めました。やるべきことをプランニングしながら、自分が持っているツールを駆使し、クエリーに答えました。

まとめ

これでLangChainの中にあるMemory以外のものをひと通り浅く紹介しました。LangChainの開発はまだ初期の段階なので、APIの設計や、ドキュメントの充実さなどの問題があります。今後は各概念を解剖する記事を書いていきます。