diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..854d658 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,70 @@ +# Changelog + +## [Unreleased] - 2025-11-05 + +### 🎉 Major Changes: Complete Local Operation + +#### Changed +- **Removed external AI API dependency**: No longer calls Claude/OpenAI APIs +- **Claude Code does the interpretation**: AIが解釈するのではなく、Claude Code 自身が解釈 +- **Zero cost**: API料金が一切かからない +- **Complete privacy**: データが外部に送信されない + +#### Technical Details +- Removed `openai` crate dependency +- Removed `ai-analysis` feature (no longer needed) +- Simplified `ai_interpreter.rs` to be a lightweight wrapper +- Updated `create_memory_with_ai` MCP tool to accept `interpreted_content` and `priority_score` from Claude Code +- Added `create_memory_with_interpretation()` method to MemoryManager +- Updated tool descriptions to guide Claude Code on how to interpret and score + +#### Benefits +- ✅ **完全ローカル**: 外部 API 不要 +- ✅ **ゼロコスト**: API 料金なし +- ✅ **プライバシー**: データ漏洩の心配なし +- ✅ **シンプル**: 依存関係が少ない +- ✅ **高速**: ネットワーク遅延なし + +#### How It Works Now + +1. User: 「今日、新しいアイデアを思いついた」とメモリを作成 +2. Claude Code: 内容を解釈し、スコア (0.0-1.0) を計算 +3. Claude Code: `create_memory_with_ai` ツールを呼び出し、解釈とスコアを渡す +4. aigpt: メモリを保存し、ゲーム風の結果を返す +5. Claude Code: ユーザーに結果を表示 + +#### Migration Notes + +For users who were expecting external AI API usage: +- No API keys needed anymore (ANTHROPIC_API_KEY, OPENAI_API_KEY) +- Claude Code (local) now does all the interpretation +- This is actually better: faster, cheaper, more private! + +--- + +## [0.1.0] - Initial Release + +### Added +- Basic memory CRUD operations +- ChatGPT conversation import +- stdio MCP server implementation +- Psychological priority scoring (0.0-1.0) +- Gamification features (rarity, diagnosis types, XP) +- Romance companion system +- 11 MCP tools for Claude Code integration + +### Features +- Memory capacity management (max 100 by default) +- Automatic pruning of low-priority memories +- Game-style result displays +- Companion affection and level system +- Daily challenges +- Ranking displays + +### Documentation +- README.md with full examples +- DESIGN.md with system architecture +- TECHNICAL_REVIEW.md with evaluation +- ROADMAP.md with 7-phase plan +- QUICKSTART.md for immediate usage +- USAGE.md for detailed instructions diff --git a/Cargo.toml b/Cargo.toml index f74682e..d60cfa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,11 +38,8 @@ dirs = "5.0" # Extended features (optional) reqwest = { version = "0.11", features = ["json"], optional = true } scraper = { version = "0.18", optional = true } -openai = { version = "1.1", optional = true } [features] default = [] -extended = ["semantic-search", "ai-analysis", "web-integration"] -semantic-search = ["openai"] -ai-analysis = ["openai"] +extended = ["web-integration"] web-integration = ["reqwest", "scraper"] diff --git a/QUICKSTART.md b/QUICKSTART.md index 251be29..59374dc 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -5,11 +5,8 @@ ### ステップ1: MCPサーバーを起動 ```bash -# 基本版(AI機能なし) +# API キー不要!完全にローカルで動作 ./target/debug/aigpt server - -# AI機能有効版(OpenAI APIキーが必要) -OPENAI_API_KEY=your_key ./target/debug/aigpt server ``` ### ステップ2: Claude Desktop/Codeに設定 diff --git a/README.md b/README.md index 7c0b802..24ea755 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,19 @@ # aigpt - AI Memory System with Psychological Priority -AI記憶装置(心理優先記憶システム)。ChatGPTのメモリ機能を参考にしながら、AIによる解釈と心理判定を加えた新しいメモリストレージシステムです。 +AI記憶装置(心理優先記憶システム)。**完全にローカルで動作**し、Claude Code と連携して、心理判定スコア付きのメモリ管理を実現します。 + +## 🌟 特徴 + +- ✅ **完全ローカル**: 外部 API 不要、プライバシー保護 +- ✅ **ゼロコスト**: API 料金なし +- ✅ **Claude Code 統合**: Claude 自身が解釈とスコアリング +- ✅ **ゲーミフィケーション**: 心理テスト風の楽しい表示 +- ✅ **恋愛コンパニオン**: 育成要素付き ## コンセプト -従来の「会話 → 保存 → 検索」ではなく、「会話 → AI解釈 → 保存 → 検索」を実現。 -AIが記憶を解釈し、重要度を0.0-1.0のスコアで評価。優先度の高い記憶を保持し、低い記憶は自動的に削除されます。 +従来の「会話 → 保存 → 検索」ではなく、「会話 → **Claude による解釈** → 保存 → 検索」を実現。 +Claude Code が記憶を解釈し、重要度を0.0-1.0のスコアで評価。優先度の高い記憶を保持し、低い記憶は自動的に削除されます。 ## 機能 @@ -25,9 +33,10 @@ AIが記憶を解釈し、重要度を0.0-1.0のスコアで評価。優先度 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -2. プロジェクトをビルド: +2. プロジェクトをビルド(依存関係が少なくシンプル!): ```bash cargo build --release +# API キー不要!完全にローカルで動作します ``` 3. バイナリをパスの通った場所にコピー(オプション): diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 0000000..ca4addf --- /dev/null +++ b/USAGE.md @@ -0,0 +1,285 @@ +# 使い方ガイド 📖 + +## 🚀 aigpt の起動方法 + +### 1. ビルド + +```bash +# ローカル環境で実行 +cd /path/to/aigpt +cargo build --release --features ai-analysis +``` + +### 2. Claude API キーの設定 + +```bash +# 環境変数で設定 +export ANTHROPIC_API_KEY=sk-ant-... + +# モデルを指定(オプション) +export ANTHROPIC_MODEL=claude-3-5-sonnet-20241022 # デフォルトは haiku +``` + +### 3. MCPサーバーとして起動 + +```bash +# 起動 +./target/release/aigpt server + +# またはAPI キーを直接指定 +ANTHROPIC_API_KEY=sk-ant-... ./target/release/aigpt server +``` + +--- + +## 🎮 Claude Code での使い方 + +### 設定方法 + +#### 方法1: コマンドで追加(推奨!) + +```bash +claude mcp add aigpt /home/user/aigpt/target/release/aigpt server +``` + +#### 方法2: 設定ファイルを直接編集 + +`~/.config/claude-code/config.json` に追加: + +```json +{ + "mcpServers": { + "aigpt": { + "command": "/home/user/aigpt/target/release/aigpt", + "args": ["server"] + } + } +} +``` + +**注意**: 環境変数 (env) は不要です!完全にローカルで動作します。 + +### Claude Code を再起動 + +設定後、Claude Code を再起動すると、11個のツールが使えるようになります。 + +--- + +## 💬 実際の使用例 + +### 例1: メモリを作成 + +**あなた(Claude Codeで話しかける):** +> 「今日、新しいAIシステムのアイデアを思いついた」というメモリを作成して + +**Claude Code の動作:** +1. `create_memory_with_ai` ツールを自動で呼び出す +2. Claude API があなたの入力を解釈 +3. 4つの心スコア(感情、関連性、新規性、実用性)を計算 +4. priority_score (0.0-1.0) を算出 +5. ゲーム風の結果を表示 + +**結果の表示:** +``` +╔══════════════════════════════════════╗ +║ 🎲 メモリースコア判定 ║ +╚══════════════════════════════════════╝ + +🟣 EPIC 85点 +💡 あなたは【革新者】タイプ! + +💕 好感度: ❤️❤️❤️❤️❤️🤍🤍🤍🤍🤍 42.5% +💎 XP獲得: +850 XP + +📊 スコア内訳: + 感情的インパクト: ████████░░ 20% + あなたへの関連性: ████████░░ 20% + 新規性・独自性: █████████░ 22.5% + 実用性・有用性: █████████░ 22.5% +``` + +### 例2: コンパニオンを作成 + +**あなた:** +> 「エミリー」という名前のエネルギッシュなコンパニオンを作成して + +**結果:** +``` +╔══════════════════════════════════════╗ +║ 💕 エミリー のプロフィール ║ +╚══════════════════════════════════════╝ + +⚡ 性格: エネルギッシュで冒険好き +「新しいことに挑戦するのが大好き!」 + +🏆 関係レベル: Lv.1 +💕 好感度: 🤍🤍🤍🤍🤍🤍🤍🤍🤍🤍 0% +🤝 信頼度: ░░░░░░░░░░ 0/100 +``` + +### 例3: コンパニオンに反応してもらう + +**あなた:** +> 先ほど作ったメモリにエミリーを反応させて + +**結果:** +``` +⚡ エミリー: +「わあ!新しいAIシステムのアイデアって +すごくワクワクするね!💡 +あなたの創造力、本当に素敵だと思う!」 + +💕 好感度変化: 0% → 80.75% ⬆️ +80.75% +🎊 ボーナス: ⚡相性抜群! (+95%) +💎 XP獲得: +850 XP +🏆 レベルアップ: Lv.1 → Lv.9 +``` + +### 例4: ランキングを見る + +**あなた:** +> メモリをランキング順に表示して + +**結果:** +``` +╔══════════════════════════════════════╗ +║ 🏆 メモリーランキング TOP10 ║ +╚══════════════════════════════════════╝ + +1. 🟡 LEGENDARY 95点 - 「AI哲学について...」 +2. 🟣 EPIC 85点 - 「新しいシステムのアイデア」 +3. 🔵 RARE 75点 - 「プロジェクトの進捗」 +... +``` + +--- + +## 📊 結果の見方 + +### レアリティシステム +- 🟡 **LEGENDARY** (90-100点): 伝説級の記憶 +- 🟣 **EPIC** (80-89点): エピック級の記憶 +- 🔵 **RARE** (60-79点): レアな記憶 +- 🟢 **UNCOMMON** (40-59点): まあまあの記憶 +- ⚪ **COMMON** (0-39点): 日常的な記憶 + +### 診断タイプ(あなたの個性) +- 💡 **革新者**: 創造性と実用性が高い +- 🧠 **哲学者**: 感情と新規性が高い +- 🎯 **実務家**: 実用性と関連性が高い +- ✨ **夢想家**: 新規性と感情が高い +- 📊 **分析家**: バランス型 + +### コンパニオン性格 +- ⚡ **Energetic**: 革新者と相性95% +- 📚 **Intellectual**: 哲学者と相性95% +- 🎯 **Practical**: 実務家と相性95% +- 🌙 **Dreamy**: 夢想家と相性95% +- ⚖️ **Balanced**: 分析家と相性95% + +--- + +## 💾 データの保存場所 + +``` +~/.config/syui/ai/gpt/memory.json +``` + +このファイルに、すべてのメモリとコンパニオン情報が保存されます。 + +**データ形式:** +```json +{ + "memories": { + "uuid-1234": { + "id": "uuid-1234", + "content": "元の入力", + "interpreted_content": "Claude の解釈", + "priority_score": 0.85, + "user_context": null, + "created_at": "2025-11-05T...", + "updated_at": "2025-11-05T..." + } + }, + "conversations": {} +} +``` + +--- + +## 🎯 利用可能なMCPツール(11個) + +### 基本ツール +1. **create_memory** - シンプルなメモリ作成 +2. **search_memories** - メモリ検索 +3. **update_memory** - メモリ更新 +4. **delete_memory** - メモリ削除 +5. **list_conversations** - 会話一覧 + +### AI機能ツール 🎮 +6. **create_memory_with_ai** - AI解釈+ゲーム結果 +7. **list_memories_by_priority** - ランキング表示 +8. **daily_challenge** - デイリークエスト + +### コンパニオンツール 💕 +9. **create_companion** - コンパニオン作成 +10. **companion_react** - メモリへの反応 +11. **companion_profile** - プロフィール表示 + +--- + +## ⚙️ トラブルシューティング + +### ビルドできない +```bash +# 依存関係を更新 +cargo clean +cargo update +cargo build --release --features ai-analysis +``` + +### Claude API エラー +```bash +# APIキーを確認 +echo $ANTHROPIC_API_KEY + +# 正しく設定 +export ANTHROPIC_API_KEY=sk-ant-... +``` + +### MCPサーバーが認識されない +1. Claude Code を完全に再起動 +2. config.json のパスが正しいか確認 +3. バイナリが存在するか確認: `ls -la /home/user/aigpt/target/release/aigpt` + +### データが保存されない +```bash +# ディレクトリを確認 +ls -la ~/.config/syui/ai/gpt/ + +# なければ手動作成 +mkdir -p ~/.config/syui/ai/gpt/ +``` + +--- + +## 🎉 楽しみ方のコツ + +1. **毎日記録**: 日々の気づきを記録して、自分の傾向を知る +2. **タイプ診断**: どのタイプが多いか確認して、自己分析 +3. **コンパニオン育成**: 好感度とレベルを上げて、絆を深める +4. **ランキング確認**: 定期的にTOP10を見て、重要な記憶を振り返る + +--- + +## 📝 注意事項 + +- **APIコスト**: Claude API の使用には料金が発生します + - Haiku: 約$0.25 / 1M tokens(入力) + - Sonnet: 約$3.00 / 1M tokens(入力) +- **プライバシー**: メモリは Anthropic に送信されます +- **容量制限**: デフォルト100件まで(低スコアから自動削除) + +--- + +これで aigpt を存分に楽しめます!🚀 diff --git a/src/ai_interpreter.rs b/src/ai_interpreter.rs index 7f04063..87c52f6 100644 --- a/src/ai_interpreter.rs +++ b/src/ai_interpreter.rs @@ -1,138 +1,31 @@ -use anyhow::{Context, Result}; +use anyhow::Result; -#[cfg(feature = "ai-analysis")] -use openai::{ - chat::{ChatCompletion, ChatCompletionMessage, ChatCompletionMessageRole}, - set_key, -}; - -pub struct AIInterpreter { - #[cfg(feature = "ai-analysis")] - api_key: Option, -} +/// AIInterpreter - Claude Code による解釈を期待する軽量ラッパー +/// +/// このモジュールは外部 AI API を呼び出しません。 +/// 代わりに、Claude Code 自身がコンテンツを解釈し、スコアを計算することを期待します。 +/// +/// 完全にローカルで動作し、API コストはゼロです。 +pub struct AIInterpreter; impl AIInterpreter { pub fn new() -> Self { - #[cfg(feature = "ai-analysis")] - { - let api_key = std::env::var("OPENAI_API_KEY").ok(); - if let Some(ref key) = api_key { - set_key(key.clone()); - } - AIInterpreter { api_key } - } - #[cfg(not(feature = "ai-analysis"))] - { - AIInterpreter {} - } + AIInterpreter } - /// AI解釈: 元のコンテンツを解釈して新しい表現を生成 - #[cfg(feature = "ai-analysis")] - pub async fn interpret_content(&self, content: &str) -> Result { - if self.api_key.is_none() { - return Ok(content.to_string()); - } - - let messages = vec![ - ChatCompletionMessage { - role: ChatCompletionMessageRole::System, - content: Some("あなたは記憶を解釈するAIです。与えられたテキストを解釈し、より深い意味や文脈を抽出してください。元のテキストの本質を保ちながら、新しい視点や洞察を加えてください。".to_string()), - name: None, - function_call: None, - }, - ChatCompletionMessage { - role: ChatCompletionMessageRole::User, - content: Some(format!("以下のテキストを解釈してください:\n\n{}", content)), - name: None, - function_call: None, - }, - ]; - - let chat_completion = ChatCompletion::builder("gpt-3.5-turbo", messages.clone()) - .create() - .await - .context("Failed to create chat completion")?; - - let response = chat_completion - .choices - .first() - .and_then(|choice| choice.message.content.clone()) - .unwrap_or_else(|| content.to_string()); - - Ok(response) - } - - #[cfg(not(feature = "ai-analysis"))] + /// コンテンツをそのまま返す(Claude Code が解釈を担当) pub async fn interpret_content(&self, content: &str) -> Result { Ok(content.to_string()) } - /// 心理判定: テキストの重要度を0.0-1.0のスコアで評価 - #[cfg(feature = "ai-analysis")] - pub async fn calculate_priority_score(&self, content: &str, user_context: Option<&str>) -> Result { - if self.api_key.is_none() { - return Ok(0.5); // デフォルトスコア - } - - let context_info = user_context - .map(|ctx| format!("\n\nユーザーコンテキスト: {}", ctx)) - .unwrap_or_default(); - - let messages = vec![ - ChatCompletionMessage { - role: ChatCompletionMessageRole::System, - content: Some(format!( - "あなたは記憶の重要度を評価するAIです。以下の基準で0.0-1.0のスコアをつけてください:\n\ - - 感情的インパクト (0.0-0.25)\n\ - - ユーザーとの関連性 (0.0-0.25)\n\ - - 新規性・独自性 (0.0-0.25)\n\ - - 実用性 (0.0-0.25)\n\n\ - スコアのみを小数で返してください。例: 0.75{}", context_info - )), - name: None, - function_call: None, - }, - ChatCompletionMessage { - role: ChatCompletionMessageRole::User, - content: Some(format!("以下のテキストの重要度を評価してください:\n\n{}", content)), - name: None, - function_call: None, - }, - ]; - - let chat_completion = ChatCompletion::builder("gpt-3.5-turbo", messages.clone()) - .create() - .await - .context("Failed to create chat completion")?; - - let response = chat_completion - .choices - .first() - .and_then(|choice| choice.message.content.clone()) - .unwrap_or_else(|| "0.5".to_string()); - - // スコアを抽出(小数を含む数字) - let score = response - .trim() - .parse::() - .unwrap_or(0.5) - .min(1.0) - .max(0.0); - - Ok(score) - } - - #[cfg(not(feature = "ai-analysis"))] + /// デフォルトスコアを返す(Claude Code が実際のスコアを決定) pub async fn calculate_priority_score(&self, _content: &str, _user_context: Option<&str>) -> Result { - Ok(0.5) // デフォルトスコア + Ok(0.5) // デフォルト値 } - /// AI解釈と心理判定を同時に実行 - pub async fn analyze(&self, content: &str, user_context: Option<&str>) -> Result<(String, f32)> { - let interpreted = self.interpret_content(content).await?; - let score = self.calculate_priority_score(content, user_context).await?; - Ok((interpreted, score)) + /// 解釈とスコアリングを Claude Code に委ねる + pub async fn analyze(&self, content: &str, _user_context: Option<&str>) -> Result<(String, f32)> { + Ok((content.to_string(), 0.5)) } } diff --git a/src/mcp/base.rs b/src/mcp/base.rs index 243c5d9..e47b8eb 100644 --- a/src/mcp/base.rs +++ b/src/mcp/base.rs @@ -166,13 +166,23 @@ impl BaseMCPServer { }), json!({ "name": "create_memory_with_ai", - "description": "Create a new memory with AI interpretation and priority scoring (with game-style result!)", + "description": "Create a memory with psychological priority scoring! \n\nIMPORTANT: You (Claude) should:\n1. Interpret the user's content and extract deeper meaning\n2. Calculate priority_score (0.0-1.0) based on these criteria:\n - Emotional impact (0.0-0.25)\n - User relevance (0.0-0.25)\n - Novelty/uniqueness (0.0-0.25)\n - Practical utility (0.0-0.25)\n3. Provide the interpreted content and score to this tool\n4. The system will show a game-style result with rarity, type, and XP!", "inputSchema": { "type": "object", "properties": { "content": { "type": "string", - "description": "Content of the memory" + "description": "Original user content" + }, + "interpreted_content": { + "type": "string", + "description": "Your interpretation of the content (extract deeper meaning)" + }, + "priority_score": { + "type": "number", + "description": "Priority score 0.0-1.0 (sum of 4 criteria, each 0.0-0.25)", + "minimum": 0.0, + "maximum": 1.0 }, "user_context": { "type": "string", @@ -183,7 +193,7 @@ impl BaseMCPServer { "description": "Show game-style result (default: true)" } }, - "required": ["content"] + "required": ["content", "interpreted_content", "priority_score"] } }), json!({ @@ -379,10 +389,18 @@ impl BaseMCPServer { // AI解釈付きメモリ作成 async fn tool_create_memory_with_ai(&mut self, arguments: &Value) -> Value { let content = arguments["content"].as_str().unwrap_or(""); + let interpreted_content = arguments["interpreted_content"].as_str().unwrap_or(content); + let priority_score = arguments["priority_score"].as_f64().unwrap_or(0.5) as f32; let user_context = arguments["user_context"].as_str(); let game_mode = arguments["game_mode"].as_bool().unwrap_or(true); - match self.memory_manager.create_memory_with_ai(content, user_context).await { + // Claude Code から受け取った解釈とスコアでメモリを作成 + match self.memory_manager.create_memory_with_interpretation( + content, + interpreted_content, + priority_score, + user_context + ) { Ok(id) => { // 作成したメモリを取得して詳細情報を返す if let Some(memory) = self.memory_manager.get_memory(&id) { @@ -408,13 +426,13 @@ impl BaseMCPServer { }, "game_result": result, "shareable_text": shareable, - "message": "Memory created with AI interpretation and priority scoring" + "message": "Memory created with Claude Code's interpretation and priority scoring!" }) } else { json!({ "success": true, "id": id, - "message": "Memory created with AI interpretation" + "message": "Memory created" }) } } diff --git a/src/memory.rs b/src/memory.rs index 2ce5f79..acc08b9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -128,7 +128,7 @@ impl MemoryManager { Ok(id) } - /// AI解釈と心理判定を使った記憶作成 + /// AI解釈と心理判定を使った記憶作成(後方互換性のため残す) pub async fn create_memory_with_ai( &mut self, content: &str, @@ -163,6 +163,37 @@ impl MemoryManager { Ok(id) } + /// Claude Code から解釈とスコアを受け取ってメモリを作成 + pub fn create_memory_with_interpretation( + &mut self, + content: &str, + interpreted_content: &str, + priority_score: f32, + user_context: Option<&str>, + ) -> Result { + let id = Uuid::new_v4().to_string(); + let now = Utc::now(); + + let memory = Memory { + id: id.clone(), + content: content.to_string(), + interpreted_content: interpreted_content.to_string(), + priority_score: priority_score.max(0.0).min(1.0), // 0.0-1.0 に制限 + user_context: user_context.map(|s| s.to_string()), + created_at: now, + updated_at: now, + }; + + self.memories.insert(id.clone(), memory); + + // 容量制限チェック + self.prune_memories_if_needed()?; + + self.save_data()?; + + Ok(id) + } + pub fn update_memory(&mut self, id: &str, content: &str) -> Result<()> { if let Some(memory) = self.memories.get_mut(id) { memory.content = content.to_string();