1. 简介
您是否喜欢钻研书籍,却困于繁杂的海量图书选择?假设有一款 AI 赋能的应用,它不仅能推荐理想的读物,还会根据您选择的图书类型提供简明摘要,让您快速掌握书本的精髓。在此 Codelab 中,我将引导您使用由 Gemini 提供支持的 BigQuery 和 Cloud Functions 构建此类应用。
项目概览
我们的应用场景围绕以下 4 个关键组件展开:
- Book Database:这是一个庞大的互联网归档图书 BigQuery 公共数据集,将作为全面的图书目录。
- AI 摘要引擎:借助 Gemini-Pro 语言模型,Google Cloud Functions 会根据用户请求生成富有洞见的摘要。
- BigQuery 集成:BigQuery 中的远程函数,可调用 Cloud Functions 函数,以便按需提供图书摘要和主题。
- 界面:托管在 Cloud Run 上的 Web 应用,可供用户查看结果。
我们将将实现分为 3 个 Codelab:
Codelab 1:使用 Gemini 为 Gemini 应用构建 Java Cloud Functions 函数。
Codelab 2:使用 Gemini 通过 BigQuery 构建仅限 SQL 的生成式 AI 应用。
Codelab 3:使用 Gemini 创建与 BigQuery 交互的 Java Spring Boot Web 应用。
2. 使用 Gemini 在 Java Cloud Functions 函数上以无服务器方式构建生成式 AI 应用
构建内容
您将创建一个
- Java Cloud Functions 应用,用于实现 Gemini 1.0 Pro,以将特定提示作为 JSON 数组形式的输入并返回响应(Json 值标记为“回复”)。
- 您将在 Gemini 的帮助下完成构建和部署步骤
3. 要求
以下是必备条件:
创建项目
- 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。
- 确保您的 Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能。
激活 Cloud Shell
- 您将使用 Cloud Shell,这是一个在 Google Cloud 中运行的命令行环境,它预加载了 bq:
在 Cloud 控制台中,点击右上角的“激活 Cloud Shell”:
- 在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且项目已设置为您的项目 ID。在 Cloud Shell 中运行以下命令,以确认您已通过身份验证:
gcloud auth list
- 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目
gcloud config list project
- 如果项目未设置,请使用以下命令进行设置:
gcloud config set project <YOUR_PROJECT_ID>
如需了解 gcloud 命令和用法,请参阅文档。
4. 启用 Gemini for Google Cloud 和必要的 API
启用 Gemini
- 前往 Marketplace 中的 Gemini for Google Cloud 启用该 API。您还可以使用以下命令:
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID
- 访问 Gemini 页面,然后点击“开始聊天”。
重要提示:请按照此 Codelab 中的第 1 步和第 2 步操作,分别开始使用 Gemini 和在 Cloud Shell IDE 中启用 Gemini。
启用其他必要的 API
我们该怎么做呢?让我们来问问 Gemini 吧。但在此之前,请记住:
LLM 具有不确定性。因此,在您尝试提出这些问题时,您收到的回答可能与我的屏幕截图中的回答有所不同。
点击右上角的“打开 Gemini”图标(位于 Google Cloud 控制台搜索栏旁边),前往 Gemini Chat 控制台。
在“在此处输入提示”部分输入以下问题:
How do I enable the cloud functions api using a gcloud command?
您应该会收到类似如下的响应:
gcloud services enable cloudfunctions.googleapis.com
复制该命令(您可以使用命令代码段顶部的复制图标),然后在 Cloud Shell 终端中运行,以启用 Cloud Functions。对 Cloud Run 执行相同的操作,因为我们需要同时构建和部署 Cloud Functions 函数:
gcloud services enable \
cloudfunctions.googleapis.com \
aiplatform.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com
5. 使用 Gemini 准备 Cloud Functions 模板
现在,我假设您已经在 Cloud Shell IDE 中启用了 Gemini。
点击 Cloud Shell 终端右上角的“打开编辑器”图标,打开 Cloud Shell 编辑器(我通常更喜欢在单独的标签页中并行打开终端和编辑器,以便在一个标签页中编写代码,在另一个标签页中构建)。
打开编辑器后,确保编辑器控制台右下角的 Gemini 徽标处于有效状态(而非取消状态)。此外,确保左下角的 Google Cloud 项目指向您当前想要处理的有效项目。如果它们处于无效状态,请点击进行授权,选择您要指向的 Google Cloud 项目并将其激活。
两者都处于有效状态后,点击左下角的项目名称,在随即打开的名为“Cloud Code”的弹出式列表中向下滚动到“新应用”。
在该列表中,选择“Cloud Functions 应用”。从弹出的列表中选择“Java”:
在结果列表中,输入项目名称“duetai-gemini-calling”而不是“helloworld”,然后点击“确定”。
太棒了!您已经使用 Gemini 引导了简单的 Java Cloud Functions 应用,除了启用和激活配置之外,您没怎么做,对吧?
您应该会看到以下项目结构:
现在,您就可以部署函数了。但这并非我们开始这项工作的原因。接下来,我们使用 Java SDK 在此 Cloud Functions 函数中构建 Gemini Pro API 实现。
现在,我们来为此用例构建功能,即在此 Cloud Functions 函数中调用 Gemini Pro 模型。为此,您可以添加更多提示,并使用 Gemini 逐步开发代码,也可以自行编写逻辑。我会同时采用这两种方法。
6. 添加依赖项
在 Gemini 聊天控制台中(左侧窗格中的 Cloud Code 编辑器内),输入以下提示:
what is the maven dependency for com.google.cloud.vertexai library
之所以专门询问 com.google.cloud.vertexai 软件包,是因为我在实现 Gemini 调用代码的源代码中使用了该软件包。
结果如下:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-vertexai</artifactId>
<version>0.1.0</version>
</dependency>
复制下面的代码并将其粘贴到 pom.xml 文件中,放在 </dependencies> 标记前面,且紧邻该标记。将版本替换为 0.1.0(如果您使用 Spring Cloud GCP BOM 为您管理 spring-cloud-gcp 版本号,则可以移除 <version> 标记)。
依赖项部分应如下所示:
如果需要,请务必更新版本号,使其与上述内容保持一致。如您所注意,我还添加了另一个依赖项:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10</version>
</dependency>
7. 修改函数入口点和类名称
- 前往“.vscode”文件夹下的“launch.json”文件。将函数名称从“function-hello-world”修改为“function-gemini-calling”。
- 将 entryPoint 值从“cloudcode.helloworld.HelloWorld”更新为“cloudcode.bookshelf.Bookshelf”。
- 现在,打开 Java 类文件“HelloWorld.java”。将软件包名称更改为 package cloudcode.bookshelf;在弹出的错误中,点击黄色灯泡,然后点击“将 HelloWorld.java 移至 package cloudcode.bookshelf”选项。
- 将类名称更新为“书架”,在弹出的错误中,点击黄色的小灯泡,然后选择“将文件重命名为“Books.java”。选择该选项。
8. 创建用于调用 Gemini Pro 的方法
我们将在 Bookshelf.java 类中实现此功能。将 Bookshelf.java 替换为以下代码:
package cloudcode.bookshelf;
import java.io.BufferedWriter;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.cloud.vertexai.VertexAI;
import com.google.cloud.vertexai.api.GenerateContentResponse;
import com.google.cloud.vertexai.api.GenerationConfig;
import com.google.cloud.vertexai.generativeai.preview.GenerativeModel;
import com.google.cloud.vertexai.generativeai.preview.ResponseHandler;
import java.io.IOException;
import java.util.List;
import java.util.Arrays;
import java.util.Map;
import java.util.LinkedHashMap;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
public class Bookshelf implements HttpFunction {
private static final Gson gson = new Gson();
@Override
public void service(HttpRequest request, HttpResponse response) throws Exception {
BufferedWriter writer = response.getWriter();
// Get the request body as a JSON object.
JsonObject requestJson = new Gson().fromJson(request.getReader(), JsonObject.class);
JsonArray calls_array = requestJson.getAsJsonArray("calls");
JsonArray calls = (JsonArray) calls_array.get(0);
String context = calls.get(0).toString().replace("\"", "");
//Invoke Gemini model
String raw_result = callGemini(context);
raw_result = raw_result.replace("\n","");
String trimmed = raw_result.trim();
List<String> result_list = Arrays.asList(trimmed);
Map<String, List<String>> stringMap = new LinkedHashMap<>();
stringMap.put("replies", result_list);
// Serialization
String return_value = gson.toJson(stringMap);
writer.write(return_value);
}
public String callGemini(String context) throws IOException{
String res = "";
try (VertexAI vertexAi = new VertexAI("REPLACE_WITH_YOUR_PROJECT_ID", "us-central1"); ) {
GenerationConfig generationConfig =
GenerationConfig.newBuilder()
.setMaxOutputTokens(2048)
.setTemperature(0.4F)
.setTopK(32)
.setTopP(1)
.build();
GenerativeModel model = new GenerativeModel("gemini-pro", generationConfig, vertexAi);
GenerateContentResponse response = model.generateContent(context);
res = ResponseHandler.getText(response);
}catch(Exception e){
System.out.println(e);
}
return res;
}
}
此类期望输入采用以下 JSON 结构:
{ "calls": [["YOUR_PROMPT_HERE"]] }
它会返回如下所示的响应:
(Json) Map<String, List<String>> {"replies": ["response"]}
在 Cloud Shell 编辑器的左侧窗格中,试用 Gemini Chat 选项来解释代码。或者,您也可以选择所有代码,然后点击所选代码左上角的黄色灯泡,然后选择“解释此代码”选项。
9. 部署 Cloud Functions 函数
现在,Cloud Functions 函数已准备就绪,我们来问问 Gemini 如何部署它。在 Cloud Code 编辑器中,前往 Gemini 聊天对话,然后输入以下内容:
How to deploy this Cloud Function with a gcloud command?
我收到的回复如下:
现在,我想进一步探究一下。于是,我继续让 Gemini 告诉我完整的 gcloud functions deploy 命令。响应如下所示:
我现在无法确定您是否会收到相同的回复,但我发现它还会提供一些额外的详细信息,这让我感到很惊讶,如下图所示:
请求正文格式:
和
响应格式:
现在,我们继续运行 Gemini 提供的 gcloud 命令,部署该函数。为此,我们需要打开 Cloud Shell 终端。您可以在 https://console.cloud.google.com 的新标签页中将其打开,并确保选择了正确的项目。点击控制台右上角的“激活 Cloud Shell”图标,打开 Cloud Shell 终端,然后使用以下命令确保您位于正确的项目文件夹中:
cd duetai-gemini-calling
然后运行以下命令:
gcloud functions deploy bookshelf --runtime java17 --trigger-http --entry-point cloudcode.bookshelf.Bookshelf --allow-unauthenticated
系统会询问您“是否允许对新函数 [bookshelf] 进行未经身份验证的调用?”说“y”并按 Enter 键。然后,系统会询问您几个问题(如果适用),并部署您的无服务器 Cloud Functions 函数,部署后的网址为:https://us-central1-*******.cloudfunctions.net/bookshelf。
现在,我们来调用已部署的 Cloud Functions 函数并对其进行测试!
注意:如果您不小心跳过了“允许未经身份验证的调用”问题或选择了“否”,则将无法访问 Cloud Functions 函数的结果,并且在未授予其他 IAM 设置的情况下,会看到“权限错误”。注意这一点
10. 调用已部署的 Cloud Functions 函数
让我们来问问 Gemini 吧?我输入了提示
How to call the deployed cloud function?
我得到的结果如下:(你不一定看到完全相同的回答,请随意尝试不同的提示,看看回答的差异)。
围绕调用已部署函数的其他方式、使用 gcloud 命令调用等方式,围绕特定问题来深入探究聊天。我提交了以下提示:
how to call the deployed cloud function using gcloud
我收到了以下回复:
您可以使用终端中的此响应(“gcloud functions call”命令),并进行调整以使其适用于我们的场景(或者,尝试在提示本身中传递参数,看看您是否能够在响应中获取详细的 gcloud functions call):
gcloud functions call bookshelf --region=us-central1 --gen2 --data '{"calls":[["Hello! This is my test prompt."]]}'
我的结果如下:
11. 清理
您可以点击 Cloud Functions 的详情页面中的“删除”按钮,删除之前创建的 Cloud Functions。
12. 恭喜
您已成功构建、部署并测试了一个 Java Cloud Functions 函数,该函数可使用 Gemini 调用 Gemini 1.0 Pro!此应用可接受与图书推荐(包含图书摘要和主题)相关的输入提示。