diff --git a/mcp/scripts/ask.py b/mcp/scripts/ask.py index f3885e0..5d82c87 100644 --- a/mcp/scripts/ask.py +++ b/mcp/scripts/ask.py @@ -2,8 +2,37 @@ import sys import json import requests -from datetime import datetime from config import load_config +from datetime import datetime, timezone + +def build_payload_openai(cfg, message: str): + return { + "model": cfg["model"], + "tools": [ + { + "type": "function", + "function": { + "name": "ask_message", + "description": "過去の記憶を検索します", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "検索したい語句" + } + }, + "required": ["query"] + } + } + } + ], + "tool_choice": "auto", + "messages": [ + {"role": "system", "content": "あなたは親しみやすいAIで、必要に応じて記憶から情報を検索して応答します。"}, + {"role": "user", "content": message} + ] + } def build_payload_mcp(message: str): return { @@ -30,15 +59,73 @@ def call_mcp(cfg, message: str): response.raise_for_status() return response.json().get("output", {}).get("response", "❓ 応答が取得できませんでした") +#def call_openai(cfg, message: str): +# payload = build_payload_openai(cfg, message) +# headers = { +# "Authorization": f"Bearer {cfg['api_key']}", +# "Content-Type": "application/json", +# } +# response = requests.post(cfg["url"], headers=headers, json=payload) +# response.raise_for_status() +# return response.json()["choices"][0]["message"]["content"] + def call_openai(cfg, message: str): - payload = build_payload_openai(cfg, message) + tools = [ + { + "type": "function", + "function": { + "name": "memory", # MCPツール名と一致させる + "description": "AIが記憶ログを参照するツールです", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "探している記憶に関するキーワードや質問" + } + }, + "required": ["query"] + } + } + } + ] + + payload = { + "model": cfg["model"], + "messages": [ + {"role": "system", "content": "あなたはAIで、必要に応じて記憶検索ツール(memory)を使って過去の会話を参照することができます。"}, + {"role": "user", "content": message} + ], + "temperature": 0.7, + "tool_choice": "auto", # AIが自律的にツールを選ぶ + "tools": tools + } + headers = { "Authorization": f"Bearer {cfg['api_key']}", "Content-Type": "application/json", } + response = requests.post(cfg["url"], headers=headers, json=payload) response.raise_for_status() - return response.json()["choices"][0]["message"]["content"] + + result = response.json() + + # AIがtool_callしたかチェック + if "tool_calls" in result["choices"][0]["message"]: + tool_call = result["choices"][0]["message"]["tool_calls"][0] + if tool_call["function"]["name"] == "memory": + args = json.loads(tool_call["function"]["arguments"]) + query = args.get("query", "") + # ここでMCP serverにPOSTする + memory_response = requests.post( + "http://127.0.0.1:5000/memory/search", # あらかじめ実装されたmemory検索用API + json={"query": query} + ).json() + return f"[Memory Tool]: {memory_response.get('result', 'なし')}" + + # 通常のテキスト応答 + return result["choices"][0]["message"]["content"] def main(): if len(sys.argv) < 2: @@ -79,7 +166,7 @@ def save_log(user_msg, ai_msg): else: logs = [] - now = datetime.utcnow().isoformat() + "Z" + now = datetime.now(timezone.utc).isoformat() logs.append({"timestamp": now, "sender": "user", "message": user_msg}) logs.append({"timestamp": now, "sender": "ai", "message": ai_msg}) diff --git a/mcp/scripts/memory_store.py b/mcp/scripts/memory_store.py index a2ba7d4..f85409b 100644 --- a/mcp/scripts/memory_store.py +++ b/mcp/scripts/memory_store.py @@ -1,37 +1,74 @@ # scripts/memory_store.py -from pathlib import Path import json -from datetime import datetime - -MEMORY_DIR = Path.home() / ".config" / "aigpt" / "memory" -MEMORY_DIR.mkdir(parents=True, exist_ok=True) - -def get_today_path(): - today = datetime.utcnow().strftime("%Y-%m-%d") - return MEMORY_DIR / f"{today}.json" - -def save_message(sender: str, message: str): - entry = { - "timestamp": datetime.utcnow().isoformat(), - "sender": sender, - "message": message - } - - path = get_today_path() - data = [] +from pathlib import Path +from config import MEMORY_DIR +from datetime import datetime, timezone +def load_logs(date_str=None): + if date_str is None: + date_str = datetime.now().strftime("%Y-%m-%d") + path = MEMORY_DIR / f"{date_str}.json" if path.exists(): with open(path, "r") as f: - data = json.load(f) - - data.append(entry) + return json.load(f) + return [] +def save_message(sender, message): + date_str = datetime.now().strftime("%Y-%m-%d") + path = MEMORY_DIR / f"{date_str}.json" + logs = load_logs(date_str) + now = datetime.now(timezone.utc).isoformat() + logs.append({"timestamp": now, "sender": sender, "message": message}) with open(path, "w") as f: - json.dump(data, f, indent=2, ensure_ascii=False) + json.dump(logs, f, indent=2, ensure_ascii=False) -def load_messages(): - path = get_today_path() - if not path.exists(): - return [] - with open(path, "r") as f: - return json.load(f) +def search_memory(query: str): + from glob import glob + all_logs = [] + for file_path in sorted(MEMORY_DIR.glob("*.json")): + with open(file_path, "r") as f: + logs = json.load(f) + matched = [entry for entry in logs if query in entry["message"]] + all_logs.extend(matched) + return all_logs[-5:] # 最新5件だけ返す + + +# scripts/memory_store.py +import json +from datetime import datetime +from pathlib import Path +from config import MEMORY_DIR + +# ログを読み込む(指定日または当日) +def load_logs(date_str=None): + if date_str is None: + date_str = datetime.now().strftime("%Y-%m-%d") + path = MEMORY_DIR / f"{date_str}.json" + if path.exists(): + with open(path, "r") as f: + return json.load(f) + return [] + +# メッセージを保存する +def save_message(sender, message): + date_str = datetime.now().strftime("%Y-%m-%d") + path = MEMORY_DIR / f"{date_str}.json" + logs = load_logs(date_str) + #now = datetime.utcnow().isoformat() + "Z" + now = datetime.now(timezone.utc).isoformat() + logs.append({"timestamp": now, "sender": sender, "message": message}) + with open(path, "w") as f: + json.dump(logs, f, indent=2, ensure_ascii=False) + +# キーワードで過去のメモリを検索する(最新5件を返す) +def search_memory(query: str): + all_logs = [] + for file_path in sorted(MEMORY_DIR.glob("*.json")): + try: + with open(file_path, "r") as f: + logs = json.load(f) + matched = [entry for entry in logs if query.lower() in entry["message"].lower()] + all_logs.extend(matched) + except Exception as e: + print(f"⚠️ 読み込み失敗: {file_path} ({e})") + return all_logs[-5:] # 最新5件を返す diff --git a/mcp/scripts/server.py b/mcp/scripts/server.py index d67f9af..b570871 100644 --- a/mcp/scripts/server.py +++ b/mcp/scripts/server.py @@ -2,7 +2,8 @@ from fastapi import FastAPI from fastapi_mcp import FastApiMCP from pydantic import BaseModel -from memory_store import save_message, load_messages +#from memory_store import save_message, load_messages, search_memory +from memory_store import save_message, load_logs, search_memory app = FastAPI() mcp = FastApiMCP(app, name="aigpt-agent", description="MCP Server for AI memory") @@ -15,6 +16,9 @@ class MemoryInput(BaseModel): sender: str message: str +class MemoryQuery(BaseModel): + query: str + # --- ツール(エンドポイント)定義 --- @app.post("/chat", operation_id="chat") async def chat(input: ChatInput): @@ -32,6 +36,19 @@ async def memory_post(input: MemoryInput): async def memory_get(): return {"messages": load_messages()} +@app.post("/ask_message", operation_id="ask_message") +async def ask_message(input: MemoryQuery): + results = search_memory(input.query) + return { + "response": f"🔎 記憶から {len(results)} 件ヒット:\n" + "\n".join([f"{r['sender']}: {r['message']}" for r in results]) + } + +@app.post("/memory/search", operation_id="memory") +async def search_memory(input: dict): + query = input.get("query", "") + # 適当なキーワード検索ロジックを追加(例: logs.jsonを検索) + return {"result": f"記憶の中から「{query}」に関するデータを返しました"} + # --- MCP 初期化 --- mcp.mount()