45395 - シコウサクゴ -

「以前対応したエラーがまた出現」問題:AIの記憶と忘却についての対応方法

AIにはセッション間の記憶がないため、前のセッションで修正したエラーも、新しいセッションでは「初めて見る問題」として扱われる。 同じ調査を繰り返し、同じ結論にたどり着き、同じ修正を適用する。 本記事では、この「記憶と忘却」の問題を構造的に分析し、エラーを「セッションの修正」から「アーキテクチャの改善」へ改善した記録を共有しています。

対象読者

  • AIと協働するなかでエラーの再発に悩んでいる人
  • AI駆動開発における知識の永続化に関心がある人

再発が観測されたエラーパターン

パターン1:外部メトリクス取得の連続失敗

1
初回発生:
2
外部メトリクスAPIからのデータ取得が3日連続で失敗
3
→ 原因: 外部データプロバイダAPIの無料枠レート制限(1分5回)
4
→ 修正: タイムアウトを30秒→60秒に変更
5
6
再発:
7
数週間後に同じタイムアウトエラー
8
→ AIは「初めて見る問題」として調査を開始
9
→ 同じ結論にたどり着く
10
→ 同じ修正を提案

パターン2:pre-commit hookの権限リセット

1
初回発生:
2
git commit 時に pre-commit hook の実行権限エラー
3
→ 原因: chmod +x が適用されていない
4
→ 修正: chmod +x .git/hooks/pre-commit
5
6
再発:
7
git操作の後に権限がリセットされる
8
→ AIは再び chmod +x を提案
9
→ 根本原因(hook管理の仕組み自体)に触れない

パターン3:外部サービス認証エラー

1
初回発生:
2
外部APIの認証トークンが期限切れ
3
→ 原因: トークンの自動更新が未実装
4
→ 修正: 手動でトークンを再取得
5
6
再発:
7
トークンが再び期限切れ
8
→ AIは「トークンを再取得してください」と提案
9
→ 自動更新の仕組みは提案しない(前回の文脈がないため)

パターン4:PYTHONPATHの設定漏れ

1
初回発生:
2
launchd経由でスクリプト実行時に ModuleNotFoundError
3
→ 原因: plistの EnvironmentVariables に PYTHONPATH 未設定
4
→ 修正: 該当plistに PYTHONPATH を追加
5
6
再発:
7
別のplistファイルで同じエラー
8
→ AIは同じ修正を提案
9
→ 「全plistを一括チェック」は提案しない

なぜ修正したはずのエラーが再発するのか

原因1:修正がセッションローカル

1
セッション内の修正は「短期記憶」だ。
2
3
セッションA:
4
外部メトリクスAPIのタイムアウトを60秒に変更 ← 修正完了
5
CLAUDE.mdには追記しなかった ← ここが問題
6
7
セッションB(翌週):
8
外部メトリクスAPIのタイムアウトエラー発生
9
AIは60秒に変更した経緯を知らない
10
→ 最初から調査をやり直す

原因2:修正がファイルローカル

1
plist_A.plist に PYTHONPATH を追加 ← 修正完了
2
plist_B.plist にも同じ問題がある ← 未修正
3
plist_C.plist にも同じ問題がある ← 未修正
4
5
ファイル単位の修正は「同じパターンの別ファイル」に波及しない。
6
AIは「今見ているファイル」を修正するが、
7
「同じ問題を持つ他のファイル」を探索しない。

原因3:修正が不完全(症状の対症療法)

1
# 対症療法(症状を消す)
2
response = requests.get(url, timeout=60) # タイムアウトを延ばす
3
4
# 根本対策(原因を潰す)
5
# → レート制限を意識した呼び出し間隔の制御
6
# → フォールバック先の追加
7
# → キャッシュの導入

原因4:知識の伝播漏れ

1
本番環境: 修正済み
2
Pre環境: 未修正 ← 同じエラーが出る
3
開発環境: 未修正 ← 同じエラーが出る
4
5
1つの環境での修正が他の環境に伝播しない。

