LangChain-Anwendung in Cloud Run erstellen und bereitstellen

1. Übersicht

In diesem Codelab erfahren Sie, wie Sie eine LangChain-App bereitstellen, mit der Sie über Gemini Fragen zu den Cloud Run-Releasenotes stellen können.

Hier ein Beispiel für die Funktionsweise der Anwendung: Wenn Sie die Frage „Kann ich einen Cloud Storage-Bucket als Volume in Cloud Run bereitstellen?“ stellen, antwortet die Anwendung mit „Ja, seit dem 19. Januar 2024“ oder Ähnliches.

Um fundierte Antworten zu liefern, ruft die App zuerst Cloud Run-Releasenotes ab, die der Frage ähneln, und fordert dann Gemini sowohl mit der Frage als auch mit den Releasenotes auf. (Dieses Muster wird allgemein als RAG bezeichnet.) Hier ist ein Diagramm der Architektur der App:

2. Einrichtung und Anforderungen

Prüfen wir zuerst, ob Ihre Entwicklungsumgebung richtig eingerichtet ist.

  • Sie benötigen ein Google Cloud-Projekt, um die für die App erforderlichen Ressourcen bereitzustellen.
  • Damit Sie die Anwendung bereitstellen können, muss auf Ihrem lokalen Computer gcloud installiert, authentifizierte und für die Verwendung des Projekts konfiguriert sein.
    • gcloud auth login
    • gcloud config set project
  • Wenn Sie die Anwendung auf Ihrem lokalen Computer ausführen möchten, müssen Sie darauf achten, dass Ihre Standardanmeldedaten für Anwendungen korrekt eingerichtet sind. Dazu gehört auch das Festlegen des Kontingentprojekts.
    • gcloud auth application-default login
    • gcloud auth application-default set-quota-project
  • Außerdem müssen Sie die folgende Software installiert haben:
    • Python (Version 3.11 oder höher erforderlich)
    • Die LangChain-Befehlszeile
    • poetry für das Abhängigkeitsmanagement
    • pipx zum Installieren und Ausführen der LangChain-Befehlszeile und von Poetry in isolierten virtuellen Umgebungen

In diesem Blog finden Sie Informationen zur Installation der Tools, die für diese Schritt-für-Schritt-Anleitung erforderlich sind.

Cloud Workstations

Anstelle Ihres lokalen Computers können Sie auch Cloud Workstations in Google Cloud verwenden. Hinweis: Ab April 2024 wird eine Python-Version niedriger als 3.11 ausgeführt. Möglicherweise müssen Sie Python aktualisieren, bevor Sie beginnen.

Cloud APIs aktivieren

Führen Sie zuerst den folgenden Befehl aus, um sicherzustellen, dass Sie das richtige Google Cloud-Projekt konfiguriert haben:

gcloud config list project

Wenn das richtige Projekt nicht angezeigt wird, können Sie es mit dem folgenden Befehl festlegen:

gcloud config set project <PROJECT_ID>

Aktivieren Sie jetzt die folgenden APIs:

gcloud services enable \
  bigquery.googleapis.com \
  sqladmin.googleapis.com \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  run.googleapis.com \
  secretmanager.googleapis.com

Region auswählen

Google Cloud ist weltweit an vielen Standorten verfügbar. Wählen Sie einen aus, um die Ressourcen bereitzustellen, die Sie für dieses Lab verwenden möchten. Legen Sie die Region als Umgebungsvariable in Ihrer Shell fest. Diese Variable wird in späteren Befehlen verwendet:

export REGION=us-central1

3. Vektordatenbankinstanz erstellen

Ein wichtiger Teil dieser App ist das Abrufen von Releasenotes, die für die Frage des Nutzers relevant sind. Wenn Sie eine Frage zu Cloud Storage stellen, soll dem Prompt der folgende Release-Hinweis hinzugefügt werden:

Mithilfe von Texteinbettungen und einer Vektordatenbank können Sie semantisch ähnliche Releasenotes finden.

