利用浏览器 AI 功能增强电子商务结账体验

1. 简介

现代 Web 开发为 AI 用途提供了基于云的往返之外的替代方案。Chrome 已开始提供内置 AI API,让您可以使用各种模型,从用于翻译的小型专家模型到功能强大的精简完整语言模型,所有这些模型都可以在用户设备上私密运行,而不会产生任何令牌费用。

与此同时,AI 智能体为用户提供了一种与 Web 应用互动的新方式,而通过 WebMCP 使您的 Web 体验能够支持智能体,有助于减少令牌使用量,同时提供比需要屏幕截图和 DOM 抓取的传统促动更准确的结果。

在此 Codelab 中,您将使用 Mont-Royal Plein Air,这是一个户外装备电子商务网站。您将学习如何构建和集成内置 AI API 和 WebMCP,以实现以下目的:

  1. 根据浏览记录推断客户意图,并使用设备端模型动态构建推荐。
  2. 公开工具代理和浏览器助理可用于浏览网站、检查商品和推荐内容以及管理用户购物车的工具。
  3. (可选)使用小型设备端专家模型实时翻译您的网站。
  4. (可选)将基本网站搜索转换为使用与外部代理相同的工具提供支持的站内代理。

学习内容

  • 如何使用 Prompt API 并强制其输出结构化 JSON。
  • 如何注册 WebMCP 工具,以及有关工具组合和粒度的思考策略。
  • (可选)如何使用 Translator API 通过设备端专家模型快速翻译内容。
  • (可选)如何自行调用 WebMCP 工具。

前提条件

  • 个人 Google 账号和 Google Cloud。
  • Google Chrome 版本 150 及更高版本,或 Canary 渠道、开发者渠道或 Beta 渠道,且已启用 #enable-webmcp-testing 和 #devtools-webmcp-support 标志。
  • 熟悉 HTML、CSS、JavaScript 和 TypeScript。
  • 熟悉 JSON 架构。

2. 项目设置

Google 账户

如果您还没有个人 Google 账号,则必须先创建一个 Google 账号。

登录 Google Cloud 控制台。

使用个人 Google 账号登录 Google Cloud 控制台。

启用结算功能

如需设置个人结算账号,请在 Cloud 控制台中启用结算功能。

创建项目(可选)

如果您没有要用于此实验的当前项目,请在此处创建一个新项目。

打开 Cloud Shell 编辑器

  1. 点击此链接可直接前往 Cloud Shell 编辑器。
  2. 如果您在今天的任何时间点收到授权提示,请点击授权以继续操作。点击以授权 Cloud Shell
  3. 如果终端未显示在屏幕底部,请打开它:
    • 点击查看。
    • 点击终端。在 Cloud Shell 编辑器中打开新终端

设置项目

  1. 在终端中,使用以下命令设置项目:
    gcloud config set project [PROJECT_ID]
    
    示例:gcloud config set project lab-project-id-example 在 Cloud Shell 编辑器终端中设置项目 ID
  2. 您会看到以下消息:Updated property [core/project].

本地和工作区设置

  1. 在终端(或 Cloud Shell 终端)中,克隆项目代码库并进入项目目录:
    git clone https://github.com/GoogleChromeLabs/codelabs mont-royal
    cd mont-royal
    
  2. 导航到 ecommerce-ai-enhancements/starter 文件夹:
    cd ecommerce-ai-enhancements/starter
    
  3. 使用 pnpm 安装依赖项:
    pnpm install
    
  4. 启动本地开发服务器:
    pnpm dev
    
    在浏览器中打开 http://localhost:5173 以查看 Mont-Royal Plein Air 商店。

(可选)安装 WebMCP 检查器扩展程序

为了更轻松地使用 WebMCP,您可以安装 WebMCP Model Context Tool Inspector。安装完成后,您需要获取 Gemini API 密钥才能使用该插件:

  • 前往 AI Studio。
  • 点击创建 API 密钥按钮,以生成与您在本步骤中之前设置的项目相关联的新 Gemini API 密钥。
  • 打开 WebMCP 模型上下文工具检查器(在侧边栏中打开),然后点击 Update Gemini API Key(更新 Gemini API 密钥)按钮。
  • 将 API 密钥粘贴到弹出式窗口中,然后保存。

