使用 Lakehouse 和 AI 根据客户反馈预测需求

本教程介绍了如何使用 Lakehouse 中的结构化销售数据来分析非结构化自由文本客户反馈,以评估新产品发布情况。

假设您是 Froyo 的数据科学家,Froyo 是一家虚构的冷冻酸奶公司,计划推出一种名为“午夜漩涡”的新口味。在确定要进行全球发布之前,企业需要回答以下三个问题:

  • 新口味涉及哪些过敏原和客户反馈问题?
  • 在未来 12 个月内,该公司应预计现有口味的需求量是多少?
  • 客户情绪和重复购买意向与预测需求相比如何?

本教程面向熟悉 BigQuery 和基本 SQL 的数据分析师、数据科学家和数据工程师。您将使用 Cloud Shell 和 Google Cloud 控制台完成每个步骤。

目标

  • 在 Lakehouse 运行时目录中创建一个目录,然后创建 Apache Iceberg 表,用于在 Lakehouse 中存储产品、销售和客户反馈数据。
  • 使用 BigQuery AI 函数 AI.CLASSIFY 和 AI.IF 从存储在 Iceberg 表中的非结构化文本中提取情感、主题和购买意向。
  • 使用 BigQuery Studio 中的对话式分析功能,通过 AI.FORECAST 预测 12 个月内的需求,并将预测结果与产品和反馈洞见相结合。

费用

在本文档中,您将使用 Google Cloud的以下收费组件:

如需根据您的预计使用量来估算费用,请使用价格计算器。

新 Google Cloud 用户可能有资格申请免费试用。

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。

准备工作

  1. 在 Google Cloud 控制台的项目选择器页面上,选择或创建 Google Cloud 项目。

    转到“项目选择器”

  2. 验证 您的 Google Cloud 项目是否已启用结算功能。

所需的角色

如需获得完成本教程中的任务所需的权限,请让您的管理员为您授予项目的以下 IAM 角色:

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

准备环境

激活 Cloud Shell,为资源设置环境变量,并启用所需的 API:

  1. 在 Google Cloud 控制台中,激活 Cloud Shell。如果系统提示您授权,请点击授权。

    激活 Cloud Shell

  2. 在 Cloud Shell 中,为您的项目 ID、区域、存储桶名称、目录 ID 和命名空间设置环境变量,以便您在本教程中重复使用这些变量:

    export PROJECT_ID=$(gcloud config get-value project)
    export REGION="us-central1"
    export BUCKET_NAME="${PROJECT_ID}-froyo-lakehouse"
    export CATALOG_ID="froyo_catalog"
    export NAMESPACE_ID="froyo_lakehouse"
  3. 为项目启用所需的 API:

    • BigQuery API (bigquery.googleapis.com)
    • BigLake API (biglake.googleapis.com),用于访问 Lakehouse 运行时目录
    • Cloud Storage API (storage.googleapis.com)
    • Agent Platform API (aiplatform.googleapis.com)
    • Conversational Analytics API (geminidataanalytics.googleapis.com)
    • Gemini for Google Cloud API (cloudaicompanion.googleapis.com)

    在 Cloud Shell 中启用以下 API:

    gcloud services enable \
        bigquery.googleapis.com \
        biglake.googleapis.com \
        storage.googleapis.com \
        aiplatform.googleapis.com \
        geminidataanalytics.googleapis.com \
        cloudaicompanion.googleapis.com \
        --project=${PROJECT_ID}

创建 Cloud Storage 存储桶

在 Cloud Shell 中,创建一个 Cloud Storage 存储桶来存储 Iceberg 表文件:

gcloud storage buckets create gs://${BUCKET_NAME} \
    --project=${PROJECT_ID} \
    --location=${REGION}

在 Lakehouse 运行时目录中创建目录

使用 Iceberg REST 目录端点,以凭据自动售卖模式创建目录,以便该目录使用其自动预配的服务账号来访问您的存储桶,而您无需创建 BigQuery 连接:

  1. 在 Cloud Shell 中,以凭据贩售模式创建多存储桶目录:

    gcloud biglake iceberg catalogs create ${CATALOG_ID} \
        --project=${PROJECT_ID} \
        --catalog-type=biglake \
        --default-location=gs://${BUCKET_NAME} \
        --primary-location=${REGION} \
        --credential-mode=vended-credentials
  2. 向目录的自动预配服务账号授予您存储桶的 Storage Object User (roles/storage.objectUser) 角色:

    CATALOG_SA=$(gcloud biglake iceberg catalogs describe ${CATALOG_ID} \
        --project=${PROJECT_ID} \
        --format='value(biglake-service-account)')
    
    gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
        --member="serviceAccount:${CATALOG_SA}" \
        --role="roles/storage.objectUser"
  3. 在目录中为表创建命名空间:

    gcloud biglake iceberg namespaces create ${NAMESPACE_ID} \
        --project=${PROJECT_ID} \
        --catalog=${CATALOG_ID}

