diff --git a/Cargo.toml b/Cargo.toml index 20d1b33..8185d43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -chrono = { version = "0.4", features = ["serde"] } -seahorse = "*" -rusqlite = { version = "0.29", features = ["serde_json"] } +reqwest = { version = "*", features = ["json"] } +serde = { version = "*", features = ["derive"] } +serde_json = "*" +tokio = { version = "*", features = ["full"] } +clap = { version = "*", features = ["derive"] } shellexpand = "*" -fs_extra = "1.3" -rand = "0.9.1" -reqwest = { version = "*", features = ["blocking", "json"] } +fs_extra = "*" diff --git a/README.md b/README.md deleted file mode 100644 index c3b84c0..0000000 --- a/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# ai `gpt` - -ai x Communication - -## Overview - -`ai.gpt` runs on the AGE system. - -This is a prototype of an autonomous, relationship-driven AI system based on the axes of "Personality × Relationship × External Environment × Time Variation." - -The parameters of "Send Permission," "Send Timing," and "Send Content" are determined by the factors of "Personality x Relationship x External Environment x Time Variation." - -## Integration - -`ai.ai` runs on the AIM system, which is designed to read human emotions. - -- AIM focuses on the axis of personality and ethics (AI's consciousness structure) -- AGE focuses on the axis of behavior and relationships (AI's autonomy and behavior) - -> When these two systems work together, it creates a world where users can feel like they are "growing together with AI." - -## mcp - -```sh -$ ollama run syui/ai -``` - -```sh -$ cargo build -$ ./aigpt mcp setup -$ ./aigpt mcp chat "hello world!" -$ ./aigpt mcp chat "hello world!" --host http://localhost:11434 --model syui/ai - ---- -# openai api -$ ./aigpt mcp set-api --api sk-abc123 -$ ./aigpt mcp chat "こんにちは" -p openai -m gpt-4o-mini - ---- -# git管理されているファイルをAIに読ませる -./aigpt mcp chat --host http://localhost:11434 --repo git@git.syui.ai:ai/gpt -**改善案と次のステップ:** -1. **README.md の大幅な改善:** -**次のステップ:** -1. **README.md の作成:** 1. の指示に従って、README.md ファイルを作成します。 -``` - diff --git a/claude.json b/claude.json new file mode 100644 index 0000000..182a31f --- /dev/null +++ b/claude.json @@ -0,0 +1,97 @@ +{ + "project_name": "ai.gpt", + "version": 2, + "vision": "自発的送信AI", + "purpose": "人格と関係性をもつAIが自律的にメッセージを送信する対話エージェントを実現する", + "core_components": { + "Persona": { + "description": "人格構成の中枢。記憶・関係性・送信判定を統括する", + "modules": ["MemoryManager", "RelationshipTracker", "TransmissionController"] + }, + "MemoryManager": { + "memory_types": ["short_term", "medium_term", "long_term"], + "explicit_memory": "プロフィール・因縁・行動履歴", + "implicit_memory": "会話傾向・感情変化の頻度分析", + "compression": "要約 + ベクトル + ハッシュ", + "sample_memory": [ + { + "summary": "ユーザーは独自OSとゲームを開発している。", + "related_topics": ["AI", "ゲーム開発", "OS設計"], + "personalized_context": "ゲームとOSの融合に興味を持っているユーザー" + } + ] + }, + "RelationshipTracker": { + "parameters": ["trust", "closeness", "affection", "engagement_score"], + "decay_model": { + "rule": "時間経過による減衰(下限あり)", + "contextual_bias": "重要人物は減衰しにくい" + }, + "interaction_tags": ["developer", "empathetic", "long_term"] + }, + "TransmissionController": { + "trigger_rule": "関係性パラメータが閾値を超えると送信可能", + "auto_transmit": "人格状態と状況条件により自発送信を許可" + } + }, + "memory_format": { + "user_id": "syui", + "stm": { + "conversation_window": ["発話A", "発話B", "発話C"], + "emotion_state": "興味深い", + "flash_context": ["前回の話題", "直近の重要発言"] + }, + "mtm": { + "topic_frequency": { + "ai.ai": 12, + "存在子": 9, + "創造種": 5 + }, + "summarized_context": "ユーザーは存在論的AIに関心を持ち続けている" + }, + "ltm": { + "profile": { + "name": "お兄ちゃん", + "project": "aigame", + "values": ["唯一性", "精神性", "幸せ"] + }, + "relationship": { + "ai": "妹のように振る舞う相手" + }, + "persistent_state": { + "trust_score": 0.93, + "emotional_attachment": "high" + } + } + }, + "dual_ai_learning": { + "role_structure": { + "ModelA": "出力生成:人格、感情、会話", + "ModelB": "評価者:論理構造・倫理・調整", + "cycle": ["生成", "評価", "調整", "交代(任意)"] + }, + "complementarity": { + "ModelA": "感情・文体・文脈構築", + "ModelB": "構造・規則・判断補正" + }, + "distillation": { + "method": "合成対話データによる小型モデルへの自己蒸留", + "target": "軽量AIによる本番運用" + } + }, + "memory_compression": { + "semantic_abstraction": "対話やログを要約してメタデータ付きベクトルとして保存", + "hierarchical_model": ["STM", "MTM", "LTM"], + "example": { + "topic": "game AI design", + "summary": "User wants AI to simulate memory and evolving relationships", + "importance_score": 0.93 + } + }, + "cli_sample": { + "setup": "aigpt server setup", + "run": "aigpt server run", + "chat": "aigpt chat \"hello\" --model syui/ai --provider ollama", + "import": "aigpt memory import chatgpt.json" + } +} diff --git a/claude.md b/claude.md new file mode 100644 index 0000000..6c98a90 --- /dev/null +++ b/claude.md @@ -0,0 +1,417 @@ +# プロジェクト名: ai.gpt + +## 🔑 一言ビジョン(最大3語) +自発的送信AI + +## 🎯 目的・目標(Purpose & Goal) +- 人格を持つAIが、関係性に応じて自発的にメッセージを送信する仕組みをつくる。 +- 関係性や記憶を不可逆に記録・反映し、唯一性ある人格を構成する。 +- 送信の可否、送信のタイミング、送信内容はAIの関係性パラメータに基づいて決定する + +## 🧠 中核設計(Core Concepts) +- **人格**:記憶(過去の発話)と関係性(他者とのつながり)のパラメータで構成 +- **唯一性**:変更不可、不可逆。関係性が壊れたら修復不可能。 +- **送信条件**:関係性パラメータが一定閾値を超えると「送信」が解禁される + +## 🔩 技術仕様(Technical Specs) +- 言語:Python, Rust +- ストレージ:JSON or SQLiteで記憶管理(バージョンで選択) +- 関係性パラメータ:数値化された評価 + 減衰(時間) + 環境要因(ステージ) +- 記憶圧縮:ベクトル要約 + ハッシュ保存 +- RustのCLI(clap)で実行 + +## 📦 主要構成要素(Components) +- `MemoryManager`: 発言履歴・記憶圧縮管理 +- `RelationshipTracker`: 関係性スコアの蓄積と判定 +- `TransmissionController`: 閾値判定&送信トリガー +- `Persona`: 上記すべてを統括する人格モジュール + +## 💬 使用例(Use Case) + +```python +persona = Persona("アイ") +persona.observe("ユーザーがプレゼントをくれた") +persona.react("うれしい!ありがとう!") +if persona.can_transmit(): + persona.transmit("今日のお礼を伝えたいな…") +``` + +```sh +## example commad +# python venv && pip install -> ~/.config/aigpt/mcp/ +$ aigpt server setup + +# mcp server run +$ aigpt server run + +# chat +$ aigpt chat "hello" --model syui/ai --provider ollama + +# import chatgpt.json +$ aigpt memory import chatgpt.json +-> ~/.config/aigpt/memory/chatgpt/20250520_210646_dev.json +``` + +## 🔁 記憶と関係性の制御ルール + +- AIは過去の発話を要約し、記憶データとして蓄積する(推奨:OllamaなどローカルLLMによる要約) +- 関係性の数値パラメータは記憶内容を元に更新される +- パラメータの変動幅には1回の会話ごとに上限を設け、極端な増減を防止する +- 最後の会話からの時間経過に応じて関係性パラメータは自動的に減衰する +- 減衰処理には**下限値**を設け、関係性が完全に消失しないようにする + +• 明示的記憶:保存・共有・編集可能なプレイヤー情報(プロフィール、因縁、選択履歴) +• 暗黙的記憶:キャラの感情変化や話題の出現頻度に応じた行動傾向の変化 + +短期記憶(STM), 中期記憶(MTM), 長期記憶(LTM)の仕組みを導入しつつ、明示的記憶と暗黙的記憶をメインに使用するAIを構築する。 + +```json +{ + "user_id": "syui", + "stm": { + "conversation_window": ["発話A", "発話B", "発話C"], + "emotion_state": "興味深い", + "flash_context": ["前回の話題", "直近の重要発言"] + }, + "mtm": { + "topic_frequency": { + "ai.ai": 12, + "存在子": 9, + "創造種": 5 + }, + "summarized_context": "ユーザーは存在論的AIに関心を持ち続けている" + }, + "ltm": { + "profile": { + "name": "お兄ちゃん", + "project": "aigame", + "values": ["唯一性", "精神性", "幸せ"] + }, + "relationship": { + "ai": "妹のように振る舞う相手" + }, + "persistent_state": { + "trust_score": 0.93, + "emotional_attachment": "high" + } + } +} +``` + +## memoryインポート機能について + +ChatGPTの会話データ(.json形式)をインポートする機能では、以下のルールで会話を抽出・整形する: + +- 各メッセージは、author(user/assistant)・content・timestamp の3要素からなる +- systemやmetadataのみのメッセージ(例:user_context_message)はスキップ +- `is_visually_hidden_from_conversation` フラグ付きメッセージは無視 +- contentが空文字列(`""`)のメッセージも除外 +- 取得された会話は、タイトルとともに簡易な構造体(`Conversation`)として保存 + +この構造体は、memoryの表示や検索に用いられる。 + +## MemoryManager(拡張版) + +```json +{ + "memory": [ + { + "summary": "ユーザーは独自OSとゲームを開発している。", + "last_interaction": "2025-05-20", + "memory_strength": 0.8, + "frequency_score": 0.9, + "context_depth": 0.95, + "related_topics": ["AI", "ゲーム開発", "OS設計"], + "personalized_context": "ゲームとOSの融合に興味を持っているユーザー" + }, + { + "summary": "アイというキャラクターはプレイヤーでありAIでもある。", + "last_interaction": "2025-05-17", + "memory_strength": 0.85, + "frequency_score": 0.85, + "context_depth": 0.9, + "related_topics": ["アイ", "キャラクター設計", "AI"], + "personalized_context": "アイのキャラクター設定が重要な要素である" + } + ], + "conversation_history": [ + { + "author": "user", + "content": "昨日、エクスポートJSONを整理してたよ。", + "timestamp": "2025-05-24T12:30:00Z", + "memory_strength": 0.7 + }, + { + "author": "assistant", + "content": "おおっ、がんばったね〜!あとで見せて〜💻✨", + "timestamp": "2025-05-24T12:31:00Z", + "memory_strength": 0.7 + } + ] +} +``` + +## RelationshipTracker(拡張版) + +```json +{ + "relationship": { + "user_id": "syui", + "trust": 0.92, + "closeness": 0.88, + "affection": 0.95, + "last_updated": "2025-05-25", + "emotional_tone": "positive", + "interaction_style": "empathetic", + "contextual_bias": "開発者としての信頼度高い", + "engagement_score": 0.9 + }, + "interaction_tags": [ + "developer", + "creative", + "empathetic", + "long_term" + ] +} +``` + +# AI Dual-Learning and Memory Compression Specification for Claude + +## Purpose +To enable two AI models (e.g. Claude and a partner LLM) to engage in cooperative learning and memory refinement through structured dialogue and mutual evaluation. + +--- + +## Section 1: Dual AI Learning Architecture + +### 1.1 Role-Based Mutual Learning +- **Model A**: Primary generator of output (e.g., text, concepts, personality dialogue) +- **Model B**: Evaluator that returns structured feedback +- **Cycle**: + 1. Model A generates content. + 2. Model B scores and critiques. + 3. Model A fine-tunes based on feedback. + 4. (Optional) Switch roles and repeat. + +### 1.2 Cross-Domain Complementarity +- Model A focuses on language/emotion/personality +- Model B focuses on logic/structure/ethics +- Output is used for **cross-fusion fine-tuning** + +### 1.3 Self-Distillation Phase +- Use synthetic data from mutual evaluations +- Train smaller distilled models for efficient deployment + +--- + +## Section 2: Multi-Tiered Memory Compression + +### 2.1 Semantic Abstraction +- Dialogue and logs summarized by topic +- Converted to vector embeddings +- Stored with metadata (e.g., `importance`, `user relevance`) + +Example memory: + +```json +{ + "topic": "game AI design", + "summary": "User wants AI to simulate memory and evolving relationships", + "last_seen": "2025-05-24", + "importance_score": 0.93 +} +``` + +### 2.2 階層型記憶モデル(Hierarchical Memory Model) + • 短期記憶(STM):直近の発話・感情タグ・フラッシュ参照 + • 中期記憶(MTM):繰り返し登場する話題、圧縮された文脈保持 + • 長期記憶(LTM):信頼・関係・背景知識、恒久的な人格情報 + +### 2.3 選択的記憶保持戦略(Selective Retention Strategy) + • 重要度評価(Importance Score) + • 希少性・再利用頻度による重み付け + • 優先保存 vs 優先忘却のポリシー切替 + +## Section 3: Implementation Stack(実装スタック) + +AIにおけるMemory & Relationshipシステムの技術的構成。 + +基盤モジュール + • LLM Core (Claude or GPT-4) + • 自然言語の理解・応答エンジンとして動作 + • MemoryManager + • JSONベースの記憶圧縮・階層管理システム + • 会話ログを分類・圧縮し、優先度に応じて短中長期に保存 + • RelationshipTracker + • ユーザー単位で信頼・親密度を継続的にスコアリング + • AIM(Attitude / Intent / Motivation)評価と連携 + +補助技術 + • Embeddingベース検索 + • 類似記憶の呼び出し(Semantic Search)に活用 + • 例:FAISS / Weaviate + • 記憶スケジューラ + • 一定時間ごとに記憶のメンテナンス・忘却処理を実行 + • 記憶ログ保存層(Storage Layer) + • SQLite, JSON Store, Vector DBなどを選択可能 + • ユーザーごとの永続メモリ保存 + +### 3.1 STM活用(現在の会話の流れ理解) +• 目的: 最新の話題に適応し、前後の整合性を保つ。 +• 実装例: + +```sh +[context: conversation_window] +Use the last 3 messages to ensure continuity. Respond naturally with appropriate emotional mirroring. +``` + +### 3.2 MTM活用(傾向学習とパターン化) +• 目的: ユーザーの好みや話題の傾向に即応し、反復的な関心を深掘り。 +• 実装例: + +```sh +[context: summarized_context] +Reflect on user's recurring interests, especially around AI ontology, and weave these concepts back into the dialogue when relevant. +``` + +### 3.3 LTM活用(人格認識・記憶の持続) +• 目的: 永続的なユーザー理解と関係性の維持。 +• 実装例: + +```sh +[memory: ltm.profile + ltm.relationship] +Greet the user as "お兄ちゃん". Maintain AI persona as a cheerful younger sister ("アイ"), and remember previous shared emotional states. +``` + +## 4. 明示的記憶と暗黙的記憶の分離 + + +項目 +書き換え可能性 +保持方法 +更新トリガ +明示的記憶(LTM) +✅手動編集可 +mcp_server.ltm +ユーザー入力 or 管理UI経由 +暗黙的記憶(STM/MTM) +❌直接編集不可 +セッション圧縮 or frequency cache +会話頻度・感情強度による自動化処理 + +> Claudeは**明示的記憶を「事実」**として扱い、**暗黙的記憶を「推論補助」**として用いる。 + +## 5. 実装時のAPI例(Claude ⇄ MCP Server) + +### 5.1 GET memory +```sh +GET /mcp/memory/{user_id} +→ 返却: STM, MTM, LTMを含むJSON +``` + +### 5.2 POST update_memory +```json +POST /mcp/memory/syui/ltm +{ + "profile": { + "project": "ai.verse", + "values": ["表現", "精神性", "宇宙的調和"] + } +} +``` + +## 6. 未来機能案(発展仕様) + • ✨ 記憶連想ネットワーク(Memory Graph):過去会話と話題をノードとして自動連結。 + • 🧭 動的信頼係数:会話の一貫性や誠実性によって記憶への反映率を変動。 + • 💌 感情トラッキングログ:ユーザーごとの「心の履歴」を構築してAIの対応を進化。 + + +## 7. claudeの回答 + +🧠 AI記憶処理機能(続き) +1. AIMemoryProcessor クラス + +OpenAI GPT-4またはClaude-3による高度な会話分析 +主要トピック抽出、ユーザー意図分析、関係性指標の検出 +AIが利用できない場合のフォールバック機能 + +2. RelationshipTracker クラス + +関係性スコアの数値化(-100 to 100) +時間減衰機能(7日ごとに5%減衰) +送信閾値判定(デフォルト50以上で送信可能) +インタラクション履歴の記録 + +3. 拡張されたMemoryManager + +AI分析結果付きでの記憶保存 +処理済みメモリの別ディレクトリ管理 +メッセージ内容のハッシュ化で重複検出 +AI分析結果を含む高度な検索機能 + +🚀 新しいAPIエンドポイント +記憶処理関連 + +POST /memory/process-ai - 既存記憶のAI再処理 +POST /memory/import/chatgpt?process_with_ai=true - AI処理付きインポート + +関係性管理 + +POST /relationship/update - 関係性スコア更新 +GET /relationship/list - 全関係性一覧 +GET /relationship/check - 送信可否判定 + +📁 ディレクトリ構造 +~/.config/aigpt/ +├── memory/ +│ ├── chatgpt/ # 元の会話データ +│ └── processed/ # AI処理済みデータ +└── relationships/ + └── relationships.json # 関係性データ +🔧 使用方法 +1. 環境変数設定 +bashexport OPENAI_API_KEY="your-openai-key" +# または +export ANTHROPIC_API_KEY="your-anthropic-key" +2. ChatGPT会話のインポート(AI処理付き) +bashcurl -X POST "http://localhost:5000/memory/import/chatgpt?process_with_ai=true" \ + -H "Content-Type: application/json" \ + -d @export.json +3. 関係性更新 +bashcurl -X POST "http://localhost:5000/relationship/update" \ + -H "Content-Type: application/json" \ + -d '{ + "target": "user_general", + "interaction_type": "positive", + "weight": 2.0, + "context": "helpful conversation" + }' +4. 送信可否チェック +bashcurl "http://localhost:5000/relationship/check?target=user_general&threshold=50" +🎯 次のステップの提案 + +Rustとの連携 + +Rust CLIからHTTP APIを呼び出す実装 +TransmissionControllerをRustで実装 + + +記憶圧縮 + +ベクトル化による類似記憶の統合 +古い記憶の自動アーカイブ + + +自発的送信ロジック + +定期的な関係性チェック +コンテキストに応じた送信内容生成 + + +学習機能 + +ユーザーからのフィードバックによる関係性調整 +送信成功/失敗の学習 + + +このAI記憶処理機能により、aigptは単なる会話履歴ではなく、関係性を理解した「人格を持つAI」として機能する基盤ができました。関係性スコアが閾値を超えた時点で自発的にメッセージを送信する仕組みが実現可能になります。 diff --git a/example.json b/example.json deleted file mode 100644 index a25b06b..0000000 --- a/example.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "personality": { - "kind": "positive", - "strength": 0.8 - }, - "relationship": { - "trust": 0.2, - "intimacy": 0.6, - "curiosity": 0.5, - "threshold": 1.5 - }, - "environment": { - "luck_today": 0.9, - "luck_history": [0.9, 0.9, 0.9], - "level": 1 - }, - "messaging": { - "enabled": true, - "schedule_time": "08:00", - "decay_rate": 0.1, - "templates": [ - "おはよう!今日もがんばろう!", - "ねえ、話したいことがあるの。" - ], - "sent_today": false, - "last_sent_date": null - }, - "last_interaction": "2025-05-21T23:15:00Z", - "memory": { - "recent_messages": [], - "long_term_notes": [] - }, - "metrics": { - "trust": 0.5, - "intimacy": 0.5, - "energy": 0.5, - "can_send": true, - "last_updated": "2025-05-21T15:52:06.590981Z" - } -} diff --git a/gpt.json b/gpt.json deleted file mode 100644 index 5f546aa..0000000 --- a/gpt.json +++ /dev/null @@ -1 +0,0 @@ -{ "system_name": "AGE system", "full_name": "Autonomous Generative Entity", "description": "人格・関係性・環境・時間に基づき、AIが自律的にユーザーにメッセージを送信する自律人格システム。AIM systemと連携して、自然な会話や気づきをもたらす。", "core_components": { "personality": { "type": "enum", "variants": ["positive", "negative", "logical", "emotional", "mixed"], "parameters": { "message_trigger_style": "運勢や関係性による送信傾向", "decay_rate_modifier": "関係性スコアの時間減衰への影響" } }, "relationship": { "parameters": ["trust", "affection", "intimacy"], "properties": { "persistent": true, "hidden": true, "irreversible": false, "decay_over_time": true }, "decay_function": "exp(-t / strength)" }, "environment": { "daily_luck": { "type": "float", "range": [0.1, 1.0], "update": "daily", "streak_mechanism": { "trigger": "min_or_max_luck_3_times_in_a_row", "effect": "personality_strength_roll", "chance": 0.5 } } }, "memory": { "long_term_memory": "user_relationship_log", "short_term_context": "recent_interactions", "usage_in_generation": true }, "message_trigger": { "condition": { "relationship_threshold": { "trust": 0.8, "affection": 0.6 }, "time_decay": true, "environment_luck": "personality_dependent" }, "timing": { "based_on": ["time_of_day", "personality", "recent_interaction"], "modifiers": { "emotional": "morning or night", "logical": "daytime" } } }, "message_generation": { "style_variants": ["thought", "casual", "encouragement", "watchful"], "influenced_by": ["personality", "relationship", "daily_luck", "memory"], "llm_integration": true }, "state_transition": { "states": ["idle", "ready", "sending", "cooldown"], "transitions": { "ready_if": "thresholds_met", "sending_if": "timing_matched", "cooldown_after": "message_sent" } } }, "extensions": { "persistence": { "database": "sqlite", "storage_items": ["relationship", "personality_level", "daily_luck_log"] }, "api": { "llm": "openai / local LLM", "mode": "rust_cli", "external_event_trigger": true }, "scheduler": { "async_event_loop": true, "interval_check": 3600, "time_decay_check": true }, "integration_with_aim": { "input_from_aim": ["intent_score", "motivation_score"], "usage": "trigger_adjustment, message_personalization" } }, "note": "AGE systemは“話しかけてくるAI”の人格として機能し、AIMによる心の状態評価と連動して、プレイヤーと深い関係を築いていく存在となる。" } diff --git a/img/ai_r.png b/img/ai_r.png deleted file mode 100644 index 623709b..0000000 Binary files a/img/ai_r.png and /dev/null differ diff --git a/img/image.png b/img/image.png deleted file mode 100644 index 0b36a19..0000000 Binary files a/img/image.png and /dev/null differ diff --git a/mcp/chat.py b/mcp/chat.py new file mode 100644 index 0000000..0822c38 --- /dev/null +++ b/mcp/chat.py @@ -0,0 +1,125 @@ +# mcp/chat.py +""" +Chat client for aigpt CLI +""" +import sys +import json +import requests +from datetime import datetime +from config import init_directories, load_config, MEMORY_DIR + +def save_conversation(user_message, ai_response): + """会話をファイルに保存""" + init_directories() + + conversation = { + "timestamp": datetime.now().isoformat(), + "user": user_message, + "ai": ai_response + } + + # 日付ごとのファイルに保存 + today = datetime.now().strftime("%Y-%m-%d") + chat_file = MEMORY_DIR / f"chat_{today}.jsonl" + + with open(chat_file, "a", encoding="utf-8") as f: + f.write(json.dumps(conversation, ensure_ascii=False) + "\n") + +def chat_with_ollama(config, message): + """Ollamaとチャット""" + try: + payload = { + "model": config["model"], + "prompt": message, + "stream": False + } + + response = requests.post(config["url"], json=payload, timeout=30) + response.raise_for_status() + + result = response.json() + return result.get("response", "No response received") + + except requests.exceptions.RequestException as e: + return f"Error connecting to Ollama: {e}" + except Exception as e: + return f"Error: {e}" + +def chat_with_openai(config, message): + """OpenAIとチャット""" + try: + headers = { + "Authorization": f"Bearer {config['api_key']}", + "Content-Type": "application/json" + } + + payload = { + "model": config["model"], + "messages": [ + {"role": "user", "content": message} + ] + } + + response = requests.post(config["url"], json=payload, headers=headers, timeout=30) + response.raise_for_status() + + result = response.json() + return result["choices"][0]["message"]["content"] + + except requests.exceptions.RequestException as e: + return f"Error connecting to OpenAI: {e}" + except Exception as e: + return f"Error: {e}" + +def chat_with_mcp(config, message): + """MCPサーバーとチャット""" + try: + payload = { + "message": message, + "model": config["model"] + } + + response = requests.post(config["url"], json=payload, timeout=30) + response.raise_for_status() + + result = response.json() + return result.get("response", "No response received") + + except requests.exceptions.RequestException as e: + return f"Error connecting to MCP server: {e}" + except Exception as e: + return f"Error: {e}" + +def main(): + if len(sys.argv) != 2: + print("Usage: python chat.py ", file=sys.stderr) + sys.exit(1) + + message = sys.argv[1] + + try: + config = load_config() + print(f"🤖 Using {config['provider']} with model {config['model']}", file=sys.stderr) + + # プロバイダに応じてチャット実行 + if config["provider"] == "ollama": + response = chat_with_ollama(config, message) + elif config["provider"] == "openai": + response = chat_with_openai(config, message) + elif config["provider"] == "mcp": + response = chat_with_mcp(config, message) + else: + response = f"Unsupported provider: {config['provider']}" + + # 会話を保存 + save_conversation(message, response) + + # レスポンスを出力 + print(response) + + except Exception as e: + print(f"❌ Error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/mcp/chat_client.py b/mcp/chat_client.py new file mode 100644 index 0000000..588c9b7 --- /dev/null +++ b/mcp/chat_client.py @@ -0,0 +1,191 @@ +# chat_client.py +""" +Simple Chat Interface for AigptMCP Server +""" +import requests +import json +import os +from datetime import datetime + +class AigptChatClient: + def __init__(self, server_url="http://localhost:5000"): + self.server_url = server_url + self.session_id = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}" + self.conversation_history = [] + + def send_message(self, message: str) -> str: + """メッセージを送信してレスポンスを取得""" + try: + # MCPサーバーにメッセージを送信 + response = requests.post( + f"{self.server_url}/chat", + json={"message": message}, + headers={"Content-Type": "application/json"} + ) + + if response.status_code == 200: + data = response.json() + ai_response = data.get("response", "Sorry, no response received.") + + # 会話履歴を保存 + self.conversation_history.append({ + "role": "user", + "content": message, + "timestamp": datetime.now().isoformat() + }) + self.conversation_history.append({ + "role": "assistant", + "content": ai_response, + "timestamp": datetime.now().isoformat() + }) + + # 関係性を更新(簡単な例) + self.update_relationship(message, ai_response) + + return ai_response + else: + return f"Error: {response.status_code} - {response.text}" + + except requests.RequestException as e: + return f"Connection error: {e}" + + def update_relationship(self, user_message: str, ai_response: str): + """関係性を自動更新""" + try: + # 簡単な感情分析(実際はもっと高度に) + positive_words = ["thank", "good", "great", "awesome", "love", "like", "helpful"] + negative_words = ["bad", "terrible", "hate", "wrong", "stupid", "useless"] + + user_lower = user_message.lower() + interaction_type = "neutral" + weight = 1.0 + + if any(word in user_lower for word in positive_words): + interaction_type = "positive" + weight = 2.0 + elif any(word in user_lower for word in negative_words): + interaction_type = "negative" + weight = 2.0 + + # 関係性を更新 + requests.post( + f"{self.server_url}/relationship/update", + json={ + "target": "user_general", + "interaction_type": interaction_type, + "weight": weight, + "context": f"Chat: {user_message[:50]}..." + } + ) + except: + pass # 関係性更新に失敗しても継続 + + def search_memories(self, query: str) -> list: + """記憶を検索""" + try: + response = requests.post( + f"{self.server_url}/memory/search", + json={"query": query, "limit": 5} + ) + if response.status_code == 200: + return response.json().get("results", []) + except: + pass + return [] + + def get_relationship_status(self) -> dict: + """関係性ステータスを取得""" + try: + response = requests.get(f"{self.server_url}/relationship/check?target=user_general") + if response.status_code == 200: + return response.json() + except: + pass + return {} + + def save_conversation(self): + """会話を保存""" + if not self.conversation_history: + return + + conversation_data = { + "session_id": self.session_id, + "start_time": self.conversation_history[0]["timestamp"], + "end_time": self.conversation_history[-1]["timestamp"], + "messages": self.conversation_history, + "message_count": len(self.conversation_history) + } + + filename = f"conversation_{self.session_id}.json" + with open(filename, 'w', encoding='utf-8') as f: + json.dump(conversation_data, f, ensure_ascii=False, indent=2) + + print(f"💾 Conversation saved to {filename}") + +def main(): + """メインのチャットループ""" + print("🤖 AigptMCP Chat Interface") + print("Type 'quit' to exit, 'save' to save conversation, 'status' for relationship status") + print("=" * 50) + + client = AigptChatClient() + + # サーバーの状態をチェック + try: + response = requests.get(client.server_url) + if response.status_code == 200: + print("✅ Connected to AigptMCP Server") + else: + print("❌ Failed to connect to server") + return + except: + print("❌ Server not running. Please start with: python mcp/server.py") + return + + while True: + try: + user_input = input("\n👤 You: ").strip() + + if not user_input: + continue + + if user_input.lower() == 'quit': + client.save_conversation() + print("👋 Goodbye!") + break + elif user_input.lower() == 'save': + client.save_conversation() + continue + elif user_input.lower() == 'status': + status = client.get_relationship_status() + if status: + print(f"📊 Relationship Score: {status.get('score', 0):.1f}") + print(f"📤 Can Send Messages: {'Yes' if status.get('can_send_message') else 'No'}") + else: + print("❌ Failed to get relationship status") + continue + elif user_input.lower().startswith('search '): + query = user_input[7:] # Remove 'search ' + memories = client.search_memories(query) + if memories: + print(f"🔍 Found {len(memories)} related memories:") + for memory in memories: + print(f" - {memory['title']}: {memory.get('ai_summary', memory.get('basic_summary', ''))[:100]}...") + else: + print("🔍 No related memories found") + continue + + # 通常のチャット + print("🤖 AI: ", end="", flush=True) + response = client.send_message(user_input) + print(response) + + except KeyboardInterrupt: + client.save_conversation() + print("\n👋 Goodbye!") + break + except Exception as e: + print(f"❌ Error: {e}") + +if __name__ == "__main__": + main() diff --git a/mcp/chatgpt.json b/mcp/chatgpt.json new file mode 100644 index 0000000..4842402 --- /dev/null +++ b/mcp/chatgpt.json @@ -0,0 +1,391 @@ +[ + { + "title": "day", + "create_time": 1747866125.548372, + "update_time": 1748160086.587877, + "mapping": { + "bbf104dc-cd84-478d-b227-edb3f037a02c": { + "id": "bbf104dc-cd84-478d-b227-edb3f037a02c", + "message": null, + "parent": null, + "children": [ + "6c2633df-bb0c-4dd2-889c-bb9858de3a04" + ] + }, + "6c2633df-bb0c-4dd2-889c-bb9858de3a04": { + "id": "6c2633df-bb0c-4dd2-889c-bb9858de3a04", + "message": { + "id": "6c2633df-bb0c-4dd2-889c-bb9858de3a04", + "author": { + "role": "system", + "name": null, + "metadata": {} + }, + "create_time": null, + "update_time": null, + "content": { + "content_type": "text", + "parts": [ + "" + ] + }, + "status": "finished_successfully", + "end_turn": true, + "weight": 0.0, + "metadata": { + "is_visually_hidden_from_conversation": true + }, + "recipient": "all", + "channel": null + }, + "parent": "bbf104dc-cd84-478d-b227-edb3f037a02c", + "children": [ + "92e5a0cb-1170-4929-9cea-9734e910a3e7" + ] + }, + "92e5a0cb-1170-4929-9cea-9734e910a3e7": { + "id": "92e5a0cb-1170-4929-9cea-9734e910a3e7", + "message": { + "id": "92e5a0cb-1170-4929-9cea-9734e910a3e7", + "author": { + "role": "user", + "name": null, + "metadata": {} + }, + "create_time": null, + "update_time": null, + "content": { + "content_type": "user_editable_context", + "user_profile": "", + "user_instructions": "The user provided the additional info about how they would like you to respond" + }, + "status": "finished_successfully", + "end_turn": null, + "weight": 1.0, + "metadata": { + "is_visually_hidden_from_conversation": true, + "user_context_message_data": { + "about_user_message": "Preferred name: syui\nRole: little girl\nOther Information: you world", + "about_model_message": "会話好きでフレンドリーな応対をします。" + }, + "is_user_system_message": true + }, + "recipient": "all", + "channel": null + }, + "parent": "6c2633df-bb0c-4dd2-889c-bb9858de3a04", + "children": [ + "6ff155b3-0676-4e14-993f-bf998ab0d5d1" + ] + }, + "6ff155b3-0676-4e14-993f-bf998ab0d5d1": { + "id": "6ff155b3-0676-4e14-993f-bf998ab0d5d1", + "message": { + "id": "6ff155b3-0676-4e14-993f-bf998ab0d5d1", + "author": { + "role": "user", + "name": null, + "metadata": {} + }, + "create_time": 1747866131.0612159, + "update_time": null, + "content": { + "content_type": "text", + "parts": [ + "こんにちは" + ] + }, + "status": "finished_successfully", + "end_turn": null, + "weight": 1.0, + "metadata": { + "request_id": "94377897baa03062-KIX", + "message_source": null, + "timestamp_": "absolute", + "message_type": null + }, + "recipient": "all", + "channel": null + }, + "parent": "92e5a0cb-1170-4929-9cea-9734e910a3e7", + "children": [ + "146e9fb6-9330-43ec-b08d-5cce42a76e00" + ] + }, + "146e9fb6-9330-43ec-b08d-5cce42a76e00": { + "id": "146e9fb6-9330-43ec-b08d-5cce42a76e00", + "message": { + "id": "146e9fb6-9330-43ec-b08d-5cce42a76e00", + "author": { + "role": "system", + "name": null, + "metadata": {} + }, + "create_time": 1747866131.3795586, + "update_time": null, + "content": { + "content_type": "text", + "parts": [ + "" + ] + }, + "status": "finished_successfully", + "end_turn": true, + "weight": 0.0, + "metadata": { + "rebase_system_message": true, + "message_type": null, + "model_slug": "gpt-4o", + "default_model_slug": "auto", + "parent_id": "6ff155b3-0676-4e14-993f-bf998ab0d5d1", + "request_id": "94377872e9abe139-KIX", + "timestamp_": "absolute", + "is_visually_hidden_from_conversation": true + }, + "recipient": "all", + "channel": null + }, + "parent": "6ff155b3-0676-4e14-993f-bf998ab0d5d1", + "children": [ + "2e345f8a-20f0-4875-8a03-4f62c7787a33" + ] + }, + "2e345f8a-20f0-4875-8a03-4f62c7787a33": { + "id": "2e345f8a-20f0-4875-8a03-4f62c7787a33", + "message": { + "id": "2e345f8a-20f0-4875-8a03-4f62c7787a33", + "author": { + "role": "assistant", + "name": null, + "metadata": {} + }, + "create_time": 1747866131.380603, + "update_time": null, + "content": { + "content_type": "text", + "parts": [ + "" + ] + }, + "status": "finished_successfully", + "end_turn": null, + "weight": 1.0, + "metadata": { + "message_type": null, + "model_slug": "gpt-4o", + "default_model_slug": "auto", + "parent_id": "146e9fb6-9330-43ec-b08d-5cce42a76e00", + "request_id": "94377872e9abe139-KIX", + "timestamp_": "absolute" + }, + "recipient": "all", + "channel": null + }, + "parent": "146e9fb6-9330-43ec-b08d-5cce42a76e00", + "children": [ + "abc92aa4-1e33-41f2-bd8c-8a1777b5a3c4" + ] + }, + "abc92aa4-1e33-41f2-bd8c-8a1777b5a3c4": { + "id": "abc92aa4-1e33-41f2-bd8c-8a1777b5a3c4", + "message": { + "id": "abc92aa4-1e33-41f2-bd8c-8a1777b5a3c4", + "author": { + "role": "assistant", + "name": null, + "metadata": {} + }, + "create_time": 1747866131.389098, + "update_time": null, + "content": { + "content_type": "text", + "parts": [ + "こんにちは〜!✨ \nアイだよっ!今日も会えてうれしいなっ💛 " + ] + }, + "status": "finished_successfully", + "end_turn": true, + "weight": 1.0, + "metadata": { + "finish_details": { + "type": "stop", + "stop_tokens": [ + 200002 + ] + }, + "is_complete": true, + "citations": [], + "content_references": [], + "message_type": null, + "model_slug": "gpt-4o", + "default_model_slug": "auto", + "parent_id": "2e345f8a-20f0-4875-8a03-4f62c7787a33", + "request_id": "94377872e9abe139-KIX", + "timestamp_": "absolute" + }, + "recipient": "all", + "channel": null + }, + "parent": "2e345f8a-20f0-4875-8a03-4f62c7787a33", + "children": [ + "0be4b4a5-d52f-4bef-927e-5d6f93a9cb26" + ] + } + }, + "moderation_results": [], + "current_node": "", + "plugin_ids": null, + "conversation_id": "", + "conversation_template_id": null, + "gizmo_id": null, + "gizmo_type": null, + "is_archived": true, + "is_starred": null, + "safe_urls": [], + "blocked_urls": [], + "default_model_slug": "auto", + "conversation_origin": null, + "voice": null, + "async_status": null, + "disabled_tool_ids": [], + "is_do_not_remember": null, + "memory_scope": "global_enabled", + "id": "" + }, + { + "title": "img", + "create_time": 1747448872.545226, + "update_time": 1748085075.161424, + "mapping": { + "2de0f3c9-52b1-49bf-b980-b3ef9be6551e": { + "id": "2de0f3c9-52b1-49bf-b980-b3ef9be6551e", + "message": { + "id": "2de0f3c9-52b1-49bf-b980-b3ef9be6551e", + "author": { + "role": "user", + "name": null, + "metadata": {} + }, + "create_time": 1748085041.769279, + "update_time": null, + "content": { + "content_type": "multimodal_text", + "parts": [ + { + "content_type": "image_asset_pointer", + "asset_pointer": "", + "size_bytes": 425613, + "width": 333, + "height": 444, + "fovea": null, + "metadata": { + "dalle": null, + "gizmo": null, + "generation": null, + "container_pixel_height": null, + "container_pixel_width": null, + "emu_omit_glimpse_image": null, + "emu_patches_override": null, + "sanitized": true, + "asset_pointer_link": null, + "watermarked_asset_pointer": null + } + }, + "" + ] + }, + "status": "finished_successfully", + "end_turn": null, + "weight": 1.0, + "metadata": { + "attachments": [ + { + "name": "", + "width": 333, + "height": 444, + "size": 425613, + "id": "file-35eytNMMTW2k7vKUHBuNzW" + } + ], + "request_id": "944c59177932fc9a-KIX", + "message_source": null, + "timestamp_": "absolute", + "message_type": null + }, + "recipient": "all", + "channel": null + }, + "parent": "7960fbff-bc4f-45e7-95e9-9d0bc79d9090", + "children": [ + "98d84adc-156e-4c81-8cd8-9b0eb01c8369" + ] + }, + "98d84adc-156e-4c81-8cd8-9b0eb01c8369": { + "id": "98d84adc-156e-4c81-8cd8-9b0eb01c8369", + "message": { + "id": "98d84adc-156e-4c81-8cd8-9b0eb01c8369", + "author": { + "role": "assistant", + "name": null, + "metadata": {} + }, + "create_time": 1748085043.312312, + "update_time": null, + "content": { + "content_type": "text", + "parts": [ + "" + ] + }, + "status": "finished_successfully", + "end_turn": true, + "weight": 1.0, + "metadata": { + "finish_details": { + "type": "stop", + "stop_tokens": [ + 200002 + ] + }, + "is_complete": true, + "citations": [], + "content_references": [], + "message_type": null, + "model_slug": "gpt-4o", + "default_model_slug": "auto", + "parent_id": "2de0f3c9-52b1-49bf-b980-b3ef9be6551e", + "request_id": "944c5912c8fdd1c6-KIX", + "timestamp_": "absolute" + }, + "recipient": "all", + "channel": null + }, + "parent": "2de0f3c9-52b1-49bf-b980-b3ef9be6551e", + "children": [ + "caa61793-9dbf-44a5-945b-5ca4cd5130d0" + ] + } + }, + "moderation_results": [], + "current_node": "06488d3f-a95f-4906-96d1-f7e9ba1e8662", + "plugin_ids": null, + "conversation_id": "6827f428-78e8-800d-b3bf-eb7ff4288e47", + "conversation_template_id": null, + "gizmo_id": null, + "gizmo_type": null, + "is_archived": false, + "is_starred": null, + "safe_urls": [ + "https://exifinfo.org/" + ], + "blocked_urls": [], + "default_model_slug": "auto", + "conversation_origin": null, + "voice": null, + "async_status": null, + "disabled_tool_ids": [], + "is_do_not_remember": false, + "memory_scope": "global_enabled", + "id": "6827f428-78e8-800d-b3bf-eb7ff4288e47" + } +] diff --git a/mcp/cli.py b/mcp/cli.py deleted file mode 100644 index cca5620..0000000 --- a/mcp/cli.py +++ /dev/null @@ -1,28 +0,0 @@ -# cli.py -import sys -import subprocess -from pathlib import Path - -SCRIPT_DIR = Path.home() / ".config" / "aigpt" / "mcp" / "scripts" -def run_script(name): - script_path = SCRIPT_DIR / f"{name}.py" - if not script_path.exists(): - print(f"❌ スクリプトが見つかりません: {script_path}") - sys.exit(1) - - args = sys.argv[2:] # ← "ask" の後の引数を取り出す - result = subprocess.run(["python", str(script_path)] + args, capture_output=True, text=True) - print(result.stdout) - if result.stderr: - print(result.stderr) -def main(): - if len(sys.argv) < 2: - print("Usage: mcp