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?

Stateの更新方法を制御する (LangGraph)

Posted at

はじめに

LangGraphのGraph APIではノードが値を返すとStateが更新されます。
更新方法は「上書き」がデフォルトですが、「マージ」や「追加」も可能です。
これを制御するのがReducerです。
例えば過去のチャット履歴を残すために、Stateに operator.add を指定することがありますが、これもReducerの一つです。

Reducerとは

Reducerは、各Stateキーに対して、ノードからの更新をどう適用するかを定義する関数です。
組み込みで用意されているものの他に、自前でカスタムしたものを使うことができます。

Reducerの指定方法

AnnotatedでキーとReducerを紐付けることで、そのキーをノードが返すと自動的にReducerが呼び出されるようになります。

from typing import Annotated

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]  # 組み込みReducer
    extracted_data: Annotated[dict, extracted_data_reducer]  # カスタムReducer
    count: int  # Reducer不使用 (更新時に上書き)

組み込みReducer

operator.add

messages: Annotated[list, operator.add]
  • チャット履歴を蓄積することができる
  • ノードが {'messages': [new_msg]} を返すと、既存リストに追加される

add_messages

from langgraph.graph.message import add_messages

messages: Annotated[list, add_messages]
  • operator.addと似ているが、同一IDのメッセージを上書きする機能が付いている

カスタムReducer

辞書のマージ例

def extracted_data_reducer(current: dict, update: dict) -> dict:
    """
    updateに含まれる操作キーに応じてStateを更新する
    - 'merge_customer_info': customerをマージ
    - 'add_product_info': productsリストに追加
    """
    result = current.copy()
    if 'merge_customer_info' in update:
        result['customer'] = {**result.get('customer', {}), **update['merge_customer_info']}
    if 'add_product_info' in update:
        result['products'] = result.get('products', []) + [update['add_product_info']]
    return result

class AgentState(TypedDict):
    extracted_data: Annotated[dict, extracted_data_reducer]


# ノード内での使い方
def extract_customer_node(state):
    # 顧客情報を部分的に追加(既存データとマージ)
    return {'extracted_data': {'merge_customer_info': {'email': 'test@example.com'}}}

def extract_product_node(state):
    # 商品情報をリストに追加
    return {'extracted_data': {'add_product_info': {'name': '商品A', 'price': 1000}}}

ポイント

Reducerは (現在値, 更新値) => 新しい値 の形式を取ります。

  • 第1引数: 現在のState値(LangGraphが自動で渡す)
  • 第2引数: ノードが返した辞書から、該当キーの値を取り出したもの(LangGraphが自動で渡す)
  • 戻り値: 新しいState値

Reducerが呼ばれるタイミング

  1. ノードが {'extracted_data': {...}} を返す
  2. LangGraphが extracted_data_reducer を呼ぶ
    extracted_data_reducer(
        current=現在のstate['extracted_data'],
        update=ノードが返した辞書の'extracted_data'の値
    )
    
  3. 戻り値が新しい state['extracted_data'] になる

ただし、ToolNodeは特殊で、その戻り値はmessagesにのみ反映されます。
よって、Reducerを定義するだけでは、ツールの戻り値によってmessages以外のStateを更新することはできません。

おわりに

今回は、ノードの戻り値をStateに反映する方法についての記事を書きました。
公式でも挙げられているように、Stateの更新方法を細かく制御するには、Reducerを用いると便利です。
次回はツールの戻り値をStateに反映する方法について書いてみたいと思います。

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?