yoi/crates/llm-worker/docs/spec/basis.md
2026-04-04 04:27:46 +09:00

144 lines
8.6 KiB
Markdown

# llm-worker
LLMを用いた自律的なワーカーを構築するためのライブラリ。
ツールの定義・呼び出し、コンテキスト管理、ストリーミングイベント処理、フック制御などを責務にもつ。
ワークスペース `insomnia` に属し、以下の3クレートで構成される:
| クレート | 概要 |
|---|---|
| `insomnia` | アプリケーション本体 |
| `llm-worker` | LLMワーカーライブラリ (本クレート) |
| `llm-worker-macros` | `#[tool_registry]` / `#[tool]` 手続きマクロ |
## 用語定義
- **Item**:
会話の基本単位。Open Responses仕様に準じた列挙型で、`Message`・`FunctionCall`・`FunctionCallOutput`・`Reasoning`の4バリアントを持つ。従来のメッセージベースAPIと異なり、ツール呼び出しや思考をファーストクラスのItemとして扱う。
- **ContentPart**:
Message Item内のコンテンツ要素。`InputText`(ユーザー入力)・`OutputText`(アシスタント出力)・`Refusal`(拒否)を区別する。
- **ブロック (Block)**:
モデルが「開始→デルタ→終了」で囲む一塊の出力。OpenAIの`response.output_item`やAnthropicの`content_block`、Geminiの`candidates[].content.parts`などが該当する。`BlockType`として`Text`・`Thinking`・`ToolUse`・`ToolResult`が定義されている。
- **メタイベント (Meta Event)**:
`Ping`・`Usage`・`Status`・`Error`など、ブロックに属さない補助イベント群。ステータス更新やハートビートとして処理される。
- **Event**:
LLMからのストリーミングレスポンスを表現する統一型。メタイベントとブロックイベント(`BlockStart`・`BlockDelta`・`BlockStop`・`BlockAbort`)から成る。`llm_client`層と`event`モジュールの双方に定義が存在する。
- **Worker**:
LLMとのインタラクションを管理する中心コンポーネント。ツール実行ループ、フック呼び出し、ストリーミングイベントのディスパッチを統括する。
以降の仕様では「ブロック」を上記の生成・ツール単位の総称として扱う。
## アーキテクチャ
モジュール構成概念図:
```plaintext
llm-worker
├── worker # Workerコア (実行ループ、状態管理)
├── state # Type-stateパターン (Mutable / CacheLocked)
├── tool # Tool trait, ToolMeta, ToolDefinition
├── tool_server # ToolServer (ツールのレジストリと実行)
├── hook # Hook trait, HookRegistry (10種のフックポイント)
├── handler # Kind/Handler trait (イベントディスパッチ)
├── subscriber # WorkerSubscriber trait (UI向けイベント購読)
├── event # Worker層の公開Event型
├── message # Item/ContentPart/Roleのre-export
├── timeline # イベントストリームの状態管理とHandlerディスパッチ
│ ├── event # Timeline内部イベント型
│ ├── timeline # Timelineコア
│ ├── text_block_collector # テキスト収集Handler
│ └── tool_call_collector # ツール呼び出し収集Handler
└── llm_client # LLMクライアント層
├── client # LlmClient trait
├── event # llm_client層のEvent型
├── error # ClientError
├── types # Item, ContentPart, Role, Request, RequestConfig, ToolDefinition
├── scheme # APIスキーマ変換
│ ├── openai # OpenAI Chat Completions API
│ ├── anthropic # Anthropic Messages API
│ └── gemini # Google Gemini API
└── providers # プロバイダ固有クライアント
├── openai # OpenAI
├── anthropic # Anthropic
├── gemini # Google Gemini
└── ollama # Ollama (ローカルLLM)
```
OpenAI互換のプロバイダでスキーマを使い回せるよう、`scheme`と`providers`モジュールは分離されている。
### scheme層
単純な変換を責務とするスキーマを定義する。
- リクエスト変換: `Request`(SystemPrompt + Items + Tools + RequestConfig) → プロバイダ固有のリクエストJSON
- レスポンス変換: SSEイベント → 型付き`Event`構造体のストリーム
各APIスキーマ(OpenAI / Anthropic / Gemini)ごとに実装を持ち、APIスキーマに準じたパース・バリデーションを行う。
### llm_client (providers) 層
`LlmClient` traitを実装する各プロバイダクライアントがリクエストを送信し、差異が吸収され統一された`Event`ストリームを出力する。
ストリーミング中のバッファリング(ToolArguments の累積等)もこの層で行う。
`ConfigWarning`により、プロバイダがサポートしない設定オプションを警告として通知する仕組みを持つ。
### timeline層
`llm_client`からのイベントストリームを受信し、登録された`Handler`にディスパッチする。
`Kind` traitがイベント型を定義し、`Handler<K: Kind>` traitが`Scope`(ブロックのライフサイクルに対応する状態)を持つイベント処理を行う。組み込みHandlerとして`TextBlockCollector`と`ToolCallCollector`が提供される。
### worker層
`Worker`はType-stateパターンにより`Mutable`状態と`CacheLocked`状態を持つ。
- **Mutable**: システムプロンプトの設定変更、メッセージ履歴の編集、ツール・フックの登録が可能
- **CacheLocked**: `Worker::lock()`で遷移。LLM APIのKVキャッシュヒット率を最大化するため、既存履歴の変更を制限する
`WorkerSubscriber` traitにより、テキスト生成やツール呼び出しのストリーミングイベントをUI等に配信できる。
## Tools
各種プロバイダのLLMのAPI仕様として存在するTool CallやFunction Callingを`Tool` traitとして統一的に扱う。
- **`ToolMeta`**: ツール名・説明・入力スキーマ(JSON Schema)を保持する不変のメタ情報。Worker登録後は変更されない。
- **`ToolDefinition`**: `Fn() -> (ToolMeta, Arc<dyn Tool>)`型のファクトリ。Worker登録時に一度呼び出され、メタ情報とインスタンスがセッションスコープでキャッシュされる。
- **`Tool` trait**: `async fn call(&self, arguments: Value) -> Result<String, ToolError>`を持つ非同期trait。セッション中に状態を保持できる。
- **`ToolServer`**: 登録済みツールのレジストリと実行を管理するインメモリサーバー。
`llm-worker-macros`クレートが提供する`#[tool_registry]` / `#[tool]`手続きマクロにより、`impl`ブロック上のメソッドから`Tool` trait実装を自動生成する。
## Hooks
Claude Codeに存在するようなHooksと同様のターン制御・介入機構を搭載する。
以下の10種のフックポイントが定義されている:
| フックポイント | タイミング | 戻り値による制御 |
|---|---|---|
| `OnPromptSubmit` | プロンプト送信時 | Continue / Cancel |
| `PreLlmRequest` | LLMリクエスト直前 | Continue / Cancel |
| `PreToolCall` | ツール実行直前 | Continue / Skip / Abort / Pause |
| `PostToolCall` | ツール実行直後 | Continue / Abort |
| `OnTurnEnd` | ターン終了時 | Finish / ContinueWithMessages / Paused |
| `OnAbort` | 中断時 | (通知のみ) |
| `OnTextDelta` | テキストデルタ受信時 | (ストリーミング) |
| `OnToolCallDelta` | ツール引数デルタ受信時 | (ストリーミング) |
| `OnStreamChunk` | ストリームチャンク受信時 | (ストリーミング) |
| `OnStreamComplete` | ストリーム完了時 | (ストリーミング) |
フックは`Hook` traitとして表現され、`HookRegistry`で管理される。
## ストリーミングイベント処理
イベントストリームを扱う設計目標として、Text / InputJSON / Thinkingのデルタと、[細粒度ツールストリーミング](https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming)を含む、デルタの低遅延・リアルタイム処理がある。
ブロックのライフサイクル: `BlockStart``BlockDelta`(複数回) → `BlockStop` / `BlockAbort`
`DeltaContent`の種別:
- `Text(String)` - テキスト生成のデルタ
- `Thinking(String)` - 思考(Extended Thinking等)のデルタ
- `InputJson(String)` - ツール引数JSONの部分文字列デルタ