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

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

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

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

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

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

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

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

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

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

במקום המחשב המקומי, אפשר גם להשתמש ב-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 ואת שירה כדי לנהל את יחסי התלות. כך מתקינים אותם באמצעות pipx:

pipx install langchain-cli poetry

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

langchain app new

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

poetry install

יצרתם עכשיו אפליקציית LangServe. LangServe עוטפת את FastAPI סביב שרשרת LangChain. הספרייה כוללת סביבה מובנית לניסוי (playground) שמאפשרת לשלוח בקלות הנחיות ולבדוק את התוצאות, כולל כל השלבים המקשרים. מומלץ לפתוח את התיקייה 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" (לא הוטמע) to:

# 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. כל הכבוד!

אלה המושגים המרכזיים:

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

הסרת המשאבים

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

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

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

  • מכונה של Cloud SQL
  • שירות Cloud Run
  • משימה ב-Cloud Run