LangChain v0.2 で チャットボットを構築
「LangChain v0.2」で チャットボットを構築してみます。
前回
1. LangChainのセットアップ
(1) パッケージのインストール。
# パッケージのインストール
!pip install langchain
!pip install langchain-openai
!pip install langchain_community
(2) 環境変数の準備。
左端の鍵アイコンで「OPENAI_API_KEY」を設定してからセルを実行してください。
import os
from google.colab import userdata
# 環境変数の準備 (左端の鍵アイコンでOPENAI_API_KEYを設定)
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")
2. LLMの準備
(1) LLMの準備。
from langchain_openai import ChatOpenAI
# LLMの準備
llm = ChatOpenAI(model="gpt-4o")
(2) 会話。
from langchain_core.messages import HumanMessage
# 会話
llm.invoke([HumanMessage(content="こんにちは!私の名前はケンです。")])
AIMessage(content='こんにちは、ケンさん!お会いできて嬉しいです。今日はどんなお手伝いができますか?', response_metadata={'token_usage': {'completion_tokens': 26, 'prompt_tokens': 17, 'total_tokens': 43}, 'model_name': 'gpt-4o', 'system_fingerprint': 'fp_729ea513f7', 'finish_reason': 'stop', 'logprobs': None}, id='run-27568c22-a970-45df-99d9-c470fdf06cb0-0')
(3) 過去の会話内容についての質問。
# 過去の会話内容についての質問
llm.invoke([HumanMessage(content="私の名前は?")])
AIMessage(content='ごめんなさい、あなたの名前はわかりません。お名前を教えていただけますか?', response_metadata={'token_usage': {'completion_tokens': 24, 'prompt_tokens': 12, 'total_tokens': 36}, 'model_name': 'gpt-4o', 'system_fingerprint': 'fp_729ea513f7', 'finish_reason': 'stop', 'logprobs': None}, id='run-842d9fa3-f503-4717-8d57-1b170136e13b-0')
単純なLLM呼び出しでは、会話履歴を持っていないため、過去の会話内容についての質問に答えることはできません。
(4) 会話履歴付きで過去の会話内容についての質問。
from langchain_core.messages import AIMessage
# 会話履歴付きで過去の会話内容についての質問
llm.invoke(
[
HumanMessage(content="こんにちは! 私の名前はケンです。"),
AIMessage(content="こんにちは、ケンさん!お会いできて嬉しいです。今日はどんなお手伝いができますか?"),
HumanMessage(content="私の名前は?"),
]
)
AIMessage(content='あなたの名前はケンさんですね。何か他にお手伝いできることがあれば教えてください!', response_metadata={'token_usage': {'completion_tokens': 27, 'prompt_tokens': 56, 'total_tokens': 83}, 'model_name': 'gpt-4o', 'system_fingerprint': 'fp_927397958d', 'finish_reason': 'stop', 'logprobs': None}, id='run-c52cce0a-cb0c-4f5d-8e31-1d2752034001-0')
3. 会話履歴付きLLM
RunnableWithMessageHistoryでLLMをラップすることで、モデルの入出力が追跡され、それらが会話履歴の保存場所に保存されます。今後の対話では、これらのメッセージがロードされ、入力の一部としてチェーンに渡されます。
(1) 会話履歴付きLLMの準備。
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 会話履歴の保存場所の準備
store = {}
# セッション毎の会話履歴の取得
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 会話履歴付きLLMの準備
llm_with_history = RunnableWithMessageHistory(llm, get_session_history)
ここで重要なのは、get_session_history として渡す関数です。この関数は session_id を受け取り、MessageHistoryを返すことが期待されています。この session_id は、個別の会話を区別するために使用され、新しいチェーンを呼び出すときにコンフィグの一部として渡す必要があります。
(2) コンフィグの準備。
会話履歴付きLLMの場合、configurable に session_id を含める必要があります。
# コンフィグの準備
config = {"configurable": {"session_id": "abc1"}}
(3) 会話。
# 会話
response = llm_with_history.invoke(
[HumanMessage(content="こんにちは! 私の名前はケンです。")],
config=config,
)
response.content
こんにちは、ケンさん! お会いできて嬉しいです。今日はどんなことをお話しされたいですか?
(4) 過去の会話内容についての質問。
# 過去の会話内容についての質問
response = llm_with_history.invoke(
[HumanMessage(content="私の名前は?")],
config=config,
)
response.content
あなたの名前はケンさんです。何か他にお手伝いできることがあれば教えてくださいね。
4. カスタム指示
プロンプト テンプレートを使用して、カスタム指示を含むシステムメッセージを追加します。
(1) システムメッセージの追加。
MessagesPlaceholder ですべてのメッセージを渡します。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# プロンプトテンプレートの準備
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"あなたは役に立つアシスタントです。すべての質問に {language} でできる限り答えてください。",
),
MessagesPlaceholder(variable_name="messages"),
]
)
chain = prompt | llm
(2) 会話履歴付きLLMの準備。
# 会話履歴付きLLMの準備
llm_with_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="messages",
)
(3) コンフィグの準備。
先程のsettion_idと別のIDを割り振ります。
# コンフィグの準備
config = {"configurable": {"session_id": "abc2"}}
(4) 会話。
# 会話
response = llm_with_history.invoke(
{"messages": [HumanMessage(content="こんにちは! 私の名前はケンです。")], "language": "英語"},
config=config,
)
response.content
Hello, Ken! How can I assist you today?
(5) 過去の会話内容についての質問。
# 過去の会話内容についての質問
response = llm_with_history.invoke(
{"messages": [HumanMessage(content="私の名前は?")], "language": "英語"},
config=config,
)
response.content
5. 会話履歴の管理
チャットボットを構築するときに理解しておくべき重要な概念の1つは、会話履歴を管理する方法です。管理されないままにしておくと、メッセージのリストが無制限に増大し、LLMのコンテキストウィンドウからオーバーフローする可能性があります。
(1) 最近のk個のメッセージに絞るメッセージフィルタを追加。
メッセージの最大長を4 (会話2ターン分) としました。
from langchain_core.runnables import RunnablePassthrough
# メッセージフィルタ (最近のk個のメッセージ)
def filter_messages(messages, k=4):
return messages[-k:]
# チェーンの準備
chain = (
RunnablePassthrough.assign(messages=lambda x: filter_messages(x["messages"]))
| prompt
| llm
)
(2) 会話履歴付きLLMの準備。
# 会話履歴付きLLMの準備
llm_with_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="messages",
)
(3) コンフィグの準備。
# コンフィグの準備
config = {"configurable": {"session_id": "abc3"}}
(4) 会話。
# 会話
response = llm_with_history.invoke(
{"messages": [HumanMessage(content="こんにちは! 私の名前はケンです。")], "language": "日本語"},
config=config,
)
response.content
こんにちは、ケンさん!お会いできて嬉しいです。今日はどんなお手伝いが必要ですか?
(5) 過去の会話内容についての質問。
# 過去の会話内容についての質問
response = llm_with_history.invoke(
{"messages": [HumanMessage(content="私の名前は?")], "language": "日本語"},
config=config,
)
response.content
こんにちは、ケンさん!お会いできて嬉しいです。今日はどんなお手伝いが必要ですか?
(6) 過去の会話内容についての質問。
# 過去の会話内容についての質問
response = llm_with_history.invoke(
{"messages": [HumanMessage(content="私の名前は?")], "language": "日本語"},
config=config,
)
response.content
申し訳ありませんが、あなたの名前を特定することはできません。もしお名前を教えていただければ、その名前でお呼びします。助けが必要なことや質問があれば、どうぞお知らせください。
名前を教えたのは4メッセージ以上前なので、忘れてしまいました。
次回
この記事が気に入ったらサポートをしてみませんか?