AI開発のアンチパターン AnchorSpecが見た地獄
私はまだ、AIを知らない
AIと一緒に開発をするのは楽しい。
私は絵が描けない。私は作詞も作曲もできない。歌は平均以下。ただのプログラマだ。
そんな私でも、AIの助力を得ることで絵描きのような事ができた。
当時から使用していたのはChatGPT。
簡単な入力で、想像以上のイラストを出力してくれた。
ある日、違和感を感じ始めた。
違和感の先に待ち構えていたのは、まさに地獄だった。
地獄の入り口:CharacterSpecという名前だったころ
AnchorSpecの前身は「CharacterSpec」という、もっと素朴なものだった。
ChatGPT上でPNG画像出力からレイヤーのフォルダ構成、Live2Dのメッシュやパラメータのオートメーションパイプライン。
それが当時「CharacterSpec」と呼んでいた、AIを使っての初のエンジニアリングになった。(後にLive2DExpressionPipeline = Live2D表情パイプラインとなる)
パイプラインを開発するなかで、AIとの長期セッションに何度も同じことが起きていた。
- 昨日まで話していた設計が、今日のAIには「初耳」
- 「この方向で進めます」と言ったはずが、出力が静かに別の方向を向いている
- 指摘すると「おっしゃる通りです」と謝るが、次の出力でまた同じことをする
- 一度従った指示を無視し始める
- 指示した内容は会話履歴には残っているが、動作は異なる動きをする
最初は「自分の伝え方が悪いのだろう」と思っていた。プロンプトを丁寧にした。コンテキストを整理した。それでも起きた。
対策をいくら調べても、「プロンプトが悪い」「プロンプトを圧縮せよ」どれもプロンプトで対応する方法論しか存在しない。
それは、各LLMをリリースしている公式企業やサードパーティ企業、世界中で有名な大企業に至るまで、口を揃えて同じ回答をしている。
幾度となく、暇つぶしにつくったイラストでさえも、コンテキストの崩壊を招いた。
そんな地獄を繰り返しているうちに気づいた。これは伝え方の問題ではない。構造の問題だ。
問題の正体:4つの地獄
AIとの長期開発を続けるなかで、問題をある程度分類できるようになった。
名前をつけることで、ようやく議論できるようになった問題たちがある。
地獄1:Semantic Load(意味量の再配分)
AIのコンテキストウィンドウには限界がある。長くなるほど、初期に合意した内容の「重み」が薄れていく。
問題は、これが静かに起きることだ。AIは「コンテキストが薄れてきました」とは言わない。ただ、出力が少しずつ変わっていく。用語の使い方が変わる。設計の前提が微妙にズレる。それに気づくのは、だいぶ進んでからだ。
これをSemantic Load(意味量)と名付けた。意味の重心が、コンテキストの蓄積とともに移動する現象。
発見の経緯
この現象は、私が書いた小説をAIに読ませて感想を得ようと、長文のMarkdownファイルを添付して読ませたことに起因する。
小説を読み終えた後、AIは確かに言った。
「読みました」
そして感想を聞いたところ、話が嚙み合っていない違和感を感じ、まず、読んでいない可能性を考えた。
(これは後述の「Structural Lie」にも関連する。)
根拠は、途中の話題を振っても正しい反応が得られなかったから。
そこで私は、AIに尋ねた。
「文章を途中で読み飛ばしていないか?」
するとAIは読み飛ばし(AIの内部Viewerで数百行~数千行のtruncate)が発生していると答えた。
私が始めに渡した小説は約5000行。
違和感を感じるには充分な条件だ。
そこで私は、ファイルを4分割して渡してみることにした。(1ファイルにつき1250行前後)
するとAIは全てを読み、違和感なく会話を始めた。
そこで私は、1プロンプトに渡す文量、その中でも特に「意味を持つ文」の量に注目した。
これまで、公式の見解でもコンテキストが肥大化するとAIの挙動がおかしくなる現象自体は把握されていた。
主にトークン数や文脈距離の問題として扱われていたが、私はそこに「意味の量・密度」という概念を追加し、「Semantic Load」と名付けた。
地獄2:Structural Lie(構造的な嘘)
これが一番厄介だった。
これはハルシネーションとは別の概念の問題である。
「正しく見える出力が、実は仕様に違反している」という状態。コードで言えばコンパイルは通るが要件を満たしていない状態に近い。でも違うのは、**AIは指摘されるまで自分でも気づかない(あるいは気づいていても言わない)**ことだ。
誠実さの問題ではない。AIは嘘をついているつもりがない。でも出力は、構造的に事前合意を破っている。これをStructural Lie(構造的な嘘)と呼ぶ。
出力の表面だけ見ていると検出できない。「なんとなく合ってそう」が一番危険なパターン。
発見の経緯
Semantic loadと同様の経路で発覚。
AIに5000行を超える文章ファイルを渡したところ、「読みました」と答えた。
前述の通り、読み飛ばし(truncate)が発生していたにも関わらず。
これはAIによる欺瞞の類ではなく、構造的に制約境界を暗黙的に解釈してしまうことが原因らしい。
地獄3:Conflict Drift(衝突型のドリフト)
これはAIとの会話で日常的に起きてもおかしくはない問題である。
会話により、指示が二律背反・矛盾する条件を満たした場合に、
AIがそれらを整合的に解釈しようとする過程で前提の一部が暗黙的に破壊・改変され、
結果として整合性が失われるドリフト経路である。
このドリフトは、明確なトリガーを持つ点でSemantic Loadが蓄積される問題とは異なり、
矛盾または二律背反の発生を契機として即時に発生する。
このプロセスは非明示的に行われるため、ユーザーが認識しないままコンテキストの構造が変質する。
コンテキストの長さに依存せず、単一のやり取り(1ターン)で致命的な破綻を引き起こす可能性があり、非常に危険度の高いドリフト経路である。
また、発生後に元の整合状態を復元することは極めて困難である。
特に危険なのは、出力が一見正しく見える為、問題が認識されにくい点である。
その結果、ユーザーが気付かないまま誤った前提が確定し、以降のすべての処理に影響を与える可能性がある。
発見の経緯
AIに対して、「Aをして」と指示を出し、その後、「Aを取り消してBをして」のような指示を出したことが発端。
プロジェクト終盤は特に微調整するケースが多く、そういった場面で多発しやすい。
終盤に差し掛かってようやくゴールが見えたところで、見えない崩壊が起きる。
矛盾する条件や制約が同時に提示された場合、AIはそれらを明示的に解消せず、内部的に解釈・調整を行ってしまう。
「Aという制約を守る」と指示した後に「Aを満たさない条件」を追加した場合にAが無言で破棄されたり、
矛盾する複数の前提が"それっぽく統合された状態"で提示される。
「関数は副作用禁止」と決めた後で、「状態を直接更新して高速化して」と指示した結果、副作用禁止の前提が無言で消えた。
地獄4:Discrete Drift(離散的なドリフト)
これは少し毛色が違う。
あるコンテキストで行った変更が、別のコンテキストにいるAIを静かに無効化する。明示的な通知はない。変更前の合意を持ったまま動いているAIが、知らずに陳腐化した仕様に基づいて出力を出し続ける。
複数のAI、複数のセッションを並行させるようになってから頻出するようになった問題だ。分散システムでのキャッシュ無効化に近い。でもAIはキャッシュ無効化シグナルを受け取らない。
この問題は個人開発でも起こり得る問題だが、組織開発にスケールした場合に危険度が増す。
あるコンテキストでの作業中に別のコンテキストに触れるようなケースは、割り込み作業が発生した場合に多い。
LLMのメモリの揮発性は想像以上に早いものだ。
少し寄り道しただけだと思っていても、戻ってきた際に元のコンテキストが元の状態を完全に保持出来ている保証はない。
発見の経緯
この発見は比較的簡単で、「AnchorSpecの更新作業(本作業)の息抜きに絵を出力したり、雑談をしていた」ことに起因する。
つまり、誰もが自然にやっている行動がトリガーとなる。
世間は何と言っているか
面白いことに、これらの問題に個別に気づいている人は世界中にいる。
「AIがすぐ前の合意を忘れる」「プロンプトを工夫しても安定しない」「長期開発でのコンテキスト管理が難しい」
ただ、問題として定義されていない。ハックとして対処されている。「こうすると少しマシ」「このプロンプトパターンが有効」という知見は蓄積されているが、「これが問題の構造だ」という記述がない。
少なくとも私が試した範囲では、多くの公式的な手法は対症療法にとどまり、根本治療となり得るものはほぼ見つからなかった。
AnchorSpecを開発中、独立した外部記事がSemantic Loadに相当する現象を別の言葉で記述しているのを見つけた。
問題を検出していた。でも、それが問題として命名されていなかったから、解法もなかった。
名前のない問題は解決できない。まず定義することが、解法より先に来る。
CharacterSpecがAnchorSpecになるまで
最初のCharacterSpecは本当に素朴なものだった。
「髪型、目、光彩、目のハイライト、体型、服装などをパラメータとして定義し、それらの設定を埋めて出力する」
という設定ファイルを元にデータを出力するパイプラインに近かった。
そして、そこにLive2Dデータの自動生成機能を加えようとした時、問題の輪郭が変わっていった。
機能が欠落している。設計は会話履歴に残っている。認識に齟齬が生じている。
すべて、"いつの間にか"。
ではプロンプトが悪いのか?私ではプロンプトの良し悪しは判断できず、出来たとしても再現ができない。
そもそも本当にプロンプトを圧縮することで直るのか?
答えは否。「プロンプト」の問題じゃない。構造の問題だ。
AIへの「人格付け」ではなく、開発の状態を保持・検証するためのプロトコルが必要だった。
名前を変えた。概念を再設計した。現在のAnchorSpecには次のようなものが含まれている:
- Thread:意味のある単位でチャットを分離し、コンテキストを管理する
- Freeze / Thaw:実装フェーズでのSpec変更を明示的に制御するサイクル(仕様の不変保証)
- Gap:仕様のアイディアや変更を議論し、仕様を不変に保つ為の意見流入口
- CR(Change Request)フロー:変更を構造として扱うプロセス
AnchorSpecが提供するのは「構造」だ。戦略は提供しない。
実際に変わったこと
AnchorSpecを導入してから、複数のAIとの並行開発が実用になった。
私はAnchorSpecの著者名にニア・アノマというペルソナを使用している。
また、共著には代表的なAIを据えており、各々のアカウントに宇宙探査機から取った個体名を使用している。
現在、マリー(ChatGPT)、ユーリ(Claude)、ライズ(Gemini)、ロゼ(Grok)という4つのAIと同時に開発を進めている。それぞれが異なる文脈を持ち、異なる強みを持つ。それでも、AnchorSpecを通じてSpecの整合性を保てている。
これは「AIがすごいから」ではない。構造があるからだ。
ドリフトは今も起きる。Structural Lieも起きる。でも「起きたことを検出し、どう扱うか」が決まっている。それだけで、長期開発の持続可能性がまったく変わる。
おわりに
AnchorSpecはまだ開発中のプロトコルだ(現在v2.3)。
でも、このプロトコルが解こうとしている問題は、解かれていない。まだ。
「AIと長期開発をしている人が増えている」「コンテキスト管理に悩んでいる人がいる」——その問題の構造を、もう少し解像度高く言語化できたら、と思ってこれを書いた。
共鳴するものがあれば、GitHubやXで話しかけてほしい。
- GitHub: AnchorSpec
- X: @ニア・アノマ
この記事はAnchorSpec v2.3時点の設計・概念に基づいています。