| 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)。若 ID 已在对话历史/上下文中出现,直接复用,不要再调 game_search.py 验证。
⚠️ 最重要的一条规则:opinion.public_feeds 聚簇键 + 分区键
opinion.public_feeds 物理上是 VIEW(自身无 BQ partition/cluster),但底层是亿级 base_feeds。每条读它的 SQL 必须同时带:
- 游戏过滤(等价聚簇键):
WHERE unified_edition_id = '<game_id>'(或 IN (...))— 绝不能省
- 时间过滤(等价分区键):
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 时间词翻译速查表;回答披露口径见 Phase 4。
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_*:无物理分区; 版聚簇 ,原版聚簇
⚠️ 指标 → 底表 决策表(写错就数值偏差 1.x ~ 87 倍)
业务 UI 上的数字背后是 cube view 路径,对应到 BigQuery 物理表。指标族不同,底表不同,混用会严重偏差:
| 指标族 | 底表 | reference | 关键说明 |
|---|
| 官号互动量 / 转发 / 观看 / 发帖 / 评论 / 点赞量 | opinion.media_account_publishing ⭐ | official_account_metrics.md §3-§6 | 物理表已预聚合"官号 × 日 × 渠道",不需 JOIN feeds_author;engagement 第 4 项用 unlike_number(非 tweets_unlike) |
| 官号粉丝数 | opinion.media_account_audience | official_account_metrics.md §7 | 唯一例外,仍走 audience 表 |
| 网红/KOL 发帖数 | opinion.public_feeds + LEFT JOIN feeds_author(排除官号) | 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 §3-§5 | ⚠️ 不支持 Reddit(底表无数据) |
| 声量 / 情感 / Brand Health / 互动 / 曝光 / 发帖人数(creators) / 视频播放量 | opinion.public_feeds | public_feeds.md | creators / publications 必带 channel_type='social' |
| 商店评分 | opinion.store_score_*_daily / _* | stores/ |
关键原则
- 要数字 → cube 路径(
media_account_publishing / media_account_audience / store_score_* / news_details)
- 要内容/列表 →
public_feeds + A 路 JOIN
- 网红"发帖数"vs"活跃 KOL"二选一 → 看 kol.md §0 决策树
- 广告投放素材/创意指标 → 与舆情底表完全无关,走
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 §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 §3.5)。
⚠️ 单指标查询禁止 PC + Mobile 跨端 UNION
游戏同时存在 PC 和 Mobile unified_edition_id 时,单一指标查询必须只用一个 game_id,禁止 IN ('<mobile>','<pc>') / 两端 UNION ALL 后 SUM·AVG / 任何变体"两端合并"(都会偏离业务单端 GT)。判定顺序:
- 问句带 PC 限定词(
PC 端 / 端游 / 主机端 / Steam / Epic / 主机版)→ PC id(--type pc)
- 问句带 Mobile 限定词(手游 / 移动端 / iOS / Android / App Store / Google Play)→ Mobile id(
--type mobile)
- 未指明端 → 用
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 |
python scripts/game_search.py "Genshin Impact"
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"
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 场景 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 — 用户未提任何"官号触发词"时聚合数字均走这里(含官号 + UGC 合计) |
| ✅ 「官号 include / exclude」聚合指标:官号互动量/发帖量/观看量/粉丝数/Top N 官号/Earned Content 剔除官号 | 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 §5.2(A 路:feeds_author.is_official_account = 1 JOIN) |
| ✅ 按具体游戏查视频/视频播放量/视频数/视频互动 | public_feeds.md §场景 5 — unified_edition_id + media_type IN ('video','live') + tweets_view;绝不走 marketing_hub_video(无 game_id 字段) |
| KOL / 创作者榜单 / 分档 / 合作创作者 | kol.md |
| 直播 / Hours Watched / Peak CCV / Avg CCV / 主播 | streaming.md(含 _uid 优先策略,必要时反链 id_mapping.md) |
| 新闻 / PR / News Brand Health | |
黄金法则:reference 里的 SQL 是经 BigQuery 全量验证的(多标注 "5/5 PASS" 等)。先看 reference 找模板,再做最小化改写,避免凭空写 SQL。
素材域路由(which-of-3,命中素材类问题时只加载一个)
素材表是 intelligence schema,与舆情 opinion.public_feeds 完全不同;不要用 public_feeds 模板。
| 用户问的形态 | 加载 |
|---|
| 要单条素材/广告明细列表(按类型/平台/渠道/国家/关键词/视频标签筛选 + TopN) | creative/materials_list.md(主表 intelligence.dwd_aix_gd_material,CLUSTER BY game_id) |
| 要单个游戏聚合数字/趋势(创意数·素材数 Total、趋势、类型分布、DoD·SDLW、多维 TopN、素材 vs DAU 对比) | creative/single_game.md(dwd_aix_gd_analysis_creatives / _stats) |
| 要多游戏(1–15)横向对比(overview/timeline/sum/国家×游戏矩阵/占比/环比) | creative/multi_game.md(同两张分析表 + dim_aix_gd_games) |
判定优先级:先看要不要单条素材明细(要 → materials_list);只要聚合再看游戏数量(单个 → single_game;多个 → multi_game)。
商店评分场景(按用户问的商店动态分流)
⚠️ 关键陷阱:手游店(App Store / Google Play / TapTap)用 unified_id 列;PC/Console 店(Steam / PlayStation / Xbox / Metacritic / OpenCritic)用 edition_id 列。写错就 0 行无报错。详见 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 时才跑脚本。
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):
- 舆情主表
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 — 执行 & 修复
python scripts/execute_sql.py --sql "<SQL>" [--schema intelligence]
python scripts/execute_sql.py --sql_file /large_tool_results/query.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 顶部 docstring 的「Common errors」小节。
3.3 自动修复循环(≤3 次)
拿到错误 → 找错误码 → 改写 SQL → 重跑;第 3 次仍报错则停止重试,给用户报告根因。
Phase 4 — 输出契约(Output Contract)
每次回答必须包含:
- 指标定义(公式)— 例
positive_rate = COUNT(DISTINCT IF(sentiment_rating IN (4,5), comment_uin, NULL)) / COUNT(DISTINCT comment_uin)(分母 = mentions,含 -1)
- 时间窗口(精确到日/秒 + UTC+8 北京时间)— 例
2025-01-01 ~ 2025-01-07 (UTC+8)。SQL 时间过滤基于 today 自算字面量整日窗口;不要加 'Asia/Shanghai' 参数,更不要用 CURRENT_TIMESTAMP() / CURRENT_DATE() / CURRENT_DATETIME()(BQ 服务时钟是 UTC)。回答末尾必须单独成行披露:数据时区:UTC+8(北京时间)。
- 过滤范围 — 游戏 / 渠道 / 国家 / 语种 / 官号 vs UGC
- 数据覆盖度 —
MAX(comment_time) 是几号;用户窗口是否被完整覆盖
- 采集稳定性提醒(如适用)— 哪些渠道当日断采
生产流量里大量错误是用户误读结果,不是 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):
- 跑 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>']
- 返回 NULL / 空集 / 0 → 主答案必须是数字
0。禁止用逃避型措辞替代:「数据未覆盖」/「尚未入库」/「无入库」/「暂无记录」/「无数据统计」/「无法查询」/「数据缺失」;绝不把 MAX(date) 那天旧数据塞过来(最严重——答非所问)。
- 关于
MAX(date) < 用户问的日期:opinion.media_account_publishing 是事件型稀疏事实表(event-driven sparse fact table),"行" = "某游戏 × 官号 × 日 × 渠道 当天发了帖"。官号当日没发帖 = 无任何行(不是未入库);MAX(date) 只是"最近有发帖事件的一天",不是数据覆盖边界。不允许用 MAX(date) < 用户问的日期 推断"未来日期未入库"。
- 例外:仅当
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
Common Patterns(Quick Recipe)
取一段时间内某游戏的整体声量(其余模板见对应 references/<topic>.md)
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`
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
- 官号路由两个方向都可能走反(生产主要错误模式):
- 方向 A:用户没说"官号"还走
official_account_metrics.md → 漏全部玩家 UGC(如 Blood Strike 仅 28K 官号互动量)。无触发词 → 必走 public_feeds.md §4 场景 3。
- 方向 B:用户明说"官号/剔除官号/Earned/玩家发的"还走
public_feeds.md 通用 §4 → 漏官号识别。命中触发词 → 必走 official_account_metrics.md。
- 中国渠道(douyin/bilibili/xiaohongshu/kuaishou/tieba/taptap/nga)→ 先按 official_account_metrics.md §0 渠道探针确认有无数据;唯一硬告知不覆盖的是 weibo(全域无 weibo 渠道)。