关于 aa-top-movers-watchlist
这是一项 Adobe Analytics 技能,可比较两个时段的指标,并按维度找出增幅和降幅最大的项目。它生成 HTML 报告和文字摘要,并规范日期窗口及低流量项目筛选。
主要功能
- 01比较两个时段的维度项目
- 02按绝对变化排列增幅与降幅
- 03标记新增和消失项目
- 04应用重要性阈值
- 05生成 HTML 报告和文字摘要
适用场景
- 01查找周环比变化最大的页面
- 02比较不同时段的营销活动或渠道表现
- 03筛选低流量项目后检查产品或地区变化
MCP 相关
使用 Adobe Analytics MCP 工具选择报表套件、解析指标和维度,并获取各时段的报表。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ adobe/ skills --skill aa-top-movers-watchlist安装后在 Claude Code 中输入/aa-top-movers-watchlist确认已加载。
附带文件evals/evals.json
详细介绍
Top Movers Watchlist(Adobe Analytics)
识别两个时间段之间关键指标增幅和降幅最大的维度项。呈现页面、渠道、营销活动、产品或任何其他细分维度中的涨幅最高项和跌幅最高项。
使用的 AA MCP 工具
findReportSuites— 选择报表包setSessionDefaults— 设置会话上下文(reportSuiteId + globalCompanyId)describeAa(REPORT_SUITE_CONTEXT_GUIDE)— 加载日历/时区上下文findMetrics— 查找关键指标 IDfindDimensions— 查找可用的细分维度runReport— 为每个维度分别运行时间段 A 和时间段 B 的报表searchDimensionItems— 按需验证特定项
阶段 0 — 设置
-
使用
findReportSuites/setSessionDefaults确认报表包。 -
调用
describeAa(REPORT_SUITE_CONTEXT_GUIDE)加载报表包上下文。记录:WEEK_START_DOW— 上下文指南中规定的一周首日。如果上下文指南未返回值,则使用星期一(ISO 8601)作为明确且确定的默认值。这不是会在不同运行之间变化的回退值:针对同一报表包的每次运行,都会根据上下文指南或固定的星期一默认值解析出相同的WEEK_START_DOW。TIMEZONE— 报表包时区。WEEK_START_DOW_SOURCE— 如果值来自describeAa,则设为"context guide";如果上下文指南未提供值而使用了星期一,则设为"default"。在产物页脚中显示此项,以便审计来源。
在阶段 1 中定义时间段 A 和时间段 B 时,将使用这些值。
findReportSuites(globalCompanyId: "<gcid>")
setSessionDefaults(globalCompanyId: "<gcid>", reportSuiteId: "<rsid>")
describeAa(guideType: "REPORT_SUITE_CONTEXT_GUIDE")
阶段 1 — 确认参数
询问用户:
-
指标 — “我应该按哪个指标对变动项进行排名?(访问量、收入、转化次数、页面浏览量等)” 如果未指定,默认使用
metrics/visits。 -
维度 — “你想按哪个维度细分?
- 页面
- 营销渠道
- 营销活动
- 产品
- 流量来源
- 地理区域
- 入口页面” 如果未指定,提供以上列表。
-
时间段 — “你想比较哪两个时间段?” 常用默认值:
- 本周与上周
- 本月与上月
- 最近 7 天与之前的 7 天
- 最近 30 天与之前的 30 天
日历规则(必须遵守): 时间段 A 和时间段 B 必须使用阶段 0 中相同的
WEEK_START_DOW— 两个时间段的startDate必须落在相同的星期几,两者长度必须完全相等,并且时间段 B 必须在时间段 A 开始前一天结束。同一次运行中绝不能混用日期规则(例如,时间段 A 为周一至周日,而时间段 B 为周日至周六)。对于自定义日期范围,将时间段 B 计算为紧接在时间段 A 开始前结束、且长度相同的时间窗口。调用
runReport前的合理性检查: 确认periodA.startDate和periodB.startDate是同一个星期几,并且periodA.startDate - periodB.endDate == 1 day。如果不满足,则重新计算。 -
项目数量上限 — 要显示多少个涨幅最高项和跌幅最高项(默认值:各 10 个)。
-
重要性阈值 — 纳入分析所需的时间段 A 或时间段 B 中的最低绝对值(过滤低流量项目产生的噪声)。默认情况下,排除两个时间段内访问量(或等效指标)均不足 100 的项目。
确认:“我将按 [指标] 对 [维度] 进行细分,显示涨幅最高和跌幅最高的各 10 项,并比较 [时间段 A] 与 [时间段 B]。”
阶段 2 — 解析组件
findMetrics(expansions: "componentType")
findDimensions(page: 1, limit: 100)
注意:
findMetrics需要expansions参数(使用"componentType"或"categories")。findDimensions需要page和limit。
记录指标和维度的 id。
阶段 3 — 运行时段报表
在所选维度上,分别为两个时间段运行该指标的报表:
runReport(
metricIds: "<metricId>",
dimensionId: "<dimensionId>",
startDate: "<period A start>T00:00",
endDate: "<period A end>T23:59",
limit: 200
)
runReport(
metricIds: "<metricId>",
dimensionId: "<dimensionId>",
startDate: "<period B start>T00:00",
endDate: "<period B end>T23:59",
limit: 200
)
注意:
runReport使用metricIds(而非metricId),日期采用 ISO 8601 格式(YYYY-MM-DDTHH:mm)的startDate/endDate,而非dateRange参数。
使用 limit: 200,以捕获足够的项目进行有意义的变动分析。
阶段 4 — 计算变化量并排序
对于任一时期中出现的每个维度项:
- 时期 A 的值(如果结果中不存在,则为 0)
- 时期 B 的值(如果结果中不存在,则为 0)
- 绝对变化量:时期 A - 时期 B
- 百分比变化:变化量 / 时期 B × 100(如果时期 B = 0,则为“+100% new”)
- 应用重要性筛选:排除 max(时期 A,时期 B)小于 重要性阈值的项
按绝对变化量降序排列增长项(正变化量)。 按绝对变化量升序排列下降项(负变化量)。
分别取两个列表中的前 N 项。
特殊情况
- 新增项(仅在时期 A 中出现):标记为“新增 — 没有上一时期的数据。”达到重要性阈值时纳入。
- 消失项(仅在时期 B 中出现,时期 A 为 0):标记为“已移除 — 没有当前时期的数据。”
- 接近零的项:占指标总值 <1% 的项——除非其百分比变化非常大,否则考虑将其从监控列表中排除。
阶段 5 — 可选:多维度监控列表
如果用户希望查看多个维度的变化项(例如,同时查看页面和渠道),则对每个附加维度重复阶段 3–4。每个附加维度会增加 2 次 runReport 调用。
runReport(metricIds: "<metricId>", dimensionId: "variables/marketingchannel",
startDate: "<A>T00:00", endDate: "<A>T23:59", ...)
runReport(metricIds: "<metricId>", dimensionId: "variables/page",
startDate: "<A>T00:00", endDate: "<A>T23:59", ...)
在 HTML 报告中按维度将结果分别归入不同的表格。
阶段 6 — 生成 HTML 报告
在行内构建变化项报告,并写入
/tmp/aa_top_movers_report_<YYYY-MM-DD_HHMMSS>.html。
渲染规则 — 每次运行都一致应用
针对同一报告套件、指标/维度和时期运行此技能两次,渲染结果必须完全一致(生成时间戳除外)。以下规则用于固定格式选择,避免 AI 的输出出现偏差。
数字格式
- KPI 值(每个摘要卡片中的大号数字,以及每行变化项的值)——使用完整数字并添加千位分隔符(
8,160、77,584、1,250,000)。不要使用K或M等 SI 后缀,即使数值很大。利益相关者需要准确数字,而非缩写。 - 百分比变化(胶囊标签和叙述性要点中)——始终保留一位小数,按远离零的方向舍入。例如,
−23.55%显示为−23.6%,绝不显示为−23.5%。使用完整精度的值进行计算;仅在显示时舍入。 - 百分点变化(适用于转换率或跳出率等本身为百分比的指标)——采用相同的舍入方式,后缀为
pp。例如:+0.40 pp。 - 货币——使用
$前缀和千位分隔符,金额 ≥ $100 时不显示小数位($1,240,000);金额 < $100 时保留美分($45.20)。
空值 / 缺失数据处理
KPI 卡片或变化项行必须如实反映报告套件返回的内容。AI 不得擅自替换为其他指标,也不得为了让报告更整洁而隐藏卡片。
- 两个时期返回的跟踪指标均为 0 或 NULL:在摘要卡片中,将
kpi-value渲染为Data unavailable,胶囊标签类设为flat,胶囊标签文本设为⚠ N/A,prior文本设为Both periods returned no data — validate instrumentation。卡片必须保留在网格中;不要省略。 - 一个时期返回有效数据,另一个时期返回 0 / NULL:将有效值作为
kpi-value渲染,胶囊标签类设为flat,胶囊标签文本设为⚠ N/A,prior文本设为Prior {period_noun}: no data。 - 绝不替换为其他指标(例如,Revenue 返回 $0 时改用 Orders)。正在分析的指标必须就是渲染的指标。
HTML 模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Top Movers — {ORG_NAME} — {METRIC_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; }
/* === Period strip === */
.period-bar { background: var(--surface); border-radius: 8px;
padding: 14px 22px; margin-bottom: 28px;
font-size: 13px; color: var(--ink-muted);
box-shadow: 0 1px 3px rgba(0,0,0,.04);
border-left: 3px solid var(--accent-red); }
.period-bar strong { color: var(--ink); font-weight: 600; }
/* === Dimension section === */
.dim-section { margin-top: 32px; }
.dim-section > h2 { font-family: "Playfair Display", Georgia, serif;
font-size: 22px; font-weight: 700; color: var(--ink);
margin-bottom: 16px; padding-bottom: 10px;
border-bottom: 1px solid var(--border); }
/* === Gainers / Losers dual panels === */
.movers-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
margin-bottom: 32px; }
.movers-panel { background: var(--surface); border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,.05); overflow: hidden; }
.movers-panel-header { padding: 18px 24px; border-bottom: 1px solid var(--border); }
.movers-panel-header.gainers { border-top: 3px solid var(--accent-green); }
.movers-panel-header.losers { border-top: 3px solid var(--accent-red); }
.movers-panel-header h2 { font-family: "Playfair Display", Georgia, serif;
font-size: 17px; font-weight: 700; }
.movers-panel-header.gainers h2 { color: var(--accent-green); }
.movers-panel-header.losers h2 { color: var(--accent-red); }
/* === Tables === */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th { background: #faf8f4; padding: 10px 18px;
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: 10px 18px; border-bottom: 1px solid #f4f1eb; }
tbody tr:last-child td { border-bottom: none; }
.delta-pos { color: var(--accent-green); font-weight: 700; }
.delta-neg { color: var(--accent-red); font-weight: 700; }
/* === Rank badges (circular) === */
.rank-badge { display: inline-flex; align-items: center; justify-content: center;
width: 24px; height: 24px;
border-radius: 50%;
background: var(--accent-red); color: #fff;
font-size: 11px; font-weight: 700;
margin-right: 8px; vertical-align: middle; }
.rank-badge.gain { background: var(--accent-green); }
.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); }
@media (max-width: 700px) { .movers-grid { grid-template-columns: 1fr; } }
/* === Print === */
@media print {
nav { display: none; position: static; }
header { padding: 36px 32px 28px; }
header h1 { font-size: 42px; }
.back-top { display: none; }
.movers-grid { page-break-inside: avoid; }
.movers-panel, .period-bar {
box-shadow: none; border: 1px solid var(--border);
}
}
</style>
</head>
<body>
<header>
<div class="header-inner">
<div class="eyebrow">Top Movers Watchlist</div>
<h1>{ORG_NAME} Top Movers</h1>
<p class="lede">Biggest gainers and decliners for {METRIC_NAME} between {PERIOD_A} and {PERIOD_B}.</p>
<div class="meta">
<span><span class="icon">📅</span> {PERIOD_A} vs {PERIOD_B}</span>
<span><span class="icon">📊</span> {REPORT_SUITE}</span>
<span><span class="icon">🕔</span> Prepared {GENERATED_DATE}</span>
</div>
</div>
</header>
<nav>
<a href="#summary">Summary</a>
</nav>
<div class="container">
<div id="summary" class="period-bar">
<strong>Metric:</strong> {METRIC_NAME} |
<strong>Period A:</strong> {PERIOD_A} |
<strong>Period B:</strong> {PERIOD_B} |
<strong>Materiality:</strong> ≥{THRESHOLD} {METRIC_UNIT}
</div>
<div id="dim-{DIM_ID}" class="dim-section">
<h2>{DIMENSION_NAME}</h2>
<div class="movers-grid">
<div class="movers-panel">
<div class="movers-panel-header gainers"><h2>▲ Top Gainers</h2></div>
<table>
<thead><tr>
<th>#</th><th>Item</th><th>Period A</th><th>Period B</th>
<th>Delta</th><th>% Change</th>
</tr></thead>
<tbody></tbody>
</table>
</div>
<div class="movers-panel">
<div class="movers-panel-header losers"><h2>▼ Top Losers</h2></div>
<table>
<thead><tr>
<th>#</th><th>Item</th><th>Period A</th><th>Period B</th>
<th>Delta</th><th>% Change</th>
</tr></thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<button class="back-top" onclick="window.scrollTo({top:0,behavior:'smooth'})">
⇧
</button>
<footer>Top Movers Watchlist — {ORG_NAME} — Generated {GENERATED_DATE}</footer>
</body>
</html>
章节标题 — 不带阶段前缀:HTML 报告中的章节标题不得包含阶段编号。仅使用纯章节名称(例如,“Top Movers”而非“Phase 3 — Top Movers”,“Dimension Breakdown”而非“Phase 4 — Dimension Breakdown”)。
写入 /tmp/aa_top_movers_report_<YYYY-MM-DD_HHMMSS>.html 并打开:
open /tmp/aa_top_movers_report_<YYYY-MM-DD_HHMMSS>.html
行内摘要(始终提供)
HTML 报告后始终附上简短的文字说明:
Top Movers: [Metric] — [Period A] vs [Period B]
GAINERS (by [Dimension])
1. [Item A] +12,400 visits (+34%)
2. [Item B] +8,200 visits (+21%)
3. [Item C] +5,100 visits (+18%)
LOSERS
1. [Item X] -9,800 visits (-41%)
2. [Item Y] -4,200 visits (-18%)
3. [Item Z] -2,900 visits (-12%)
Key takeaway: [1 sentence explanation of the story]
防护措施
- 始终应用重要性阈值,过滤掉数量可以忽略不计的项目,避免产生噪声。
- 明确标记新增项目(上一时段不存在)和消失项目(当前时段不存在)——它们往往透露出重要信息。
- 对百分比变化进行排名时,优先按绝对变化量排序(而非百分比),以避免数量较小的项目因百分比虚高而排在前面。
- 为提高调用效率,维度列表最多包含 6 项。可应要求再运行其他维度。
示例交互
“上周哪些页面的增幅和降幅最大?”
- 确认报告套件。
- 确认指标:页面浏览量。维度:页面名称。时段:最近 7 天与之前 7 天。数量限制:各取前 10 项。
- 针对页面 × 访问量运行 2 份报告。
- 计算变化量,并应用重要性标准(任一时段的页面浏览量至少为 500)。
- 生成 HTML 报告并提供内联摘要。
- “增幅最大:/products/new-feature,页面浏览量增加 18,200(+312%)。降幅最大:/promo/expired-campaign,页面浏览量减少 9,400(-88%)。”