GuidesOneMux

ツール呼び出し・人間による承認・実行制限を備えたLangGraphカスタマーサポートエージェントの構築

カスタマーサポートのトリアージに使えるLangGraphの実践パターンを紹介。問い合わせの振り分け、検索ツールの呼び出し、人による承認まで返金を保留する方法、グラフ実行を制限する方法を解説します。

目次

設計上の判断:エージェントには返金の調査を任せ、確定はさせない

サポートエージェントには、購入情報の収集や該当レコードの検索を許可しても、独断で返金を実行させるべきではありません。LangGraphなら、この区別をワークフローとして表現できます。問い合わせを振り分け、必要に応じて読み取り専用の検索ツールを呼び出し、返金処理の前で一時停止して、人が承認または却下します。

このチュートリアルでは、カスタマーサポートのトリアージを題材に、このパターンを構築します。オーケストレーションにはLangGraph、検索にはツール呼び出し対応のチャットモデルを使い、影響の大きいノードの前に人間によるチェックポイントを設け、再帰上限でグラフの実行を制限します。ここに示すコードは応用のためのパターンであり、OneMuxとの連携をテストしたものではありません。

この記事で参照しているLangSmithのチュートリアルでは、検索用と返金用のサブグラフを分けたデジタル音楽ストアのサポートボットを構築しています。顧客情報や購入情報の抽出、不足情報の確認に加え、最終応答、ツール呼び出しの経路、個々のステップを評価する方法も説明しています。返金の例ではデータベースの削除を行い、認証など本番環境向けのセキュリティ対策は明示的に省略しています。このデモの破壊的な動作を、稼働中のサポートシステムにそのままコピーしないでください。 LangSmithの複雑なエージェント評価チュートリアルを読む。

前提条件と境界

LangGraphと、選択したモデルプロバイダーに対応するLangChain統合をインストールしてください。元のチュートリアルではlanggraphとlangchain[openai]を使用していますが、以下の例ではモデルの初期化方法をプロバイダーごとの設定に委ねています。また、読み取り専用の購入情報検索と、アプリケーションが制御する返金処理を備えたデータベースまたはサービスが必要です。

モデルを接続する前に、各ツールでできることを定義してください。

  • **検索:**認証済みの顧客が参照を許可されているレコードのみを返します。
  • **返金の準備:**対象の取引と金額を特定します。レコードは変更しません。
  • **返金の実行:**認可と監査ログを適用し、承認された操作をアプリケーションコードで実行します。

顧客認証、認可、冪等性、返金ポリシーをモデルの裁量に委ねないでください。モデルが生成した顧客名や請求書IDは、本人確認の証拠にはなりません。

人間によるチェックポイントを含むグラフを構築する

この例では、ツール呼び出しに対応したモデル、検索ツール、独立した返金ノードを使います。指定箇所のアプリケーション関数は、認可済みの独自サービス呼び出しに置き換えてください。モデルとツールのスキーマは、設定するエンドポイントがサポートしている必要があります。互換性は、エンドポイントとパラメーターが公式にサポートされている場合に限られます。このコードは、特定のモデルやOneMuxの設定でテスト済みであることを示すものではありません。

from typing import Annotated, Literal

from langchain.tools import tool
from langchain_core.messages import AnyMessage
from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt
from typing_extensions import TypedDict

# Replace with your provider's documented LangChain model setup.
# Confirm that the chosen endpoint supports tool calling and the parameters used.
model = make_your_chat_model().bind_tools([])  # bind the tools below in your application

class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]
    refund_request: dict | None
    approval: str | None

@tool
def find_purchases(customer_reference: str) -> list[dict]:
    """Find purchases for an already authenticated customer."""
    return authorized_purchase_lookup(customer_reference)

tools = [find_purchases]
model = make_your_chat_model().bind_tools(tools)

def assistant(state: State):
    return {"messages": [model.invoke(state["messages"])]}

def prepare_refund(state: State):
    # Build this from validated application data, not model-invented values.
    request = build_validated_refund_request(state["messages"])
    decision = interrupt({
        "type": "refund_approval",
        "request": request,
        "instruction": "Approve or reject this exact refund request.",
    })
    if decision != "approve":
        return {"approval": "rejected", "refund_request": request}
    return {"approval": "approved", "refund_request": request}

def execute_refund(state: State):
    if state.get("approval") != "approved":
        return {"messages": [{"role": "assistant", "content": "The refund was not approved."}]}
    result = issue_refund_safely(state["refund_request"])
    return {"messages": [{"role": "assistant", "content": format_refund_result(result)}]}

builder = StateGraph(State)
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
builder.add_node("prepare_refund", prepare_refund)
builder.add_node("execute_refund", execute_refund)
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", tools_condition)
builder.add_edge("tools", "assistant")
builder.add_edge("prepare_refund", "execute_refund")
builder.add_edge("execute_refund", END)

graph = builder.compile(checkpointer=MemorySaver())

これはグラフ構造の例であり、ルーティングを含む完全な実装ではありません。検証済みの返金意図はprepare_refundに、通常の質問はアシスタント/検索経路に送るよう、アプリケーション側でルーティングする必要があります。interrupt()を呼び出すとグラフが一時停止し、承認ペイロードが呼び出し元に返されます。人間によるレビュー画面には、判断の前に顧客、取引、金額、ポリシーの情報を表示してください。再開する際は、同じスレッド識別子を使い、LangGraphのCommand(resume=...)の仕組みで同じスレッドを再開します。チェックポイントは適切に永続化してください。インメモリのチェックポイントは開発用であり、永続性が必要な本番ワークフローには適しません。

