AIエージェントの再現性を上げる設計:成功ではなく「失敗の記録」を記憶に積む

AIエージェントを長く運用していると、不思議なことに気づきます。モデルを賢いものに替えても、同じ種類の失敗は減らない。一方で、モデルはそのままなのに、ある時期から「一度で直らなかった作業が、一発で固まる」ようになる。
差を生んでいたのは賢さではなく、記憶に何を積むかでした。とくに「過去にどこで失敗し、何をやらないと決めたか」という失敗の記録を記憶レイヤーに積んだとき、再現性がはっきり上がりました。
この記事は、その設計の話です。複数のAIエージェントを実運用するなかで固まってきた「失敗記録を中心に据えた記憶設計」を、なぜそう切るのかの判断とともに整理します。実装寄りのコード・チェックリストは別記事(Qiita)に分けています。
なぜ「成功手順」より「失敗の記録」なのか
記憶というと、うまくいった手順や正解の保存を思い浮かべます。ですが、設計の軸に置くべきは失敗の記録のほうです。理由は3つあります。
- 成功手順は外から再現できる。 結果を見れば真似できるし、賢いモデルなら毎回それなりの正解を出します。差別化にも、判断の節約にもなりにくい。
- 失敗の記録は外から見えない。 「その運用が過去にどんな落とし穴を踏み、何をやらないと決めたか」は、踏んだ本人にしか残りません。これは模倣困難な資産になります。
- 失敗の記録は判断を「最初から正しい側」に縛る。 成功手順は「やればうまくいく候補」を増やすだけですが、失敗の記録は「やらないこと」を確定させ、探索空間を削ります。後者のほうが、再現性に直接効きます。
賢さは「一回の出力の上限」を決め、記憶は「次の出力を前回より良くできるか」を決める。
設計のゴールは、エージェントが毎回「はじめまして」から手探りする状態をやめ、過去の失敗を制約として先に読み込んでから着手する状態にすることです。
失敗記録を「制約」としてモデル化する
ただの反省メモを大量に置いても効きません。失敗記録が次の判断に効くためには、制約として読める形にしておく必要があります。記憶1件を次の3要素で構造化します。
- トリガー条件:いつこの記憶を思い出すべきか(どんな作業・対象のときに関係するか)
- やらないこと:踏んではいけない具体的な行動(否定形で1行)
- 代わりにやること:正しい側の手順 or ゲート
例として、自分たちの運用で実際に積んでいる失敗記録を抽象化すると、こうなります。
| トリガー | やらないこと | 代わりに |
|---|---|---|
| 見た目を持つ成果物の完了報告前 | コード・存在確認だけで「完了」と言う | 実画面/公開後URLを目視してから報告 |
| ハッシュ・UUID・キー名を扱うとき | 記憶や見た目から手打ちする | コマンド出力から転記し実物照合 |
| 本番アカウントへ到達しうるテスト | 本番に届く経路でテストする | 下書き専用 or 捨てアカウントに隔離 |
ポイントは、「やらないこと」を否定形の制約として確定させることです。「気をつける」ではなく「この経路を塞ぐ」。これが、次回の判断を最初から狭めます。
設計の3つの柱
失敗記録を効かせるために、運用フローの側にも設計が要ります。突き詰めると3つです。
柱1:着手前に記憶を読む(read-before-act)
エージェントが作業に入る前に、関連する失敗記録を先に読み込ませます。ここで重要なのは「全部読む」ではないことです。全件を毎回読むと、コンテキストが膨らみ、肝心の制約が薄まります。
そこで索引(Digest)→関連する確定記憶だけ→現タスクの作業メモ、という読み込み順を設計します。索引には「この記憶は何のときに効くか」の一行だけを並べ、本文は別ファイルに逃がす。エージェントは索引を見て、いま関係するものだけを引き込みます。
柱2:報告の前に実物で検証する(verification gate)
再現性を壊す最大の要因は、「確認せずに完了と宣言する」ことでした。そこで、成果物の種類ごとに完了の定義を変えます。
- 見た目を持つもの(UI/画像/サイト):実画面で目視するまで完了でない
- 公開系(deploy/publish):本番URLが200かつ本文・導線が出るまで完了でない
- データ処理/API:出力の実値を照合するまで完了でない
検証手順そのものも記憶に残します。すると次回も同じゲートを省かずに通せます。
柱3:その場しのぎでなく型に寄せる(template over ad-hoc)
同種の作業を毎回少しずつ違うやり方でやると、失敗の出方も毎回変わり、記憶が効きません。同種作業は単一のテンプレートに集約し、変えるのは差分だけにします。型に寄せると、「どこで失敗しやすいか」が型の側に蓄積され、失敗記録と型が結びつきます。
危険な失敗ほど、想起の優先度を上げる
すべての失敗記録が同じ重みではありません。とくに取り返しのつかない領域——秘密情報の漏洩、本番環境の破壊、権限、法務——の失敗は、想起の優先度を最大にします。
設計としては、記憶に階層(仮・確定・実働)を持たせ、危険系の失敗記録は確定層〜索引へ昇格させ、recall時に最優先で当たるようにします。逆に、めったに使わない記録は奥の層へ退避させ、索引を軽く保ちます。
量をためることがゴールではない。必要なときに、正しい制約が先に出てくる整理がゴール。
ここで一つ、自分たちが実際に踏んだ「設計の失敗」も共有します。索引(Digest)が肥大化し、エージェント起動時のブートストラップで索引の約3割が読み込み前に切り捨てられていました。索引が「毎回正しく読める」サイズを超えると、記憶は積んでも効きません。索引は積むほど良いのではなく、圧縮と階層分離が要る——これも失敗記録として確定させました。
モデルの賢さとの切り分け
念のため。これは「賢いモデルは要らない」という話ではありません。最新モデルはできることの天井を上げます。ですが、その天井に毎回届かせるのは記憶と検証の運用です。記憶がなければ、賢いモデルでも毎回手探りで同じ失敗を繰り返します。
そして、モデルは選び直せますが、積み上げた記憶は置き場所を間違えると引き継げません。記憶をモデルの外に持つ設計(たとえばMCPサーバーとして外出しする)が要るのは、このためです。
まとめ
- 記憶設計の軸は成功手順ではなく失敗の記録。模倣困難で、判断を最初から正しい側に縛る。
- 失敗記録は「トリガー/やらないこと/代わりに」の制約として構造化する。
- 運用フローに3つの柱を入れる:着手前に読む・実物で検証・型に寄せる。
- 危険系の失敗は想起優先度を最大に。索引は肥大化させず圧縮・階層分離する。
実装(記憶ファイルのスキーマ、read-before-actの組み方、検証ゲートの具体)はこちらにまとめています。
記憶を会話の外に積み上げるサービスとして、この設計思想をそのまま形にしています。
Discussion