aa-conversion-funnel-analysis

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Conversion Funnel Analysis (Adobe Analytics)

转化漏斗分析(Adobe Analytics)

Analyze a multi-step conversion funnel to identify where visitors drop off, which steps have the worst leakage, and what drives visitors to convert or abandon. Uses AA visit-level segment-based reporting to simulate funnel steps.
AA Container Model: AA funnels use hit, visit, and visitor containers — not CJA's event/session/person. Funnel steps are defined as visit-level segments (visitors who completed the step during a visit).
Reporting approach: AA does not have a native sequential fallout API accessible via MCP. This skill approximates fallout by creating or finding visit-level segments for each funnel step, then running the metric for each step segment to compute pass-through rates.

分析多步骤转化漏斗,识别访客流失的环节、泄漏最严重的步骤,以及推动访客转化或放弃的因素。使用基于AA访问层级细分的报告来模拟漏斗步骤。
AA容器模型: AA漏斗使用 hitvisitvisitor 容器——而非CJA的event/session/person。漏斗步骤被定义为访问层级细分(在一次访问中完成该步骤的访客)。
报告方法: AA没有可通过MCP访问的原生序列脱落API。此技能通过为每个漏斗步骤创建或查找访问层级细分,然后运行每个步骤细分的指标来计算通过率,以此近似模拟用户脱落情况。

AA MCP Tools Used

使用的AA MCP工具

  • findReportSuites
    — select report suite
  • setSessionDefaults
    — set session context (reportSuiteId + globalCompanyId)
  • findDimensions
    — discover page/event dimensions for step definition
  • findMetrics
    — find visits or orders as the counting metric
  • searchDimensionItems
    — validate page names or event values
  • findSegments
    — find existing step segments if available
  • upsertSegment
    — create visit-level step segments if not found
  • runReport
    — run visits count for each step segment

  • findReportSuites
    — 选择报表套件
  • setSessionDefaults
    — 设置会话上下文(reportSuiteId + globalCompanyId)
  • findDimensions
    — 发现用于步骤定义的页面/事件维度
  • findMetrics
    — 查找作为计数指标的访问量或订单量
  • searchDimensionItems
    — 验证页面名称或事件值
  • findSegments
    — 查找现有步骤细分(如果存在)
  • upsertSegment
    — 如果未找到则创建访问层级步骤细分
  • runReport
    — 运行每个步骤细分的访问量统计

Phase 0 — Setup

阶段0 — 准备工作

  1. Confirm report suite with
    findReportSuites
    /
    setSessionDefaults
    .
  2. Ask the user about the overall funnel scope (visit or visitor level):
    • Visit-level funnel: all steps happen within a single visit (typical for checkout funnels)
    • Visitor-level funnel: steps can span multiple visits (typical for lifecycle funnels)
findReportSuites(globalCompanyId: "<gcid>", page: 0, limit: 10)
setSessionDefaults(globalCompanyId: "<gcid>", reportSuiteId: "<rsid>")

  1. 通过
    findReportSuites
    /
    setSessionDefaults
    确认报表套件。
  2. 询问用户漏斗的整体范围(访问层级或访客层级):
    • 访问层级漏斗: 所有步骤在单次访问内完成(典型的结账漏斗)
    • 访客层级漏斗: 步骤可跨多次访问完成(典型的生命周期漏斗)
findReportSuites(globalCompanyId: "<gcid>", page: 0, limit: 10)
setSessionDefaults(globalCompanyId: "<gcid>", reportSuiteId: "<rsid>")

Phase 1 — Define the Funnel Steps

阶段1 — 定义漏斗步骤

Ask the user to describe each step of the funnel in plain language. Prompt for 3–8 steps. Example:
  1. Product page viewed
  2. Add to cart
  3. Checkout started
  4. Payment info entered
  5. Order confirmed (purchase)
For each step, ask:
  • "Is this defined by a page view (page name or URL), a custom event, or a combination?"
  • "Should this step be at the hit level (single page/event) or visit level (any visit where this happened)?"

请用户用直白语言描述漏斗的每个步骤。提示用户提供3–8个步骤。示例:
  1. 浏览产品页面
  2. 加入购物车
  3. 开始结账
  4. 输入支付信息
  5. 确认订单(完成购买)
针对每个步骤,询问:
  • “该步骤是通过页面浏览(页面名称或URL)、自定义事件,还是两者结合来定义的?”
  • “该步骤应设置为 hit 层级(单个页面/事件)还是 visit 层级(发生该行为的任意访问)?”

Phase 2 — Discover Components

阶段2 — 发现组件

2.1 Validate page names

2.1 验证页面名称