此扩展程序具有可使用 WebMCP 的基本代理,让您可以通过该代理试用网站,而不是通过 DevTools 直接调用 WebMCP 工具。

3. 网页上的内置 AI 模型

什么是 Prompt API?

借助 Prompt API,Web 应用可以直接访问完全在用户设备上运行的小型多模态语言模型。由于推理是在本地执行的:

  • 零云延迟:无需网络往返或后端服务器排队。
  • 默认情况下为私密:提示以及传递给提示的任何上下文绝不会离开设备
  • 零成本:推理在客户端硬件上运行,无需支付令牌费用或云托管费用。
  • 支持离线使用:即使没有有效的互联网连接,也能可靠地运行。

在 Chrome 中,可通过全局 LanguageModel 接口访问 Prompt API。

// 1. Set your expected inputs and outputs. This is multimodal, so you can
// include other types here, too
const languages = {
   expectedInputs: [{type: "text", languages: ["en"]}],
   expectedOutputs: [{type: "text", languages: ["en"]}],
}

// 2. Check availability. Always pass in your expected inputs and outputs,
// as some models may only support some inputs and outputs
const availability = await LanguageModel.availability(languages);
// 'available', 'downloading', or 'unavailable'

// 3. Create an isolated session with system prompt and language constraints
const session = await LanguageModel.create({
  ...languages,
  // Optional: add initial context to your session, like a system prompt
  initialPrompts: [
   {
      role: 'system',
      content: 'You are an expert outdoors sales assistant...'
   }
  ]
});

// 4. Define your expected output as JSON Schema
const schema = {
   //...
}

// 5. Prompt the model (supports plain text or structured models)
const result = await session.prompt('Recommend a 3-season tent footprint.', {
  responseConstraint: schema,
});

练习 1:基于历程的推荐

电子商务网站通常会提供“经常一起购买”商品的轮播界面或追加销售。对于户外网站等网站,根据浏览记录生成这些建议可能更有用。

为此,您将构建一个三阶段推荐流水线:

  1. 旅程分析器:使用 Prompt API,您将分析客户的按时间顺序排列的浏览时间轴,尝试推断用户的真实冒险意向。
  2. 商品查询:您将从该 intent 向虚假商品后端发出 API 请求,以获取相关商品。
  3. 商品推荐:您将这些商品传递回 Product API 以对其进行排名,并返回与用户正在查看的内容最契合的 5 个配套商品。

任务 1.1:Prompt API 初始化

您不必在每次想使用 Prompt API 时都单独对其进行初始化,而是将调用集中到一个文件中,通过系统提示记忆化创建过程,以缩短对同一代理进行多次调用的启动时间。

前往文件 src/ai/prompt-api.ts,并检查第 27-58 行中的 getPromptSession():

export async function getPromptSession(
  systemPrompt: string = '',
  options: { signal?: AbortSignal } = {}
): Promise<LanguageModel>
  1. 重复使用缓存的基础会话。第 21 行上已有一个缓存映射 baseSessions,以系统提示为键。重复使用基本模型而不是创建新模型可缩短启动时间,并继承核心设置,而不会让每个会话相互干扰。clone()返回克隆 - 调用方可以自由地与其互动,包括 destroy() 它,而无需舍弃缓存的基础。
  2. 检测 API 功能。如果 API 存在,则进行功能检测;如果 API 不存在,则抛出错误。
  3. 声明模态和语言。内置模型可以支持多模态输入。您需要声明所需模式和要支持的语言。您传递给 LLM 的所有内容均为英文。
  4. 在创建任何内容之前,请先检查是否有空闲时间。即使存在 API,也可能没有满足您要求的模型。检查是否存在。如果不是,则抛出错误。如果只是尚未下载,则报告下载进度。
  5. 创建会话。设置用于创建新的 Prompt API 会话的选项,以及系统提示(如果包含)

任务 1.2:实现用户体验分析器

前往 src/ai/journey-profiler.ts 文件,并检查第 42-100 行的目标函数:

