| name | alimama-cli |
| description | 万相台 AI 无界(one.alimama.com / 阿里妈妈 onebp)数据查询 + 单元关停 CLI。给 AI 代理一行命令拉取自家店铺的广告推广数据 — 涵盖"报表"(11 种历史复盘) + "推广"(3 种当前在投计划) + 单元/商品开关查询 + 账户余额 / 营销活动。查询类全只读;唯一写操作 promo-off(按宝贝ID关停在投单元)默认 dry-run,必须 --execute 才执行。触发场景:用户提到"万相台/阿里妈妈/广告投放/推广复盘/推广计划/onebp/alimama/广告效果/广告花费/ROI/计划报表/关键词推广/人群推广/货品全站推广/营销场景报表/广告数据/广告诊断/关停广告/关掉某商品"等。 |
| author | rakel |
| version | 0.12.0 |
| tags | ["taobao","alimama","advertising","ecommerce","cli"] |
alimama-cli — 万相台 AI 无界 数据查询 + 单元关停 CLI
一句话上手
~/.claude/skills/alimama-cli/scripts/alimama.sh doctor
~/.claude/skills/alimama-cli/scripts/alimama.sh charge-summary
适用人群
阿里妈妈广告主自己拉取自家店铺数据 + 关停广告。查询类全只读;唯一的写操作是 promo-off(按宝贝ID关停在投单元),默认只列清单不执行,必须显式 --execute 才动,且不调价/不删除/不新建。
前置条件
重要:模块结构(别再搞混 "报表" vs "推广")
万相台 AI 无界
├─ 📊 报表(看历史数据复盘) → report-* 子命令 + charge-summary
└─ 🚀 推广(看当前在投的计划) → promo-* 子命令
| 维度 | 📊 报表 | 🚀 推广 |
|---|
| 时间 | 历史区间 | 当前快照 |
| 关心 | "昨天/上周花了多少、ROI 多少、谁转化好" | "现在哪些计划在跑、出价多少、日预算多少" |
| 接口 | /report/query.json(带 startTime/endTime) | /campaign/horizontal/findPage.json(无日期) |
| 用户问"昨天花了多少" | ✅ 用这个 | ❌ |
| 用户问"现在在投哪些关键词" | ❌ | ✅ 用这个 |
全部子命令(23 个,含 1 个写操作)
🔧 工具/账户类(5 个)
| 子命令 | 用途 |
|---|
doctor | 检查 cookie / 登录态 |
account-balance | 账户余额(实时) |
activity-list | 营销活动列表 |
campaign-list | 推广计划清单(仅 ID + 名字,无业务数据) |
api <path> | 通用接口探测(debug 用,AI 代理一般不调) |
📊 报表类(11 个)—— 看历史数据
每个都接受:--date YYYY-MM-DD --end-date YYYY-MM-DD --limit N --window 1|7|15 --raw --out file
| 子命令 | 对应万相台页面 | 干嘛用 |
|---|
charge-summary | 营销场景报表 | 总览:各推广场景(关键词推广/人群推广)各花了多少 |
scene-summary | 场景大盘(大屏) | 某场景大盘汇总:展现量(adPv)/点击/花费/成交/ROI/加购/转化,默认过去7天,--biz 选场景 |
scene-daily | 营销场景报表→分日详情 | 某场景按天的展现/点击/花费/点击率/成交额/笔数/转化率/ROI 时序(含合计行),默认过去7天,--biz 选场景,--window 1|7|15 转化窗口 |
report-campaign | 计划报表 | 按"每个推广计划"看花费 + ROI |
report-adgroup | 单元报表 | 按"计划下的单元"看 |
report-keyword | 关键词报表 | 按"每个关键词"看,找高 ROI 词加价/低 ROI 词砍 |
report-crowd | 人群报表 | 按"每个定向人群"看转化率 |
report-item | 商品报表 | 按"每个被推广的商品"看 |
report-creative | 创意报表 | 按"每个广告图/视频/标题"看点击率 |
report-area | 地域报表 | 按"客户城市"看 |
report-coupon | 权益报表 | 优惠券效果 |
report-realtime | 实时报表 | 今天到现在的实时数据(按小时) |
report-other | 其他推广报表 | 杂项 |
🚀 推广类(3 个)—— 看当前在投
每个接受:--limit N --page N --status start pause --raw --out file(不需要日期)
| 子命令 | bizCode | 干嘛用 |
|---|
promo-wholesite | onebpSite | 货品全站推广 - 当前在跑哪些计划 |
promo-keyword | onebpSearch | 关键词推广 - 当前在跑哪些计划 |
promo-crowd | onebpDisplay | 人群推广 - 当前在跑哪些计划 |
promo-* 还支持 --item <宝贝ID> 反查(这宝贝在哪个计划里推)。
promo-items --campaign <计划ID>:列出一个计划里的全部商品 + 每个商品的开/关状态(测款计划这类"一计划多商品"必用)。--biz 可限定玩法,默认自动搜全部。开关取自单元的 onlineStatus(1=开/0=关);标题为"商品已删除/下架"=广告开着但宝贝没了,该清理。
promo-units:把所有计划的全部单元(=商品广告位)拉平成一张表,相当于网页的"单元 Tab"。--biz 限定玩法(默认扫全部 3 种);--item <宝贝ID> 反查某商品散落在哪些计划、各自开关(这是"关掉某商品全部投放"的前置视图——一个商品常进多条计划,每条算一个独立单元各有开关);--unit <单元ID> 按单元ID精确定位一条单元。--item 和 --unit 都走服务端过滤(不全量拉回来再筛),命中即停。
AI 代理决策指南(用户说什么 → 调用什么)
| 用户问 | 调用 |
|---|
| "看昨天广告花了多少" / "昨天的 ROI" | charge-summary --date YYYY-MM-DD |
| "哪些计划最赚钱" / "ROI 最高的计划" | report-campaign --date X --end-date Y --limit 10 |
| "哪些关键词在浪费钱" | report-keyword --date X --raw 然后 jq 过滤 charge>5 and alipayInshopAmt==0 |
| "现在关键词推广有多少计划在跑" | promo-keyword |
| "看货品全站推广现在的状况" | promo-wholesite |
| "宝贝 XXX 现在在哪个全站/关键词/人群计划里推" | promo-wholesite --item XXX(自动翻全部页反查,命中显示计划ID/预算/出价/状态) |
| "计划 XXX 里有哪些商品 / 哪个开哪个关" | promo-items --campaign XXX |
| "宝贝 XXX 散在哪些计划里 / 各自开关" | promo-units --item XXX(服务端过滤;三种玩法都准,含关键词推广) |
| "单元 XXX 是什么 / 看某个单元ID的信息" | promo-units --unit XXX(服务端精确定位,命中即停) |
| "把所有计划的单元拉平成一张表看" | promo-units(相当于网页"单元 Tab") |
| "把宝贝 XXX 的广告全关了" | ⚠️写:promo-off --item XXX(先看 dry-run 清单),确认后 promo-off --item XXX --execute |
| "人群/关键词推广的展现量/点击/花费/ROI 大盘" | scene-summary [--biz crowd](默认过去7天,展现量=adPv) |
| "关键词推广这几天每天花费/ROI 怎么走的" / "某场景分日趋势" | scene-daily --biz keyword --date X --end-date Y(按天时序 + 合计行) |
| "看哪个人群转化好" | report-crowd --date X --end-date Y |
| "看每个商品的广告效果" | report-item |
| "看哪个城市出单多" | report-area |
| "看实时数据" | report-realtime |
| "账户还剩多少钱" | account-balance |
| 报错或验证环境 | doctor |
默认 --date 是昨天(避免今天数据不全)。
报表类输出 schema(喂给 LLM 分析时用)
charge-summary 输出格式化文本,加 --raw 拿 JSON:
{
"data": {
"totalCharge": 12345.67,
"searchCharge": 6789.01,
"displayCharge": 5556.66,
"contentSceneCharge": 0,
"activitySceneCharge": 0,
"crowdSceneCharge": 0,
"shopSceneCharge": 0,
"itemSceneCharge": 0,
"siteSceneCharge": 0,
"agencySceneCharge": 0
}
}
report-* 输出格式:
{
"data": {
"count": 57,
"totalData": {"charge": 1234.56, "alipayInshopAmt": 8888.88, "roi": 7.20},
"list": [
{
"campaignId": 0,
"promotionName": "<计划名>",
"charge": 100.00,
"alipayInshopAmt": 1000.00,
"roi": 10.00,
"click": 200,
"ctr": 0.040,
"ecpc": 0.50,
"cvr": 0.010,
"cartRate": 0.05,
"alipayInshopNum": 5
}
]
}
}
完整指标 (queryFieldIn)(v0.10 起含 adPv 展现量,报表输出已带"展现"列):
adPv(展现量) / charge(花费) / click(点击量) / ctr(点击率) / ecpc(平均点击花费) / alipayInshopAmt(成交金额) / alipayInshopNum(成交笔数) / alipayDirNum(直接成交单数) / cartInshopNum(加购数) / cvr(转化率) / roi(投产比) / cartRate(加购率) / cartCost(加购成本) / colCartCost(收藏加购成本) / itemColCartCost(商品收藏加购成本) / inshopPotentialUvRate(潜客率) / newAlipayInshopUvRate(新成交客户率)
不同 report-X 子命令的 row 里名称字段不同:
| 子命令 | 名称字段 |
|---|
report-campaign | promotionName |
report-adgroup | adgroupName |
report-keyword | originalWord |
report-crowd | crowdName |
report-item | itemTitle |
report-creative | creativeName |
report-area | provinceName / province |
推广类输出 schema
promo-* 输出:
{
"data": {
"count": 33,
"list": [
{
"campaignId": 0,
"campaignName": "<计划名>",
"bizCode": "onebpSearch",
"displayStatus": "start",
"dayBudget": 260.0,
"bidUnit": "平均点击成本${constraintValue}元",
"constraintValue": 0.27,
"bidTypeV2": "smart_bid",
"launchPeriodDisplayTime": "18:30-19:00",
"promotionType": "item",
"topStatus": true,
"gmtCreate": "2026-03-09 15:42:30"
}
]
}
}
判定状态:displayStatus == "start" 在投,"pause" 暂停。
宝贝 ID ↔ 计划 的对应(反查关键情报)
findPage 顶层的 itemId / itemIdList / scopeItems 恒为 null,网页上能看到宝贝 ID 是因为请求体带了 adgroupRequired:true,服务端才回填单元:
计划行.adgroupList[] → 该计划下的所有单元(一计划可含多个单元)
计划行.adgroupList[i].material.materialId → 宝贝 ID(lastAdgroup.material 兜底)
计划行.adgroupList[i].material.title → 商品标题(被删/下架时为 null)
计划行.adgroupList[i].onlineStatus → 单元开关:1=投放中 / 0=未投放
_promo_item() 取第一个商品;_promo_all_items() 取全部单元 + 开关,供 promo-items/promo-units 用。CLI 已默认 adgroupRequired:true。
单元级接口(推荐用它做单元/商品查询)
POST /adgroup/horizontal/findPage.json?bizCode=<X> —— 扁平单元列表,每行一个商品广告位,三种玩法都直接返回 material.materialId(宝贝ID)+ material.title + onlineStatus + campaignId/campaignName。
请求体:{bizCode, offset, pageSize, statusList:[start,pause,end], campaignId?, itemId?, adgroupId?}。代码见 fetch_all_adgroups() + _adgroup_unit(),promo-units/promo-items/promo-off 都走它。
三个服务端过滤参数(默认优先用,别再全量拉回客户端筛):
| body 参数 | 作用 | 实测 |
|---|
campaignId | 只取某计划下的单元 | promo-items 用 |
itemId(数字) | 只取某宝贝ID的单元 | 返回该商品散落各计划的全部单元(验过 1 商品命中 15 单元),对 promo-off 关停安全;promo-units --item/promo-off 用 |
adgroupId(数字) | 精确定位某个单元ID | count=1 命中即停;promo-units --unit 用 |
⚠️ 工作准则:有 ID(计划/宝贝/单元)就走服务端过滤,命中即停;不要把全量(关键词单元上千)拉回客户端再 filter——慢且易超时。仅当用户要"全表"才不带过滤。
为什么不用"计划级 findPage + adgroupRequired"取单元(踩过的坑):
| 玩法 | 计划级嵌套单元 material | 单元级接口 material | 备注 |
|---|
| 货品全站 onebpSite | ✅ 有宝贝ID | ✅ | 一计划=一商品 |
| 人群推广 onebpDisplay | ✅ 有宝贝ID | ✅ | 一计划=多商品;同商品常进多计划 |
| 关键词 onebpSearch | ❌ 恒 null | ✅ 有宝贝ID | 计划级认不出商品,必须走单元级接口 |
- 计划级
adgroupRequired:true 对关键词推广 material 恒 null,且单元巨多(单计划见过 266/1848 总),响应体大易超时 → 单元/商品查询一律用单元级接口,不要再用 adgroupRequired 取单元。
- 单请求超时默认 30s(
ALIMAMA_TIMEOUT 可覆盖);onebpSearch 服务端偏慢,fetch_all_adgroups 用 pageSize=50。
_promo_item()/_promo_all_items()(计划级取单元)仅保留给货品全站/人群的快速取首图场景。
写接口:开关单元(promo-off 用)
POST /adgroup/updatePart.json?csrfId=<X>&bizCode=<biz> —— HAR 实测:
body: {"bizCode":"<biz>","adgroupList":[{"campaignId":<cid>,"adgroupId":<aid>,"displayStatus":"pause"}],"csrfId":"<X>"}
displayStatus: "pause"=关(响应 onlineStatus:0) / "start"=开(onlineStatus:1)
adgroupList 可一次传多个单元(同 bizCode 批量)
- 成功标志:响应
data.errorCount == 0
loginPointId / bx-v 头:HAR 里有,但同源读接口不带也成 → 判定为可选埋点,CLI 省略(首次实测确认)
- 代码:
set_adgroups_status();命令 promo-off(默认 dry-run,--execute 才真发)
写操作铁律:promo-off 默认只列清单不动;必须 --execute 才调写接口;AI 代理执行前必须把清单给用户确认。只关单元(pause),不调价/不删/不新建。
场景大盘汇总(scene-summary)—— 展现量等大盘指标
展现量字段 = adPv。各推广场景的大盘汇总走 POST /report/query.json,场景过滤靠 URL 的 ?bizCode=<scene>(body 里的 bizCode 不生效,会返回全账户合计!):
URL : /report/query.json?bizCode=onebpDisplay&csrfId=<X>
body: {bizCode, byPage:false, fromRealTime:true, startTime, endTime,
splitType:"sum", computeType:"sum", sourceList:["scene","adgroup_list"],
queryDomains:[], queryFieldIn:[adPv,click,charge,ctr,ecpm,cvr,roi,...]}
- 实测:人群(onebpDisplay) + 关键词(onebpSearch) 两场景之和 = 全账户合计(货品全站该店多暂停≈0)。
fromRealTime:true=实时归因(与网页一致);false=历史。昨天数据凌晨可能未出 → 默认查过去7天。
- ⚠️
onebpSite(货品全站) 的 sum 查询服务端偏慢、常 30s 超时;命令已优雅降级显示 ⚠️。
- 代码
fetch_scene_summary() / cmd_scene_summary;命令 scene-summary [--biz] [--date --end-date] [--no-realtime]。
字段字典与发现方法论(v0.12+,AI 取数主力走这里)
主力不是背命令,是查字典。 仓库根目录 fields.json 是机器可读字段字典,每条 = 字段码 → {cn 中文名, scope 适用命令, fmt 格式, status, note 口径备注}。
标准取数动线:
- 先读
fields.json 找到要的字段码 + 它的 note(口径警告)
- 用
--fields 选列取数:report-keyword --fields charge,roi,alipayInshopUv --date X --end-date Y;想要字典里某命令的全部字段用 --all-fields
- 预设命令(
charge-summary/scene-summary 等)只是常用查询的快捷方式,不是唯一入口
status: candidate 的字段中文名尚未破译,用前先验证;note 里的口径警告必须遵守(见下方坑规矩)。
发现方法论三招(字典里没有的字段,按成本从低到高)
- 翻响应自带元数据:部分接口响应自带字段码+中文名,
--raw 拉一次全收(sycm 侧 data.columns 是典范)。
queryFieldIn 试探:把候选字段码塞进 --fields 发请求 —— 返回带值=接口认,没返回=不认,试错零损失。万相台报表接口就是"你报字段名、有就发货"的点单式接口。
- 网页对照:登录网页找到目标指标,两条路——(a) 表头 DOM 的
mx-stickytable-drag 属性直接写着字段码(2026-07-20 用它破了 naturalPayAmt 自然流量转化金额 / orgNaturalPv 自然流量曝光量);(b) 拿网页显示数值当答案纸,--raw 全量拉回后按数值反查字段码。
写回规矩(越用字典越厚)
三招探出新字段并实测核准后,必须以 status: verified 写回 fields.json,note 附验证日期与方法;试探失败的候选也记一笔(status: rejected + note),防后人重复踩。fields.json 永不含真实数值/店名,可进公共库。
坑规矩(口径警告,写数前必看)
- 自然流量两列(
naturalPayAmt/orgNaturalPv):T-2 之前的日期才可信,最新 1-2 天恒为 0(归因未完成)。
- 单日成交/ROI 归因未完成会偏低:复盘查 7 天+区间(
scene-summary/scene-daily 默认已 14 天)。
配套工具:sycm-cli(生意参谋店铺数据体检)同样有 fields.json 字典 + 三招方法论,广告×自然流量交叉复盘两个一起用。
安全护栏
硬约束(真正的风险信号才停):
| 项 | 默认值 | 触发后 |
|---|
| 连续失败次数上限 | 2 | 立即停,不重试 |
| 风控关键词 | "滑块/验证码/操作过于频繁/请重新登录" | 抛 RiskTriggered 退出码 2 |
| 夜禁时段 | 1:00 – 6:00 | 阻止运行;ALIMAMA_BYPASS_CURFEW=1 可绕 |
软建议(不停止,只 stderr 提示):
| 项 | 默认值 | 触发后 |
|---|
| 请求间隔(随机抖动) | 1.8 ~ 3.5 秒 | 自动等待 |
| 累计请求软警告点 | 200 次 | stderr 提醒一次,继续运行 |
| 可选硬上限 | 无(默认不启用) | 设 ALIMAMA_REQUEST_LIMIT=N 启用,达到 N 次停 |
风控按"短时高频"判定,不按总量 — 日常批量拉报表无问题。
绝不调用任何含 add/create/modify/update/delete/save/batch 的接口。
典型 AI 代理调用示例
场景 1:用户问"昨天广告效果怎么样"
DATE=$(date -v-1d +%Y-%m-%d)
~/.claude/skills/alimama-cli/scripts/alimama.sh charge-summary --date $DATE --out /tmp/wxt-$DATE.json
~/.claude/skills/alimama-cli/scripts/alimama.sh report-campaign --date $DATE --limit 10 --out /tmp/wxt-camp-$DATE.json
场景 2:用户问"现在哪些关键词推广计划在跑"
~/.claude/skills/alimama-cli/scripts/alimama.sh promo-keyword --limit 30 --out /tmp/promo-kw.json
场景 3:用户问"找出在投但 ROI < 1 的计划(赔本货)"
DATE=$(date -v-1d +%Y-%m-%d)
~/.claude/skills/alimama-cli/scripts/alimama.sh report-campaign --date $DATE --limit 100 --raw \
| jq '[.data.list[] | select(.charge > 50 and .roi < 1)]'
故障排查
| 现象 | 原因 | 处理 |
|---|
doctor 报"未找到 alimama 登录态" | Chrome 没登录 one.alimama.com | 去 Chrome 打开 one.alimama.com 一次 |
| Windows 首次运行打开 Chrome/Edge | 正在创建 CLI 专用登录环境 | 登录一次,CLI 会自动检测并继续;以后无需重复登录 |
| Windows 等待登录超时 | 5 分钟内没有完成登录 | 保留浏览器窗口,登录后重新运行;可用 ALIMAMA_LOGIN_TIMEOUT 调整秒数 |
| Windows 找不到浏览器 | Chrome/Edge 未安装在常规位置 | 设置 ALIMAMA_BROWSER_PATH 指向 chrome.exe 或 msedge.exe |
Windows 报 AppData\Roaming\uv\python: 拒绝访问 | AI 沙箱只允许访问工作区 | 更新 CLI 后运行 scripts\alimama.cmd doctor;新版会把 Python、依赖和登录 Profile 放在项目内 |
| 任意子命令返回 list:[] 但 count > 0 | 缺关键参数(如 orderBy) | CLI 已内置正确参数,正常不会遇到 |
RiskTriggered: 滑块 | 触发风控 | 立即停 24 小时,不要重试 |
| HTTP 5810 / "需要登录" | session 超时 | 去 Chrome 重新打开 one.alimama.com |
| 报错 "夜间禁跑" | 当前 1:00–6:00 | ALIMAMA_BYPASS_CURFEW=1 alimama-cli ... |
反编译笔记(接口情报)
来自 https://g.alicdn.com/mm/onebp/<version>/onebp/merge.js 和实际 HAR 抓包。
统一鉴权:
- macOS Cookie 从本机 Chrome 直读;Windows 从 CLI 专用 Chrome/Edge 的 CDP 读取
- Windows 的
.runtime/ 包含登录 Profile,已被 Git 忽略;不要提交、打包或分享
- 所有 POST 自动注入 URL
?bizCode=universalBP&csrfId=xxx
- csrfId 启动时一次性
POST /member/checkAccess.json 拿,进程内缓存
- 无动态 sign,无 WASM 加密(跟 sycm 同档简单)
关键接口映射:
- 报表通用入口:
POST /report/query.json + rptType + queryDomains
- 营销场景汇总:
POST /report/chargeSum.json
- 推广列表通用:
POST /campaign/horizontal/findPage.json?bizCode=X
详细字段说明见 README.md(含完整模块树)。
局限性
- 只覆盖读接口;操作类(创建/调价/暂停)故意不做(避免误操作烧钱)
- 推广类只做了 3 种(关键词/人群/全站),其他(店铺直选/内容营销/智惠券)未做
- 部分 row 字段(如
bidUnit)服务端可能返回 None,CLI 已处理但不保证完美
- 不同
--window(1/7/15 天)会影响转化数据,默认 15