Files
Andrey Lutsenko d696623f31 Add quickstart and RAG MCP starter docs.
Made-with: Cursor
2026-04-20 22:55:26 +10:00

86 lines
2.6 KiB
Python

#!/usr/bin/env python3
import os
import requests
from fastmcp import FastMCP
from qdrant_client import QdrantClient
MCP_NAME = os.getenv("MCP_NAME", "local-rag")
QDRANT_URL = os.getenv("QDRANT_URL", "http://127.0.0.1:6333")
COLLECTION = os.getenv("QDRANT_COLLECTION", "docs")
EMBED_MODEL = os.getenv("EMBED_MODEL", "nomic-embed-text")
GEN_MODEL = os.getenv("GEN_MODEL", "qwen2.5-coder:7b")
mcp = FastMCP(MCP_NAME)
qdrant = QdrantClient(url=QDRANT_URL)
def embed(text: str) -> list[float]:
r = requests.post(
"http://127.0.0.1:11434/api/embeddings",
json={"model": EMBED_MODEL, "prompt": text},
timeout=120,
)
r.raise_for_status()
return r.json()["embedding"]
def retrieve(query: str, top_k: int = 5):
query_vec = embed(query)
return qdrant.search(collection_name=COLLECTION, query_vector=query_vec, limit=top_k)
def sanitize(text: str, max_len: int = 1800) -> str:
return (text or "").strip()[:max_len]
@mcp.tool
def search_docs(query: str, top_k: int = 5) -> str:
hits = retrieve(query, top_k=top_k)
if not hits:
return "No relevant docs found."
lines = []
for i, hit in enumerate(hits, start=1):
src = hit.payload.get("source", "unknown")
txt = sanitize(hit.payload.get("text", ""))
lines.append(f"[{i}] source={src}\n{txt}")
return "\n\n---\n\n".join(lines)
@mcp.tool
def answer_with_citations(query: str, top_k: int = 5) -> str:
hits = retrieve(query, top_k=top_k)
if not hits:
return "Не нашел релевантных документов."
context = []
citations = []
for i, hit in enumerate(hits, start=1):
src = hit.payload.get("source", "unknown")
txt = sanitize(hit.payload.get("text", ""))
context.append(f"[{i}] SOURCE: {src}\n{txt}")
citations.append(f"[{i}] {src}")
prompt = (
"Ты отвечаешь только на основе контекста.\n"
"Если данных недостаточно, явно скажи об этом.\n"
"Добавь ссылки на источники в формате [номер].\n\n"
f"Вопрос: {query}\n\n"
"Контекст:\n"
+ "\n\n".join(context)
)
r = requests.post(
"http://127.0.0.1:11434/api/generate",
json={"model": GEN_MODEL, "prompt": prompt, "stream": False},
timeout=240,
)
r.raise_for_status()
answer = r.json().get("response", "").strip()
return f"{answer}\n\nИсточники:\n" + "\n".join(citations)
if __name__ == "__main__":
mcp.run()