export async function inferJourneyProfile(
  context: ProfilerInputContext,
  catalogMap: ReadonlyMap<string, Product>
): Promise<JourneyProfile>
  1. 使用任务 1 中的函数生成要使用的新提示会话。
  2. 使用提供的提示文本提示模型,并使用 journeyProfileSchema 作为结构化返回的 JSON 架构。
  3. 解析提示输出并去重目标类别。
  4. 将硬编码的字符串返回值替换为提示的实际结果。
  5. 在会话结束后将其销毁,以释放内存。

任务 1.3:实现协同效应重排名器

前往 src/ai/synergy-reranker.ts 文件,并检查第 30-71 行的目标函数:

export async function rankComplementaryGear(
  profile: JourneyProfile,
  candidates: readonly Product[],
  currentProduct?: Product | null,
  cartProducts?: readonly Product[],
  limit: number = 5
): Promise<readonly RecommendedItem[]>
  1. 使用任务 1 中的函数生成要使用的新提示会话。
  2. 使用提供的提示文本和生成的架构来提示模型。
  3. 解析提示输出。
  4. 使用 assembleRecommendations 返回组装的推荐列表。
  5. 在会话结束后将其销毁,以释放内存。

验证推荐流水线

  1. 打开 Chrome,然后前往 http://localhost:5173/catalog/tent-mont-tremblant-3p。
  2. 向下滚动到为您推荐部分。
  3. 观察 5 张配套卡片如何更新,以反映智能套装配对(例如地面足迹、睡垫和轻型地钉),而不是随机产品。
  4. 打开 Chrome 开发者工具控制台,检查两个推理步骤的实时执行时长。

(可选)任务 1.4:探索更多内置 AI 模型

还有其他几种内置 AI 模型可供使用,包括 Translator API 专家模型。此代码库中的组件已连接到可监听当前语言变化并请求翻译的服务,您的任务是将所有内容连接起来。

找到相应文件:src/ai/translator.ts

检查第 29-45 行的目标函数 createTranslator:

export async function createTranslator(
  targetLang: SupportedLanguage
): Promise<Translator | null>
  1. 检查是否支持 Translator API,并确保目标语言不是英语(内容默认使用英语)。
  2. 构建一个语言对(从英语到目标语言),然后检查该语言对是否可用。
  3. 为翻译器创建选项,并附加一个用于显示模型下载进度的监控器。
  4. 返回已初始化的翻译器。

接下来,检查第 50-58 行的目标函数 translate:

export async function translate(
  text: string,
  targetLang: SupportedLanguage
): Promise<string>
  1. 通过传入 targetLang 来检查是否有可用的翻译器 getCachedTranslator。如果没有,则返回清理后的文本。
  2. 如果有,请使用翻译器翻译清理后的文本。

找到相应文件:src/ai/translator.ts

检查 第 204 行的目标函数 render:

export async function translate(
  text: string,
  targetLang: SupportedLanguage
): Promise<string>
  1. 启用语言选择器组件

4. 使用 WebMCP 改善代理体验

什么是 WebMCP?

随着用户开始使用自主代理和共同浏览代理,网站必须进行调整。智能体通常依赖于促动(截取屏幕截图并抓取 DOM 和无障碍树)来确定如何与网站互动。这种方法速度慢、不稳定且容易出错,还会浪费用户令牌。

WebMCP 可让开发者直接向智能体提供工具,以便智能体与当前网页互动,从而帮助解决此问题。支持这些工具的代理可以使用这些工具,就像使用编码框架中的工具一样,并根据您的指示提供清晰、结构化的输入和返回值。

// Register a tool directly on document.modelContext
document.modelContext.registerTool({
  name: 'get_product_specs',
  title: 'Get Product Specs',
  description: 'Retrieve technical specifications for the active product.',
  inputSchema: {
    type: 'object',
    properties: {
      // ...
    },
  },
  annotations: {
    readOnlyHint: true, // Informs the agent this tool causes no side-effects
  },
  execute: async (input) => {
    return {
      weight: '1.8 kg',
      capacity: '3 persons',
      seasons: '3-season',
    };
  },
});

当代理连接到网页时,它会查询 document.modelContext.getTools()。每个已注册的工具都会公开:

  • name:代理在执行工具时使用的程序化工具标识符(例如 'get_product_specs')。
  • title:检查器和代理界面(例如 'Get Product Specs')使用的人类可读的显示标签。
  • description:纯文本说明,用于告知模型工具的用途以及何时调用该工具。代理会根据此描述来确定是否应调用该工具,因此请务必提供清晰的描述。
  • inputSchema:定义接受的实参的 JSON 架构。
  • annotations:行为提示,例如 { readOnlyHint: true }(适用于不会导致状态副作用的只读工具)。
  • execute:在调用工具时执行的异步函数。

