yoi/tickets/worker-auto-lock.md
2026-04-11 17:30:32 +09:00

2.6 KiB

Worker: run() 時の自動キャッシュロックと ToolDefinition ファクトリ遅延初期化

背景

現状の Worker は Type-state パターンで Mutable / CacheLocked の2状態を持つが、 lock() を呼ばなくても run() できてしまうため、キャッシュ保護の存在を知らないユーザーは 常に非最適パスを通ることになる。

また register_tool() 時に ToolDefinition のファクトリクロージャが即時呼び出しされており、 本来の意図である遅延初期化になっていない。

方針

run() 時の自動ロック

run() の冒頭で、Mutable 状態なら自動的に CacheLocked へ遷移する。 これにより lock を知らないユーザーでも嫌でもキャッシュ保護される。

ターンの合間に history や system prompt を編集したい場合は、明示的に unlock() を挟む。 次の run() で再び自動ロックされる。

let mut worker = Worker::new(client);
worker.set_system_prompt("...");
worker.register_tool(my_tool)?;

// Mutable のまま run() → 自動で lock される
worker.run("Hello").await?;

// ターン間で内容を弄りたい場合
worker.unlock();
worker.history_mut().truncate(5);
// 次の run() で再 lock
worker.run("Continue").await?;

設計ポイント

  • run()&mut self を取る以上、内部で状態遷移しても外部の型は変わらない。 実装は内部フラグ(is_locked: bool)で管理し、Mutable / CacheLocked の type-state はそのまま維持する。Worker<C, Mutable>run() が内部で lock 相当の 処理を行い、unlock() が呼ばれるまでキャッシュ破壊的な操作を(ランタイムで)ブロックする
  • Worker<C, CacheLocked> は従来どおり。明示的に lock() してから使うパスも残る
  • interceptor, max_turns, callbacks 等キャッシュに影響しない設定は lock 状態でも自由に変更可能

ToolDefinition ファクトリの遅延初期化

register_tool() は定義を蓄積するだけにし、ファクトリの実行を初回 run() まで遅延させる。

  • register_tool()ToolDefinition を Vec に push するだけ
  • 初回 run() の自動 lock 時にファクトリを一括実行し、ToolServer を構築
  • unlock() 後に追加登録された tool は次の run() で初期化

移行

  • lock() / unlock() は引き続き使える(明示的なキャッシュ管理用)
  • Worker::new()run() のパスが自動保護されるため、既存コードは変更不要