Ich zeige Ihnen, wie Sie PostgreSQL in Cloud SQL als Vektordatenbank verwenden. Das Erstellen einer neuen Cloud SQL-Instanz dauert einige Zeit. Fangen wir also gleich damit an.

gcloud sql instances create sql-instance \
  --database-version POSTGRES_14 \
  --tier db-f1-micro \
  --region $REGION

Sie können diesen Befehl ausführen und mit den nächsten Schritten fortfahren. Später müssen Sie eine Datenbank erstellen und einen Nutzer hinzufügen. Aber verschwenden wir jetzt nicht unsere Zeit damit, auf das rotierende Symbol zu starren.

PostgreSQL ist ein relationaler Datenbankserver. Bei jeder neuen Instanz von Cloud SQL ist standardmäßig die Erweiterung pgvector installiert. Sie können sie also auch als Vektordatenbank verwenden.

4. LangChain-App erstellen

Um fortzufahren, müssen Sie die LangChain-Befehlszeile und Poetry zum Verwalten von Abhängigkeiten installiert haben. So installieren Sie sie mit pipx:

pipx install langchain-cli poetry

Erstellen Sie mit dem folgenden Befehl ein Skelett für die LangChain-App. Geben Sie auf Nachfrage den Namen run-rag für den Ordner ein und überspringen Sie die Installation von Paketen, indem Sie die Eingabetaste drücken:

langchain app new

Zum Verzeichnis run-rag wechseln und Abhängigkeiten installieren

poetry install

Sie haben gerade eine LangServe-App erstellt. LangServe umhüllt FastAPI in einer LangChain-Kette. Es verfügt über einen integrierten Playground, über das du ganz einfach Prompts senden und die Ergebnisse einschließlich aller Zwischenschritte prüfen kannst. Öffnen Sie den Ordner run-rag in Ihrem Editor und sehen Sie sich die Inhalte an.

5. Indexierungsjob erstellen

Bevor Sie mit dem Erstellen der Webanwendung beginnen, sollten Sie prüfen, ob die Cloud Run-Releasenotes in der Cloud SQL-Datenbank indexiert sind. In diesem Abschnitt erstellen Sie einen Indexierungsjob, der Folgendes tut:

Der Indexierungsjob nimmt Release-Notes, wandelt sie mithilfe eines Text-Embedding-Modells in Vektoren um und speichert sie in einer Vektordatenbank. So können Sie anhand der semantischen Bedeutung nach ähnlichen Release Notes suchen.

Erstellen Sie im Ordner run-rag/app eine Datei indexer.py mit folgendem Inhalt:

import os
from google.cloud.sql.connector import Connector
import pg8000
from langchain_community.vectorstores.pgvector import PGVector
from langchain_google_vertexai import VertexAIEmbeddings
from google.cloud import bigquery


# Retrieve all Cloud Run release notes from BigQuery 
client = bigquery.Client()
query = """
SELECT
  CONCAT(FORMAT_DATE("%B %d, %Y", published_at), ": ", description) AS release_note
FROM `bigquery-public-data.google_cloud_release_notes.release_notes`
WHERE product_name= "Cloud Run"
ORDER BY published_at DESC
"""
rows = client.query(query)

print(f"Number of release notes retrieved: {rows.result().total_rows}")

# Set up a PGVector instance 
connector = Connector()

def getconn() -> pg8000.dbapi.Connection:
    conn: pg8000.dbapi.Connection = connector.connect(
        os.getenv("DB_INSTANCE_NAME", ""),
        "pg8000",
        user=os.getenv("DB_USER", ""),
        password=os.getenv("DB_PASS", ""),
        db=os.getenv("DB_NAME", ""),
    )
    return conn

store = PGVector(
    connection_string="postgresql+pg8000://",
    use_jsonb=True,
    engine_args=dict(
        creator=getconn,
    ),
    embedding_function=VertexAIEmbeddings(
        model_name="textembedding-gecko@003"
    ),
    pre_delete_collection=True  
)

# Save all release notes into the Cloud SQL database
texts = list(row["release_note"] for row in rows)
ids = store.add_texts(texts)

print(f"Done saving: {len(ids)} release notes")

Fügen Sie die erforderlichen Abhängigkeiten hinzu:

poetry add \
  "cloud-sql-python-connector[pg8000]" \
  langchain-google-vertexai==1.0.5 \
  langchain-community==0.2.5 \
  pgvector

Datenbank und Nutzer erstellen

Erstellen Sie die Datenbank release-notes in der Cloud SQL-Instanz sql-instance:

gcloud sql databases create release-notes --instance sql-instance

Erstellen Sie einen Datenbanknutzer mit dem Namen app:

gcloud sql users create app --instance sql-instance --password "myprecious"

Indexierungsjob bereitstellen und ausführen

Stellen Sie nun den Job bereit und führen Sie ihn aus:

DB_INSTANCE_NAME=$(gcloud sql instances describe sql-instance --format="value(connectionName)")

gcloud run jobs deploy indexer \
  --source . \
  --command python \
  --args app/indexer.py \
  --set-env-vars=DB_INSTANCE_NAME=$DB_INSTANCE_NAME \
  --set-env-vars=DB_USER=app \
  --set-env-vars=DB_NAME=release-notes \
  --set-env-vars=DB_PASS=myprecious \
  --region=$REGION \
  --execute-now

Das ist ein langer Befehl. Sehen wir uns an, was passiert:

Mit dem ersten Befehl wird der Verbindungsname (eine eindeutige ID im Format project:region:instance) abgerufen und als Umgebungsvariable DB_INSTANCE_NAME festgelegt.

Der zweite Befehl stellt den Cloud Run-Job bereit. Die Markierungen haben folgende Bedeutung:

  • --source .: Gibt an, dass sich der Quellcode für den Job im aktuellen Arbeitsverzeichnis befindet (das Verzeichnis, in dem Sie den Befehl ausführen).
  • --command python: Legt den Befehl fest, der im Container ausgeführt werden soll. In diesem Fall ist es Python.
  • --args app/indexer.py: Hier werden die Argumente für den Python-Befehl angegeben. Dadurch wird das Script „indexer.py“ im App-Verzeichnis ausgeführt.
  • --set-env-vars: Legt Umgebungsvariablen fest, auf die das Python-Skript während der Ausführung zugreifen kann.
  • --region=$REGION: Gibt die Region an, in der der Job bereitgestellt werden soll.
  • --execute-now: Cloud Run wird angewiesen, den Job sofort nach der Bereitstellung zu starten.

So prüfen Sie, ob der Job erfolgreich abgeschlossen wurde:

  • Sie können die Protokolle der Jobausführung über die Webkonsole lesen. Es sollte die Meldung „Speichern abgeschlossen: xxx Versionshinweise“ angezeigt werden, wobei „xxx“ die Anzahl der gespeicherten Versionshinweise ist.
  • Sie können auch die Cloud SQL-Instanz in der Webkonsole aufrufen und mit Cloud SQL Studio die Anzahl der Einträge in der Tabelle langchain_pg_embedding abfragen.

6. Webanwendung schreiben

Öffnen Sie die Datei app/server.py in Ihrem Editor. Sie sehen eine Zeile mit folgendem Inhalt:

# Edit this to add the chain you want to add

Ersetzen Sie diesen Kommentar durch das folgende Snippet:

# (1) Initialize VectorStore
connector = Connector()


def getconn() -> pg8000.dbapi.Connection:
    conn: pg8000.dbapi.Connection = connector.connect(
        os.getenv("DB_INSTANCE_NAME", ""),
        "pg8000",
        user=os.getenv("DB_USER", ""),
        password=os.getenv("DB_PASS", ""),
        db=os.getenv("DB_NAME", ""),
    )
    return conn


vectorstore = PGVector(
    connection_string="postgresql+pg8000://",
    use_jsonb=True,
    engine_args=dict(
        creator=getconn,
    ),
    embedding_function=VertexAIEmbeddings(
        model_name="textembedding-gecko@003"
    )
)

# (2) Build retriever


def concatenate_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)


notes_retriever = vectorstore.as_retriever() | concatenate_docs

# (3) Create prompt template
prompt_template = PromptTemplate.from_template(
    """You are a Cloud Run expert answering questions. 
Use the retrieved release notes to answer questions
Give a concise answer, and if you are unsure of the answer, just say so.

Release notes: {notes}

Here is your question: {query}
Your answer: """)

# (4) Initialize LLM
llm = VertexAI(
    model_name="gemini-1.0-pro-001",
    temperature=0.2,
    max_output_tokens=100,
    top_k=40,
    top_p=0.95
)

# (5) Chain everything together
chain = (
    RunnableParallel({
        "notes": notes_retriever,
        "query": RunnablePassthrough()
    })
    | prompt_template
    | llm
    | StrOutputParser()
)

Außerdem müssen Sie die folgenden Importe hinzufügen:

import pg8000
import os
from google.cloud.sql.connector import Connector
from langchain_google_vertexai import VertexAI
from langchain_google_vertexai import VertexAIEmbeddings
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.vectorstores.pgvector import PGVector

Ändern Sie abschließend die Zeile mit „NotImplemented“ in:

# add_routes(app, NotImplemented)
add_routes(app, chain)

7. Webanwendung in Cloud Run bereitstellen

Verwenden Sie im Verzeichnis run-rag den folgenden Befehl, um die App in Cloud Run bereitzustellen:

DB_INSTANCE_NAME=$(gcloud sql instances describe sql-instance --format="value(connectionName)")

gcloud run deploy run-rag \
  --source . \
  --set-env-vars=DB_INSTANCE_NAME=$DB_INSTANCE_NAME \
  --set-env-vars=DB_USER=app \
  --set-env-vars=DB_NAME=release-notes \
  --set-env-vars=DB_PASS=myprecious \
  --region=$REGION \
  --allow-unauthenticated

Mit diesem Befehl wird Folgendes ausgeführt:

  • Quellcode in Cloud Build hochladen
  • Führen Sie „docker build“ aus.
  • Übertragen Sie das resultierende Container-Image per Push in Artifact Registry.
  • Erstellen Sie mit dem Container-Image einen Cloud Run-Dienst.

Nach Abschluss des Befehls wird eine HTTPS-URL auf der Domain „run.app“ angezeigt. Dies ist die öffentliche URL Ihres neuen Cloud Run-Dienstes.

8. Playground entdecken

Öffnen Sie die Cloud Run-Dienst-URL und rufen Sie /playground auf. Daraufhin wird ein Textfeld angezeigt. Sie können damit Fragen zu den Cloud Run-Versionshinweisen stellen, z. B.:

9. Glückwunsch

Sie haben eine LangChain-App in Cloud Run erstellt und bereitgestellt. Gut gemacht!

Hier sind die wichtigsten Konzepte:

  • LangChain-Framework zum Erstellen einer RAG-Anwendung (Retrieval Augmented Generation) verwenden
  • PostgreSQL in Cloud SQL als Vektordatenbank mit pgvector verwenden, das standardmäßig in Cloud SQL installiert ist
  • Einen länger laufenden Indexierungsjob als Cloud Run-Job und eine Webanwendung als Cloud Run-Dienst ausführen
  • Mit LangServe können Sie eine LangChain-Kette in eine FastAPI-Anwendung einbetten und so eine praktische Schnittstelle für die Interaktion mit Ihrer RAG-App bereitstellen.

Bereinigen

So vermeiden Sie, dass Ihrem Google Cloud Platform-Konto die in dieser Anleitung verwendeten Ressourcen berechnet werden:

  • Wechseln Sie in der Cloud Console zur Seite „Ressourcen verwalten“.
  • Wählen Sie in der Projektliste Ihr Projekt aus und klicken Sie auf „Löschen“.
  • Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf „Beenden“, um das Projekt zu löschen.

Wenn Sie das Projekt behalten möchten, müssen Sie die folgenden Ressourcen löschen:

  • Cloud SQL-Instanz
  • Cloud Run-Dienst
  • Cloud Run-Job