任务 2.1:推荐和产品工具

首先,您需要注册一个工具,以便代理检索您在上一个练习中刚刚构建的推荐。

找到相应文件:src/components/product/ForYouSection.ts

检查第 71-94 行的 registerWebMCPTools():

private registerWebMCPTools(): void
  1. 检查 WebMCP 是否受支持。
  2. 如果已注册,请注册该工具。
  • 选择一个能吸引代理选择该工具的标题和说明 - 这里需要考虑提示工程。
  • 没有输入,因此只需包含一个空对象 JSON 架构。
  • 这是一个只读工具。
  • 在执行函数中,使用 recommendationStore 获取当前的一组推荐,然后返回当前商品 ID 和一组商品推荐。
  • 添加中止信号并捕获该信号,以免抛出错误。这样可确保在组件卸载时正确移除该工具。

找到相应文件:src/views/ProductView.ts

  1. 重复相同的流程来注册 get_current_product 工具,这次返回完整商品以及建议

任务 2.2:购物车抽屉式导航栏工具

找到相应文件:src/components/cart/CartDrawer.ts

检查第 83-114 行中的 registerWebMCPTools():

private registerWebMCPTools(): void
  1. 注册 view_cart 工具:
  • 其 inputSchema 应该是一个包含单个布尔值选项 openDrawer 的对象。
  • 它应该是只读的。
  • 在 execute 函数中读取输入内容,决定是显示还是隐藏购物车,刷新 recommendationStore,然后返回购物车摘要
  1. 注册 manage_cart 工具:
  • 其 inputSchema 应该是一个包含 items 的对象,是一个对象数组,其中包含 productID、add、remove 和 update action 枚举以及整数 quantity,
  • 其 execute 函数应将商品传递给 this.updateCart

任务 2.3:语言切换工具

通过向外部代理公开语言工具,进一步提升 WebMCP 的功能。

找到相应文件:src/components/navigation/LanguagePicker.ts

检查第 61-63 行中的 registerWebMCPTools():

private registerWebMCPTools(): void
  1. 注册 switch_language 工具:
    • inputSchema 应具有一个 language 属性,该属性是 SUPPORTED_LANGUAGE_CODES 的枚举。
    • 其 execute 函数应使用所提供的语言调用 this.switchLanguage。

任务 2.4:存储导航工具

使自主代理能够浏览商店、跳转到商品页面、浏览类别或发起搜索。

找到相应文件:src/components/navigation/SiteHeader.ts

检查第 155-171 行中的 registerWebMCPTools():

private registerWebMCPTools(): void
  1. 检查 WebMCP 是否受支持。
  2. 注册 search 工具:
    • 输入架构采用 query 字符串。
    • 在 execute 中,调用 this.search(input?.query)。
  3. 注册 go_to_catalog 工具:
    • 输入架构接受可选的 activity(ACTIVITIES 的枚举)和 category(PRODUCT_CATEGORIES 的枚举)。
    • 在 execute 中,调用 this.goToCatalog(input?.activity, input?.category)。
  4. 注册 get_categories_and_activities 工具:
    • 不含输入参数的只读工具。
    • 在 execute 中,调用 this.getCategoriesAndActivities()。
  5. 注册 go_to_product 工具:
    • 输入架构需要 productId 字符串。
    • 在 execute 中,调用 this.goToProduct(input?.productId)。

使用 WebMCP 检查器进行验证

  1. 在 Chrome 中重新加载 http://localhost:5173/catalog/tent-mont-tremblant-3p,然后前往某个商品页面。
  2. 依次打开 Chrome 开发者工具 >“应用”>“WebMCP”。
  3. 您应该会看到 WebMCP 工具:get_recommendations、get_current_product、view_cart、manage_cart、switch_language、search、go_to_catalog、get_categories_and_activities 和 go_to_product。
  4. 点击其中一个可查看其详细信息和参数。
  5. 选择一个带参数的工具,或选择多个工具,然后点击运行工具,即可查看其运行效果。
  6. (可选)如果您已安装并设置 WebMCP 模型上下文工具检查器,请将其打开,它会在 Chrome 的侧边栏中打开。使用聊天界面告知智能体执行您认为应该调用某个工具的操作,看看智能体如何响应。

