本教程介绍如何将基于检索增强生成 (RAG) 的大语言模型 (LLM) 应用与上传到 Cloud Storage 存储桶的 PDF 文件集成。
本指南使用数据库作为存储和语义搜索引擎,用于保存所上传文档的表示法(嵌入)。您将使用 Langchain 框架与嵌入进行交互,并使用通过 Vertex AI 提供的 Gemini 模型。
Langchain 是一种流行的开源 Python 框架,可简化许多机器学习任务,并具有与不同矢量数据库和 AI 服务集成的界面。
本教程适用于想要将 RAG LLM 应用部署到 GKE 和 Cloud Storage 的云平台管理员和架构师、机器学习工程师以及 MLOps (DevOps) 专业人员。
创建集群
创建 Qdrant、Elasticsearch 或 Postgres 集群:
Qdrant
按照在 GKE 上部署 Qdrant 矢量数据库中的说明,创建在 Autopilot 模式或 Standard 模式 GKE 集群上运行的 Qdrant 集群。
Elasticsearch
按照在 GKE 上部署 Elasticsearch 矢量数据库中的说明,创建在 Autopilot 模式或 Standard 模式 GKE 集群上运行的 Elasticsearch 集群。
PGVector
按照在 GKE 上部署 PostgreSQL 矢量数据库中的说明,创建在 Autopilot 模式或 Standard 模式 GKE 集群上运行且包含 PGVector 的 Postgres 集群。
Weaviate
按照在 GKE 上部署 Weaviate 矢量数据库中的说明,创建在 Autopilot 或 Standard 模式 GKE 集群上运行的 Weaviate 集群。
设置环境
使用 Cloud Shell 设置您的环境:
设置项目的环境变量:
Qdrant
export PROJECT_ID=PROJECT_ID export KUBERNETES_CLUSTER_PREFIX=qdrant export CONTROL_PLANE_LOCATION=us-central1 export REGION=us-central1 export DB_NAMESPACE=qdrant
将
PROJECT_ID
替换为您的Google Cloud 项目 ID。Elasticsearch
export PROJECT_ID=PROJECT_ID export KUBERNETES_CLUSTER_PREFIX=elasticsearch export CONTROL_PLANE_LOCATION=us-central1 export REGION=us-central1 export DB_NAMESPACE=elastic
将
PROJECT_ID
替换为您的Google Cloud 项目 ID。PGVector
export PROJECT_ID=PROJECT_ID export KUBERNETES_CLUSTER_PREFIX=postgres export CONTROL_PLANE_LOCATION=us-central1 export REGION=us-central1 export DB_NAMESPACE=pg-ns
将
PROJECT_ID
替换为您的Google Cloud 项目 ID。Weaviate
export PROJECT_ID=PROJECT_ID export KUBERNETES_CLUSTER_PREFIX=weaviate export CONTROL_PLANE_LOCATION=us-central1 export REGION=us-central1 export DB_NAMESPACE=weaviate
将
PROJECT_ID
替换为您的Google Cloud 项目 ID。验证 GKE 集群是否正在运行:
gcloud container clusters list --project=${PROJECT_ID} --location=${CONTROL_PLANE_LOCATION}
输出类似于以下内容:
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS [KUBERNETES_CLUSTER_PREFIX]-cluster us-central1 1.30.1-gke.1329003 <EXTERNAL IP> e2-standard-2 1.30.1-gke.1329003 6 RUNNING
从 GitHub 克隆示例代码库:
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
导航到
databases
目录:cd kubernetes-engine-samples/databases
准备基础架构
创建 Artifact Registry 代码库,构建 Docker 映像,并将 Docker 映像推送到 Artifact Registry:
创建 Artifact Registry 代码库:
gcloud artifacts repositories create ${KUBERNETES_CLUSTER_PREFIX}-images \ --repository-format=docker \ --location=${REGION} \ --description="Vector database images repository" \ --async
为 Compute Engine 服务账号设置
storage.objectAdmin
和artifactregistry.admin
权限,以使用 Cloud Build 为embed-docs
和chatbot
Service 构建和推送 Docker 映像。export PROJECT_NUMBER=PROJECT_NUMBER gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \ --role="roles/storage.objectAdmin" gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \ --role="roles/artifactregistry.admin"
将
PROJECT_NUMBER
替换为您的Google Cloud 项目编号。为
embed-docs
和chatbot
Service 构建 Docker 映像。embed-docs
映像包含接收 Eventarc 转发器请求的应用和嵌入作业的 Python 代码。Qdrant
export DOCKER_REPO="${REGION}-docker.pkg.dev/${PROJECT_ID}/${KUBERNETES_CLUSTER_PREFIX}-images" gcloud builds submit qdrant/docker/chatbot --region=${REGION} \ --tag ${DOCKER_REPO}/chatbot:1.0 --async gcloud builds submit qdrant/docker/embed-docs --region=${REGION} \ --tag ${DOCKER_REPO}/embed-docs:1.0 --async
Elasticsearch
export DOCKER_REPO="${REGION}-docker.pkg.dev/${PROJECT_ID}/${KUBERNETES_CLUSTER_PREFIX}-images" gcloud builds submit elasticsearch/docker/chatbot --region=${REGION} \ --tag ${DOCKER_REPO}/chatbot:1.0 --async gcloud builds submit elasticsearch/docker/embed-docs --region=${REGION} \ --tag ${DOCKER_REPO}/embed-docs:1.0 --async
PGVector
export DOCKER_REPO="${REGION}-docker.pkg.dev/${PROJECT_ID}/${KUBERNETES_CLUSTER_PREFIX}-images" gcloud builds submit postgres-pgvector/docker/chatbot --region=${REGION} \ --tag ${DOCKER_REPO}/chatbot:1.0 --async gcloud builds submit postgres-pgvector/docker/embed-docs --region=${REGION} \ --tag ${DOCKER_REPO}/embed-docs:1.0 --async
Weaviate
export DOCKER_REPO="${REGION}-docker.pkg.dev/${PROJECT_ID}/${KUBERNETES_CLUSTER_PREFIX}-images" gcloud builds submit weaviate/docker/chatbot --region=${REGION} \ --tag ${DOCKER_REPO}/chatbot:1.0 --async gcloud builds submit weaviate/docker/embed-docs --region=${REGION} \ --tag ${DOCKER_REPO}/embed-docs:1.0 --async
验证映像:
gcloud artifacts docker images list $DOCKER_REPO \ --project=$PROJECT_ID \ --format="value(IMAGE)"
输出类似于以下内容:
$REGION-docker.pkg.dev/$PROJECT_ID/${KUBERNETES_CLUSTER_PREFIX}-images/chatbot $REGION-docker.pkg.dev/$PROJECT_ID/${KUBERNETES_CLUSTER_PREFIX}-images/embed-docs
部署具有运行 Kubernetes 作业的权限的 Kubernetes 服务账号:
Qdrant
sed "s/<PROJECT_ID>/$PROJECT_ID/;s/<CLUSTER_PREFIX>/$KUBERNETES_CLUSTER_PREFIX/" qdrant/manifests/05-rag/service-account.yaml | kubectl -n qdrant apply -f -
Elasticsearch
sed "s/<PROJECT_ID>/$PROJECT_ID/;s/<CLUSTER_PREFIX>/$KUBERNETES_CLUSTER_PREFIX/" elasticsearch/manifests/05-rag/service-account.yaml | kubectl -n elastic apply -f -
PGVector
sed "s/<PROJECT_ID>/$PROJECT_ID/;s/<CLUSTER_PREFIX>/$KUBERNETES_CLUSTER_PREFIX/" postgres-pgvector/manifests/03-rag/service-account.yaml | kubectl -n pg-ns apply -f -
Weaviate
sed "s/<PROJECT_ID>/$PROJECT_ID/;s/<CLUSTER_PREFIX>/$KUBERNETES_CLUSTER_PREFIX/" weaviate/manifests/04-rag/service-account.yaml | kubectl -n weaviate apply -f -
使用 Terraform 创建 GKE 集群并将
create_service_account
设置为 true 时,系统会创建一个单独的服务账号,供集群和节点使用。向此 Compute Engine 服务账号授予artifactregistry.serviceAgent
角色,以允许节点从为embed-docs
和chatbot
创建的 Artifact Registry 中拉取映像。export CLUSTER_SERVICE_ACCOUNT=$(gcloud container clusters describe ${KUBERNETES_CLUSTER_PREFIX}-cluster \ --location=${CONTROL_PLANE_LOCATION} \ --format="value(nodeConfig.serviceAccount)") gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="serviceAccount:${CLUSTER_SERVICE_ACCOUNT}" \ --role="roles/artifactregistry.serviceAgent"
如果未向该服务账号授予访问权限,则部署
embed-docs
和chatbot
Service 时,节点在尝试从 Artifact Registry 拉取映像时可能会遇到权限问题。为
embed-docs
和chatbot
Service 部署 Kubernetes Deployment。Deployment 是一个 Kubernetes API 对象,可让您运行在集群节点中分布的多个 Pod 副本。Qdrant
sed "s|<DOCKER_REPO>|$DOCKER_REPO|" qdrant/manifests/05-rag/chatbot.yaml | kubectl -n qdrant apply -f - sed "s|<DOCKER_REPO>|$DOCKER_REPO|" qdrant/manifests/05-rag/docs-embedder.yaml | kubectl -n qdrant apply -f -
Elasticsearch
sed "s|<DOCKER_REPO>|$DOCKER_REPO|" elasticsearch/manifests/05-rag/chatbot.yaml | kubectl -n elastic apply -f - sed "s|<DOCKER_REPO>|$DOCKER_REPO|" elasticsearch/manifests/05-rag/docs-embedder.yaml | kubectl -n elastic apply -f -
PGVector
sed "s|<DOCKER_REPO>|$DOCKER_REPO|" postgres-pgvector/manifests/03-rag/chatbot.yaml | kubectl -n pg-ns apply -f - sed "s|<DOCKER_REPO>|$DOCKER_REPO|" postgres-pgvector/manifests/03-rag/docs-embedder.yaml | kubectl -n pg-ns apply -f -
Weaviate
sed "s|<DOCKER_REPO>|$DOCKER_REPO|" weaviate/manifests/04-rag/chatbot.yaml | kubectl -n weaviate apply -f - sed "s|<DOCKER_REPO>|$DOCKER_REPO|" weaviate/manifests/04-rag/docs-embedder.yaml | kubectl -n weaviate apply -f -
为 GKE 启用 Eventarc 触发器:
gcloud eventarc gke-destinations init
出现提示时,输入
y
。使用 Terraform 部署 Cloud Storage 存储桶并创建 Eventarc 触发器:
export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token) terraform -chdir=vector-database/terraform/cloud-storage init terraform -chdir=vector-database/terraform/cloud-storage apply \ -var project_id=${PROJECT_ID} \ -var region=${REGION} \ -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX} \ -var db_namespace=${DB_NAMESPACE}
出现提示时,请输入
yes
。该命令可能需要几分钟才能完成。Terraform 会创建以下资源:
- 用于上传文档的 Cloud Storage 存储桶
- Eventarc 触发器
- 名为
service_account_eventarc_name
且有权使用 Eventarc 的 Google Cloud 服务账号。 - 名为
service_account_bucket_name
且有权读取存储桶和访问 Vertex AI 模型的 Google Cloud 服务账号。
输出内容类似如下:
... # Several lines of output omitted Apply complete! Resources: 15 added, 0 changed, 0 destroyed. ... # Several lines of output omitted
加载文档并运行聊天机器人查询
上传演示文档,然后运行查询以使用聊天机器人搜索演示文档:
将示例
carbon-free-energy.pdf
文档上传到您的存储桶:gcloud storage cp vector-database/documents/carbon-free-energy.pdf gs://${PROJECT_ID}-${KUBERNETES_CLUSTER_PREFIX}-training-docs
验证文档嵌入器作业是否已成功完成:
kubectl get job -n ${DB_NAMESPACE}
输出类似于以下内容:
NAME COMPLETIONS DURATION AGE docs-embedder1716570453361446 1/1 32s 71s
获取负载均衡器的外部 IP 地址:
export EXTERNAL_IP=$(kubectl -n ${DB_NAMESPACE} get svc chatbot --output jsonpath='{.status.loadBalancer.ingress[0].ip}') echo http://${EXTERNAL_IP}:80
在网络浏览器中打开外部 IP 地址:
http://EXTERNAL_IP
聊天机器人会返回类似于如下所示的消息:
How can I help you?
提出与所上传文档的内容相关的问题。如果聊天机器人找不到任何内容,则会回答
I don't know
。例如,您可能会提出以下问题:You: Hi, what are Google plans for the future?
聊天机器人的示例输出类似于以下内容:
Bot: Google intends to run on carbon-free energy everywhere, at all times by 2030. To achieve this, it will rely on a combination of renewable energy sources, such as wind and solar, and carbon-free technologies, such as battery storage.
向聊天机器人提出与上传的文档无关的问题。例如,您可能会提出以下问题:
You: What are Google plans to colonize Mars?
聊天机器人的示例输出类似于以下内容:
Bot: I don't know. The provided context does not mention anything about Google's plans to colonize Mars.
关于应用代码
本部分介绍应用代码的工作原理。Docker 映像中包含三个脚本:
endpoint.py
:在每次上传文档时接收 Eventarc 事件,并启动 Kubernetes 作业来处理这些事件。embedding-job.py
:从存储桶下载文档,创建嵌入,然后将嵌入插入到矢量数据库中。chat.py
:对存储的文档内容运行查询。
该图展示了使用文档数据生成回答的过程:
在该图中,应用会加载 PDF 文件,将文件拆分为多个分块,然后将这些分块转换为矢量,最后将相应矢量发送到矢量数据库。之后,用户向聊天机器人提问。RAG 链使用语义搜索来搜索矢量数据库,然后将上下文与问题一起返回给 LLM。LLM 会回答问题,并将问题存储到聊天记录中。
关于 endpoint.py
此文件处理来自 Eventarc 的消息,创建用于嵌入文档的 Kubernetes 作业,并通过端口 5001 接受来自任何位置的请求
Qdrant
Elasticsearch
PGVector
Weaviate
关于 embedding-job.py
此文件处理文档并将其发送到矢量数据库。
Qdrant
Elasticsearch
PGVector
Weaviate
关于 chat.py
此文件将模型配置为仅使用提供的上下文和以前的回答来回答问题。如果上下文或对话历史记录与任何数据都不匹配,则模型会返回 I don't know
。