dead/packages/: qdrant-hybrid-search-0.1.0 metadata and description
Hybrid search service using Qdrant vector database
| description_content_type | text/markdown |
| metadata_version | 2.4 |
| requires_dist |
|
| requires_python | >=3.10 |
Because this project isn't in the mirror_whitelist,
no releases from root/pypi are included.
| File | Tox results | History |
|---|---|---|
qdrant_hybrid_search-0.1.0-py3-none-any.whl
|
|
|
qdrant_hybrid_search-0.1.0.tar.gz
|
|
Qdrant Hybrid Search
Універсальний пакет для гібридного пошуку з використанням Qdrant. Поєднує:
- Dense vectors (semantic search) - embedding моделі
- Sparse vectors (BM25/keyword search) - текстовий пошук
- Matryoshka embeddings - прогресивний пошук для швидкості
- RRF Fusion - об'єднання результатів різних методів
- LLM Reranking - перерейтинг через LLM
Встановлення
pip install qdrant-hybrid-search
# або
uv add qdrant-hybrid-search
Швидкий старт
1. Запуск Qdrant
docker-compose up -d
Або окремо:
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
2. Створення індексу
from qdrant_hybrid_search import SearchConfig, IndexConfig, QdrantIndexer
# Конфігурація Qdrant та embeddings
search_config = SearchConfig(
qdrant_url="http://localhost:6333",
collection_name="api_endpoints",
embedding_api_key="your-openrouter-key",
)
# Конфігурація індексу:
# - text_fields: поля для semantic + BM25 пошуку
# - payload_indexes: поля для фільтрації
index_config = IndexConfig(
id_field="operation_id",
text_fields=["summary", "path", "description", "tags"],
payload_indexes={
"resource_type": "keyword",
"effect_type": "keyword",
"method": "keyword",
},
)
# Створення індексу
indexer = QdrantIndexer(search_config, index_config)
indexer.create_collection(reset=True)
indexer.create_payload_indexes()
# Індексація з JSONL файлу
indexer.index_from_jsonl("api-endpoints.jsonl")
3. Пошук
from qdrant_hybrid_search import SearchConfig, QdrantHybridSearch
config = SearchConfig(
qdrant_url="http://localhost:6333",
collection_name="api_endpoints",
embedding_api_key="your-openrouter-key",
top_k=10,
)
search = QdrantHybridSearch(config)
# Простий пошук
results = search.search("send document for signature")
# Пошук з фільтрами
results = search.search(
"get user documents",
filters={"resource_type": "document", "effect_type": "read"},
)
for r in results:
print(f"[{r.score:.3f}] {r.payload['operation_id']}: {r.payload['summary']}")
Інтеграція з LangGraph
Проста нода (без генерації запитів)
from langgraph.graph import StateGraph, END
from qdrant_hybrid_search import create_simple_search_node, SearchConfig
class MyState(TypedDict):
query: str
search_results: list
config = SearchConfig(
collection_name="api_endpoints",
embedding_api_key="...",
)
search_node = create_simple_search_node(config)
graph = StateGraph(MyState)
graph.add_node("search", search_node)
graph.set_entry_point("search")
graph.add_edge("search", END)
app = graph.compile()
result = app.invoke({"query": "send reminder to signer"})
Повна нода з LLM генерацією запитів
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from qdrant_hybrid_search import (
create_search_node,
SearchNodeConfig,
SearchConfig,
)
class AgentState(TypedDict):
user_command: str
context: dict
search_queries: list
search_results: list
node_config = SearchNodeConfig(
search_config=SearchConfig(
collection_name="api_endpoints",
embedding_api_key="...",
top_k=15,
),
generate_queries=True, # LLM генерує пошукові запити
max_queries=3, # Макс. кількість запитів
state_input_key="user_command",
state_output_key="search_results",
)
search_node = create_search_node(node_config)
llm = ChatOpenAI(model="gpt-4o-mini")
graph = StateGraph(AgentState)
graph.add_node("search", lambda state: search_node(state, llm))
graph.set_entry_point("search")
graph.add_edge("search", END)
app = graph.compile()
result = app.invoke({
"user_command": "відправ нагадування підписанту документа",
"context": {"tenant_id": "123"},
})
print(f"Згенеровані запити: {result['search_queries']}")
print(f"Знайдено результатів: {len(result['search_results'])}")
Конфігурація
SearchConfig
| Параметр | Тип | Default | Опис |
|---|---|---|---|
qdrant_url |
str | http://localhost:6333 |
URL Qdrant сервера |
collection_name |
str | search_index |
Назва колекції |
embedding_api_key |
str | - | API ключ для embeddings |
embedding_base_url |
str | https://openrouter.ai/api/v1 |
Base URL API |
embedding_model |
str | openai/text-embedding-3-small |
Модель для embeddings |
embedding_dims |
int | 1536 |
Розмірність векторів |
top_k |
int | 10 |
Кількість результатів |
min_score |
float | 0.1 |
Мін. поріг релевантності |
use_reranking |
bool | True |
Використовувати LLM reranking |
rerank_model |
str | anthropic/claude-3-haiku |
Модель для reranking |
matryoshka_dims |
list[int] | [64, 128, 256, 512] |
Розміри matryoshka |
rrf_k |
int | 60 |
K параметр для RRF fusion |
Всі параметри можна встановити через env змінні з префіксом SEARCH_:
SEARCH_QDRANT_URL=http://localhost:6333
SEARCH_COLLECTION_NAME=my_index
SEARCH_EMBEDDING_API_KEY=sk-...
IndexConfig
| Параметр | Тип | Default | Опис |
|---|---|---|---|
text_fields |
list[str] | ["content"] |
Поля для пошуку (dense + BM25) |
id_field |
str | id |
Поле з ID документа |
payload_indexes |
dict[str, str] | {} |
Поля для фільтрації |
store_text |
bool | True |
Зберігати об'єднаний текст |
batch_size |
int | 50 |
Розмір батча при індексації |
Типи для payload_indexes: keyword, integer, float, bool, text
Кастомні синоніми
from qdrant_hybrid_search import SynonymExpander, QdrantHybridSearch
# Власні синоніми для вашого домену
synonyms = {
"контракт": ["договір", "угода", "документ"],
"підписати": ["завізувати", "затвердити", "виконати"],
"надіслати": ["відправити", "переслати"],
}
expander = SynonymExpander(synonyms)
search = QdrantHybridSearch(
config=config,
synonym_expander=expander,
)
CLI
# Створити колекцію та проіндексувати
python -m qdrant_hybrid_search.cli index \
--collection api_endpoints \
--create \
--file api-endpoints.jsonl \
--id-field operation_id \
--text-fields summary,path,description,tags \
--payload-indexes resource_type:keyword,effect_type:keyword
qdrant_hybrid_search.cli index \
--collection api_endpoints \
--create \
--file api-endpoints.jsonl \
--id-field operation_id \
--text-fields summary,path,description,tags \
--payload-indexes resource_type:keyword,effect_type:keyword
# Інтерактивний пошук
python -m qdrant_hybrid_search.cli search --collection api_endpoints
# Пошук з запитом
python -m qdrant_hybrid_search.cli search \
--collection api_endpoints \
--query "send document" \
--top-k 5
# Інформація про колекцію
python -m qdrant_hybrid_search.cli info --collection api_endpoints
Приклад: API Endpoints
# index_api.py
from qdrant_hybrid_search import SearchConfig, IndexConfig, QdrantIndexer
search_config = SearchConfig(
collection_name="signnow_api",
embedding_api_key=os.environ["OPENROUTER_API_KEY"],
)
index_config = IndexConfig(
id_field="operation_id",
text_fields=["summary", "path", "description", "method", "tags"],
payload_indexes={
"resource_type": "keyword",
"effect_type": "keyword",
"method": "keyword",
},
)
indexer = QdrantIndexer(search_config, index_config)
indexer.create_collection(reset=True)
indexer.create_payload_indexes()
indexer.index_from_jsonl("api-endpoints.jsonl")
print(indexer.get_collection_info())
# search_api.py
from qdrant_hybrid_search import SearchConfig, QdrantHybridSearch
config = SearchConfig(
collection_name="signnow_api",
embedding_api_key=os.environ["OPENROUTER_API_KEY"],
)
search = QdrantHybridSearch(config)
# Семантичний + keyword пошук
results = search.search("resend signing invitation reminder")
# З фільтрами
results = search.search(
"get documents",
filters={"effect_type": "read", "resource_type": "document"},
top_k=5,
)
Як це працює
Гібридний пошук
- Dense Search: Семантичний пошук через embedding вектори
- Sparse Search (BM25): Keyword пошук через sparse vectors
- Matryoshka Search: Прогресивний пошук - спочатку швидкий з малими векторами, потім точніший
- RRF Fusion: Об'єднання результатів через Reciprocal Rank Fusion
- LLM Reranking: Фінальний перерейтинг через LLM (опційно)
BM25 та text_fields
Поля вказані в text_fields об'єднуються в один текст, який використовується для:
- Генерації dense embedding (semantic search)
- Генерації sparse vector (BM25/keyword search)
Sparse vector будується через хешування слів, що дозволяє точний keyword matching паралельно з семантичним пошуком.
Payload Indexes
Індекси на payload полях дозволяють ефективну фільтрацію:
results = search.search("query", filters={"status": "active"})
Структура проекту
qdrant-hybrid-search/
├── src/
│ ├── __init__.py # Public API
│ ├── config.py # SearchConfig, IndexConfig
│ ├── models.py # SearchResult, SearchQuery, IndexDocument
│ ├── embeddings.py # Embedding client
│ ├── synonyms.py # Synonym expansion
│ ├── indexer.py # QdrantIndexer
│ ├── search.py # QdrantHybridSearch
│ ├── langgraph_node.py # LangGraph integration
│ └── cli.py # CLI
├── docker-compose.yml
├── pyproject.toml
└── README.md
Ліцензія
MIT