For page-based steps:
findDimensions(page: 1, limit: 500)   # returns all available dimensions; look for variables/page
searchDimensionItems(
  dimensionId: "variables/page",
  searchOr: "<step page keywords>",   # space-separated keywords OR'd together
  startDate: "<start>",
  endDate: "<end>",
  page: 1,
  limit: 20
)
Common page dimension IDs:
variables/page
,
variables/entrypage
,
variables/exitpage
. Confirm the correct page name value with the user if multiple matches exist.
Note:
searchDimensionItems
uses
searchOr
or
searchAnd
for filtering — not
searchTerm
. If no rows return, try a wider date range — some report suites only have historical data.
对于基于页面的步骤:
findDimensions(page: 1, limit: 500)   # 返回所有可用维度;查找变量/页面
searchDimensionItems(
  dimensionId: "variables/page",
  searchOr: "<step page keywords>",   # 空格分隔的关键词,逻辑或
  startDate: "<start>",
  endDate: "<end>",
  page: 1,
  limit: 20
)
常见页面维度ID:
variables/page
variables/entrypage
variables/exitpage
。如果存在多个匹配项,请与用户确认正确的页面名称值。
注意:
searchDimensionItems
使用
searchOr
searchAnd
进行过滤——而非
searchTerm
。如果没有返回结果,请尝试扩大日期范围——部分报表套件仅包含历史数据。

2.2 Validate events/metrics

2.2 验证事件/指标

For event-based steps (add to cart, checkout, purchase):
findMetrics(expansions: "componentType,categories", page: 0, limit: 200)
对于基于事件的步骤(加入购物车、结账、购买):
findMetrics(expansions: "componentType,categories", page: 0, limit: 200)

Filter results locally by name: visits, orders, pageviews, etc.

按名称本地过滤结果:访问量、订单量、页面浏览量等


---

---

Phase 3 — Find or Create Step Segments

阶段3 — 查找或创建步骤细分

For each funnel step, search for an existing segment:
findSegments(searchTerm: "<step description>")
If an appropriate visit-level segment exists, use it directly.
If not, create a new visit-level segment for each step:
upsertSegment(
  definition: {
    "name": "Visit: Checkout Started",
    "description": "Visits where the visitor reached the checkout page",
    "reportSuiteID": "<rsid>",
    "container": {
      "func": "segment",
      "context": "visits",
      "pred": {
        "func": "streq",
        "val": "/checkout",
        "str": "/checkout",
        "dimension": "variables/page"
      }
    },
    "tags": [{ "name": "funnel" }]
  }
)
Create all step segments before running reports. Record each segment
id
.
Important: Only create new segments with explicit user confirmation. Present the list of segments to be created and ask: "I need to create N visit-level segments to define your funnel steps. Is that OK?"

针对每个漏斗步骤,搜索现有细分:
findSegments(searchTerm: "<step description>")
如果存在合适的访问层级细分,直接使用它。
如果不存在,则为每个步骤创建新的访问层级细分:
upsertSegment(
  definition: {
    "name": "Visit: Checkout Started",
    "description": "Visits where the visitor reached the checkout page",
    "reportSuiteID": "<rsid>",
    "container": {
      "func": "segment",
      "context": "visits",
      "pred": {
        "func": "streq",
        "val": "/checkout",
        "str": "/checkout",
        "dimension": "variables/page"
      }
    },
    "tags": [{ "name": "funnel" }]
  }
)
在运行报告前创建所有步骤细分,并记录每个细分的
id
重要提示: 仅在获得用户明确确认后创建新细分。列出要创建的细分并询问:“我需要创建N个访问层级细分来定义您的漏斗步骤,可以吗?”

Phase 4 — Run Funnel Step Reports

阶段4 — 运行漏斗步骤报告

For each step segment, run the visits count over the analysis period:
runReport(
  dimensionId: "variables/page",    # required by AA runReport
  metricIds: "metrics/visits",      # note: plural field name "metricIds"
  segmentIds: "<step segment id>",  # note: plural field name "segmentIds"
  startDate: "<start>",
  endDate: "<end>",
  limit: 1
)
针对每个步骤细分,在分析周期内运行访问量统计:
runReport(
  dimensionId: "variables/page",    # AA runReport必填项
  metricIds: "metrics/visits",      # 注意:字段名为复数形式"metricIds"
  segmentIds: "<step segment id>",  # 注意:字段名为复数形式"segmentIds"
  startDate: "<start>",
  endDate: "<end>",
  limit: 1
)

summaryData.totals[0] is the total visits for this segment

summaryData.totals[0]是该细分的总访问量


This is 1 call per funnel step. For a 5-step funnel = 5 calls.

Also run total visits (no segment) as the 100% baseline:
runReport( dimensionId: "variables/page", metricIds: "metrics/visits", startDate: "<start>", endDate: "<end>", limit: 1 )

每个漏斗步骤对应一次调用。5步漏斗需要5次调用。

同时运行无细分的总访问量,作为100%基准:
runReport( dimensionId: "variables/page", metricIds: "metrics/visits", startDate: "<start>", endDate: "<end>", limit: 1 )

Use summaryData.totals[0] as the baseline visit count

使用summaryData.totals[0]作为基准访问量


