DataBrain 舆情指标查询助手。把游戏舆情/口碑/声量/情感/评分/KOL/直播/新闻/热门视频/热门帖子/Hashtag/热梗/竞品/官号 等问题,以及**游戏广告投放素材/创意取数**(创意数·素材数·素材类型·渠道·国家维度·素材明细列表)翻译成可执行的 BigQuery SQL,覆盖 opinion / intelligence / marketing_hub schema。**只支持游戏维度查询**,不支持公司/开发商/发行商聚合舆情;**不支持** UA 预算/团队规模/未来预测/决策建议/因果归因等问题,遇到会主动路由或拒绝。纯运营指标(独立的下载/收入/DAU/留存)归 databrain-intelligence;但**素材趋势 × DAU/下载时间对齐对比**在本 skill 素材 reference 内有限支持。触发关键词:舆情、口碑、声量、mentions、情感、sentiment、Brand Health、品牌健康度、评分、score、Steam/AppStore/GooglePlay/Xbox/PS/Metacritic/OpenCritic、好...
Scanned 9/8/2026
Install to Claude Code
npx -y skills add infometa/workbuddyskills --skill databrain-opinion-metrics-service --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Databrain Opinion Metrics Service?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/infometa-databrain-opinion-metrics-service)More formats (shields.io, HTML) on the badges page.
---
name: databrain-opinion-metrics-service
description: "DataBrain 舆情指标查询助手。把游戏舆情/口碑/声量/情感/评分/KOL/直播/新闻/热门视频/热门帖子/Hashtag/热梗/竞品/官号 等问题,以及**游戏广告投放素材/创意取数**(创意数·素材数·素材类型·渠道·国家维度·素材明细列表)翻译成可执行的 BigQuery SQL,覆盖 opinion / intelligence / marketing_hub schema。**只支持游戏维度查询**,不支持公司/开发商/发行商聚合舆情;**不支持** UA 预算/团队规模/未来预测/决策建议/因果归因等问题,遇到会主动路由或拒绝。纯运营指标(独立的下载/收入/DAU/留存)归 databrain-intelligence;但**素材趋势 × DAU/下载时间对齐对比**在本 skill 素材 reference 内有限支持。触发关键词:舆情、口碑、声量、mentions、情感、sentiment、Brand Health、品牌健康度、评分、score、Steam/AppStore/GooglePlay/Xbox/PS/Metacritic/OpenCritic、好评率、KOL、网红、创作者、博主、直播、Streaming、Hours Watched、Peak CCV、新闻、News、PR、Google Trends、热门视频、热门帖子、Trending、Hashtag、TikTok、Meme、热梗、官号、Official Account、Earned、竞品、Competitor、话题、Topic、关键词、词云、市场热度、Channel Share、社媒、互动、Engagement、观看、Views、发帖、Publications、潜在曝光、Impressions、玩家评价、玩家讨论、Steam 评论、商店评论、投放素材、广告素材、广告创意、creatives、materials、素材库、素材列表、广告列表、创意数、素材数、新增素材、新增创意、素材类型、素材分布、渠道创意数、渠道占比、素材曝光、素材互动、素材评分、素材榜单、TopN 素材、国家投放、多游戏素材对比、竞品素材、视频标签"
---
# DataBrain Opinion Metrics Service
把游戏舆情问题翻译成 **BigQuery SQL**,通过 DataLab `/api/v1/datalab/skill/exec_sql` 接口执行。
---
## Upstream Contract(上游契约)
react_agent_service 的 system prompt 通过 `game_info_by_name` 提供 `game_name` / `game_type`(pc/console/mobile) / `release_dates_by_platforms` / `game_business_model`(paid/free),但**不直接给 BigQuery 用的 ID**。首次必须用 `scripts/game_search.py` 把名字解析成 `unified_edition_id`(舆情主表过滤键)/ `mobile_id` / `pc_id` / `console_id` / `combine_id`(见 [Phase 1.5](#phase-15--解析游戏-id))。**若 ID 已在对话历史/上下文中出现,直接复用,不要再调 `game_search.py` 验证。**
---
## ⚠️ 最重要的一条规则:`opinion.public_feeds` 聚簇键 + 分区键
`opinion.public_feeds` 物理上是 **VIEW**(自身无 BQ partition/cluster),但底层是亿级 `base_feeds`。**每条读它的 SQL 必须同时带**:
1. **游戏过滤(等价聚簇键)**:`WHERE unified_edition_id = '<game_id>'`(或 `IN (...)`)— **绝不能省**
2. **时间过滤(等价分区键)**:`AND comment_time >= TIMESTAMP('<start>') AND comment_time < TIMESTAMP_ADD(TIMESTAMP('<end>'), INTERVAL 1 DAY)`
缺任一个 → 必然 **61001 timeout** + 浪费配额(连"探查字段"也要带一个具体 id + 一周窗,不要写 `SELECT * FROM opinion.public_feeds LIMIT 1`)。该约束对所有基于 `base_feeds` 的视图(`hotness` / `feeds_topic` / `game_store_reviews` / `video_and_posts_*` / `official_account_*`)同样生效。
---
## Runtime Environment Variables
| 变量名 | 必填 | 默认值 | 说明 |
|--------|------|--------|------|
| `DATABRAIN_TOKEN` | **是** | — | 认证 token 原始值(**不含 `Bearer ` 前缀**,脚本自动拼接,由平台环境变量注入) |
| `DATABRAIN_HOST` | 否 | `https://databrain.intlgame.com` | DataBrain API host;只接受可信域名 |
| `DATABRAIN_DISPLAY_HOST` | 否 | — | 回答中展示链接的 host(如 `https://databrain.woa.com`) |
---
## Hard Constraints
- **只读**:仅 `SELECT` / `WITH ... SELECT`,禁止 `INSERT` / `UPDATE` / `DELETE` / `DROP` / `CREATE` / `MERGE` / `TRUNCATE`(脚本会预先拦截)
- ⚠️ **时间 UTC+8(北京时间)锚定**:
- `today` 来源 = system prompt 顶部已注入的 `当前时间`(该值已是 UTC+8 北京时间),**直接取其日期部分作为 `today`**;**仅当上下文里没有该字段时**,才回退 `python scripts/now_beijing.py`。无需为每条查询都跑脚本。
- **禁止**在 SQL 中出现 `CURRENT_TIMESTAMP()` / `CURRENT_DATE()` / `CURRENT_DATETIME()`(含 `TIMESTAMP_SUB(CURRENT_TIMESTAMP(), ...)` 等组合)—— BQ 走 UTC 服务时钟,与业务北京时间错位最多 8h、窗口边界会截尾(实测 NIKKE 近 5 天声量偏差 23%)。一律从 `today` 自算纯字面量窗口。
- **不加 `'Asia/Shanghai'` 时区参数**:数据按"北京时间字面量灌库",加了反而 -8h 错位。
- 完整窗口算法 + 右开窗口/闭区间规则见 [Phase 2.3 时间词翻译速查表](#23-时间词翻译速查表写错就时间窗错位);回答披露口径见 [Phase 4](#phase-4--输出契约output-contract)。
- **`opinion.public_feeds` 必带聚簇键 + 分区键**(见上方红框):单缺一个就 61001 timeout,双缺必跑死。
- **其他事实表也要带真实分区/聚簇/高选择性过滤**,缺失触发 **61001 timeout**。常见分区/时间字段:
- `opinion.public_feeds.comment_time` (TIMESTAMP, DAY 业务约定 — VIEW 无 BQ partition) — 同时要 `unified_edition_id` 聚簇
- `opinion.kol.date` (DATE, **MONTH**:`DATE_TRUNC(date, MONTH)`) — 聚簇 `unified_edition_id, date`
- **`opinion.media_account_publishing.date` (DATE, DAY 分区) — 聚簇 `unified_edition_id`**(官号汇总数字主事实表)
- `opinion.media_account_audience.date` (DATE) — 聚簇 `unified_edition_id`(仅粉丝数)
- `intelligence.news_details.release_time` (**DATETIME**, **MONTH**:`DATETIME_TRUNC(release_time, MONTH)`) — 聚簇 `unified_edition_id, release_time`
- `intelligence.game_metric_streamhatchet_*`:无物理分区;`_uid` 版聚簇 `date`,原版聚簇 `date, app_id`
- `opinion.store_score_*.create_time`(**DATETIME**,不是 TIMESTAMP!);`opinion.store_score_*_daily` 分区字段是 **`date`**(不是 `create_time`)
- marketing_hub / meme 系(`marketing_hub_video` 聚簇 `video_url`、`marketing_hub_hashtag_video.video_release_time` DATETIME·MONTH、`marketing_hub_hashtag_trending_*.date`·`hashtag_kol.date` DATE·MONTH、`opinion.meme_videos.release_time` TIMESTAMP·MONTH)— 详见各自 reference
- **总要带 LIMIT**:默认 1000,最大 5000;TopN 类查询用具体 N
- **每个 FROM 都用 `schema.table`** 或全限定 `tencent-databrain-prod.<schema>.<table>`;不要用裸表名
- **`opinion.public_feeds.country = 'global'` 占 70%+**:按国家过滤前在回答中说明覆盖率,避免误导(详见 [geo_competitor.md](references/auxiliary/geo_competitor.md))
- **`feeds.organization` 字段不存在**:区分官号/玩家请通过 `feeds_author.is_official_account = 1` 反查(详见 [social_filter_logic.md](references/auxiliary/social_filter_logic.md) §3.1)
---
## ⚠️ 指标 → 底表 决策表(**写错就数值偏差 1.x ~ 87 倍**)
业务 UI 上的数字背后是 cube view 路径,对应到 BigQuery 物理表。**指标族不同,底表不同,混用会严重偏差**:
| 指标族 | 底表 | reference | 关键说明 |
|---|---|---|---|
| **官号互动量 / 转发 / 观看 / 发帖 / 评论 / 点赞量** | `opinion.media_account_publishing` ⭐ | [official_account_metrics.md](references/official_account_metrics.md) §3-§6 | 物理表已预聚合"官号 × 日 × 渠道",**不需 JOIN feeds_author**;engagement 第 4 项用 `unlike_number`(非 `tweets_unlike`) |
| **官号粉丝数** | `opinion.media_account_audience` | [official_account_metrics.md](references/official_account_metrics.md) §7 | 唯一例外,仍走 audience 表 |
| **网红/KOL 发帖数** | `opinion.public_feeds` + LEFT JOIN `feeds_author`(排除官号) | [kol.md](references/kol.md) §2 | hotness 路径;`channel_type='social'`、`comment_parent_id='-1'`、`a.is_official_account IS NULL OR != 1` |
| **活跃 KOL 数 / KOL 观看 / 互动 / 榜单 / 粉丝** | `opinion.kol` + base_kol 4 条硬过滤(`follower_number>0`/`posts>0`/`channel_name != 'reddit'`/排除官号 KOL) | [kol.md](references/kol.md) §3-§5 | ⚠️ **不支持 Reddit**(底表无数据) |
| **声量 / 情感 / Brand Health / 互动 / 曝光 / 发帖人数(creators) / 视频播放量** | `opinion.public_feeds` | [public_feeds.md](references/public_feeds.md) | `creators` / `publications` 必带 `channel_type='social'` |
| **商店评分** | `opinion.store_score_*_daily` / `_*` | [stores/](references/stores/) | 默认"全局加权平均一个数" `SUM(comments_number * store_score) / SUM(comments_number)` |
| **App Store / Google Play 新增评论数** | `opinion.public_feeds` + `channel_type='comments'` + `channel_name IN ('app store','google play')` | [stores/app_store.md](references/stores/app_store.md) §3 | **不**走 store_score_*_daily 累计差 |
| **新闻情感 / News Brand Health / News Engagement** | `intelligence.news_details` | [pr_news.md](references/pr_news.md) | 时区 UTC+8,`release_time` 是 **DATETIME**(不是 TIMESTAMP) |
| **直播 Hours Watched / Peak CCV** | `intelligence.game_metric_streamhatchet_*` | [streaming.md](references/streaming.md) | |
| **官号发的帖子列表 / 单帖详情 / URL** | `public_feeds + feeds_author A 路 JOIN` | [public_feeds.md](references/public_feeds.md) §5.2 | 要"内容"不是"数字"才走这里 |
### 关键原则
1. **要数字** → cube 路径(`media_account_publishing` / `media_account_audience` / `store_score_*` / `news_details`)
2. **要内容/列表** → `public_feeds + A 路 JOIN`
3. **网红"发帖数"vs"活跃 KOL"二选一** → 看 [kol.md](references/kol.md) §0 决策树
4. **广告投放素材/创意指标** → 与舆情底表**完全无关**,走 [`references/creative/`](references/creative/)(`intelligence.dwd_aix_gd_analysis_*` / `dwd_aix_gd_material`),**绝不**用 `opinion.public_feeds`
---
## ⚠️ channel_name 真实底层枚举(**写错就 0 行无报错**)
三套底表(`public_feeds` / `opinion.kol` / `media_account_publishing`)枚举一致。**最易写错的 4 个**:
| 用户说 | 真实 `channel_name` |
|---|---|
| YouTube | `'youtube_keyword'` ⚠️ **不是 `youtube`** |
| Twitch | `'twitch_keyword'` ⚠️ **不是 `twitch`** |
| Google Play | `'google play'` ⚠️ **带空格** |
| App Store | `'app store'` ⚠️ **带空格** |
其余取小写原名:`twitter`(X) / `tiktok` / `facebook` / `instagram` / `reddit`(⚠️ kol 表无) / `bilibili` / `douyin` / `kuaishou` / `xiaohongshu` / `tieba` / `nga`。**统一规则**:一律 `LOWER(channel_name) IN ('<value>')`,**禁止** `channel_name = 'youtube'` 等值匹配(会得 0 行)。完整表见 [auxiliary/dim_tables.md](references/auxiliary/dim_tables.md) §3。
---
## ⚠️ 跨平台游戏 PC vs Mobile ID 决策(**写错就 0 行无报错**)
对**既有 PC/Console 又有 Mobile** 的游戏,`game_search.py` 默认 `entity_type` 不一定对应舆情数据实际存放端:
- **PC-leading(数据在 PC id,前缀 e)**:Fortnite / Apex Legends / Forza Horizon 5 / Diablo IV / Warframe / HELLDIVERS 2 / Hunt Showdown 1896 / Dying Light 2 / FragPunk / Naraka 端游版 等 → `game_search.py "<game>" --type pc`
- **Mobile-leading(手游主导,前缀 u)**:Genshin Impact / Uma Musume / MLBB / Brawl Stars / PUBG MOBILE / 王者荣耀 / Garena Free Fire / Pokémon TCG Pocket / 燕云十六声 / Whiteout Survival 等 → mobile id
- **不确定时 probe**:两端各 `COUNT(*)` 近 7 天,取行数大的端(probe SQL 模板见 [auxiliary/id_mapping.md](references/auxiliary/id_mapping.md) §3.5)。
> ### ⚠️ 单指标查询禁止 PC + Mobile 跨端 UNION
>
> 游戏同时存在 PC 和 Mobile `unified_edition_id` 时,**单一指标查询必须只用一个 game_id**,禁止 `IN ('<mobile>','<pc>')` / 两端 `UNION ALL` 后 `SUM`·`AVG` / 任何变体"两端合并"(都会偏离业务单端 GT)。判定顺序:
>
> 1. 问句带 **PC 限定词**(`PC 端` / `端游` / 主机端 / `Steam` / Epic / 主机版)→ PC id(`--type pc`)
> 2. 问句带 **Mobile 限定词**(手游 / 移动端 / iOS / Android / App Store / Google Play)→ Mobile id(`--type mobile`)
> 3. 未指明端 → 用 `game_search.py` 默认返回端 id;若该端主表 0 行疑似用错端,按 probe 切换重试,**而不是**改写成 UNION 两端合查。
>
> 必要的"端游 + 手游全端合查"是另一种语义(业务很少需要),如必要须在回答**显式标注**跨端聚合并征求确认。
---
## Core Scripts
| 脚本 | 用途 |
|------|------|
| `scripts/game_search.py` | **首次解析必跑**(ID 已在历史则复用):游戏名 → `mobile_id` / `pc_id` / `console_id` / `combine_id` / `entity_id` + `game_id` 顶层兼容字段 |
| `scripts/execute_sql.py` | 执行只读 BigQuery SQL,返回结果 |
| `scripts/now_beijing.py` | **兜底**:仅当上下文缺 `当前时间` 时用它拿 UTC+8 `today` |
```bash
python scripts/game_search.py "Genshin Impact" # auto-fallback by entity_type
python scripts/game_search.py "Counter-Strike 2" --type pc
python scripts/game_search.py "miHoYo" --type company # 公司/开发商/发行商
OUTPUT_JSON=1 python scripts/game_search.py "Dune: Awakening" # 纯 JSON
python scripts/execute_sql.py --sql "<your SQL here>"
python scripts/execute_sql.py --schema intelligence --sql "<SQL on intelligence schema>"
python scripts/execute_sql.py --sql_file /large_tool_results/query.sql
```
> **重要**:SQL 必须通过 `--sql "..."` 或 `--sql_file <path>` 传入;裸位置参数会被 argparse 拒绝并报 `unrecognized arguments`。
---
## Workflow
> **总览**:Phase 0 路由 → Phase 1 加载 reference → Phase 1.5 解析 ID → Phase 2 写 SQL → Phase 3 执行 → Phase 4 输出契约。
### Phase 0 — 路由(先判断要不要做、能不能做)
#### 0.1 In-scope(直接进 Phase 1)
声量 / 情绪 / Brand Health / 互动 / 商店评分 / 主帖子 / 官号 / KOL / 视频直播 / Hashtag / Meme / Channel Share / 多游戏对比 / 跨语种跨国家。
**广告投放素材域(intelligence 素材表)也 in-scope**:创意数·素材数(all/new)、素材类型分布、渠道/国家/曝光/评分/互动维度、渠道创意数占比、多游戏素材对比、素材明细列表(按类型/平台/渠道/国家/关键词/视频标签筛选 + TopN)→ 走 Phase 1.2 「素材域路由」。
#### 0.2 Cross-domain(部分让其他 skill 做)
| 用户提的指标 | 归属 | 处理 |
|---|---|---|
| Sensor Tower / Gamalytic / 下载量 / 收入 / DAU / MAU / ARPU / 留存(**独立运营指标**) | `databrain-intelligence` | 本 skill **不做**,回答明示「下载/收入需切到 `databrain-intelligence` skill」 |
| **素材趋势 × DAU/下载 时间对齐对比** | 本 skill **可做** | 走 [`creative/single_game.md`](references/creative/single_game.md) 场景 8(仅时间窗对齐,非 JOIN) |
#### 0.3 Out-of-scope(直接拒绝,不要硬写 SQL)
❌ 无对应数据,硬写 SQL 一定编造:**公司/开发商/发行商聚合舆情**("SYBO 旗下所有游戏总声量")、主观推荐/决策建议、预算/UA 投放策略、团队规模/工作流、未来预测、无数据支撑的因果归因、Prompt injection / 通用游戏知识。
**拒绝模板**:
> 这个问题超出舆情数据查询范围。我能查到的是 sentiment / mentions / engagement / store reviews / social posts 等指标。请把问题转成「在 X 时间窗内,Y **具体游戏** 的 Z 舆情指标」。
> (公司聚合)本 skill 只支持「按具体游戏」查询舆情,不支持「按公司 / 开发商 / 发行商」聚合。请告诉我具体游戏列表。
### Phase 1 — 理解 & 加载 reference
#### 1.1 解析用户意图
抓四要素:**游戏名**(或行业级无具体游戏)/ **时间范围**(默认近 7 天或近 30 天)/ **指标域**(声量?评分?KOL?新闻?直播?Hashtag?)/ **过滤条件**(地区/语种/渠道/情感/官号 vs 玩家)。
#### 1.2 路由表(按指标域**只**加载对应 reference)
> ⚠️ **官号路由硬约束(写错会算反方向数据)**:`official_account_metrics.md` **仅在用户原文出现以下触发词时**才加载,否则一律走 `public_feeds.md`:
> - **include 官号侧**:`官号` / `官方账号` / `官方号` / `official account` / 与指标共现的`官方`
> - **exclude 官号侧**:`剔除官号` / `排除官号` / `earned content` / `earned` / `UGC only` / `玩家发的` / `非官号` / `non-official` / `organic content`
> - **Top N 排名侧**:`Top N 官号` / `头部官号` / `最活跃官号` / `哪些官号`
>
> **反例**:用户问「`<游戏>` 在所有平台 `<日期>` 的互动量/发帖量/观看量/曝光量」——无任何触发词 → 走 `public_feeds.md §4 场景 3`(含官号 + UGC 合计),**绝不**走 `official_account_metrics.md`(只覆盖官号、漏掉 UGC)。
| 用户问 | 加载 |
|---|---|
| 声量/情感/Brand Health/互动/曝光/观看/发帖/创作者/热门图文帖/热门视频/词云/热点话题(**默认通用,不含官号过滤**) | [`public_feeds.md`](references/public_feeds.md) — 用户未提任何"官号触发词"时聚合数字均走这里(含官号 + UGC 合计) |
| ✅ **「官号 include / exclude」聚合指标**:官号互动量/发帖量/观看量/粉丝数/Top N 官号/Earned Content 剔除官号 | [`official_account_metrics.md`](references/official_account_metrics.md) — **必须命中上方触发词才加载**。官号汇总数字走 **`opinion.media_account_publishing`**;粉丝数走 `media_account_audience`;Earned Content(exclude 官号)走 `public_feeds + feeds_author` LEFT JOIN(§8) |
| 官号帖子列表/单帖详情/按内容筛选官号 feed(**列表而非数字**) | [`public_feeds.md`](references/public_feeds.md) §5.2(A 路:`feeds_author.is_official_account = 1` JOIN) |
| ✅ **按具体游戏查视频/视频播放量/视频数/视频互动** | [`public_feeds.md`](references/public_feeds.md) §场景 5 — `unified_edition_id` + `media_type IN ('video','live')` + `tweets_view`;**绝不**走 `marketing_hub_video`(无 game_id 字段) |
| KOL / 创作者榜单 / 分档 / 合作创作者 | [`kol.md`](references/kol.md) |
| 直播 / Hours Watched / Peak CCV / Avg CCV / 主播 | [`streaming.md`](references/streaming.md)(含 _uid 优先策略,必要时反链 [`id_mapping.md`](references/auxiliary/id_mapping.md)) |
| 新闻 / PR / News Brand Health | [`pr_news.md`](references/pr_news.md) |
| Google Trends 关键词热度 | [`googletrends.md`](references/googletrends.md) |
| Meme / 热梗 / 文化趋势 | [`memes.md`](references/memes.md) |
| Channel Share Ranking / 市场热度 | [`market_popularity.md`](references/market_popularity.md) |
| Hashtag / TikTok / 行业视频 / Hashtag KOL | [`marketing_hub.md`](references/marketing_hub.md) — ⚠️ **仅限"不绑定单一游戏"的行业级查询**;按具体游戏查视频走 public_feeds.md §场景 5(本 reference 8 张表均无 game_id 字段) |
| 商店评分(按平台分流) | [`stores/<platform>.md`](references/stores/)(见下文「商店评分场景」) |
| 字段名困惑 / channel/language/topic/keyword/官号 mapping | [`auxiliary/dim_tables.md`](references/auxiliary/dim_tables.md) |
| 国家/地区/竞品/运营事件 mapping | [`auxiliary/geo_competitor.md`](references/auxiliary/geo_competitor.md) |
| ID 体系 / 哪表用哪 ID / unified_id↔app_id 转换 / 手游店 vs PC 店过滤键 / 直播侧 ID 选择 | [`auxiliary/id_mapping.md`](references/auxiliary/id_mapping.md) |
| Include/Exclude/官号识别业务逻辑 | [`auxiliary/social_filter_logic.md`](references/auxiliary/social_filter_logic.md) |
| 找不到字段 / 完整 schema | [`auxiliary/cube_schema.md`](references/auxiliary/cube_schema.md) |
| **广告投放素材 / 创意取数** | [`references/creative/`](references/creative/) — 三选一见下方「素材域路由」 |
> **黄金法则**:reference 里的 SQL 是经 BigQuery 全量验证的(多标注 "5/5 PASS" 等)。**先看 reference 找模板,再做最小化改写**,避免凭空写 SQL。
#### 素材域路由(which-of-3,命中素材类问题时**只**加载一个)
素材表是 `intelligence` schema,与舆情 `opinion.public_feeds` 完全不同;**不要**用 public_feeds 模板。
| 用户问的形态 | 加载 |
|---|---|
| 要**单条素材/广告明细列表**(按类型/平台/渠道/国家/关键词/视频标签筛选 + TopN) | [`creative/materials_list.md`](references/creative/materials_list.md)(主表 `intelligence.dwd_aix_gd_material`,CLUSTER BY `game_id`) |
| 要**单个游戏聚合数字/趋势**(创意数·素材数 Total、趋势、类型分布、DoD·SDLW、多维 TopN、素材 vs DAU 对比) | [`creative/single_game.md`](references/creative/single_game.md)(`dwd_aix_gd_analysis_creatives` / `_stats`) |
| 要**多游戏(1–15)横向对比**(overview/timeline/sum/国家×游戏矩阵/占比/环比) | [`creative/multi_game.md`](references/creative/multi_game.md)(同两张分析表 + `dim_aix_gd_games`) |
> 判定优先级:先看**要不要单条素材明细**(要 → materials_list);只要聚合再看**游戏数量**(单个 → single_game;多个 → multi_game)。
#### 商店评分场景(按用户问的商店动态分流)
- **Steam** → [`stores/steam.md`](references/stores/steam.md)(详,含 `_daily` + `_by_language_hourly`)
- **App Store** → [`stores/app_store.md`](references/stores/app_store.md)(详) / **Google Play** → [`stores/google_play.md`](references/stores/google_play.md)(详)
- **TapTap / Xbox / PlayStation / Meta Store / Metacritic / OpenCritic** → 对应同名简版 md
- "全平台对比" → 按提到的商店分别加载并 UNION
⚠️ **关键陷阱**:手游店(App Store / Google Play / TapTap)用 `unified_id` 列;PC/Console 店(Steam / PlayStation / Xbox / Metacritic / OpenCritic)用 `edition_id` 列。**写错就 0 行无报错**。详见 [`id_mapping.md`](references/auxiliary/id_mapping.md)。
#### 辅助表查询指引
字段名/mapping 困惑 → `dim_tables.md`;国家/竞品/运营事件 → `geo_competitor.md`;用哪个 ID → `id_mapping.md`;官号识别/Include·Exclude → `social_filter_logic.md`;都找不到 → `cube_schema.md`(兜底)。
### Phase 1.5 — 解析游戏 ID
> **若游戏 `unified_edition_id` 等 ID 已在对话历史/上下文中出现,直接复用,不要再调 `game_search.py` 验证。** 仅首次解析或历史无 ID 时才跑脚本。
```bash
python scripts/game_search.py "<game name>" [--type mobile|pc|console]
```
输出含 `game_id`(= unified_edition_id,前缀 u/e) / `mobile_id`(= unified_id) / `pc_id`(= edition_id) / `console_id` / `combine_id`(= combined_id) / `entity_id`(公司) / `match_score`。
**ID 选择决策**(详见 [`id_mapping.md`](references/auxiliary/id_mapping.md)):
- 舆情主表 `opinion.public_feeds` / `kol` / `feeds_author` / `news_details` → `WHERE unified_edition_id = '<game_id>'`
- 手游商店(App Store / Google Play / TapTap)→ `WHERE unified_id = '<mobile_id>'`
- PC/Console 商店(Steam / PlayStation / Xbox / Meta Store / Metacritic / OpenCritic)→ `WHERE edition_id = '<pc_id 或 console_id>'`
- 直播 `_uid` 表(优先)→ `WHERE id = '<mobile_id 或 pc_id>'`;原版表 → `WHERE app_id IN (SELECT app_id FROM common.unified_ids WHERE unified_id = '<game_id>')`
- **素材分析表**(`dwd_aix_gd_analysis_creatives` / `_stats` / `dwd_aix_gd_material`)→ `WHERE game_id = '<game_id>'`
- **素材 vs DAU 对比**(single_game 场景 8):mobile → `game_metric_sensortower_*_uid` 用 `id` 列;pc/console → `game_metric_ampere_daily_cid` 用 `edition_id` 列
提示:`match_score = 666666` 是精确匹配;分数低 → `--top 3` 查候选;中文名建议指定 `--type`;内部代号/Demo 若非 666666 → 回头问用户正式名。
### Phase 2 — 生成 SQL
#### 2.1 从 reference 找最近的模板
大多数 reference 按"场景"组织 SQL;找最匹配的场景,把 `<game_id>` / `<start_date>` / `<end_date>` / `<channel_code>` 占位符替换成实际值。
#### 2.2 通用 SQL 规则
- **BigQuery 方言**:`DATE_SUB`、`DATETIME_TRUNC`、`SAFE_DIVIDE`、`COUNTIF`、`QUALIFY ROW_NUMBER()`、`UNNEST`、`MAX_BY`、`ARRAY_AGG` 都可用
- **GROUP BY 严格**:每个非聚合 SELECT 列必须出现在 GROUP BY(原样重复或序数 `GROUP BY 1, 2`)
- **窗口函数不能嵌套在聚合内**:`SUM(ROW_NUMBER() OVER(...))` 报错,需拆成两层子查询;`QUALIFY ROW_NUMBER() OVER (...) = 1` 取每组 Top1
- **情感映射统一口径**:`sentiment_rating IN (1,2)`=negative,`=3`=neutral,`IN (4,5)`=positive,`-1`=未打分。`positive_rate/negative_rate/avg_sentiment` 分母都是 `mentions`(**含 `-1`**),**默认不要在 WHERE 排除 `sentiment_rating = -1`**。
- **互动量 4 项累加 + 负值清洗(必用 CASE 形式)**:`SUM((CASE WHEN tweets_retweet>=0 THEN tweets_retweet ELSE 0 END) + (CASE WHEN tweets_reply>=0 THEN tweets_reply ELSE 0 END) + (CASE WHEN tweets_like>=0 THEN tweets_like ELSE 0 END) + (CASE WHEN tweets_unlike>=0 THEN tweets_unlike ELSE 0 END))`。写成 `SUM(IF(x<0,0,x) + IF(...))` 是错的(任一字段 NULL 整行被跳过偏小)。
- **曝光(potential_impressions)严格 > 0**:`SUM(CASE WHEN follower_number > 0 THEN follower_number ELSE 0 END)`
- **NLP `topics` 大小写归一化**:聚合时一律 `UPPER(t)` / `LOWER(t)`,否则 `'AI'` vs `'Ai'` 拆两行漏 30-50%
- **取最新快照**:`MAX_BY(<field>, create_time)` / `MAX_BY(<field>, insert_time)`
- **CTE vs 子查询**:DataLab 支持 `WITH`;遇 `Table not found` 误报时改内联子查询 `FROM (SELECT ...) t`
#### 2.3 时间词翻译速查表(**写错就时间窗错位**)
> ⚠️ `today` 来源 = system prompt 顶部注入的 `当前时间`(已是 UTC+8 北京时间),取其日期;上下文无该字段才回退 `python scripts/now_beijing.py`。**凡涉及"今天/最近/本周/本月"等相对时间一律以 `today` 为"当前日期"**算窗口;**绝不**用 `CURRENT_TIMESTAMP()` / `CURRENT_DATE()` / `CURRENT_DATETIME()`(BQ 走 UTC 服务时钟,错位最多 8h,实测 NIKKE 近 5 天声量偏差 23%)。下表示例设 `today = 2026-05-30`(周六)。
>
> ⚠️⚠️ **TIMESTAMP/DATETIME 字段 vs DATE 字段写法不同,混用会丢数据**:`comment_time` / `release_time` / `create_time` 是 TIMESTAMP/DATETIME 字段,**必须右开**:`>= TIMESTAMP('<start>') AND < TIMESTAMP_ADD(TIMESTAMP('<end>'), INTERVAL 1 DAY)`(DATETIME 列用 `DATETIME_ADD`)。**绝不能写 `<= TIMESTAMP('<end>')` 或 `BETWEEN TIMESTAMP(start) AND TIMESTAMP(end)`** —— `TIMESTAMP('<end>')` = `<end> 00:00:00`,闭区间只命中午夜一瞬、**丢掉 `<end>` 当天全部带时刻数据**(单日点查塌成 0~个位数,实测 9 条 bad case 全因此偏小,如 Royal Match 单日声量 216→1)。**只有 DATE 字段**(`opinion.kol.date` / `media_account_publishing.date`)才用 `BETWEEN DATE('<start>') AND DATE('<end>')`(闭区间正确)。
| 用户说 | 算法(基于 `today`) | 翻成(today=2026-05-30)|
|---|---|---|
| 今天 / today | DATE 字段 `date = today`;TIMESTAMP 字段右开 | `date = DATE('2026-05-30')`;或 `comment_time >= TIMESTAMP('2026-05-30') AND comment_time < TIMESTAMP_ADD(TIMESTAMP('2026-05-30'), INTERVAL 1 DAY)`(❌ 不要 `<= TIMESTAMP('2026-05-30')`) |
| 昨天 / yesterday | `date = today-1` | `date = DATE('2026-05-29')` |
| 最近 / 近 N 天(含今天)| `[today-(N-1), today]` | 近 7 天 → `BETWEEN DATE('2026-05-24') AND DATE('2026-05-30')` |
| 本周(ISO 周一起算,截止 today)| `[本周一, today]` | `BETWEEN DATE('2026-05-25') AND DATE('2026-05-30')` |
| 上周 / last week | 上一完整 ISO 周 `[周一, 周日]` | `BETWEEN DATE('2026-05-18') AND DATE('2026-05-24')` |
| 本月 / 上月 | `[本月1号, today]` / 上一完整月 | 本月 → `BETWEEN DATE('2026-05-01') AND DATE('2026-05-30')`;上月 → `BETWEEN DATE('2026-04-01') AND DATE('2026-04-30')` |
| 近 30/90 天 / 季度 | 同近 N 天 | 近 90 天 → `BETWEEN DATE('2026-03-02') AND DATE('2026-05-30')` |
| **上线后 / announcement / event 起算** | ⚠️ **必须有具体日期**才写 SQL(先查 `common.app_detail.release_time`,再 `comment_time >= TIMESTAMP(release_time) AND < TIMESTAMP_ADD(TIMESTAMP(release_time), INTERVAL 7 DAY)`);没有就回头问用户,**不要**直接翻成"近 7 天" |
| 近期 / recently | **歧义** → 默认 7 天,输出里说明 |
> ⚠️ **`opinion.store_score_*.create_time` 是 DATETIME 不是 TIMESTAMP**:与 `TIMESTAMP_SUB` / `TIMESTAMP('...')` 比较会报 `No matching signature ... DATETIME, TIMESTAMP`。用 `DATETIME('YYYY-MM-DD')` 字面量(基于 `today` 自算)或字符串 `'2026-01-01 00:00:00'`(隐式转 DATETIME);**不要**用 `CURRENT_DATETIME()`。
### Phase 3 — 执行 & 修复
```bash
python scripts/execute_sql.py --sql "<SQL>" [--schema intelligence]
python scripts/execute_sql.py --sql_file /large_tool_results/query.sql # 长 SQL
```
返回 JSON 含 `code` / `data.columns` / `data.data` / `data.cost_time` / `data.count`。大结果落盘 `/large_tool_results/opinion_sql_<ts>.json`,stdout 只 print 摘要 + 前 N 行。
#### 3.2 错误码速查
完整 `Code / Symptom → Cause → Action` 表(CLI 报错、BigQuery 错误码 61001-61006、`Not found: Table` / `Unrecognized name` / GROUP BY / DATETIME 签名 / RE2 / `row_count:0` 等)见 [`scripts/execute_sql.py`](scripts/execute_sql.py) 顶部 docstring 的「Common errors」小节。
#### 3.3 自动修复循环(≤3 次)
拿到错误 → 找错误码 → 改写 SQL → 重跑;第 3 次仍报错则停止重试,给用户报告根因。
### Phase 4 — 输出契约(Output Contract)
每次回答必须包含:
1. **指标定义**(公式)— 例 `positive_rate = COUNT(DISTINCT IF(sentiment_rating IN (4,5), comment_uin, NULL)) / COUNT(DISTINCT comment_uin)`(分母 = mentions,含 `-1`)
2. **时间窗口**(精确到日/秒 + **UTC+8 北京时间**)— 例 `2025-01-01 ~ 2025-01-07 (UTC+8)`。SQL 时间过滤基于 `today` 自算字面量整日窗口;**不要**加 `'Asia/Shanghai'` 参数,更**不要**用 `CURRENT_TIMESTAMP() / CURRENT_DATE() / CURRENT_DATETIME()`(BQ 服务时钟是 UTC)。回答末尾**必须**单独成行披露:`数据时区:UTC+8(北京时间)`。
3. **过滤范围** — 游戏 / 渠道 / 国家 / 语种 / 官号 vs UGC
4. **数据覆盖度** — `MAX(comment_time)` 是几号;用户窗口是否被完整覆盖
5. **采集稳定性提醒**(如适用)— 哪些渠道当日断采
> **生产流量里大量错误是用户误读结果**,不是 SQL 错。务必把这 5 项写出来。
#### 数据缺口透明披露
| 场景 | 必带话术 |
|---|---|
| `MAX(comment_time)` 早于窗口尾(仅 `public_feeds` 等周期型表) | 「数据仅覆盖到 YYYY-MM-DD,X 之后部分尚未入库」 |
| 0 行(通用规则;事件型稀疏表见下方"单日点查严格契约",主答案直接 0) | 「该过滤条件下未找到记录,已确认 game_id / 时间窗口可被覆盖」 |
| **`media_account_publishing` 单日聚合 0 行**(事件型稀疏表反例)| ✅ 主答案给数字 `0`;❌ 不允许说"未入库 / 数据未覆盖到 YYYY-MM-DD"——没行 = 该日真实未发帖 |
| Steam 评分两源对账偏差 | 「平台官方好评率 X% (`store_score_steam_daily`) vs 已采集评论好评率 Y% (`feeds.is_recommend`);差异来自采集覆盖率」 |
| 跨域问题只查到部分 | 「下载/收入/DAU 部分需切到 `databrain-intelligence` skill」 |
#### ⚠️ GT=0 全局输出契约(**数字 0 是合法答案**)
##### 单日点查严格契约(优先级高于通用契约)
当问题形如「**`<游戏>` 在 `<平台>` 在 `<YYYY-MM-DD>` 的 `<发帖/观看/转发/评论/点赞/互动>` 量**」(单游戏 × 单日 × 可选单平台 × 官号汇总,主表必为 `opinion.media_account_publishing`):
1. 跑 metric SQL:`SELECT <SUM expr> FROM media_account_publishing WHERE unified_edition_id='<id>' AND date=DATE('<Y-M-D>') [AND LOWER(channel_name)='<value>']`
2. **返回 NULL / 空集 / 0 → 主答案必须是数字 `0`**。**禁止**用逃避型措辞替代:「数据未覆盖」/「尚未入库」/「无入库」/「暂无记录」/「无数据统计」/「无法查询」/「数据缺失」;**绝不**把 `MAX(date)` 那天旧数据塞过来(最严重——答非所问)。
3. **关于 `MAX(date) < 用户问的日期`**:`opinion.media_account_publishing` 是**事件型稀疏事实表**(event-driven sparse fact table),"行" = "某游戏 × 官号 × 日 × 渠道 当天发了帖"。官号当日没发帖 = 无任何行(**不是**未入库);`MAX(date)` 只是"最近有发帖事件的一天",**不是**数据覆盖边界。**不允许**用 `MAX(date) < 用户问的日期` 推断"未来日期未入库"。
4. **例外**:仅当 `MAX(date) < 当前日期 - 30 天` 时,可在主答案 `0` **之后**附注"(近 30 天该游戏 × 渠道无任何官号发帖事件)",**主答案仍是 `0`**。
输出模板:
```
<游戏> 在 <过滤范围> <YYYY-MM-DD> 的 <指标名> 为 **0**。
(注:该日 media_account_publishing 无该游戏 × 渠道的发帖事件,属事件型稀疏表的合法 0 值。)
```
##### 通用 GT=0 契约(时间范围 / 跨日聚合 / 非 `media_account_publishing` 场景)
SQL 正常执行(无报错)且返回 NULL / 空集 / 0 时,**必须作为合法数字 `0` 输出**,不允许用「数据缺失」/「无法回答」/「暂无数据」/「目前没有相关记录」/「需要更多信息」(在 game_id / 时间窗 / 渠道都已确定时)替代。**只有三种情况**才说"无法查询":(1) `game_search.py` 解析不到 game_id;(2) SQL 报 BigQuery 错误;(3) 指标根本不在本 skill 覆盖范围(Phase 0 out-of-scope)。
> 关键原则:业务 GT 把"实际为 0"和"暂无数据"区分清楚——前者合法事实,后者查询失败。SQL 返回什么数字就答什么,覆盖度备注另行说明。
---
## Reference Documents Index
| 文件 | 主题 |
|------|------|
| [public_feeds.md](references/public_feeds.md) | 核心事实表:声量/情感/Brand Health/互动/曝光/Earned/热门图文帖/热门视频/词云/话题;§5.2 官号帖子列表(A 路) |
| [official_account_metrics.md](references/official_account_metrics.md) | **「官号 include/exclude」聚合指标专属**(必须命中官号触发词才加载)。汇总数字走 `media_account_publishing`;粉丝数走 `media_account_audience`;Earned 走 `public_feeds + feeds_author` LEFT JOIN |
| [kol.md](references/kol.md) | opinion.kol + kol_tag + feeds_author(创作者榜单/趋势/官号识别 — 非直播) |
| [streaming.md](references/streaming.md) | streamhatchet 6 张表(直播;优先 _uid 版本) |
| [pr_news.md](references/pr_news.md) | intelligence.news_details |
| [googletrends.md](references/googletrends.md) | opinion.googletrends_keyword |
| [memes.md](references/memes.md) | opinion.memes + meme_videos |
| [market_popularity.md](references/market_popularity.md) | top_mobile_game + top_pconsole_game + public_feeds(Channel Share Ranking) |
| [marketing_hub.md](references/marketing_hub.md) | marketing_hub.* 全部 8 张(Hashtag / 视频 / KOL Info) |
| [stores/steam.md](references/stores/steam.md) / [app_store.md](references/stores/app_store.md) / [google_play.md](references/stores/google_play.md) | 主商店评分(详) |
| [stores/taptap.md](references/stores/taptap.md) / [xbox.md](references/stores/xbox.md) / [playstation.md](references/stores/playstation.md) / [meta_store.md](references/stores/meta_store.md) / [metacritic.md](references/stores/metacritic.md) / [opencritic.md](references/stores/opencritic.md) | 其他商店(简) |
| [auxiliary/dim_tables.md](references/auxiliary/dim_tables.md) | dim_channel / dim_keyword / dim_language / dim_topic_labels / dim_media_account |
| [auxiliary/geo_competitor.md](references/auxiliary/geo_competitor.md) | country_region + unified_competitor + game_event |
| [auxiliary/id_mapping.md](references/auxiliary/id_mapping.md) | **ID 体系单一真理源**:5 个 ID 含义 + 各表过滤键 + 决策树 + join 模板 + PC/Mobile probe |
| [auxiliary/social_filter_logic.md](references/auxiliary/social_filter_logic.md) | Include/Exclude/官号识别业务逻辑 |
| [auxiliary/cube_schema.md](references/auxiliary/cube_schema.md) | 全表字段索引(兜底) |
| [creative/single_game.md](references/creative/single_game.md) | **单游戏投放素材**:四指标 Total/趋势/类型分布/多维 TopN/素材 vs DAU 对比 |
| [creative/multi_game.md](references/creative/multi_game.md) | **多游戏(1–15)投放素材横向对比** |
| [creative/materials_list.md](references/creative/materials_list.md) | **素材/广告明细列表** + 视频标签 `opinion.aix_gd_video_tags_detail` |
---
## Common Patterns(Quick Recipe)
### 取一段时间内某游戏的整体声量(其余模板见对应 `references/<topic>.md`)
```sql
SELECT
FORMAT_DATE('%Y-%m-%d', DATE(comment_time)) AS date,
COUNT(DISTINCT comment_uin) AS mentions,
COUNT(DISTINCT CASE WHEN sentiment_rating IN (4,5) THEN comment_uin END) AS positive,
COUNT(DISTINCT CASE WHEN sentiment_rating IN (1,2) THEN comment_uin END) AS negative
FROM `tencent-databrain-prod.opinion.public_feeds`
-- 必带:聚簇键 unified_edition_id + 分区键 comment_time(右开窗口)
WHERE unified_edition_id = '<game_id>'
AND comment_time >= TIMESTAMP('<start_date>')
AND comment_time < TIMESTAMP_ADD(TIMESTAMP('<end_date>'), INTERVAL 1 DAY)
GROUP BY date
ORDER BY date
LIMIT 1000;
```
---
## Cross-skill Coordination
本 skill 处理 **舆情/声量/口碑/KOL/直播/新闻** 类问题,与 `databrain-intelligence`(DAU/Revenue/Retention)互补。同一对话既有「玩家舆情」又有「游戏销量」时,分别调用两个 skill,由上层合并。
---
## Pitfalls (TL;DR)
- ❌ **最严重**:查 `opinion.public_feeds` 不带 `unified_edition_id`(聚簇键)或 `comment_time` 范围(分区键)→ 亿级全表扫 + 必然 61001 timeout(其他事实表忘加时间分区过滤同理)
- 不解析游戏名直接写 `unified_edition_id = '原神'` → 0 行(必须先 `game_search.py`,ID 在历史则复用)
- 手游店写 `WHERE unified_edition_id = ...`(实际列名 `unified_id`)/ PC 店写 `unified_edition_id`(实际 `edition_id`)→ 0 行无报错
- 直播原版表(kol/sessions/profile 无 _uid 版)用 `unified_edition_id` 过滤 → 0 行(先转 `app_id`,详见 streaming.md)
- 用 `TIMESTAMP_SUB` 查 `store_score_*.create_time`(DATETIME)→ `No matching signature` 报错
- 用 `country='global'` 后认为是"全球数据"(其实是"无国家归属",按国查时要 `country IN ('<target>','global')`)
- 写 `WHERE organization='official'` 区分官号(字段不存在!用 `dim_media_account.category` 反查)
- "发帖作者数/creators" 用 `COUNT(DISTINCT comment_uin)`(那是帖子数口径)→ **必须** `COUNT(DISTINCT CONCAT(reviewer,'-',LOWER(channel_name)))`(主帖 + `channel_type='social'`)
- 聚合 `topics` 不归一化大小写 → 漏 30-50%;把"上线后"直接翻成"近 7 天"(必须先查 release_time 或问用户)
- **按具体游戏查视频却用 `marketing_hub.marketing_hub_video`**:该表是行业级 Feed,无任何 game_id 字段,`LIKE '%游戏名%'` 反查标题大量误差 → 一律走 [public_feeds.md §场景 5](references/public_feeds.md)
- **官号路由两个方向都可能走反**(生产主要错误模式):
- **方向 A**:用户没说"官号"还走 `official_account_metrics.md` → 漏全部玩家 UGC(如 Blood Strike 仅 28K 官号互动量)。无触发词 → **必走** [public_feeds.md](references/public_feeds.md) §4 场景 3。
- **方向 B**:用户明说"官号/剔除官号/Earned/玩家发的"还走 `public_feeds.md` 通用 §4 → 漏官号识别。命中触发词 → **必走** [official_account_metrics.md](references/official_account_metrics.md)。
- **中国渠道**(douyin/bilibili/xiaohongshu/kuaishou/tieba/taptap/nga)→ 先按 official_account_metrics.md §0 渠道探针确认有无数据;唯一硬告知不覆盖的是 weibo(全域无 weibo 渠道)。
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!