LangGraph — 止まるエージェントを作る
目標
LangGraphで在庫を尋ねる小さなエージェントを作ります。グラフ、積み上がる状態、条件付きブランチ、止まるループ、ツールノード、失敗経路、チェックポインター、経路の記録まで、8つのステップです。
モデルは呼び出しません
判断するノードをルールで置きます。このラボが教えるのはモデルの呼び出しではなくグラフだからです。いつ止まるか、何を状態として持ち歩くか、ツールが失敗したらどこへ行くかです。エージェントで本当に難しいのはそこで、モデルをつなぐのは、あとでノード1つを差し替えるだけの作業です。
形式
/root/work/agent/graph.pyに、次を置きます。
build_graph(checkpoint: bool = False): compileされたグラフを返します。CATALOG: 品目別の在庫ディクショナリです(ステップ5から)。
状態には、最低でもquestion、steps(リスト)、tries(整数)、answerを持たせます。
グラフを1つ作って動かしてみる
mkdir -p /root/work/agentのあと、graph.pyにbuild_graph()を置いてください。StateGraphを作ってノードを1つ付け、compile()したものを返します。状態にstepsリストを置いて、ノードが自分の名前を残すようにしてください。
build_graph().invoke()が動き、stepsにノード名が残っています。
状態が積み上がるようにする
ノードを2つに増やして、両方がstepsに名前を残すようにしてください。Annotated[list, operator.add]を使わないと、最後のものだけが残ります。たどってきた道が消えてしまいます。
異なるノードを2つ以上通り、stepsにすべて残っています。
質問に応じてブランチを分ける
add_conditional_edgesでブランチを作ってください。韓国語で「在庫」を意味する語が入った質問はlookupノードへ進み、そうでなければ別のノードへ進みます。
在庫を尋ねる質問(韓国語)はlookupを通り、挨拶は通りません。
回すが、必ず止まる
見つからなければリトライするループを作ってください。状態にtriesを置いて、上限(3回程度)を設けます。上限がないと、再帰の上限に引っかかって例外で終了します。
ループが2回以上回り、5回以内に自分で止まります。
ツールの結果を実際に使う
CATALOGディクショナリに3件以上を置き(品目: 数量)、lookupが質問から品目を探して照会するようにしてください。照会した値がanswerに実際に入る必要があります。
CATALOGの値がanswerに反映されています。
ツールの失敗は例外ではなく経路である
一覧にないものを尋ねたら、fallbackノードへ進み、「見つからなかった」を答えにしてください。何も見つからなかった結果をそのまま渡すと、でっち上げることになり、それがハルシネーションの最もよくある経路です。
存在しない品目の質問がfallbackを通り、answerが「わからない」という意味を含んでいます。
会話をつなげる
build_graph(checkpoint=True)を受け取れるようにして、MemorySaverを付けてください。thread_idで状態が分かれるかを確認します。同じidはつながり、違うidは別々に残ります。
get_stateで状態が残り、thread_idごとに別々に管理されています。
たどってきた道を記録する
異なるブランチを通る質問を2つ実行して、{질문: [노드, ...]}(プレースホルダーは質問とノード名です)を/root/work/agent/08-trace.jsonに保存してください。記録が実際の実行と違ってしまうと、その記録では何も診断できません。
08-trace.jsonの経路が実際の実行と一致し、2つの経路が互いに異なります。