5. (可选)构建站内代理

在网页中调用 WebMCP 工具

WebMCP 工具不仅适用于外部浏览器扩展程序或第三方机器人,您自己的应用也可以直接调用它们。通过查询 document.modelContext.getTools(),您的客户端代码可以动态发现和执行工具。

您可以使用此功能构建站内 AI 购物助理,将搜索栏转变为基于自然语言输入的产品发现工具。您无需编写自定义命令式代码来协调众多分面过滤条件,而是让搜索栏将自然语言查询传递给 Prompt API,由该 API 决定要调用哪些页内 WebMCP 工具。

如需从 JavaScript 发现和调用 WebMCP 工具,请执行以下操作:

// getTools() returns a Promise — it must be awaited.
const tools = await document.modelContext.getTools();
const tool = tools.find(t => t.name === 'list_items');

const json = await document.modelContext.executeTool(tool, args);
const result = JSON.parse(json); // executeTool resolves with a DOMString

任务 3.1:边栏中的分面工具

向 WebMCP 公开每个目录边栏分面,以便代理(以及您的网页内搜索助理)能够以编程方式切换过滤条件。

src/components/facets/ 中的每个分面组件都已在其类上提供了一个辅助方法,并在 src/utils/filter-schemas.ts 中提供了一个导出的输入架构:

  1. 在 src/components/facets/ActivityFacet.ts 中:
    • 使用 activityFilterSchema 注册 activity_filter。
    • 在 execute 中,调用 this.filterActivity(input?.activity, input?.selected)。
  2. 在 src/components/facets/CategoryFacet.ts 中:
    • 使用 categoryFilterSchema 注册 category_filter。
    • 在 execute 中,调用 this.filterCategory(input?.category, input?.selected)。
  3. 在 src/components/facets/ConditionFacet.ts 中:
    • 使用 conditionFilterSchema 注册 condition_filter。
    • 在 execute 中,调用 this.filterCondition(input?.condition, input?.selected)。
  4. 在 src/components/facets/PriceFacet.ts 中:
    • 使用 priceFilterSchema 注册 price_filter。
    • 在 execute 中,调用 this.filterPrice(input?.priceRange || input?.bracket, input?.selected)。
  5. 在 src/components/facets/WeightFacet.ts 中:
    • 使用 weightFilterSchema 注册 weight_filter。
    • 在 execute 中,调用 this.filterWeight(input?.weightRange || input?.bracket, input?.selected)。
  6. 在 src/components/facets/RatingFacet.ts 中:
    • 使用 ratingFilterSchema 注册 rating_filter。
    • 在 execute 中,调用 this.filterRating(input?.minRating || input?.rating, input?.selected)。
  7. 在 src/components/facets/SearchFacet.ts 中:
    • 使用 keywordFilterSchema 注册 keyword_filter。
    • 在 execute 中,调用 this.updateKeyword(input?.keyword)。

请务必检查 WebMCP 支持情况,并按照第 2 步中的说明传递 { signal: this.signal } 和 .catch(() => {})。

任务 3.2:目录视图工具

现在,在 document.modelContext 上注册目录级商品详情、过滤条件重置和语义搜索工具。

找到相应文件:src/views/CatalogView.ts

检查第 125-140 行中的 registerWebMCPTools():

private registerWebMCPTools(): void
  1. 检查是否支持 WebMCP。
  2. 注册 list_items 工具:
    • 它不接受任何输入参数,并且是只读的。
    • 其 execute 函数应调用 this.getItemsSummary()。
  3. 注册 reset_filters 工具:
    • 它不接受任何输入参数。
    • 其 execute 函数应调用 this.resetFilters()。
  4. 注册 semantic_catalog_filter 工具:
    • 使用 semanticCatalogFilterSchema。
    • 其 execute 函数应调用 interpretAndApplySemanticFilter(input.query)。

任务 3.3:编排语义过滤代理

现在,该将所有设备连接起来了。此任务将搜索转变为为 semantic_catalog_filter 工具提供支持的语义流水线:

找到相应文件:src/ai/catalog-semantic-filter.ts

检查第 84-88 行中的 prewarmSemanticFilterSession():

export async function prewarmSemanticFilterSession(): Promise<void>
  1. 检查 warmSession 是否已存在;如果存在,则提前返回。
  2. 使用 getPromptSession() 通过 SEMANTIC_FILTER_SYSTEM_PROMPT 预热 Prompt API 会话,并将其存储在 warmSession 中。封装在 try/catch 代码块中,以便在预热失败时,搜索创建仍能正常回退。

检查第 93-134 行中的 inferSearchFacets():

async function inferSearchFacets(
  searchTerm: string,
  cachedJourney: JourneyProfile | null
): Promise<{ parsed: SemanticFilterResponse; cacheHit: boolean }>
  1. 从 warmSession 中声明预热会话(如果可用),并将 warmSession 重置为 null;或者使用 getPromptSession(SEMANTIC_FILTER_SYSTEM_PROMPT) 创建新会话。
  2. 使用 semanticFilterSchema 作为 responseConstraint,通过 promptText 向模型发出提示。
  3. 解析返回的 JSON,使用 cacheKey 将解析结果存储在持久缓存中,并在 finally 块中销毁会话。

检查第 139-270 行中的 applyFacetsViaWebMCP():

async function applyFacetsViaWebMCP(
  searchTerm: string,
  parsed: SemanticFilterResponse
): Promise<{ appliedFilters: SemanticFilterResult['appliedFilters']; toolsExecuted: string[] }>
  1. 使用 await document.modelContext.getTools() 发现 document.modelContext 上已注册的工具。
  2. 通过调用 await executeFacet('reset_filters', {}) 重置过滤条件,以便从头开始。
  3. 使用 await Promise.all(facetTasks) 同时应用有效分面。

检查第 272-316 行中的 interpretAndApplySemanticFilter():

export async function interpretAndApplySemanticFilter(
  searchTerm: string
): Promise<SemanticFilterResult>
  1. 通过调用 inferSearchFacets(searchTerm, cachedJourney) 推断搜索分面。
  2. 使用 applyFacetsViaWebMCP(searchTerm, parsed) 将推断出的分面应用于目录。

验证页内代理

  1. 导航到 http://localhost:5173/catalog。
  2. 在顶部的搜索栏中,输入:"lightweight 3 season tent for wet hikes"
  3. 观看 Prompt API 解析请求、调用 activity_filter(远足)、category_filter(帐篷)和 condition_filter(潮湿)功能,无需手动点击任何复选框,即可立即将目录缩小到匹配的装备。

6. 学习内容

恭喜!您已将电子商务应用转变为完全由设备端 AI 和标准 Web 平台 API 提供支持的智能代理优先体验。

您构建的内容:

  • Prompt API:使用 LanguageModel 合成用户浏览时间轴,以生成实时 JourneyProfile 上下文,并使用结构化 JSON 架构 (responseConstraint) 构建 rankComplementaryGear 协同引擎。
  • WebMCP 组件工具:通过 document.modelContext 在界面组件(get_recommendations、get_current_product、view_cart、manage_cart)中注册的模块化封装工具。
  • 代理转发架构:使用 Chrome WebMCP 检查器扩展程序测试并验证了工具执行情况。
  • 网站内网页代理:直接在浏览器中编排客户端工具,以便从网站标题搜索栏中进行多方面的自然语言目录搜索。
  • 专用设备端 AI 模型:探索了使用 Translator 的动态设备端翻译。

要点总结:

  1. 零延迟和高隐私保护:Gemini Nano 等设备端模型消除了云 API 密钥、网络延迟和数据渗漏方面的顾虑。
  2. 结构化输出至关重要:将 JSON 架构与 responseConstraint 搭配使用,可将创意文本生成转化为可靠且类型安全的应用逻辑。
  3. WebMCP 为 AI 智能体做好网络准备:直接在 DOM 中注册工具可让内部辅助功能和外部自主浏览器代理都能执行经过验证的确定性操作。
  4. 让模型提出建议,但不执行操作:将模型限制为仅使用您注册的工具的 enum,并自行调度结果,这样可以确保代理行为可审核,并防止其自行创建操作。