Découvrez comment créer et déployer une application LangChain sur Cloud Run

1. Présentation

Dans cet atelier de programmation, vous allez apprendre à déployer une application LangChain qui utilise Gemini pour vous permettre de poser des questions sur les notes de version de Cloud Run.

Voici un exemple de fonctionnement de l'application : si vous posez la question "Puis-je monter un bucket Cloud Storage en tant que volume dans Cloud Run ?", l'application répond "Oui, depuis le 19 janvier 2024" ou quelque chose de similaire.

Pour renvoyer des réponses ancrées, l'application récupère d'abord les notes de version Cloud Run qui sont similaires à la question, puis invite Gemini avec la question et les notes de version. (Il s'agit d'un modèle couramment appelé RAG.) Voici un schéma de l'architecture de l'application :

2. Préparation

Tout d'abord, assurez-vous que votre environnement de développement est correctement configuré.

  • Vous aurez besoin d'un projet Google Cloud pour déployer les ressources dont vous aurez besoin pour l'application.
  • Pour déployer l'application, vous devez avoir installé gcloud sur votre ordinateur local, vous être authentifié et avoir configuré l'outil pour qu'il utilise le projet.
    • gcloud auth login
    • gcloud config set project
  • Si vous souhaitez exécuter l'application sur votre machine locale, ce que je vous recommande, vous devez vous assurer que vos identifiants par défaut de l'application sont correctement configurés, y compris en définissant le projet de quota.
    • gcloud auth application-default login
    • gcloud auth application-default set-quota-project
  • Vous devez également avoir installé les logiciels suivants :
    • Python (version 3.11 ou ultérieure requise)
    • LangChain CLI
    • poetry pour la gestion des dépendances
    • pipx pour installer et exécuter la CLI LangChain et poetry dans des environnements virtuels isolés

Voici un blog qui vous aide à installer les outils nécessaires pour ce tutoriel.

Cloud Workstations

Au lieu de votre machine locale, vous pouvez également utiliser Cloud Workstations sur Google Cloud. Notez qu'en avril 2024, il exécute une version de Python antérieure à la version 3.11. Vous devrez peut-être donc mettre à niveau Python avant de commencer.

Activer les API Cloud

Tout d'abord, exécutez la commande suivante pour vous assurer d'avoir configuré le bon projet Google Cloud à utiliser :

gcloud config list project

Si vous obtenez un résultat différent, exécutez cette commande :

gcloud config set project <PROJECT_ID>

Activez ensuite les API suivantes :

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

Sélectionner une région

Google Cloud est disponible dans de nombreux endroits du monde. Vous devez en choisir un pour déployer les ressources que vous utiliserez dans cet atelier. Définissez la région comme variable d'environnement dans votre interface système (les commandes ultérieures utilisent cette variable) :

export REGION=us-central1

3. Créer l'instance de base de données vectorielle

Une partie essentielle de cette application consiste à récupérer les notes de version pertinentes pour la question de l'utilisateur. Pour être plus concret, si vous posez une question sur Cloud Storage, vous souhaitez que la note de version suivante soit ajoutée à la requête :

Vous pouvez utiliser des embeddings textuels et une base de données vectorielle pour trouver des notes de version sémantiquement similaires.

Je vais vous montrer comment utiliser PostgreSQL sur Cloud SQL comme base de données vectorielles. La création d'une instance Cloud SQL prend un certain temps. Nous allons donc le faire maintenant.

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

Vous pouvez laisser cette commande s'exécuter et passer aux étapes suivantes. Vous devrez créer une base de données et ajouter un utilisateur à un moment donné, mais ne perdons pas de temps à regarder le spinner maintenant.

PostgreSQL est un serveur de base de données relationnelle. Chaque nouvelle instance Cloud SQL est associée à l'extension pgvector installée par défaut, ce qui signifie que vous pouvez également l'utiliser comme base de données vectorielle.

4. Échafauder l'application LangChain

Pour continuer, vous devez avoir installé LangChain CLI et poetry pour gérer les dépendances. Voici comment les installer à l'aide de pipx :

pipx install langchain-cli poetry

Créez l'application LangChain avec la commande suivante. Lorsque vous y êtes invité, nommez le dossier run-rag et ignorez l'installation des packages en appuyant sur Entrée :

langchain app new

Accédez au répertoire run-rag et installez les dépendances.

poetry install

Vous venez de créer une application LangServe. LangServe encapsule FastAPI autour d'une chaîne LangChain. Il est fourni avec un bac à sable intégré qui permet d'envoyer facilement des requêtes et d'inspecter les résultats, y compris toutes les étapes intermédiaires. Je vous suggère d'ouvrir le dossier run-rag dans votre éditeur et d'explorer son contenu.

5. Créer le job d'indexation

Avant de commencer à créer l'application Web, assurez-vous que les notes de version de Cloud Run sont indexées dans la base de données Cloud SQL. Dans cette section, vous allez créer un job d'indexation qui effectue les opérations suivantes :

Le job d'indexation prend les notes de version, les convertit en vecteurs à l'aide d'un modèle d'embedding textuel et les stocke dans une base de données vectorielle. Cela permet de rechercher efficacement des notes de version similaires en fonction de leur signification sémantique.

Dans le dossier run-rag/app, créez un fichier indexer.py avec le contenu suivant :

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

Ajoutez les dépendances requises :

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

Créer la base de données et un utilisateur

Créez une base de données release-notes sur l'instance Cloud SQL sql-instance :

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

Créez un utilisateur de base de données appelé app :

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

Déployer et exécuter le job d'indexation

Déployez et exécutez le job :

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

Cette commande est longue. Voyons ce qui se passe :

La première commande récupère le nom de la connexion (un ID unique au format project:region:instance) et le définit comme variable d'environnement DB_INSTANCE_NAME.

La deuxième commande déploie le job Cloud Run. Voici ce que font les indicateurs :

  • --source . : indique que le code source du job se trouve dans le répertoire de travail actuel (le répertoire dans lequel vous exécutez la commande).
  • --command python : définit la commande à exécuter dans le conteneur. Dans ce cas, il s'agit d'exécuter Python.
  • --args app/indexer.py : fournit les arguments à la commande Python. Cela lui indique d'exécuter le script indexer.py dans le répertoire de l'application.
  • --set-env-vars : définit les variables d'environnement auxquelles le script Python peut accéder lors de l'exécution.
  • --region=$REGION : spécifie la région dans laquelle le job doit être déployé.
  • --execute-now : indique à Cloud Run de démarrer le job immédiatement après son déploiement.

Pour vérifier que le job a bien été exécuté, vous pouvez procéder comme suit :

  • Lisez les journaux d'exécution du job dans la console Web. Il doit indiquer "Enregistrement terminé : notes de version xxx" (où xxx correspond au nombre de notes de version enregistrées).
  • Vous pouvez également accéder à l'instance Cloud SQL dans la console Web et utiliser Cloud SQL Studio pour interroger le nombre d'enregistrements dans la table langchain_pg_embedding.

6. Écrire l'application Web

Ouvrez le fichier app/server.py dans votre éditeur. Vous trouverez une ligne indiquant :

# Edit this to add the chain you want to add

Remplacez ce commentaire par l'extrait de code suivant :

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

Vous devez également ajouter les importations suivantes :

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

Enfin, remplacez la ligne "NotImplemented" par :

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

7. Déployer l'application Web sur Cloud Run

Depuis le répertoire run-rag, utilisez la commande suivante pour déployer l'application sur Cloud Run :

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

Cette commande effectue les opérations suivantes :

  • Importer le code source dans Cloud Build
  • Exécutez docker build.
  • Transférez l'image de conteneur obtenue vers Artifact Registry.
  • Créez un service Cloud Run à l'aide de l'image de conteneur.

Une fois la commande terminée, elle liste une URL HTTPS sur le domaine run.app. Il s'agit de l'URL publique de votre nouveau service Cloud Run.

8. Explorer le terrain de jeu

Ouvrez l'URL du service Cloud Run et accédez à /playground. Un champ de texte s'affiche. Utilisez-le pour poser des questions sur les notes de version de Cloud Run, comme ici :

9. Félicitations

Vous avez créé et déployé une application LangChain sur Cloud Run. Bravo !

Voici les concepts clés :

  • Utiliser le framework LangChain pour créer une application de génération augmentée par récupération (RAG).
  • Utiliser PostgreSQL sur Cloud SQL comme base de données vectorielles avec pgvector, qui est installé par défaut sur Cloud SQL.
  • Exécutez un job d'indexation de longue durée en tant que job Cloud Run et une application Web en tant que service Cloud Run.
  • Encapsulez une chaîne LangChain dans une application FastAPI avec LangServe, ce qui fournit une interface pratique pour interagir avec votre application RAG.

Effectuer un nettoyage

Afin d'éviter la facturation sur votre compte Google Cloud Platform des ressources utilisées dans ce tutoriel, procédez comme suit :

  • Dans Cloud Console, accédez à la page "Gérer les ressources".
  • Dans la liste des projets, sélectionnez votre projet, puis cliquez sur "Supprimer".
  • Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur "Arrêter" pour supprimer le projet.

Si vous souhaitez conserver le projet, veillez à supprimer les ressources suivantes :

  • Instance Cloud SQL
  • Service Cloud Run
  • Job Cloud Run