מידע על פיתוח ופריסה של אפליקציית LangChain ב-Cloud Run

1. סקירה כללית

בשיעור ה-Lab הזה תלמדו איך לפרוס אפליקציית LangChain שמשתמשת ב-Gemini כדי לאפשר לכם לשאול שאלות על הערות הגרסה של Cloud Run.

הנה דוגמה לאופן הפעולה של האפליקציה: אם שואלים את השאלה 'האם אפשר לטעון קטגוריה של Cloud Storage כנפח ב-Cloud Run?', האפליקציה משיבה 'כן, מאז 19 בינואר 2024', או משהו דומה.

כדי להחזיר תשובות מבוססות, האפליקציה מאחזרת קודם הערות על הגרסה של Cloud Run שדומות לשאלה, ואז מציגה ל-Gemini את השאלה ואת ההערות על הגרסה. (זהו דפוס שנקרא בדרך כלל RAG). תרשים שמציג את הארכיטקטורה של האפליקציה:

2. הגדרה ודרישות

קודם כול, נוודא שסביבת הפיתוח מוגדרת בצורה נכונה.

  • כדי לפרוס את המשאבים שדרושים לאפליקציה, תצטרכו פרויקט בענן של Google.
  • כדי לפרוס את האפליקציה, צריך לוודא שgcloud מותקן במחשב המקומי, שהאימות בוצע ושהוא מוגדר לשימוש בפרויקט.
    • gcloud auth login
    • gcloud config set project
  • אם רוצים להריץ את האפליקציה במחשב המקומי, וזה מה שאני ממליץ לעשות, צריך לוודא שפרטי הכניסה שמוגדרים כברירת מחדל באפליקציה מוגדרים בצורה נכונה, כולל הגדרת פרויקט המכסה.
    • gcloud auth application-default login
    • gcloud auth application-default set-quota-project
  • צריך גם להתקין את התוכנות הבאות:
    • Python (נדרשת גרסה 3.11 ומעלה)
    • LangChain CLI
    • poetry לניהול יחסי תלות
    • pipx להתקנה ולהרצה של LangChain CLI ו-poetry בסביבות וירטואליות מבודדות

בבלוג הזה מוסבר איך להתקין את הכלים שנדרשים למדריך הזה.

תחנות עבודה בענן

במקום להשתמש במחשב המקומי, אפשר גם להשתמש ב-Cloud Workstations ב-Google Cloud. שימו לב: נכון לאפריל 2024, הוא פועל בגרסת Python נמוכה מ-3.11, ולכן יכול להיות שתצטרכו לשדרג את Python לפני שתתחילו.

הפעלת ממשקי Cloud API

קודם מריצים את הפקודה הבאה כדי לוודא שהגדרתם את פרויקט Google Cloud הנכון לשימוש:

gcloud config list project

אם הפרויקט הנכון לא מוצג, אפשר להגדיר אותו באמצעות הפקודה הבאה:

gcloud config set project <PROJECT_ID>

עכשיו מפעילים את ממשקי ה-API הבאים:

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

בחירת אזור

‫Google Cloud זמין בהרבה מיקומים ברחבי העולם, ואתם צריכים לבחור אחד כדי לפרוס את המשאבים שתשתמשו בהם במעבדה הזו. מגדירים את האזור כמשתנה סביבה בשורת הפקודה (פקודות מאוחרות יותר משתמשות במשתנה הזה):

export REGION=us-central1

3. יצירת מכונת מסד נתונים וקטורי

חלק מרכזי באפליקציה הזו הוא אחזור של הערות על הגרסה שרלוונטיות לשאלה של המשתמש. כדי להמחיש את זה, אם אתם שואלים שאלה לגבי Cloud Storage, אתם רוצים שהערת הגרסה הבאה תתווסף להנחיה:

אפשר להשתמש בהטמעות טקסט ובמסד נתונים וקטורי כדי למצוא הערות על גרסאות שדומות מבחינה סמנטית.

אראה לך איך להשתמש ב-PostgreSQL ב-Cloud SQL כמסד נתונים וקטורי. יצירת מכונה חדשה של Cloud SQL לוקחת זמן, אז בואו נעשה את זה עכשיו.

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

אפשר להריץ את הפקודה הזו ולהמשיך לשלבים הבאים. בשלב מסוים תצטרכו ליצור מסד נתונים ולהוסיף משתמש, אבל לא נבזב זמן בצפייה בסמל הטעינה עכשיו.

‫PostgreSQL הוא שרת של מסד נתונים רלציוני, ובכל מופע חדש של Cloud SQL מותקנת כברירת מחדל התוסף pgvector, כך שאפשר להשתמש בו גם כמסד נתונים וקטורי.

4. יצירת תבנית לאפליקציית LangChain

כדי להמשיך, צריך להתקין את LangChain CLI ואת poetry לניהול תלות. כך מתקינים אותם באמצעות pipx:

pipx install langchain-cli poetry

מריצים את הפקודה הבאה כדי ליצור את האפליקציה של LangChain. כשמתבקשים, נותנים לתיקייה את השם run-rag ומדלגים על התקנת החבילות על ידי הקשה על Enter:

langchain app new

עוברים לספרייה run-rag ומתקינים את התלות

poetry install

הרגע יצרת אפליקציית LangServe. ‏LangServe עוטף את FastAPI סביב שרשרת LangChain. הוא כולל סביבת משחק מובנית שמאפשרת לשלוח הנחיות ולבדוק את התוצאות בקלות, כולל כל השלבים המקדימים. מומלץ לפתוח את התיקייה run-rag בכלי העריכה ולבדוק מה יש בה.

5. יצירת משימת האינדוקס

