一時的な問いにレビューを置かないのは、手抜きではない
自由診療プラットフォームを運営するTRIBEAU社CTOの小尾氏は、自社のtext-to-SQL基盤を運用してきた記録を公開している(出典)。氏が最初に直面した問題は、AIが書くSQLの構文が間違うことではなかった。「SQLは構文として正しくても、JOINがずれたりフィルタが一つ抜けたりすると、エラーにならず『それっぽいけど間違った数字』を返す」という、実行が成功したように見えて答えが違う種類の失敗だった。
氏が組んだのは、ツール、ガードレール、コンテキスト、改善ループの4要素からなる基盤だった。ガードレールはdry_run_queryが発行する許可トークンなしではexecute_queryが動かない仕組みで、コストの見積もりを必ず通過させる。SQLは構文木に分解したうえで、SELECTやWITH系のみを通す許可リスト方式で検証する。「危ないものを止める」のではなく「安全と分かっているものだけ通す」という設計方針を、氏自身が明示している。改善ループは、実行された問いと実際のSQL、スキャン量をログとして突き合わせ、同じ問いでSQLがばらつく箇所や重いクエリが繰り返される箇所を、あとからテンプレート化やモデル追加の候補として拾い上げる。
四軸を当てはめれば防げる、と思いかけた
PRレビューツールParryを開発している立場からこの基盤を読むと、真っ先に浮かぶのは、Parryの四軸をこの分析クエリにも当てはめれば同じ失敗を防げるのではないか、という発想だ。JOINやフィルタの選択理由をWhyとして書かせ、レビューを経てから実行させれば、意味的に間違ったクエリを実行前に止められるように見える。
ところが、氏が実装した4要素のどこにも、クエリ一つひとつをレビューに通す関門はない。intentログはクエリの実行と同時に記録されるだけで、実行を止める役目を持っていない。レビューという発想そのものが採用されていない。
一時的な問いと永続するコードの境界
なぜレビューを置かなかったのかを考えると、分析クエリという作業の性質に行き当たる。一つの問い(「先月の初回予約からのリピート率を媒体別に見たい」)に対する一つのSQLは、その場で答えを得たら役目を終える。翌週には別の問いが立ち、別のSQLが書かれる。コードとして残り続けるわけではない。
この境界には見覚えがある。Armin Ronacherが「The Coming Loop」で、エージェントのループが機能する数少ない領域として挙げていたのが、変換にすぎないコードか、意図的に寿命の短いコードのどちらかだった(出典)。分析クエリはまさに後者に当たる。氏がクエリ単位のレビューを置かず、実行前の構造的な制約(許可リスト、コストトークン)と実行後の統計的な検知(intentログの分析)という二段構えで代替したのは、一時的な作業に永続的なコードと同じ関門を置いても割に合わないという判断と符合する。レビューを外したのは手を抜いたからではなく、レビューが効く場所とそうでない場所の境界を、結果としてなぞっていたことになる。
境界の内側に残る空白
ただし、この基盤には昇格の瞬間がある。改善ループが「dbtモデル外のテーブル参照」や「同じ問いでSQLがばらつく」兆候を拾い上げたとき、それは「モデル化の候補」としてdbtに組み込まれる。一時的な問いが、200を超えるモデル群に加わる永続的なコードへ変わる瞬間だ。Ronacherの区分にならえば、ここでようやくレビューが必要になる境界を越えている。
だが記事には、この昇格を誰が最終的に判断しているかが書かれていない。候補として上がった変更を、氏を含む分析チームが人力でレビューしているのか、それとも別の統計的な基準で自動的に採用しているのかは分からない。サイレントエラーが最も静かに紛れ込めるとすれば、一時的な問いの中ではなく、それが永続するモデルへ格上げされるこの一点のはずだ。氏の記事はそこまでは書いていない。
コメント (0)
コメントを投稿するにはログインが必要です
まだコメントはありません
最初のコメントを投稿してみましょう!