Archive summarized messages instead of deleting them

要約で長期メモリに移したメッセージをDBから物理削除していたため、
一度要約されると人間も内容を確認できなくなっていた。削除をやめ、
messages に archived_at(要約に取り込まれた時刻)と memory_id
(どの要約に取り込まれたか)を持たせる。

モデルからは従来どおり見えない:
- get_context_messages() がアーカイブ済みを除外する
- maybe_summarize() の閾値判定も現役メッセージだけを数える
  (ここを除外し忘れると件数が減らず再要約が繰り返される)
- backfill_tags() もアーカイブ済みは対象外にする

人間からは確認できる:
- GET /chat/sessions/{id}/messages がアーカイブ済みも archived_at
  付きで返す。export/import もアーカイブ状態を保持する
- チャット画面では半透明で表示し、最後のアーカイブ済みメッセージの
  直後に「ここから先がモデルに渡る」区切り線を出す

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 14:33:18 +09:00
co-authored by Claude Opus 5
parent be3af44891
commit 2ee7238afb
12 changed files with 101 additions and 19 deletions
+5 -3
View File
@@ -48,13 +48,15 @@ SSE でストリーミング応答を返す。
---
### GET /chat/sessions/{session_id}/messages
セッションのメッセージ一覧を `created_at` 昇順で返す。
セッションのメッセージ一覧を `created_at` 昇順で返す。要約に取り込まれた(アーカイブ済みの)
メッセージも含めて返し、`archived_at` で区別する。アーカイブ済みのものは LLM に渡す
コンテキストには入らないが、履歴としては残っている。
**レスポンス**
```json
[
{ "id": 1, "role": "user", "content": "...", "created_at": "..." },
{ "id": 2, "role": "assistant", "content": "...", "created_at": "..." }
{ "id": 1, "role": "user", "content": "...", "created_at": "...", "archived_at": "2026-08-20T04:00:00+00:00", "tags": [] },
{ "id": 2, "role": "assistant", "content": "...", "created_at": "...", "archived_at": null, "tags": [] }
]
```
+1 -1
View File
@@ -91,7 +91,7 @@ LLM に渡すコンテキストは `memory_kb/memory.py` の `get_context_messag
1. メッセージ数が `MEMORY_SUMMARIZE_THRESHOLD`(既定 20)以上になったら発火
2. 古い側の半分(既定 10 件)を LLM で要約し `memories` に 1 行保存
3. **要約した元メッセージは DB から物理削除**する(復元不可。エクスポートしても生ログは残らない)
3. **要約した元メッセージは `archived_at` / `memory_id` を立ててアーカイブ**する(物理削除はしない)。LLM に渡すコンテキストからも、次回以降の要約対象からも外れるが、DB には残るため履歴表示・エクスポートからは読める
4. 保存した要約に `tagging.tag_memory()` でタグを付ける
#### ツール経由の遅延読み込み
+3 -3
View File
@@ -58,7 +58,7 @@ Discord 連携は [discord-bot.md](discord-bot.md)、ノートの操作方法は
- `GET /chat/sessions/{id}/export` がセッションのメタ情報と全メッセージ(タグ込み)を JSON で返す。
- `POST /chat/sessions/import` は同じ形式の JSON から**新しいセッションを作成**する(上書きではない)。
サイドバーの ⭱ ボタンからファイルを選ぶだけで取り込める。
- 注意: 要約済みの会話は元メッセージが物理削除されているため、エクスポートしても生ログは戻らない。
- 要約済み(アーカイブ済み)のメッセージも `archived_at` 付きでそのまま書き出され、取り込み時もアーカイブ状態を保つ。
## 3. コンテキスト管理(5 層)
@@ -79,7 +79,7 @@ Discord 連携は [discord-bot.md](discord-bot.md)、ノートの操作方法は
- メッセージ数が `MEMORY_SUMMARIZE_THRESHOLD`(既定 20)を超えると、古い側の半分を LLM が要約して
`memories` に 1 行として保存する。
- **要約した元メッセージは DB から物理削除される**(復元不可)。
- **要約した元メッセージはアーカイブされる**(物理削除はしない)。LLM に渡るコンテキストからは外れるが、チャット画面では薄く表示され、区切り線から先だけがモデルに渡ることが分かるようになっている。
- 保存した要約にもタグが付き、Discord Bot の話題選びの材料になる。
- 溜まった要約は `GET /memory/{session_id}` で確認、`DELETE /memory/{id}` で個別削除できる(API のみ。専用の画面は無い)。
@@ -182,7 +182,7 @@ Discord 連携は [discord-bot.md](discord-bot.md)、ノートの操作方法は
- **トークン数ベースの制御が一切ない**。すべて件数・文字数ベースなので、層 3(Markdown 全文 ×3)は
会話が短くてもコンテキストを大きく押し上げる(層 1 は見出しのみなので軽い)。
- **長期メモリに上限がない**。長いセッションでは要約が積み上がり続ける(要約の再要約はしない)。
- **要約は不可逆**。要約対象になった元メッセージは DB から消える。
- **要約自体は不可逆**。アーカイブされた元メッセージは残るが、要約をやり直したり元の状態に戻す手段は無い。
- **ワードプールは揮発性**。再起動でリセットされ、直近 200 件から作り直される。
- **DB マイグレーションは簡易実装**。Alembic は依存に入っているが使っておらず、起動時に
`PRAGMA table_info` を見て不足カラムを `ALTER TABLE` で足すだけ(SQLite 前提)。