yoi/docs/persistence.md

3.8 KiB
Raw Blame History

永続化設計

概要

llm-worker-persistence クレートは、llm-workerWorker セッション状態を JSONL append-only ログとして永続化する。ログを読み込んで集約することで Worker 状態を復元する。

設計方針

  • JSONL append-only ログ: 1セッション = 1つの .jsonl ファイル。書き込みは末尾追記のみ。
  • Pause/正常終了で構造に差異なし: Worker の状態は Pause 時も正常終了時も同じ形 (history: Vec<Item> + turn_count + request_config)。 resume() は「ユーザー入力を追加せず run_turn_loop() に再入する」だけなので、 復元に必要なのは history の中身であり、前回の終了理由ではない。 RunOutcomeFinished/Paused 区分は監査用メタデータであり、状態復元の分岐には使わない。
  • クレート分離: llm-worker は永続化を知らない。Session ラッパーが外から Worker を包む。

命名規約

名前 用途
SessionLog / LogEntry 状態復元用の構造化された記録(永続化の本体)
EventTrace / TraceEntry デバッグ用の生ストリームイベント全録(オプション、デフォルト OFF

クレート構成

llm-worker-persistence → llm-worker → llm-worker-macros

llm-worker-persistencellm-worker に依存するが、逆方向の依存はない。

ファイル配置

{root}/{session_id}.jsonl          -- セッションログ
{root}/{session_id}.trace.jsonl    -- イベントトレース(デバッグ時のみ)

SessionId は UUID v7uuid クレート)。タイムスタンプ埋め込みで辞書順 = 時系列順。

LogEntry

各エントリは Worker の特定の状態変更に対応する:

エントリ Worker 上の対応箇所 collect_state での効果
SessionStart セッション開始 / fork system_prompt, config, history を初期化
UserInput worker.rs:229 history に追加
AssistantItems worker.rs:1040-1041 history に追加
ToolResults worker.rs:897-900, 1072-1076 history に追加
HookInjectedItems worker.rs:1055 history に追加
TurnEnd worker.rs:1033 turn_count を更新
CacheLocked Worker::lock() locked_prefix_len を設定
CacheUnlocked Worker::unlock() locked_prefix_len を 0 に
RunOutcome run() / resume() 終了時 interrupted フラグのみ(監査用)
ConfigChanged set_* メソッド群 config を更新

Session ラッパー

pub struct Session<C: LlmClient, St: Store> {
    pub worker: Worker<C, Mutable>,
    store: St,
    session_id: SessionId,
}
  • Session::new() — SessionStart を書き込み
  • Session::run() — Worker::run() の前後で history を比較、差分をログ記録
  • Session::resume() — 同上
  • Session::restore() — ログを読み込み、状態を集約して Worker を再構築
  • Session::fork() — 現在の history をシードにした新セッションを作成
  • Session::fork_at() — 任意のログ地点から分岐

Store trait

pub trait Store: Send + Sync {
    fn append(&self, id: SessionId, entry: &LogEntry) -> impl Future<...> + Send;
    fn read_all(&self, id: SessionId) -> impl Future<...> + Send;
    fn list_sessions(&self) -> impl Future<...> + Send;
    fn create_session(&self, id: SessionId, entries: &[LogEntry]) -> impl Future<...> + Send;
    fn exists(&self, id: SessionId) -> impl Future<...> + Send;
    fn append_trace(&self, id: SessionId, entry: &TraceEntry) -> impl Future<...> + Send;
}

初期実装は FsStore(ファイルシステム JSONL。RPITIT 使用、async_trait 不要。