创建 Iceberg 表

在目录中创建三个 Iceberg 表,并使用示例数据加载这些表:

  • products:存储产品列表,包括新的午夜漩涡口味和基于该口味的现有口味。
  • sales_history:按区域存储现有口味 24 个月的每月单位销量。
  • customer_feedback:存储反馈记录。每行都将结构化元数据(feedback_id、submitted_date、flavor_name 和 channel)与存储非结构化自由文本的 comment STRING 列配对。

在 BigQuery 中,您可以使用四部分 P.C.N.T. 命名结构(Project.Catalog.Namespace.Table)引用 Lakehouse 表。

  1. 在 Cloud Shell 中,创建 products、sales_history 和 customer_feedback 表,并使用示例数据填充这些表。脚本按顺序运行每条语句,大约需要一分钟才能完成:

    bq query --project_id=${PROJECT_ID} --location=${REGION} --use_legacy_sql=false << EOF
    -- Product list.
    CREATE TABLE \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.products\` (
      product_name STRING,
      base_flavor STRING,
      status STRING,
      allergens STRING,
      target_regions STRING);
    
    INSERT INTO \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.products\` VALUES
      ('Midnight Swirl', 'Classic Chocolate', 'Planned launch',
       'Milk, Soy', 'North America, Europe'),
      ('Classic Chocolate', 'Classic Chocolate', 'In market',
       'Milk, Soy', 'North America, Europe, Asia Pacific'),
      ('Vanilla Bean', 'Vanilla Bean', 'In market',
       'Milk', 'North America, Europe, Asia Pacific'),
      ('Strawberry Swirl', 'Strawberry Swirl', 'In market',
       'Milk', 'North America, Europe, Asia Pacific');
    
    -- Monthly sales history for the flavors that are in market.
    CREATE TABLE \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.sales_history\` (
      sale_month DATE,
      flavor_name STRING,
      region STRING,
      units_sold INT64);
    
    INSERT INTO \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.sales_history\`
    WITH
      months AS (
        SELECT sale_month
        FROM UNNEST(GENERATE_DATE_ARRAY(
          '2024-01-01', '2025-12-01', INTERVAL 1 MONTH)) AS sale_month
      ),
      flavors AS (
        SELECT * FROM UNNEST([
          STRUCT('Classic Chocolate' AS flavor_name, 1200 AS base_units),
          ('Vanilla Bean', 1000),
          ('Strawberry Swirl', 800)])
      ),
      regions AS (
        SELECT * FROM UNNEST([
          STRUCT('North America' AS region, 1.0 AS region_factor),
          ('Europe', 0.8),
          ('Asia Pacific', 0.6)])
      )
    SELECT
      m.sale_month,
      f.flavor_name,
      r.region,
      CAST(
        f.base_units * r.region_factor
        -- Two percent month-over-month growth.
        * (1 + 0.02 * DATE_DIFF(m.sale_month, DATE '2024-01-01', MONTH))
        -- Seasonal peak in July.
        * (1 + 0.25 * SIN(2 * ACOS(-1)
            * (EXTRACT(MONTH FROM m.sale_month) - 4) / 12))
        AS INT64) AS units_sold
    FROM months AS m
    CROSS JOIN flavors AS f
    CROSS JOIN regions AS r;
    
    -- Customer feedback records with unstructured text in the comment column.
    CREATE TABLE \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.customer_feedback\` (
      feedback_id INT64,
      submitted_date DATE,
      flavor_name STRING,
      channel STRING,
      comment STRING);
    
    INSERT INTO \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.customer_feedback\` VALUES
      (1, '2025-11-03', 'Midnight Swirl', 'Taste panel',
       "Rich, deep chocolate with a hint of salt. Honestly better than the classic chocolate. I'd order this every week."),
      (2, '2025-11-03', 'Midnight Swirl', 'Taste panel',
       "Loved the dark chocolate swirl, but it was a bit too bitter for my kids."),
      (3, '2025-11-04', 'Midnight Swirl', 'Taste panel',
       "Best froyo I've tried this year. Smooth texture and not too sweet."),
      (4, '2025-11-04', 'Midnight Swirl', 'Taste panel',
       "Great taste. I'd definitely buy it again if it's priced like the other flavors."),
      (5, '2025-11-05', 'Midnight Swirl', 'Taste panel',
       "Does this contain soy? I have a soy allergy so I skipped it. Please label it clearly."),
      (6, '2025-11-05', 'Midnight Swirl', 'Taste panel',
       "Tastes like a premium dessert. The sea salt really makes it."),
      (7, '2025-06-12', 'Classic Chocolate', 'App review',
       "Solid chocolate flavor, my go-to order."),
      (8, '2025-07-02', 'Classic Chocolate', 'App review',
       "It's fine, but a little too sweet compared to other brands."),
      (9, '2025-08-19', 'Classic Chocolate', 'Support email',
       "Consistently good. I wish the cups were bigger for the price."),
      (10, '2025-09-07', 'Classic Chocolate', 'Support email',
       "My chocolate froyo was icy this time and the texture was off."),
      (11, '2025-10-21', 'Classic Chocolate', 'App review',
       "Classic for a reason. Always creamy. I'll keep ordering it."),
      (12, '2025-11-05', 'Classic Chocolate', 'Taste panel',
       "Good but forgettable next to the new dark chocolate sample."),
      (13, '2025-05-14', 'Vanilla Bean', 'App review',
       "Real vanilla flavor, you can see the specks. Love it."),
      (14, '2025-06-30', 'Vanilla Bean', 'App review',
       "Pretty plain. I only order it as a base for toppings."),
      (15, '2025-08-02', 'Vanilla Bean', 'App review',
       "Creamy and simple, perfect for my toddler."),
      (16, '2025-09-15', 'Vanilla Bean', 'Support email',
       "The store was out of vanilla bean two weekends in a row."),
      (17, '2025-06-08', 'Strawberry Swirl', 'App review',
       "Tastes like fresh strawberries. Great in the summer."),
      (18, '2025-07-26', 'Strawberry Swirl', 'App review',
       "Too artificial tasting. Not a fan."),
      (19, '2025-08-11', 'Strawberry Swirl', 'Support email',
       "My favorite flavor. Please never discontinue it."),
      (20, '2025-10-03', 'Strawberry Swirl', 'App review',
       "The price went up, but it's still worth it.");
    EOF
  2. 验证您的目录是否列出了 products、sales_history 和 customer_feedback 表:

    gcloud biglake iceberg tables list \
        --project=${PROJECT_ID} \
        --catalog=${CATALOG_ID} \
        --namespace=${NAMESPACE_ID}

使用 AI 函数分析反馈

customer_feedback 表中的 comment 列包含无法直接汇总或联接的非结构化文本。使用 BigQuery AI 函数从每条评论中提取结构化值,并创建一个新的 feedback_insights 表:

  • AI.CLASSIFY 会根据您定义的类别列表,为每条评论分配一种情感(positive、neutral 或 negative)和一个主要主题(例如 taste、texture、price 或 allergens)。
  • AI.IF 会针对每条评论评估自然语言条件,并返回 BOOL 值。在这种情况下,该值表示客户是否打算再次购买相应商品。

这些受管理的 AI 函数使用您的用户凭据来调用 Gemini,因此您无需创建远程模型或连接。

  1. 在 Cloud Shell 中,创建并填充 feedback_insights 表:

    bq query --project_id=${PROJECT_ID} --location=${REGION} --use_legacy_sql=false << EOF
    CREATE TABLE \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.feedback_insights\` (
      feedback_id INT64,
      flavor_name STRING,
      channel STRING,
      comment STRING,
      sentiment STRING,
      topic STRING,
      would_buy_again BOOL);
    
    INSERT INTO \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.feedback_insights\`
    SELECT
      feedback_id,
      flavor_name,
      channel,
      comment,
      AI.CLASSIFY(
        comment,
        categories => ['positive', 'neutral', 'negative']) AS sentiment,
      AI.CLASSIFY(
        comment,
        categories => ['taste', 'texture', 'price', 'allergens',
                       'availability', 'other']) AS topic,
      AI.IF(
        ('This customer says or implies that they would buy the product ',
         'again. Comment: ', comment)) AS would_buy_again
    FROM \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.customer_feedback\`;
    EOF
  2. 总结每种风味的反馈指标:

    bq query --project_id=${PROJECT_ID} --location=${REGION} --use_legacy_sql=false << EOF
    SELECT
      flavor_name,
      COUNT(*) AS comments,
      COUNTIF(sentiment = 'positive') AS positive,
      COUNTIF(would_buy_again) AS would_buy_again,
      ROUND(COUNTIF(sentiment = 'positive') / COUNT(*), 2) AS positive_share
    FROM \`${PROJECT_ID}.${CATALOG_ID}.${NAMESPACE_ID}.feedback_insights\`
    GROUP BY flavor_name
    ORDER BY positive_share DESC;
    EOF

    输出类似于以下内容:

    +-------------------+----------+----------+-----------------+----------------+
    |    flavor_name    | comments | positive | would_buy_again | positive_share |
    +-------------------+----------+----------+-----------------+----------------+
    | Strawberry Swirl  |        4 |        3 |               2 |           0.75 |
    | Midnight Swirl    |        6 |        4 |               3 |           0.67 |
    | Vanilla Bean      |        4 |        2 |               2 |            0.5 |
    | Classic Chocolate |        6 |        2 |               3 |           0.33 |
    +-------------------+----------+----------+-----------------+----------------+
    

利用对话式分析预测需求

借助 BigQuery Studio 中的对话式分析,您可以使用自然语言分析 Iceberg 表。它从 Lakehouse 运行时目录中检索表架构,生成并运行 BigQuery SQL(包括 AI.FORECAST 等 AI 函数),并直接在对话中返回表格和图表。

围绕表格数据开始对话

  1. 在 Google Cloud 控制台中,前往 BigQuery 代理页面。

    前往“代理”

  2. 在 BigQuery Studio 编辑器窗格中,点击代理标签页以打开对话窗格,然后点击新对话。

  3. 在与数据对话窗格中,点击知识来源标签页。 在搜索来源字段中,使用以下值搜索并选择您的三个 Iceberg 表。将 PROJECT_ID 替换为您的项目 ID:

    • PROJECT_ID.froyo_catalog.froyo_lakehouse.products
    • PROJECT_ID.froyo_catalog.froyo_lakehouse.feedback_insights
    • PROJECT_ID.froyo_catalog.froyo_lakehouse.sales_history
  4. 点击 Chat。

通过提问来预测需求并评估发布情况

提出一系列自然语言问题,以解答有关 Midnight Swirl 发布的三项业务问题:

  1. 在提问字段中,输入以下提示以查看 Midnight Swirl 的产品详情和客户反馈,然后点击 send_spark 发送:

    Using products and feedback_insights, what are Midnight Swirl's allergens,
    base flavor, positive sentiment share, would_buy_again count, and customer
    concerns from negative or neutral comments?
    

    代理会查询这两个表,并报告 Midnight Swirl 基于 Classic Chocolate,且包含牛奶和大豆。报告还显示,正面情绪占比为 0.67,六位客户中有三位打算再次购买该产品,并重点指出了对大豆过敏原标签和黑巧克力苦味的担忧。

  2. 输入以下提示,预测现有口味的 12 个月需求,然后点击 send_spark 发送:

    Forecast monthly sales by flavor for the next 12 months, plotting only the
    trend lines without confidence intervals.
    

    对话式分析会运行包含 AI.FORECAST 的查询,该查询使用 BigQuery ML 中的内置 TimesFM 模型,无需训练单独的模型,并显示每种口味的 12 个月预测图表。

  3. 输入以下提示,将需求预测与客户反馈相结合,获取发布建议,然后点击 send_spark 发送:

    Join the total 12-month forecasted units_sold for each flavor with its
    positive sentiment share, would_buy_again count, and top complaint topics
    from feedback_insights, and recommend next steps for the Midnight Swirl
    launch.
    
  4. 可选:如需检查生成的 SQL 查询或查看代理的推理步骤,请展开显示推理过程。

在此处,您可以在对话中提出后续问题(例如按区域细分预测),也可以点击详细信息窗格中的创建代理,以保存可重复使用的数据代理并与团队分享。

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留该项目但删除各个资源。

删除项目

  1. 在 Google Cloud 控制台中,前往管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除。
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除各个资源

如果您想保留项目,请删除您创建的各个资源。

  1. 如需删除对话,请在 BigQuery Studio 编辑器窗格中选择代理标签页。在左侧导航面板(可能需要展开)中,找到最近的对话下的相应对话,然后依次点击 more_vert 查看操作 > 删除。

  2. 在 Cloud Shell 中,删除这四个表、命名空间、目录和 Cloud Storage 存储桶:

    for TABLE in products sales_history customer_feedback feedback_insights; do
      gcloud biglake iceberg tables delete ${TABLE} \
          --project=${PROJECT_ID} \
          --catalog=${CATALOG_ID} \
          --namespace=${NAMESPACE_ID} \
          --quiet
    done
    
    gcloud biglake iceberg namespaces delete ${NAMESPACE_ID} \
        --project=${PROJECT_ID} \
        --catalog=${CATALOG_ID} \
        --quiet
    
    gcloud biglake iceberg catalogs delete ${CATALOG_ID} \
        --project=${PROJECT_ID} \
        --quiet
    
    gcloud storage rm -r gs://${BUCKET_NAME}

后续步骤

如需探索更多使用 Lakehouse 数据的方法,请参阅以下资源: