docs: add storage protection design analysis for microSD write reduction
Map each persisted data type by in-process vs out-of-process consumers to identify write deferral opportunities. Session files and stats have no real-time external readers during operation, making write-behind caching safe. Estimate 97% write reduction (720→24 writes/hour) via 5-min periodic flush + shutdown hook. Detail turn-scoped read cache for MEMORY.md to eliminate redundant ReadLongTerm() calls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0128a0632a
commit
a52434e5c2
1 changed files with 83 additions and 0 deletions
83
CLAUDE.md
83
CLAUDE.md
|
|
@ -320,3 +320,86 @@ func Truncate(s string, max int) string {
|
|||
```
|
||||
|
||||
文字数を正しく数えるために `[]rune` へ変換するのは正しい。しかし **①変換前に `len(s)` で byte 長をチェックして早期 return できる**(ASCII なら byte 長 == rune 長)、**②実際の入力が ASCII 主体であれば `utf8.RuneCountInString` + `utf8.RuneError` チェックでアロケーションなしに処理できる**。`[]rune(s)` は文字列全体をヒープにコピーするため、長い文字列では無視できないコストになる。**utils/string.go の2関数、git/worktree.go で観察された。**
|
||||
|
||||
---
|
||||
|
||||
## ストレージ保護設計 — 書き込みの遅延・バッチ化
|
||||
|
||||
> 追記 2026-02-24。microSD上で動作する前提でのFS書き込み最適化。
|
||||
|
||||
### 「誰がこのデータを必要とするか」マップ
|
||||
|
||||
現状の永続化データを**消費者**と**書き込み頻度**で整理すると、書き込みを遅延できる余地が大きく異なる。
|
||||
|
||||
| データ | プロセス内読者 | プロセス外読者 | 書き込み頻度(現状) | 損失許容度 |
|
||||
|--------|--------------|--------------|-----------------|----------|
|
||||
| `sessions/*.json` | AgentLoop (ターン毎 `GetHistory`) | **なし**(起動時ロードのみ) | **メッセージ毎** | 中(会話消失は困るが致命ではない) |
|
||||
| `state/stats.json` | StatusAPI, Mini App(in-process) | CLI `cmd_status` | **LLM呼び出し毎 + ユーザーメッセージ毎** | 低(数件のロスは許容) |
|
||||
| `memory/MEMORY.md` | AgentLoop (ターン毎) | CLI, Mini App, **外部エディタ** | ステップ完了毎・LLM edit_file | 高(プラン状態が失われると復帰不能) |
|
||||
| `memory/YYYYMM/DD.md` | AgentLoop(プランなし時) | 外部エディタ | 日次ノート追記時(低頻度) | 低 |
|
||||
|
||||
### 重要な観察: セッションファイルはプロセス内専用データ
|
||||
|
||||
`sessions/*.json` は**稼働中に外部プロセスが読まない**。唯一の利用タイミングは起動時の `loadSessions()`。つまり書き込みの目的は「クラッシュリカバリ」だけであり、**メッセージ毎の即時書き込みは過剰**。
|
||||
|
||||
同様に `state/stats.json` も、Mini App や CLI はプロセス内の `Tracker.GetStats()` 経由でメモリから読む。ファイルはプロセス再起動時の引き継ぎ専用。
|
||||
|
||||
### 推奨書き込み戦略
|
||||
|
||||
#### sessions/*.json — Write-behind (ダーティフラグ + 定期フラッシュ)
|
||||
|
||||
```
|
||||
AddFullMessage() → in-memory のみ更新、dirty フラグ立て
|
||||
↓
|
||||
定期タイマー (5分) or メッセージ数閾値 (20件)
|
||||
またはシャットダウンフック → Save()
|
||||
```
|
||||
|
||||
- リカバリウィンドウ: 最大5分 or 20メッセージ分
|
||||
- 書き込み回数削減率: 会話速度次第だが **10〜50倍**
|
||||
- 実装: `SessionManager` に `dirtyKeys map[string]bool` + バックグラウンドフラッシャーgoroutine
|
||||
|
||||
#### state/stats.json — 定期フラッシュのみ
|
||||
|
||||
```
|
||||
RecordUsage() / RecordPrompt() → in-memory のみ更新
|
||||
↓
|
||||
定期タイマー (5分) → save()
|
||||
+ シャットダウンフック
|
||||
```
|
||||
|
||||
- 損失リスク: 最大5分分の統計カウント(許容範囲)
|
||||
- 書き込み回数削減率: **LLM呼び出し頻度 × 5分** = 数十〜数百倍
|
||||
|
||||
#### memory/MEMORY.md — ターンスコープキャッシュ (書き込みは即時維持)
|
||||
|
||||
書き込みは現状通り即時。読み取りの問題だけ解決する。
|
||||
|
||||
```
|
||||
エージェントターン開始 → content := ReadLongTerm() を1回だけ
|
||||
↓ content を引数として全ヘルパーに渡す
|
||||
(HasActivePlan(content), GetPlanStatus(content), ...)
|
||||
エージェントターン終了 → content キャッシュ破棄
|
||||
```
|
||||
|
||||
- 外部エディタとの整合: ターン境界でリフレッシュされるので1ターン以内の外部編集のみ見逃す(許容範囲)
|
||||
- LLM の edit_file 経由の書き込み: ファイルシステムに即座に書かれるため次ターンで自動反映
|
||||
- 読み取り回数削減: 1ターンあたり `5回以上 → 1回`
|
||||
|
||||
### microSD 寿命への影響試算
|
||||
|
||||
一般的な会話セッション(1時間、60メッセージ、10 LLM呼び出し/分)の場合:
|
||||
|
||||
| データ | 現状の書き込み回数/時 | 改善後 | 削減率 |
|
||||
|--------|-------------------|--------|-------|
|
||||
| sessions/*.json | ~60回 (メッセージ毎) | ~12回 (5分毎) | **80%減** |
|
||||
| stats.json | ~660回 (LLM呼+prompt毎) | ~12回 (5分毎) | **98%減** |
|
||||
| MEMORY.md | ステップ数分(変わらず) | 同左 | — |
|
||||
| **合計** | **720+ 回/時** | **~24回/時** | **97%減** |
|
||||
|
||||
### 実装上の注意点
|
||||
|
||||
- **シャットダウンフック必須**: `SIGTERM` / `SIGINT` で dirty なデータを強制フラッシュ。フラッシュ失敗時はログに記録。
|
||||
- **クラッシュ後のリカバリ**: dirty データが失われた場合、セッション履歴は最後のチェックポイント以降が消える。ユーザーへの通知が必要か検討。
|
||||
- **フラッシュ中の競合**: フラッシュgoroutineと `Save()` の同時呼び出しを防ぐため、既存の mutex を流用。
|
||||
- **MEMORY.md のターンキャッシュ**: `edit_file` ツールが MEMORY.md を書き込んだ場合、**同ターン内のキャッシュを無効化**する仕組みが必要(`MemoryStore.InvalidateCache()` を edit_file のコールバックから呼ぶなど)。そうしないと同ターン内の後続の `GetPlanStatus()` などが古いキャッシュを読む。
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue