AI に既存ロジックの修正依頼をしただけなのに…
- 新しい関数が勝手に生まれる
- 命名が崩れる
と、想定外のことが起きることが何度かあったので、内部で何が起こっているのかを軽く検証したいなと思いました。
本記事では、AIがなぜ既存コードを無視するのかを
Next.js + TypeScript で自作した RAG 基盤の検証で明らかにします。
結論:
検索が外れると、生成も必ず迷走するので
「壊さないAI」には 視野 × 語彙 × 安全柵 が必要です
🔧 実験環境
- https://github.com/ka2-ya/next-codex-lab
- Next.js(App Router)+ TypeScript
- レイヤー構成
UI:src/app
Domain:src/domain
Repo:src/server/repos - ts-morph でコードを解析し、AIが参照する索引を
.rag/*.jsonに保存 - RAG検索(pnpm script)
pnpm rag:search:base # 名前一致 pnpm rag:search:embed # 単語の近さで検索(類似度) pnpm rag:search:hybrid # 層情報や言い換えを補強 pnpm rag:eval # MRR評価
目標:
Repo層の TodoRepo.toggle に辿り着けるか?
💥 RAGは既存コードを見つけられるか?(Base / Embed)
Base検索(名前そのまま)
名前一致なら強いが、言い換えると(当たり前だが)見つけられない。
pnpm rag:search:base toggle # ✅当たる
pnpm rag:search:base "invert boolean" # ❌当たらない
→ 自然言語で頼む現場では 現実的ではない
Embed検索(単語の近さ)
近い語を拾えるが 層を知らないため…
- UIの関数が混ざる
- Domainの一般語が上位に出る
誤ヒットが原因で
既存を壊す提案が生まれやすい。
→ 層の概念がないことが本質的な問題
✅ Hybrid検索(改善版)
Hybrid検索は以下を補強:
| 問題 | Hybridでの対策 |
|---|---|
| UIに吸われる | Repo層を優先 |
| 言い換えに弱い | 同義語(SYN辞書) |
| 関連箇所を逃す | 依存関係から補完 |
| 単語の偶然一致 | BM25で精度向上 |
実行👇
pnpm rag:search:hybrid "invert boolean"
結果
TodoRepo.toggle が 常にTop2 に!
→ 正しい場所が見える状態を実現
📈 評価(MRR)
| クエリ | Base MRR | Hybrid MRR |
|---|---|---|
| toggle | 1.00 | 1.00 |
| invert boolean | 0.00 | 0.50 |
→ 言い換えでも正解に辿れるように
🧠 学び:AIはなぜ迷子になるのか
| 起きること | 原因 |
|---|---|
| 既存を無視する | 見つけられてない |
| UI層もいじる | 層を知らない |
| 新しい概念を生む | “存在しない”誤認 |
→ 検索フェーズの失敗 = 生成フェーズの失敗
🛡 壊さないAIのための 3 原則
| 原則 | 効果 | 方法例 |
|---|---|---|
| ① 視野を渡す | 正しい階層へ導く | クラス図 / ドメイン図 / 主要フローを提示 |
| ② 語彙を揃える | 当てる言葉へ寄せる | SYN辞書 / 命名規約 / ユビキタス言語 |
| ③ 安全柵を敷く | 間違いを拒否する | 類似API存在時はCIでFail |
→「見つけて使う」をAI必ず実行させる
✅ まとめ
- AIが壊す理由は 検索失敗
- 補強することで 既存到達率が安定化
- 運用は 視野 × 語彙 × 安全柵がセット
共通の言葉を使う、正しい情報だけを渡す、層ごとに区切るといった当たり前のことが大切なのだと痛感できました。誰にとっても理解しやすいリポジトリにしていこうと思います。