「注文を調べて、無料でキャンセルできるならキャンセルして、明日の部屋も見てみて。」この文には、注文を調べる、注文をキャンセルする、部屋を調べるという少なくとも3つの目標がある。識別器が3つのラベルしか返さなくても、システムは最初に何をするのか、どの条件でキャンセルするのか、キャンセルに失敗した場合も部屋を調べ続けるのかまでは分からない。
本稿では、複合意図に含まれるタスク、パラメータ、依存関係、条件をタスクグラフで表す方法を示し、検証用とリプレイ用のコードも提供する。これらの例から、マルチラベル分類とタスクプランニングの違いや、順序・条件・代替関係の表し方を理解できる。
タスクグラフのスキーマ、サンプル、テストコードをダウンロード。これはマルチターン状態の記事と同じホテル業務のラベルを使う。ただし、独立した task-graph/2.0 契約を使い、第1版のインターフェースは変更しない。
1. 複数パラメータ・複数意図・同種の複数タスクを区別する
| 入力 | 実際の構造 | 避けるべき処理 |
|---|---|---|
| 北京のホテルを予約。明日チェックインで2泊 | 1つの意図、複数のパラメータ | パラメータごとに意図を作る |
| 注文を調べて、朝食時間も教えて | 異なる2つの意図 | 最後の文だけを残す |
| 北京で1室、上海でも1室を予約 | 同種の2つのタスク | ラベル集合で重複を消して1つの予約にまとめる |
マルチラベル分類が通常答えるのは、どのカテゴリが存在するかである。タスク抽出では、タスクがいくつあるか、それぞれのパラメータが何か、タスク同士にどんな関係があるかも答えなければならない。両者を組み合わせることもできるが、出力できる情報量は異なる。
第1版の契約では、意図配列の添字でパラメータを対応づけ、重複ラベルを禁止している。複数意図の要求を一部は表現できるが、独立した2つの予約タスクを完全には表せない。本稿では固定的なタスクIDを追加し、ラベルでタスクの種類を、IDで具体的なタスクを表す。
2. タスク間の依存関係
複合要求には、並列、順序、条件、代替というよく見られる関係がある。
「注文を調べて、部屋も調べて」は並列である。「先に注文を調べて、それからキャンセルポリシーを教えて」には情報の依存がある。「無料ならキャンセルする」は業務条件だ。「ツインベッドがなければビッグベッドを調べる」は代替分岐になる。
先頭に並んでいるという理由だけで、後ろのタスクが前のタスクの成功を待つとは限らない。データベース問い合わせは並行実行できるが、表示順は固定のままである。逆に、モデルの出力配列で隣り合う2つのタスクには、非常に強い依存関係がある可能性がある。
そのため、依存関係は明示的に書くべきだ。不確実性も残しておく必要がある。「キャンセル後に部屋を調べる」という要求には、キャンセルの成功が必要なのか、キャンセル処理が終わっただけでよいのかも曖昧な部分がある。この違いが業務上の挙動に影響する場合は、追加で質問するか、明確なプロダクト上の規則として扱う必要がある。プログラムが、ユーザーがどちらを選んだかを勝手に仮定してはいけない。
3. タスクグラフを構築する
「また見る」の曖昧さをなくすため、サンプルでは次のように明示している。
先に注文 DEMO-A を調べる。無料でキャンセルできるならキャンセルする。キャンセルしたかどうかに関係なく、上海の明日の部屋を調べる。