לפני שמתחילים להרכיב את אפליקציית האינטרנט, צריך לוודא שהערות הגרסה של Cloud Run מאונדקסות במסד הנתונים של Cloud SQL. בקטע הזה, תיצרו משימת יצירת אינדקס שתבצע את הפעולות הבאות:

תהליך יצירת האינדקס לוקח הערות על הגרסה, ממיר אותן לווקטורים באמצעות מודל הטמעת טקסט ומאחסן אותן במסד נתונים של וקטורים. כך אפשר לחפש ביעילות הערות דומות על גרסאות, על סמך המשמעות הסמנטית שלהן.

בתיקייה run-rag/app, יוצרים קובץ indexer.py עם התוכן הבא:

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

מוסיפים את יחסי התלות הנדרשים:

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

יצירת מסד נתונים ומשתמש

יוצרים מסד נתונים release-notes במכונה של Cloud SQL sql-instance:

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

יוצרים משתמש במסד הנתונים בשם app:

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

פריסה והפעלה של משימת ההוספה לאינדקס

עכשיו פורסים ומריצים את העבודה:

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

זו פקודה ארוכה, בואו נראה מה קורה:

הפקודה הראשונה מאחזרת את שם החיבור (מזהה ייחודי בפורמט project:region:instance) ומגדירה אותו כמשתנה הסביבה DB_INSTANCE_NAME.

הפקודה השנייה פורסת את משימת Cloud Run. הנה הסבר על הפעולות של הדגלים:

  • --source .: מציין שקוד המקור של העבודה נמצא בספריית העבודה הנוכחית (הספרייה שבה מריצים את הפקודה).
  • --command python: מגדיר את הפקודה להרצה בתוך הקונטיינר. במקרה הזה, כדי להריץ Python.
  • --args app/indexer.py: מספק את הארגומנטים לפקודת python. הפקודה הזו מורה להפעיל את הסקריפט indexer.py בספריית האפליקציה.
  • --set-env-vars: הגדרת משתני סביבה שהסקריפט Python יכול לגשת אליהם במהלך ההרצה.
  • --region=$REGION: ציון האזור שבו צריך לפרוס את העבודה.
  • --execute-now: מציין ל-Cloud Run להתחיל את המשימה מיד אחרי הפריסה.

כדי לוודא שהעבודה הושלמה בהצלחה, אפשר:

  • קוראים את היומנים של הרצת המשימה דרך מסוף האינטרנט. הדוח צריך להציג את ההודעה 'השמירה הסתיימה: xxx הערות לגבי הגרסה' (כאשר xxx הוא מספר ההערות לגבי הגרסה שנשמרו).
  • אפשר גם לעבור למופע Cloud SQL במסוף האינטרנט ולהשתמש ב-Cloud SQL Studio כדי להריץ שאילתה על מספר הרשומות בטבלה langchain_pg_embedding.

6. כתיבת אפליקציית האינטרנט

פותחים את הקובץ app/server.py בכלי העריכה. תופיע שורה עם הטקסט הבא:

# Edit this to add the chain you want to add

מחליפים את התגובה בקטע הקוד הבא:

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

צריך גם להוסיף את הייבוא הבא:

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

לבסוף, משנים את השורה עם הכיתוב NotImplemented לשורה הבאה:

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

7. פריסת אפליקציית האינטרנט ב-Cloud Run

מהספרייה run-rag, מריצים את הפקודה הבאה כדי לפרוס את האפליקציה ב-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

הפקודה הזו מבצעת את הפעולות הבאות:

  • העלאת קוד המקור ל-Cloud Build
  • מריצים את הפקודה docker build.
  • מעבירים בדחיפה את קובץ האימג' של הקונטיינר שנוצר אל Artifact Registry.
  • יוצרים שירות Cloud Run באמצעות קובץ האימג' של הקונטיינר.

בסיום הפקודה, מוצגת כתובת URL בפרוטוקול HTTPS בדומיין run.app. זוהי כתובת ה-URL הציבורית של שירות Cloud Run החדש

8. התנסות עם ארגז החול

פותחים את כתובת ה-URL של שירות Cloud Run ומנווטים אל /playground. יופיע שדה טקסט. אפשר להשתמש בו כדי לשאול שאלות על הערות הגרסה של Cloud Run, כמו בדוגמה הבאה:

9. מזל טוב

יצרתם ופרסתם בהצלחה אפליקציית LangChain ב-Cloud Run. כל הכבוד!

הנה המושגים העיקריים:

  • שימוש ב-framework של LangChain כדי ליצור אפליקציה של Retrieval Augmented Generation (יצירה משולבת-אחזור, RAG).
  • שימוש ב-PostgreSQL ב-Cloud SQL כמסד נתונים וקטורי עם pgvector, שמותקן כברירת מחדל ב-Cloud SQL.
  • להריץ משימת אינדוקס ארוכה כמשימה ב-Cloud Run ואפליקציית אינטרנט כשירות ב-Cloud Run.
  • עוטפים שרשרת LangChain באפליקציית FastAPI באמצעות LangServe, כדי לספק ממשק נוח לאינטראקציה עם אפליקציית ה-RAG.

הסרת המשאבים

כדי להימנע מחיובים בחשבון Google Cloud Platform בגלל השימוש במשאבים שנעשה במסגרת המדריך הזה:

  • במסוף Cloud, נכנסים לדף Manage resources.
  • ברשימת הפרויקטים, בוחרים את הפרויקט ולוחצים על Delete (מחיקה).
  • כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

אם רוצים לשמור את הפרויקט, צריך למחוק את המשאבים הבאים:

  • מופע Cloud SQL
  • שירות Cloud Run
  • משימה ב-Cloud Run