Major refactor: Switch to complete local operation
- Remove external AI API dependency (no more OpenAI/Claude API calls) - Claude Code now does all interpretation and scoring locally - Zero cost: No API fees - Complete privacy: No data sent to external servers - Simplified dependencies: Removed openai crate and ai-analysis feature Changes: - ai_interpreter.rs: Simplified to lightweight wrapper - Cargo.toml: Removed ai-analysis feature and openai dependency - mcp/base.rs: Updated create_memory_with_ai to accept interpreted_content and priority_score from Claude Code - memory.rs: Added create_memory_with_interpretation() method - Documentation: Updated README, QUICKSTART, USAGE to reflect local-only operation - Added CHANGELOG.md to track changes How it works now: User → Claude Code (interprets & scores) → aigpt (stores) → game result Benefits: ✅ 完全ローカル (Fully local) ✅ ゼロコスト (Zero cost) ✅ プライバシー保護 (Privacy protected) ✅ 高速 (Faster - no network latency) ✅ シンプル (Simpler - fewer dependencies)
This commit is contained in:
@@ -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<String>,
|
||||
}
|
||||
/// 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<String> {
|
||||
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<String> {
|
||||
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<f32> {
|
||||
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::<f32>()
|
||||
.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<f32> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user