> **AA runReport field names:** Use `metricIds` (not `metricId`) and `segmentIds` (not `segmentId`).
> Use `startDate`/`endDate` (ISO 8601) rather than a `dateRange` object.
> Always read totals from `summaryData.totals[0]`, not from `rows`.

---

> **AA runReport字段名:** 使用`metricIds`(而非`metricId`)和`segmentIds`(而非`segmentId`)。使用`startDate`/`endDate`(ISO 8601格式)而非`dateRange`对象。始终从`summaryData.totals[0]`读取统计值,而非`rows`。

---

Phase 5 — Compute Funnel Metrics

阶段5 — 计算漏斗指标

For each step, compute:
MetricFormula
Step visitsRaw count from
runReport
Step conversion rateStep visits / Total visits × 100
Step-to-step rateStep N visits / Step N-1 visits × 100
Step-to-step drop-offStep N-1 visits - Step N visits
Drop-off rate100 - step-to-step rate
Identify the biggest leakage step (highest absolute drop-off count) and the weakest conversion step (lowest step-to-step rate).

针对每个步骤,计算:
指标公式
步骤访问量
runReport
返回的原始统计值
步骤转化率步骤访问量 / 总访问量 × 100
步骤间通过率第N步访问量 / 第N-1步访问量 × 100
步骤间流失量第N-1步访问量 - 第N步访问量
流失率100 - 步骤间通过率
识别泄漏最严重的步骤(绝对流失量最高)和转化率最低的步骤(步骤间通过率最低)。

Phase 6 — Drill Into the Worst Step

阶段6 — 深入分析最差步骤

For the step with the highest drop-off, run a dimension breakdown to find what differentiates visitors who progressed vs. those who dropped:
runReport(
  dimensionId: "variables/mobiledevicetype",
  metricIds: "metrics/visits",
  segmentIds: "<worst step segment>",
  startDate: "<start>",
  endDate: "<end>",
  limit: 10
)

runReport(
  dimensionId: "variables/mobiledevicetype",
  metricIds: "metrics/visits",
  segmentIds: "<next step segment>",
  startDate: "<start>",
  endDate: "<end>",
  limit: 10
)
Compare the device-type mix between visitors who completed the worst step and those who made it to the next step. Repeat for 1–2 other dimensions (e.g., traffic source, new vs. returning).

针对流失量最高的步骤,运行维度细分,找出完成步骤和流失访客的差异:
runReport(
  dimensionId: "variables/mobiledevicetype",
  metricIds: "metrics/visits",
  segmentIds: "<worst step segment>",
  startDate: "<start>",
  endDate: "<end>",
  limit: 10
)

runReport(
  dimensionId: "variables/mobiledevicetype",
  metricIds: "metrics/visits",
  segmentIds: "<next step segment>",
  startDate: "<start>",
  endDate: "<end>",
  limit: 10
)
比较完成最差步骤的访客和进入下一步的访客的设备类型分布。对1–2个其他维度重复此操作(例如,流量来源、新访客vs回访客)。

Phase 7 — Generate HTML Report

阶段7 — 生成HTML报告

Build the funnel report inline and write to
/tmp/aa_funnel_analysis_report_<YYYY-MM-DD_HHMMSS>.html
.
在线构建漏斗报告并写入
/tmp/aa_funnel_analysis_report_<YYYY-MM-DD_HHMMSS>.html

HTML template