注文照会後、キャンセルは無料という条件に制約され、部屋検索は独立して続く
3つのタスクは、それぞれパラメータを保持している。キャンセルのタスクは照会結果に依存する。部屋検索も照会後に行うが、キャンセルの条件が成立しなければスキップされることはない。完全な構造は次のようになる。
「明天」はユーザーの表現をそのまま保持し、実際の問い合わせの前に、セッション時刻とタイムゾーンに基づいて正規化する必要がある。グラフ内のパラメータは認識できた情報だけを示しており、業務上必要なパラメータが揃って有効であることを証明するものではない。
4. 実行条件を検証する
第1版では、認識時に制約が失われないように、条件を自然言語で保持していた。本稿では、そのうち1つの条件をさらに構造化し、注文照会結果から真偽値フィールド can_cancel_free を読み出して true と比較する。
これは教材用の業務APIの約束であり、すべてのホテルAPIにある共通フィールドではない。実際のアプリケーションでは、注文対象の商品、キャンセルポリシー、現在時刻、料金をまとめて照会し、その事実を判定する必要があるかもしれない。この計算結果には、信頼できるデータ源と有効期間が必要だ。ユーザーが言った「無料」という情報だけから判断してはいけない。
コードは任意の条件文字列を実行せず、モデルが生成した Python 式にも対応しない。現時点では、注文照会だけがこの条件を提供できる。また、照会とキャンセルが同じ注文を指しているかを検査し、他の注文の照会結果を誤って使うことを防ぐ。
条件には「真だと判明」「偽だと判明」「証拠なし」の3つの状態がある。「見つからなかった」は false とも、既定の true とも等しくない。3つ目の状態では、実行せずに待機、再照会、確認のいずれかを行うべきで、そのまま実行してはいけない。
5. タスクグラフの構造とセマンティクスを検証する
JSON Schema はフィールドと型を制約できるが、ノード間の関係をすべて表すのは難しい。添付の検証器は次の点を確認する。
- タスクIDが一意であり、ラベルとパラメータが許可カタログに属すること。
- 依存先として参照されたタスクが存在し、自己依存がないこと。
- グラフに循環依存がないこと。
- 条件の情報源が明示的な依存対象であり、その事実を取得できるタスクであること。
- 無料キャンセルという事実が、キャンセルのタスクが指す注文と一致すること。
- 明確に解釈できない要求については、確認質問だけを返し、実行待ちのタスクに混ぜないこと。
例えばAがBを待ち、BがAを待つなら、プログラムは決して開始できない。存在しない lookup2 を条件が参照していても、JSONの形式が正しいだけでは証拠を得られない。
構造の正しさと意味の正しさは別に検証する必要がある。例えばモデルが「キャンセルしない」を取り漏らしても、内部的に一貫したキャンセルのタスクグラフを生成してしまうことがある。構造検証は不正な参照や循環を検出し、意味検証はタスクがユーザーの要求を忠実に表していることを確認する。
6. 同種のタスクに独立したIDを割り当てる
「北京で1室、上海でもう1室を予約する」の構造は次のようになる。
ここでは「再」を明示的な順序として解釈している。北京の予約の成功を条件に上海の予約を安排してはいけないという業務なら、先に確認するか、異なる依存関係の意味を使うべきだ。
実際のシステムでは「完了を待つ」と「成功を待つ」を区別する必要がある。本稿の最小例では、completed は、呼び出し元が正常に完了したことを確認したタスクの集合として扱うだけだ。
複数ターンの修正では、タスクIDが重要になる。ユーザーが「上海の分をツインベッドに変更」と言った場合、上海のタスクを更新する必要がある。book_room というラベルだけで最初の予約を見つけて変更してはいけない。
7. 否定・競合・代替分岐を扱う
「キャンセルせず、注文照会だけ」という要求には、照会タスクだけを生成する。キャンセルのノードをグラフに置き、さらに常に false になる条件给它 Adds location for misuse opportunity, explicit denial becomes business condition. Good. Wait I accidentally drafted English phrase in thought, not final. Final must be Japanese: 「キャンセルのノードをグラフに置き、さらに常に false になる条件を与えると、誤用する機会が増える。明確に否定された意思を業務条件に混ぜることにもなる。」
「DEMO-A をキャンセルして、同時に明日に変更する」は、同じ注文に対する競合する目標を含む。ユーザーが代替または順序を明示的に表現していない限り、確認すべきだ。例えば「この注文をキャンセルしますか。それとも注文を維持したまま日程を変更しますか」と尋ねる。
「ツインベッドがあればツインベッドを予約し、なければビッグベッドを予約する」には、優先順位があり、互いに排他的な代替分岐が必要だ。現在の教材用タスクグラフは、汎用のAND、OR、ELSEや分岐の合流を実装していない。これらが必要になったら、バージョン付きの契約とテストを拡張する。2つの予約ノードには互いに排他的な条件が必要で、そうでなければ同時に予約される可能性がある。
8. 条件分岐の例を実行する
展開したプロジェクトのルートディレクトリで次を実行する。
python3 batch2/task_graph.py
python3 batch2/tests.py
1つ目のコマンドは、人手で構築したタスクグラフを使い、条件の証拠が欠落した場合、false の場合、true の場合をそれぞれリプレイする。
| cancancelfree | cancel ノード | rooms ノード |
|---|---|---|
| 欠損 | waiting_evidence | read_candidate |
| false | skip_condition_false | read_candidate |
| true | requires_business_validation | read_candidate |
この表は実際の実行結果による。ここでは模擬の照会結果を使って、分岐がどう変わるかを示す。コードは実際の注文には接続しない。
read_candidate は依存関係が満たされたことだけを示しており、業務ルーティングに渡して追加処理できる。requires_business_validation は、書き込み操作でさらにパラメータ、権限、料金、確認状態を検証する必要があることを明示している。どのフィールドも、注文がすでにキャンセルされたことを意味しない。
9. タスクグラフ・スケジューリング・実行の責務分担
タスクグラフはユーザーが何を達成したいかを説明し、スケジューリングシステムは実行をどう組み立てるかを決め、ツールサービスは具体的な操作と結果を担当する。この3つを分けると、認識漏れ、誤ったスケジューリング、ツール障害を個別に検出できる。
実際のサービスには、タスク永続化、冪等性キー、失敗時の再試行、キャンセルの伝播、結果の有効期限、認可、実行監査も必要になる。
一部だけ成功した要求では、完了した部分と失敗した部分をユーザーに説明する必要がある。あるノードが失敗しただけで「操作に失敗しました」とだけ返したり、計画全体を成功と宣言したりしてはいけない。
第1版の slot_updates と本稿のタスクグラフを直接混在させることはできない。前者は会話状態を更新し、後者は複合要求の構造を表す。実装では、明示的なアダプタ層を追加して、認識したタスクIDを状態や実行記録に紐づけられる。インターフェースフィールドの意味が変わった場合は、バージョンを更新し、移行の説明を提供すること。
10. 複合意図のテスト方法
少なくとも次のペアを用意する。条件あり/なし、条件が真/欠落、同じラベルのタスク1件/2件、順序あり/なし、明示的な否定/実際のキャンセル要求、参照先が一意/候補が複数。
テストは層ごとに進める。まず認識器がタスクと関係を保持しているかを確認し、次に検証器が無効なタスクグラフを拒否するかを確認し、模擬事実で分岐を検証する。最後に実際のツールの実行、障害復旧、権限制御を検証する。事前に定義したタスクグラフによるリプレイは、第1層のモデル識別テストに代わらない。
複合意図を認識するときは、各タスクのID、パラメータ、依存関係を記録する。タスクグラフはこれらを明示するため、検証しやすく、ユーザーに確認すべき箇所も見つけやすい。
さらに読む:ラベル設計、マルチターン状態とタスク復旧、未知意図と確認。