RAG exclusivo para VPC en un EC2: Ollama, Qdrant y LangChain
Sustituya el conocimiento tribal por un asistente basado en un manual citado que impide que los documentos salgan de su cuenta de AWS: tres contenedores en un EC2 con GPU.
El problema del oráculo de conocimiento no oficial
Toda organización de ingeniería cuenta con personas que poseen las respuestas: cómo funcionan los despliegues, qué establece la política de licencias, por qué Kafka superó a SQS. Eso funciona hasta que esas personas están de licencia, cambian de equipo o un nuevo empleado necesita la respuesta a las 11 PM durante un incidente.
Una forma mejor: cualquiera escribe “¿Puedo hacer un despliegue el viernes?” y recibe una respuesta documentada en segundos, basada en manuales reales e acompañada de una cita. No es una suposición. Ni una alucinación convincente.
Una restricción guió el diseño: ningún documento interno sale de la cuenta de AWS. Sin claves de OpenAI. Sin base de datos vectorial alojada en la nube. Todo tenía que ejecutarse dentro de la VPC en una sola máquina EC2.
Lo que obtienes
Un chat sencillo de Streamlit. Haz una pregunta; expande las fuentes para ver qué archivo y sección sirvieron de base en la respuesta. Ejemplo: las dependencias del servicio de pago se obtienen de deployment-runbook.md, sin inventar nombres de servidor.
Arquitectura: tres contenedores
Un comando docker compose up activa:
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 — modelo de incrustación local además de un modelo de chat (por ejemplo, llama3.1). Sin claves API, sin facturación por token.
- Qdrant — almacena vectores por bloques y recupera rápidamente los vecinos más cercanos.
- LangChain — recuperación → organización del contexto → generación, con citas.
Todo esto se ejecuta en una instancia GPU EC2 dentro de la VPC, de modo que los corpus nunca salen de dicha zona.
Conexión del flujo de preguntas
La cadena al estilo LCEL recupera, formatea e invita al modelo de chat local a responder:
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
)
)
La recuperación mantiene su carácter léxico y denso según la configuración establecida; la generación se realiza mediante Ollama. Las citaciones provienen de los metadatos de los fragmentos que devuelve Qdrant.
Notas sobre la ingesta y las operaciones
Inserte los documentos en formato markdown o runbooks en la ruta de ingesta, córtelos en fragmentos, incorpórelos con el modelo de incrustación local y actualícelos en Qdrant. Ejecute nuevamente el proceso cuando cambien los documentos. Monitoree el espacio en disco ocupado por los modelos, la memoria de la GPU para las solicitudes concurrentes y los grupos de seguridad para asegurarse de que solo la VPN o el bastión tengan acceso a Streamlit y Qdrant.
Por qué este patrón es óptimo para el conocimiento interno
Usted sacrifica la calidad máxima del modelo a cambio de la gravedad de los datos y el control de costos. Para preguntas como “¿qué dice nuestro runbook?”, ese sacrificio es adecuado. Incluso cuando Aaron no está disponible, el runbook sigue respondiendo, y la citación indica dónde verificar la información.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de salud y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Mantenga la implementación sencilla: utilice Compose, pruebas de estado y un único lugar para rotar modelos. La sencillez es la forma en que las herramientas internas sobreviven al contacto con los horarios reales de atención al cliente.
Restricción: los datos nunca salen de la VPC
La regla ineludible determinó cada elección de proveedor. Las API gestionadas compatibles con OpenAI quedaron descartadas, al igual que las bases de datos vectoriales alojadas en la nube. Incluso las subidas “temporales” a entornos de pruebas fueron rechazadas. Esto dejó solo una instancia EC2 con GPU, un archivo compose y las normas de IAM/grupos de seguridad.
Si su equipo legal no puede delimitar claramente el ENI de EC2, esta arquitectura no es adecuada para usted. Si sí pueden hacerlo, obtendrá las referencias necesarias sin tener que enviar PDFs a terceros.
Detalles del pipeline de ingesta
Los documentos de procedimientos y las soluciones de problemas se almacenan en formato markdown dentro de un directorio bajo vigilancia. Una tarea los divide en fragmentos superpuestos, los procesa con el modelo de incrustación local mediante Ollama y actualiza los datos en Qdrant junto con la metadata del camino y la sección correspondiente. En caso de fallo, se debe interrumpir la actualización de ese archivo en lugar de dejar sin procesar parcialmente un documento de política.
Vuelve a incrustar el contenido cuando cambie el hash de un archivo. Evita volver a incrustar todo el corpus con cada pequeña edición si puedes hacer diferencias por ruta.
Comportamiento en tiempo de consulta
La aplicación Streamlit envía la pregunta del usuario a un recuperador configurado con un valor pequeño de k. Los textos recuperados se formatean en una instrucción que indica al modelo de chat que responda únicamente a partir del contexto y que se niegue cuando este sea escaso. Las citaciones se generan a partir de los metadatos para que una persona pueda abrir la sección exacta del manual.
Ese comportamiento de negación ante contextos escasos es más importante que los ajustes de temperatura. Un texto sin sentido pero con una citación falsa es peor que “No veo eso en los manuales”.
Modos de fallo a considerar
Agotamiento de memoria de la GPU cuando alguien carga un modelo de chat más grande sin aumentar el tamaño de la instancia. Crecimiento excesivo del disco en Qdrant si se ingieren datos binarios innecesarios. Respuestas obsoletas tras un cambio en el proceso que nadie volvió a procesar. Pilas de Compose que vinculan puertos a 0.0.0.0 en una subred pública: hay que mantenerlas privadas.
La persona de guardia debe saber cómo reiniciar Ollama, reconstruir el volumen de Qdrant a partir del markdown original y verificar que una pregunta clave siga haciendo referencia al archivo correcto.
Qué significa “listo”
Un nuevo empleado ya en el segundo día puede hacer preguntas sobre despliegues sin tener que contactar a Aaron. Las respuestas incluyen rutas específicas. Los límites de la VPC están bajo revisión constante. El costo es un EC2 más almacenamiento: algo predecible. Eso ya constituye éxito suficiente para un asistente de conocimiento interno; solo se debe buscar mejorar la calidad de los modelos avanzados una vez que las referencias sean fiables.
Lecturas relacionadas
- RAG local completo con LangChain, Ollama y Chroma — Procese PDFs localmente, intégrelos con Ollama, guárdelos en Chroma y responda sin conexión; no se necesitan claves de API en la nube una vez que se hayan cargado los modelos.
- LangChain 1.x en la práctica: Cadenas, RAG, herramientas y agentes localmente — Aprenda a crear cadenas, sistemas de generación mejorada por recuperación de información, herramientas y RAG con agentes utilizando LangChain 1.x con una configuración local gratuita de Ollama, sin necesidad de claves de API.
- RAG con conciencia de dominio mediante registros MLflow y enrutamiento de LLM — Clasifique la intención, elija modelos de incrustación del dominio desde MLflow, recupere la información de la colección correspondiente en Qdrant y luego dirija el proceso de generación.