1. 概览
想象一下,您可以通过线上或线下的方式进入玩具店,轻松找到合适的礼物。您可以描述自己想要的玩具、上传玩具的照片,甚至可以设计自己的作品,商店会立即了解您的需求,并提供量身定制的体验。这并非科幻想象,而是由 AI、云技术和个性化电子商务愿景赋能的现实。
挑战:仅仅找到与您的想象相符的完美产品可能就很难。常规搜索字词、关键字和模糊搜索通常效果不佳,浏览无数网页可能会很乏味,如果您想象的与实际提供的内容不符,可能会感到沮丧。
解决方案:该演示版应用正面应对这一挑战,利用 AI 的强大功能,通过情境搜索和与搜索情境匹配的商品自定义生成功能,提供真正个性化且顺畅的体验。
构建内容
在本实验中,您将执行以下操作:
- 创建 AlloyDB 实例并加载 Toys 数据集
- 在 AlloyDB 中启用 pgvector 和生成式 AI 模型扩展
- 根据产品说明生成嵌入,并对用户搜索文本执行实时余弦相似度搜索
- 调用 Gemini 2.0 Flash 来描述用户上传的图片,以进行内容相关的玩具搜索
- 调用 Imagen 3,根据用户的兴趣自定义创建玩具
- 调用使用 Gen AI Toolbox for Databases 创建的价格预测工具,以获取自定义玩具的价格详情
- 在无服务器 Cloud Run Functions 中部署解决方案
要求
2. 架构
数据流:我们来详细了解一下数据在系统中的流动方式:
- 依托 AI 技术的 RAG(检索增强生成)赋能的上下文搜索
举个例子:系统不仅会搜索“红色汽车”,还会理解以下内容:
“适合 3 岁男孩的小型车辆”。
以 AlloyDB 为基础:我们使用 AlloyDB(Google Cloud 的全托管式 PostgreSQL 兼容数据库)来存储玩具数据,包括说明、图片网址和其他相关属性。
用于语义搜索的 pgvector:pgvector 是 PostgreSQL 扩展程序,可让我们存储玩具说明和用户搜索查询的矢量嵌入。这支持语义搜索,也就是说,系统不仅会理解字词的确切含义,还会理解字词背后的含义。
用于衡量相关性的余弦相似度:我们使用余弦相似度来衡量用户的搜索向量与玩具说明向量之间的语义相似度,以显示最相关的结果。
ScaNN 索引,可提高速度和准确性:为确保快速准确地获得结果,尤其是随着玩具目录的不断扩大,我们集成了 ScaNN(可扩容的最近邻)索引。这大大提高了向量搜索的效率和召回率。
- 使用 Gemini 2.0 Flash 进行基于图片的搜索和理解
假设用户想要上传一张熟悉的玩具的照片,并使用该照片进行搜索,而不是以文本形式输入上下文。用户可以上传自己喜欢的玩具的图片,并通过此功能获得相关功能。我们利用 Google 的 Gemini 2.0 Flash 模型(使用 LangChain4j 调用)来分析图片并提取相关背景信息,例如玩具的颜色、材质、类型和目标年龄段。
- 使用生成式 AI 打造您的专属梦想玩具:Imagen 3
当用户决定自行制作玩具时,真正的魔力就会发生。借助 Imagen 3,他们可以使用简单的文本提示描述自己的梦想玩具。想象一下,您可以说“我想要一个紫色翅膀、面容友好的毛绒龙”,然后看到这条龙在屏幕上栩栩如生地出现!然后,Imagen 3 会生成自定义玩具的图片,让用户清晰地直观呈现其创作成果。
- 依托代理和数据库专用 Gen AI Toolbox 的价格预测
我们实现了一项价格预测功能,可估算定制玩具的制作费用。此功能由包含复杂价格计算工具的代理提供支持。
适用于数据库的 Gen AI 工具包:此代理使用 Google 的新开源工具(适用于数据库的 Gen AI 工具包)与我们的数据库无缝集成。这样,代理就可以访问有关材料成本、制造流程和其他相关因素的实时数据,以提供准确的价格估算值。如需了解详情,请点击此处。
- 使用 Java Spring Boot、Gemini Code Assist 和 Cloud Run 简化开发流程并实现无服务器部署
整个应用是使用 Java Spring Boot(一种强大且可伸缩的框架)构建的。我们在整个开发过程中都利用了 Gemini Code Assist,尤其是在前端开发中,这显著缩短了开发周期并提高了代码质量。我们使用 Cloud Run 部署整个应用,并使用 Cloud Run Functions 将数据库和代理功能部署为独立端点。
3. 准备工作
创建项目
- 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。
- 确保您的 Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能。
- 您将使用 Cloud Shell,这是一个在 Google Cloud 中运行的命令行环境,它预加载了 bq。点击 Google Cloud 控制台顶部的“激活 Cloud Shell”。
- 连接到 Cloud Shell 后,您可以使用以下命令检查自己是否已通过身份验证,以及项目是否已设置为您的项目 ID:
gcloud auth list
- 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目。
gcloud config list project
- 如果项目未设置,请使用以下命令进行设置:
gcloud config set project <YOUR_PROJECT_ID>
- 在 Cloud Shell 终端中依次运行以下命令,启用所需的 API:
您还可以使用单个命令运行以下命令,但如果您是试用账号用户,则在尝试批量启用这些功能时可能会遇到配额问题。因此,命令会单独列在一行中。
gcloud services enable alloydb.googleapis.com
gcloud services enable compute.googleapis.com
gcloud services enable cloudresourcemanager.googleapis.com
gcloud services enable servicenetworking.googleapis.com
gcloud services enable run.googleapis.com
gcloud services enable cloudbuild.googleapis.com
gcloud services enable cloudfunctions.googleapis.com
gcloud services enable aiplatform.googleapis.com
您可以通过控制台搜索各个产品或使用此链接,以替代 gcloud 命令。
如果缺少任何 API,您随时可以在实现过程中启用它。
如需了解 gcloud 命令和用法,请参阅文档。
4. 数据库设置
在本实验中,我们将使用 AlloyDB 作为数据库来存储玩具店数据。它使用集群来存储所有资源,例如数据库和日志。每个集群都有一个主实例,用于提供对数据的访问点。表将存储实际数据。
我们来创建一个 AlloyDB 集群、实例和表,以便加载电子商务数据集。
创建集群和实例
- 在 Cloud 控制台中,前往 AlloyDB 页面。在 Cloud 控制台中查找大多数页面时,最简单的方法是使用控制台的搜索栏进行搜索。
- 从该页面中选择创建集群:
- 您将看到如下所示的界面。使用以下值创建集群和实例(如果您要从代码库克隆应用代码,请确保这些值一致):
- 集群 ID:“
vector-cluster
” - password:“
alloydb
” - 与 PostgreSQL 15 兼容
- 区域:“
us-central1
” - 网络:“
default
”
- 选择默认网络后,您会看到如下所示的界面。
选择设置连接。
- 然后,选择使用自动分配的 IP 范围并点击“继续”。查看信息后,选择“创建关联”。
- 设置好网络后,您可以继续创建集群。点击创建集群以完成集群设置,如下所示:
请务必将实例 ID 更改为“
vector-instance"
。
请注意,创建集群大约需要 10 分钟。成功完成后,您应该会看到一个屏幕,其中显示了您刚刚创建的集群的概览。
5. 数据提取
现在,我们需要添加一个包含商店数据的表格。前往 AlloyDB,选择主集群,然后选择 AlloyDB Studio:
您可能需要等待实例创建完成。完成后,使用您在创建集群时创建的凭据登录 AlloyDB。使用以下数据对 PostgreSQL 进行身份验证:
- 用户名:“
postgres
” - 数据库:“
postgres
” - 密码:“
alloydb
”
成功通过身份验证登录 AlloyDB Studio 后,您可以在编辑器中输入 SQL 命令。您可以使用最后一个窗口右侧的加号添加多个编辑器窗口。
您将在编辑器窗口中输入 AlloyDB 命令,并根据需要使用“Run”“Format”和“Clear”选项。
启用扩展程序
如需构建此应用,我们将使用扩展程序 pgvector
和 google_ml_integration
。借助 pgvector 扩展程序,您可以存储和搜索向量嵌入。google_ml_integration 扩展程序提供了一些函数,可用于访问 Vertex AI 预测端点,以便在 SQL 中获取预测结果。通过运行以下 DDL 启用这些扩展程序:
CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;
如果您想检查数据库上已启用的扩展程序,请运行以下 SQL 命令:
select extname, extversion from pg_extension;
创建表
使用以下 DDL 语句创建表:
CREATE TABLE toys ( id VARCHAR(25), name VARCHAR(25), description VARCHAR(20000), quantity INT, price FLOAT, image_url VARCHAR(200), text_embeddings vector(768)) ;
成功执行上述命令后,您应该能够在数据库中查看该表。
注入数据
在本实验中,我们在此 SQL 文件中提供了约 72 条记录的测试数据。它包含 id, name, description, quantity, price, image_url
字段。我们将在稍后的实验中填写其他字段。
从中复制相应行/插入语句,然后将这些行粘贴到空白编辑器标签页中,并选择“运行”。
如需查看表内容,请展开“Explorer”部分,直到看到名为 apparels 的表。选择三点状图标 (⋮) 即可看到用于查询表的选项。系统会在新建的“编辑器”标签页中打开一个 SELECT 语句。
授予权限
运行以下语句可向用户 postgres
授予对 embedding
函数的执行权限:
GRANT EXECUTE ON FUNCTION embedding TO postgres;
向 AlloyDB 服务账号授予 Vertex AI User 角色
前往 Cloud Shell 终端,然后输入以下命令:
PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
6. 为上下文创建嵌入
计算机处理数字比处理文本要容易得多。嵌入系统会将文本转换为一组浮点数,这些浮点数应能代表文本,无论文本的措辞、所用语言等如何。
不妨考虑描述海边地点。这类房源可能被称为“on the water”“beachfront”“从房间步行到海边”“sur la mer”“на берегу океана”等。这些术语看起来各不相同,但它们的语义含义(或用机器学习术语来说,它们的嵌入)应该非常接近。
现在,数据和上下文已准备就绪,我们将运行 SQL 查询,将商品说明的嵌入向量添加到表中的 embedding
字段。您可以使用多种嵌入模型。我们将使用 Vertex AI 中的 text-embedding-005
。请务必在整个项目中使用相同的嵌入模型!
注意:如果您使用的是之前创建的现有 Google Cloud 项目,则可能需要继续使用较低版本的文本嵌入模型,例如 textembedding-gecko。
返回“AlloyDB Studio”标签页,然后输入以下 DML:
UPDATE toys set text_embeddings = embedding( 'text-embedding-005', description);
再次查看 toys
表,看看其中的一些嵌入。请务必重新运行 SELECT 语句,以查看更改。
SELECT id, name, description, price, quantity, image_url, text_embeddings FROM toys;
这应该会返回玩具说明的嵌入矢量,该矢量看起来像一个浮点数数组,如下所示:
注意:在免费层级下新创建的 Google Cloud 项目在向嵌入模型发出的每秒嵌入请求数方面可能会遇到配额问题。我们建议您使用 ID 过滤查询,然后选择 1-5 条记录等,以便生成嵌入。
7. 执行矢量搜索
现在,表格、数据和嵌入都已准备就绪,接下来我们将针对用户搜索文本执行实时向量搜索。
假设用户询问:
“I want a white plush teddy bear toy with a floral pattern
”。
您可以通过运行以下查询找到匹配项:
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;
我们来详细看看这个查询:
在此查询中,
- 用户的搜索文本为:“
I want a white plush teddy bear toy with a floral pattern.
” - 我们将在
embedding()
方法中使用模型text-embedding-005
将其转换为嵌入。与上一步(我们将嵌入函数应用于表格中的所有项)相比,此步骤应该会显得熟悉。 - “
<=>
”表示使用余弦相似度距离方法。您可以在 pgvector 文档中找到所有可用的相似度衡量方法。 - 我们将嵌入方法的结果转换为向量类型,以使其与存储在数据库中的向量兼容。
- LIMIT 5 表示我们要提取搜索文本的 5 个最近邻。
结果如下所示:
如您在结果中所见,匹配项与搜索文本非常接近。尝试更改文字,看看结果如何变化。
重要提示:
现在,假设我们想使用 ScaNN 索引提高此矢量搜索结果的性能(查询时间)、效率和召回率。请阅读这篇博文中的步骤,比较使用和不使用索引时的结果差异。
可选步骤:使用 ScaNN 索引提高效率和召回率
为方便起见,下面列出了索引创建步骤:
- 由于我们已经创建了集群、实例、上下文和嵌入,因此只需使用以下语句安装 ScaNN 扩展程序即可:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
- 接下来,我们将创建索引 (ScaNN):
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);
在上面的 DDL 中,apparel_index 是索引的名称
“toys”是我的表格
“scann”是索引方法
“embedding”是我要为其编制索引的表中的列
“cosine”是我要与索引搭配使用的距离方法
“8”是应用于此索引的分区数量。设置为 1 到 1048576 之间的任意值。如需详细了解如何确定此值,请参阅调整 ScaNN 索引。
我使用了 ScaNN 代码库中建议的数据点数量的平方根(在进行分区时,num_leaves 应大致为数据点数量的平方根)。
- 使用以下查询检查是否已创建索引:
SELECT * FROM pg_stat_ann_indexes;
- 使用我们在未使用索引时使用的相同查询执行 Vector Search:
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;
上述查询与我们在实验的第 8 步中使用的查询相同。不过,现在我们已为该字段编入索引。
- 使用简单的搜索查询进行测试,分别测试有索引和无索引(通过删除索引)的查询:
此用例只有 72 条记录,因此索引实际上不会生效。对于在其他用例中进行的测试,结果如下:
对已编入索引的嵌入数据执行相同的 Vector Search 查询,可获得优质的搜索结果并提高效率。使用索引后,效率大大提高(在执行时间方面:不使用 ScaNN 时为 10.37 毫秒,使用 ScaNN 时为 0.87 毫秒)。如需详细了解此主题,请参阅这篇博文。
8. 使用 LLM 进行匹配验证
在继续创建服务以针对应用返回最匹配的回答之前,我们先使用生成式 AI 模型来验证这些潜在回答是否确实相关且可以安全地与用户分享。
确保已为实例设置 Gemini
首先,检查您的集群和实例是否已启用 Google ML 集成。在 AlloyDB Studio 中,输入以下命令:
show google_ml_integration.enable_model_support;
如果该值显示为“开启”,您可以跳过接下来的 2 个步骤,直接设置 AlloyDB 与 Vertex AI 模型的集成。
- 前往 AlloyDB 集群的主实例,然后点击“修改主实例”
- 前往“高级配置选项”中的“标志”部分。并确保将
google_ml_integration.enable_model_support flag
设置为“on
”,如下所示:
如果未设置为“开启”,请将其设置为“开启”,然后点击更新实例按钮。此步骤需要几分钟时间。
AlloyDB 与 Vertex AI 模型集成
现在,您可以连接到 AlloyDB Studio,并运行以下 DML 语句,以便在 AlloyDB 中设置 Gemini 模型访问权限,并在指示位置使用您的项目 ID。在运行该命令之前,您可能会收到语法错误警告,但该命令应该可以正常运行。
首先,我们创建 Gemini 1.5 模型连接,如下所示。请务必将以下命令中的 $PROJECT_ID
替换为您的 Google Cloud 项目 ID。
CALL
google_ml.create_model( model_id => 'gemini-1.5',
model_request_url => 'https://us-central1-aiplatform.googleapis.com/v1/projects/$PROJECT_ID/locations/us-central1/publishers/google/models/gemini-1.5-pro:streamGenerateContent',
model_provider => 'google',
model_auth_type => 'alloydb_service_agent_iam');
您可以在 AlloyDB Studio 中通过以下命令查看已配置为可访问的模型:
select model_id,model_type from google_ml.model_info_view;
最后,我们需要向数据库用户授予执行 ml_predict_row 函数的权限,以便通过 Google Vertex AI 模型运行预测。运行以下命令:
GRANT EXECUTE ON FUNCTION ml_predict_row to postgres;
注意:如果您使用的是现有的 Google Cloud 项目和一段时间前创建的现有 AlloyDB 集群/实例,则可能需要删除对 gemini-1.5 模型的旧引用,并使用上述 CALL 语句重新创建,然后再次对函数 ml_predict_row 运行 grant execute,以防在接下来的 gemini-1.5 调用中遇到问题。
评估回答
虽然我们最终会在下一部分中使用一个大型查询来确保查询的响应合理,但该查询可能很难理解。我们现在来看看这些组成部分,并在几分钟后看看它们是如何组合在一起的。
- 首先,我们会向数据库发送请求,以获取与用户查询最相符的 10 个结果。
- 为了确定回答的有效性,我们将使用外部查询,在其中说明如何评估回答。它使用搜索文本
recommended_text
字段和内部表的content
(即玩具说明字段)作为查询的一部分。 - 然后,我们将根据该指标来评估返回的回答的“优质程度”。
predict_row
会以 JSON 格式返回结果。代码“-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"
”用于从该 JSON 中提取实际文本。如需查看实际返回的 JSON,您可以移除此代码。- 最后,为了获取 LLM 回答,我们使用
REGEXP_REPLACE(gemini_validation,
'[^a-zA-Z,: ]',
'',
'g')
进行提取
SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') AS gemini_validation
FROM (SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
CAST(ARRAY_AGG(LLM_RESPONSE) AS TEXT) AS gemini_validation
FROM (SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
json_array_elements(google_ml.predict_row(model_id => 'gemini-1.5',
request_body => CONCAT('{ "contents": [ { "role": "user", "parts": [ { "text": "User wants to buy a toy and this is the description of the toy they wish to buy: ', recommended_text, '. Check if the following product items from the inventory are close enough to really, contextually match the user description. Here are the items: ', content, '. Return a ONE-LINE response with 3 values: 1) MATCH: if the 2 contexts are reasonably matching in terms of any of the color or color family specified in the list, approximate style match with any of the styles mentioned in the user search text: This should be a simple YES or NO. Choose NO only if it is completely irrelevant to users search criteria. 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear one-line easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match. " } ] } ] }')::JSON)) -> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text' :: TEXT AS LLM_RESPONSE
FROM (SELECT id,
name,
description AS content,
quantity,
price,
image_url,
'Pink panther standing' AS recommended_text
FROM toys
ORDER BY text_embeddings <=> embedding('text-embedding-005',
'Pink panther standing')::VECTOR
LIMIT 10) AS xyz) AS X
GROUP BY id,
name,
content,
quantity,
price,
image_url,
recommended_text) AS final_matches
WHERE REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') LIKE '%MATCH%:%YES%';
虽然这可能看起来令人却步,但希望您能对此有更深入的了解。结果会显示是否有匹配项、匹配百分比,以及对分级的某些说明。
请注意,Gemini 模型默认处于流式传输状态,因此实际回答会分布在多行中:
9. 将玩具搜索功能无服务器地迁移到云端
准备好将此应用移植到 Web 平台了吗?请按照以下步骤使用 Cloud Run 函数将此知识引擎服务器化:
- 前往 Google Cloud 控制台中的 Cloud Run Functions 页面,创建新的 Cloud Run 函数,或使用以下链接:https://console.cloud.google.com/functions/add。
- 将“环境”选择为“Cloud Run 函数”。提供函数名称“get-toys-alloydb”,并选择“us-central1”作为区域。将“Authentication”(身份验证)设置为“Allow unauthenticated invocations”(允许未经身份验证的调用),然后点击 NEXT(下一步)。选择 Java 17 作为运行时,并为源代码选择内嵌编辑器。
- 默认情况下,它会将入口点设置为“
gcfv2.HelloHttpFunction
”。将 Cloud Run 函数的HelloHttpFunction.java
和pom.xml
中的占位符代码分别替换为 HelloHttpFunction.java 和 pom.xml 中的代码。 - 请务必在 Java 文件中将 <<YOUR_PROJECT>> 占位符和 AlloyDB 连接凭据更改为您的值。AlloyDB 凭据是我们在本 Codelab 开头时使用的凭据。如果您使用了其他值,请在 Java 文件中进行相应修改。
- 点击部署。
部署完成后,为了允许 Cloud Functions 函数访问 AlloyDB 数据库实例,我们将创建 VPC 连接器。
重要步骤:
准备好部署后,您应该可以在 Google Cloud Run Functions 控制台中看到这些函数。搜索新创建的函数 (get-toys-alloydb
),点击该函数,然后点击修改并更改以下内容:
- 前往“运行时、构建、连接和安全设置”
- 将超时时间延长至 180 秒
- 前往“连接”标签页:
- 在“入站流量”设置下,确保已选择“允许所有流量”。
- 在“出站流量设置”下,点击“网络”下拉菜单,然后选择“添加新的 VPC 连接器”选项,并按照随即弹出的对话框中显示的说明操作:
- 为 VPC 连接器提供一个名称,并确保区域与您的实例相同。将“网络”值保留为默认值,并将“子网”设置为“自定义 IP 范围”,IP 范围为 10.8.0.0 或其他可用的类似 IP 地址。
- 展开“显示缩放设置”,并确保将配置设置为完全如下所示:
- 点击“创建”,此连接器现在应已列在出站流量设置中。
- 选择新创建的连接器
- 选择将所有流量都通过此 VPC 连接器路由。
- 依次点击下一步和部署。
10. 测试 Cloud Run 函数
更新后的 Cloud Functions 函数部署完毕后,您应该会看到采用以下格式的端点:
https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/get-toys-alloydb
或者,您也可以按如下所示测试 Cloud Run 函数:
PROJECT_ID=$(gcloud config get-value project)
curl -X POST https://us-central1-$PROJECT_ID.cloudfunctions.net/get-toys-alloydb \
-H 'Content-Type: application/json' \
-d '{"search":"I want a standing pink panther toy"}' \
| jq .
结果如下:
大功告成!只需这样,即可使用嵌入模型对 AlloyDB 数据执行相似性矢量搜索。
11. 构建 Web 应用客户端!
在本部分中,我们将构建一个 Web 应用,供用户与之互动,并根据文字、图片查找匹配的玩具,甚至根据自己的需求创建新玩具。由于应用已构建完毕,您可以按照以下步骤将其复制到 IDE 并启动应用。
- 由于我们使用 Gemini 2.0 Flash 来描述用户可能会上传的图片以查找匹配的玩具,因此我们需要获取此应用的 API 密钥。为此,请前往 https://aistudio.google.com/apikey,获取您要实现此应用的有效 Google Cloud 项目的 API 密钥,并将该密钥保存到某个位置:
- 前往 Cloud Shell 终端
- 使用以下命令克隆代码库:
git clone https://github.com/AbiramiSukumaran/toysearch
cd toysearch
- 克隆代码库后,您应该能够通过 Cloud Shell Editor 访问该项目。
- 您需要从克隆的项目中删除“get-toys-alloydb”和“toolbox-toys”文件夹,因为这两个文件夹包含 Cloud Run Functions 代码,您可以在需要时从代码库中引用这些代码。
- 在构建和部署应用之前,请确保已设置所有必要的环境变量。前往 Cloud Shell 终端,然后执行以下命令:
PROJECT_ID=$(gcloud config get-value project)
export PROJECT_ID $PROJECT_ID
export GOOGLE_API_KEY <YOUR API KEY that you saved>
- 在本地构建并运行应用:
确保您位于项目目录中,然后运行以下命令:
mvn package
mvn spring-boot:run
- 在 Cloud Run 上部署
gcloud run deploy --source .
12. 了解生成式 AI 的详细信息
您无需执行任何操作。请注意:
现在,您已经有了要部署的应用,不妨花点时间了解我们是如何实现搜索(文本和图片)和生成功能的。
- 基于用户文本的向量搜索:
我们在“使用 Vector Search 应用 Web 版”部分部署的 Cloud Run Functions 中已经解决了这个问题。
- 基于图片上传的 Vector Search:
假设用户想要上传一张熟悉的玩具的照片,并使用该照片进行搜索,而不是以文本形式输入上下文。用户可以上传自己喜欢的玩具的图片,并通过此功能获得相关功能。
我们利用 Google 的 Gemini 2.0 Flash 模型(使用 LangChain4j 调用)来分析图片并提取相关背景信息,例如玩具的颜色、材质、类型和目标年龄段。
只需 5 步,我们便可使用开源框架将用户多模态数据输入与大型语言模型调用进行匹配。探索以下问题:
package cloudcode.helloworld.web;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import java.util.Base64;
import java.util.Optional;
public class GeminiCall {
public String imageToBase64String(byte[] imageBytes) {
String base64Img = Base64.getEncoder().encodeToString(imageBytes);
return base64Img;
}
public String callGemini(String base64ImgWithPrefix) throws Exception {
String searchText = "";
// 1. Remove the prefix
String base64Img = base64ImgWithPrefix.replace("data:image/jpeg;base64,", "");
// 2. Decode base64 to bytes
byte[] imageBytes = Base64.getDecoder().decode(base64Img);
String image = imageToBase64String(imageBytes);
// 3. Get API key from environment variable
String apiKey = Optional.ofNullable(System.getenv("GOOGLE_API_KEY"))
.orElseThrow(() -> new IllegalArgumentException("GOOGLE_API_KEY environment variable not set"));
// 4. Invoke Gemini 2.0
ChatLanguageModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(apiKey)
.modelName("gemini-2.0-flash-001")
.build();
Response<AiMessage> response = gemini.generate(
UserMessage.from(
ImageContent.from(image, "image/jpeg"),
TextContent.from(
"The picture has a toy in it. Describe the toy in the image in one line. Do not add any prefix or title to your description. Just describe that toy that you see in the image in one line, do not describe the surroundings and other objects around the toy in the image. If you do not see any toy in the image, send response stating that no toy is found in the input image.")));
// 5. Get the text from the response and send it back to the controller
searchText = response.content().text().trim();
System.out.println("searchText inside Geminicall: " + searchText);
return searchText;
}
}
- 了解我们如何使用 Imagen 3 结合生成式 AI 技术,根据用户需求打造个性化玩具。
然后,Imagen 3 会生成自定义玩具的图片,让用户清晰地直观呈现其创作成果。我们只需 5 个步骤即可实现这一目标:
// Generate an image using a text prompt using an Imagen model
public String generateImage(String projectId, String location, String prompt)
throws ApiException, IOException {
final String endpoint = String.format("%s-aiplatform.googleapis.com:443", location);
PredictionServiceSettings predictionServiceSettings =
PredictionServiceSettings.newBuilder().setEndpoint(endpoint).build();
// 1. Set up the context and prompt
String context = "Generate a photo-realistic image of a toy described in the following input text from the user. Make sure you adhere to all the little details and requirements mentioned in the prompt. Ensure that the user is only describing a toy. If it is anything unrelated to a toy, politely decline the request stating that the request is inappropriate for the current context. ";
prompt = context + prompt;
// 2. Initialize a client that will be used to send requests. This client only needs to be created
// once, and can be reused for multiple requests.
try (PredictionServiceClient predictionServiceClient =
PredictionServiceClient.create(predictionServiceSettings)) {
// 3. Invoke Imagen 3
final EndpointName endpointName =
EndpointName.ofProjectLocationPublisherModelName(
projectId, location, "google", "imagen-3.0-generate-001"); //"imagegeneration@006"; imagen-3.0-generate-001
Map<String, Object> instancesMap = new HashMap<>();
instancesMap.put("prompt", prompt);
Value instances = mapToValue(instancesMap);
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("sampleCount", 1);
paramsMap.put("aspectRatio", "1:1");
paramsMap.put("safetyFilterLevel", "block_few");
paramsMap.put("personGeneration", "allow_adult");
paramsMap.put("guidanceScale", 21);
paramsMap.put("imagenControlScale", 0.95); //Setting imagenControlScale
Value parameters = mapToValue(paramsMap);
// 4. Get prediction response image
PredictResponse predictResponse =
predictionServiceClient.predict(
endpointName, Collections.singletonList(instances), parameters);
// 5. Return the Base64 Encoded String to the controller
for (Value prediction : predictResponse.getPredictionsList()) {
Map<String, Value> fieldsMap = prediction.getStructValue().getFieldsMap();
if (fieldsMap.containsKey("bytesBase64Encoded")) {
bytesBase64EncodedOuput = fieldsMap.get("bytesBase64Encoded").getStringValue();
}
}
return bytesBase64EncodedOuput.toString();
}
}
价格预测
在上一部分中,我们讨论了 Imagen 如何生成用户希望自行设计的玩具的图片。为了让用户能够购买,应用需要为其设置价格,我们采用了直观的逻辑来为定制玩具定义价格。其逻辑是使用与用户设计的玩具最相符(在说明方面)的前 5 个玩具的平均价格。
生成的玩具的价格预测是此应用的重要组成部分,我们使用代理方法生成了此预测。推出适用于数据库的生成式 AI 工具箱。
13. 适用于数据库的生成式 AI 工具箱
适用于数据库的生成式 AI 工具包是 Google 推出的一款开源服务器,可让您更轻松地构建用于与数据库交互的生成式 AI 工具。它可以处理连接池、身份验证等复杂问题,让您能够更轻松、更快速、更安全地开发工具。它可帮助您构建生成式 AI 工具,让客服人员能够访问数据库中的数据。
您需要按照以下步骤进行设置,以便准备好您的工具并使我们的应用代理化:指向 Toolbox Codelab 的链接
您的应用现在可以使用此已部署的 Cloud Run 函数端点,为自定义定制玩具图片填充价格以及生成的 Imagen 结果。
14. 测试您的 Web 应用
现在,应用的所有组件都已构建并部署完毕,可以开始在云端提供服务了。针对所有场景测试您的应用。您可以观看以下视频,了解可能出现的情况:
https://www.youtube.com/shorts/ZMqUAWsghYQ
着陆页如下所示:
15. 清理
为避免系统因本博文中使用的资源向您的 Google Cloud 账号收取费用,请按照以下步骤操作:
- 在 Google Cloud 控制台中,前往管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关停以删除项目。
16. 恭喜
恭喜!您已成功使用 AlloyDB、pgvector、Imagen 和 Gemini 2.0 执行了玩具店情境搜索和生成,同时利用开源库构建了强大的集成。通过结合 AlloyDB、Vertex AI 和 Vector Search 的功能,我们在让情境搜索和矢量搜索变得简单易用、高效且真正以意义为导向方面取得了长足进步。