Startseite / Artikel / VPC-einschränkter RAG auf einem EC2: Ollama, Qdrant und LangChain

VPC-einschränkter RAG auf einem EC2: Ollama, Qdrant und LangChain

Ersetzen Sie das traditionelle Wissen durch einen zitierten Runbook-Assistenten, der verhindert, dass Dokumente Ihr AWS-Konto verlassen – drei Container auf einem GPU-EC2.

1723 Wörter

Das Problem des inoffiziellen Wissensorakels

Jede Ingenieurdienststelle hat Mitarbeiter, die die Antworten kennen: Wie Deploys funktionieren, was die Urlaubsrichtlinien vorschreiben, warum Kafka SQS verdrängt hat. Das funktioniert, solange diese Mitarbeiter im Urlaub sind, das Team wechseln oder ein Neuzugang mitten in der Nacht während eines Incidents eine Antwort benötigt.

Eine bessere Lösung: Jeder kann „Kann ich am Freitag deployen?“ eingeben und innerhalb von Sekunden eine dokumentierte Antwort erhalten – basierend auf echten Runbooks mit Quellenangabe. Keine Vermutung. Keine künstlich erzeugte Fiktion.

Eine Einschränkung leitete die Gestaltung vor: kein internes Dokument verlässt das AWS-Konto. Keine OpenAI-Schlüssel. Keine gehosteten Vektor-Datenbanken. Alles musste innerhalb der VPC auf einem einzigen EC2-Server laufen.

Was Sie erhalten

Ein einfacher Streamlit-Chat. Stellen Sie eine Frage; erweitern Sie die Quellen, um zu sehen, welche Datei und welcher Abschnitt die Antwort begründet hat. Beispiel: Die Abhängigkeiten des Zahlungsdienstes werden aus deployment-runbook.md beantwortet, es werden keine Hostnamen erfunden.

Architektur – drei Container

Mit einem Befehl docker compose up werden folgende Komponenten gestartet:

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 – lokales Embedding-Modell sowie ein Chat-Modell (zum Beispiel llama3.1). Keine API-Schlüssel, keine Abrechnung pro Token.
  • Qdrant – speichert Chunk-Vektoren und holt schnell die nächstgelegenen Nachbarn ab.
  • LangChain – Abruf → Zusammenfassung des Kontexts → Erstellung der Antwort mit Quellenangaben.

Alles befindet sich auf einer GPU-EC2-Instanz innerhalb des VPC, sodass Korpora niemals die Grenzen überschreiten.

Vernetzung des Anfragepfades

Die LCEL-ähnliche Kette ruft das lokale Chat-Modell ab, formatiert die Eingaben und gibt sie an dieses Modell weiter:

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
    )
)

Die Abruffunktion bleibt so, wie Sie sie konfiguriert haben – lexikalisch dicht – während die Generierung weiterhin über Ollama erfolgt. Die Zitate stammen aus den Metadaten der von Qdrant zurückgegebenen Blöcke.

Einspeisung und Betriebshinweise

Laden Sie Markdown-Dateien oder Runbooks in den Einspeisungspfad, teilen Sie sie in Blöcke auf, embedden Sie diese mit dem lokalen Embedding-Modell und fügen Sie sie in Qdrant ein. Führen Sie den Vorgang erneut aus, wenn sich die Dokumente ändern. Überwachen Sie den Festplattenspeicher für die Modelle, den GPU-Speicher für gleichzeitige Anfragen sowie die Sicherheitsgruppen, damit nur VPN-Verbindungen bzw. Bastionserver auf Streamlit und Qdrant zugreifen können.

Warum dieses Muster für interne Kenntnisse vorteilhaft ist

Man tauscht die maximale Modellqualität gegen Datenkonsistenz und Kostenkontrolle ein. Für die Frage „Was steht in unserem Runbook?“ ist dieser Tausch sinnvoll. Auch wenn Aaron nicht erreichbar ist, liefert das Runbook weiterhin Antworten – und die Zitate zeigen an, wo man nachprüfen kann.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Machen Sie den Deployment-Prozess langweilig: Use von Compose, Health Checks sowie einen einzigen Ort zur Rotation der Modelle. Genau dadurch überstehen interne Tools den Umgang mit den tatsächlichen Bereitschaftsplänen.

Beschränkung: Die Daten verlassen niemals die VPC

Die unverhandelbare Regel bestimmte jede Wahl des Anbieters. Verwaltete, OpenAI-kompatible APIs waren ausgeschlossen, ebenso gehostete Vektordatenbanken. Selbst „temporäre“ Staging-Uploads waren nicht zulässig. Damit blieben nur eine einzige EC2-GPU-Instanz, ein compose-Datei sowie strenge Regeln bezüglich IAM und Sicherheitsgruppen übrig.

Falls Ihr Rechtsteam keine klare Grenze für die EC2-ENI ziehen kann, eignet sich diese Architektur nicht für Sie. Kann es das jedoch, können Sie Referenzen erhalten, ohne PDFs an Dritte zu senden.

Details zum Eingabepipeline

Runbooks und ADRs werden als Markdown-Dateien in einem überwachten Verzeichnis abgelegt. Eine Aufgabe teilt sie in überschneidende Abschnitte auf, embeddet diese mithilfe des lokalen Embedding-Modells über Ollama und fügt die entsprechenden Punkte mit Pfad- sowie Abschnittsmetadaten in Qdrant ein. Bei Fehlern sollte die Einfügung für diese Datei abgebrochen werden, anstatt halb fertige Richtliniedokumente stillschweigend zu ignorieren.

Ersetzen Sie den Inhalt erneut, wenn sich der Hash einer Datei ändert. Vermeiden Sie es, das gesamte Korpus bei jeder kleinen Änderung erneut einzubinden, wenn Sie durch Pfadunterschiede differenzieren können.

Verhalten zur Abfragezeit

Die Streamlit-App sendet die Frage des Benutzers an einen Retriever, der mit einem kleinen k konfiguriert ist. Die abgerufenen Texte werden in einen Prompt formatiert, der das Chat-Modell anweist, ausschließlich auf Basis des Kontexts zu antworten und bei mangelndem Kontext abzulehnen. Zitate werden aus Metadaten generiert, damit ein Mensch den genauen Abschnitt des Runbooks öffnen kann.

Dieses Verhalten, bei mangelndem Kontext abzulehnen, ist wichtiger als Einstellungen zur „Temperatur“. Flüssiger Unsinn mit einer falschen Quelle ist schlimmer als „Das sehe ich in den Runbooks nicht.“

Fehlermöglichkeiten, die berücksichtigt werden müssen

GPU-OOM, wenn jemand ein größeres Chat-Modell herunterlädt, ohne die Instanzgröße zu erhöhen. Wachstum der Qdrant-Disks, wenn binäre Unmengen eingeführt werden. Veraltete Antworten nach einem Prozesswechsel, die niemand neu eingeführt hat. Compose-Stacks, die Ports auf 0.0.0.0 in einem öffentlichen Subnetz binden – halten Sie sie privat.

Die On-Call-Mitarbeiter sollten wissen, wie man Ollama neu startet, das Qdrant-Volumen aus dem Quell-Markdown wieder aufbaut und überprüft, ob eine „goldene“ Frage weiterhin auf die richtige Datei verweist.

Wie „erledigt“ aussieht

Ein Neuzugang kann bereits am zweiten Tag Fragen zur Bereitstellung stellen, ohne Aaron zu kontaktieren. Die Antworten enthalten die entsprechenden Pfade. Die Grenzen des VPCs werden ständig überprüft. Die Kosten bestehen aus einem EC2-Instanz und Speicher – vorhersehbar. Das reicht bereits aus, um einen internen Wissensassistenten erfolgreich zu machen; erst wenn die Zitierungen zuverlässig sind, sollte man nach höherer Modellqualität streben.