これまでの記事は、ラベル、コーパス、モデル、状態、タスク依存関係、サービスをそれぞれ扱いました。今度はこれらの構成要素をまとめ、連続して対話できるホテルアシスタントを実装します。ポリシーに関する質問に答え、模擬注文を照会し、予約に必要なパラメータを補い、変更や条件付きキャンセルを処理して、確認後にシミュレーション結果を書き込みます。
この.selectionでは各モジュールの役割分担と、インストール、実行、認識器の切り替え、テストの方法を説明します。完全版プロジェクト v3.0 をダウンロード。
1. 対話の実行例
展開後、intent-recognition-lab ディレクトリに移動します。デフォルトのデモは Python 標準ライブラリだけで動きます。
python3 batch3/assistant.py
予約やポリシー、条件付きキャンセル、注文選択、変更、スコープ外のリクエストといったシナリオを実行し、各ターンの入力、ルーティング、状態、結果を出力します。予約シナリオの主な変化は次のとおりです。
| ユーザー入力 | 現在の状態 | 返される結果 |
|---|---|---|
| 部屋を予約したい | 予約目標は確定済み。都市と日程が未入力 | clarify:不足している情報を補完 |
| 北京 2026-10-01 2026-10-03 | 必要な情報が揃い、確認待ちの予約案を作成 | confirm:予約案を表示 |
| 確認 | 模擬注文を書き込み、セッションのタスクが終了 | mock_write:注文番号を返す |
ここで「パラメータを補完する」と「予約を完了する」は別の段階です。ユーザーが日程を入力した段階では、プログラムは予約案の作成までです。明示的に確認されてから、シミュレーション操作を実行します。
注文データはすべてローカル SQLite に保存されます。DEMO-A のデモ上のキャンセル費用は 0、DEMO-B は 50 です。DEMO-X は別のデモユーザーに属します。これらは条件や権限の分岐を確認するためのもので、実際のホテルや決済システムには接続されていません。
2. モジュールの役割とリクエストの流れ
「注文 DEMO-A をキャンセル」を例にすると、HTTP または CLI の入口がセッション情報、リクエスト ID、状態バージョン、テキストを受け取ります。サービスが現在の状態を読み、完了済みリクエストに対する再試行かを判定してから、新しいリクエストを対話処理へ渡します。
認識器がキャンセルの意図を判定し、スロット抽出器が注文番号を見つけます。状態モジュールが意図と対象を結び付け、ルーティング層が注文の所属、状態、費用、バージョンを照会して確認待ちの案を作ります。この時点では confirm を返し、データベース内の注文は booked のままです。
次のターンの「確認」は、コンテキストのない分類問題として処理するのではなく、確認待ちの状態から解釈されます。実行前にも注文のバージョンと条件を再度確認し、成功したら、同じローカルトランザクション内で模擬注文、セッション状態、重複排除結果を更新します。
各コンポーネントの責務とファイルの対応は次のとおりです。
| ファイル | 責務 |
|---|---|
| core.py | 第1バッチの出力契約と単一タスクの状態更新 |
| llm.py | 任意の構造化 LLM 認識インターフェース |
| batch2/baselines.py | ルール、TF-IDF、文ベクトルの候補スコア |
| batch2/policy.py | スコアと差分の受理、棄却と澄清の判断 |
| batch2/task_graph.py | 複数タスクの構造と依存関係の検証 |
| batch3/assistant.py | 文脈の継続、業務ルーティング、模擬ツール、永続化 |
| batch3/planner.py | シミュレーション照会結果に基づいてタスクグラフを進める |
| batch3/service.py | ローカル HTTP リクエストの契約 |
| batch3/evaluate.py | 予測結果の読み込み、分類と棄却の指標算出 |
第1バッチの認識出力契約と第2バッチのタスクグラフは、それぞれ別の形で維持しています。単一タスクの状態と複数タスクの計画は役割が異なるため、明示的な入口で接続しています。すべてのフィールドを肥大化し続ける1つの JSON に詰めるより、この構成の方が理解しやすいです。
3. 短い返信とコンテキストの処理
ユーザーが「注文をキャンセル」と言っても注文番号を示さない場合、デモは「1 DEMO-A、2 DEMO-B」という2つの選択肢を表示します。次のターンで入力された「1」は、現在の有効な選択肢として解釈され、DEMO-A に解決されます。そのうえでキャンセル手順を続けます。
システム全体で「数字の1は注文のキャンセルを表す」というルールに入ることはありません。選択肢の記録は、現在のセッションとタスクに属します。別のセッションで「1」と言っても、同じ意味にはなりません。
日程や部屋タイプの補完も同様です。予約タスクの進行中なら、「北京 2026-10-01 2026-10-03」を現在のパラメータに補完できます。ユーザーが新しい予約目標を明示した場合はタスクを切り替え、新しく出た要望を古いタスクのパラメータ補足と誤認してはいけません。
そのため、プログラムは確認への返信、選択肢の選択、パラメータの補完を先に処理してから、汎用意図認識器を呼び出します。既存のコンテキストを使えば、不要な推測を減らせます。
4. 条件をまたいで保持する
「無料なら注文 DEMO-B をキャンセル」と実行すると、システムはデモ上の費用 50 を照会し、condition_false を返します。確認段階には進まず、注文もキャンセルされません。
ユーザーが最初に「無料なら注文をキャンセル」だけと言った場合、条件は現在のタスクに紐づけて保存する必要があります。次のターンで DEMO-B を選んでも、「無料」という制約を必ず確認します。そのターンが数字だけだからと条件を捨ててはいけません。
条件を提示した時点では成立していても、確認前に変わることがあります。添付された案には、その案で使った注文バージョンを保存し、送信前に費用と状態を読み直します。バージョンが一致しない場合や条件が変わった場合は、競合を返して上位層で案を再提示させます。
より複雑な複数タスクの要求は、次のコマンドで実行できます。
python3 batch3/planner.py
このコマンドは、シリーズ第6回で定義したタスクグラフを受け取ります。実際の実行では、注文照会と空室照会ノードが完了し、キャンセルノードは requires_business_validation で停止します。依存関係を進める様子と、書き込み操作の確認入口を保っています。自由な表現を任意のタスクグラフに変換するのは、認識・計画モジュールの課題です。このコマンドは、明示的な構造化計画から始まります。
5. 意図認識器を切り替える
デフォルトのルールは、そのまま読んで動かせるっており、チュートリアルで使う表現に対応しています。文ベクトルによる候補が必要なら、モデル実験用の依存パッケージをインストールして起動します。
python -m pip install -r batch2/requirements.txt
python batch3/service.py --recognizer vector --db /tmp/intent-vector.sqlite3
文ベクトル方式では、以前に固定したモデルと検証パラメータを引き継ぎます。十分に扱えない入力は、澄清のための質問として返されることがあります。候補方法を変更しても、注文確認、パラメータチェック、確認フローが自動的に緩められるわけではありません。
--recognizer llm を選ぶと、アダプターは OPENAI_API_KEY と OPENAI_MODEL を読み込み、既存の構造化インターフェースを呼び出します。テンプレートの置き場所は batch3/.env.example です。サービスは自動では読み込まないので、実行環境から環境変数を注入する必要があります。LLM が既知の単一意図を返しても、同じ状態管理と業務制約の処理に渡します。
現在のデモでは、候補認識と簡易スロット抽出を分けており、日付には明示的な ISO 形式を使っています。自然な日付表現、文脈依存の複雑な参照、LLM による完全なスロット結果に対応する場合は、状態モジュールを迂回してツールを直接実行するのではなく、インターフェースアダプター層で正規化とフィールド検証を追加してください。ルール、文ベクトル、任意の LLM の認識範囲が同じともって前提にはしません。
今回の検証では、ローカルのルール経路と文ベクトル連携を確認しました。LLM 入口には実行可能な設定を残Dimensが、オンライン API は利用していません。銀行8クラスのスチューデントモデルは訓練手順を示すためのもので、ラベルがホテル側のものと異なるため、ホテル用ルーティングには直接接続していません。
6. HTTP インターフェースから対話する
python3 batch3/service.py --db /tmp/intent-demo.sqlite3 --port 8765
最初のターンでは、キャンセルをリクエストします。
バージョン 1 の確認待ち案を受け取った後、次のリクエストを送ります。
レスポンスが失われた場合は、2 つ目のリクエストを同じ内容でもう一度送れば、保存済みの結果を取得できます。再送のために新しいリクエスト ID を発行しないでください。同じ ID に別のテキストを入れると、冪等性コンフリクトが返ります。新しいリクエストで古い状態バージョンを送ると、状態コンフリクトが返ります。
サンプルをもう一度実行するときは、セッション ID を変更して構いません。模擬注文はデータベース上の永続オブジェクトなので、新しいセッションにしただけでは、すでにキャンセルされた注文は元に戻りません。完全にリセットする必要がある場合は、新しいデータベースファイルのパスを使ってください。
7. テストとモデル実験を実行する
プロジェクトルートで次を実行します。
python3 -m unittest discover -s tests -v
python3 batch2/tests.py
python3 batch3/tests.py
python3 batch3/http_smoke.py
最初の3つは契約、状態、タスクグラフ、完整ワークフローを確認します。最後の1つは実際のループバック HTTP サービスを起動し、リクエストを送信してレスポンスを照合した後、サービスを停止します。
主なテストケースは、スロット補完後の予約、問い合わせでは操作が実行されないこと、条件のターン横断保持、確認前の注文変更、費用変更、重複リクエストで二重に書き込まないこと、セッション間の分離、サービスタイムアウト時のロールバック、プロセス再起動後のタスク継続です。
モデルと評価指標の実験は、別途次のコマンドを実行します。
python batch3/evaluate.py
python batch3/train_small.py
python batch3/infer_small.py "My card was charged twice"
機能テストはプログラムの動きを確認し、分類実験はモデルがデータをどう判定するかを確認します。どちらの結果もプロジェクトに保存されるため、阅读時に failed record..." Need Japanese "失敗した記録を起点に、対応するコンポーネントまでたどることができます。"。
8. 自分の業務に組み込む
まず機能カタログとラベルの定義を置き換え、そのあとで独自のスロットと業務オブジェクトに適合させます。模擬注文ツールを実際のサービスに置き換える場合は、認証対象、ツールの冪等性キー、タイムアウト後の結果照会、費用変更、操作確認を明確に定義してください。
ナレッジ分岐では現在、出典付きの少数のポリシー項目だけを使い、ルーティングと根拠の返し方を把握できるようにしています。文書検索を組み込んだ後は、チャンク分割、ハイブリッド検索、リランキング、回答構成を追加できます。ただし、現在のユーザーが注文した内容を取得する処理は、権限で保護された業務ツール側に置いておく必要があります。
同時実行されるサーバーでは、時間のかかる認識処理を短いトランザクションの外側で実行し、コミット時にバージョンを確認します。遠隔操作は、自身に持つ冪等性と照合のしくみで復旧させます。モデル、プロンプト、ラベル、しきい値、ルーティングバージョンはすべて一緒に保存し、実運用での1回の判断を再現できるようにします。
新しい業務に組み込む際は、ユーザーの目標、コンテキスト、条件制約を保持し、それらに基づいて次の処理を選びます。操作が完了したかどうかは、業務システムに保存された結果を基準に判定します。
9. シリーズ読解のナビ
| 解决的问题 | 対応する記事 |
|---|---|
| システムで意図認識が何を担当するか | 概要 |
| ラベルをどう分けるか | ラベル設計 |
| 複雑な対話をどうアノテーションするか | コーパスとダウンロード |
| ルール、ベクトル、小さなモデルをどう選ぶか | 基本手法 |
| LLM から安定したインターフェースをどう返すか | 構造化認識 |
| 短い返信と言い直しをどう理解するか | マルチターン状態 |
| 複数目標と条件をどう表現するか | タスク依存関係 |
| いつ棄却し、いつ質問するか | 未知の意図と澄清 |
| 改善が効いたかどう確認するか | 評価方法 |
| より軽量な認識器をどう訓練するか | 分類ヘッドと蒸留 |
| 認識後、どの処理経路に進むか | RAG と Agent ルーティング |
| サービスをどう安定運用するか | サービスとエラーのクローズループ |
プロジェクトを読み、動かしたうえで各記事を振り返ると、それぞれの概念がなぜ分けて設計されているのか、またそれらが一つの対話の最終結果にどう影響するのかが理解しやすくなります。