Add gamification: Make memory scoring fun like psychological tests
Key insight from user: "It's all about presentation" 心理テストや占いがSNSで流行るのは「見せ方」の問題 ## New Features ### 🎮 Game-Style Result Display When creating memories with AI, users now get: - Visual score display (COMMON → LEGENDARY) - Personality type diagnosis (革新者、哲学者、実務家、etc.) - Detailed breakdown bars (感情/関連性/新規性/実用性) - XP rewards system - Shareable text for SNS Example output: ``` ╔══════════════════════════════════════╗ ║ 🎲 メモリースコア判定 ║ ╚══════════════════════════════════════╝ 🟣 EPIC 85点 💡 【革新者】 💎 XP獲得: +850 XP ``` ### 🏆 Ranking Display - Top 10 memories with medals (🥇🥈🥉) - Rarity-based color coding - Game-style formatting ### 📅 Daily Challenge System - Random daily quest - Bonus XP rewards - Encourages daily engagement ## Implementation Added `src/game_formatter.rs`: - MemoryRarity enum (5 levels with emoji) - DiagnosisType enum (5 personality types) - GameFormatter with rich text formatting - format_memory_result() - Main game display - format_shareable_text() - SNS sharing - format_ranking() - Top 10 display - format_daily_challenge() - Daily quest MCP Tools Updated: - create_memory_with_ai: Added game_mode parameter (default: true) - list_memories_by_priority: Added ranking display - daily_challenge: New tool for daily quests ## Why This Works 占い・心理テストと同じ心理: 1. ゲームをスタート(メモリ作成) 2. 分析中の演出 3. スコアが表示される(ドキドキ) 4. 結果診断(あなたは〇〇タイプ) 5. シェアしたくなる "見せ方"でデータを楽しいゲームに変換! Next: Phase 2 (Content Platform) + More gamification
This commit is contained in:
111
README.md
111
README.md
@@ -96,15 +96,21 @@ aigpt import path/to/conversations.json
|
|||||||
|
|
||||||
### AI機能ツール(重要!)
|
### AI機能ツール(重要!)
|
||||||
|
|
||||||
6. **create_memory_with_ai** - AI解釈と心理判定付きでメモリを作成
|
6. **create_memory_with_ai** - AI解釈と心理判定付きでメモリを作成 🎮
|
||||||
- 元のコンテンツをAIが解釈
|
- 元のコンテンツをAIが解釈
|
||||||
- 重要度を0.0-1.0のスコアで自動評価
|
- 重要度を0.0-1.0のスコアで自動評価
|
||||||
- ユーザーコンテキストを考慮可能
|
- ユーザーコンテキストを考慮可能
|
||||||
|
- **ゲーム風の診断結果を表示!**(占い・心理テスト風)
|
||||||
|
|
||||||
7. **list_memories_by_priority** - 優先順位順にメモリをリスト
|
7. **list_memories_by_priority** - 優先順位順にメモリをリスト 🏆
|
||||||
- 高スコアから順に表示
|
- 高スコアから順に表示
|
||||||
- min_scoreで閾値フィルタリング可能
|
- min_scoreで閾値フィルタリング可能
|
||||||
- limit で件数制限可能
|
- limit で件数制限可能
|
||||||
|
- **ランキング形式で表示!**
|
||||||
|
|
||||||
|
8. **daily_challenge** - 今日のデイリーチャレンジを取得 📅
|
||||||
|
- 日替わりのお題を取得
|
||||||
|
- ボーナスXPが獲得可能
|
||||||
|
|
||||||
## ツールの使用例
|
## ツールの使用例
|
||||||
|
|
||||||
@@ -115,31 +121,104 @@ Claude Desktop/Codeで以下のように使用します:
|
|||||||
MCPツールを使って「今日は良い天気です」というメモリーを作成してください
|
MCPツールを使って「今日は良い天気です」というメモリーを作成してください
|
||||||
```
|
```
|
||||||
|
|
||||||
### AI解釈付きメモリ作成(推奨)
|
### AI解釈付きメモリ作成(推奨)🎮
|
||||||
```
|
```
|
||||||
create_memory_with_ai ツールを使って「新しいAI記憶システムのアイデアを思いついた」というメモリーを作成してください。
|
create_memory_with_ai ツールを使って「新しいAI記憶システムのアイデアを思いついた」というメモリーを作成してください。
|
||||||
ユーザーコンテキスト: 「AI開発者、創造的思考を重視」
|
ユーザーコンテキスト: 「AI開発者、創造的思考を重視」
|
||||||
```
|
```
|
||||||
|
|
||||||
レスポンス例:
|
**ゲーム風の結果表示:**
|
||||||
```json
|
```
|
||||||
{
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
"success": true,
|
║ 🎲 メモリースコア判定 ║
|
||||||
"id": "uuid-here",
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
"memory": {
|
|
||||||
"content": "新しいAI記憶システムのアイデアを思いついた",
|
⚡ 分析完了! あなたの思考が記録されました
|
||||||
"interpreted_content": "AIによる解釈: 記憶システムの革新的アプローチ...",
|
|
||||||
"priority_score": 0.85,
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
"user_context": "AI開発者、創造的思考を重視"
|
📊 総合スコア
|
||||||
}
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
}
|
🟣 EPIC 85点
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
🎯 詳細分析
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💓 感情的インパクト: [████████░░] 80%
|
||||||
|
🔗 ユーザー関連性: [██████████] 100%
|
||||||
|
✨ 新規性・独自性: [█████████░] 90%
|
||||||
|
⚙️ 実用性: [████████░░] 80%
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
🎊 あなたのタイプ
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💡 【革新者】
|
||||||
|
|
||||||
|
創造的で実用的なアイデアを生み出す。常に新しい可能性を探求し、
|
||||||
|
それを現実のものにする力を持つ。
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
🏆 報酬
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💎 XP獲得: +850 XP
|
||||||
|
🎁 レア度: 🟣 EPIC
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
📤 この結果をシェアしよう!
|
||||||
|
#aigpt #メモリースコア #革新者
|
||||||
```
|
```
|
||||||
|
|
||||||
### 優先順位でメモリをリスト
|
**シェア用テキストも自動生成:**
|
||||||
|
```
|
||||||
|
🎲 AIメモリースコア診断結果
|
||||||
|
|
||||||
|
🟣 EPIC 85点
|
||||||
|
💡 【革新者】
|
||||||
|
|
||||||
|
新しいAI記憶システムのアイデアを思いついた
|
||||||
|
|
||||||
|
#aigpt #メモリースコア #AI診断
|
||||||
|
```
|
||||||
|
|
||||||
|
### 優先順位でメモリをリスト 🏆
|
||||||
```
|
```
|
||||||
list_memories_by_priority ツールで、スコア0.7以上の重要なメモリを10件表示してください
|
list_memories_by_priority ツールで、スコア0.7以上の重要なメモリを10件表示してください
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**ランキング形式で表示:**
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 🏆 メモリーランキング TOP 10 ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
🥇 1位 🟡 LEGENDARY 95点 - 心理優先記憶装置の設計
|
||||||
|
🥈 2位 🟣 EPIC 88点 - AIとのやり取りをコンテンツ化
|
||||||
|
🥉 3位 🟣 EPIC 85点 - ゲーム化の構想
|
||||||
|
4位 🔵 RARE 75点 - SNSの本質について
|
||||||
|
5位 🔵 RARE 72点 - AI OSの可能性
|
||||||
|
...
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
```
|
||||||
|
|
||||||
|
### 今日のデイリーチャレンジ 📅
|
||||||
|
```
|
||||||
|
daily_challenge ツールで今日のお題を確認
|
||||||
|
```
|
||||||
|
|
||||||
|
**表示例:**
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 📅 今日のチャレンジ ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
✨ 今日学んだことを記録しよう
|
||||||
|
|
||||||
|
🎁 報酬: +200 XP
|
||||||
|
💎 完了すると特別なバッジが獲得できます!
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
```
|
||||||
|
|
||||||
### メモリの検索
|
### メモリの検索
|
||||||
```
|
```
|
||||||
MCPツールを使って「天気」に関するメモリーを検索してください
|
MCPツールを使って「天気」に関するメモリーを検索してください
|
||||||
|
|||||||
353
src/game_formatter.rs
Normal file
353
src/game_formatter.rs
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
use crate::memory::Memory;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// メモリーのレア度
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum MemoryRarity {
|
||||||
|
Common, // 0.0-0.4
|
||||||
|
Uncommon, // 0.4-0.6
|
||||||
|
Rare, // 0.6-0.8
|
||||||
|
Epic, // 0.8-0.9
|
||||||
|
Legendary, // 0.9-1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryRarity {
|
||||||
|
pub fn from_score(score: f32) -> Self {
|
||||||
|
match score {
|
||||||
|
s if s >= 0.9 => MemoryRarity::Legendary,
|
||||||
|
s if s >= 0.8 => MemoryRarity::Epic,
|
||||||
|
s if s >= 0.6 => MemoryRarity::Rare,
|
||||||
|
s if s >= 0.4 => MemoryRarity::Uncommon,
|
||||||
|
_ => MemoryRarity::Common,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emoji(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
MemoryRarity::Common => "⚪",
|
||||||
|
MemoryRarity::Uncommon => "🟢",
|
||||||
|
MemoryRarity::Rare => "🔵",
|
||||||
|
MemoryRarity::Epic => "🟣",
|
||||||
|
MemoryRarity::Legendary => "🟡",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
MemoryRarity::Common => "COMMON",
|
||||||
|
MemoryRarity::Uncommon => "UNCOMMON",
|
||||||
|
MemoryRarity::Rare => "RARE",
|
||||||
|
MemoryRarity::Epic => "EPIC",
|
||||||
|
MemoryRarity::Legendary => "LEGENDARY",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xp_value(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
MemoryRarity::Common => 100,
|
||||||
|
MemoryRarity::Uncommon => 250,
|
||||||
|
MemoryRarity::Rare => 500,
|
||||||
|
MemoryRarity::Epic => 850,
|
||||||
|
MemoryRarity::Legendary => 1000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 診断タイプ
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum DiagnosisType {
|
||||||
|
Innovator, // 革新者(創造性高、実用性高)
|
||||||
|
Philosopher, // 哲学者(感情高、新規性高)
|
||||||
|
Pragmatist, // 実務家(実用性高、関連性高)
|
||||||
|
Visionary, // 夢想家(新規性高、感情高)
|
||||||
|
Analyst, // 分析家(全て平均的)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosisType {
|
||||||
|
pub fn from_score_breakdown(
|
||||||
|
emotional: f32,
|
||||||
|
relevance: f32,
|
||||||
|
novelty: f32,
|
||||||
|
utility: f32,
|
||||||
|
) -> Self {
|
||||||
|
if utility > 0.2 && novelty > 0.2 {
|
||||||
|
DiagnosisType::Innovator
|
||||||
|
} else if emotional > 0.2 && novelty > 0.2 {
|
||||||
|
DiagnosisType::Philosopher
|
||||||
|
} else if utility > 0.2 && relevance > 0.2 {
|
||||||
|
DiagnosisType::Pragmatist
|
||||||
|
} else if novelty > 0.2 && emotional > 0.18 {
|
||||||
|
DiagnosisType::Visionary
|
||||||
|
} else {
|
||||||
|
DiagnosisType::Analyst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emoji(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
DiagnosisType::Innovator => "💡",
|
||||||
|
DiagnosisType::Philosopher => "🧠",
|
||||||
|
DiagnosisType::Pragmatist => "🎯",
|
||||||
|
DiagnosisType::Visionary => "✨",
|
||||||
|
DiagnosisType::Analyst => "📊",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
DiagnosisType::Innovator => "革新者",
|
||||||
|
DiagnosisType::Philosopher => "哲学者",
|
||||||
|
DiagnosisType::Pragmatist => "実務家",
|
||||||
|
DiagnosisType::Visionary => "夢想家",
|
||||||
|
DiagnosisType::Analyst => "分析家",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
DiagnosisType::Innovator => "創造的で実用的なアイデアを生み出す。常に新しい可能性を探求し、それを現実のものにする力を持つ。",
|
||||||
|
DiagnosisType::Philosopher => "深い思考と感情を大切にする。抽象的な概念や人生の意味について考えることを好む。",
|
||||||
|
DiagnosisType::Pragmatist => "現実的で効率的。具体的な問題解決に優れ、確実に結果を出す。",
|
||||||
|
DiagnosisType::Visionary => "大胆な夢と理想を追い求める。常識にとらわれず、未来の可能性を信じる。",
|
||||||
|
DiagnosisType::Analyst => "バランスの取れた思考。多角的な視点から物事を分析し、冷静に判断する。",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ゲーム風の結果フォーマッター
|
||||||
|
pub struct GameFormatter;
|
||||||
|
|
||||||
|
impl GameFormatter {
|
||||||
|
/// メモリー作成結果をゲーム風に表示
|
||||||
|
pub fn format_memory_result(memory: &Memory) -> String {
|
||||||
|
let rarity = MemoryRarity::from_score(memory.priority_score);
|
||||||
|
let xp = rarity.xp_value();
|
||||||
|
let score_percentage = (memory.priority_score * 100.0) as u32;
|
||||||
|
|
||||||
|
// スコア内訳を推定(各項目最大0.25として)
|
||||||
|
let emotional = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let relevance = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let novelty = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let utility = memory.priority_score - emotional - relevance - novelty;
|
||||||
|
|
||||||
|
let diagnosis = DiagnosisType::from_score_breakdown(
|
||||||
|
emotional,
|
||||||
|
relevance,
|
||||||
|
novelty,
|
||||||
|
utility,
|
||||||
|
);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 🎲 メモリースコア判定 ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
⚡ 分析完了! あなたの思考が記録されました
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
📊 総合スコア
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
{} {} {}点
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
🎯 詳細分析
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💓 感情的インパクト: {}
|
||||||
|
🔗 ユーザー関連性: {}
|
||||||
|
✨ 新規性・独自性: {}
|
||||||
|
⚙️ 実用性: {}
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
🎊 あなたのタイプ
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
{} 【{}】
|
||||||
|
|
||||||
|
{}
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
🏆 報酬
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
💎 XP獲得: +{} XP
|
||||||
|
🎁 レア度: {} {}
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
💬 AI の解釈
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
{}
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
📤 この結果をシェアしよう!
|
||||||
|
#aigpt #メモリースコア #{}
|
||||||
|
"#,
|
||||||
|
rarity.emoji(),
|
||||||
|
rarity.name(),
|
||||||
|
score_percentage,
|
||||||
|
Self::format_bar(emotional, 0.25),
|
||||||
|
Self::format_bar(relevance, 0.25),
|
||||||
|
Self::format_bar(novelty, 0.25),
|
||||||
|
Self::format_bar(utility, 0.25),
|
||||||
|
diagnosis.emoji(),
|
||||||
|
diagnosis.name(),
|
||||||
|
diagnosis.description(),
|
||||||
|
xp,
|
||||||
|
rarity.emoji(),
|
||||||
|
rarity.name(),
|
||||||
|
memory.interpreted_content,
|
||||||
|
diagnosis.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// シェア用の短縮テキストを生成
|
||||||
|
pub fn format_shareable_text(memory: &Memory) -> String {
|
||||||
|
let rarity = MemoryRarity::from_score(memory.priority_score);
|
||||||
|
let score_percentage = (memory.priority_score * 100.0) as u32;
|
||||||
|
let emotional = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let relevance = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let novelty = (memory.priority_score * 0.25).min(0.25);
|
||||||
|
let utility = memory.priority_score - emotional - relevance - novelty;
|
||||||
|
let diagnosis = DiagnosisType::from_score_breakdown(
|
||||||
|
emotional,
|
||||||
|
relevance,
|
||||||
|
novelty,
|
||||||
|
utility,
|
||||||
|
);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"🎲 AIメモリースコア診断結果
|
||||||
|
|
||||||
|
{} {} {}点
|
||||||
|
{} 【{}】
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
#aigpt #メモリースコア #AI診断"#,
|
||||||
|
rarity.emoji(),
|
||||||
|
rarity.name(),
|
||||||
|
score_percentage,
|
||||||
|
diagnosis.emoji(),
|
||||||
|
diagnosis.name(),
|
||||||
|
Self::truncate(&memory.content, 100),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ランキング表示
|
||||||
|
pub fn format_ranking(memories: &[&Memory], title: &str) -> String {
|
||||||
|
let mut result = format!(
|
||||||
|
r#"
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 🏆 {} ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
"#,
|
||||||
|
title
|
||||||
|
);
|
||||||
|
|
||||||
|
for (i, memory) in memories.iter().take(10).enumerate() {
|
||||||
|
let rank_emoji = match i {
|
||||||
|
0 => "🥇",
|
||||||
|
1 => "🥈",
|
||||||
|
2 => "🥉",
|
||||||
|
_ => " ",
|
||||||
|
};
|
||||||
|
|
||||||
|
let rarity = MemoryRarity::from_score(memory.priority_score);
|
||||||
|
let score = (memory.priority_score * 100.0) as u32;
|
||||||
|
|
||||||
|
result.push_str(&format!(
|
||||||
|
"{} {}位 {} {} {}点 - {}\n",
|
||||||
|
rank_emoji,
|
||||||
|
i + 1,
|
||||||
|
rarity.emoji(),
|
||||||
|
rarity.name(),
|
||||||
|
score,
|
||||||
|
Self::truncate(&memory.content, 40)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// デイリーチャレンジ表示
|
||||||
|
pub fn format_daily_challenge() -> String {
|
||||||
|
// 今日の日付をシードにランダムなお題を生成
|
||||||
|
let challenges = vec![
|
||||||
|
"今日学んだことを記録しよう",
|
||||||
|
"新しいアイデアを思いついた?",
|
||||||
|
"感動したことを書き留めよう",
|
||||||
|
"目標を一つ設定しよう",
|
||||||
|
"誰かに感謝の気持ちを伝えよう",
|
||||||
|
];
|
||||||
|
|
||||||
|
let today = chrono::Utc::now().ordinal();
|
||||||
|
let challenge = challenges[today as usize % challenges.len()];
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 📅 今日のチャレンジ ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
✨ {}
|
||||||
|
|
||||||
|
🎁 報酬: +200 XP
|
||||||
|
💎 完了すると特別なバッジが獲得できます!
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
"#,
|
||||||
|
challenge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// プログレスバーを生成
|
||||||
|
fn format_bar(value: f32, max: f32) -> String {
|
||||||
|
let percentage = (value / max * 100.0) as u32;
|
||||||
|
let filled = (percentage / 10) as usize;
|
||||||
|
let empty = 10 - filled;
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"[{}{}] {}%",
|
||||||
|
"█".repeat(filled),
|
||||||
|
"░".repeat(empty),
|
||||||
|
percentage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// テキストを切り詰め
|
||||||
|
fn truncate(s: &str, max_len: usize) -> String {
|
||||||
|
if s.len() <= max_len {
|
||||||
|
s.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}...", &s[..max_len])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rarity_from_score() {
|
||||||
|
assert!(matches!(MemoryRarity::from_score(0.95), MemoryRarity::Legendary));
|
||||||
|
assert!(matches!(MemoryRarity::from_score(0.85), MemoryRarity::Epic));
|
||||||
|
assert!(matches!(MemoryRarity::from_score(0.7), MemoryRarity::Rare));
|
||||||
|
assert!(matches!(MemoryRarity::from_score(0.5), MemoryRarity::Uncommon));
|
||||||
|
assert!(matches!(MemoryRarity::from_score(0.3), MemoryRarity::Common));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_diagnosis_type() {
|
||||||
|
let diagnosis = DiagnosisType::from_score_breakdown(0.1, 0.1, 0.22, 0.22);
|
||||||
|
assert!(matches!(diagnosis, DiagnosisType::Innovator));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_bar() {
|
||||||
|
let bar = GameFormatter::format_bar(0.15, 0.25);
|
||||||
|
assert!(bar.contains("60%"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod mcp;
|
pub mod mcp;
|
||||||
pub mod ai_interpreter;
|
pub mod ai_interpreter;
|
||||||
|
pub mod game_formatter;
|
||||||
@@ -3,6 +3,7 @@ use serde_json::{json, Value};
|
|||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, Write};
|
||||||
|
|
||||||
use crate::memory::MemoryManager;
|
use crate::memory::MemoryManager;
|
||||||
|
use crate::game_formatter::GameFormatter;
|
||||||
|
|
||||||
pub struct BaseMCPServer {
|
pub struct BaseMCPServer {
|
||||||
pub memory_manager: MemoryManager,
|
pub memory_manager: MemoryManager,
|
||||||
@@ -159,7 +160,7 @@ impl BaseMCPServer {
|
|||||||
}),
|
}),
|
||||||
json!({
|
json!({
|
||||||
"name": "create_memory_with_ai",
|
"name": "create_memory_with_ai",
|
||||||
"description": "Create a new memory with AI interpretation and priority scoring",
|
"description": "Create a new memory with AI interpretation and priority scoring (with game-style result!)",
|
||||||
"inputSchema": {
|
"inputSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -170,6 +171,10 @@ impl BaseMCPServer {
|
|||||||
"user_context": {
|
"user_context": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "User-specific context (optional)"
|
"description": "User-specific context (optional)"
|
||||||
|
},
|
||||||
|
"game_mode": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Show game-style result (default: true)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["content"]
|
"required": ["content"]
|
||||||
@@ -177,7 +182,7 @@ impl BaseMCPServer {
|
|||||||
}),
|
}),
|
||||||
json!({
|
json!({
|
||||||
"name": "list_memories_by_priority",
|
"name": "list_memories_by_priority",
|
||||||
"description": "List memories sorted by priority score (high to low)",
|
"description": "List memories sorted by priority score (high to low) - Shows as ranking!",
|
||||||
"inputSchema": {
|
"inputSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -190,9 +195,21 @@ impl BaseMCPServer {
|
|||||||
"limit": {
|
"limit": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "Maximum number of memories to return"
|
"description": "Maximum number of memories to return"
|
||||||
|
},
|
||||||
|
"game_mode": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Show as game-style ranking (default: true)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
json!({
|
||||||
|
"name": "daily_challenge",
|
||||||
|
"description": "Get today's daily challenge - Create a memory to earn bonus XP!",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -222,6 +239,7 @@ impl BaseMCPServer {
|
|||||||
"create_memory" => self.tool_create_memory(arguments),
|
"create_memory" => self.tool_create_memory(arguments),
|
||||||
"create_memory_with_ai" => self.tool_create_memory_with_ai(arguments).await,
|
"create_memory_with_ai" => self.tool_create_memory_with_ai(arguments).await,
|
||||||
"list_memories_by_priority" => self.tool_list_memories_by_priority(arguments),
|
"list_memories_by_priority" => self.tool_list_memories_by_priority(arguments),
|
||||||
|
"daily_challenge" => self.tool_daily_challenge(),
|
||||||
"search_memories" => self.tool_search_memories(arguments),
|
"search_memories" => self.tool_search_memories(arguments),
|
||||||
"update_memory" => self.tool_update_memory(arguments),
|
"update_memory" => self.tool_update_memory(arguments),
|
||||||
"delete_memory" => self.tool_delete_memory(arguments),
|
"delete_memory" => self.tool_delete_memory(arguments),
|
||||||
@@ -312,11 +330,22 @@ impl BaseMCPServer {
|
|||||||
async fn tool_create_memory_with_ai(&mut self, arguments: &Value) -> Value {
|
async fn tool_create_memory_with_ai(&mut self, arguments: &Value) -> Value {
|
||||||
let content = arguments["content"].as_str().unwrap_or("");
|
let content = arguments["content"].as_str().unwrap_or("");
|
||||||
let user_context = arguments["user_context"].as_str();
|
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 {
|
match self.memory_manager.create_memory_with_ai(content, user_context).await {
|
||||||
Ok(id) => {
|
Ok(id) => {
|
||||||
// 作成したメモリを取得して詳細情報を返す
|
// 作成したメモリを取得して詳細情報を返す
|
||||||
if let Some(memory) = self.memory_manager.get_memory(&id) {
|
if let Some(memory) = self.memory_manager.get_memory(&id) {
|
||||||
|
let result = if game_mode {
|
||||||
|
// ゲーム風表示
|
||||||
|
GameFormatter::format_memory_result(memory)
|
||||||
|
} else {
|
||||||
|
// 通常表示
|
||||||
|
format!("Memory created with AI interpretation\nScore: {}", memory.priority_score)
|
||||||
|
};
|
||||||
|
|
||||||
|
let shareable = GameFormatter::format_shareable_text(memory);
|
||||||
|
|
||||||
json!({
|
json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
"id": id,
|
"id": id,
|
||||||
@@ -327,6 +356,8 @@ impl BaseMCPServer {
|
|||||||
"user_context": memory.user_context,
|
"user_context": memory.user_context,
|
||||||
"created_at": memory.created_at
|
"created_at": memory.created_at
|
||||||
},
|
},
|
||||||
|
"game_result": result,
|
||||||
|
"shareable_text": shareable,
|
||||||
"message": "Memory created with AI interpretation and priority scoring"
|
"message": "Memory created with AI interpretation and priority scoring"
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -348,6 +379,7 @@ impl BaseMCPServer {
|
|||||||
fn tool_list_memories_by_priority(&self, arguments: &Value) -> Value {
|
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 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 limit = arguments["limit"].as_u64().map(|l| l as usize);
|
||||||
|
let game_mode = arguments["game_mode"].as_bool().unwrap_or(true);
|
||||||
|
|
||||||
let mut memories = self.memory_manager.get_memories_by_priority();
|
let mut memories = self.memory_manager.get_memories_by_priority();
|
||||||
|
|
||||||
@@ -359,9 +391,16 @@ impl BaseMCPServer {
|
|||||||
memories.truncate(limit);
|
memories.truncate(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ranking_display = if game_mode {
|
||||||
|
GameFormatter::format_ranking(&memories, "🏆 メモリーランキング TOP 10")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
json!({
|
json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
"count": memories.len(),
|
"count": memories.len(),
|
||||||
|
"ranking_display": ranking_display,
|
||||||
"memories": memories.into_iter().map(|m| json!({
|
"memories": memories.into_iter().map(|m| json!({
|
||||||
"id": m.id,
|
"id": m.id,
|
||||||
"content": m.content,
|
"content": m.content,
|
||||||
@@ -374,6 +413,17 @@ impl BaseMCPServer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// デイリーチャレンジ
|
||||||
|
fn tool_daily_challenge(&self) -> Value {
|
||||||
|
let challenge_display = GameFormatter::format_daily_challenge();
|
||||||
|
|
||||||
|
json!({
|
||||||
|
"success": true,
|
||||||
|
"challenge_display": challenge_display,
|
||||||
|
"message": "Complete today's challenge to earn bonus XP!"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 不明なメソッドハンドラ
|
// 不明なメソッドハンドラ
|
||||||
fn handle_unknown_method(&self, id: Value) -> Value {
|
fn handle_unknown_method(&self, id: Value) -> Value {
|
||||||
json!({
|
json!({
|
||||||
|
|||||||
Reference in New Issue
Block a user