fix memory mcp

This commit is contained in:
syui 2025-05-23 17:24:10 +09:00
parent 2f27e10f62
commit df128e5048
Signed by: syui
GPG Key ID: 5417CFEBAD92DF56
4 changed files with 131 additions and 39 deletions

View File

@ -60,18 +60,19 @@ def call_mcp(cfg, message: str):
return response.json().get("output", {}).get("response", "❓ 応答が取得できませんでした") return response.json().get("output", {}).get("response", "❓ 応答が取得できませんでした")
def call_openai(cfg, message: str): def call_openai(cfg, message: str):
# ツール定義
tools = [ tools = [
{ {
"type": "function", "type": "function",
"function": { "function": {
"name": "memory", # MCPツール名と一致させる "name": "memory",
"description": "AIが記憶ログを参照するツールです", "description": "記憶を検索する",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
"query": { "query": {
"type": "string", "type": "string",
"description": "探している記憶に関するキーワードや質問" "description": "検索する語句"
} }
}, },
"required": ["query"] "required": ["query"]
@ -80,15 +81,15 @@ def call_openai(cfg, message: str):
} }
] ]
# 最初のメッセージ送信
payload = { payload = {
"model": cfg["model"], "model": cfg["model"],
"messages": [ "messages": [
{"role": "system", "content": "あなたはAIで、必要に応じて記憶検索ツールmemoryを使って過去の会話を参照することができます。"}, {"role": "system", "content": "あなたはAIで、必要に応じてツールmemoryを使って記憶を検索します。"},
{"role": "user", "content": message} {"role": "user", "content": message}
], ],
"temperature": 0.7, "tools": tools,
"tool_choice": "auto", # AIが自律的にツールを選ぶ "tool_choice": "auto"
"tools": tools
} }
headers = { headers = {
@ -96,25 +97,43 @@ def call_openai(cfg, message: str):
"Content-Type": "application/json", "Content-Type": "application/json",
} }
response = requests.post(cfg["url"], headers=headers, json=payload) res1 = requests.post(cfg["url"], headers=headers, json=payload)
response.raise_for_status() res1.raise_for_status()
result = res1.json()
result = response.json() # 🧠 tool_call されたか確認
# AIがtool_callしたかチェック
if "tool_calls" in result["choices"][0]["message"]: if "tool_calls" in result["choices"][0]["message"]:
tool_call = result["choices"][0]["message"]["tool_calls"][0] tool_call = result["choices"][0]["message"]["tool_calls"][0]
if tool_call["function"]["name"] == "memory": if tool_call["function"]["name"] == "memory":
args = json.loads(tool_call["function"]["arguments"]) args = json.loads(tool_call["function"]["arguments"])
query = args.get("query", "") query = args.get("query", "")
# ここでMCP serverにPOSTする print(f"🛠️ ツール実行: memory(query='{query}')")
memory_response = requests.post(
"http://127.0.0.1:5000/memory/search", # あらかじめ実装されたmemory検索用API # MCPエンドポイントにPOST
json={"query": query} memory_res = requests.post("http://127.0.0.1:5000/memory/search", json={"query": query})
).json() memory_json = memory_res.json()
return f"[Memory Tool]: {memory_response.get('result', 'なし')}" tool_output = memory_json.get("result", "なし")
# 通常のテキスト応答 # tool_outputをAIに返す
followup = {
"model": cfg["model"],
"messages": [
{"role": "system", "content": "あなたはAIで、必要に応じてツールmemoryを使って記憶を検索します。"},
{"role": "user", "content": message},
{"role": "assistant", "tool_calls": result["choices"][0]["message"]["tool_calls"]},
{"role": "tool", "tool_call_id": tool_call["id"], "name": "memory", "content": tool_output}
]
}
print(tool_output)
print(cfg["model"])
res2 = requests.post(cfg["url"], headers=headers, json=followup)
res2.raise_for_status()
final_response = res2.json()
print(final_response)
return final_response["choices"][0]["message"]["content"]
# ツール未使用 or 通常応答
return result["choices"][0]["message"]["content"] return result["choices"][0]["message"]["content"]
def call_ollama(cfg, message: str): def call_ollama(cfg, message: str):
@ -127,7 +146,6 @@ def call_ollama(cfg, message: str):
response = requests.post(cfg["url"], headers=headers, json=payload) response = requests.post(cfg["url"], headers=headers, json=payload)
response.raise_for_status() response.raise_for_status()
return response.json().get("response", "❌ 応答が取得できませんでした") return response.json().get("response", "❌ 応答が取得できませんでした")
def main(): def main():
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: ask.py 'your message'") print("Usage: ask.py 'your message'")

View File

