RAG только в VPC на одном EC2: Ollama, Qdrant и LangChain
Замените племенные знания на вспомогательное руководство с ссылками, которое никогда не позволяет документам покидать вашу учетную запись AWS — три контейнера на GPU EC2.
Проблема неофициального источника знаний
В каждой инженерной команде есть люди, которые знают ответы: как происходит развертывание, что предусматривает политика отпусков, почему Kafka победил SQS. Всё работает до тех пор, пока эти люди не уходят в отпуск, не меняют команду или пока новый сотрудник в полночь во время инцидента не нуждается в точной информации.
Лучший вариант: любой может написать «Могу ли я развернуть что-то в пятницу?», и через несколько секунд получить задокументированный ответ, основанный на реальных руководствах с указанием источника. Ни догадок, ни вымышленных ответов.
Одно ограничение определило дизайн: никакие внутренние документы не выходят за пределы учётной записи AWS. Никаких ключей OpenAI. Никаких хостингованных векторных баз данных. Всё должно работать внутри VPC на одном сервере EC2.
Что вы получаете
Простой чат в Streamlit. Задайте вопрос; раскройте источники, чтобы увидеть, какой файл и раздел легли в основу ответа. Пример: информация о зависимостях платежного сервиса берется из deployment-runbook.md, без выдумки имен хостов.
Архитектура — три контейнера
Команда docker compose up запускает:
version: "3.9"
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
qdrant:
image: qdrant/qdrant:v1.18.2
container_name: qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__ENABLE_CORS=true
restart: unless-stopped
app:
build:
context: .
dockerfile: Dockerfile
container_name: rag-app
ports:
- "8501:8501"
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- QDRANT_URL=http://qdrant:6333
- COLLECTION=company_docs
- EMBED_MODEL=nomic-embed-text
- LLM_MODEL=llama3.1:8b
- S3_CORPUS_BUCKET=${S3_CORPUS_BUCKET}
- S3_CORPUS_PREFIX=${S3_CORPUS_PREFIX:-corpus/}
- AWS_REGION=${AWS_REGION:-us-east-1}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
volumes:
- corpus_data:/app/corpus
- ./scripts:/app/scripts
depends_on:
- ollama
- qdrant
restart: unless-stopped
volumes:
ollama_data:
qdrant_data:
corpus_data:
- Ollama — локальная модель эмбеддингов плюс модель чата (например, llama3.1). Нет API-ключей, нет счетов за каждый токен.
- Qdrant — хранит векторы фрагментов и быстро находит ближайших соседей.
- LangChain — получение данных → форматирование контекста → генерация с цитатами.
Все это работает на инстанце GPU EC2 внутри VPC, поэтому корпуса данных никогда не покидают эту сеть.
Схема обработки запросов
Цепочка в стиле LCEL получает данные, форматирует их и отправляет в локальную модель чата:
from operator import itemgetter
from langchain_core.runnables import RunnableLambda, RunnableParallel
chain = RunnableParallel(
question=itemgetter("question"), # pass question through
docs=itemgetter("question") | retriever, # embed + search Qdrant
).assign(
answer=(
{"context": itemgetter("docs") | RunnableLambda(format_docs),
"question": itemgetter("question")}
| prompt_template # inject into system/human message
| llm # send to Ollama
| str_parser # extract text from response
)
)
Поиск остается лексикально насыщенным в соответствии с вашими настройками; генерация осуществляется через Ollama. Цитаты берутся из метаданных, возвращаемых Qdrant.
Примечания по загрузке и эксплуатации
Поместите markdown-документы и руководства в путь загрузки, разделите их на части, интегрируйте с местной моделью эмбеддингов и добавьте в Qdrant. Перезапустите процесс при изменении документов. Следите за использованием диска для хранения моделей, памяти GPU при одновременных запросах, а также за группами безопасности, чтобы только VPN или бастион могли обращаться к Streamlit и Qdrant.
Почему этот подход эффективен для внутренних знаний
Вы идете на компромисс между высоким качеством модели и контролем затрат. Для вопросов типа «что говорится в нашем руководстве?» такой компромисс оправдан. Даже когда Аарон отсутствует, руководство продолжает давать ответы — причем цитаты указывают, где можно это проверить.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Сделайте процесс развертывания простым: использование Compose, проверки работоспособности и единое место для замены моделей. Именно простота позволяет внутренним инструментам выдерживать работу с реальными графиками дежурств.
Ограничение: данные никогда не покидают VPC
Это абсолютное правило определило выбор всех поставщиков. Управляемые API, совместимые с OpenAI, были исключены. Векторные базы данных, размещаемые на серверах сторонних провайдеров, также были исключены. Даже «временная» загрузка данных на промежуточные серверы не допускалась. В итоге остался лишь один экземпляр GPU-устройства EC2, файл compose и строгие правила управления IAM/группами безопасности.
Если ваша юридическая команда не может четко определить границы использования ENI в EC2, такая архитектура не подходит вам. Если же они могут это сделать, вы сможете соблюдать требования без необходимости отправки PDF-файлов третьим сторонам.
Подробности конвейера обработки данных
Инструкции и регламенты обработки сохраняются в формате markdown в отведенной директории. Задача разделяет их на части, встраивает данные с помощью локальной модели генерации векторов Ollama и добавляет информацию о пути и разделе в базу данных Qdrant. При сбоях должна прерываться обработка только данного файла, а не происходить молчаливое игнорирование части документа с правилами.
Повторно встраивайте данные при изменении хэша файла. Если возможно использовать сравнение по пути, избегайте повторного встраивания всего корпуса при каждой незначительной правке.
Поведение во время запроса
Приложение Streamlit отправляет вопрос пользователя в механизм поиска, настроенный с небольшим значением k. Полученные тексты форматируются в запрос, указывающий модели чата отвечать только на основе контекста и отказываться, если контекст недостаточен. Цитаты отображаются из метаданных, чтобы человек мог открыть точный раздел руководства.
Это поведение по отказу при недостаточном контексте важнее, чем настройки параметра температуры. Бессмысленные ответы с фальшивыми цитатами хуже, чем фраза «Я не вижу этого в руководстве».
Возможные сбои, на которые следует ориентироваться
Проблема нехватки памяти GPU, когда кто-то загружает более крупную модель чата, не увеличивая размер инстанса. Рост объема диска Qdrant при загрузке бинарных ненужных данных. Устаревшие ответы после изменения процесса, которые никто не перезагружал. Стеки Compose, привязанные к портам 0.0.0.0 в публичном сабнете — их следует оставить приватными.
Сотрудник, отвечающий за работу в часы кризисов, должен знать, как перезапустить Ollama, воссоздать том Qdrant из исходного markdown-файла и проверить, что «золотой» вопрос по-прежнему ссылается на правильный файл.
Как выглядит «завершенная» работа
Новый сотрудник уже на второй день может задавать вопросы по развертыванию без обращения к Аарону. Ответы содержат пути к файлам. Границы VPC находятся под контролем и регулярно проверяются. Расходы составляют один EC2-сервер плюс хранилище — что является предсказуемым показателем. Этого достаточно для внутреннего помощника по знаниям; стремление к высокому качеству моделей следует преследовать только после того, как ссылки станут надежными.