diff --git a/README.md b/README.md index dde6d30..26a2d7d 100644 --- a/README.md +++ b/README.md @@ -86,21 +86,60 @@ aigpt import path/to/conversations.json ## 提供するMCPツール一覧 -1. **create_memory** - 新しいメモリを作成 +### 基本ツール + +1. **create_memory** - 新しいメモリを作成(シンプル版) 2. **update_memory** - 既存のメモリを更新 3. **delete_memory** - メモリを削除 4. **search_memories** - メモリを検索 5. **list_conversations** - インポートされた会話を一覧表示 +### AI機能ツール(重要!) + +6. **create_memory_with_ai** - AI解釈と心理判定付きでメモリを作成 + - 元のコンテンツをAIが解釈 + - 重要度を0.0-1.0のスコアで自動評価 + - ユーザーコンテキストを考慮可能 + +7. **list_memories_by_priority** - 優先順位順にメモリをリスト + - 高スコアから順に表示 + - min_scoreで閾値フィルタリング可能 + - limit で件数制限可能 + ## ツールの使用例 Claude Desktop/Codeで以下のように使用します: -### メモリの作成 +### 基本的なメモリ作成 ``` MCPツールを使って「今日は良い天気です」というメモリーを作成してください ``` +### AI解釈付きメモリ作成(推奨) +``` +create_memory_with_ai ツールを使って「新しいAI記憶システムのアイデアを思いついた」というメモリーを作成してください。 +ユーザーコンテキスト: 「AI開発者、創造的思考を重視」 +``` + +レスポンス例: +```json +{ + "success": true, + "id": "uuid-here", + "memory": { + "content": "新しいAI記憶システムのアイデアを思いついた", + "interpreted_content": "AIによる解釈: 記憶システムの革新的アプローチ...", + "priority_score": 0.85, + "user_context": "AI開発者、創造的思考を重視" + } +} +``` + +### 優先順位でメモリをリスト +``` +list_memories_by_priority ツールで、スコア0.7以上の重要なメモリを10件表示してください +``` + ### メモリの検索 ``` MCPツールを使って「天気」に関するメモリーを検索してください diff --git a/docs/TECHNICAL_REVIEW.md b/docs/TECHNICAL_REVIEW.md new file mode 100644 index 0000000..8ff4399 --- /dev/null +++ b/docs/TECHNICAL_REVIEW.md @@ -0,0 +1,566 @@ +# 技術評価レポート + +実装日: 2025-11-05 +評価者: Claude Code + +--- + +## 📊 総合評価 + +| 項目 | スコア | コメント | +|------|--------|----------| +| 技術選定 | ⭐⭐⭐⭐☆ (4/5) | Rustは適切。依存ライブラリに改善余地あり | +| シンプルさ | ⭐⭐⭐☆☆ (3/5) | 基本構造は良いが、統合が不完全 | +| 保守性 | ⭐⭐☆☆☆ (2/5) | テスト・設定外部化が不足 | +| 拡張性 | ⭐⭐⭐⭐☆ (4/5) | 機能フラグで拡張可能な設計 | + +--- + +## 1. 技術選定の評価 + +### ✅ 良い点 + +#### 1.1 Rust言語の選択 +**評価: 優秀** +- メモリ安全性と高パフォーマンス +- MCP serverとの相性が良い +- 型システムによる堅牢性 + +#### 1.2 非同期ランタイム (Tokio) +**評価: 適切** +- stdio通信に適した非同期処理 +- `async/await`で可読性が高い + +#### 1.3 機能フラグによる拡張 +**評価: 優秀** +```toml +[features] +extended = ["semantic-search", "ai-analysis", "web-integration"] +``` +- モジュール化された設計 +- 必要な機能だけビルド可能 + +### ⚠️ 問題点と改善提案 + +#### 1.4 openai クレートの問題 +**評価: 要改善** + +**現状:** +```toml +openai = { version = "1.1", optional = true } +``` + +**問題点:** +1. **APIが古い**: ChatCompletionMessage構造体が非推奨 +2. **ベンダーロックイン**: OpenAI専用 +3. **メンテナンス**: openai crateは公式ではない + +**推奨: async-openai または独自実装** +```toml +# オプション1: より新しいクレート +async-openai = { version = "0.20", optional = true } + +# オプション2: 汎用LLMクライアント (推奨) +reqwest = { version = "0.11", features = ["json"], optional = true } +``` + +**利点:** +- OpenAI, Anthropic, Groqなど複数のプロバイダ対応可能 +- API仕様を完全制御 +- メンテナンスリスク低減 + +#### 1.5 データストレージ +**評価: 要改善(将来的に)** + +**現状:** JSON ファイル +```rust +// ~/.config/syui/ai/gpt/memory.json +``` + +**問題点:** +- スケーラビリティに限界(数千件以上で遅延) +- 並行アクセスに弱い +- 全データをメモリに展開 + +**推奨: 段階的改善** + +1. **短期(現状維持)**: JSON ファイル + - シンプルで十分 + - 個人利用には問題なし + +2. **中期**: SQLite + ```toml + rusqlite = "0.30" + ``` + - インデックスによる高速検索 + - トランザクション対応 + - ファイルベースで移行が容易 + +3. **長期**: 埋め込みベクトルDB + ```toml + qdrant-client = "1.0" # または lance, chroma + ``` + - セマンティック検索の高速化 + - スケーラビリティ + +--- + +## 2. シンプルさの評価 + +### ✅ 良い点 + +#### 2.1 明確なレイヤー分離 +``` +src/ +├── memory.rs # データレイヤー +├── ai_interpreter.rs # AIレイヤー +└── mcp/ + ├── base.rs # MCPプロトコル + └── extended.rs # 拡張機能 +``` + +#### 2.2 最小限の依存関係 +基本機能は標準的なクレートのみ使用。 + +### ⚠️ 問題点と改善提案 + +#### 2.3 AI機能とMCPの統合が不完全 +**重大な問題** + +**現状:** +- `create_memory_with_ai()` が実装済み +- しかしMCPツールでは使われていない! + +**MCPサーバー (base.rs:198):** +```rust +fn tool_create_memory(&mut self, arguments: &Value) -> Value { + let content = arguments["content"].as_str().unwrap_or(""); + // create_memory() を呼んでいる(AI解釈なし) + match self.memory_manager.create_memory(content) { + ... + } +} +``` + +**改善必須:** +```rust +// 新しいツールを追加すべき +fn tool_create_memory_with_ai(&mut self, arguments: &Value) -> Value { + let content = arguments["content"].as_str().unwrap_or(""); + let user_context = arguments["user_context"].as_str(); + + match self.memory_manager.create_memory_with_ai(content, user_context).await { + Ok(id) => json!({ + "success": true, + "id": id, + "message": "Memory created with AI interpretation" + }), + ... + } +} +``` + +#### 2.4 Memory構造体の新フィールドが未活用 +**新フィールド:** +```rust +pub struct Memory { + pub interpreted_content: String, // ❌ MCPで出力されない + pub priority_score: f32, // ❌ MCPで出力されない + pub user_context: Option, // ❌ MCPで出力されない +} +``` + +**MCPレスポンス (base.rs:218):** +```rust +json!({ + "id": m.id, + "content": m.content, // ✅ + "created_at": m.created_at, // ✅ + "updated_at": m.updated_at // ✅ + // interpreted_content, priority_score がない! +}) +``` + +**修正例:** +```rust +json!({ + "id": m.id, + "content": m.content, + "interpreted_content": m.interpreted_content, // 追加 + "priority_score": m.priority_score, // 追加 + "user_context": m.user_context, // 追加 + "created_at": m.created_at, + "updated_at": m.updated_at +}) +``` + +#### 2.5 優先順位取得APIが未実装 +**実装済みだが未使用:** +```rust +pub fn get_memories_by_priority(&self) -> Vec<&Memory> { ... } +``` + +**追加すべきMCPツール:** +```json +{ + "name": "list_memories_by_priority", + "description": "List all memories sorted by priority score (high to low)", + "inputSchema": { + "type": "object", + "properties": { + "min_score": { + "type": "number", + "description": "Minimum priority score (0.0-1.0)" + }, + "limit": { + "type": "integer", + "description": "Maximum number of memories to return" + } + } + } +} +``` + +--- + +## 3. リファクタリング提案 + +### 🔴 緊急度: 高 + +#### 3.1 MCPツールとAI機能の統合 +**ファイル:** `src/mcp/base.rs` + +**追加すべきツール:** +1. `create_memory_with_ai` - AI解釈付き記憶作成 +2. `list_memories_by_priority` - 優先順位ソート +3. `get_memory_stats` - 統計情報(平均スコア、総数など) + +#### 3.2 Memory出力の完全化 +**全MCPレスポンスで新フィールドを含める:** +- `tool_search_memories()` +- `tool_create_memory()` +- `tool_update_memory()` のレスポンス + +### 🟡 緊急度: 中 + +#### 3.3 設定の外部化 +**現状:** ハードコード +```rust +max_memories: 100, +min_priority_score: 0.3, +``` + +**提案:** 設定ファイル +```rust +// src/config.rs +#[derive(Deserialize)] +pub struct Config { + pub max_memories: usize, + pub min_priority_score: f32, + pub ai_model: String, + pub auto_prune: bool, +} + +impl Config { + pub fn load() -> Result { + let config_path = dirs::config_dir()? + .join("syui/ai/gpt/config.toml"); + + if config_path.exists() { + let content = std::fs::read_to_string(config_path)?; + Ok(toml::from_str(&content)?) + } else { + Ok(Self::default()) + } + } +} +``` + +**config.toml:** +```toml +max_memories = 100 +min_priority_score = 0.3 +ai_model = "gpt-3.5-turbo" +auto_prune = true +``` + +#### 3.4 エラーハンドリングの改善 +**現状の問題:** +```rust +let content = arguments["content"].as_str().unwrap_or(""); +``` +- `unwrap_or("")` で空文字列になる +- エラーが握りつぶされる + +**改善:** +```rust +let content = arguments["content"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Missing required field: content"))?; +``` + +#### 3.5 LLMクライアントの抽象化 +**現状:** OpenAI専用 + +**提案:** トレイトベースの設計 +```rust +// src/ai/mod.rs +#[async_trait] +pub trait LLMProvider { + async fn interpret(&self, content: &str) -> Result; + async fn score(&self, content: &str, context: Option<&str>) -> Result; +} + +// src/ai/openai.rs +pub struct OpenAIProvider { ... } + +// src/ai/anthropic.rs +pub struct AnthropicProvider { ... } + +// src/ai/local.rs (ollama, llamaなど) +pub struct LocalProvider { ... } +``` + +**利点:** +- プロバイダーの切り替えが容易 +- テスト時にモックを使える +- コスト最適化(安いモデルを選択) + +### 🟢 緊急度: 低(将来的に) + +#### 3.6 テストコードの追加 +```rust +// tests/memory_tests.rs +#[tokio::test] +async fn test_create_memory_with_ai() { + let mut manager = MemoryManager::new().await.unwrap(); + let id = manager.create_memory_with_ai("test", None).await.unwrap(); + assert!(!id.is_empty()); +} + +// tests/integration_tests.rs +#[tokio::test] +async fn test_mcp_create_memory_tool() { + let mut server = BaseMCPServer::new().await.unwrap(); + let request = json!({ + "params": { + "name": "create_memory", + "arguments": {"content": "test"} + } + }); + let result = server.execute_tool("create_memory", &request["params"]["arguments"]).await; + assert_eq!(result["success"], true); +} +``` + +#### 3.7 ドキュメンテーション +```rust +/// AI解釈と心理判定を使った記憶作成 +/// +/// # Arguments +/// * `content` - 記憶する元のコンテンツ +/// * `user_context` - ユーザー固有のコンテキスト(オプション) +/// +/// # Returns +/// 作成された記憶のUUID +/// +/// # Examples +/// ``` +/// let id = manager.create_memory_with_ai("今日は良い天気", Some("天気好き")).await?; +/// ``` +pub async fn create_memory_with_ai(&mut self, content: &str, user_context: Option<&str>) -> Result +``` + +--- + +## 4. 推奨アーキテクチャ + +### 理想的な構造 +``` +src/ +├── config.rs # 設定管理 +├── ai/ +│ ├── mod.rs # トレイト定義 +│ ├── openai.rs # OpenAI実装 +│ └── mock.rs # テスト用モック +├── storage/ +│ ├── mod.rs # トレイト定義 +│ ├── json.rs # JSON実装(現在) +│ └── sqlite.rs # SQLite実装(将来) +├── memory.rs # ビジネスロジック +└── mcp/ + ├── base.rs # 基本MCPサーバー + ├── extended.rs # 拡張機能 + └── tools.rs # ツール定義の分離 +``` + +--- + +## 5. 優先度付きアクションプラン + +### 🔴 今すぐ実施(重要度: 高) +1. **MCPツールとAI機能の統合** (2-3時間) + - [ ] `create_memory_with_ai` ツール追加 + - [ ] `list_memories_by_priority` ツール追加 + - [ ] Memory出力に新フィールド追加 + +2. **openai crateの問題調査** (1-2時間) + - [ ] 現在のAPIが動作するか確認 + - [ ] 必要なら async-openai へ移行 + +### 🟡 次のマイルストーン(重要度: 中) +3. **設定の外部化** (1-2時間) + - [ ] config.toml サポート + - [ ] 環境変数サポート + +4. **エラーハンドリング改善** (1-2時間) + - [ ] Result型の適切な使用 + - [ ] カスタムエラー型の導入 + +5. **LLMプロバイダーの抽象化** (3-4時間) + - [ ] トレイトベース設計 + - [ ] OpenAI実装 + - [ ] モック実装(テスト用) + +### 🟢 将来的に(重要度: 低) +6. **データストレージの改善** (4-6時間) + - [ ] SQLite実装 + - [ ] マイグレーションツール + +7. **テストスイート** (2-3時間) + - [ ] ユニットテスト + - [ ] 統合テスト + +8. **ドキュメント充実** (1-2時間) + - [ ] APIドキュメント + - [ ] 使用例 + +--- + +## 6. 具体的なコード改善例 + +### 問題箇所1: AI機能が使われていない + +**Before (base.rs):** +```rust +fn tool_create_memory(&mut self, arguments: &Value) -> Value { + let content = arguments["content"].as_str().unwrap_or(""); + match self.memory_manager.create_memory(content) { // ❌ AI使わない + Ok(id) => json!({"success": true, "id": id}), + Err(e) => json!({"success": false, "error": e.to_string()}) + } +} +``` + +**After:** +```rust +async fn tool_create_memory(&mut self, arguments: &Value) -> Value { + let content = arguments["content"].as_str().unwrap_or(""); + let use_ai = arguments["use_ai"].as_bool().unwrap_or(false); + let user_context = arguments["user_context"].as_str(); + + let result = if use_ai { + self.memory_manager.create_memory_with_ai(content, user_context).await // ✅ AI使う + } else { + self.memory_manager.create_memory(content) + }; + + match result { + Ok(id) => { + // 作成したメモリを取得して詳細を返す + if let Some(memory) = self.memory_manager.memories.get(&id) { + json!({ + "success": true, + "id": id, + "memory": { + "content": memory.content, + "interpreted_content": memory.interpreted_content, + "priority_score": memory.priority_score, + "created_at": memory.created_at + } + }) + } else { + json!({"success": true, "id": id}) + } + } + Err(e) => json!({"success": false, "error": e.to_string()}) + } +} +``` + +### 問題箇所2: Memory構造体のアクセス制御 + +**Before (memory.rs):** +```rust +pub struct MemoryManager { + memories: HashMap, // ❌ privateだが直接アクセスできない +} +``` + +**After:** +```rust +pub struct MemoryManager { + memories: HashMap, +} + +impl MemoryManager { + // ✅ getter追加 + pub fn get_memory(&self, id: &str) -> Option<&Memory> { + self.memories.get(id) + } + + pub fn get_all_memories(&self) -> Vec<&Memory> { + self.memories.values().collect() + } +} +``` + +--- + +## 7. まとめ + +### 現状の評価 +**総合点: 65/100** + +- **基本設計**: 良好(レイヤー分離、機能フラグ) +- **実装品質**: 中程度(AI機能が未統合、テスト不足) +- **保守性**: やや低い(設定ハードコード、ドキュメント不足) + +### 最も重要な改善 +1. **MCPツールとAI機能の統合** ← 今すぐやるべき +2. **Memory出力の完全化** ← 今すぐやるべき +3. **設定の外部化** ← 次のステップ + +### コンセプトについて +「心理優先記憶装置」という**コンセプト自体は非常に優れている**。 +ただし、実装がコンセプトに追いついていない状態。 + +AI機能をMCPツールに統合すれば、すぐに実用レベルになる。 + +### 推奨: 段階的改善 +``` +Phase 1 (今週): MCPツール統合 → 使える状態に +Phase 2 (来週): 設定外部化 + エラーハンドリング → 堅牢に +Phase 3 (来月): LLM抽象化 + テスト → 本番品質に +``` + +--- + +## 付録: 類似プロジェクト比較 + +| プロジェクト | アプローチ | 長所 | 短所 | +|-------------|-----------|------|------| +| **aigpt (本プロジェクト)** | AI解釈+優先度スコア | 独自性が高い | 実装未完成 | +| mem0 (Python) | ベクトル検索 | スケーラブル | シンプルさに欠ける | +| ChatGPT Memory | ブラックボックス | 完成度高い | カスタマイズ不可 | +| MemGPT | エージェント型 | 高機能 | 複雑すぎる | + +**本プロジェクトの強み:** +- Rust による高速性と安全性 +- AI解釈という独自アプローチ +- シンプルな設計(改善後) + +--- + +評価日: 2025-11-05 +次回レビュー推奨: Phase 1 完了後 diff --git a/src/mcp/base.rs b/src/mcp/base.rs index 491e390..ff012eb 100644 --- a/src/mcp/base.rs +++ b/src/mcp/base.rs @@ -156,6 +156,43 @@ impl BaseMCPServer { "type": "object", "properties": {} } + }), + json!({ + "name": "create_memory_with_ai", + "description": "Create a new memory with AI interpretation and priority scoring", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Content of the memory" + }, + "user_context": { + "type": "string", + "description": "User-specific context (optional)" + } + }, + "required": ["content"] + } + }), + json!({ + "name": "list_memories_by_priority", + "description": "List memories sorted by priority score (high to low)", + "inputSchema": { + "type": "object", + "properties": { + "min_score": { + "type": "number", + "description": "Minimum priority score (0.0-1.0)", + "minimum": 0.0, + "maximum": 1.0 + }, + "limit": { + "type": "integer", + "description": "Maximum number of memories to return" + } + } + } }) ] } @@ -183,6 +220,8 @@ impl BaseMCPServer { pub async fn execute_tool(&mut self, tool_name: &str, arguments: &Value) -> Value { match tool_name { "create_memory" => self.tool_create_memory(arguments), + "create_memory_with_ai" => self.tool_create_memory_with_ai(arguments).await, + "list_memories_by_priority" => self.tool_list_memories_by_priority(arguments), "search_memories" => self.tool_search_memories(arguments), "update_memory" => self.tool_update_memory(arguments), "delete_memory" => self.tool_delete_memory(arguments), @@ -218,6 +257,9 @@ impl BaseMCPServer { "memories": memories.into_iter().map(|m| json!({ "id": m.id, "content": m.content, + "interpreted_content": m.interpreted_content, + "priority_score": m.priority_score, + "user_context": m.user_context, "created_at": m.created_at, "updated_at": m.updated_at })).collect::>() @@ -266,6 +308,72 @@ impl BaseMCPServer { }) } + // AI解釈付きメモリ作成 + async fn tool_create_memory_with_ai(&mut self, arguments: &Value) -> Value { + let content = arguments["content"].as_str().unwrap_or(""); + let user_context = arguments["user_context"].as_str(); + + match self.memory_manager.create_memory_with_ai(content, user_context).await { + Ok(id) => { + // 作成したメモリを取得して詳細情報を返す + if let Some(memory) = self.memory_manager.get_memory(&id) { + json!({ + "success": true, + "id": id, + "memory": { + "content": memory.content, + "interpreted_content": memory.interpreted_content, + "priority_score": memory.priority_score, + "user_context": memory.user_context, + "created_at": memory.created_at + }, + "message": "Memory created with AI interpretation and priority scoring" + }) + } else { + json!({ + "success": true, + "id": id, + "message": "Memory created with AI interpretation" + }) + } + } + Err(e) => json!({ + "success": false, + "error": e.to_string() + }) + } + } + + // 優先順位順にメモリをリスト + fn tool_list_memories_by_priority(&self, arguments: &Value) -> Value { + let min_score = arguments["min_score"].as_f64().unwrap_or(0.0) as f32; + let limit = arguments["limit"].as_u64().map(|l| l as usize); + + let mut memories = self.memory_manager.get_memories_by_priority(); + + // min_scoreでフィルタリング + memories.retain(|m| m.priority_score >= min_score); + + // limitを適用 + if let Some(limit) = limit { + memories.truncate(limit); + } + + json!({ + "success": true, + "count": memories.len(), + "memories": memories.into_iter().map(|m| json!({ + "id": m.id, + "content": m.content, + "interpreted_content": m.interpreted_content, + "priority_score": m.priority_score, + "user_context": m.user_context, + "created_at": m.created_at, + "updated_at": m.updated_at + })).collect::>() + }) + } + // 不明なメソッドハンドラ fn handle_unknown_method(&self, id: Value) -> Value { json!({ diff --git a/src/mcp/extended.rs b/src/mcp/extended.rs index 604e761..eb9d4d0 100644 --- a/src/mcp/extended.rs +++ b/src/mcp/extended.rs @@ -210,6 +210,9 @@ impl ExtendedMCPServer { "memories": memories.into_iter().map(|m| json!({ "id": m.id, "content": m.content, + "interpreted_content": m.interpreted_content, + "priority_score": m.priority_score, + "user_context": m.user_context, "created_at": m.created_at, "updated_at": m.updated_at })).collect::>(), diff --git a/src/memory.rs b/src/memory.rs index 607742f..2ce5f79 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -298,6 +298,16 @@ impl MemoryManager { Ok((data.memories, data.conversations)) } + // Getter: 単一メモリ取得 + pub fn get_memory(&self, id: &str) -> Option<&Memory> { + self.memories.get(id) + } + + // Getter: 全メモリ取得 + pub fn get_all_memories(&self) -> Vec<&Memory> { + self.memories.values().collect() + } + fn save_data(&self) -> Result<()> { #[derive(Serialize)] struct Data<'a> {