はじめに

ソフトウェアを作るとき、多くの開発者はまず正常に動くことから始めます。ユーザーが期待どおりに操作して、システムが期待どおりに動く。その理想的な流れを先に固め、エラー処理は後から追加するもの——そんな順序が当たり前のように思われています。
まず動くものを作れ、洗練は後でいい。プロトタイプ文化の中では、これは合理的な判断に見えます。
しかし実際にシステムを動かすと、すぐに現実に気づきます。
エラーは例外ではありません。失敗は日常です。
AIエージェントを複数組み合わせて使うシステムでは、この問いに毎日直面します。タスクが途中で中断する。品質ゲートで差し戻しが起きる。コンテキストが圧縮されて作業状態が失われる。
これらは「まれに起きる異常事態」ではなく、設計が最初から考慮すべき仕様の一部です。

第1章 エラーは例外ではなく、仕様である

「異常は後回しでいい」という考え方は、初期段階では合理的に見えます。まず動くものを作る。エラーは動いてから考える。スピードを重視するチームでよく聞く言葉です。
しかし問題は、後から追加したエラー処理は構造に入り込まず、表面に貼り付くだけになるということです。try-catchが随所に散らばり、エラーメッセージが曖昧なまま残される。ユーザーは次に何をすればよいかわからない。開発者は何が起きたかを追えない。これは設計の失敗ではなく、そもそも設計がなかったということです。
エラーを仕様として扱うとは、システムの入出力として失敗を明示することです。この操作は成功する場合と、タイムアウトする場合と、拒否される場合がある——と最初から定義する。そう決めると、設計が変わります。どこに状態を持つか、失敗をどう記録するか、次のアクションを誰に伝えるか。これらが自然に問いとして立ち上がります。
マルチエージェントシステムを設計・運用してきた経験から言えば、差し戻しやタスクの中断を最初から想定している場合と、していない場合では、システムの育ち方が根本的に異なります。最初から差し戻しを想定して設計されたシステムは、差し戻しのたびに情報が蓄積され、次の精度が上がります。想定していないシステムは、差し戻しが来るたびに対処を後付けするだけで、同じ失敗を繰り返します。

第2章 失敗の種類を分類する

柔道を習い始めたとき、最初に教わるのは技ではなく受け身です。投げられたとき、転んだとき、いかに安全に着地するか?前方、後方、側方、それぞれで体の使い方が違います。受け身の形をひとつしか知らない柔道家は、想定外の方向に崩されたとき対応できないのです。エラー設計も同じで、失敗の種類を知らないと、来た失敗に適切に受け身が取れません。

失敗にはおおよそ三つの種類があります。
一つ目は一時的な失敗です。APIのタイムアウト、ネットワークの断絶、並行処理の競合。これらは原因が解消すれば、もう一度試せばうまくいきます。設計上の対処はシンプルです。適切な待機時間を置いて再実行する仕組みを入れておく。それだけで多くの一時的失敗を吸収できます。
二つ目は論理的な失敗です。品質ゲートの不通過、検証の失敗、期待された形式との齟齬。これらはただ再試行しても意味がありません。何かを修正してから試みる必要があります。AIエージェントが出力した内容が品質基準を満たさず差し戻されるとき、それは一時的な失敗ではありません。同じ入力で同じ処理をすれば、同じ結果になります。修正という行為が必要です。
三つ目は設計的な失敗です。前提が間違っていた、要件の解釈がずれていた、環境の想定が現実と異なった。これらは修正でも再試行でも解決しません。一度立ち止まって、設計そのものを問い直すことが答えになります。
この三種類を見分けることが重要です。設計的な失敗に再試行を重ねると、無駄なリソースを消費するだけです。一時的な失敗に方針変更を叫ぶと、変えなくてよいものを変えて混乱します。失敗の種類を判断する目を設計に組み込んでおくことで、問題解決のサイクルが整います。

第3章 回復可能にするための設計思想

失敗の種類を分類したら、次は回復の設計です。ここで核心になるのは冪等性という考え方です。どこで失敗しても、もう一度最初から実行すれば同じ状態に戻れる。この性質を持つ処理は、途中で止まっても再開できます。
そのためには、処理の途中状態を外部に記録する必要があります。「ここまで完了した」という事実をファイルやデータベースに残しておく。再起動したとき、前回どこまで進んでいたかが分かります。完了済みの処理を再度実行しても害がない形にしておくと、再起動が安全な操作になります。
失敗を記録する仕組みも欠かせません。何が起きたか、どんな入力のときにどんなエラーが出たか、差し戻しの理由は何か——これらを蓄積していくと、失敗は情報に変わります。次のサイクルの設計改善に使える根拠になり、同じ失敗を防ぐルールになる。失敗の記録がないシステムでは、同じ種類のエラーが何度でも繰り返されます。原因を分析する材料が残っていないからです。
エラーの通知設計にも意識が必要です。失敗が起きたとき、誰に何を伝えるか。運用者には詳細なログが必要ですが、利用者には次のアクションを示す必要があります。沈黙は最悪の対処です。何も返ってこないとき、利用者は何が起きたかわからず、開発者は何を直すべきかわかりません。ここの認知コストは膨大です。
プロダクション環境でAPIキーが設定されていない状態でシステムが起動した場合、それはサーバーの設定ミスです。この失敗を設定ミスという種類として最初から仕様に含めていれば、起動時チェックが設計に組み込まれます。500エラーを返して止まるだけでなく、何が不足していて何を設定すればよいかを伝えられます。エラーメッセージは設計の一部であり、コードの一部です。

おわりに

エラーの設計は、保険ではありません。システムを長く使い続けるための根幹です。
正常に動くことへの設計と、うまく失敗することへの設計——その両方を持って初めて、システムは安定して使えるものになります。正常系だけ設計されたシステムは、初めて壊れたとき迷子になります。エラーを設計に含んでいるシステムは、壊れたとき何をすべきかがわかっています。失敗を受け止めて、次のサイクルに進む形を最初から持っています。
AIエージェントを使って仕事を進める場面でも同じことが言えます。エージェントが完璧に動くことを前提にするのではなく、エージェントが失敗したとき何が起きるかを先に考える。差し戻しが来たとき記録を残す。中断が起きたとき再開できる構造にしておく。その想定があると、失敗がシステムの材料になります。
異常を仕様として扱うことは、悲観的な設計ではありません。むしろ逆です。失敗を直視して設計に折り込んだシステムは、失敗に強い。受け身を知っている人間が投げられても起き上がれるように、失敗の受け方を設計されたシステムは、壊れても回復できます。その強さが、長く使われるシステムの基盤になります。
誰でもAIでつくれるからこそ、設計に思いを馳せるのが一層重要になったと感じます。