@ -22,16 +22,29 @@ def save_message(sender, message):
with open(path, "w") as f: with open(path, "w") as f:
json.dump(logs, f, indent=2, ensure_ascii=False) json.dump(logs, f, indent=2, ensure_ascii=False)
#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"]]
# matched = [entry for entry in logs if query.lower() in entry["message"].lower()]
# all_logs.extend(matched)
# return all_logs[-5:] # 最新5件だけ返す
def search_memory(query: str): def search_memory(query: str):
from glob import glob from glob import glob
all_logs = [] all_logs = []
pattern = re.compile(re.escape(query), re.IGNORECASE)
for file_path in sorted(MEMORY_DIR.glob("*.json")): for file_path in sorted(MEMORY_DIR.glob("*.json")):
with open(file_path, "r") as f: with open(file_path, "r") as f:
logs = json.load(f) logs = json.load(f)
matched = [entry for entry in logs if query in entry["message"]] matched = [entry for entry in logs if pattern.search(entry["message"])]
all_logs.extend(matched) all_logs.extend(matched)
return all_logs[-5:] # 最新5件だけ返す
return all_logs[-5:]
# scripts/memory_store.py # scripts/memory_store.py
import json import json
@ -60,15 +73,31 @@ def save_message(sender, message):
with open(path, "w") as f: with open(path, "w") as f:
json.dump(logs, f, indent=2, ensure_ascii=False) json.dump(logs, f, indent=2, ensure_ascii=False)
# キーワードで過去のメモリを検索する最新5件を返す
def search_memory(query: str): def search_memory(query: str):
from glob import glob
all_logs = [] all_logs = []
for file_path in sorted(MEMORY_DIR.glob("*.json")): for file_path in sorted(MEMORY_DIR.glob("*.json")):
try: with open(file_path, "r") as f:
with open(file_path, "r") as f: logs = json.load(f)
logs = json.load(f) matched = [
matched = [entry for entry in logs if query.lower() in entry["message"].lower()] entry for entry in logs
all_logs.extend(matched) if entry["sender"] == "user" and query in entry["message"]
except Exception as e: ]
print(f"⚠️ 読み込み失敗: {file_path} ({e})") all_logs.extend(matched)
return all_logs[-5:] # 最新5件を返す return all_logs[-5:] # 最新5件だけ返す
def search_memory(query: str):
from glob import glob
all_logs = []
seen_messages = set() # すでに見たメッセージを保持
for file_path in sorted(MEMORY_DIR.glob("*.json")):
with open(file_path, "r") as f:
logs = json.load(f)
for entry in logs:
if entry["sender"] == "user" and query in entry["message"]:
# すでに同じメッセージが結果に含まれていなければ追加
if entry["message"] not in seen_messages:
all_logs.append(entry)
seen_messages.add(entry["message"])
return all_logs[-5:] # 最新5件だけ返す

View File

@ -1,9 +1,8 @@
# server.py # server.py
from fastapi import FastAPI from fastapi import FastAPI, Body
from fastapi_mcp import FastApiMCP from fastapi_mcp import FastApiMCP
from pydantic import BaseModel from pydantic import BaseModel
#from memory_store import save_message, load_messages, search_memory from memory_store import save_message, load_logs, search_memory as do_search_memory
from memory_store import save_message, load_logs, search_memory
app = FastAPI() app = FastAPI()
mcp = FastApiMCP(app, name="aigpt-agent", description="MCP Server for AI memory") mcp = FastApiMCP(app, name="aigpt-agent", description="MCP Server for AI memory")
@ -43,11 +42,37 @@ async def ask_message(input: MemoryQuery):
"response": f"🔎 記憶から {len(results)} 件ヒット:\n" + "\n".join([f"{r['sender']}: {r['message']}" for r in results]) "response": f"🔎 記憶から {len(results)} 件ヒット:\n" + "\n".join([f"{r['sender']}: {r['message']}" for r in results])
} }
## こちらはうまくいく
## curl -X POST http://127.0.0.1:5000/memory/search -H "Content-Type: application/json" -d '{"query": "昨日"}'
## {"result":"記憶の中から「昨日」に関するデータを返しました"}
#@app.post("/memory/search", operation_id="memory")
#async def search_memory(input: dict):
# query = input.get("query", "")
# results = do_search_memory(query)
# if not results:
# return {"result": "該当する記憶は見つかりませんでした"}
# return {
# "result": "記憶検索結果:\n" + "\n".join([f"{r['sender']}: {r['message']}" for r in results])
# }
#
## こちらはうまくいかない
## curl -X POST http://127.0.0.1:5000/memory/search -H "Content-Type: application/json" -d '{"query": "昨日"}'
## Internal Server Error
#@app.post("/memory/search", operation_id="memory")
#async def memory_search(query: MemoryQuery):
# hits = search_memory(query.query)
# if not hits:
# return {"result": "🔍 記憶の中に該当する内容は見つかりませんでした。"}
# summary = "\n".join([f"{e['sender']}: {e['message']}" for e in hits])
# return {"result": f"🔎 見つかった記憶:\n{summary}"}
@app.post("/memory/search", operation_id="memory") @app.post("/memory/search", operation_id="memory")
async def search_memory(input: dict): async def memory_search(query: MemoryQuery):
query = input.get("query", "") hits = do_search_memory(query.query) # ←ここを修正
# 適当なキーワード検索ロジックを追加(例: logs.jsonを検索 if not hits:
return {"result": f"記憶の中から「{query}」に関するデータを返しました"} return {"result": "🔍 記憶の中に該当する内容は見つかりませんでした。"}
summary = "\n".join([f"{e['sender']}: {e['message']}" for e in hits])
return {"result": f"🔎 見つかった記憶:\n{summary}"}
# --- MCP 初期化 --- # --- MCP 初期化 ---
mcp.mount() mcp.mount()

20
mcp/scripts/t.py Normal file
View File

@ -0,0 +1,20 @@
from fastapi import FastAPI
from fastapi_mcp import FastApiMCP
app = FastAPI()
@app.get("/items/{item_id}", operation_id="get_item")
async def read_item(item_id: int):
return {"item_id": item_id, "name": f"Item {item_id}"}
# MCPサーバを作成し、FastAPIアプリにマウント
mcp = FastApiMCP(
app,
name="My API MCP",
description="My API description"
)
mcp.mount()
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)