以前の記事で Verifiable Reasoning を紹介しました — エージェント生成の数値を信頼できるものにするために Tablize が Deep Analysis モードで実行するプロトコル。この記事は完全なウォークスルー:各ステップ、何をするか、naive なエージェントが見逃すものを捕捉する例、設計の根拠。
真剣な分析作業 — 金融、研究、間違った数値があなたにコストを生むあらゆる場所 — のために Tablize を評価しているなら、これが なぜ 出力を信頼できるかを説明する記事です。プロトコルの思考はオープンソース化しています;実装は Tablize ランタイムにあります。
なぜ 9 ステップで、それより少なくないのか
「9 ステッププロトコル」への一般的な反応は「やりすぎでは」です。これは葛藤しました。最初の内部版は 4 ステップでした。4 ステップが防げなかった実際の失敗モードを捕捉するたびに 5 つ追加しました。
各ステップはインシデント故に存在します — ユーザー(または内部テスト)に出荷した実際の間違った答えで、特定のギャップに遡って追跡されたもの。各ステップの下に失敗モードを引用します。
プロトコルはフェイルセーフ:すべての質問ですべてのステップが発火するわけではありません。質問が単純な場合、エージェントはどのステップをスキップするか判断します。完全なシーケンスは集約、テーブル横断結合、または何らかの算術を含む質問で発火します。
ステップ 1:質問を明確化(必要に応じて)
何をするか: ユーザーの質問の曖昧性を検出し、明確化質問をするか、賢明なデフォルトを選んで仮定を開示します。
捕捉する失敗モード: 「収益で上位 10 顧客」。収益はいつ? 生涯、過去 1 年、過去 90 日? 収益はグロスか返金後ネットか? 顧客は削除アカウントを含むか除外するか? 各解釈で異なる答え。naive なエージェントは黙って 1 つを選びます。ユーザーは聞いた答えを得たと思います。
ヒューリスティック: 間違った仮定が答えを 10% 以上変えるなら尋ねる。差が純粋にスタイル的ならデフォルトを選ぶ。
インタラクション例:
ユーザー:「収益で上位 10 顧客」 エージェント:「2 通り解釈できます。(a) 顧客あたり生涯収益、(b) 過去 30 日。窓を指定していないので (a) をデフォルトにします — (b) が欲しい場合は教えてください。」
デフォルトが表示されます。代替案も表示されます。ユーザーは 1 語で修正できます。黙った仮定はありません。
ステップ 2:データプランを作成
何をするか: クエリを実行する前に、エージェントはプランを書きます:どのテーブル、どのカラム、どの結合、どのフィルター、どの集約。
捕捉する失敗モード: エージェントが orders.amount をセント単位だと思っている(Stripe API がそう返すから)が、実際はドル単位(ETL パイプラインがすでに 100 で割っているから)。naive なエージェントはクエリを実行して MRR は $4,720 と報告(実際は $47.20)。データプランは仮定をクエリ 前に 露出します — ミスを見つけて修正できます。
プランの見た目:
「プラン:
stripe_subscriptionsを status=‘active’ でフィルター。amount_dollarsカラム(カラム名からすでにドル単位と仮定;フルクエリ前に 5 行サンプルして確認)を合計。started_atを使って月別にグループ化。注:失敗したサブスクリプション試行は含まない;含めたい場合は教えて。」
設計の根拠: プランは生成が安く(数百トークン)、クエリコストが発生する前にエラーを捕捉し、エージェントのロジックの監査可能な紙の証跡を作成します。
ステップ 3:集約前にサンプリング
何をするか: 集約を実行する前にカラムセマンティクスを検証するため、関連各テーブルから 10〜100 行を引きます。
捕捉する失敗モード: status というカラムに paid、refunded、pending の値があります。エージェントのプランは WHERE status = 'completed' と言います。クエリは 0 行を返します。naive なエージェントは「今月完了した注文なし」と報告 — completed が間違ったステータス値なので、間違い。サンプルステップが即座にこれを捕捉:エージェントが実際の値を見てフィルターを修正します。
他に捕捉するもの: amount カラム内の負の数(しばしば返金行)。日付文字列に埋め込まれたタイムゾーン。0/1 vs true/false vs Y/N で保存されたブール値カラム。ASCII と思われていたカラム内の Unicode。実データセット内の実バグすべて。
コスト: テーブルあたり数百ミリ秒。ミリ秒ごとの価値があります。
ステップ 4:実際のクエリを実行
何をするか: クエリを実行します。
捕捉する失敗モード: それ自体では何も。このステップは簡単な方。興味深いステップはその周りにあります。
なぜプロトコルで別のステップなのか: プロトコルが「プランを作った」と「クエリを実行した」を区別するから。プランが間違っていればクエリは無駄。プランが正しいがクエリが失敗(タイムアウト、権限エラー、構文エラー)なら、エージェントはより小さなバリアントで再試行。これらを別のステップとして保つことで、エージェントが適切なレベルで失敗から回復できます。
ステップ 5:結果のサニティチェック
何をするか: 結果を期待される桁と比較します。月次収益が約 $50K で、現在の月が $4.8M で戻ってきたら、エージェントが疑わしいとフラグします。
捕捉する失敗モード: ステップ 2 の単位エラー(プランで捕捉しなかった場合、ここで捕捉)。デカルト積爆発を引き起こすフィルター忘れ。行を倍増する間違った結合キー。過去 1 か月を望んでいたのに 10 年分のデータを含む日付フィルターのバグ。
ヒューリスティック: 最近のベースラインから 10 倍外の結果をフラグ。> 1 億行の結果をフラグ。> 200% のパーセンテージ変化をフラグ。すべてのグループが同じ値を持つ結果をフラグ(しばしば GROUP BY が間違っている兆候)。
出力例:
「先月の MRR:$48,200。今月の MRR:$4,820,000。これは前月の 100 倍で、単位エラーが疑われる — ドルではなくセントで集約したか確認させてください。」
エージェントは数値を報告するだけではありません。数値が奇妙だと気づき、提供する前に調査します。
ステップ 6:独立したクエリで相互検証
何をするか: 高リスク質問では、異なるデータパスを使って答えを 2 通り計算します。
捕捉する失敗モード: 2 つのシステムが共に注文データを持つ — Shopify と Stripe。Shopify の注文テーブルが収益は $X と言う。Stripe の charges テーブルが収益は $Y と言う。X ≠ Y(端数を超えて)なら、一方が間違いです。naive なエージェントはプランが最初に参照したものを選ぶ;相互検証は不一致を浮上させ、どちらを信頼するかをユーザーに決定させます。
ヒューリスティック: 質問が重要(金融、決定駆動)で、2 つの独立データパスが存在するときに相互検証。1 つのデータソースのみなら省略。
出力例:
「相互チェック:Shopify 注文経由で計算した先四半期のネット収益 = $487,200。Stripe captures 経由の同じ計算 = $471,810。$15,390 (3.2%) の不一致は、おそらく (a) Shopify 注文が税を含む、(b) Stripe captures が税を除く ためでしょう。収益認識には Stripe の数値、グロス売上報告には Shopify の数値を推奨。」
エージェントは黙って 1 つを選びません。不一致を説明します。
ステップ 7:インライン算術
何をするか: 答えに算術が含まれる場合、エージェントが計算の各ステップを表示します。
捕捉する失敗モード: 間違ったベースのパーセンテージエラー。「MRR は前月から 12% 成長」 — 何のベースから 12%? $50,000 のパーセンテージとして $5,200 増加を計算すると、12% ではなく 10.4%。エージェントが計算を表示:
「先月の MRR:$50,000。今月の MRR:$55,200。差分:+$5,200。パーセンテージ:$5,200 / $50,000 = 10.4%。」
これは衒学的。また、モデルがパーセンテージ言語で「of」と「from」を混同することから来る LLM エラーのクラスに対する単一最良の防御です。常に算術を表示してください。
他に捕捉するもの: 間違った時間期間比較(年次比較を月次比較と混同)。一貫しない丸め。損失のある浮動小数点比較。
ステップ 8:仮定を宣言
何をするか: エージェントが行ったすべての仮定をリスト — 曖昧な質問部分について、どのデータセットを使うか、タイムゾーン、集約関数について。
捕捉する失敗モード: 結果的に間違っていることがわかる暗黙の仮定。エージェントがタイムスタンプは UTC だと仮定;実際は America/Los_Angeles。エージェントが revenue は送料を除くと仮定;送料を含む。エージェントが null 値はゼロを意味すると仮定;実際は未知を意味する。
形式:
「仮定:
- 「今四半期」 = 暦 2026 Q1(1 月 1 日 - 3 月 31 日、UTC)
- 収益はグロス売上、税と送料を含む
- テスト注文(@testdata.com にマッチするメールの注文)を除外
- 14 注文の shipping_cost が null だった;0 として扱った
- 月末レートで通貨換算」
ユーザーが仮定を読みます。間違っているものがあれば、自然な日本語で修正します。エージェントは修正された仮定で再実行します。
ステップ 9:結論を明確に述べる
何をするか: 上記の冗長な検証の後、最下部に 1 文の最終回答を生成します。
捕捉する失敗モード: 混乱した読者。プロトコルの最初の 8 ステップはテキストの壁を生成 — 信頼のために必要だが、ざっと読むには困難。ステップ 9 は答えを単一の宣言文に蒸留し、すでにエージェントを信頼している読者が 2 秒でボトムラインを得られるようにします。
形式:
「2026 Q1 ネット収益:$487,300、2025 Q4 から 18% 増。」
それが行。上に:すべての作業。下に:次の質問。
これがユーザー体験をどう変えるか
Standard モードでは、質問して 3 秒で答えを得ます。Deep Analysis モードでは、プロトコルが実行されるため同じ質問が 30〜60 秒かかります。リアルタイムでステップが発火するのを見ます — 各ツール呼び出しがどのステップに奉仕するかでラベル付けされています。
結果は遅いが良い意味で退屈:数値は数値であり、推測する必要はありません。重要な決定には、50 秒の待ちが適切な価格です。
まだ取り組んでいること
ロードマップにあるいくつかのプロトコル強化:
結果に対する信頼区間。 基礎データがスパースまたはノイズが多い場合、エージェントは点推定を主張するのではなく「先月の MRR は $48,200 ± $2,100(95% CI)でした」と言うべき。これは聞こえるより難しい — ほとんどの分析クエリは自然な誤差バーを持たない — が、予測または標本化された値には扱いやすい。
反事実チェック。 「仮定 3 を X から Y に変えると、答えは Z になります。」 質問全体を再度尋ねずに、特定の仮定に対する感度をユーザーが探索できます。
マルチエージェント相互検証。 最高リスク質問では、異なるモデル(例:Claude + GLM)を持つ 2 つの独立エージェントインスタンスを通して同じ質問を実行し、不一致を浮上させる。高価だが、取締役会レベルの数値には適切な形。
推論リプレイ。 プロトコルトレースを保存し、6 か月後にユーザーが同じトレースで再実行できるようにする。同じデータに対して分析が再現可能であることを検証し、現在のデータに対して再実行した場合に何が変わったかを示す。
率直な要約
Verifiable Reasoning は銀の弾丸ではありません。多くの一般的なエラーを捕捉し、エージェントの仮定をユーザーに浮上させる — しかし正しさを保証することはできません。どんなプロトコルもできないから。できることは、エージェント生成の数値を「自己責任で信頼」から「注意深いジュニアアナリストを信頼するのと同じ方法で信頼」に移すこと。
ほとんどの分析作業にとって、それが重要なバーです。注意深いジュニアは作業を示し、仮定を宣言し、明白なエラーをサニティチェックし、会議で擁護できる答えを生成します。Verifiable Reasoning プロトコルは、エージェントをデフォルトでその注意深さにしようとする私たちの試みです。
自分のデータでプロトコルが発火するのを見たいなら、任意の Tablize チャットを Deep Analysis モードに切り替え(トグルはメッセージ入力の上)、非自明な集約を含む質問をしてください。
関連記事: