Compila e implementa una app de LangChain en Cloud Run

1. Descripción general

En este codelab, aprenderás a implementar una app de LangChain que usa Gemini para permitirte hacer preguntas sobre las notas de la versión de Cloud Run.

Este es un ejemplo de cómo funciona la app: Si haces la pregunta “¿Puedo activar un bucket de Cloud Storage como volumen en Cloud Run?”, la app responderá con “Sí, desde el 19 de enero de 2024”, o algo similar.

Para devolver respuestas fundamentadas, la app primero recupera notas de la versión de Cloud Run que son similares a la pregunta y, luego, le indica a Gemini tanto la pregunta como las notas de la versión. (este es un patrón comúnmente conocido como RAV). En el siguiente diagrama, se muestra la arquitectura de la app:

2. Configuración y requisitos

Primero, asegurémonos de que tu entorno de desarrollo esté configurado correctamente.

  • Para ello, necesitarás un proyecto de Google Cloud a fin de implementar los recursos necesarios para la app.
  • Para implementar la app, necesitas tener la función gcloud installed en tu máquina local, autenticada y configurada para usar el proyecto.
    • gcloud auth login
    • gcloud config set project
  • Si quieres ejecutar la aplicación en tu máquina local, que recomendamos, debes asegurarte de que tus credenciales predeterminadas de la aplicación estén configuradas correctamente, lo que incluye la configuración del proyecto de cuota.
    • gcloud auth application-default login
    • gcloud auth application-default set-quota-project
  • También debes tener instalado el siguiente software:
    • Python (se requiere la versión 3.11 o posterior)
    • La CLI de LangChain
    • poesía para la gestión de dependencias
    • pipx para instalar y ejecutar la CLI y la poesía de LangChain en entornos virtuales aislados

En este blog encontrarás ayuda para comenzar a instalar las herramientas necesarias para esta explicación.

Cloud Workstations

En lugar de tu máquina local, también puedes usar Cloud Workstations en Google Cloud. Ten en cuenta que, a partir de abril de 2024, ejecuta una versión de Python anterior a la 3.11, por lo que es posible que debas actualizar Python antes de comenzar.

Habilita las APIs de Cloud

Primero, ejecuta el siguiente comando para asegurarte de que configuraste el proyecto de Google Cloud correcto:

gcloud config list project

Si no se muestra el proyecto correcto, puedes configurarlo con este comando:

gcloud config set project <PROJECT_ID>

Ahora habilita las siguientes 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

Choose a region

Google Cloud se encuentra disponible en muchas ubicaciones del mundo, y debes elegir una para implementar los recursos que usarás en este lab. Establece la región como una variable de entorno en tu shell (los comandos posteriores usarán esta variable):

export REGION=us-central1

3. Crea la instancia de base de datos de vectores

Una parte clave de esta app es recuperar las notas de la versión que son relevantes para la pregunta del usuario. Para ser más concreto, si haces una pregunta sobre Cloud Storage, querrás que se agregue la siguiente nota de la versión a la instrucción:

Puedes usar incorporaciones de texto y una base de datos de vectores para encontrar notas de la versión similares en cuanto a semántica.

Te mostraré cómo usar PostgreSQL en Cloud SQL como una base de datos de vectores. Crear una instancia nueva de Cloud SQL lleva tiempo, así que hagámoslo ahora.

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

Puedes permitir que se ejecute este comando y continuar con los pasos siguientes. En algún momento, deberás crear una base de datos y agregar un usuario, pero no perdamos tiempo viendo el ícono giratorio.

PostgreSQL es un servidor de base de datos relacional y cada instancia nueva de Cloud SQL tiene la extensión pgvector instalada de forma predeterminada, lo que significa que también puedes usarla como base de datos de vectores.

4. Usa el andamiaje de la app de LangChain

Para continuar, deberás tener instalada la CLI de LangChain y la poesía para administrar las dependencias. A continuación, se explica cómo instalarlos con pipx:

pipx install langchain-cli poetry

Usa el siguiente comando para andamiar la app de LangChain. Cuando se te solicite, asigna el nombre run-rag a la carpeta y presiona Intro para omitir la instalación de paquetes:

langchain app new

Cambia al directorio run-rag y, luego, instala las dependencias.

poetry install

Acabas de crear una aplicación LangServe. LangServe une FastAPI en una cadena de LangChain. Viene con una zona de pruebas integrada que facilita el envío de instrucciones y la inspección de resultados, incluidos todos los pasos intermedios. Te sugerimos que abras la carpeta run-rag en tu editor y explores lo que contiene.

5. Crea el trabajo de indexación

Antes de comenzar a armar la app web, asegurémonos de que las notas de la versión de Cloud Run estén indexadas en la base de datos de Cloud SQL. En esta sección, crearás un trabajo de indexación que hace lo siguiente:

El trabajo de indexación toma notas de la versión, las convierte en vectores con un modelo de incorporación de texto y las almacena en una base de datos de vectores. Esto permite una búsqueda eficiente de notas de versiones similares según su significado semántico.

En la carpeta run-rag/app, crea un archivo indexer.py con el siguiente contenido:

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

Agrega las dependencias necesarias:

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

Crea la base de datos y un usuario

Crea una base de datos release-notes en la instancia de Cloud SQL sql-instance:

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

Crea un usuario de base de datos llamado app:

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

Implementa y ejecuta el trabajo de indexación

Ahora implementa y ejecuta el trabajo:

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

Ese es un comando largo. Veamos qué sucede:

El primer comando recupera el nombre de la conexión (un ID único con formato project:region:instance) y lo establece como la variable de entorno DB_INSTANCE_NAME.

El segundo comando implementa el trabajo de Cloud Run. Esto es lo que hacen las marcas:

  • --source .: Especifica que el código fuente del trabajo se encuentra en el directorio de trabajo actual (el directorio en el que ejecutas el comando).
  • --command python: Configura el comando que se ejecutará dentro del contenedor. En este caso, es para ejecutar Python.
  • --args app/indexer.py: Proporciona los argumentos al comando de Python. Esto le indica que ejecute la secuencia de comandos indexer.py en el directorio de la app.
  • --set-env-vars: Establece variables de entorno a las que la secuencia de comandos de Python puede acceder durante la ejecución.
  • --region=$REGION: Especifica la región en la que se debe implementar el trabajo.
  • --execute-now: Indica a Cloud Run que inicie el trabajo de inmediato después de implementarlo.

Para verificar que el trabajo se haya completado correctamente, puedes hacer lo siguiente:

  • Lee los registros de la ejecución del trabajo a través de la consola web. Debería mostrar el mensaje "Listo de guardar: notas de la versión xx" (donde xxx es el número de notas de la versión guardadas).
  • También puedes navegar a la instancia de Cloud SQL en la consola web y usar Cloud SQL Studio para consultar la cantidad de registros en la tabla langchain_pg_embedding.

6. Escribe la aplicación web

Abre el archivo app/server.py en el editor. Encontrarás una línea que dice lo siguiente:

# Edit this to add the chain you want to add

Reemplaza ese comentario por el siguiente fragmento:

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

También debes agregar estas importaciones:

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

Por último, cambia la línea que dice "NotImplemented". a:

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

7. Implementa la aplicación web en Cloud Run

Desde el directorio run-rag, usa el siguiente comando para implementar la app en 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

Este comando realiza las siguientes acciones:

  • Sube el código fuente a Cloud Build
  • Ejecutar docker build.
  • Envía la imagen de contenedor resultante a Artifact Registry.
  • Crear un servicio de Cloud Run con la imagen de contenedor

Cuando se completa el comando, aparece una URL HTTPS en el dominio run.app. Esta es la URL pública de tu nuevo servicio de Cloud Run

8. Explora la zona de juegos

Abre la URL del servicio de Cloud Run y navega a /playground. Aparecerá un campo de texto. Úsalo para hacer preguntas sobre las notas de la versión de Cloud Run, como aquí:

9. Felicitaciones

Compilaste e implementaste correctamente una app de LangChain en Cloud Run. ¡Bien hecho!

Estos son los conceptos clave:

  • Cómo usar el framework de LangChain para compilar una aplicación de generación de aumento de recuperación (RAG)
  • Usar PostgreSQL en Cloud SQL como una base de datos de vectores con pgvector, que se instala de forma predeterminada en Cloud SQL
  • Ejecuta un trabajo de indexación en ejecución más largo como trabajos de Cloud Run y una aplicación web como servicio de Cloud Run.
  • Une una cadena de LangChain en una aplicación de FastAPI con LangServe, lo que proporciona una interfaz conveniente para interactuar con tu aplicación RAG.

Limpia

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform para los recursos que se usaron en este instructivo:

  • En la consola de Cloud, ve a la página Administrar recursos.
  • En la lista de proyectos, selecciona tu proyecto y haz clic en Borrar.
  • En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrarlo.

Si deseas conservar el proyecto, asegúrate de borrar los siguientes recursos:

  • Instancia de Cloud SQL
  • Servicio de Cloud Run
  • Trabajo de Cloud Run