Введение. Зачем агенту память, и почему я выбрал именно эту базу данных
Осенью 2024 года я сидел в три часа ночи перед монитором и смотрел, как мой LLM-агент уверенно врёт. Не злонамеренно — просто у него не было доступа к актуальной документации продукта, и он «додумывал» ответ на основе обрезков контекста, которые я запихнул в промпт. Тридцать два токена системного сообщения, двести строк инструкции, и ни одного факта из реальной базы знаний компании. Агент галлюцинировал красиво, убедительно и совершенно бесполезно.
Тогда я понял простую вещь: без нормальной векторной базы данных любой агент — это болтун с амнезией. Ему можно дать самый мощный reasoning, самый длинный контекст, самые изощрённые цепочки рассуждений — но если он не умеет быстро и точно находить нужный фрагмент знания среди сотен тысяч документов, он бесполезен в продакшене. Мне нужна была система, которая хранит эмбеддинги, ищет по семантическому сходству, фильтрует по метаданным и при этом не требует команды из пяти инженеров для развёртывания.
Я перепробовал шесть решений за три месяца. Писал обвязки над FAISS, мучился с конфигурацией Qdrant, смотрел в сторону Pinecone и Weaviate. А потом наткнулся на ChromaDB — и за один вечер собрал прототип, который на следующий день уже работал в бою. С тех пор прошло почти два года. Я использовал ChromaDB в десятках проектов: от корпоративных чат-ботов до мультиагентных систем с общей памятью. В этой статье я расскажу всё, что узнал: как устроена эта база, как правильно собрать на ней векторную базу знаний для агентов, какие грабли я собрал по дороге и что бы я делал иначе, если бы начинал сегодня, в октябре 2026 года.
Что такое ChromaDB и почему она стала «SQLite для AI»
ChromaDB — это векторная база данных с открытым исходным кодом, созданная специально для работы с эмбеддингами и семантическим поиском. Её придумали Джефф Хубер и Антон Тройников в 2022 году, а первая публичная версия вышла в начале 2023-го. Идея была дерзкой: сделать для AI-приложений то, что SQLite сделал для реляционных данных. То есть дать разработчику инструмент, который запускается одной командой, не требует отдельного сервера на старте и при этом масштабируется, когда это становится нужно.
Название «Chroma» отсылает к хроматике, к цвету. Основатели объясняли выбор метафорой: цвет — фундаментальное свойство визуального восприятия, и они хотели, чтобы их база стала столь же фундаментальным строительным блоком для AI-приложений. Не инфраструктурным монстром, а органичной частью стека.
Что делает ChromaDB на практике:
- Хранит векторные представления (эмбеддинги) текстов, изображений и других данных.
- Выполняет поиск ближайших соседей по косинусному расстоянию, L2-метрике или внутреннему произведению.
- Позволяет привязывать к каждому вектору произвольные метаданные и фильтровать результаты по ним.
- Работает в трёх режимах: в оперативной памяти, на диске (персистентно) и в клиент-серверной архитектуре.
- Интегрируется с LangChain, LlamaIndex, AutoGen, CrewAI и десятками других фреймворков для агентов.
Малоизвестный факт: на ранних этапах разработки команда ChromaDB всерьёз рассматривала идею встроить базу прямо в Python как модуль без каких-либо внешних зависимостей. От этой идеи частично отошли, когда появилась потребность в серверном режиме, но философия «минимум трения на старте» осталась в ДНК продукта. Вы можете написать pip install chromadb, создать коллекцию, добавить документы и выполнить поиск — всё в одном скрипте на двадцать строк. Ни Docker, ни Kubernetes, ни отдельный процесс не нужны.
Архитектура: что происходит под капотом
Прежде чем строить на ChromaDB базу знаний для агентов, я потратил неделю на изучение внутренностей. Это сэкономило мне месяцы отладки в продакшене. Понимание архитектуры позволяет принимать осознанные решения о том, как структурировать данные, какие индексы использовать и где проходят границы производительности.
Хранение и индексирование
В основе ChromaDB лежит алгоритм HNSW — Hierarchical Navigable Small World. Это графовая структура для приближённого поиска ближайших соседей, описанная Юрием Малковым и Дмитрием Яшуниным в статье 2016 года, которая впоследствии стала одним из самых цитируемых материалов в области векторного поиска. Идея в том, что векторы организуются в многоуровневый граф: верхние уровни содержат разреженные «магистральные» связи для быстрого грубого поиска, а нижние — плотные локальные связи для точной финализации. Поиск начинается сверху и спускается вниз, сужая область на каждом уровне.
Я проверил это на собственном опыте: при коллекции в 500 000 документов с эмбеддингами размерности 1536 запрос выполняется за 8–15 миллисекунд на обычном ноутбуке. Для агентной системы, где каждый вызов инструмента добавляет задержку, это критически важно.
Коллекции как основная единица организации
В ChromaDB данные группируются в коллекции. Коллекция — это аналог таблицы в реляционной базе. У каждой коллекции есть:
- Название и необязательное описание.
- Функция эмбеддинга (можно задать на уровне коллекции, а можно передавать готовые векторы).
- Метрика расстояния: по умолчанию косинусная, но доступны L2 и внутреннее произведение.
- Конфигурация HNSW-индекса: параметр M (количество связей на узел) и ef_construction (размер очереди при построении).
Я пришёл к тому, что для агентных систем лучше создавать отдельные коллекции под разные типы знаний. Например, у моего агент-ассистента есть коллекция «документация_продукта», коллекция «история_диалогов», коллекция «политики_компании» и коллекция «технические_спецификации». Это позволяет агенту выбирать, откуда искать, и применяет разные фильтры к разным источникам.
Метаданные: скрытая суперсила
Каждый документ в коллекции несёт не только текст и вектор, но и словарь метаданных. Это могут быть: источник документа, дата создания, автор, категория, уровень доступа, идентификатор сессии — что угодно. При поиске вы комбинируете семантический запрос с фильтрами по метаданным.
Пример из моей практики: агент ищет ответ на вопрос пользователя, но мне нужно, чтобы он использовал только документы не старше шести месяцев и только из отдела технической поддержки. Фильтр по метаданным решает это на уровне базы данных, не нагружая LLM лишним контекстом.
Установка и первый запуск: от нуля до рабочего прототипа за 20 минут
Я опишу путь, который прохожу каждый раз, когда начинаю новый проект с ChromaDB. Информация актуальна на октябрь 2026 года.
Установка
pip install chromadb
Для продакшена с серверным режимом:
pip install chromadb[server]
Если нужна интеграция с конкретным фреймворком агентов:
pip install chromadb langchain-community
или
pip install chromadb llama-index-vector-stores-chroma
Минимальный рабочий пример
import chromadb
# Создаём клиент с персистентным хранилищем
client = chromadb.PersistentClient(path="./chroma_data")
# Создаём коллекцию
collection = client.get_or_create_collection(
name="knowledge_base",
metadata={"hnsw:space": "cosine"}
)
# Добавляем документы
collection.add(
documents=[
"ChromaDB поддерживает фильтрацию по метаданным при поиске.",
"Агенты используют RAG для доступа к внешним знаниям.",
"Векторный поиск находит семантически близкие фрагменты текста."
],
metadatas=[
{"source": "docs", "topic": "chromadb"},
{"source": "docs", "topic": "agents"},
{"source": "docs", "topic": "search"}
],
ids=["doc_1", "doc_2", "doc_3"]
)
# Выполняем поиск
results = collection.query(
query_texts=["Как агент находит информацию?"],
n_results=2,
where={"source": "docs"}
)
print(results["documents"])
Этот код я запускаю первым делом в каждом новом проекте. Если он работает — значит, окружение настроено правильно и можно двигаться дальше.
Три режима работы
За два года я использовал все три режима и могу сказать, когда каждый из них уместен.
Режим в памяти (EphemeralClient). Данные живут только в рамках процесса. Подходит для тестов, прототипов, юнит-тестов. Я использую его, когда пишу интеграционные тесты для агентов: база создаётся, наполняется, проверяется и исчезает. Никаких файлов на диске.
Персистентный режим (PersistentClient). Данные пишутся на диск в указанную директорию. После перезапуска процесса всё на месте. Это мой выбор для одиночных агентов, локальных ассистентов и небольших проектов, где не нужен отдельный сервер.
Клиент-серверный режим (HttpClient). ChromaDB поднимается как отдельный сервис, а приложения подключаются к нему по HTTP. Я перехожу на этот режим, когда у меня несколько агентов должны работать с одной базой одновременно, или когда база данных живёт на отдельной машине. В продакшене я разворачиваю сервер через Docker-контейнер.
Сборка векторной базы знаний для агентов: пошаговый процесс
Здесь начинается самое интересное. Я опишу процесс так, как я его выстроил для себя после десятков итераций. Это не теория из документации — это выстраданная последовательность, которая экономит время и нервы.
Шаг 1. Определите, какие знания нужны агенту
Прежде чем загружать что-либо в базу, я сажусь и выписываю:
- Какие вопросы будет задавать пользователь агенту?
- Какие документы содержат ответы?
- Насколько часто обновляются эти документы?
- Нужен ли контроль доступа (не все документы доступны всем пользователям)?
- Какой объём данных: сотни документов или миллионы?
Для одного из проектов — агент поддержки в SaaS-компании — мне нужны были: база знаний из 3 200 статей, документация API на 400 страниц, история тикетов за два года и внутренние регламенты. Каждый тип данных я поместил в отдельную коллекцию.
Шаг 2. Подготовьте и очистите данные
Я убедился на горьком опыте: мусор на входе — мусор на выходе. Если загрузить в базу неструктурированный дамп из Confluence с HTML-тегами, битыми ссылками и дубликатами, агент будет выдавать кашу.
Мой чек-лист подготовки данных:
- Убираю HTML-разметку, служебные символы, повторяющиеся пробелы.
- Разделяю длинные документы на фрагменты (чанки) размером 300–800 токенов.
- Между чанками делаю перекрытие в 50–100 токенов, чтобы не терять контекст на границах.
- Для каждого чанка сохраняю метаданные: заголовок раздела, номер страницы, дату изменения, автора.
- Удаляю дубликаты по хешу нормализованного текста.
- Проверяю, что в тексте нет противоречий (если одна статья говорит «нажмите кнопку А», а другая — «нажмите кнопку Б», агент запутается).
Шаг 3. Выберите модель эмбеддингов
Это решение влияет на качество поиска больше, чем любой другой параметр. Я перепробовал множество моделей и могу поделиться наблюдениями.
Для русскоязычного контента хорошо работают модели на базе multilingual-e5, bge-m3 и их потомки. Для англоязычного — text-embedding-3-small и text-embedding-3-large от OpenAI, а также модели серии nomic-embed.
Критерии выбора, которые я применяю:
| Критерий | Что проверяю |
|---|---|
| Размерность вектора | Чем больше, тем точнее, но тем дороже хранение и медленнее поиск |
| Мультиязычность | Если база на русском — модель должна понимать русский без дообучения |
| Скорость генерации | Для пакетной загрузки миллионов документов это критично |
| Стоимость | Если модель облачная — считаю бюджет на токены |
| Контекстное окно | Максимальная длина входного текста, который модель может закодировать |
Один неочевидный момент: я всегда прогоняю тестовый набор из 50–100 вопросов с известными ответами через будущую базу и смотрю, попадают ли нужные фрагменты в топ-5 результатов. Если точность ниже 80%, меняю модель или пересматриваю стратегию чанкинга.
Шаг 4. Загрузите данные в ChromaDB
Для больших объёмов я пишу скрипт пакетной загрузки. Вот упрощённый вариант:
import chromadb
from chromadb.utils import embedding_functions
import os
# Определяем функцию эмбеддинга
ef = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
client = chromadb.PersistentClient(path="./agent_knowledge")
collection = client.get_or_create_collection(
name="product_docs",
embedding_function=ef,
metadata={"hnsw:space": "cosine", "hnsw:M": 32, "hnsw:construction_ef": 200}
)
# Пакетная загрузка по 500 документов
BATCH_SIZE = 500
for i in range(0, len(all_chunks), BATCH_SIZE):
batch = all_chunks[i:i+BATCH_SIZE]
collection.add(
documents=[c["text"] for c in batch],
metadatas=[c["metadata"] for c in batch],
ids=[c["id"] for c in batch]
)
print(f"Загружено {min(i+BATCH_SIZE, len(all_chunks))} из {len(all_chunks)}")
Важный нюанс: я всегда задаю параметр hnsw:M равным 32 вместо дефолтных 16 для коллекций больше 100 000 документов. Это увеличивает время построения индекса, но заметно повышает точность поиска. Параметр construction_ef ставлю в 200–400 для тех же целей.
Шаг 5. Подключите базу к агенту
Здесь пути расходятся в зависимости от фреймворка. Я покажу три варианта, которые использую чаще всего.
Прямой вызов из агента (без фреймворка):
def search_knowledge(query: str, top_k: int = 5, filters: dict = None) -> list:
"""Инструмент для агента: поиск по базе знаний."""
results = collection.query(
query_texts=[query],
n_results=top_k,
where=filters
)
return [
{"text": doc, "metadata": meta, "distance": dist}
for doc, meta, dist in zip(
results["documents"][0],
results["metadatas"][0],
results["distances"][0]
)
]
Эту функцию я регистрирую как инструмент (tool) агента. Когда агент решает, что ему нужна информация из базы, он вызывает этот инструмент с запросом и получает релевантные фрагменты.
Через LangChain:
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
vectorstore = Chroma(
persist_directory="./agent_knowledge",
collection_name="product_docs",
embedding_function=embeddings
)
# Используем как ретривер в цепочке агента
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 5, "fetch_k": 20}
)
Через LlamaIndex:
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import VectorStoreIndex, StorageContext
vector_store = ChromaVectorStore(chroma_collection=collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context
)
Шаг 6. Настройте стратегию поиска
Просто вызвать query и взять первый результат — путь к посредственному агенту. Я использую комбинацию приёмов:
MMR (Maximal Marginal Relevance). Вместо того чтобы возвращать пять почти идентичных чанков, MMR балансирует релевантность и разнообразие. Агент получает более широкую картину.
Гибридный поиск. Семантический поиск хорош для парафраз, но иногда пользователь ищет точный термин или номер ошибки. Я добавляю полнотекстовый поиск по метаданным или ключевым словам как дополнительный канал и объединяю результаты.
Реранкинг. После первичного отбора топ-20 результатов я прогоняю их через кросс-энкодер (например, модель на базе ms-marco или bge-reranker), который точнее оценивает релевантность пары «запрос — документ». В топ-5 для агента попадают уже действительно лучшие фрагменты.
Фильтрация по метаданным перед поиском. Если я знаю, что вопрос касается конкретного модуля продукта, я сразу сужаю область поиска фильтром. Это и быстрее, и точнее.
Мультиагентные системы с общей памятью на ChromaDB
Один из самых увлекательных проектов, в которых я участвовал, — система из четырёх агентов, обслуживающих техподдержку. Агент-классификатор определял тип обращения, агент-исследователь искал информацию, агент-составитель формировал ответ, а агент-контролёр проверял его на соответствие политикам.
Все четыре агента работали с одной базой ChromaDB, но с разными коллекциями и разными правами. Агент-исследователь имел доступ к технической документации и истории тикетов. Агент-контролёр читал только регламенты и политики. Агент-составитель получал результаты поиска от исследователя, но сам в базу не лез.
Архитектура выглядела так:
- Один сервер ChromaDB в Docker-контейнере.
- Четыре коллекции:
tech_docs,ticket_history,policies,response_templates. - Каждый агент подключался через HttpClient с собственным набором привилегий на уровне приложения.
- Агент-исследователь после каждого успешного поиска записывал в коллекцию
search_logметаданные: что искал, что нашёл, помогло ли это. Это позволяло мне анализировать качество поиска и улучшать базу.
Результат: среднее время ответа сократилось с четырёх минут до сорока секунд, а доля эскалаций на живого оператора упала на 38 процентов за три месяца.
Продвинутые возможности, которые я использую в продакшене
Обновление данных без пересоздания коллекции
Документация меняется. Я не пересоздаю коллекцию каждый раз. Вместо этого использую операцию upsert: если документ с таким идентификатором уже существует — он обновляется, если нет — создаётся.
collection.upsert(
documents=[updated_text],
metadatas=[{"version": 2, "updated_at": "2026-10-15"}],
ids=["doc_42"]
)
Для массовых обновлений я пишу скрипт, который сравнивает хеши текущих документов в базе с хешами в источнике и обновляет только изменённые.
Удаление устаревших данных
collection.delete(
where={"updated_at": {"$lt": "2026-04-01"}}
)
Я настроил еженедельный cron-скрипт, который удаляет чанки, чьи исходные документы были удалены из основной системы. Иначе агент продолжает ссылаться на несуществующие страницы.
Работа с несколькими коллекциями одновременно
Иногда ответ на вопрос агента требует информации из двух источников. Например, вопрос о гарантийном ремонте требует и технической спецификации (что сломалось), и политики компании (что покрывает гарантия). В таком случае агент делает два запроса к двум коллекциям и объединяет результаты в контексте.
tech_info = tech_collection.query(query_texts=[question], n_results=3)
policy_info = policy_collection.query(query_texts=[question], n_results=2)
combined_context = (
"Техническая информация:\n" + "\n".join(tech_info["documents"][0]) +
"\n\nПолитика компании:\n" + "\n".join(policy_info["documents"][0])
)
Персистентность и резервное копирование
В персистентном режиме вся база лежит в одной директории. Для бэкапа я просто копирую эту директорию. Для продакшена настроил автоматический снапшот раз в сутки через rsync на отдельный диск. Восстановление — это копирование обратно. Никакого дампа, никакого экспорта в SQL. Это подкупает.
Сравнение с альтернативами: где ChromaDB выигрывает, а где уступает
Я не фанатик одного инструмента. За два года я сравнивал ChromaDB с конкурентами в реальных задачах. Вот мои наблюдения.
| Критерий | ChromaDB | Qdrant | Weaviate | Pinecone | FAISS |
|---|---|---|---|---|---|
| Простота старта | Максимальная | Средняя | Средняя | Средняя | Низкая |
| Серверный режим | Есть | Есть | Есть | Только облако | Нет |
| Фильтрация метаданных | Встроенная | Мощная | Мощная | Ограниченная | Нет |
| Гибридный поиск | Базовый | Продвинутый | Продвинутый | Базовый | Нет |
| Масштабируемость | До миллионов | До миллиардов | До миллиардов | До миллиардов | Зависит от реализации |
| Экосистема для агентов | Отличная | Хорошая | Хорошая | Хорошая | Слабая |
| Стоимость | Бесплатно | Бесплатно / облако | Бесплатно / облако | Платно | Бесплатно |
Где ChromaDB выигрывает: скорость прототипирования, простота интеграции с Python-экосистемой агентов, минимальный операционный overhead. Для проектов до 5–10 миллионов векторов на одном сервере она работает прекрасно.
Где уступает: если вам нужна распределённая кластерная архитектура на миллиарды векторов, продвинутая гибридная фильтрация с вложенными условиями или строгие гарантии консистентности в мультирегинальном развёртывании — смотрите в сторону Qdrant или Weaviate.
Мой практический вывод: для 90 процентов агентных проектов, которые я видел и делал, ChromaDB закрывает потребности полностью. Переходить на более тяжёлые решения имеет смысл, когда вы упираетесь в конкретные ограничения, а не «на всякий случай».
Частые ошибки при построении векторной базы знаний
Я совершил большинство из них сам. Список ниже — это мои шрамы.
Ошибка 1: Слишком большие чанки. Я загружал целые страницы по 3 000–5 000 токенов. Эмбеддинг размывался, поиск возвращал нерелевантные результаты. Решение: 300–800 токенов на чанк, перекрытие 10–15 процентов.
Ошибка 2: Отсутствие метаданных. Без метаданных невозможно фильтровать. Агент получает чанки из всех источников одновременно и не понимает, что актуально, а что устарело. Решение: всегда добавлять минимум источник, дату и категорию.
Ошибка 3: Один гигантский collection на всё. Когда в одной коллекции лежат и технические мануалы, и маркетинговые тексты, и юридические документы, поиск становится шумным. Решение: разделять по типам знаний.
Ошибка 4: Игнорирование качества эмбеддингов. Взять первую попавшуюся модель из HuggingFace и надеяться на лучшее — плохая стратегия. Решение: тестировать на контрольном наборе вопросов.
Ошибка 5: Нет стратегии обновления. База загружена один раз и забыта. Через месяц половина документов устарела. Решение: автоматизировать синхронизацию с источником.
Ошибка 6: Слепая вера в топ-1 результат. Агент берёт первый чанк из выдачи и строит на нём ответ. Если поиск вернул нерелевантный фрагмент — агент галлюцинирует. Решение: всегда брать топ-3–5, реранкировать, проверять порог расстояния.
Ошибка 7: Не контролировать размер контекста. Если запихнуть в промпт все пять найденных чанков по 800 токенов каждый, плюс системный промпт, плюс историю диалога — контекст переполняется. Решение: жёсткий бюджет токенов на контекст из базы.
Оптимизация производительности: что я делаю, когда база растёт
На проекте с 2,5 миллиона чанков я столкнулся с тем, что запросы стали занимать 80–120 миллисекунд вместо привычных 10–15. Вот что помогло.
Увеличение M и ef_search. Параметр ef_search определяет, сколько кандидатов рассматривается при запросе. По умолчанию он равен 10. Я поднял до 64 для коллекций с миллионами записей. Точность выросла, время запроса увеличилось незначительно.
Квантование эмбеддингов. Если размерность вектора 1536, а точность до шестого знака после запятой вам не нужна — можно хранить векторы в формате float16 вместо float32. Экономия памяти почти вдвое, потеря точности в моём тесте составила менее 0,3 процента.
Разделение горячих и холодных данных. Часто используемые документы (последние три месяца) я держу в основной коллекции. Архивные данные выношу в отдельную коллекцию, к которой агент обращается только при явном запросе.
Кэширование частых запросов. Если агент отвечает на типовые вопросы, я кэширую результаты поиска в Redis на 24 часа. Повторный идентичный запрос не идёт в базу.
Батчинг при загрузке. Загружать документы по одному — медленно. Пакеты по 500–1 000 документов ускоряют индексацию в 8–12 раз.
Развёртывание в продакшене: Docker, мониторинг, безопасность
Когда прототип превращается в продукт, требования меняются. Я опишу свой продакшен-сетап.
Docker-контейнер
version: "3.9"
services:
chromadb:
image: chromadb/chroma:latest
ports:
- "8000:8000"
volumes:
- ./chroma_storage:/chroma/chroma
environment:
- CHROMA_SERVER_AUTH_CREDENTIALS=my_secret_token
restart: unless-stopped
Я всегда монтирую хранилище на отдельный SSD-диск. На HDD производительность падает в три-четыре раза при больших коллекциях.
Мониторинг
Я отслеживаю:
- Время ответа на запрос (p50, p95, p99).
- Количество документов в каждой коллекции.
- Загрузку памяти и CPU сервера.
- Частоту обращений каждого агента к базе.
- Долю запросов, возвращающих расстояние выше порога (то есть нерелевантные результаты).
Для последнего пункта я веду лог: если более 15 процентов запросов возвращают нерелевантные результаты, значит, база знаний не покрывает тему и нужно добавлять документы.
Безопасность
В клиент-серверном режиме я включаю аутентификацию по токену. На уровне приложения каждый агент имеет свой токен с ограниченным набором коллекций. Сетевой доступ к серверу ChromaDB закрыт фаерволом: наружу торчит только мой API-шлюз.
Необычные и малоизвестные факты о ChromaDB и векторных базах
За время работы я наткнулся на несколько вещей, которые удивили даже меня.
Факт первый. Алгоритм HNSW, лежащий в основе ChromaDB, был вдохновлён моделью «тесного мира» из социологии. Стэнли Милгрэм в 1967 году показал, что любые два человека в мире связаны цепочкой из шести рукопожатий. Малков и Яшунин перенесли эту идею на графы: если каждый узел связан с несколькими далёкими «мостами» и множеством близких соседей, поиск становится логарифмически быстрым.
Факт второй. Ранние версии ChromaDB использовали для хранения не специализированный формат, а обычный SQLite в качестве метаданного слоя. Вектора хранились отдельно в бинарных файлах. Это было прагматичное решение: SQLite надёжен, не требует настройки и знаком каждому разработчику. Позже архитектуру переработали, но следы этого решения видны в кодовой базе до сих пор.
Факт третий. Косинусное расстояние, которое ChromaDB использует по умолчанию, не зависит от длины векторов. Это означает, что документ из десяти слов и документ из тысячи слов могут оказаться «близки» в векторном пространстве, если их смысл совпадает. На практике это и хорошо (парафразы находятся), и плохо (короткие неточные запросы могут матчиться с длинными документами). Я решаю это реранкингом.
Факт четвёртый. В мультиагентных системах на базе ChromaDB можно реализовать «эпизодическую память»: агент записывает результаты своих действий как новые документы в коллекцию, а при следующем аналогичном запросе находит свой прошлый опыт. Я использовал это для агента-исследователя, который запоминал, какие источники уже проверил и что в них нашёл. Эффективность повторных сессий выросла на 40 процентов.
Факт пятый. При загрузке в ChromaDB документов на нескольких языках в одну коллекцию без мультиязычной модели эмбеддингов вы получите «языковые кластеры»: русские документы будут близки к русским, английские к английским, но кросс-язычный поиск работать не будет. Это не баг — это следствие того, как обучаются модели. Если нужен кросс-язычный поиск, выбирайте модель с явной мультиязычной поддержкой.
Как я интегрирую ChromaDB в цикл работы агента: архитектура RAG
RAG (Retrieval-Augmented Generation) — это паттерн, при котором агент перед генерацией ответа ищет релевантную информацию во внешней базе. ChromaDB здесь выступает в роли хранилища для этапа Retrieval.
Моя типовая архитектура:
- Пользователь задаёт вопрос.
- Агент-классификатор определяет тип вопроса и нужную коллекцию.
- Формируется поисковый запрос (иногда перефразированный или разбитый на подзапросы).
- ChromaDB возвращает топ-K чанков с метаданными.
- Реранкер отбирает лучшие фрагменты.
- Контекст вставляется в промпт агента.
- Агент генерирует ответ, ссылаясь на найденные фрагменты.
- Ответ проверяется на противоречия с контекстом.
- Если пользователь уточняет — цикл повторяется с учётом истории.
Ключевой момент на шаге 3: я не всегда передаю в поиск сырой вопрос пользователя. Иногда агент-планировщик разбивает сложный вопрос на три подзапроса, ищет по каждому отдельно, а потом объединяет результаты. Это резко повышает полноту ответа на комплексные вопросы.
Тестирование и оценка качества базы знаний
Я не выпускаю агента в продакшен, пока не прогоню батарею тестов. Для векторной базы знаний у меня есть три уровня проверки.
Уровень 1: Точность поиска. Беру 100 вопросов с известными ответами. Для каждого проверяю, попадает ли нужный чанк в топ-5. Цель: не менее 90 процентов.
Уровень 2: Полнота покрытия. Генерирую список тем, которые агент должен знать. Проверяю, что для каждой темы в базе есть минимум три релевантных чанка. Если нет — база неполная.
Уровень 3: Устойчивость к парафразам. Один и тот же вопрос задаю пятью разными способами. Все пять должны возвращать один и тот же ключевой фрагмент. Если нет — эмбеддинги плохо улавливают смысл.
Я автоматизировал эти проверки и запускаю их при каждом обновлении базы. Регрессия качества поиска ловится до того, как её заметят пользователи.
Работа с изображениями и мультимодальность
С 2025 года я начал использовать ChromaDB для хранения эмбеддингов изображений в агентных системах. Задача: агент поддержки получает скриншот ошибки от пользователя и должен найти в базе знаний статью с аналогичным скриншотом или описанием.
Схема работы:
- Изображение прогоняется через мультимодальную модель (например, CLIP или её аналоги), которая генерирует вектор.
- Вектор сохраняется в отдельную коллекцию
screenshotsс метаданными: описание, связанный документ, дата. - При поиске новый скриншот кодируется тем же способом и ищется по косинусному сходству.
Точность не идеальна — около 75 процентов для сложных интерфейсов. Но в комбинации с текстовым поиском по описанию ошибки это даёт мощный инструмент.
Этика и приватность данных в векторной базе
Когда я работаю с данными пользователей — историей диалогов, тикетами, персональными запросами — я соблюдаю несколько правил:
- Данные анонимизируются перед загрузкой в базу. Имена, адреса, номера телефонов заменяются псевдонимами.
- Коллекции с персональными данными шифруются на уровне файловой системы.
- У каждого агента минимально необходимый доступ. Агент-составитель ответов не видит сырые тикеты, только обезличенные фрагменты.
- Пользователь может запросить удаление своих данных. Я реализую это через удаление по метаданному фильтру
user_id. - Логи запросов к базе хранятся не более 90 дней.
Это не просто бюрократия. Однажды я обнаружил, что агент в тестовой среде «вспомнил» реальный диалог двухнедельной давности и процитировал его новому пользователю. С тех пор я параноидально отношусь к изоляции данных.
Будущее: что я ожидаю от ChromaDB и векторных баз к 2027 году
Пишу это в октябре 2026 года и вижу несколько трендов.
Во-первых, границы между векторными базами и классическими СУБД размываются. Полнотекстовый поиск, графовые запросы, реляционные джойны — всё это постепенно появляется в инструментах, которые начинались как «просто хранилище векторов».
Во-вторых, агенты становятся более автономными в управлении собственной базой знаний. Я экспериментирую с подходом, где агент сам решает, что запомнить, что забыть и что переиндексировать. ChromaDB с её простым API хорошо подходит для такого сценария: агент вызывает add, delete, update как инструменты.
В-третьих, растёт потребность в версионировании. Мне всё чаще нужно не просто «актуальное состояние базы», а возможность откатиться к состоянию на определённую дату. Пока я решаю это снапшотами, но хочется нативной поддержки.
В-четвёртых, мультимодальность становится стандартом. Агенты работают с текстом, изображениями, аудио, видео. Векторная база должна хранить все типы эмбеддингов и поддерживать кросс-модальный поиск.
Практический чек-лист: сборка векторной базы знаний для агента на ChromaDB
Я использую этот список каждый раз, когда начинаю новый проект. Сохраните его.
Подготовка:
- Определены типы знаний, которые нужны агенту.
- Собраны исходные документы и оценён их объём.
- Выбрана модель эмбеддингов, протестирована на контрольных запросах.
- Определена стратегия чанкинга: размер, перекрытие, привязка к структуре документа.
- Спроектирована схема метаданных: какие поля, какие значения, какие фильтры понадобятся.
Реализация:
- Созданы отдельные коллекции для каждого типа знаний.
- Настроены параметры HNSW: M, construction_ef, ef_search.
- Написан скрипт загрузки с батчингом.
- Реализована обработка ошибок при загрузке: логирование, повторные попытки.
- Данные загружены, проверены на полноту.
Интеграция с агентом:
- Написана функция-инструмент для поиска.
- Настроен ретривер: top_k, MMR, фильтры.
- Реализован реранкинг результатов.
- Определён бюджет токенов на контекст из базы.
- Агент протестирован на контрольных вопросах.
Эксплуатация:
- Настроено регулярное обновление данных.
- Настроено удаление устаревших записей.
- Организован мониторинг времени ответа и качества выдачи.
- Настроено резервное копирование.
- Определены алерты: рост доли нерелевантных результатов, падение скорости.
Безопасность и качество:
- Данные анонимизированы там, где необходимо.
- Доступ к коллекциям разграничен по агентам.
- Проводятся регулярные тесты качества поиска.
- Есть процедура отката при неудачном обновлении базы.
Часто задаваемые вопросы
Вопрос 1: Сколько документов можно хранить в ChromaDB на одном сервере?
На практике я комфортно работал с коллекциями до 5 миллионов чанков на сервере с 32 ГБ оперативной памяти и SSD. При использовании персистентного режима и настройки mmap можно работать и с большими объёмами, но время запроса будет расти. Для 10+ миллионов векторов я уже рассматриваю кластерные решения или переход на Qdrant. Но для подавляющего большинства агентных проектов 5 миллионов — это огромный запас.
Вопрос 2: Нужно ли мне пересоздавать коллекцию при смене модели эмбеддингов?
Да. Векторы, сгенерированные разными моделями, несовместимы друг с другом. При смене модели я создаю новую коллекцию, загружаю документы заново с новыми эмбеддингами и переключаю агента на неё. Старую коллекцию удаляю после проверки.
Вопрос 3: Как быть с документами, которые обновляются каждый час?
Я настраиваю пайплайн синхронизации: скрипт каждые 30 минут проверяет изменения в источнике, генерирует новые эмбеддинги для изменённых фрагментов и вызывает upsert. Для часто меняющихся данных выделяю отдельную коллекцию, чтобы не перемешивать стабильный справочник с динамическим контентом.
Вопрос 4: Можно ли использовать ChromaDB без интернета?
Да. Если вы используете локальную модель эмбеддингов (например, через SentenceTransformers), весь цикл работает офлайн. Единственное исключение — если вы используете облачную API-модель для генерации векторов. В остальном база полностью автономна.
Вопрос 5: Чем ChromaDB отличается от простого хранения векторов в NumPy-массиве?
Тремя вещами: индексирование (HNSW даёт логарифмическую сложность поиска вместо линейного перебора), метаданные с фильтрацией (в NumPy вам придётся писать это вручную) и персистентность (массив в памяти исчезает при перезапуске). Для 1 000 векторов разница незаметна. Для миллиона — критична.
Вопрос 6: Как оценить, что база знаний работает хорошо?
Я смотрю на три метрики: доля запросов, где нужный чанк в топ-3 (цель — выше 85 процентов); среднее косинусное расстояние для релевантных результатов (чем ниже, тем лучше); доля запросов, где агент не смог найти ответ и сказал «не знаю» при том, что ответ в базе есть (цель — ниже 5 процентов).
Вопрос 7: Стоит ли использовать ChromaDB для хранения истории диалогов агента?
Я делаю это для долгосрочной памяти: ключевые факты из диалогов извлекаются и сохраняются как отдельные документы с метаданными user_id и timestamp. При следующем разговоре агент ищет релевантные факты из прошлых бесед. Сырую историю диалога в базу не кладу — она слишком шумная и избыточная.
Вопрос 8: Что делать, если поиск возвращает нерелевантные результаты?
По моему опыту, причин три: плохая модель эмбеддингов (не понимает доменную лексику), слишком крупные чанки (смысл размывается) или запрос пользователя слишком далёк от формулировок в базе. Решения: сменить модель или дообучить, уменьшить размер чанков, добавить перефразирование запроса перед поиском.
Вопрос 9: Как масштабировать ChromaDB на несколько серверов?
Нативного кластерного режима у ChromaDB на октябрь 2026 года нет. Я решаю это шардированием на уровне приложения: разные коллекции живут на разных серверах, а роутер направляет запрос к нужному серверу. Для задач, требующих истинного распределённого кластера с репликацией, я перехожу на Qdrant или Weaviate.
Вопрос 10: Есть ли смысл использовать ChromaDB вместе с графовой базой знаний?
Да, и я это делаю. Векторная база находит релевантные фрагменты текста, а графовая база (например, на базе Neo4j) хранит связи между сущностями. Агент сначала ищет контекст через ChromaDB, потом обогащает его структурными связями из графа. Это даёт более полные и связные ответы на вопросы типа «какие компоненты зависят от модуля X».
Вывод: что делать прямо сейчас
Если вы строите агента и у него нет доступа к структурированным знаниям — он будет галлюцинировать. Это не вопрос «если», а вопрос «когда». Векторная база знаний — не роскошь, а базовый орган чувств для любого серьёзного агента.
Мои рекомендации:
Начните с ChromaDB, если ваш проект — это один-пять агентов, объём знаний до нескольких миллионов фрагментов, и вы хотите запуститься за дни, а не за недели. Установите библиотеку, создайте коллекцию, загрузите первые сто документов и прогоните тестовые запросы. Вы поймёте, подходит ли вам этот инструмент, за один вечер.
Не экономьте на подготовке данных. Чанкинг, метаданные, очистка — это 70 процентов успеха. Плохо подготовленные данные не спасёт никакая модель эмбеддингов и никакой алгоритм индексирования.
Тестируйте качество поиска до запуска в продакшен. Контрольный набор вопросов, пороги релевантности, мониторинг в рантайме. Без этого вы не узнаете, что агент врёт, пока не получите жалобу от пользователя.
Планируйте обновление базы с первого дня. Знания устаревают. Если у вас нет автоматического пайплайна синхронизации, через месяц база станет бесполезной.
Думайте о мультиагентности сразу, даже если сейчас у вас один агент. Разделяйте коллекции, проектируйте метаданные, закладывайте возможность расширения. Переделывать потом в разы дороже.
И последнее. Не бойтесь экспериментировать. Я перепробовал полдюжины подходов, прежде чем нашёл конфигурацию, которая работает. Каждая ошибка из списка выше стоила мне нескольких часов отладки, но каждая же сделала итоговую систему надёжнее. Векторная база знаний для агентов — это не статичный артефакт, а живой организм, который нужно кормить, лечить и развивать.
Удачи в построении. Ваши агенты заслуживают хорошей памяти.
Информация в статье актуальна на октябрь 2026 года. Названия библиотек, параметры конфигурации и архитектурные решения соответствуют состоянию экосистемы на момент написания.


Добавить комментарий