Verify By Phone(電話による確認)
このスキルは、エージェントが組織に関する保存情報がまだ有効かどうかを確認する必要があり、その手段として組織の公開電話番号への電話が適切である場合に使用します。
verify-by-phone は、検証ワークフロースキルです。1レコードにつき、開示済みのCALL-Eアウトバウンドコールを1回だけ発信し、文字起こしから該当箇所を引用して回答を抽出し、透明な一致重み計算を使用して保存レコードと照合し、調整された信頼度または明示的な棄権を返します。設計目標は、不確かな電話回答を決して自信満々のデータベース書き込みに変換しないエージェントです。
使用すべき状況
以下の場合にこのスキルを使用します:
- プロバイダーディレクトリの掲載情報の検証: この診療所は実在するか、連絡可能か、新規患者を受け入れているか、特定の保険プランに対応しているか
- 保存された事業所レコード(営業時間、サービス、空き状況)が現実と一致するかどうかの確認
- 電話が信頼できる情報源であり、レコードが古くなっている可能性があるデータセットの更新
- 「不明」という結果を、推測ではなく正直に記録する必要があるワークフロー
- 一度に1レコードずつ、人間が承認した受信者リストに対して
使用すべきでない状況
このスキルを以下の目的で使用しないでください:
- 非開示または偽装の電話をかけること。すべての電話は、自動アシスタントであることと発信理由を、同じ発言内で明示します。
- 携帯電話や個人番号にかけること。検証対象は、公表された組織番号です。
- マーケティング、セールス、リードジェネレーション、またはプロモーション目的のアウトリーチ。
- レコードごとに人間の承認なしにリストに一括ダイヤルすること。
- 通話結果から直接、保存レコードを上書きすること。出力は証拠付きの評決であり、書き込みの決定はオペレーターに委ねられます。
- 回答者が与えなかった答えを推測すること。通話で利用可能な回答が得られなかった場合、結果は棄権であり、それが正しい出力です。
同意と開示のルール
これらは重要であり、お決まりの文言ではありません:
- 通話は、何よりも先に、AIの身分と目的を一緒に述べ、通話が録音される可能性があることを伝えて開始します。
- 回答者が自動発信者との会話を拒否した場合、通話は感謝の意を伝えて即座に終了します。結果は、拒否を検証不可能な結果として記録します。
- 保留時間には上限があります。回答者が待つように求めた場合、少し待ってから、無期限に待つのではなく、その旨を報告します。
- エージェントは、通話中または結果において、与えられなかった情報を決して作り出しません。具体的には、クリニックはしばしば発信者に名前、生年月日、保険の会員番号やカード番号、または来院理由を尋ねます。エージェントは、これはディレクトリ確認のための電話であり予約リクエストではないため、その情報を持っていないことを明確に伝え、電話で尋ねるためにかけた質問を繰り返します。プレースホルダーは捏造です。
- ボイスメールは回答ではありません。ボイスメールや留守番電話に接続された場合、通話はメッセージを残さずに即座に終了します。録音からディレクトリ情報を確定することはできず、誰も自分の回線にロボットメッセージが残っているのを見つけるべきではありません。
- 通話は情報確認目的であり、宣伝目的ではありません。すべての通話は、全当事者の同意要件を考慮して録音されているものとして扱います。
検証ワークフロー
- 最初に、ラベル付きシナリオデータを使用して
scripts/calibrate.pyで棄権しきい値を調整します。これにより、「確信」が測定可能な意味を持ちます。デフォルトレベルでは、真の回答が予測セット内に少なくとも90%の確率で含まれます。ステップ6で使用するqhatを出力します。これは認証情報や通話を必要としないため、ダイヤルする前に一度だけ実行できます。 - 検証するレコードを収集します: 組織名、E.164形式の公開電話番号、そして確認する主張(例: 新規患者を受け入れている、指定されたプランに対応している)。
- オペレーターがこの特定の電話番号へのこの特定の通話を承認することを確認します。
scripts/place_verify_call.pyで通話タスクを構築します。このスクリプトはデフォルトでドライランを実行し、正確なタスクと受信者を印刷するだけでダイヤルしません。ドライランの出力が適切に見えた場合のみ--liveを渡します。scripts/poll_result.pyで最終結果をポーリングし、完全なペイロードをローカルファイルに保存します。- ステップ1の
scripts/extract_answer.py --qhatで回答を抽出します。抽出されたすべてのフィールドには、それを裏付ける文字起こしの該当箇所と文字オフセットが含まれます。あいまいな回答(「そう思います」)は、その極性を減衰された信頼スコアで保持します。応答がないターン(間違い電話、拒否、「後でかけ直して」)は決して回答としてカウントされません。回答は、調整された予測セットが単一の値であり、その値が「不明」でない場合にのみ提供されます。そうでない場合、結果は棄権です。--qhatを省略すると、すべてが棄権され、出力はuncalibratedとラベル付けされます。なぜなら、キャリブレーションされていないしきい値は何も保証できないからです。 scripts/reconcile_record.pyで保存レコードと照合します。一致する各フィールドは文書化された量の証拠を追加し、矛盾する各フィールドは証拠を減算し、評決は確認済み、矛盾、または検証不能のいずれかになります。
メンテナンスに関する注意
このスキルのスクリプトは、Attestバックエンド(ソースリポジトリの backend/app)のリファレンス実装の自己完結型コピーです。スキルのインストール時にそのリポジトリへの依存関係が生じないよう、意図的に小さく保たれています。
そのコピーは、約束ではなく、強制されます。ソースリポジトリの tests/test_skill_parity.py は、このスキルの抽出器とバックエンドの抽出器を同じ文字起こしで実行し、回答、引用箇所の文字オフセット、または手がかりの語彙自体について不一致があった場合に失敗します。このメモの以前のバージョンでは、人間による手動での再同期を求めていましたが、コピーはそれでもずれていきました。バックエンドがターン内の最後の手がかりを信頼し、「問題ない」を同意と解釈することを学習した一方で、このスクリプトはまだ最初の手がかりを信頼していたため、単純な「いいえ、違います」がここでは棄権し、向こうでは回答していました。
クイックスタート
pip install calle-ai
# 1. 最初にゲートを調整します。ステップ5で必要な qhat を出力します。
python3 scripts/calibrate.py --data references/sample-scenarios.jsonl --alpha 0.1
# 2. ドライラン: タスクとマスクされた受信者を出力し、ダイヤルはしません。
python3 scripts/place_verify_call.py \
--org "相談センター例" \
--phone "+15550101234" \
--claim-accepting-new-patients yes \
--claim-plan "例: ヘルスPPO"
# 3. 実際に電話をかけます(CALLE_API_KEY が必要です)。
python3 scripts/place_verify_call.py ... --live
# 4. 最終ペイロードを待ちます。
python3 scripts/poll_result.py --call-id call_abc123 --out result.json
# 5. 調整されたしきい値でゲートされた、引用箇所に基づく回答を抽出します。
python3 scripts/extract_answer.py --payload result.json --qhat 0.750
# 6. 保存レコードと照合します。
python3 scripts/reconcile_record.py --payload result.json \
--claim-accepting-new-patients yes --claim-plan-accepted yes
このスキル内のすべてのサンプル電話番号は予約済みの架空の番号です。ドライランパスとバンドルされたシナリオデータにより、ステップ3を除くすべての手順が認証情報や実際の通話なしで実行できます。
出力の例
extract_answer.py は、主張ごとに1つのJSONオブジェクトを出力します:
{
"claim": "accepting_new_patients",
"answer": "yes",
"trust_score": 0.9,
"hedged": false,
"span": {"turn": 6, "text": "はい。", "char_start": 0, "char_end": 3},
"abstain": false,
"gate": "conformal(qhat=0.750)"
}
棄権の場合も同じ構造を保ちますが、"abstain": true となり、調整された予測セットに複数のラベルが含まれていたか、抽出器が回答をまったく見つけられなかった("answer": "unknown")ことが原因です。gate フィールドは決定を下したしきい値を示し、--qhat が指定されなかった場合は uncalibrated と表示されます。これは、発言内容に関係なくすべての主張が棄権する唯一のケースです。reconcile_record.py は、一致重み計算と評決を追加します。どのフィールドも、それを裏付ける引用箇所か明示的な棄権なしに表示されることはありません。
副作用とキャンセル
- 副作用:
--liveを指定した1回の呼び出しにつき、オペレーターが指定した番号へのアウトバウンド電話が1回発生します。再発はありません。このスキルにスケジューラーはありません。 - コスト: ライブ実行1回につき、1回の有料CALL-E通話が発生します。ドライランは無料です。
- キャンセル: CALL-Eは通話のキャンセル機能を公開していないため、停止するタイミングは
--liveの前です。ドライランのデフォルトはまさにその理由で存在します。 - データ: ペイロードはオペレーターが指定した名前のローカルファイルに書き込まれます。電話番号はコンソール出力でマスクされます。このスキル内で結果をCALL-E API自体以外に送信するものはありません。
参考資料
references/api-notes.md: このスキルが依存する、経験的に観測されたCALL-Eペイロードの形状とAPI動作。文書化ではなくテストによって発見された事実を含みます。references/verification-protocol.md: 完全な開示スクリプト、アウトバウンド確認通話の法的姿勢、そしてなぜ棄権が中核的な設計判断であるか。references/sample-scenarios.jsonl:scripts/calibrate.pyで使用されるラベル付き架空シナリオデータ。これにより、認証情報や通話なしで調整を実行できます。