忘却のスペクトラム:記憶の強さの5段階

1
記憶の強さ(弱→強):
2
3
Level 1: セッション記憶
4
→ セッション終了で消失
5
→ 最も弱い記憶
6
→ 例: 「外部メトリクスのタイムアウトを60秒にした」
7
8
Level 2: CLAUDE.md記憶
9
→ セッションをまたいで保持
10
→ AIがCLAUDE.mdを読めば思い出す
11
→ 例: 「よくあるエラー表に追記」
12
13
Level 3: コードレベルの修正
14
→ ファイルに永続化される
15
→ ただし同種の問題を別ファイルでは防げない
11 collapsed lines
16
→ 例: 「timeout=60をコードに直接書く」
17
18
Level 4: テストレベルの修正
19
→ 自動で回帰を検出する
20
→ CIで毎回実行される
21
→ 例: 「タイムアウトのフォールバック動作をテスト」
22
23
Level 5: アーキテクチャレベルの修正
24
→ 問題のカテゴリ全体を不可能にする
25
→ 最も強い記憶
26
→ 例: 「フォールバックチェーン実装で取得失敗を撲滅」

セッション修正をアーキテクチャ改善に引き上げる

具体例:メトリクス取得エラーの進化

1
# Level 1: セッション修正(次のセッションで忘れる)
2
# 「外部メトリクスAPIのタイムアウトを60秒に変更」
3
response = requests.get(url, timeout=60)
1
# Level 2: CLAUDE.md修正(AIが読めば思い出す)
2
# よくあるエラー表に追記:
3
4
| エラー | 原因 | 対処法 |
5
| ------------------ | ----------------------------------------------- | ---------------------------------- |
6
| メトリクス取得失敗 | 外部データプロバイダAPIの無料枠制限。1分5回まで | タイムアウト60秒を確認。頻度を制限 |
1
# Level 3: コードレベルの修正(ファイルに永続化)
2
METRICS_API_TIMEOUT_SECONDS = 60 # 外部データプロバイダ無料枠対応
3
METRICS_API_MAX_CALLS_PER_MINUTE = 5
4
5
response = requests.get(url, timeout=METRICS_API_TIMEOUT_SECONDS)
1
# Level 4: テストレベルの修正(回帰を自動検出)
2
from unittest import mock
3
from requests.exceptions import Timeout
4
5
def test_metrics_provider_handles_timeout():
6
"""タイムアウト時にフォールバック値を返すことを検証."""
7
provider = MetricsProvider()
8
with mock.patch("requests.get", side_effect=Timeout):
9
result = provider.get_metrics()
10
assert result == provider.DEFAULT_METRIC # フォールバック値を返す
11
12
def test_metrics_provider_respects_rate_limit():
13
"""レート制限を超えない呼び出し間隔を検証."""
14
provider = MetricsProvider()
15
call_times = []
12 collapsed lines
16
original_get = requests.get
17
18
def tracking_get(*args, **kwargs):
19
call_times.append(time.time())
20
return original_get(*args, **kwargs)
21
22
with mock.patch("requests.get", side_effect=tracking_get):
23
for _ in range(6):
24
provider.get_metrics()
25
26
# 6回の呼び出しが1分以上に分散されていること
27
assert call_times[-1] - call_times[0] >= 60
1
# Level 5: アーキテクチャ修正(問題カテゴリの撲滅)
2
# MetricsProviderにフォールバックチェーンを実装
3
# 外部データプロバイダ → キャッシュ値 → デフォルト値
4
5
def get_metrics(self) -> float:
6
"""外部メトリクスを取得する。複数のフォールバックで取得失敗を防ぐ."""
7
# 1st: プライマリソース
8
try:
9
metrics = self._fetch_from_provider()
10
self._update_cache(metrics)
11
return metrics
12
except (Timeout, ConnectionError, RateLimitError) as e:
13
logger.warning(f"[Metrics] プライマリ取得失敗: {e}")
14
15
# 2nd: キャッシュ値(最終取得成功値)
8 collapsed lines
16
cached = self._get_cached_metrics()
17
if cached is not None and self._is_cache_fresh(cached):
18
logger.info(f"[Metrics] キャッシュ値を使用: {cached.value}")
19
return cached.value
20
21
# 3rd: デフォルト値
22
logger.warning(f"[Metrics] デフォルト値を使用: {self.DEFAULT_METRIC}")
23
return self.DEFAULT_METRIC

