「チャットボットにもっと複雑な業務を任せたいけれど、プロンプトを繋げれば繋げるほど挙動が不安定になる」
そんな課題に直面していませんか?
初期のPoC(概念実証)では、ユーザーの入力を受け取り、プロンプトに埋め込んでLLM(大規模言語モデル)に投げ、その結果を返すという単純な「一本道(リニアチェーン)」で十分でした。しかし、予約代行や複雑なデータ分析、あるいは社内システムと連携した自律的なエージェントを構築しようとすると、この一本道のアプローチはすぐに破綻します。
実際のユーザーの発話パターンや人間の思考プロセスは、決して一本道ではありません。複数の可能性を並行して検討したり、情報が足りなければ前のステップに戻ったり、条件によって判断を変えたりします。この複雑で動的な対話フローをシステムとして実装し、自然なユーザー体験と業務要件を両立させるために不可欠なのが、DAG(有向非巡回グラフ)によるオーケストレーションです。
今回は特定のライブラリ(LangChainやLangGraphなど)の使い方ではなく、それらの裏側にある「設計思想」そのものについてお話しします。自社独自のプロダクトに、どのようにして堅牢なLLMオーケストレーション層を組み込むか。対話AIを設計・実装するエンジニアの視点で、API設計の全貌を探求していきましょう。
1. 概念モデル:なぜDAG(有向非巡回グラフ)が必要なのか
まず、APIの仕様に入る前に、なぜ「グラフ構造」を必要としているのか、その技術的根拠を整理しておきます。これは単なる実装スタイルの違いではなく、LLMアプリケーションの品質(Quality)と信頼性(Reliability)を決定づける構造的な問題です。
リニアチェーン vs グラフ構造
従来のスクリプト的な処理(リニアチェーン)は、Step A -> Step B -> Step C と進みます。これは実装が簡単ですが、以下の致命的な欠陥を抱えています。
- コンテキストの汚染: すべてのステップの履歴を保持し続けると、コンテキストウィンドウ(入力トークン数)があっという間に溢れます。関係のない情報が混ざることで、LLMの推論精度も低下します。
- エラー伝播の不可逆性: Step Bで誤った判断をした場合、一本道ではリカバリーが効きません。そのまま誤った情報をStep Cに渡し、最終的にユーザーへ誤回答を出力してしまいます。
- レイテンシの増大: 独立して実行できるタスク(例:Web検索と社内DB検索)があっても、直列処理では待ち時間が発生します。
これに対し、DAG(Directed Acyclic Graph)モデルでは、タスクを「ノード(点)」、依存関係を「エッジ(線)」として定義します。これにより、必要な情報だけを次のノードに渡すことができ、分岐や並列実行が可能になります。
ステート管理とコンテキストウィンドウの最適化
対話設計を行う際、重要となるのが「ステート(状態)」の管理です。
DAGオーケストレーションにおけるAPI設計の核心は、グローバルな状態(Global State)とローカルな状態(Local State)の分離にあります。
- Global State: ユーザーの最終ゴールや、これまでの決定事項など、ワークフロー全体で共有すべき情報。
- Local State: 特定のノード内だけで必要な一時的な情報(例:検索クエリの中間生成物など)。
APIを通じてDAGを定義することで、各ノードが受け取る入力(Input)と出力(Output)を厳密に定義できます。これにより、「Step CにはStep Aの結果だけを渡し、Step Bのノイズは渡さない」といった制御が可能になり、トークンコストの削減と精度の向上を同時に実現できるのです。
APIが提供する抽象化レイヤー
これから定義するAPIは、複雑なPythonコードや条件分岐ロジックを、宣言的なデータ構造(JSON)として抽象化するものです。これにより、プロンプトエンジニアや非エンジニアでも、ワークフローの構造を視覚的に理解・修正できるようになります。AIエンジニアの視点から、この「制御盤」をどう設計するかという視点で読み進めていただければと思います。
2. 認証と認可:セキュアなエージェント実行環境
エンタープライズ環境で自律エージェントを動かす場合、セキュリティは「後付け」できません。エージェントは自律的にツール(APIやDB)を操作するため、人間以上に厳格な権限管理が必要です。
APIキーと署名リクエスト
基本となるのはAPIキーによる認証ですが、LLMワークフロー特有の要件として、リクエスト署名(Request Signing)の導入を推奨します。ワークフローの定義自体が改ざんされると、エージェントが悪意ある動作をする「プロンプトインジェクション」のリスクが高まるからです。
X-Agent-Signature: t=1679982000,v1=a1b2c3...
このように、リクエストボディのハッシュ値をヘッダーに含めることで、通信経路での改ざんを防止します。
実行権限のスコープ設計(read/write/execute)
ここで重要なのが、「誰が実行するか」ではなく「このワークフローには何の権限を与えるか」というスコープ設計です。OAuth 2.0のスコープ概念を拡張し、エージェント用の権限モデルを定義します。
agent:search.read: 検索ツールの読み取りのみ許可agent:database.write: 特定テーブルへの書き込み許可agent:email.send: メール送信アクションの許可
これらの権限をワークフロー登録時(POST /workflows)に宣言させ、実行時(POST /executions)にトークンとして発行する仕組みを採用することで、「情報収集エージェント」が誤って「メール送信」を行うといった事故をシステムレベルで防ぐことができると考えられます。
テナント分離の仕様
B2B SaaSとして提供する場合、テナント(顧客)ごとのデータ分離は必須です。各ノードがアクセスするベクトルデータベースのネームスペースや、保存されるメモリ(会話履歴)は、必ずtenant_idによって論理的に隔離されるよう、APIのパスやヘッダーで強制する設計にします。
3. リソース定義:Node(タスク)とEdge(依存関係)のデータ構造
ここからが本題です。DAGを構成する「ノード」と「エッジ」を、APIのリソースとしてどう表現するか。実装者が最も知りたいデータモデルの詳細を見ていきましょう。
Nodeオブジェクトのスキーマ仕様
ノードは「単一の思考ステップ」を表します。LLM呼び出しだけでなく、関数実行や人への確認もノードになり得ます。
{
"id": "node_summarize",
"type": "llm_chain",
"config": {
"model": "gpt_standard_alias",
"temperature": 0.2,
"prompt_template_id": "tmpl_summary_v1",
"input_map": {
"context": "$.steps.node_search.output.results"
}
},
"output_schema": {
"type": "object",
"properties": {
"summary": { "type": "string" },
"sentiment": { "type": "string" }
}
}
}
設計のポイント(Rationale):
- モデル指定の抽象化:
modelフィールドには、具体的なバージョン名を直接記述するのではなく、gpt_standard_aliasのようなシステム内で定義した抽象的な識別子(エイリアス)を使用することを強く推奨します。2026年現在、LLMのライフサイクルは劇的に高速化しています。例えば、最新のChatGPTの最新モデル(2025年12月リリース)のような高性能モデルが登場する一方で、かつての主力だったChatGPT世代などのレガシーモデルは順次廃止やアクセス制限の対象となっています。API定義側でバージョンをハードコードせず、バックエンドの設定でエイリアスを実際の最新モデルIDにマッピングする設計にすることで、コードを変更せずにモデルを安全にアップデートできる柔軟性が生まれます。 - input_map: 前のノードの出力を、次のノードの入力変数にどうマッピングするかをJSONPath(
$.steps...)で記述します。これにより、データの流れが明示的になります。 - output_schema: LLMの出力は非構造化テキストですが、ここでJSON Schemaを定義することで、システム側で構造化データ(JSON)へのパースを強制します。これが後続の処理を安定させる鍵です。
Edgeオブジェクトと条件付き分岐(Conditional Edges)
エッジは単なる接続線ではありません。「条件によって行き先を変える」というロジックを含みます。
[
{
"source": "node_classify",
"target": "node_support",
"condition": "$.output.category == 'complaint'"
},
{
"source": "node_classify",
"target": "node_sales",
"condition": "$.output.category == 'inquiry'"
}
]
このように、前のノードの出力結果(category)に基づいて、次に実行すべきノードを動的に決定します。これが「if-then」ロジックをグラフ構造に落とし込む方法です。
Stateスキーマとデータの受け渡し
ワークフロー全体で持ち回る「状態(State)」の定義も重要です。例えば、「ユーザー名」「これまでの要約」「残りの予算」などです。APIでは、ワークフロー定義時に初期Stateのスキーマを登録し、実行時にバリデーションを行うことで、予期せぬデータの欠落を防ぎます。
4. エンドポイント仕様:ワークフローの登録と実行
設計したデータ構造を操作するためのREST APIエンドポイントを定義します。LLMの処理は時間がかかるため、基本的に非同期処理として設計するのが鉄則です。
POST /v1/workflows (グラフ定義)
まず、DAGの構造自体を登録します。
- Request: NodeとEdgeの配列、初期State定義、権限スコープ。
- Response:
workflow_idとバージョン番号。 - Validation: 登録時に「循環参照がないか(A→B→Aとなっていないか)」「未定義のノードへのリンクがないか」を厳密にチェックします。実行時エラーを減らすための重要な防壁です。
POST /v1/executions (非同期実行)
登録済みのワークフローを実行します。
- Request:
workflow_idと初期入力データ(input)。 - Response:
execution_idとステータス(PENDING)。
ここでは即座に結果を返さず、ジョブキューにタスクを積んでIDだけを返します。クライアント側はポーリングするか、Webhookを待つ設計にします。
GET /v1/executions/{id}/trace (実行トレース)
デバッグや精度改善のために、実行の経過詳細を取得するエンドポイントです。単なるログではなく、「どのノードを通り、各ステップでどんなプロンプトが生成され、LLMが何を返したか」を時系列で構造化して返します。
LangSmithなどの最新のLLM運用プラットフォームでは、トレース機能が高度化しており、エージェントの挙動評価やマルチモーダルデータの可視化までカバーされています。自社APIを設計する際も、これらと同様の視認性(オブザーバビリティ)を提供することが理想的です。
具体的には、以下の要素をレスポンスに含めることを検討してください:
- ステップごとの入出力: 各ノードが受け取ったコンテキストと生成した出力。
- ツール実行履歴: 外部APIを呼び出した際の引数とレスポンス。
- パフォーマンス指標: 各ステップのレイテンシ(処理時間)とトークン消費量。
- エラー詳細: 失敗した場合のスタックトレースやLLMからの拒否理由。
これにより、開発者は「なぜその回答になったのか」を論理的に追跡でき、プロンプトエンジニアリングの改善サイクルを回しやすくなります。
5. エラーハンドリングとリトライ戦略
AIシステムにおいて、エラーは「例外」ではなく「日常」です。APIレベルで堅牢な回復メカニズムを組み込む必要があります。
HTTPステータスコードと独自エラーコード
LLM APIのタイムアウトやレート制限(429 Too Many Requests)は、プラットフォーム側で吸収すべきエラーです。クライアントには 503 Service Unavailable などを返しつつ、内部的には自動で待機・再試行を行います。
一方、プロンプトの内容が悪くてJSONパースに失敗した場合などは、422 Unprocessable Entity と共に、「どのノードでパースに失敗したか」を特定できる詳細なエラーオブジェクトを返します。
ノードレベルの自動リトライ設定(Exponential Backoff)
各ノード定義には、リトライポリシーを含めるべきです。
"retry_policy": {
"max_attempts": 3,
"backoff_coefficient": 2,
"on_error": "fallback_node_id"
}
このように、「3回失敗したら諦める」だけでなく、「失敗したら別の簡易モデル(フォールバック用ノード)に切り替える」といった指定を可能にします。対話の破綻を防ぐ適切なフォールバック設計は、システムの可用性とユーザー体験を劇的に高めます。
Human-in-the-loop(人間による介入)のエンドポイント
自律エージェントの暴走を防ぐ最後の砦が「人間」です。特定のノード(例:契約確定や送金)の前に approval_required: true フラグを設定できる仕様にします。
実行プロセスはこのノードで一時停止(PAUSEDステータス)し、人間が以下のエンドポイントを叩くまで再開しません。
POST /v1/executions/{id}/approve: 承認して次のノードへ進む。POST /v1/executions/{id}/reject: 拒否して終了、あるいは修正フローへ分岐する。
この仕組みをAPIに組み込むことで、安心して業務にAIを導入できる信頼感が生まれます。
6. SDK実装サンプル:Pythonによるオーケストレーション
最後に、これら抽象的なAPI仕様を、開発者がどう使うかをイメージするためのSDK(Software Development Kit)コード例を示します。Builderパターンを使って、直感的にグラフを構築できるようにします。
クライアント初期化とグラフ構築
from my_llm_sdk import WorkflowBuilder, Node, Edge
# ワークフローの定義
builder = WorkflowBuilder(name="CustomerSupportBot")
# ノードの追加
search_node = Node(id="search", type="tool", tool_name="knowledge_base")
# モデル名はプロジェクトの要件に合わせて指定
llm_node = Node(id="generate_answer", type="llm", model="gpt-latest")
# 条件付きエッジの追加
# 検索結果が0件なら、別の対応フローへ
builder.add_conditional_edge(
source="search",
condition=lambda x: len(x['results']) == 0,
target="escalate_to_human",
else_target="generate_answer"
)
# グラフの登録
workflow_id = client.register_workflow(builder.build())
実行結果のストリーミング取得
# 非同期実行
execution = client.start_execution(workflow_id, input={"query": "APIの使い方は?"})
# ストリーミングで各ステップのログを表示
for event in execution.stream():
print(f"[{event.node_id}] {event.status}: {event.output}")
このように、複雑なJSONを手書きさせず、型ヒントの効いたSDKを提供することで、開発体験(DX)を向上させることができます。
まとめ:信頼性の高いAI基盤を設計するために
ここまで、DAGオーケストレーションのためのAPI設計について、概念から実装詳細まで解説しました。重要なポイントを振り返ります。
- 構造化: リニアな連鎖からDAGへ移行し、コンテキストと依存関係を制御する。
- 安全性: 署名とスコープでエージェントの権限を縛り、セキュアな実行環境を作る。
- 堅牢性: エラーを前提としたリトライ機構と、人間による承認フロー(HITL)を組み込む。
もちろん、LangChainやLangGraphといった既存の強力なフレームワークを活用するのも効率的な選択肢です。ただし、これらを実務に導入する場合は、その進化の速さとアーキテクチャの変更を正しく理解しておく必要があります。
最新の公式情報によると、LangChainは機能の肥大化を防ぎ安定性を高めるため、アーキテクチャの再構成を行いました。具体的には、機能が以下の3つのパッケージに分割されています。
- langchain-core: 抽象化された基本コンポーネントのみを含む中核ライブラリ
- langchain-community: サードパーティ製品との連携機能
- langchain: これらを統合し、chainやagentなどの高レベルAPIを提供するパッケージ
また、バージョニングルールが明確化され、安定版(0.1.x系など)と開発版の区別が厳格になりました。一方で、新しいバージョン(0.2.x系以降)では、利用者の少ない古い機能の削除や後方互換性に影響する変更も計画されています。
LangGraphについても、スケジュール実行機能の強化やストリーミング接続の安定性向上など、本番運用を見据えたアップデートが頻繁に行われています。実装の際は、必ず公式ドキュメントで最新の仕様と推奨パターンを確認してください。
自社の業務フローに深く組み込むなら、こうしたフレームワークのライフサイクル管理コストを見積もった上で採用するか、あるいは本記事で紹介したような「オーケストレーション層」を自社で設計し、制御可能な範囲を広げるか。ユーザーテストと改善のサイクルを回しながら、実際に使われるチャットボットを構築していく上で、その判断こそが今後のAIプロダクト開発における競争力になります。
皆さんのプロジェクトが、単なる実験で終わらず、実運用で価値を生み出す、自然で効果的な対話AIシステムになることを応援しています。
コメント