From 46c315e3df45956576f6f60752c1dd5777db66fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=B8=D1=8F=20=D0=91=D0=B5=D1=80=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=D0=B0?= Date: Thu, 28 May 2026 09:16:52 +0000 Subject: [PATCH] =?UTF-8?q?MCP-=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D1=8C=D1=8E?= =?UTF-8?q?=20=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=D0=B0:=20solution.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solution.py | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 solution.py diff --git a/solution.py b/solution.py new file mode 100644 index 0000000..d138fff --- /dev/null +++ b/solution.py @@ -0,0 +1,156 @@ +import json +from typing import List, Dict, Any + +import httpx +from fastapi import FastAPI, HTTPException, Body +from pydantic import BaseModel +from qdrant_client import QdrantClient +from qdrant_client.http.models import PointStruct, VectorParams +from langchain.memory import ConversationBufferMemory +from langchain.embeddings.openai import OpenAIEmbeddings + +# ------------------------------------------------------------------ +# Конфигурация сервера и клиента +# ------------------------------------------------------------------ + +QDRANT_HOST = "localhost" +QDRANT_PORT = 6333 +COLLECTION_NAME = "agent_memory" + +OPENAI_API_KEY = "YOUR_OPENAI_API_KEY" # замените на свой ключ + +# ------------------------------------------------------------------ +# Модели данных для API +# ------------------------------------------------------------------ + +class MemoryItem(BaseModel): + text: str + metadata: Dict[str, Any] | None = None + + +class QueryParams(BaseModel): + query: str + top_k: int = 5 + + +# ------------------------------------------------------------------ +# Инициализация Qdrant и LangChain +# ------------------------------------------------------------------ + +client_qdrant = QdrantClient(host=QDRANT_HOST, port=QDRANT_PORT) + +# Создаём коллекцию при первом запуске (если её нет) +if COLLECTION_NAME not in client_qdrant.get_collections().collections: + client_qdrant.create_collection( + collection_name=COLLECTION_NAME, + vectors_config=VectorParams(size=1536, distance="Cosine"), + ) + +embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY) +memory_buffer = ConversationBufferMemory(memory_key="chat_history", return_messages=True) + + +# ------------------------------------------------------------------ +# FastAPI приложение +# ------------------------------------------------------------------ + +app = FastAPI(title="MCP Memory Server") + + +@app.post("/memory/add") +def add_memory(item: MemoryItem): + """ + Добавляет новый элемент памяти в Qdrant. + """ + vector = embeddings.embed_query(item.text) + point_id = client_qdrant.upload_collection( + collection_name=COLLECTION_NAME, + points=[PointStruct(id=None, vector=vector, payload=item.metadata or {})], + ).points[0].id + return {"status": "ok", "point_id": point_id} + + +@app.post("/memory/query") +def query_memory(params: QueryParams): + """ + Выполняет поиск похожих элементов памяти. + """ + query_vector = embeddings.embed_query(params.query) + search_result = client_qdrant.search( + collection_name=COLLECTION_NAME, + query_vector=query_vector, + limit=params.top_k, + ) + results = [ + {"id": hit.id, "score": hit.score, "payload": hit.payload} + for hit in search_result + ] + return {"results": results} + + +@app.post("/memory/clear") +def clear_memory(): + """ + Очищает всю коллекцию памяти. + """ + client_qdrant.delete_collection(collection_name=COLLECTION_NAME) + # Пересоздаём пустую коллекцию + client_qdrant.create_collection( + collection_name=COLLECTION_NAME, + vectors_config=VectorParams(size=1536, distance="Cosine"), + ) + return {"status": "cleared"} + + +# ------------------------------------------------------------------ +# Клиентская часть для взаимодействия с сервером +# ------------------------------------------------------------------ + +class MemoryClient: + """ + Простой клиент для работы с MCP-сервером памяти. + """ + + def __init__(self, base_url: str = "http://localhost:8000"): + self.base_url = base_url.rstrip("/") + + def add(self, text: str, metadata: Dict[str, Any] | None = None) -> int: + payload = {"text": text, "metadata": metadata} + resp = httpx.post(f"{self.base_url}/memory/add", json=payload) + if resp.status_code != 200: + raise RuntimeError(f"Failed to add memory: {resp.text}") + return resp.json()["point_id"] + + def query(self, query: str, top_k: int = 5) -> List[Dict[str, Any]]: + payload = {"query": query, "top_k": top_k} + resp = httpx.post(f"{self.base_url}/memory/query", json=payload) + if resp.status_code != 200: + raise RuntimeError(f"Failed to query memory: {resp.text}") + return resp.json()["results"] + + def clear(self) -> None: + resp = httpx.post(f"{self.base_url}/memory/clear") + if resp.status_code != 200: + raise RuntimeError(f"Failed to clear memory: {resp.text}") + + +# ------------------------------------------------------------------ +# Пример использования (можно запустить как скрипт) +# ------------------------------------------------------------------ + +if __name__ == "__main__": + import uvicorn + from rich.console import Console + + console = Console() + console.print("[bold green]Запуск MCP Memory Server...[/]") + uvicorn.run(app, host="0.0.0.0", port=8000) + + # Пример клиента (не будет выполнен при запуске сервера) + client = MemoryClient() + point_id = client.add("Привет, как дела?", {"source": "user"}) + console.print(f"Добавлен пункт памяти с id={point_id}") + results = client.query("привет") + console.print("[bold]Результаты поиска:[/]") + for r in results: + console.print(r) \ No newline at end of file