はじめに
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が呼ばれるタイミング
- ノードが
{'extracted_data': {...}}を返す - LangGraphが
extracted_data_reducerを呼ぶextracted_data_reducer( current=現在のstate['extracted_data'], update=ノードが返した辞書の'extracted_data'の値 ) - 戻り値が新しい
state['extracted_data']になる
ただし、ToolNodeは特殊で、その戻り値はmessagesにのみ反映されます。
よって、Reducerを定義するだけでは、ツールの戻り値によってmessages以外のStateを更新することはできません。
おわりに
今回は、ノードの戻り値をStateに反映する方法についての記事を書きました。
公式でも挙げられているように、Stateの更新方法を細かく制御するには、Reducerを用いると便利です。
次回はツールの戻り値をStateに反映する方法について書いてみたいと思います。