なぜ「動いている」のに後で困るのか。AI駆動開発、検収の内側で起きていること
「テストは通っています」「動作確認は済んでいます」。そう聞くと、発注側としては一安心します。実際に画面を触ってみても、思った通りに動きます。
ところが、しばらく経ってから「実は追加の対応が必要でした」という連絡を受けることがあります。動いているものに、なぜ後から手が入るのか。今回はその理由を、実際に起きた小さな2つの例で見ていきます。
先に断っておくと、これはどこかの開発会社の技量が足りなかった、という話ではありません。指示しなかったことについて、AIがその場で妥当に見える選択をした結果、という構造の話です。
「動いている」と「後で困らない」は別の基準
AIにコードを書かせる開発では、テストが通り、画面も動く状態まで、驚くほど早くたどり着きます。ここで検収を終えれば、一見問題はありません。
ただし、「テストが通ること」と「後で困らないこと」は、実は別の基準です。テストは、書かれた範囲でしか動作を確認しません。指示されなかった部分については、AIが実行時にエラーにならない書き方を、その場で選んで埋めています。その選び方が、後になって困る原因になることがあります。
言葉で説明してもピンと来にくいと思うので、実際にあった2つの例を見てみます。
実例で見る、目に見えない2つの落とし穴
データを保存した時刻を記録する、指示しなかった場合
何かを保存するとき、「その時刻も記録しておく」という処理はよくあります。次の2行は、ほぼ同じに見えます。
# Before
saved_at = datetime.now(timezone.utc).astimezone().isoformat()
# After
saved_at = datetime.now(timezone.utc).isoformat()違いは .astimezone() という一文だけです。ところがこの一文があると、時刻は「システムが動いている場所のローカル時刻」に変換されて保存されます。
開発中のパソコンと、本番のサーバーで設定されている時刻帯(タイムゾーン)が違うと、保存された記録の間で時刻の基準がバラバラになります。同じ日に登録したはずのデータなのに、記録上は数時間ズレて見える、といったことが起こります。
指示していれば After の書き方で済みます。指示しなかったので、AIは「動く」書き方の中から、たまたまこちらを選んだ、というだけです。
明細の合計と総額が一致するかを検証する、指示しなかった場合
明細の合計と、記録されている総額が一致しているかを確認する処理も、よくある実装です。
# Before
if sum(item_amounts) != total_amount:
raise ValueError("合計が一致しません")
# After
if sum(Decimal(str(a)) for a in item_amounts) != Decimal(str(total_amount)):
raise ValueError("合計が一致しません")一見すると同じ処理です。ところが Before の書き方では、明細に小数を含む金額があると、明細と総額は実際には一致しているのに、「一致していない」というエラーが返ってくることがあります。
これはコンピュータが小数をどう扱うかという、古くから知られている性質によるものです。0.1 と 0.2 を足しても、コンピュータの内部では 0.3 ぴったりにはなりません。金額の計算では、誤差の出ない形に変換してから比較する書き方が昔から決まっていて、指示さえあれば After のように数行で対応できます。今回AIが特別な間違いを書いたわけではなく、指示がなかったので、対応していない書き方のまま実装された、というだけです。
これらは「特殊な失敗」ではない
今回見つかったのは、この2つだけではありませんでした。他にも、次のようなものが見つかっています。
- 外部のサービスを呼び出す処理に、応答が返ってこなかったときの待ち時間の上限が設定されていなかった
- 動作確認用の詳細なログ出力が、そのまま残っていた
- 入力された値の形式チェックが一部抜けていて、原因不明のエラーとして返っていた
- 必要な分だけでなく、関連する情報をまとめて取得する処理になっていた
いずれも、動作はします。テストも通ります。共通しているのは、「指示していなかった部分について、AIがその場で動く選択をした」という構造です。特定の失敗というより、指示の外側で繰り返し起きるパターンだと考えたほうが実態に近いです。
「開発会社次第では」「そこまで発注側に求めるのは無理では」と思ったら
ここまで読んで、いくつか思うところがあるかもしれません。
「開発会社がちゃんとレビューすればいい話では?」。その通りです。実際、レビュー体制でかなりの部分は防げます。ただし今回の2つの例のように、動いていて、テストも通っているコードは、レビューでも見落とされやすい部類に入ります。「動いている」という事実そのものが、確認を後回しにする理由になってしまうためです。
「そんな細かいことまで発注側が指定できるわけがない」。これも自然な感覚です。時刻の保存方法や金額の比較方法を、発注側が仕様として書き下すのは現実的ではありません。この記事も、発注側にそこまで求めるものではありません。
「じゃあAIを使わなければいいのでは」。今回の2つの例は、人が書いても起こりうるものです。AI特有の欠陥というより、指示されていない部分をどう埋めるかという、開発全般に元々あった問題が、開発速度が上がったことで表に出てきやすくなった、というのが実際のところに近いと考えています。
どれも極端な話ではなく、「動いているかどうか」を確認する行為そのものが、この種の問題を発見できるようにはできていない、という一点に集約されます。
まとめ
最初の問いに戻ります。なぜ「動いている」のに後で困るのか。
動いているものの内側は、動作からは判断できません。だから、後で困ります。テストや動作確認は、「指示した範囲が正しく動くか」を確認するものであり、「指示しなかった部分に何が埋まっているか」までは教えてくれません。
では、発注側に手がかりが何もないかというと、そうではありません。動作確認の外側に、聞ける質問が1つあります。「指示していない部分は、どうやって埋めましたか」。動いているかどうかではなく、指示しなかった部分をどう埋めたかを尋ねる質問です。この問いに具体的に答えられるかどうかが、1つの手がかりになります。
こうした確認は、開発側から先に示せるものでもあります。Amanityでは、実際の開発の中でこうした「指示していない部分」を洗い出す確認を行っています。同じような不安をお持ちでしたら、一度ご相談ください。
