Обновить vector_store.py

This commit is contained in:
2026-05-28 13:06:27 +00:00
parent 90769e4943
commit 7825305fb0
+36 -63
View File
@@ -1,100 +1,73 @@
"""
vector_store.py — модуль для работы с векторным хранилищем Qdrant + Ollama.
"""
from __future__ import annotations
import uuid
from typing import List, Tuple
from langchain_ollama import OllamaEmbeddings
from langchain_qdrant import QdrantVectorStore from langchain_qdrant import QdrantVectorStore
from langchain_ollama import OllamaEmbeddings
from langchain_core.documents import Document from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from qdrant_client import QdrantClient from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams from qdrant_client.models import Distance, VectorParams
from langchain.text_splitter import RecursiveCharacterTextSplitter
import uuid
# ── Конфигурация ────────────────────────────────────────────────────────────── COLLECTION_NAME = "knowledge_base"
QDRANT_URL = "http://localhost:6333" EMBEDDING_MODEL = "nomic-embed-text"
COLLECTION_NAME = "rag_memory" VECTOR_SIZE = 768
EMBEDDING_MODEL = "nomic-embed-text"
EMBEDDING_DIM = 768 # размер вектора nomic-embed-text
CHUNK_SIZE = 500
CHUNK_OVERLAP = 100
# ── Эмбеддинги через Ollama ─────────────────────────────────────────────────── def get_embeddings():
embeddings = OllamaEmbeddings(model=EMBEDDING_MODEL) return OllamaEmbeddings(model=EMBEDDING_MODEL)
# ── Qdrant-клиент и коллекция ───────────────────────────────────────────────── def get_qdrant_client():
def _get_client() -> QdrantClient: return QdrantClient(host="localhost", port=6333)
return QdrantClient(url=QDRANT_URL)
def init_collection() -> None: def init_collection(client: QdrantClient):
"""Создаёт коллекцию в Qdrant, если её ещё нет.""" collections = [c.name for c in client.get_collections().collections]
client = _get_client() if COLLECTION_NAME not in collections:
existing = [c.name for c in client.get_collections().collections]
if COLLECTION_NAME not in existing:
client.create_collection( client.create_collection(
collection_name=COLLECTION_NAME, collection_name=COLLECTION_NAME,
vectors_config=VectorParams(size=EMBEDDING_DIM, distance=Distance.COSINE), vectors_config=VectorParams(size=VECTOR_SIZE, distance=Distance.COSINE),
) )
print(f"[vector_store] Коллекция «{COLLECTION_NAME}» создана.")
else:
print(f"[vector_store] Коллекция «{COLLECTION_NAME}» уже существует.")
def _get_store() -> QdrantVectorStore: def get_vector_store() -> QdrantVectorStore:
"""Возвращает готовый QdrantVectorStore.""" client = get_qdrant_client()
init_collection(client)
embeddings = get_embeddings()
return QdrantVectorStore( return QdrantVectorStore(
client=_get_client(), client=client,
collection_name=COLLECTION_NAME, collection_name=COLLECTION_NAME,
embedding=embeddings, embedding=embeddings,
) )
# ── Чанкинг ─────────────────────────────────────────────────────────────────── def add_documents(content: str, title: str) -> int:
def split_text(content: str, title: str = "") -> List[Document]:
"""
Разбивает текст на чанки с помощью RecursiveCharacterTextSplitter
и добавляет метаданные (title, chunk_index).
"""
splitter = RecursiveCharacterTextSplitter( splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE, chunk_size=500,
chunk_overlap=CHUNK_OVERLAP, chunk_overlap=50,
separators=["\n\n", "\n", ".", " ", ""], separators=["\n\n", "\n", ".", " ", ""],
) )
chunks = splitter.split_text(content) chunks = splitter.split_text(content)
docs = [ docs = [
Document( Document(
page_content=chunk, page_content=chunk,
metadata={"title": title, "chunk_index": i, "source": title or "manual"}, metadata={"title": title, "chunk_index": i, "source": title},
) )
for i, chunk in enumerate(chunks) for i, chunk in enumerate(chunks)
] ]
return docs store = get_vector_store()
# ── Публичный API ─────────────────────────────────────────────────────────────
def add_documents(content: str, title: str = "") -> int:
"""
Добавляет документ в векторное хранилище.
Возвращает количество добавленных чанков.
"""
docs = split_text(content, title)
store = _get_store()
store.add_documents(docs) store.add_documents(docs)
return len(docs) return len(docs)
def search(query: str, max_results: int = 5) -> List[Tuple[Document, float]]: def search_documents(query: str, max_results: int = 5) -> list[dict]:
""" store = get_vector_store()
Семантический поиск с метрикой релевантности.
Возвращает список (Document, score).
"""
store = _get_store()
results = store.similarity_search_with_relevance_scores(query, k=max_results) results = store.similarity_search_with_relevance_scores(query, k=max_results)
return results output = []
for doc, score in results:
output.append(
{
"content": doc.page_content,
"metadata": doc.metadata,
"score": round(score, 4),
}
)
return output