見出し画像

LangChain v0.2 で チャットボットを構築

「LangChain v0.2」で チャットボットを構築してみます。

Build a Chatbot

・LangChain v0.2.0


前回

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メッセージ以上前なので、忘れてしまいました。

次回



この記事が気に入ったらサポートをしてみませんか?