HTML模板

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Funnel Health &mdash; {ORG_NAME} &mdash; {FUNNEL_NAME}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    --bg: #f5f4f1;
    --surface: #ffffff;
    --ink: #1a1a1a;
    --ink-muted: #6b6b6b;
    --border: #e5e2dc;
    --header-bg: #0e0e10;
    --header-warm: #3a1010;
    --accent-red: #c8312f;
    --accent-red-bright: #ff6b68;
    --accent-red-soft: #fdecea;
    --accent-green: #1f7a4d;
    --accent-yellow: #d4a017;
  }
  body { font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
         background: var(--bg); color: var(--ink); line-height: 1.5;
         -webkit-font-smoothing: antialiased; }

  /* === Header === */
  header { background: linear-gradient(120deg, var(--header-bg) 0%, #1a0d0d 55%, var(--header-warm) 100%);
           color: #fff; padding: 56px 56px 44px; position: relative; overflow: hidden; }
  header::after { content: ""; position: absolute; right: -140px; top: -140px;
                  width: 460px; height: 460px;
                  background: radial-gradient(circle, rgba(200,49,47,.35) 0%, transparent 70%);
                  pointer-events: none; }
  .header-inner { max-width: 1080px; margin: 0 auto; position: relative; z-index: 1; }
  .eyebrow { display: inline-flex; align-items: center; gap: 8px;
             padding: 6px 14px; border: 1px solid rgba(255,107,104,.55);
             border-radius: 999px; color: var(--accent-red-bright);
             font-size: 11px; font-weight: 600; letter-spacing: 1.2px;
             text-transform: uppercase; margin-bottom: 24px;
             background: rgba(200,49,47,.10); }
  .eyebrow::before { content: ""; width: 6px; height: 6px;
                     background: var(--accent-red-bright); border-radius: 50%; }
  header h1 { font-family: "Playfair Display", Georgia, serif;
              font-size: 56px; font-weight: 700; letter-spacing: -1.5px;
              line-height: 1.05; margin-bottom: 14px; color: #fff; }
  header .lede { font-size: 16px; max-width: 600px;
                 color: rgba(255,255,255,.80); margin-bottom: 24px;
                 line-height: 1.55; }
  header .meta { display: flex; flex-wrap: wrap; gap: 22px;
                 font-size: 13px; color: rgba(255,255,255,.60); }
  header .meta span { display: inline-flex; align-items: center; gap: 6px; }
  header .meta .icon { opacity: .8; }

  /* === Tabs === */
  nav { background: var(--surface); border-bottom: 1px solid var(--border);
        padding: 0 56px; display: flex; gap: 28px;
        position: sticky; top: 0; z-index: 50; }
  nav a { display: block; padding: 16px 0; font-size: 14px;
          color: var(--ink); text-decoration: none;
          border-bottom: 2px solid transparent;
          transition: border-color .15s ease; }
  nav a:hover { border-bottom-color: var(--accent-red); }

  /* === Container === */
  .container { max-width: 1080px; margin: 0 auto; padding: 36px 56px 60px; }

  /* === Section label === */
  .section-label { font-size: 11px; font-weight: 700;
                   text-transform: uppercase; letter-spacing: 1.4px;
                   color: var(--ink-muted); margin-bottom: 14px;
                   padding-bottom: 10px; border-bottom: 1px solid var(--border); }

  /* === Funnel visualization (horizontal stepped bars) ===
     Bar color is set per step by tier:
       .tier-strong = green  (rate >= 70% pass-through)
       .tier-mid    = yellow (40-69%)
       .tier-weak   = red    (< 40%)
     The biggest-leakage step also gets .step-bar.worst (red, regardless of tier). */
  .funnel-wrap { background: var(--surface); border-radius: 8px;
                 padding: 28px 32px; margin-bottom: 28px;
                 box-shadow: 0 1px 3px rgba(0,0,0,.04); }
  .funnel-step { display: flex; align-items: center; margin-bottom: 10px; }
  .step-label { width: 220px; font-size: 14px; font-weight: 600;
                text-align: right; padding-right: 16px; white-space: nowrap;
                overflow: hidden; text-overflow: ellipsis; color: var(--ink); }
  .step-bar-wrap { flex: 1; position: relative; height: 44px;
                   background: #f4f1eb; border-radius: 4px; }
  .step-bar { height: 44px; border-radius: 4px;
              display: flex; align-items: center; padding: 0 14px;
              color: #fff; font-size: 13px; font-weight: 700;
              transition: width 0.4s ease;
              background: var(--accent-green); }
  .step-bar.tier-strong { background: var(--accent-green); }
  .step-bar.tier-mid    { background: var(--accent-yellow); color: #3a2a05; }
  .step-bar.tier-weak   { background: var(--accent-red); }
  .step-bar.worst       { background: var(--accent-red); color: #fff; }
  .step-meta { width: 200px; padding-left: 16px; font-size: 12px;
               color: var(--ink-muted); }
  .step-meta .rate { font-family: "Playfair Display", Georgia, serif;
                     font-size: 18px; font-weight: 700;
                     color: var(--ink); line-height: 1; }
  .step-meta .drop { color: var(--accent-red); font-weight: 600;
                     display: block; margin-top: 4px; }

  /* === Collapsible sections === */
  .section { background: var(--surface); border-radius: 8px;
             box-shadow: 0 1px 3px rgba(0,0,0,.04);
             margin-bottom: 22px; overflow: hidden; }
  .section-header { padding: 18px 28px; border-bottom: 1px solid var(--border);
                    display: flex; justify-content: space-between;
                    align-items: center; cursor: pointer; user-select: none; }
  .section-header h2 { font-family: "Playfair Display", Georgia, serif;
                       font-size: 18px; font-weight: 700; }
  .toggle { font-size: 16px; color: var(--accent-red); }

  /* === Tables === */
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  thead th { background: #faf8f4; padding: 12px 22px;
             text-align: left; font-weight: 600;
             text-transform: uppercase; letter-spacing: .6px;
             font-size: 11px; color: var(--ink-muted);
             border-bottom: 1px solid var(--border); }
  tbody td { padding: 12px 22px; border-bottom: 1px solid #f4f1eb; }
  tbody tr:last-child td { border-bottom: none; }

  /* === Badges === */
  .badge { display: inline-block; padding: 3px 9px; border-radius: 4px;
           font-size: 11px; font-weight: 600; }
  .badge.green  { background: #ebf5ef; color: var(--accent-green); }
  .badge.yellow { background: #fef6e3; color: #b67a08; }
  .badge.red    { background: var(--accent-red-soft); color: var(--accent-red); }

  /* === Insight callout === */
  .insight { background: var(--accent-red-soft);
             border-left: 4px solid var(--accent-red);
             border-radius: 6px; padding: 16px 22px;
             margin: 16px 28px; font-size: 14px; line-height: 1.6;
             color: #4a2222; }

  .back-top { position: fixed; bottom: 24px; right: 24px; background: var(--accent-red);
              color: #fff; width: 44px; height: 44px; border-radius: 50%; border: none;
              font-size: 20px; cursor: pointer;
              box-shadow: 0 4px 12px rgba(200,49,47,.35); }
  footer { text-align: center; padding: 32px 24px;
           font-size: 12px; color: var(--ink-muted); }

  /* === Print === */
  @media print {
    nav { display: none; position: static; }
    header { padding: 36px 32px 28px; }
    header h1 { font-size: 42px; }
    .section-header { cursor: default; }
    .back-top { display: none; }
    .funnel-wrap, .section {
      box-shadow: none; border: 1px solid var(--border); break-inside: avoid;
    }
  }
</style>
</head>
<body>
<header>
  <div class="header-inner">
    <div class="eyebrow">Funnel Health Report</div>
    <h1>{ORG_NAME} Funnel Health</h1>
    <p class="lede">Step-by-step pass-through for the {FUNNEL_NAME} funnel during {DATE_RANGE}.</p>
    <div class="meta">
      <span><span class="icon">&#128197;</span> {DATE_RANGE}</span>
      <span><span class="icon">&#128202;</span> {REPORT_SUITE}</span>
      <span><span class="icon">&#128340;</span> Prepared {GENERATED_DATE}</span>
    </div>
  </div>
</header>
<nav>
  <a href="#funnel">Funnel</a>
  <a href="#steps">Step Detail</a>
  <a href="#leakage">Leakage Analysis</a>
  <a href="#recommendations">Recommendations</a>
</nav>
<div class="container">
  <!-- Funnel Visualization -->
  <div class="section-label">Funnel Pass-Through</div>
  <div id="funnel" class="funnel-wrap">
    <!-- One .funnel-step per step:
         - Bar width = (step visits / total visits) * 100%
         - Apply tier class to .step-bar based on step pass-through rate:
             tier-strong (>=70%), tier-mid (40-69%), tier-weak (<40%)
         - Worst step also gets .step-bar.worst (red, always)
    -->
  </div>

  <!-- Step Detail Table -->
  <div id="steps" class="section">
    <div class="section-header" onclick="toggle('steps-body')">
      <h2>Step Detail</h2>
      <span class="toggle" id="steps-body-icon">&#9662;</span>
    </div>
    <div id="steps-body">
      <table>
        <thead><tr>
          <th>Step</th><th>Visits</th><th>% of Total</th>
          <th>Step Rate</th><th>Drop-off</th>
        </tr></thead>
        <tbody><!-- step rows with badge coloring on step rate --></tbody>
      </table>
    </div>
  </div>

  <!-- Leakage Analysis -->
  <div id="leakage" class="section">
    <div class="section-header" onclick="toggle('leak-body')">
      <h2>Leakage Analysis (Worst Step: {WORST_STEP_NAME})</h2>
      <span class="toggle" id="leak-body-icon">&#9662;</span>
    </div>
    <div id="leak-body">
      <div class="insight">{LEAKAGE_INSIGHT_TEXT}</div>
      <table>
        <thead><tr>
          <th>Dimension</th><th>Entered Step</th><th>Completed Step</th>
          <th>Step Rate</th><th>vs. Average</th>
        </tr></thead>
        <tbody><!-- leakage breakdown rows --></tbody>
      </table>
    </div>
  </div>

  <!-- Recommendations -->
  <div id="recommendations" class="section">
    <div class="section-header" onclick="toggle('rec-body')">
      <h2>Recommendations</h2>
      <span class="toggle" id="rec-body-icon">&#9662;</span>
    </div>
    <div id="rec-body">
      <!-- insight divs with specific recommendations per step -->
    </div>
  </div>
</div>
<button class="back-top" onclick="window.scrollTo({top:0,behavior:'smooth'})">
  &#8679;
</button>
<footer>Funnel Health &mdash; {ORG_NAME} &mdash; Generated {GENERATED_DATE}</footer>
<script>
function toggle(id) {
  var el = document.getElementById(id);
  var ic = document.getElementById(id + '-icon');
  if (el.style.display === 'none') { el.style.display = ''; ic.textContent = '\u25be'; }
  else { el.style.display = 'none'; ic.textContent = '\u25b8'; }
}
</script>
</body>
</html>
Section titles — no phase prefix: Section headings in the HTML report must not include the phase number. Use the plain section name only (e.g., "Funnel Overview" not "Phase 2 — Funnel Overview", "Drop-off Analysis" not "Phase 3 — Drop-off Analysis").
Write to
/tmp/aa_funnel_analysis_report_<YYYY-MM-DD_HHMMSS>.html
and open:
bash
open /tmp/aa_funnel_analysis_report_<YYYY-MM-DD_HHMMSS>.html

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Funnel Health &mdash; {ORG_NAME} &mdash; {FUNNEL_NAME}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    --bg: #f5f4f1;
    --surface: #ffffff;
    --ink: #1a1a1a;
    --ink-muted: #6b6b6b;
    --border: #e5e2dc;
    --header-bg: #0e0e10;
    --header-warm: #3a1010;
    --accent-red: #c8312f;
    --accent-red-bright: #ff6b68;
    --accent-red-soft: #fdecea;
    --accent-green: #1f7a4d;
    --accent-yellow: #d4a017;
  }
  body { font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
         background: var(--bg); color: var(--ink); line-height: 1.5;
         -webkit-font-smoothing: antialiased; }

  /* === Header === */
  header { background: linear-gradient(120deg, var(--header-bg) 0%, #1a0d0d 55%, var(--header-warm) 100%);
           color: #fff; padding: 56px 56px 44px; position: relative; overflow: hidden; }
  header::after { content: ""; position: absolute; right: -140px; top: -140px;
                  width: 460px; height: 460px;
                  background: radial-gradient(circle, rgba(200,49,47,.35) 0%, transparent 70%);
                  pointer-events: none; }
  .header-inner { max-width: 1080px; margin: 0 auto; position: relative; z-index: 1; }
  .eyebrow { display: inline-flex; align-items: center; gap: 8px;
             padding: 6px 14px; border: 1px solid rgba(255,107,104,.55);
             border-radius: 999px; color: var(--accent-red-bright);
             font-size: 11px; font-weight: 600; letter-spacing: 1.2px;
             text-transform: uppercase; margin-bottom: 24px;
             background: rgba(200,49,47,.10); }
  .eyebrow::before { content: ""; width: 6px; height: 6px;
                     background: var(--accent-red-bright); border-radius: 50%; }
  header h1 { font-family: "Playfair Display", Georgia, serif;
              font-size: 56px; font-weight: 700; letter-spacing: -1.5px;
              line-height: 1.05; margin-bottom: 14px; color: #fff; }
  header .lede { font-size: 16px; max-width: 600px;
                 color: rgba(255,255,255,.80); margin-bottom: 24px;
                 line-height: 1.55; }
  header .meta { display: flex; flex-wrap: wrap; gap: 22px;
                 font-size: 13px; color: rgba(255,255,255,.60); }
  header .meta span { display: inline-flex; align-items: center; gap: 6px; }
  header .meta .icon { opacity: .8; }

  /* === Tabs === */
  nav { background: var(--surface); border-bottom: 1px solid var(--border);
        padding: 0 56px; display: flex; gap: 28px;
        position: sticky; top: 0; z-index: 50; }
  nav a { display: block; padding: 16px 0; font-size: 14px;
          color: var(--ink); text-decoration: none;
          border-bottom: 2px solid transparent;
          transition: border-color .15s ease; }
  nav a:hover { border-bottom-color: var(--accent-red); }

  /* === Container === */
  .container { max-width: 1080px; margin: 0 auto; padding: 36px 56px 60px; }

  /* === Section label === */
  .section-label { font-size: 11px; font-weight: 700;
                   text-transform: uppercase; letter-spacing: 1.4px;
                   color: var(--ink-muted); margin-bottom: 14px;
                   padding-bottom: 10px; border-bottom: 1px solid var(--border); }

  /* === Funnel visualization (horizontal stepped bars) ===
     Bar color is set per step by tier:
       .tier-strong = green  (rate >= 70% pass-through)
       .tier-mid    = yellow (40-69%)
       .tier-weak   = red    (< 40%)
     The biggest-leakage step also gets .step-bar.worst (red, regardless of tier). */
  .funnel-wrap { background: var(--surface); border-radius: 8px;
                 padding: 28px 32px; margin-bottom: 28px;
                 box-shadow: 0 1px 3px rgba(0,0,0,.04); }
  .funnel-step { display: flex; align-items: center; margin-bottom: 10px; }
  .step-label { width: 220px; font-size: 14px; font-weight: 600;
                text-align: right; padding-right: 16px; white-space: nowrap;
                overflow: hidden; text-overflow: ellipsis; color: var(--ink); }
  .step-bar-wrap { flex: 1; position: relative; height: 44px;
                   background: #f4f1eb; border-radius: 4px; }
  .step-bar { height: 44px; border-radius: 4px;
              display: flex; align-items: center; padding: 0 14px;
              color: #fff; font-size: 13px; font-weight: 700;
              transition: width 0.4s ease;
              background: var(--accent-green); }
  .step-bar.tier-strong { background: var(--accent-green); }
  .step-bar.tier-mid    { background: var(--accent-yellow); color: #3a2a05; }
  .step-bar.tier-weak   { background: var(--accent-red); }
  .step-bar.worst       { background: var(--accent-red); color: #fff; }
  .step-meta { width: 200px; padding-left: 16px; font-size: 12px;
               color: var(--ink-muted); }
  .step-meta .rate { font-family: "Playfair Display", Georgia, serif;
                     font-size: 18px; font-weight: 700;
                     color: var(--ink); line-height: 1; }
  .step-meta .drop { color: var(--accent-red); font-weight: 600;
                     display: block; margin-top: 4px; }

  /* === Collapsible sections === */
  .section { background: var(--surface); border-radius: 8px;
             box-shadow: 0 1px 3px rgba(0,0,0,.04);
             margin-bottom: 22px; overflow: hidden; }
  .section-header { padding: 18px 28px; border-bottom: 1px solid var(--border);
                    display: flex; justify-content: space-between;
                    align-items: center; cursor: pointer; user-select: none; }
  .section-header h2 { font-family: "Playfair Display", Georgia, serif;
                       font-size: 18px; font-weight: 700; }
  .toggle { font-size: 16px; color: var(--accent-red); }

  /* === Tables === */
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  thead th { background: #faf8f4; padding: 12px 22px;
             text-align: left; font-weight: 600;
             text-transform: uppercase; letter-spacing: .6px;
             font-size: 11px; color: var(--ink-muted);
             border-bottom: 1px solid var(--border); }
  tbody td { padding: 12px 22px; border-bottom: 1px solid #f4f1eb; }
  tbody tr:last-child td { border-bottom: none; }

  /* === Badges === */
  .badge { display: inline-block; padding: 3px 9px; border-radius: 4px;
           font-size: 11px; font-weight: 600; }
  .badge.green  { background: #ebf5ef; color: var(--accent-green); }
  .badge.yellow { background: #fef6e3; color: #b67a08; }
  .badge.red    { background: var(--accent-red-soft); color: var(--accent-red); }

  /* === Insight callout === */
  .insight { background: var(--accent-red-soft);
             border-left: 4px solid var(--accent-red);
             border-radius: 6px; padding: 16px 22px;
             margin: 16px 28px; font-size: 14px; line-height: 1.6;
             color: #4a2222; }

  .back-top { position: fixed; bottom: 24px; right: 24px; background: var(--accent-red);
              color: #fff; width: 44px; height: 44px; border-radius: 50%; border: none;
              font-size: 20px; cursor: pointer;
              box-shadow: 0 4px 12px rgba(200,49,47,.35); }
  footer { text-align: center; padding: 32px 24px;
           font-size: 12px; color: var(--ink-muted); }

  /* === Print === */
  @media print {
    nav { display: none; position: static; }
    header { padding: 36px 32px 28px; }
    header h1 { font-size: 42px; }
    .section-header { cursor: default; }
    .back-top { display: none; }
    .funnel-wrap, .section {
      box-shadow: none; border: 1px solid var(--border); break-inside: avoid;
    }
  }
</style>
</head>
<body>
<header>
  <div class="header-inner">
    <div class="eyebrow">Funnel Health Report</div>
    <h1>{ORG_NAME} Funnel Health</h1>
    <p class="lede">Step-by-step pass-through for the {FUNNEL_NAME} funnel during {DATE_RANGE}.</p>
    <div class="meta">
      <span><span class="icon">&#128197;</span> {DATE_RANGE}</span>
      <span><span class="icon">&#128202;</span> {REPORT_SUITE}</span>
      <span><span class="icon">&#128340;</span> Prepared {GENERATED_DATE}</span>
    </div>
  </div>
</header>
<nav>
  <a href="#funnel">Funnel</a>
  <a href="#steps">Step Detail</a>
  <a href="#leakage">Leakage Analysis</a>
  <a href="#recommendations">Recommendations</a>
</nav>
<div class="container">
  <!-- Funnel Visualization -->
  <div class="section-label">Funnel Pass-Through</div>
  <div id="funnel" class="funnel-wrap">
    <!-- One .funnel-step per step:
         - Bar width = (step visits / total visits) * 100%
         - Apply tier class to .step-bar based on step pass-through rate:
             tier-strong (>=70%), tier-mid (40-69%), tier-weak (<40%)
         - Worst step also gets .step-bar.worst (red, always)
    -->
  </div>

  <!-- Step Detail Table -->
  <div id="steps" class="section">
    <div class="section-header" onclick="toggle('steps-body')">
      <h2>Step Detail</h2>
      <span class="toggle" id="steps-body-icon">&#9662;</span>
    </div>
    <div id="steps-body">
      <table>
        <thead><tr>
          <th>Step</th><th>Visits</th><th>% of Total</th>
          <th>Step Rate</th><th>Drop-off</th>
        </tr></thead>
        <tbody><!-- step rows with badge coloring on step rate --></tbody>
      </table>
    </div>
  </div>

  <!-- Leakage Analysis -->
  <div id="leakage" class="section">
    <div class="section-header" onclick="toggle('leak-body')">
      <h2>Leakage Analysis (Worst Step: {WORST_STEP_NAME})</h2>
      <span class="toggle" id="leak-body-icon">&#9662;</span>
    </div>
    <div id="leak-body">
      <div class="insight">{LEAKAGE_INSIGHT_TEXT}</div>
      <table>
        <thead><tr>
          <th>Dimension</th><th>Entered Step</th><th>Completed Step</th>
          <th>Step Rate</th><th>vs. Average</th>
        </tr></thead>
        <tbody><!-- leakage breakdown rows --></tbody>
      </table>
    </div>
  </div>

  <!-- Recommendations -->
  <div id="recommendations" class="section">
    <div class="section-header" onclick="toggle('rec-body')">
      <h2>Recommendations</h2>
      <span class="toggle" id="rec-body-icon">&#9662;</span>
    </div>
    <div id="rec-body">
      <!-- insight divs with specific recommendations per step -->
    </div>
  </div>
</div>
<button class="back-top" onclick="window.scrollTo({top:0,behavior:'smooth'})">
  &#8679;
</button>
<footer>Funnel Health &mdash; {ORG_NAME} &mdash; Generated {GENERATED_DATE}</footer>
<script>
function toggle(id) {
  var el = document.getElementById(id);
  var ic = document.getElementById(id + '-icon');
  if (el.style.display === 'none') { el.style.display = ''; ic.textContent = '\u25be'; }
  else { el.style.display = 'none'; ic.textContent = '\u25b8'; }
}
</script>
</body>
</html>
章节标题要求——无阶段前缀:HTML报告中的章节标题不得包含阶段编号。仅使用纯章节名称(例如,“漏斗概览”而非“阶段2 — 漏斗概览”,“流失分析”而非“阶段3 — 流失分析”)。
写入
/tmp/aa_funnel_analysis_report_<YYYY-MM-DD_HHMMSS>.html
并打开:
bash
open /tmp/aa_funnel_analysis_report_<YYYY-MM-DD_HHMMSS>.html

Guardrails

约束规则

  • Always present the list of segments to be created (step 3) and get user confirmation before calling
    upsertSegment
    .
  • If the user already has named segments for funnel steps, prefer using those rather than creating new ones — check
    findSegments
    first.
  • Limit funnel to 8 steps maximum for practical API call budgets.
  • Funnel conversion rates using visit segments are approximations — they count visits that touched each step, not strict sequential fallout. Note this limitation to the user.

  • 在调用
    upsertSegment
    前,始终列出要创建的细分(步骤3)并获得用户确认。
  • 如果用户已有漏斗步骤的命名细分,优先使用现有细分,而非创建新细分——先调用
    findSegments
    检查。
  • 漏斗步骤最多限制为8个,以控制API调用成本。
  • 使用访问细分计算的漏斗转化率是近似值——统计的是触及每个步骤的访问量,而非严格的序列脱落情况。需向用户说明此限制。

Example Interaction

交互示例

"Analyze our checkout funnel — I want to see where visitors drop off."
  1. Confirm report suite and date range (last 30 days).
  2. Define 5 steps: Product View → Add to Cart → Checkout → Payment → Purchase.
  3. Validate page names with
    searchDimensionItems
    .
  4. Find existing step segments or create new ones (with user approval).
  5. Run 6 reports (5 steps + total).
  6. Compute: 100% → 42% (add to cart) → 28% (checkout) → 19% (payment) → 11% (purchase). Overall conversion: 11%.
  7. Worst step: Add-to-Cart to Checkout (33% drop-off rate).
  8. Drill in: Mobile users have only 18% checkout rate vs. 31% on Desktop.
  9. Generate and open HTML report.
  10. Key insight: "Mobile is your funnel's biggest liability. Consider simplifying the mobile checkout experience."
“分析我们的结账漏斗——我想看看访客在哪里流失。”
  1. 确认报表套件和日期范围(过去30天)。
  2. 定义5个步骤:浏览产品 → 加入购物车 → 开始结账 → 支付 → 完成购买。
  3. 通过
    searchDimensionItems
    验证页面名称。
  4. 查找现有步骤细分或创建新细分(获得用户批准)。
  5. 运行6次报告(5个步骤 + 总访问量)。
  6. 计算结果:100% → 42%(加入购物车)→ 28%(开始结账)→ 19%(支付)→ 11%(完成购买)。整体转化率:11%。
  7. 最差步骤:加入购物车到开始结账(流失率33%)。
  8. 深入分析:移动端用户的结账率仅为18%,而桌面端为31%。
  9. 生成并打开HTML报告。
  10. 核心洞察:“移动端是您漏斗的最大短板。考虑简化移动端结账流程。”