Quellengestützter Ernährungs-Chatbot: RAG mit FastAPI & Pinecone
Erfahren Sie, wie Sie einen zuverlässigen KI-Ernährungsassistenten entwickeln. Dieses Handbuch zeigt das Einbetten wissenschaftlicher Daten, Pinecone-Vektorsuche und FastAPI-Bereitstellung.
Auf dieser Seite
„Ist rotes Fleisch ungesund?" „Was ist die beste Quelle für veganes Protein?" Im Zeitalter der Informationsüberflutung ist es überraschend schwierig, eine klare, zuverlässige Antwort auf Ernährungsfragen zu erhalten. Man wird oft mit widersprüchlichen Blogbeiträgen, veraltetem Rat oder algorithmengesteuerten Inhalten konfrontiert, denen wissenschaftliche Grundlagen fehlen. Für Entwickler stellt dieses Chaos eine faszinierende technische Herausforderung dar: Können wir ein Tool entwickeln, das genaue, evidenzbasierte Ernährungsempfehlungen liefert?
Genau das werden wir in diesem Projekt-Showcase tun. Wir werden einen „NutriRAG"-Chatbot erstellen, einen intelligenten KI-Assistenten, der Ernährungsfragen ausschließlich auf Basis einer vertrauenswürdigen Wissensdatenbank wissenschaftlicher Artikel beantwortet.
Wir werden einen leistungsstarken, modernen KI-Stack verwenden:
- FastAPI für unser hochperformantes, benutzerfreundliches API-Backend.
- Pinecone als unsere verwaltete Vektordatenbank für blitzschnelle Ähnlichkeitssuchen.
- Sentence-Transformers zur Umwandlung unserer wissenschaftlichen Texte in aussagekräftige Vektor-Embeddings.
- Ein Großes Sprachmodell (LLM) wie eines von OpenAI oder ein lokales Modell zur Generierung menschlich anmutender Antworten.
Dieser Artikel führt Sie durch den gesamten Prozess, von der Datenaufbereitung bis zur Bereitstellung eines Live-API-Endpunkts. Sie werden die Kernkonzepte der Retrieval-Augmented Generation (RAG) verstehen und die praktischen Fähigkeiten erwerben, um Ihre eigenen spezialisierten Chatbots zu erstellen.
Das Problem verstehen
Standard-LLMs werden auf riesigen Mengen von Internetdaten trainiert. Obwohl sie unglaublich wissend sind, haben sie zwei wesentliche Einschränkungen für unseren Anwendungsfall:
- Halluzination: Sie können Fakten erfinden oder veraltete Informationen mit hoher Sicherheit präsentieren, was bei gesundheitsbezogenen Themen gefährlich ist.
- Fehlende Spezifität: Ihr Wissen ist allgemein. Sie wurden nicht speziell auf einem kuratierten Satz aktueller, peer-reviewed Ernährungswissenschaftspapiere trainiert.
Eine RAG-Architektur löst dieses Problem. Anstatt ein LLM einfach direkt zu fragen, rufen wir zunächst relevante Informationen aus unserer vertrauenswürdigen Wissensdatenbank ab und übergeben diese Informationen dann als Kontext an das LLM. Dies zwingt das Modell dazu, seine Antwort auf unsere bereitgestellten Quellen zu stützen, was die Genauigkeit drastisch erhöht und es uns ermöglicht, die genauen Dokumente zu zitieren, die zur Generierung der Antwort verwendet wurden.
Unser Ansatz ist besser, weil er überprüfbar, spezialisiert und vertrauenswürdig ist.
Voraussetzungen
Um mitzuarbeiten, benötigen Sie:
- Python 3.8+: Stellen Sie sicher, dass eine aktuelle Version von Python installiert ist.
- Pinecone-Konto: Registrieren Sie sich für ein kostenloses Kontingent auf Pinecone.io, um Ihren API-Schlüssel zu erhalten.
- LLM API-Schlüssel: Ein API-Schlüssel von einem Anbieter wie OpenAI oder einem lokal laufenden LLM, auf den Sie zugreifen können.
- Vertrautheit mit Python und APIs: Grundlegende Kenntnisse der Python-Programmierung und REST-API-Konzepte sind hilfreich.
- Kuratierte Dokumente: Eine Sammlung wissenschaftlicher Artikel oder vertrauenswürdiger Texte über Ernährung (z.B. im
.txt- oder.pdf-Format). Für dieses Projekt können Sie zunächst Abstracts von Quellen wie PubMed speichern.
Zunächst richten wir unsere Projektumgebung ein. Erstellen Sie ein neues Projektverzeichnis und eine virtuelle Umgebung:
mkdir nutritrag-project
cd nutritrag-project
python -m venv venv
source venv/bin/activate # Unter Windows verwenden Sie `venv\Scripts\activate`
Erstellen Sie als Nächstes eine requirements.txt-Datei mit den erforderlichen Bibliotheken.
# requirements.txt
fastapi
uvicorn[standard]
pinecone-client
sentence-transformers
python-dotenv
langchain
langchain-openai
Installieren Sie sie mit pip:
pip install -r requirements.txt
Erstellen Sie abschließend eine .env-Datei zur sicheren Speicherung Ihrer API-Schlüssel.
# .env
PINECONE_API_KEY="IHR_PINECONE_API_SCHLÜSSEL"
OPENAI_API_KEY="IHR_OPENAI_API_SCHLÜSSEL"
Schritt 1: Datenaufbereitung und Embedding
Was wir tun
Der erste Schritt in jedem RAG-System ist die Vorbereitung der Wissensdatenbank. Dies umfasst das Laden unserer Ernährungsartikel, das Aufteilen in handhabbare Chunks und das Umwandeln dieser Chunks in Vektor-Embeddings. Embeddings sind numerische Repräsentationen von Text, die deren semantische Bedeutung erfassen und es uns ermöglichen, ähnliche Textteile durch den Vergleich ihrer Vektoren zu finden.
Implementierung
Für diesen Schritt erstellen wir ein Skript namens ingest.py. Nehmen wir an, Sie haben Ihre wissenschaftlichen Artikel in einem Verzeichnis namens data/.
# ingest.py
import os
from dotenv import load_dotenv
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import DirectoryLoader
from langchain_community.embeddings import HuggingFaceEmbeddings
from pinecone import Pinecone, ServerlessSpec
# Umgebungsvariablen laden
load_dotenv()
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
PINECONE_INDEX_NAME = "nutritrag"
def ingest_data():
"""Dokumente laden, chunken, einbetten und in Pinecone speichern."""
# 1. Dokumente aus dem Datenverzeichnis laden
loader = DirectoryLoader('data/', glob="**/*.txt", show_progress=True)
documents = loader.load()
print(f"{len(documents)} Dokumente geladen.")
# 2. Dokumente in kleinere Teile aufteilen
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
docs = text_splitter.split_documents(documents)
print(f"In {len(docs)} Chunks aufgeteilt.")
# 3. Embeddings erstellen
embeddings_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
print("Embedding-Modell geladen.")
# 4. Pinecone initialisieren und Index erstellen
pc = Pinecone(api_key=PINECONE_API_KEY)
if PINECONE_INDEX_NAME not in pc.list_indexes().names():
pc.create_index(
name=PINECONE_INDEX_NAME,
dimension=384, # Dimension für all-MiniLM-L6-v2
metric='cosine',
spec=ServerlessSpec(
cloud='aws',
region='us-east-1'
)
)
print(f"Pinecone-Index '{PINECONE_INDEX_NAME}' erstellt.")
index = pc.Index(PINECONE_INDEX_NAME)
# 5. Embeddings in Batches an Pinecone übertragen
batch_size = 100
for i in range(0, len(docs), batch_size):
batch = docs[i:i+batch_size]
ids = [f"doc_{i+j}" for j in range(len(batch))]
texts = [doc.page_content for doc in batch]
embeds = embeddings_model.embed_documents(texts)
metadata = [doc.metadata for doc in batch]
# Vektoren für Upsert vorbereiten
vectors_to_upsert = []
for doc_id, embed, meta in zip(ids, embeds, metadata):
vectors_to_upsert.append({
"id": doc_id,
"values": embed,
"metadata": { "text": texts[ids.index(doc_id)], **meta }
})
index.upsert(vectors=vectors_to_upsert)
print(f"Batch {i//batch_size + 1} übertragen.")
print("Datenerfassung abgeschlossen.")
if __name__ == "__main__":
ingest_data()
Wie es funktioniert
- Laden: Wir verwenden
DirectoryLoadervon LangChain, um alle.txt-Dateien aus unseremdata-Ordner zu laden. - Chunking: Große Dokumente werden mit
RecursiveCharacterTextSplitterin kleinere, semantisch kohärente Chunks aufgeteilt. Dies stellt sicher, dass unsere Embeddings fokussiert und relevant sind. - Embedding: Wir verwenden das
all-MiniLM-L6-v2-Modell von Sentence-Transformers, eine beliebte Wahl aufgrund seines Gleichgewichts zwischen Leistung und Größe. Es wandelt jeden Text-Chunk in einen 384-dimensionalen Vektor um. - Speichern: Wir initialisieren den Pinecone-Client, erstellen einen neuen Index, falls dieser nicht existiert, und laden dann die Embeddings und Metadaten unserer Chunks hoch. Das Speichern des Originaltexts in den Metadaten ist entscheidend für den Generierungsschritt.
Um dies auszuführen, führen Sie einfach das Skript in Ihrem Terminal aus:
python ingest.py
Schritt 2: Aufbau des FastAPI-Backends
Was wir tun
Nun, da unser Wissen in Pinecone gespeichert ist, benötigen wir eine API, um es abzufragen. Wir erstellen eine FastAPI-Anwendung mit einem einzelnen Endpunkt /ask, der eine Benutzerfrage akzeptiert, relevanten Kontext aus Pinecone abruft und ein LLM verwendet, um eine Antwort zu generieren.
Implementierung
Erstellen Sie eine Datei namens main.py.
# main.py
import os
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from dotenv import load_dotenv
from pinecone import Pinecone
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_pinecone import PineconeVectorStore
# Umgebungsvariablen laden
load_dotenv()
# API-Schlüssel und Einstellungen initialisieren
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PINECONE_INDEX_NAME = "nutritrag"
# FastAPI-App initialisieren
app = FastAPI(
title="NutriRAG API",
description="Eine API zum Stellen von Ernährungsfragen, gestützt durch wissenschaftliche Artikel.",
version="1.0.0",
)
# Pydantic-Modell für den Anfragekörper
class QueryRequest(BaseModel):
question: str
# Embeddings und LLM initialisieren
embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
llm = ChatOpenAI(api_key=OPENAI_API_KEY, model_name='gpt-3.5-turbo', temperature=0.0)
# Pinecone-Vektorspeicher initialisieren
vectorstore = PineconeVectorStore.from_existing_index(
index_name=PINECONE_INDEX_NAME,
embedding=embeddings_model
)
# Die RetrievalQA-Kette erstellen
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(),
return_source_documents=True
)
@app.post("/ask")
async def ask_question(request: QueryRequest):
"""
Empfängt eine Ernährungsfrage, ruft relevanten Kontext aus Pinecone ab
und generiert eine Antwort mit einem LLM.
"""
if not request.question:
raise HTTPException(status_code=400, detail="Frage darf nicht leer sein.")
try:
response = qa_chain.invoke({"query": request.question})
answer = response.get("result", "Keine Antwort gefunden.")
source_documents = response.get("source_documents", [])
sources = []
if source_documents:
sources = [
{"source": doc.metadata.get("source", "Unbekannt"), "text": doc.page_content}
for doc in source_documents
]
return {"answer": answer, "sources": sources}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/")
def read_root():
return {"message": "Willkommen bei der NutriRAG API. Verwenden Sie den /docs-Endpunkt zum Testen."}
Wie es funktioniert
- FastAPI-Setup: Wir erstellen eine Standard-FastAPI-Anwendung.
- Pydantic-Modell:
QueryRequestdefiniert die erwartete JSON-Struktur für eingehende POST-Anfragen und gewährleistet Datenvalidierung. - Initialisierung: Wir laden unser Embedding-Modell und LLM. Außerdem initialisieren wir den
PineconeVectorStorevon LangChain, der als Brücke zu unserem Pinecone-Index dient. - RetrievalQA-Kette: Dies ist der Kern unserer RAG-Logik. Die
RetrievalQA-Kette von LangChain automatisiert den Prozess:- Sie nimmt die
querydes Benutzers. - Verwendet den
retriever(unseren Pinecone-Vektorspeicher), um die relevantesten Dokumente zu finden. - „Stopft" diese Dokumente in den Kontext des LLMs zusammen mit der Anfrage.
- Generiert die endgültige Antwort.
- Sie nimmt die
- Endpunkt-Logik: Der
/ask-Endpunkt ruft dieqa_chainauf, formatiert die Antwort, um die Antwort und die Quelldokumente einzubeziehen, und gibt sie als JSON zurück.
Alles zusammenfügen
Um Ihren API-Server auszuführen, verwenden Sie uvicorn:
uvicorn main:app --reload
Uvicorn startet einen lokalen Server, normalerweise unter http://127.0.0.1:8000.
Testen der API
FastAPI bietet automatische interaktive Dokumentation. Öffnen Sie Ihren Browser und navigieren Sie zu http://127.0.0.1:8000/docs. Sie sehen eine Swagger-UI, in der Sie den /ask-Endpunkt direkt testen können.
-
Klicken Sie auf den
/ask-Endpunkt, um ihn zu erweitern. -
Klicken Sie auf „Try it out".
-
Geben Sie im Anfragekörper eine Frage ein, die sich auf Ihre Dokumente bezieht, zum Beispiel:
json { "question": "Was ist der Einfluss von Omega-3-Fettsäuren auf die kognitive Funktion?" } -
Klicken Sie auf „Execute".
Sie sollten eine JSON-Antwort erhalten, die die vom LLM generierte Antwort und eine Liste der Quelltext-Chunks enthält, die zu ihrer Erstellung verwendet wurden.
Leistungsaspekte
- Embedding-Modell: Die Wahl des Embedding-Modells ist ein Kompromiss zwischen Geschwindigkeit, Kosten und Genauigkeit.
all-MiniLM-L6-v2eignet sich hervorragend für den Einstieg, aber größere Modelle können eine bessere Retrieval-Qualität bieten. - Batching: Bei der Erfassung großer Datenmengen sollten Sie immer in Batches verarbeiten und hochladen, um Ihre Verbindung nicht zu überlasten oder API-Ratenlimits zu erreichen. Unser
ingest.py-Skript tut dies. - LLM-Latenz: Der Generierungsschritt ist oft der langsamste. Erwägen Sie die Verwendung kleinerer, schnellerer LLMs oder das Streamen der Antwort für ein besseres Benutzererlebnis.
Best Practices für die Sicherheit
- Umgebungsvariablen: Hartcodieren Sie niemals API-Schlüssel in Ihrem Quellcode. Verwenden Sie eine
.env-Datei für die lokale Entwicklung und Umgebungsvariablen in der Produktion. - Eingabevalidierung: Die Pydantic-Integration von FastAPI übernimmt die grundlegende Eingabevalidierung und schützt vor vielen gängigen Injection-Angriffen.
- Authentifizierung: Für eine Produktionsanwendung schützen Sie Ihren API-Endpunkt mit einem Authentifizierungsmechanismus wie OAuth2 oder API-Schlüsseln, um den Zugriff zu kontrollieren.
Fazit
Sie haben erfolgreich ein vollständiges Retrieval-Augmented Generation-System gebaut! Dieses Projekt demonstriert, wie man einen spezialisierten, vertrauenswürdigen Chatbot erstellt, der seine Antworten auf einer kuratierten Wissensdatenbank stützt. Durch die Kombination der Stärken von FastAPIs Leistung, Pinecones effizienter Vektorsuche und der generativen Kraft von LLMs haben Sie ein leistungsstarkes Tool zur Bekämpfung von Fehlinformationen in einem kritischen Bereich wie der Ernährung geschaffen.
Von hier aus können Sie das Projekt erweitern durch:
- Erstellung einer Frontend-Oberfläche mit einem Framework wie React oder Streamlit.
- Experimentieren mit verschiedenen Chunking-Strategien und Embedding-Modellen.
- Hinzufügen einer Konversationshistorie für ein zustandsbehafteteres Chatbot-Erlebnis.
Ressourcen
- FastAPI-Dokumentation: https://fastapi.tiangolo.com/
- Pinecone-Dokumentation: https://docs.pinecone.io/
- LangChain Python-Dokumentation: https://python.langchain.com/
- Sentence-Transformers-Bibliothek: https://www.sbert.net/
Kurzübersicht
| Aspekt | Wichtige Informationen |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und -bewusstsein |
| Maßnahme | Besprechen Sie dies mit Ihrem Arzt |
| Häufigkeit | Nach Empfehlung Ihres Behandlungsteams |
| Evidenz | Basierend auf aktuellen klinischen Richtlinien |
Häufig gestellte Fragen
F: Woher weiß ich, ob dies auf mich zutrifft? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrem Arzt, um Ihre individuellen Risikofaktoren einzuschätzen und geeignete nächste Schritte zu bestimmen.
F: Was sollte ich meinen Arzt fragen? A: Gute Fragen sind: „Was bedeuten meine Ergebnisse?", „Was sind meine Optionen?", „Welche Lebensstiländerungen würden helfen?" und „Wann sollte ich nachuntersuchen?"
F: Gibt es Lebensstiländerungen, die helfen können? A: Die meisten Erkrankungen profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), качественый Schlaf (7-9 Stunden), Stressbewältigung und Vermeidung von Tabak.
Haftungsausschluss
Dieser Artikel dient nur zu Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie immer einen qualifizierten Arzt, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Pflege treffen. Individuelle Umstände variieren, und was für eine Person geeignet ist, ist möglicherweise nicht für eine andere geeignet.
Wenn Sie einen medizinischen Notfall haben, rufen Sie sofort 112 (oder Ihre lokale Notrufnummer) an.