Level 5まで引き上げると、メトリクス取得の失敗はシステムの動作に影響しなくなる。個別のエラーに対処するのではなく、「外部API呼び出しは失敗する前提」でアーキテクチャを設計する。


CLAUDE.mdの「よくあるエラー」表

再発するエラーに対応するたびに、CLAUDE.mdに知見を蓄積してきた。現在8件以上のエントリがある。

1
## よくあるエラーと対処法
2
3
| エラー | 原因 | 対処法 |
4
| ----------------------------- | ----------------------------------- | ----------------------------------------------- |
5
| メトリクス取得失敗 | 外部データプロバイダAPIの無料枠制限 | タイムアウト60秒、フォールバックチェーン確認 |
6
| ModuleNotFoundError (launchd) | plistのPYTHONPATH未設定 | EnvironmentVariablesにPYTHONPATH追加 |
7
| 外部サービス認証エラー | トークン期限切れ | トークン再取得。自動更新の仕組みを確認 |
8
| pre-commit hook 権限エラー | chmod +x 未適用 | chmod +x .git/hooks/pre-commit |
9
| Parquetスキーマ不一致 | カラム追加後の旧ファイルとの混在 | マイグレーションスクリプト実行 |
10
| Slack通知失敗 | Webhook URL未設定 or 期限切れ | .envのSLACK_WEBHOOK_URLを確認 |
11
| DB接続タイムアウト | TimescaleDBのconnection pool枯渇 | max_connectionsの設定確認、コネクション漏れ調査 |
12
| pytest収集エラー | conftest.pyの循環import | conftest.pyのimport構造を確認 |

このテーブルが機能する理由

1
AIの動作フロー:
2
1. セッション開始 → CLAUDE.mdを読み込み
3
2. 「よくあるエラー」テーブルを認識
4
3. ユーザーからエラー報告を受ける
5
4. テーブルと照合 → 既知のエラーなら即座に対処法を提示
6
7
テーブルがない場合:
8
1. セッション開始
9
2. ユーザーからエラー報告を受ける
10
3. ゼロから調査を開始
11
4. 30分かけて同じ結論にたどり着く

再発防止の階層的アプローチ

修正後のチェックリスト

エラーを修正した後、以下を確認する。

1
## エラー修正後チェックリスト
2
3
1. [ ] CLAUDE.mdの「よくあるエラー」表を更新したか?
4
→ 次のセッションでAIが同じ問題をすぐ対処できるように
5
6
2. [ ] 同じパターンが他のファイルにないか確認したか?
7
→ grep/find で同種の問題を探索
8
9
3. [ ] テストを追加したか?
10
→ このエラーが再発した場合にCIで検出できるように
11
12
4. [ ] アーキテクチャレベルの改善は必要か?
13
→ 個別対応ではなく、問題カテゴリ全体を防ぐ設計変更

実際の適用例:PYTHONPATH問題

Terminal window
1
# Level 2: CLAUDE.mdに追記
2
# 「plist作成時は必ずPYTHONPATHをEnvironmentVariablesに設定」
3
4
# Level 3: 全plistを一括チェック
5
grep -rL "PYTHONPATH" ~/Library/LaunchAgents/com.project.*.plist
6
# → PYTHONPATH未設定のplistを一覧表示
7
8
# Level 4: テスト追加
9
# plistのバリデーションテストで必須環境変数をチェック
1
def test_all_plists_have_required_env_vars():
2
"""全plistファイルに必須の環境変数が設定されていることを検証."""
3
required_vars = ["PROJECT_ROOT", "PYTHONPATH"]
4
plist_dir = Path.home() / "Library" / "LaunchAgents"
5
plists = list(plist_dir.glob("com.project.*.plist"))
6
7
for plist_path in plists:
8
with open(plist_path, "rb") as f:
9
plist_data = plistlib.load(f)
10
11
env_vars = plist_data.get("EnvironmentVariables", {})
12
for var in required_vars:
13
assert var in env_vars, (
14
f"{plist_path.name}{var} が未設定"
15
)
1
# Level 5: アーキテクチャ改善
2
# plist生成スクリプトにテンプレートを導入し、
3
# 必須環境変数を自動設定
4
def generate_plist(job_name: str, script_path: str) -> dict:
5
"""plistを生成する。必須環境変数は自動設定."""
6
return {
7
"Label": f"com.project.{job_name}",
8
"ProgramArguments": ["/usr/bin/python3", script_path],
9
"EnvironmentVariables": {
10
"PROJECT_ROOT": str(PROJECT_ROOT),
11
"PYTHONPATH": str(PROJECT_ROOT),
12
# 手動設定を不要にすることで設定漏れを防ぐ
13
},
14
}

「忘却」を前提とした設計

AIの記憶モデル

1
人間の記憶:
2
短期記憶 → 反復 → 長期記憶 → 忘却曲線で徐々に薄れる
3
4
AIの記憶:
5
セッション記憶 → セッション終了 → 完全消失
6
CLAUDE.md → 永続 → ただし読まれないと意味がない
7
コード → 永続 → ただし「なぜ」は残らない
8
テスト → 永続 + 自動検証 → 最も信頼できる記憶

設計指針

1
memory_design_principles = {
2
"assume_forgetting": "AIは忘れる前提で設計する",
3
"externalize_decisions": "意思決定は外部ファイルに永続化する",
4
"automate_verification": "記憶に頼らずテストで検証する",
5
"escalate_fixes": "セッション修正をアーキテクチャ改善に引き上げる",
6
}

学んだこと

1. セッション内の修正は最も弱い記憶。CLAUDE.md、テスト、アーキテクチャの順に記憶が強くなる

セッション修正は翌日には消える。CLAUDE.mdに書けばAIが読む限り残る。テストに落とせばCIが自動で検証する。アーキテクチャを変えれば問題自体が発生しなくなる。修正を「どのレベルの記憶にするか」を意識的に判断する。

2. 「同じエラーが再発した」はCLAUDE.mdの更新漏れのサイン

同じエラーが2回出たら、それはCLAUDE.mdに追記すべきだったサインだ。修正後にCLAUDE.mdを更新するルールを設けることで、2回目の発生時にはAIが即座に対処できるようになる。修正そのものより「修正の記録」が重要。

3. AIの「よくあるエラー」表は、人間のFAQと同じ——頻出問題を事前に教えることでセッション品質が上がる

人間の新人にFAQを渡すように、AIにも「よくあるエラー」表を渡す。8件のエントリは少なく見えるが、これだけでセッション冒頭の30分の調査時間を節約できる。エラー対応の知見は蓄積すればするほど、AIの初動が速くなる。


まとめ

「以前対応したエラーがまた出現」問題への対策は以下の3点だ。

  1. エラー修正を5段階の記憶レベルで管理する: セッション修正(最弱)→ CLAUDE.md追記 → コード修正 → テスト追加 → アーキテクチャ改善(最強)。再発頻度と影響度に応じて適切なレベルを選ぶ
  2. CLAUDE.mdの「よくあるエラー」表を運用する: エラーを修正するたびにテーブルに追記する。AIは次のセッション開始時にこのテーブルを読み込み、既知の問題に即座に対応できる
  3. 「忘れる前提」で設計する: AIはセッション間の記憶を持たない。これはバグではなく仕様だ。外部ファイルへの永続化、テストによる自動検証、アーキテクチャによる問題撲滅——記憶に頼らない仕組みを設計する

Article title:「以前対応したエラーがまた出現」問題:AIの記憶と忘却についての対応方法
Article author:45395
Release time:2026-03-30