1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DatabricksでLangGraphのクイックスタートを動かしてみる(その3)

Last updated at Posted at 2025-01-30

こちらの続きです。

こちらのPart 3: Adding Memory to the Chatbotを動かします。

パート3: チャットボットにメモリを追加する

私たちのチャットボットは、ツールを使ってユーザーの質問に答えることができますが、以前のやり取りのコンテキストを覚えていません。これでは、一貫性のあるマルチターンの会話をする能力が制限されます。

LangGraphは、永続的なチェックポイント機能を通じてこの問題を解決します。グラフをコンパイルする際にcheckpointerを提供し、グラフを呼び出す際にthread_idを指定すると、LangGraphは各ステップの後に自動的に状態を保存します。同じthread_idを使用してグラフを再度呼び出すと、グラフは保存された状態をロードし、チャットボットが前回の続きから再開できるようにします。

後で説明するように、チェックポイント機能は単純なチャットメモリよりもはるかに強力です。エラー回復、人間を介したワークフロー、タイムトラベルインタラクションなど、複雑な状態をいつでも保存および再開することができます。しかし、先走る前に、マルチターンの会話を可能にするためにチェックポイント機能を追加しましょう。

セットアップ

まず、必要なパッケージをインストールし、環境を設定します:

%%capture --no-stderr
%pip install -U langgraph langsmith langchain_openai openai tavily-python langchain_community
%restart_python
import os
os.environ["OPENAI_API_KEY"] = dbutils.secrets.get(scope="demo-token-takaaki.yayoi", key="openai_api_key")

# TavilyのAPIキー
os.environ["TAVILY_API_KEY"] = "<TavilyのAPIキー>"

まず、MemorySaverチェックポインタを作成します。

from langgraph.checkpoint.memory import MemorySaver

memory = MemorySaver()

ここではインメモリチェックポインタを使用しています。これはチュートリアルには便利です(すべてをインメモリに保存します)。本番アプリケーションでは、SqliteSaverPostgresSaverを使用して独自のDBに接続するように変更することが多いでしょう。

次にグラフを定義します。すでに独自のBasicToolNodeを構築しているので、LangGraph組み込みのToolNodetools_conditionに置き換えます。これらは並列API実行などの便利な機能を提供します。それ以外は、すべてパート2からコピーされています。

from typing import Annotated

from langchain_openai import ChatOpenAI
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

# 条件付きエッジを追加
graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
# ツールが呼び出されるたびに、次のステップを決定するためにチャットボットに戻る
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")

最後に、提供されたチェックポインタを使用してグラフをコンパイルします。

graph = graph_builder.compile(checkpointer=memory)

グラフの接続性はパート2から変わっていないことに注意してください。私たちが行っているのは、グラフが各ノードを処理する際にStateをチェックポイントすることだけです。

from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # This requires some extra dependencies and is optional
    pass

download.png

これでボットと対話できます!まず、この会話のキーとして使用するスレッドを選択してください。

config = {"configurable": {"thread_id": "1"}}

次にチャットbotを呼び出します。

user_input = "こんにちは!私の名前はTakaです。"

# configはstream()またはinvoke()の**2番目の引数**です!
events = graph.stream(
    {"messages": [{"role": "user", "content": user_input}]},
    config,
    stream_mode="values",
)
for event in events:
    event["messages"][-1].pretty_print()

Screenshot 2025-01-30 at 12.20.45.png

注意:
構成はグラフを呼び出すときに2番目の引数として提供されました。重要なのは、グラフ入力({'messages': []})内にネストされていないことです。

フォローアップを尋ねてみましょう: あなたの名前を覚えているかどうか確認してください。

user_input = "私の名前を覚えていますか?"

# configはstream()またはinvoke()の**2番目の引数**です!
events = graph.stream(
    {"messages": [{"role": "user", "content": user_input}]},
    config,
    stream_mode="values",
)
for event in events:
    event["messages"][-1].pretty_print()

Screenshot 2025-01-30 at 12.21.29.png

メモリに外部リストを使用していないことに注意してください。すべてチェックポインタによって処理されます!このLangSmithトレースで完全な実行を確認して、何が起こっているかを確認できます。

信じられませんか?別の設定を使用してこれを試してみてください。

# 唯一の違いは、ここで `thread_id` を "1" ではなく "2" に変更することです
events = graph.stream(
    {"messages": [{"role": "user", "content": user_input}]},
    {"configurable": {"thread_id": "2"}},
    stream_mode="values",
)
for event in events:
    event["messages"][-1].pretty_print()

Screenshot 2025-01-30 at 12.21.55.png

変更したのはconfigのthread_idを修正したことだけです。この呼び出しのLangSmithトレースを比較のために参照してください。

現在、2つの異なるスレッドでいくつかのチェックポイントを作成しました。しかし、チェックポイントには何が含まれているのでしょうか?任意の時点で特定のconfigのグラフのstateを検査するには、get_state(config)を呼び出します。

snapshot = graph.get_state(config)
snapshot
StateSnapshot(values={'messages': [HumanMessage(content='こんにちは!私の名前はTakaです。', additional_kwargs={}, response_metadata={}, id='c646da36-f0ba-442e-9b59-22dba8b8e657'), AIMessage(content='こんにちは、Takaさん!どのようにお手伝いできますか?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 89, 'total_tokens': 108, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-db9cd933-bc92-4945-a961-e1cdd4bbe391-0', usage_metadata={'input_tokens': 89, 'output_tokens': 19, 'total_tokens': 108, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), HumanMessage(content='私の名前を覚えていますか?', additional_kwargs={}, response_metadata={}, id='8d20da02-230a-4bea-aa14-a824df5f33f2'), AIMessage(content='はい、Takaさんの名前を覚えています!他に何かお手伝いできることはありますか?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 29, 'prompt_tokens': 123, 'total_tokens': 152, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-febb8f96-99ad-4781-80d1-9158cb42441b-0', usage_metadata={'input_tokens': 123, 'output_tokens': 29, 'total_tokens': 152, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efdeac8-3f45-62d8-8004-7bd258a57fa7'}}, metadata={'source': 'loop', 'writes': {'chatbot': {'messages': [AIMessage(content='はい、Takaさんの名前を覚えています!他に何かお手伝いできることはありますか?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 29, 'prompt_tokens': 123, 'total_tokens': 152, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-febb8f96-99ad-4781-80d1-9158cb42441b-0', usage_metadata={'input_tokens': 123, 'output_tokens': 29, 'total_tokens': 152, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}, 'thread_id': '1', 'step': 4, 'parents': {}}, created_at='2025-01-30T01:50:00.134296+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efdeac8-27a5-6e7d-8003-28bb0f0f974a'}}, tasks=())
snapshot.next
()

上記のスナップショットには、現在の状態値、対応する設定、および次に処理するノードnextが含まれています。私たちの場合、グラフはEND状態に達しているため、nextは空になっています。

おめでとうございます! LangGraphのチェックポイントシステムのおかげで、チャットボットはセッション間で会話の状態を維持できるようになりました。これにより、より自然で文脈に沿った対話が可能になります。LangGraphのチェックポイントは、任意の複雑なグラフ状態も処理でき、単純なチャットメモリよりもはるかに表現力があり強力です。

次のパートでは、ボットが進行する前にガイダンスや検証が必要な状況に対処するために、人間の監視を導入します。

こちらに続きます。

はじめてのDatabricks

はじめてのDatabricks

Databricks無料トライアル

Databricks無料トライアル

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?