add: docs
This commit is contained in:
parent
9b608f1a54
commit
d03172610d
55
CLAUDE.md
Normal file
55
CLAUDE.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code when working with code in this repository.
|
||||
|
||||
## Development Commands
|
||||
|
||||
- **Build**: `cargo build --workspace` or `cargo check --workspace` for quick validation
|
||||
- **Tests**: `cargo test --workspace` (note: tests currently fail due to unsafe environment variable operations)
|
||||
- **Fix warnings**: `cargo fix --lib -p worker`
|
||||
- **Dev environment**: Uses Nix flake - run `nix develop` to enter dev shell with Rust toolchain
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
This is a Rust workspace implementing a multi-LLM worker system with tool calling capabilities. The architecture follows the Core Crate Pattern to avoid circular dependencies:
|
||||
|
||||
### Workspace Structure
|
||||
- **`worker-types`**: Core type definitions (Tool trait, Message, StreamEvent, hook types) - foundational types used by all other crates
|
||||
- **`worker-macros`**: Procedural macros (`#[tool]`, `#[hook]`) for automatic Tool and WorkerHook trait implementations
|
||||
- **`worker`**: Main library containing Worker struct, LLM clients, prompt composer, and session management
|
||||
|
||||
### Key Components
|
||||
|
||||
**Worker**: Central orchestrator that manages LLM interactions, tool execution, and session state. Supports streaming responses via async streams.
|
||||
|
||||
**LLM Providers**: Modular clients in `worker/src/llm/` for:
|
||||
- Anthropic (Claude)
|
||||
- Google (Gemini)
|
||||
- OpenAI (GPT)
|
||||
- xAI (Grok)
|
||||
- Ollama (local models)
|
||||
|
||||
**Tool System**: Uses `#[tool]` macro to convert async functions into LLM-callable tools. Tools must return `ToolResult<T>` and take a single struct argument implementing `Deserialize + Serialize + JsonSchema`.
|
||||
|
||||
**Hook System**: Uses `#[hook]` macro to create lifecycle event handlers. Hook functions take `HookContext` and return `(HookContext, HookResult)` tuple, supporting events like OnMessageSend, PreToolUse, PostToolUse, and OnTurnCompleted with regex matcher support.
|
||||
|
||||
**Prompt Management**: Handlebars-based templating system for dynamic prompt generation based on roles and context.
|
||||
|
||||
**MCP Integration**: Model Context Protocol support for external tool server communication.
|
||||
|
||||
## Development Notes
|
||||
|
||||
- Edition 2024 Rust with async/await throughout
|
||||
- Uses `tokio` for async runtime and `reqwest` for HTTP clients
|
||||
- Environment-based configuration for API keys and base URLs
|
||||
- Session persistence using JSON serialization
|
||||
- Streaming responses via `futures-util` streams
|
||||
- Current test suite has unsafe environment variable operations that need `unsafe` blocks to compile
|
||||
|
||||
## Important Patterns
|
||||
|
||||
- **Error Handling**: `ToolResult<T>` for tools, `WorkerError` for library operations with automatic conversions
|
||||
- **Streaming**: All LLM interactions return `StreamEvent` streams for real-time UI updates
|
||||
- **Tool Registration**: Dynamic tool registration at runtime using boxed trait objects
|
||||
- **Hook Registration**: Dynamic hook registration with lifecycle event filtering and regex matching
|
||||
- **Core Crate Pattern**: `worker-macros` references `worker-types` directly via complete paths to prevent circular dependencies
|
||||
50
docs/architecture.md
Normal file
50
docs/architecture.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
## 各クレートの役割
|
||||
|
||||
- **`worker-types`**: 全ての基本型定義(Tool, Message, StreamEvent, フック関連型等)
|
||||
- **`worker-macros`**: プロシージャルマクロ(#[tool])による自動実装
|
||||
- **`worker`**: メインライブラリ(Worker, LLMクライアント, プロンプトコンポーザー等)
|
||||
- **`tools`**: 具体的なツール実装(read, write, bash, mcp等)
|
||||
|
||||
`worker`クレートは`worker-types`の全型を再エクスポートし、外部からは`worker::types::`でアクセス可能です。
|
||||
|
||||
## エラーハンドリング戦略
|
||||
|
||||
- **`worker-types`**: `ToolResult<T>` - 汎用的なツールエラー(`Result<T, Box<dyn std::error::Error + Send + Sync>>`)
|
||||
- **`worker`**: `WorkerError` - ライブラリ固有エラー(設定、ネットワーク、ツール実行、JSON解析等)
|
||||
|
||||
自動的な型変換(`From` trait実装)により、一貫したエラーハンドリングを実現しています。認証エラーの自動検出機能も含まれています。
|
||||
|
||||
## マクロ実装
|
||||
|
||||
`worker-macros`は完全修飾パスで型を参照し、Core Crate Patternに準拠:
|
||||
|
||||
```rust
|
||||
// #[tool]マクロが生成するコード例
|
||||
impl ::worker_types::Tool for ReadFileTool {
|
||||
fn name(&self) -> &str { "read_file" }
|
||||
fn description(&self) -> &str { "ファイルを読み込みます" }
|
||||
fn parameters_schema(&self) -> ::worker_types::serde_json::Value { /* スキーマ */ }
|
||||
|
||||
#[::worker_types::async_trait::async_trait]
|
||||
async fn execute(&self, args: ::worker_types::serde_json::Value)
|
||||
-> ::worker_types::ToolResult<::worker_types::serde_json::Value> {
|
||||
// 元の関数呼び出し
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 利用ガイドライン
|
||||
|
||||
- **新しいツール**: `#[tool]`マクロでasync関数を装飾し、`worker::types::ToolResult`を返り値型に使用
|
||||
- **新しいLLMプロバイダ**: `worker/src/llm/`にクライアント実装を追加し、`LlmClient` enumに追加
|
||||
- **新しいフック**: `WorkerHook` traitを実装し、`HookManager`で管理
|
||||
- **型の変更**: `worker-types`で基本型を変更し、コンパイルエラーで影響範囲を確認
|
||||
- **MCP統合**: `mcp_tool`モジュールを使用してModel Context Protocolサーバーとの連携を実装
|
||||
|
||||
## 主要な設計パターン
|
||||
|
||||
- **委譲パターン**: `LlmClient` enumでの各プロバイダクライアントへの委譲
|
||||
- **ストリーミング**: async streamを使用したリアルタイムイベント処理
|
||||
- **プロンプトテンプレート**: Handlebarsベースの動的プロンプト生成
|
||||
- **セッション管理**: メッセージ履歴の永続化と復元
|
||||
- **並列処理**: MCPサーバーの並列初期化による高速化
|
||||
718
docs/hooks.md
Normal file
718
docs/hooks.md
Normal file
|
|
@ -0,0 +1,718 @@
|
|||
# Worker Hook システム
|
||||
|
||||
Workerのフックシステムは、LLMとの対話プロセスの各段階でカスタムロジックを実行し、システムの動作を柔軟に制御できる強力な機能です。
|
||||
|
||||
## 概要
|
||||
|
||||
Hooksシステムは、WorkerがLLMとの対話で実行する各フェーズ(メッセージ処理、ツール実行、ターン完了)でユーザー定義の処理を挿入できる機能です。これにより、以下のような高度なカスタマイズが可能になります:
|
||||
|
||||
- **メッセージの前処理**: ユーザーメッセージにコンテキスト情報を追加
|
||||
- **ツール実行後の処理**: ファイル操作後の自動コミットやフォーマット
|
||||
- **ターン完了時の処理**: 統計記録や追加情報の提供
|
||||
- **ストリーミング制御**: リアルタイム応答の変更・拡張
|
||||
|
||||
## アーキテクチャ
|
||||
|
||||
### コア型
|
||||
|
||||
```rust
|
||||
// worker-types/src/lib.rs
|
||||
pub trait WorkerHook: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
fn hook_type(&self) -> &str;
|
||||
fn matcher(&self) -> &str;
|
||||
async fn execute(&self, context: HookContext) -> (HookContext, HookResult);
|
||||
}
|
||||
|
||||
pub struct HookManager {
|
||||
hooks: HashMap<HookEvent, Vec<Box<dyn WorkerHook>>>,
|
||||
}
|
||||
|
||||
pub enum HookEvent {
|
||||
OnMessageSend,
|
||||
PreToolUse,
|
||||
PostToolUse,
|
||||
OnTurnCompleted,
|
||||
}
|
||||
```
|
||||
|
||||
### Hook実行フロー
|
||||
|
||||
```
|
||||
1. ユーザーメッセージ受信
|
||||
2. OnMessageSend hooks 実行
|
||||
3. プロンプト構築・LLMへ送信
|
||||
4. ストリーミング応答開始
|
||||
5. ツール呼び出し検出
|
||||
├── PreToolUse hooks 実行
|
||||
├── ツール実行
|
||||
└── PostToolUse hooks 実行
|
||||
6. 応答完了
|
||||
7. OnTurnCompleted hooks 実行
|
||||
8. 結果返却
|
||||
```
|
||||
|
||||
## Hook の種類
|
||||
|
||||
### フェーズ別Hook
|
||||
|
||||
| フェーズ | タイプ名 | 実行タイミング | ストリーミング |
|
||||
|---------|----------|---------------|---------------|
|
||||
| メッセージ送信時 | `OnMessageSend` | ユーザーメッセージをLLMに送信する直前 | × |
|
||||
| ツール実行前 | `PreToolUse` | 特定のツール実行直前 | ○ |
|
||||
| ツール実行後 | `PostToolUse` | 特定のツール実行完了後 | ○ |
|
||||
| ターン完了時 | `OnTurnCompleted` | AIの応答が完了した時点 | ○ |
|
||||
|
||||
### マッチャーパターン
|
||||
|
||||
`PreToolUse`、`PostToolUse`および`OnTurnCompleted`では、`matcher`パラメータで特定のツールに対してのみHookを実行できます:
|
||||
|
||||
```rust
|
||||
// Edit または Create ツールの実行後のみ実行
|
||||
#[hook(hook_type = "PostToolUse", matcher = "Edit|Create")]
|
||||
|
||||
// すべてのツールで実行(matcher省略時)
|
||||
#[hook(hook_type = "OnTurnCompleted")]
|
||||
|
||||
// 正規表現マッチング
|
||||
#[hook(hook_type = "PostToolUse", matcher = r"^(Read|Write)File.*")]
|
||||
```
|
||||
|
||||
## Hook の作成方法
|
||||
|
||||
### 基本構文
|
||||
|
||||
```rust
|
||||
use worker::types::{HookContext, HookResult, Role};
|
||||
use worker_macros::hook;
|
||||
|
||||
#[hook(hook_type = "フェーズ名", matcher = "パターン")]
|
||||
pub async fn your_hook_name(mut context: HookContext) -> HookResult {
|
||||
// カスタムロジックをここに実装
|
||||
HookResult::Continue
|
||||
}
|
||||
```
|
||||
|
||||
### マクロを使わない実装
|
||||
|
||||
```rust
|
||||
use worker::types::{WorkerHook, HookContext, HookResult};
|
||||
use async_trait::async_trait;
|
||||
|
||||
pub struct CustomHook {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl CustomHook {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WorkerHook for CustomHook {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn hook_type(&self) -> &str {
|
||||
"OnMessageSend"
|
||||
}
|
||||
|
||||
fn matcher(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
async fn execute(&self, mut context: HookContext) -> (HookContext, HookResult) {
|
||||
// Hook処理をここに実装
|
||||
(context, HookResult::Continue)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## HookContext API
|
||||
|
||||
`HookContext`は、Hook内で使用できる情報とメソッドを提供します:
|
||||
|
||||
### 利用可能なデータ
|
||||
|
||||
```rust
|
||||
pub struct HookContext {
|
||||
pub content: String, // 処理対象のコンテンツ
|
||||
pub workspace_path: String, // 現在のワークスペースパス
|
||||
pub message_history: Vec<Message>, // これまでの会話履歴
|
||||
pub tools: Vec<DynamicToolDefinition>, // 利用可能なツール一覧
|
||||
pub variables: HashMap<String, String>, // Hook間で共有可能な変数
|
||||
pub tool_name: Option<String>, // 実行中のツール名(ツール関連フックのみ)
|
||||
pub tool_args: Option<serde_json::Value>, // ツール実行引数(ツール関連フックのみ)
|
||||
pub tool_result: Option<String>, // ツール実行結果(PostToolUseのみ)
|
||||
}
|
||||
```
|
||||
|
||||
### 操作メソッド
|
||||
|
||||
```rust
|
||||
impl HookContext {
|
||||
// ワークスペースでコマンドを実行
|
||||
pub async fn run_command(&self, command: &str) -> Result<String, Box<dyn std::error::Error>>;
|
||||
|
||||
// ツールを実行(将来実装予定)
|
||||
pub async fn run_tool(&self, tool_name: &str, args: serde_json::Value) -> Result<String, Box<dyn std::error::Error>>;
|
||||
|
||||
// メッセージを履歴に追加
|
||||
pub fn add_message(&mut self, content: String, role: Role);
|
||||
|
||||
// コンテンツを書き換え
|
||||
pub fn set_content(&mut self, content: String);
|
||||
|
||||
// 変数の設定・取得
|
||||
pub fn set_variable(&mut self, key: String, value: String);
|
||||
pub fn get_variable(&self, key: &str) -> Option<&String>;
|
||||
|
||||
// ツール情報の取得
|
||||
pub fn get_tool(&self, tool_name: &str) -> Option<&DynamicToolDefinition>;
|
||||
pub fn list_tool_names(&self) -> Vec<&String>;
|
||||
}
|
||||
```
|
||||
|
||||
### ストリーミング用メソッド
|
||||
|
||||
```rust
|
||||
impl HookContext {
|
||||
// ストリーミング中にメッセージを送信
|
||||
pub fn stream_message(&self, content: String, role: Role);
|
||||
|
||||
// ストリーミング中にシステム通知を送信
|
||||
pub fn stream_system_message(&self, content: String);
|
||||
|
||||
// ストリーミング中にデバッグ情報を送信
|
||||
pub fn stream_debug(&self, title: String, data: serde_json::Value);
|
||||
}
|
||||
```
|
||||
|
||||
## HookResult の種類
|
||||
|
||||
Hook関数は以下のいずれかの結果を返す必要があります:
|
||||
|
||||
```rust
|
||||
pub enum HookResult {
|
||||
// 処理を続行
|
||||
Continue,
|
||||
|
||||
// コンテンツを変更して続行
|
||||
ModifyContent(String),
|
||||
|
||||
// システムメッセージを追加して続行
|
||||
AddMessage(String, Role),
|
||||
|
||||
// 複数のメッセージを追加して続行
|
||||
AddMessages(Vec<Message>),
|
||||
|
||||
// ターンを強制完了
|
||||
Complete,
|
||||
|
||||
// エラーでターンを終了
|
||||
Error(String),
|
||||
|
||||
// Hook処理をスキップ(デバッグ用)
|
||||
Skip,
|
||||
}
|
||||
```
|
||||
|
||||
## 実用例
|
||||
|
||||
### 1. タイムスタンプ付きメッセージ
|
||||
|
||||
```rust
|
||||
/// メッセージ送信時に現在時刻を追加するHook
|
||||
#[hook(hook_type = "OnMessageSend")]
|
||||
pub async fn add_timestamp_hook(mut context: HookContext) -> HookResult {
|
||||
let timestamp = chrono::Local::now().format("%H:%M:%S").to_string();
|
||||
let enhanced_content = format!("[{}] {}", timestamp, context.content);
|
||||
HookResult::ModifyContent(enhanced_content)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. ファイル操作後の自動Git追跡
|
||||
|
||||
```rust
|
||||
/// ファイル編集後にGitステータスを確認するHook
|
||||
#[hook(hook_type = "PostToolUse", matcher = "Edit|Create|Write")]
|
||||
pub async fn file_change_notification_hook(mut context: HookContext) -> HookResult {
|
||||
match context.run_command("git status --porcelain").await {
|
||||
Ok(output) => {
|
||||
if !output.trim().is_empty() {
|
||||
context.add_message(
|
||||
format!("📝 ファイル変更を検出:\n{}", output),
|
||||
Role::System,
|
||||
);
|
||||
HookResult::Continue
|
||||
} else {
|
||||
HookResult::Continue
|
||||
}
|
||||
}
|
||||
Err(_) => HookResult::Continue,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 長い応答への注意喚起
|
||||
|
||||
```rust
|
||||
/// 長い応答が生成された際に注意を促すHook
|
||||
#[hook(hook_type = "OnTurnCompleted")]
|
||||
pub async fn long_response_warning_hook(context: HookContext) -> HookResult {
|
||||
if context.content.len() > 2000 {
|
||||
HookResult::AddMessage(
|
||||
"⚠️ 長い応答が生成されました。スクロールして全体を確認してください。".to_string(),
|
||||
Role::System,
|
||||
)
|
||||
} else {
|
||||
HookResult::Continue
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. ツール実行前のバリデーション
|
||||
|
||||
```rust
|
||||
/// 危険なコマンドの実行前に確認を行うHook
|
||||
#[hook(hook_type = "PreToolUse", matcher = "Execute|Shell")]
|
||||
pub async fn dangerous_command_hook(context: HookContext) -> HookResult {
|
||||
if let Some(args) = &context.tool_args {
|
||||
if let Some(command) = args.get("command").and_then(|v| v.as_str()) {
|
||||
let dangerous_commands = ["rm -rf", "format", "dd if="];
|
||||
|
||||
for dangerous in &dangerous_commands {
|
||||
if command.contains(dangerous) {
|
||||
return HookResult::Error(format!(
|
||||
"危険なコマンド「{}」の実行が阻止されました: {}",
|
||||
dangerous, command
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HookResult::Continue
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 自動読み取りHook (TUI用)
|
||||
|
||||
```rust
|
||||
/// ファイル作成・編集後に自動的にファイル内容を読み取るHook
|
||||
#[hook(hook_type = "PostToolUse", matcher = "Edit|Create|Write")]
|
||||
pub async fn auto_read_hook(mut context: HookContext) -> HookResult {
|
||||
// ツール実行結果からファイルパスを抽出
|
||||
if let Some(tool_result) = &context.tool_result {
|
||||
if let Ok(result_data) = serde_json::from_str::<serde_json::Value>(tool_result) {
|
||||
if let Some(file_path) = result_data.get("file_path").and_then(|v| v.as_str()) {
|
||||
// ファイル内容を読み取って表示
|
||||
match tokio::fs::read_to_string(file_path).await {
|
||||
Ok(content) => {
|
||||
context.stream_system_message(format!(
|
||||
"📄 **{}** の内容:\n```\n{}\n```",
|
||||
file_path, content
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
context.stream_system_message(format!(
|
||||
"❌ ファイル読み取りエラー ({}): {}",
|
||||
file_path, e
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HookResult::Continue
|
||||
}
|
||||
```
|
||||
|
||||
## Hook の登録と管理
|
||||
|
||||
### Workerへの登録
|
||||
|
||||
```rust
|
||||
use worker::Worker;
|
||||
|
||||
// 単一のHookを登録
|
||||
worker.register_hook(Box::new(YourHook::new()));
|
||||
|
||||
// 複数のHookを一括登録
|
||||
let hooks = vec![
|
||||
Box::new(TimestampHook::new()),
|
||||
Box::new(FileNotificationHook::new()),
|
||||
Box::new(DebugHook::new()),
|
||||
];
|
||||
worker.register_hooks(hooks);
|
||||
|
||||
// TUI用デフォルトHookの登録
|
||||
let tui_hooks = crate::tui::hooks::get_default_hooks();
|
||||
worker.register_hooks(tui_hooks);
|
||||
```
|
||||
|
||||
### Hook の実行順序
|
||||
|
||||
同じフェーズで複数のHookが登録されている場合、登録順に実行されます。先に実行されたHookの結果(コンテキストの変更など)は、後続のHookに引き継がれます。
|
||||
|
||||
```rust
|
||||
// 実行順序の例
|
||||
worker.register_hook(Box::new(TimestampHook)); // 1番目
|
||||
worker.register_hook(Box::new(ValidationHook)); // 2番目
|
||||
worker.register_hook(Box::new(LoggingHook)); // 3番目
|
||||
```
|
||||
|
||||
### Hook の中断
|
||||
|
||||
`HookResult::Complete`または`HookResult::Error`を返すHookがあると、それ以降のHookは実行されず、処理が中断されます。
|
||||
|
||||
### Hook の動的管理
|
||||
|
||||
```rust
|
||||
impl Worker {
|
||||
// Hook一覧を取得
|
||||
pub fn list_hooks(&self) -> Vec<(&str, &str)>; // (name, hook_type)
|
||||
|
||||
// 特定のHookを削除
|
||||
pub fn remove_hook(&mut self, hook_name: &str) -> bool;
|
||||
|
||||
// フェーズ別Hookを削除
|
||||
pub fn remove_hooks_by_phase(&mut self, hook_type: &str);
|
||||
|
||||
// すべてのHookをクリア
|
||||
pub fn clear_hooks(&mut self);
|
||||
}
|
||||
```
|
||||
|
||||
## ストリーミング処理
|
||||
|
||||
### ストリーミング中のHook実行
|
||||
|
||||
```rust
|
||||
// worker/src/lib.rs の process_with_shared_state より
|
||||
stream! {
|
||||
// ... LLM応答処理中 ...
|
||||
|
||||
// ツール呼び出し検出時
|
||||
if let Some(tool_calls) = &response.tool_calls {
|
||||
for tool_call in tool_calls {
|
||||
// PreToolUse hooks 実行
|
||||
let (context, hook_result) = execute_hooks(
|
||||
HookEvent::PreToolUse,
|
||||
tool_call.name.clone()
|
||||
).await;
|
||||
|
||||
match hook_result {
|
||||
HookResult::Error(msg) => {
|
||||
yield Ok(StreamEvent::Error(msg));
|
||||
continue;
|
||||
}
|
||||
HookResult::Complete => break,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// ツール実行
|
||||
let result = execute_tool(tool_call).await;
|
||||
|
||||
// PostToolUse hooks 実行(ストリーミング中)
|
||||
let (context, hook_result) = execute_hooks(
|
||||
HookEvent::PostToolUse,
|
||||
tool_call.name.clone()
|
||||
).await;
|
||||
|
||||
// Hook結果を即座にストリーミング
|
||||
if let HookResult::AddMessage(msg, role) = hook_result {
|
||||
yield Ok(StreamEvent::HookMessage {
|
||||
hook_name: "PostToolUse".to_string(),
|
||||
content: msg,
|
||||
role,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ベストプラクティス
|
||||
|
||||
### 1. エラーハンドリング
|
||||
|
||||
```rust
|
||||
#[hook(hook_type = "OnTurnCompleted")]
|
||||
pub async fn robust_hook(mut context: HookContext) -> HookResult {
|
||||
match context.run_command("potentially_failing_command").await {
|
||||
Ok(output) => {
|
||||
// 成功時の処理
|
||||
context.add_message(format!("実行完了: {}", output), Role::System);
|
||||
HookResult::Continue
|
||||
}
|
||||
Err(error) => {
|
||||
// エラーログを記録するが、処理は継続
|
||||
tracing::warn!("Hook実行時にエラー: {}", error);
|
||||
HookResult::Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. パフォーマンス配慮
|
||||
|
||||
```rust
|
||||
#[hook(hook_type = "OnTurnCompleted")]
|
||||
pub async fn performance_aware_hook(context: HookContext) -> HookResult {
|
||||
// 重い処理は条件分岐で制限
|
||||
if context.content.len() > 10000 {
|
||||
// 大きなコンテンツの場合はスキップ
|
||||
return HookResult::Skip;
|
||||
}
|
||||
|
||||
// 非同期処理は適切にawaitする
|
||||
let result = tokio::time::timeout(
|
||||
Duration::from_secs(5),
|
||||
expensive_operation(&context)
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok(output) => HookResult::AddMessage(output, Role::System),
|
||||
Err(_) => {
|
||||
tracing::warn!("Hook処理がタイムアウトしました");
|
||||
HookResult::Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 設定可能なHook
|
||||
|
||||
```rust
|
||||
#[hook(hook_type = "OnMessageSend")]
|
||||
pub async fn configurable_hook(mut context: HookContext) -> HookResult {
|
||||
// 環境変数で動作を制御
|
||||
let enabled = std::env::var("HOOK_ENABLED")
|
||||
.unwrap_or_default()
|
||||
.parse::<bool>()
|
||||
.unwrap_or(false);
|
||||
|
||||
if !enabled {
|
||||
return HookResult::Skip;
|
||||
}
|
||||
|
||||
// 設定ファイルからオプション読み込み
|
||||
let config_path = format!("{}/.nia/hook_config.json", context.workspace_path);
|
||||
if let Ok(config_content) = tokio::fs::read_to_string(&config_path).await {
|
||||
if let Ok(config) = serde_json::from_str::<HookConfig>(&config_content) {
|
||||
// 設定に基づく処理
|
||||
return process_with_config(&mut context, &config).await;
|
||||
}
|
||||
}
|
||||
|
||||
HookResult::Continue
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 条件付きHook
|
||||
|
||||
```rust
|
||||
#[hook(hook_type = "PostToolUse", matcher = ".*")]
|
||||
pub async fn conditional_hook(context: HookContext) -> HookResult {
|
||||
// ワークスペースの種類に応じて処理を変更
|
||||
let is_git_repo = context.run_command("git status").await.is_ok();
|
||||
let is_rust_project = tokio::fs::metadata(
|
||||
format!("{}/Cargo.toml", context.workspace_path)
|
||||
).await.is_ok();
|
||||
|
||||
match (is_git_repo, is_rust_project) {
|
||||
(true, true) => {
|
||||
// Rustプロジェクト + Git
|
||||
context.run_command("cargo fmt").await.ok();
|
||||
HookResult::AddMessage("Rustコードをフォーマットしました".to_string(), Role::System)
|
||||
}
|
||||
(true, false) => {
|
||||
// その他のGitプロジェクト
|
||||
HookResult::AddMessage("Gitプロジェクトで作業中です".to_string(), Role::System)
|
||||
}
|
||||
_ => HookResult::Continue
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## デバッグとテスト
|
||||
|
||||
### Hook のテスト
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use worker::types::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_timestamp_hook() {
|
||||
let mut context = HookContext {
|
||||
content: "Hello, world!".to_string(),
|
||||
workspace_path: "/tmp".to_string(),
|
||||
message_history: vec![],
|
||||
tools: vec![],
|
||||
variables: HashMap::new(),
|
||||
tool_name: None,
|
||||
tool_args: None,
|
||||
tool_result: None,
|
||||
};
|
||||
|
||||
let result = add_timestamp_hook(context).await;
|
||||
|
||||
match result {
|
||||
HookResult::ModifyContent(content) => {
|
||||
assert!(content.contains("Hello, world!"));
|
||||
assert!(content.contains("["));
|
||||
assert!(content.contains("]"));
|
||||
}
|
||||
_ => panic!("Expected ModifyContent"),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hook のデバッグ
|
||||
|
||||
```rust
|
||||
#[hook(hook_type = "OnMessageSend")]
|
||||
pub async fn debug_hook(context: HookContext) -> HookResult {
|
||||
tracing::debug!(
|
||||
"Hook実行 - コンテンツ長: {}, ツール数: {}, 履歴数: {}",
|
||||
context.content.len(),
|
||||
context.tools.len(),
|
||||
context.message_history.len()
|
||||
);
|
||||
|
||||
// デバッグ情報をストリーミング
|
||||
context.stream_debug(
|
||||
"Hook Debug Info".to_string(),
|
||||
serde_json::json!({
|
||||
"content_length": context.content.len(),
|
||||
"tool_count": context.tools.len(),
|
||||
"history_count": context.message_history.len(),
|
||||
"workspace": context.workspace_path
|
||||
})
|
||||
);
|
||||
|
||||
HookResult::Continue
|
||||
}
|
||||
```
|
||||
|
||||
## 高度なHookパターン
|
||||
|
||||
### 状態を持つHook
|
||||
|
||||
```rust
|
||||
pub struct StatefulHook {
|
||||
counter: Arc<Mutex<u32>>,
|
||||
}
|
||||
|
||||
impl StatefulHook {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: Arc::new(Mutex::new(0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WorkerHook for StatefulHook {
|
||||
fn name(&self) -> &str { "stateful_hook" }
|
||||
fn hook_type(&self) -> &str { "OnTurnCompleted" }
|
||||
fn matcher(&self) -> &str { "" }
|
||||
|
||||
async fn execute(&self, mut context: HookContext) -> (HookContext, HookResult) {
|
||||
let mut count = self.counter.lock().unwrap();
|
||||
*count += 1;
|
||||
|
||||
context.set_variable("turn_count".to_string(), count.to_string());
|
||||
|
||||
if *count % 10 == 0 {
|
||||
(
|
||||
context,
|
||||
HookResult::AddMessage(
|
||||
format!("🎉 {}回目のターンです!", count),
|
||||
Role::System
|
||||
)
|
||||
)
|
||||
} else {
|
||||
(context, HookResult::Continue)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### チェーン可能なHook
|
||||
|
||||
```rust
|
||||
pub struct HookChain {
|
||||
hooks: Vec<Box<dyn WorkerHook>>,
|
||||
}
|
||||
|
||||
impl HookChain {
|
||||
pub fn new() -> Self {
|
||||
Self { hooks: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add_hook(mut self, hook: Box<dyn WorkerHook>) -> Self {
|
||||
self.hooks.push(hook);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WorkerHook for HookChain {
|
||||
fn name(&self) -> &str { "hook_chain" }
|
||||
fn hook_type(&self) -> &str { "OnMessageSend" }
|
||||
fn matcher(&self) -> &str { "" }
|
||||
|
||||
async fn execute(&self, mut context: HookContext) -> (HookContext, HookResult) {
|
||||
for hook in &self.hooks {
|
||||
let (new_context, result) = hook.execute(context).await;
|
||||
context = new_context;
|
||||
|
||||
match result {
|
||||
HookResult::Continue | HookResult::Skip => continue,
|
||||
other => return (context, other),
|
||||
}
|
||||
}
|
||||
|
||||
(context, HookResult::Continue)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## よくある質問
|
||||
|
||||
### Q: Hookはどのように読み込まれますか?
|
||||
|
||||
A: 現在、HookはRustコードとしてコンパイル時に組み込まれます。`#[hook]`マクロを使用するか、`WorkerHook`トレイトを実装して`worker.register_hook()`で登録します。
|
||||
|
||||
### Q: Hook内でファイルシステムにアクセスできますか?
|
||||
|
||||
A: はい。`run_command`メソッドを使用してシェルコマンドを実行できるほか、Rustの標準ライブラリやtokioを使用した直接的なファイル操作も可能です。
|
||||
|
||||
### Q: Hook間でデータを共有できますか?
|
||||
|
||||
A: `HookContext`の`variables`を使用して、同一ターン内のHook間でデータを共有できます。永続的なデータ共有には、ファイルやデータベースを使用してください。
|
||||
|
||||
### Q: ストリーミング中にHookの結果を表示できますか?
|
||||
|
||||
A: はい。`context.stream_message()`や`context.stream_system_message()`を使用することで、ストリーミング中にリアルタイムでメッセージを送信できます。
|
||||
|
||||
### Q: Hookでエラーが発生した場合はどうなりますか?
|
||||
|
||||
A: `HookResult::Error`を返すと、そのターンは中断されます。継続したい場合は、エラーをログに記録して`HookResult::Continue`を返してください。
|
||||
|
||||
## 関連ドキュメント
|
||||
|
||||
- [worker.md](worker.md) - Worker全体の文書
|
||||
- [worker-macro.md](worker-macro.md) - マクロシステム
|
||||
- `worker/src/lib.rs` - Hook実装コード
|
||||
- `worker-types/src/lib.rs` - Hook型定義
|
||||
- `nia-cli/src/tui/hooks/` - TUI用Hook実装例
|
||||
71
docs/prompt-composer.md
Normal file
71
docs/prompt-composer.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# PromptComposer
|
||||
|
||||
テンプレートベースのプロンプト構築システム。Handlebarsテンプレートエンジンによる動的プロンプト生成。
|
||||
|
||||
## 基本使用方法
|
||||
|
||||
```rust
|
||||
// 初期化
|
||||
let composer = PromptComposer::from_config_file("role.yaml", context)?;
|
||||
composer.initialize_session(&messages)?;
|
||||
|
||||
// プロンプト構築
|
||||
let messages = composer.compose(&user_messages)?;
|
||||
```
|
||||
|
||||
## テンプレート構文
|
||||
|
||||
### 変数展開
|
||||
```handlebars
|
||||
{{workspace.project_name}} # プロジェクト名
|
||||
{{workspace.project_type}} # プロジェクト種別
|
||||
{{model.provider}}/{{model.model_name}} # モデル情報
|
||||
{{tools_schema}} # ツールスキーマ
|
||||
```
|
||||
|
||||
### 条件分岐
|
||||
```handlebars
|
||||
{{#if workspace.has_nia_md}}
|
||||
Project info: {{workspace_content}}
|
||||
{{/if}}
|
||||
|
||||
{{#eq workspace.project_type "Rust"}}
|
||||
Focus on memory safety and performance.
|
||||
{{/eq}}
|
||||
```
|
||||
|
||||
### 繰り返し処理
|
||||
```handlebars
|
||||
{{#each tools}}
|
||||
- **{{name}}**: {{description}}
|
||||
{{/each}}
|
||||
```
|
||||
|
||||
### パーシャルテンプレート
|
||||
```handlebars
|
||||
{{> header}}
|
||||
{{> coding_guidelines}}
|
||||
{{> footer}}
|
||||
```
|
||||
|
||||
## カスタムヘルパー
|
||||
|
||||
### include_file
|
||||
外部ファイルを読み込み:
|
||||
```handlebars
|
||||
{{include_file "~/.config/nia/templates/guidelines.md"}}
|
||||
```
|
||||
|
||||
### workspace_content
|
||||
ワークスペースのnia.md内容を取得:
|
||||
```handlebars
|
||||
{{workspace_content}}
|
||||
```
|
||||
|
||||
## 利用可能なコンテキスト変数
|
||||
|
||||
- `workspace`: プロジェクト情報(root_path、project_type、git_info等)
|
||||
- `model`: LLMモデル情報(provider、model_name、capabilities)
|
||||
- `session`: セッション情報(conversation_id、message_count)
|
||||
- `user_input`: ユーザー入力内容
|
||||
- `tools_schema`: ツール定義JSON
|
||||
115
docs/worker-macro.md
Normal file
115
docs/worker-macro.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Worker Macro Documentation
|
||||
|
||||
## `#[tool]` マクロ
|
||||
|
||||
関数をLLMツールとして登録するためのプロシージャルマクロです。関数のドキュメントコメントを自動抽出してツール説明として利用し、JSONスキーマ生成、動的ツール登録に必要な`Tool` trait実装を自動生成します。
|
||||
|
||||
**重要**: このマクロはCore Crate Patternに基づき`worker-macros`クレートで実装され、`worker-types`クレートの型を完全修飾パスで直接参照しています。これにより循環依存を防ぎ、保守性の高いアーキテクチャを実現しています。
|
||||
|
||||
### 基本的な使用方法
|
||||
|
||||
```rust
|
||||
use worker_macros::tool;
|
||||
use worker::types::ToolResult; // worker-typesからの再エクスポート
|
||||
use serde::{Deserialize, Serialize};
|
||||
use schemars::JsonSchema;
|
||||
|
||||
#[derive(Deserialize, Serialize, JsonSchema)]
|
||||
struct CalculateArgs {
|
||||
/// 第一の数値
|
||||
a: i32,
|
||||
/// 第二の数値
|
||||
b: i32,
|
||||
}
|
||||
|
||||
/// 二つの数値を加算し、結果を返します。
|
||||
/// このコメントはLLMに送られるツール説明になります。
|
||||
#[tool]
|
||||
async fn calculate(args: CalculateArgs) -> ToolResult<i32> {
|
||||
Ok(args.a + args.b)
|
||||
}
|
||||
```
|
||||
|
||||
### 動的ツール登録での使用方法
|
||||
|
||||
```rust
|
||||
// Worker初期化
|
||||
let mut worker = Worker::new(
|
||||
LlmProvider::Gemini,
|
||||
"gemini-1.5-flash",
|
||||
&api_keys,
|
||||
None
|
||||
)?;
|
||||
|
||||
// マクロで生成されたツールを登録
|
||||
worker.register_tool(Box::new(CalculateTool::new()))?;
|
||||
|
||||
// ツール実行
|
||||
let args = serde_json::json!({"a": 10, "b": 20});
|
||||
let result = worker.execute_tool("calculate", args).await?;
|
||||
println!("Result: {}", result); // Result: 30
|
||||
```
|
||||
|
||||
### 機能
|
||||
|
||||
1. **自動ドキュメント抽出**: 関数のドキュメントコメント(`///`)を自動抽出してツール説明として使用
|
||||
2. **JSONスキーマ生成**: `schemars`を使用して引数型からJSONスキーマを自動生成
|
||||
3. **フォールバック説明**: ドキュメントコメントがない場合、関数名からデフォルト説明を生成
|
||||
4. **Tool trait実装**: 動的ツール登録に必要な全メソッドを自動実装
|
||||
5. **エラーハンドリング**: `ToolResult<T>`型を使用した一貫したエラー処理
|
||||
|
||||
### 要件
|
||||
|
||||
- **関数シグニチャ**: `async fn`である必要があります
|
||||
- **引数型**: 単一の構造体で`Deserialize + Serialize + JsonSchema`トレイトを実装必要
|
||||
- **返り値型**: `ToolResult<T>`で`T`は`Serialize + JsonSchema`トレイトを実装必要
|
||||
- **エラー型**: `ToolResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>`(`worker-types`で定義)
|
||||
|
||||
**注意**: プリミティブ型(`i32`, `String`等)を直接引数に使用することはできません。必ず構造体でラップしてください。
|
||||
|
||||
### 生成されるコード
|
||||
|
||||
マクロは以下の要素を自動生成します:
|
||||
|
||||
1. **元の関数**: 元のasync関数をそのまま保持
|
||||
2. **Tool構造体**: 関数名からPascalCaseで構造体名を生成(`read_file` → `ReadFileTool`)
|
||||
3. **Tool trait実装**: 以下のメソッドを自動実装:
|
||||
- `name()`: 関数名を返す
|
||||
- `description()`: ドキュメントコメントから抽出
|
||||
- `parameters_schema()`: 引数型からJSONスキーマを生成
|
||||
- `execute()`: 元の関数を呼び出して結果をシリアライズ
|
||||
|
||||
**実装詳細**: マクロは`::worker_types::`名前空間で型を参照し、`async_trait`を使用してtrait実装を行います。
|
||||
|
||||
### 自動生成される構造体名
|
||||
|
||||
関数名からsnake_caseをPascalCaseに変換し、`Tool`サフィックスを付加:
|
||||
- `read_file` → `ReadFileTool`
|
||||
- `calculate_sum` → `CalculateSumTool`
|
||||
- `fetch_data` → `FetchDataTool`
|
||||
- `send_email` → `SendEmailTool`
|
||||
- `process_image` → `ProcessImageTool`
|
||||
|
||||
**コンストラクタ**: 生成された構造体には`new()`メソッドが自動的に提供されます。
|
||||
|
||||
## Core Crate Patternとの統合
|
||||
|
||||
`#[tool]`マクロはCore Crate Patternの利点を最大限に活用:
|
||||
|
||||
- **循環依存の完全回避**: `worker-macros` → `worker-types`の一方向依存のみ
|
||||
- **コンパイル時型安全性**: 型チェックで実行時エラーを事前検出
|
||||
- **自動更新保守性**: `worker-types`の型変更がマクロ生成コードに自動反映
|
||||
- **拡張容易性**: 新しいtraitや機能を簡単に追加可能
|
||||
- **パフォーマンス**: コンパイル時コード生成で実行時オーバーヘッドなし
|
||||
|
||||
## メリットと使用事例
|
||||
|
||||
このアーキテクチャにより、以下のような利点が得られます:
|
||||
|
||||
- **簡单なツール作成**: 関数にマクロを付けるだけでLLMツール完成
|
||||
- **ドキュメント連動**: コメントがそのままLLMのツール説明に
|
||||
- **型安全なインターフェース**: JSONシリアライゼーションを自動処理
|
||||
- **動的登録サポート**: 実行時ツールの追加・削除が可能
|
||||
- **MCP統合**: Model Context Protocolとのシームレスな連携
|
||||
|
||||
これにより、柔軟性と使いやすさを兼ね備えたツールシステムが実現されています。
|
||||
185
docs/worker.md
Normal file
185
docs/worker.md
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# `worker` ライブラリ
|
||||
|
||||
`worker`は、LLM(大規模言語モデル)との対話を抽象化し、複数のLLMプロバイダを統一的に扱うための共通ライブラリです。動的ツール登録システム、フックシステム、MCPプロトコル統合を提供します。
|
||||
|
||||
## アーキテクチャ
|
||||
|
||||
**Core Crate Pattern**を採用:`worker-types`(基本型)→ `worker-macros`(マクロ)→ `worker`(メインライブラリ)
|
||||
|
||||
循環依存を解消し、型安全性と保守性を向上させています。
|
||||
|
||||
## 概要
|
||||
|
||||
LLMプロバイダ(Gemini, Claude, OpenAI, Ollama, XAI)を統一インターフェースで利用できます。`Worker`構造体が中心となり、プロンプト管理、ツール登録、フックシステム、MCPサーバー統合を提供します。
|
||||
|
||||
## 主要なコンポーネント
|
||||
|
||||
### `Worker`
|
||||
|
||||
LLMとの対話における中心的な構造体です。
|
||||
|
||||
- **LLMクライアントの保持**: `LlmClient` enumを通じて複数プロバイダの統一インターフェースを提供
|
||||
- **プロンプト管理**: `PromptComposer`によるテンプレートベースのプロンプト組み立て
|
||||
- **ストリーミング処理**: リアルタイムイベントストリーム(`process_task_stream`)とメッセージ履歴管理(`process_task_with_history`)
|
||||
- **動的ツール管理**: 実行時ツール登録・実行、MCPサーバー統合
|
||||
- **フックシステム**: メッセージ送信、ツール利用、ターン完了時の拡張ポイント
|
||||
- **セッション管理**: メッセージ履歴の保存・復元機能
|
||||
|
||||
### `Tool` トレイト
|
||||
|
||||
動的ツール登録の基盤。`worker-types`で定義され、`#[tool]`マクロで自動実装可能です。
|
||||
|
||||
### `StreamEvent`
|
||||
|
||||
LLMストリーミング応答のイベント型。テキストチャンク、ツール呼び出し、ツール結果、完了通知、デバッグ情報、フックメッセージをサポートします。
|
||||
|
||||
### `LlmClient` Enum
|
||||
|
||||
各LLMプロバイダクライアントの統合enum。`LlmClientTrait`を実装し、統一されたストリーミング対話と接続確認機能を提供します。
|
||||
|
||||
### フックシステム
|
||||
|
||||
- `WorkerHook` トレイト: カスタムフック実装の基盤
|
||||
- `HookManager`: フック登録・実行管理
|
||||
- `HookEvent`: OnMessageSend、PreToolUse、PostToolUse、OnTurnCompleted
|
||||
- `HookContext`: フック実行時のコンテキスト情報
|
||||
|
||||
### その他の主要型
|
||||
|
||||
- `Message`: LLM対話のメッセージ(role + content + tool_calls)
|
||||
- `LlmDebug`: デバッグログ制御と詳細出力
|
||||
- `ModelInfo`: モデル情報とサポート機能
|
||||
- `SessionData`: セッション永続化データ
|
||||
|
||||
## 基本的な使用方法
|
||||
|
||||
```rust
|
||||
use worker::{Worker, LlmProvider, types::{Message, Role, StreamEvent}};
|
||||
use futures_util::StreamExt;
|
||||
|
||||
// Worker初期化
|
||||
let mut worker = Worker::new(
|
||||
LlmProvider::Gemini,
|
||||
"gemini-1.5-flash",
|
||||
&api_keys,
|
||||
None
|
||||
)?;
|
||||
|
||||
// ツール登録(オプション)
|
||||
worker.register_tool(Box::new(some_tool))?;
|
||||
|
||||
// メッセージ送信とストリーム処理(履歴あり)
|
||||
let mut stream = worker.process_task_with_history(
|
||||
"Hello, how are you?".to_string(),
|
||||
None
|
||||
).await;
|
||||
|
||||
while let Some(Ok(event)) = stream.next().await {
|
||||
match event {
|
||||
StreamEvent::Chunk(text) => print!("{}", text),
|
||||
StreamEvent::ToolCall(call) => println!("Tool: {}", call.name),
|
||||
StreamEvent::ToolResult { tool_name, result } => {
|
||||
println!("Result from {}: {:?}", tool_name, result);
|
||||
},
|
||||
StreamEvent::Completion(_) => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ツール登録と実行
|
||||
|
||||
```rust
|
||||
// 個別ツール登録
|
||||
worker.register_tool(Box::new(ReadFileTool::new()))?;
|
||||
|
||||
// MCPサーバーからのツール登録
|
||||
let mcp_config = McpServerConfig {
|
||||
name: "filesystem".to_string(),
|
||||
command: "npx".to_string(),
|
||||
args: vec!["-y".to_string(), "@modelcontextprotocol/server-filesystem".to_string()],
|
||||
env: HashMap::new(),
|
||||
};
|
||||
worker.register_mcp_tools(mcp_config).await?;
|
||||
|
||||
// 複数MCPサーバーの並列初期化
|
||||
worker.queue_mcp_server(config1);
|
||||
worker.queue_mcp_server(config2);
|
||||
worker.init_mcp_servers().await?;
|
||||
|
||||
// ツール実行
|
||||
let result = worker.execute_tool("read_file", json!({"path": "./file.txt"})).await?;
|
||||
```
|
||||
|
||||
### フックシステム
|
||||
|
||||
```rust
|
||||
// カスタムフック実装例
|
||||
struct MyHook;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl WorkerHook for MyHook {
|
||||
fn name(&self) -> &str { "my_hook" }
|
||||
fn hook_type(&self) -> &str { "OnMessageSend" }
|
||||
fn matcher(&self) -> &str { "" }
|
||||
|
||||
async fn execute(&self, mut context: HookContext) -> (HookContext, HookResult) {
|
||||
// メッセージ前処理
|
||||
let new_content = format!("[処理済み] {}", context.content);
|
||||
context.set_content(new_content);
|
||||
(context, HookResult::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
// フック登録
|
||||
worker.register_hook(Box::new(MyHook));
|
||||
```
|
||||
|
||||
### 主要API
|
||||
|
||||
- `register_tool()`: ツール登録
|
||||
- `register_mcp_tools()`: MCPサーバーからのツール登録
|
||||
- `register_hook()`: フック登録
|
||||
- `get_tools()`: 登録済みツール一覧
|
||||
- `execute_tool()`: ツール実行
|
||||
- `process_task_stream()`: LLMストリーミング対話(履歴なし)
|
||||
- `process_task_with_history()`: メッセージ履歴付きストリーミング対話
|
||||
- `get_session_data()`: セッションデータ取得
|
||||
- `load_session()`: セッション復元
|
||||
|
||||
## 型システム
|
||||
|
||||
**Core Crate Pattern**により型を分離:
|
||||
- `worker-types`: 基本型(Tool, Message, StreamEvent等)
|
||||
- `worker`: 実装とエラー型(WorkerError等)
|
||||
|
||||
後方互換性のため`worker::types::`経由で全型にアクセス可能。
|
||||
|
||||
## 主要型
|
||||
|
||||
- `ToolResult<T>`: ツール実行結果
|
||||
- `DynamicToolDefinition`: ツール定義情報
|
||||
- `WorkerError`: ライブラリ固有エラー型
|
||||
|
||||
## LLMプロバイダサポート
|
||||
|
||||
全プロバイダでストリーミング対話、ツール呼び出し、モデル一覧取得、接続確認を完全実装:
|
||||
- Gemini (Google)
|
||||
- Claude (Anthropic)
|
||||
- OpenAI
|
||||
- Ollama
|
||||
- XAI (Grok)
|
||||
|
||||
## 主要機能
|
||||
|
||||
- **Core Crate Pattern**: 循環依存解消による保守性向上
|
||||
- **動的ツール登録**: 実行時ツール追加・実行
|
||||
- **#[tool]マクロ**: 関数からツール自動生成
|
||||
- **フックシステム**: メッセージ送信・ツール利用・ターン完了時の拡張ポイント
|
||||
- **ストリーミング対話**: リアルタイムLLM応答とイベント処理
|
||||
- **複数プロバイダ**: 統一インターフェースでの5つのLLMプロバイダサポート
|
||||
- **MCP統合**: Model Context Protocolサーバーとの動的連携
|
||||
- **セッション管理**: メッセージ履歴の永続化・復元
|
||||
- **ワークスペース検出**: Git情報を含む作業環境の自動認識
|
||||
- **型安全性**: 強い型チェックとエラーハンドリング
|
||||
- **並列処理**: MCPサーバーの並列初期化による高速化
|
||||
Loading…
Reference in New Issue
Block a user