Qdrant хранит векторы и ищет похожие фрагменты, а модели нужны на двух этапах: эмбеддинги для индексации и поиска, chat-модель для ответа. Обе части можно взять из Heli с одним ключом sk-heli-, а Qdrant запустить у себя в Docker или использовать облачный. Векторы и документы остаются в вашей базе — в Heli уходят только тексты для эмбеддингов и запросы к модели.
Установка
pip install langchain-openai langchain-qdrant qdrant-client
docker run -p 6333:6333 qdrant/qdrantИндексация и ответ
import os
from langchain_core.documents import Document
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_qdrant import QdrantVectorStore
HELI = dict(base_url="https://getheli.ru/v1", api_key=os.environ["HELI_API_KEY"])
embeddings = OpenAIEmbeddings(
model="openai/text-embedding-3-small",
check_embedding_ctx_length=False,
**HELI,
)
docs = [Document(page_content="Ключ Heli создаётся во вкладке «Ключи» кабинета.")]
store = QdrantVectorStore.from_documents(
docs, embedding=embeddings, url="http://localhost:6333", collection_name="kb"
)
retriever = store.as_retriever(search_kwargs={"k": 4})
llm = ChatOpenAI(model="openai/gpt-5.4-mini", **HELI)
question = "Где взять ключ Heli?"
context = "\n\n".join(d.page_content for d in retriever.invoke(question))
answer = llm.invoke([
("system", "Отвечай только по контексту. Если ответа в нём нет, так и скажи."),
("human", f"Контекст: {context} Вопрос: {question}"),
])
print(answer.content)Важные детали
- check_embedding_ctx_length=False заставляет OpenAIEmbeddings отправлять обычный текст. Иначе LangChain режет текст на токены через tiktoken и шлёт массивы токенов — для OpenAI-совместимых API надёжнее отправлять строки.
- Размер вектора задаётся моделью: 1536 у text-embedding-3-small и 3072 у text-embedding-3-large. Коллекция создаётся под эту размерность, поэтому при смене модели эмбеддингов нужна новая коллекция и переиндексация.
- Индексируйте и ищите одной и той же моделью эмбеддингов — векторы разных моделей несравнимы.
- Документы режьте на фрагменты, например через RecursiveCharacterTextSplitter из langchain-text-splitters. Целые документы дают плохой поиск и раздувают контекст.
- Для Qdrant Cloud передайте url кластера и api_key Qdrant — это отдельный ключ, не ключ Heli.
Модели
Для большинства баз знаний на русском хватает text-embedding-3-small. Для генерации ответа берите быструю модель и передавайте ей 3–5 релевантных фрагментов, а не весь найденный текст.
Обновление базы знаний
Чтобы при повторной загрузке документа не плодить дубли, задавайте фрагментам стабильные идентификаторы и передавайте их в add_documents через параметр ids. Qdrant принимает в качестве id только целые числа или UUID, поэтому удобно строить UUID из пути документа и номера фрагмента через uuid5.
В metadata документа сохраняйте источник, раздел и дату: по этим полям Qdrant умеет фильтровать поиск, и retriever можно ограничить, например, только актуальными документами. Если пользователи часто ищут точные термины и артикулы, посмотрите гибридный режим поиска в langchain-qdrant, где плотные эмбеддинги дополняются разреженными.
Попробуйте Heli
Ключ sk-heli- появляется после подтверждения почты. Через Google и Яндекс — сразу в кабинете.