重要な制御フロー上の注意点として、interruptは一時停止の仕組みであり、権限管理システムではありません。返金実行ノードでも承認値を再確認し、返金サービス自体が認可、取引の適格性、重複リクエストを検証するようにしてください。

実行を制限し、失敗を可視化する

グラフの呼び出し時に再帰上限を渡します。これはグラフのスーパーステップ数を制限するもので、モデルのトークン数、ツールの実行時間、データベースクエリのコスト、ユーザーリクエストの件数は制限しません。モデル呼び出しとツールには、別途タイムアウトとリソース制限を設定してください。

from langgraph.errors import GraphRecursionError

config = {
    "configurable": {"thread_id": "support-case-123"},
    "recursion_limit": 12,
}

try:
    result = graph.invoke(
        {"messages": [{"role": "user", "content": "I need help with a purchase."}]},
        config=config,
    )
except GraphRecursionError:
    # Return a safe handoff or ask for clarification; log the trace for review.
    result = {"status": "needs_human_review"}

想定されるワークフローに合わせて上限を設定し、通常の完了パターンとループしやすい入力の両方をテストしてください。上限に達したグラフを何も考えずに再試行しないでください。ツールが冪等でなければ、再試行によって呼び出しが繰り返される可能性があります。読み取り専用の検索ツールは、副作用のある操作より安全に再試行できます。

回答だけでなく実行経路も評価する

参照したLangSmithのチュートリアルでは、評価対象として次の3つが挙げられています。最終応答(顧客への回答は適切だったか)、実行経路(エージェントは期待されるツール呼び出しの経路をたどったか)、個々のステップ(特定の判断で正しいツールを選んだか)です。返金のトリアージでは、本人確認情報が不足しているケース、購入が複数一致するケース、検索結果がないケース、承認が却下されるケース、返金が承認されるケースを用意してください。

必要な情報を捏造せずに確認すること、検索によって返金が実行されないこと、承認なしでは返金を実行できないことを検証してください。テストで実際の顧客レコードが変更されないよう、評価にはモックまたはテスト用サービスを使います。参照元のチュートリアルではデータベース操作にモックモードを使っていますが、説明されているのはそのデモ環境であり、本番環境の安全性を保証するものではありません。

モデルアクセスにOneMuxを使う

OneMuxは、単一のOpenAI互換APIを通じて主要なAIモデルを利用できるサービスです。モデルアクセスに共通のAPIインターフェースを使いたい場合は、OneMuxクイックスタート、APIドキュメント、利用可能なモデルを確認してください。このワークフローとの互換性は、選択したモデルエンドポイントがツール呼び出しと必要なパラメーターを公式にサポートしているかどうかによります。デプロイ前に確認してください。この記事は、LangGraphの例がOneMuxでテスト済みだと主張するものではありません。

制限事項とトラブルシューティング

  • **モデルがツールを呼び出さない:**選択したエンドポイントがツール呼び出しに対応しているか、またツールスキーマがドキュメント指定の形式で渡されているかを確認します。
  • **グラフがループする:**アシスタントとツール間の遷移を調べ、グラフのステップ数を制限し、ツールの処理結果によって次の判断が変わるようにします。
  • **承認後に別のケースが再開される:**サポートケースにひもづく安定したスレッド識別子を使用し、再開前にチェックポイントを確認します。
  • **返金が二重に実行される:**サービス境界に冪等性を実装し、承認されたリクエストと処理結果を記録します。
  • **エージェントが別の顧客のレコードを表示する:**検索サービスの認可処理を修正します。プロンプトの指示はアクセス制御にはなりません。

まとめ

LangGraphを使えば、単一のモデル応答に判断と実行を任せるのではなく、明示的なワークフローステップとしてサポートのトリアージを表現できます。検索は読み取り専用にし、返金の前に一時停止し、実行時に承認を検証し、グラフのステップ数を制限してください。そのうえで、回答とエージェントがたどった経路を評価します。こうした制御によってワークフローを調査しやすくできますが、本番環境のセキュリティとポリシーの適用は、引き続きアプリケーション側の責任です。

参考資料

よくある質問

LangGraphエージェントによる返金の前に、人間の承認を必須にするにはどうすればよいですか?

返金実行ノードの前でLangGraphのinterruptを使ってグラフを一時停止し、検証済みのリクエストをレビュー担当者に提示します。明示的な判断を得た後に限り、同じチェックポイントから再開してください。また、実行ノードでも承認を再確認し、返金サービスでポリシーを適用してください。

LangGraphの再帰上限で、ツールの実行時間やモデルのトークン数も制限できますか?

いいえ。再帰上限が制限するのはグラフのスーパーステップ数であり、モデルのトークン使用量、ツールの実行時間、データベースのコスト、受信リクエストの数ではありません。個別にタイムアウトとリソース制限を設定してください。

顧客が返金を受ける権限があるかどうかを、モデルに判断させてもよいですか?

いいえ。モデルの出力は信頼できない入力として扱ってください。顧客を認証し、取引へのアクセス権と返金の適格性をアプリケーションコードとサービスコードで検証します。

カスタマーサポートエージェントでは、何を評価すべきですか?

最終応答、ツール呼び出しの順序、個々のツール選択ステップを評価します。必要情報の不足、購入の曖昧さ、承認却下、返金承認の各ケースを含め、副作用のある処理にはテスト用サービスを使ってください。

関連記事