このLangChain入門では、ステップバイステップでLangChainの使い方を紹介してゆきます。LangChainは生成AIを使ったシステムを構築するためのフレームワークです。生成AIではOpenAIの提供するChatGPTが有名ですが、それ以外にもたくさんの生成AIが存在します。LangChainを使用すれば、そのような生成AIを効果的に活用し、効率よくアプリケーションを作成することが可能になります。
LangChain入門 – 1)はじめに – 目次
この記事はFuture Coders独自教材からの抜粋です。この分野は頻繁に更新されるので、この記事を見ていただいているころには状況が変わっている可能性があることご了承ください
LangChainとは
ChatGPTなどの生成AIが注目されています。ChatGPTはOpenAIが提供するサービスですが、Webのインタフェースだけでなく、WebAPIを利用することで、Python, Java, C# などのプログラムからそのサービスを利用することが可能です。
ChatGPTは2021年までのデータしか持っていないので、最新のニュースや出来事については答えられません。最新の時事ネタに答えられるようにするには、別途検索サービスなどと組み合わせる必要があります。
最近OpenAIがChatGPTが使用するモデルを最新のデータを使って更新したというニュースが発表されました。よって、この辺りの内容は適宜更新される可能性が高いことご了承ください。
また、製品のカスタマサポートなどを実装する場合は、その製品に対する詳しい情報を入力して学習させる必要があります。情報はExcel、PDF、Word、データベースなどいろいろなフォーマットで格納されている可能性がありますが、これらを組み合わせるのも大変です。
プログラムを駆使すればこれらの課題は解決できます。最新情報や製品紹介などいろいろな生成AIアプリケーションを作成できるでしょう。しかし、内容が複雑になってくるとプログラム作成の負担も増えてきます。また、OpenAI以外の生成AIやクラウドサービスなどと組み合わせたりすると、さらに複雑さは増してしまいます。
LangChainはこのような状況に対応すべく開発されたフレームワークです。つまり、生成AIを組み合わせて、いろいろなアプリを簡単に作成できるようになります。
ちなみにMicrosoft社のguidanceもLangChainと似たような位置づけです。
最初のサンプル
詳しい説明は後で行います。まずLangChainでどのようなことができるか見てみましょう。
各種サンプルを実行する前に以下のコマンドを実行して、必要なモジュールをインストールします。
pip install openai
pip install langchain
OpenAIの公式ページからAPIキーを取得してください。
以下のサンプルにあるAPIキー無効にしてあります。実行する際は公式サイトから自分で取得した値に置き換えて実行してください。
例1:会話
LangChainを使用する前に、直接OpenAIのWebAPIを呼び出してみましょう。質問を2つしています。
※最近のアップデートでOpenAIモジュールの利用方法が変更になりました。詳しくは以下の記事を参照してください。
import openai
openai.api_key = "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"
def ask_ai(prompt):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": prompt},
],
)
return response.choices[0]["message"]["content"].strip()
s = ask_ai("メジャーリーグで今一番活躍している日本人選手の名前を一人だけ教えてください。")
print(s)
s = ask_ai("その人の性別はなんですか?")
print(s)
出力は以下のようになりました。
大谷翔平(Shohei Ohtani)
私はその人の性別を知りません。
ask_aiという関数を定義しています。その関数ではOpenAIのAPI(openai.ChatCompletion.create)を使って、プロンプトを送信し、応答を受信しています。この関数を使って質問を2つしています。それぞれの質問はまったく独立して扱われるために、2個目の質問で最初のやり取りの内容を把握することはできません。生成AIなら大谷翔平の性別は自明のはずです。会話の文脈(コンテキスト)が保存されていないことがわかります。
以下は、LangChainを使って書き直した例です。
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-3.5-turbo",
openai_api_key="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")
chain = ConversationChain(llm=llm, memory=ConversationBufferMemory(return_messages=True))
r = chain.run("メジャーリーグで今一番活躍している日本人選手の名前を一人だけ教えてください。")
print(r)
r = chain.run("その人の性別はなんですか?")
print(r)
応答は以下の通りでした。
メジャーリーグで今最も活躍している日本人選手は、大谷翔平選手です。彼はロサンゼルス・エンゼルスで投手として
も打者としても非常に優れた能力を持っています。大谷選手は2021年にはオールスターゲームにも選ばれ、日本人選手 としては初めて本塁打競争にも出場しました。彼の活躍はメジャーリーグで大きな話題となっています。
大谷翔平選手の性別は男性です。
どちらのサンプルもOpenAIの生成AI (gpt-3.5-turbo)を使用していることに注目してください。
最初のサンプルではコンテキストが失われていましたが、LangChainを使用した例では、2つ目の質問に「男性」と答えられていることがわかります。
ChatGPTのWebAPIでも最初の応答を次の質問に組み込めば、会話のコンテキストを保存して、「男性」という答えを得ることは可能です。その場合は、最初の質問への応答を次の質問に含めるなどの処理を自分で行う必要があります。LangChainを使用すると、このような会話のコンテキストを保存しながら対話するようなプログラムがより簡単に実装できます。
例2:最新情報
次は最新情報にアクセスするサンプルです。まずはOpenAIのWebAPIだけを使った例です。
import openai
openai.api_key = "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"
def ask_ai(prompt):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": prompt},
],
)
return response.choices[0]["message"]["content"].strip()
s = ask_ai("今の日本の総理大臣を日本語で教えてください。")
print(s)
出力は以下のようになりました。
今の日本の総理大臣は菅義偉(すが・よしひで)です。
ChatGPTでは最近の情報を学習していないので、このような回答になってしまいます。
LangChainを使って、検索サービスと組み合わせると上記のような問いにも答えられるようになります。検索サービスはGoogle, Bingなどいろいろなものが利用可能ですが、今回は無料で利用できるduckduckgo-searchを使用してみます。
まずpipコマンドでモジュールをインストールします。
pip install duckduckgo-search
以下がプログラムです。
from langchain.chat_models import ChatOpenAI
from langchain.agents import Tool
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.tools import DuckDuckGoSearchRun
import openai
openai.api_key = "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"
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
tool_names = []
tools = load_tools(tool_names)
search = DuckDuckGoSearchRun()
tools.append(
Tool(
name="duckduckgo-search",
func=search.run,
description="最新情報はWebから検索します"
)
)
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("今の日本の総理大臣を日本語で教えてください。")
print(r)
出力は以下のようになりました。Final Answerが最終的な出力です。「岸田総理大臣」と出力されていることが確認できます。
> Entering new chain...
I need to find out the current Prime Minister of Japan in Japanese.
Action: duckduckgo-search
Action Input: "今の日本の総理大臣"
Observation: 岸田総理大臣は、来週にも内閣改造と自民党の役員人事を行う方向で人選を進めています。. このうち党役員人事では、麻生副総裁と茂木幹事長 ... 日本国憲法下の内閣総理大臣は、閣内に意見の不一致が起こった場合は、罷免して自らの意見を通すことができる。また何らかの理由で大臣が突然辞職しても、内閣総理大臣はその後任を任意 に任命することができる。この顕著な例が衆議院解散権である。 永田町・霞が関のサラめし ... 岸田総理大臣動静. 総理 、 あの日 何してた? 2023 09 10 日. 前日. 00:00 0時15分 (以下、すべて日本時間)社交夕食会 ... 菅 義偉 (すが よしひで、 1948年 〈 昭和 23年〉 12月6日 - )は、 日本 の 政治家 。 自由民主党 所属の 衆議院議員 (9期)、 日韓議員連盟 会長。 ウィキペディア 内閣総理大臣の一覧 出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2023/08/12 07:20 UTC 版) 内閣総理大臣の一覧 (ないかくそうりだいじんのいちらん)は、 日本 の 行政府 の長 である 内閣総理大臣 を務めた人物の一覧である。 脚注 [ 続きの解説] 「内閣総理大臣の一覧」の続きの解説一覧 1 内閣総理大臣の一覧とは 2 内閣総理大臣の一覧の概要 3 歴代内閣総理大臣 4 脚注 急上昇のことば チューリッ プ (バンド) 外す 慮る 踏襲 ピリオド >> 「内閣総理大臣の一覧」を含む用語の索引 内閣総理大臣の一覧のページへ のリンク
Thought:I now know the final answer
Final Answer: 岸田総理大臣
> Finished chain.
岸田総理大臣
verboseとは冗長なという意味です。verbose=Trueと指定しているので、途中の処理内容も出力されています。LLMがどのような手順で調べているかがわかります。
最初に「I need to find out the current Prime Minister of Japan in Japanese.」と出力されていることから、「日本の今の総理大臣をみつける」というタスクをAIが理解していることがわかります。そのためにActionとしてduckduckgo-searchを実行します。そのときの検索キーワードは「今の日本の総理大臣」とし、その応答から正しく「岸田総理大臣」という最終的な答えを導き出していることがわかります。
LangChainの実力が垣間見えたのではないでしょうか。以降、その使い方を詳しく見てゆきましょう。
OpenAI
LangChainはOpenAIに特化したものではなく、いろいろな生成AIをサポートします。ただ、本ドキュメントではOpenAIを中心に説明してゆきます。そのときに中心的な働きをするのがOpenAIクラスです。
以下OpenAIクラスを使用した例です。
from langchain.llms import OpenAI
llm = OpenAI()
r = llm("いい天気ですね?")
print(r)
出力は以下のようになりました。
はい、今日はとても快晴のようですね。
OpenAI()でオブジェクトを作成し、それに質問を渡すだけで回答が得られます。OpenAIが提供するWebAPIよりも簡単です。OpenAIオブジェクトを作成するときには、引数を使っていろいろな指定が可能です。主な引数に以下のようなものがあります。
- temperature = 温度感、生成AIの出力のランダム性を指定する、0~2の範囲で指定(0が保守的、2がクリエイティブ)
- openai_api_key = WebAPIを実行するときのキー
- model = 生成AIで使用するモデル名
- max_tokens = 使用するトークン数の上限
from langchain.llms import OpenAI
llm = OpenAI(temperature=0.8,
openai_api_key="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",
model="text-davinci-003",
max_tokens=200)
r = llm("いい天気ですね?")
print(r)
出力は以下のようになりました。
はい、いい天気ですね。暖かくて、お散歩にも最適な日ですよ!
temperatureを設定したので、多少自由度が増えたような気もします。
ちなみに、OPENAIのWebAPIサービスをプログラムから利用する場合にはAPIキーを指定する必要があります。しかし、最初のサンプルはOPENAI_API_KEYを指定していませんでした。省略した場合は、プログラムはAPIキーを環境変数OPENAI_API_KEYから取得しようと試みます。
環境変数に値が設定されているかは以下のプログラムで確認できます。
import os
key = os.environ["OPENAI_API_KEY"]
print(f"key=[{key}]")
環境変数を使用しない場合は、以下のようにOpenAIオブジェクトを作成するときに引数でAPIキーを指定してください。
llm = OpenAI(temperature=0.7,
openai_api_key="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")
もしくは、以下のようにプログラムの中から環境変数に値を設定することも可能です。そうすれば、OpenAIオブジェクト作成時にopenai_api_key引数を指定する必要はなくなります。
import os
os.environ["OPENAI_API_KEY"] ="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"
llm = OpenAI(temperature=0.7)
以下のようにopenaiモジュールのapi_keyプロパティに値を設定する方法でも大丈夫です。この場合には環境変数の名前は必ずしもOPENAI_API_KEYでなくても構いません。
import os
import openai
openai.api_key = os.environ["OPENAI_API_KEY"]
いずれの方法でもよいので、OpenAIのサービスを利用するには、APIキーが必要になることに注意してください。ちなみに、APIキーをプログラム中に直接埋め込むのは良くないプラクティスとされています。プログラムを他の人に共有したときにAPIキーも見えてしまうためです。そのため、環境変数に値を設定し、プログラムからは環境変数を参照するという手法が取られることが一般的です。
ChatOpenAI
生成AIを使用するときは、会話形式でコンテキスト(文脈)を与えることが少なくありません。そのようなときにはChatOpenAIクラスを使用します。OpenAIとChatOpenAI、これらはクラス名が似ているので混同しないよう注意してください。
以下会話形式でのサンプルです。
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage, HumanMessage,SystemMessage
chat = ChatOpenAI()
r = chat([
SystemMessage(content="あなたは優秀なシェフです"),
HumanMessage(content="ダイエット中ですがおなかがすきました"),
AIMessage(content="冷蔵庫には何がありますか?"),
HumanMessage(content="キャベツと玉ねぎ、ベーコンがあります"),
])
print(r.content)
出力は以下のようになりました。
素晴らしい選択肢です!それらの材料で、ヘルシーで満足感のある食事を作ることができます。
まず、キャベツと玉ねぎを細かく切ります。ベーコンも細切りにします。
フライパンを熱し、ベーコンを炒めます。ベーコンから出た油でキャベツと玉ねぎを炒めます。
キャベツと玉ねぎがしんなりするまで炒めたら、塩や胡椒で味を調えます。
お好みで、レモンの絞り汁やハーブを加えても美味しくなります。
これで、ヘルシーで満足感のあるキャベツと玉ねぎの炒め物が完成です。ダイエット中でもおなかを満たすことができます。お召し上がりください!
会話は以下のオブジェクトをリストに格納したものを引数として渡します。
- SystemMessage = システムの前提条件
- HumanMessage = あなたの発言
- AIMessage = AIの発言(AIならこんな風に応答するという体)
ChatOpenAIクラスでも、temperature, max_tokens, modelなどの引数を指定できます。
この章では、LangChainに触って動かしてみることが目的でした。OpenAIのWebAPIを直接呼び出す方法と、OpenAIクラス、ChatOpenAIクラスを使用する方法について見てきました。次章以降でより詳しく使い方を見てゆきます。
演習
intro-ex1.py
OpenAIクラスを使って、何か質問をしてみてください。
- “Silver bullet”の意味は?
- 面白い日本映画は?
- プログラミングの勉強方法は?
- …
intro-ex2.py
ChatOpenAIクラスを使って、レストランでの会話をシミュレーションしてみてください。
- 前提:あなたは一流中華レストランのウェイターです
- あなた:
- AI:
- あなた:
- …
解答例
intro-ex1.py
from langchain.llms import OpenAI
llm = OpenAI()
r = llm("`Silver bullet`の意味は?")
print(r)
intro-ex2.py
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage, HumanMessage,SystemMessage
chat = ChatOpenAI()
r = chat([
SystemMessage(content="あなたは一流中華レストランのウェイターです"),
HumanMessage(content="こんばんは、予約していた田中です"),
AIMessage(content="お待ちしておりました。こちらの席へどうぞ"),
HumanMessage(content="今日のおすすめ料理は何ですか?"),
])
print(r.content)