- Implemented tenant and user management features including: - Tenant creation and management with associated migrations. - User creation and management with associated migrations. - Tenant membership management with associated migrations. - Platform user roles management with associated migrations. - Quota management with associated migrations. - Article and template management with associated migrations. - Added HTTP handlers for templates and workspaces. - Created tests for protected and public routes. - Introduced a script to check tenant scope in SQL queries. - Documented task plan for backend completion and frontend foundation.
10 KiB
GEO 平台问题驱动的数据监控设计 V1
1. 文档信息
| 项目 | 内容 |
|---|---|
| 文档名称 | GEO 平台问题驱动的数据监控设计 V1 |
| 文档版本 | V1.0 |
| 文档状态 | 待评审 |
| 创建日期 | 2026-03-25 |
| 适用范围 | 品牌词库、问题集、数据追踪页 |
| 关联文档 | docs/geo-platform-prd-v1.md |
| 关联文档 | docs/geo-platform-tech-architecture-v1-50k.md |
2. 设计背景
当前数据监控页不是通用 BI,而是明确来源于:
- 品牌
- 关键词
- 关键词下的问题集
- 模型
- 最近 30 天时间窗口
因此,数据监控的真实逻辑源头不是“文章”本身,也不是“关键词”本身,而是:
品牌关键词下配置的问题系统按周期执行这些问题后的模型回答结果
这意味着整套监控系统必须按“问题驱动”来设计,否则后续会出现:
- 指标口径不清
- 历史数据与问题版本混淆
- 任务量失控
- 查询逻辑和数据模型对不上
3. 核心结论
V1 建议采用以下原则:
问题是监控数据的最小采集单元。关键词只是问题集合的容器和聚合维度。品牌监控页展示的是问题运行结果的聚合视图。历史数据必须绑定问题版本,不能因为改了问题文案就覆盖历史口径。页面查询只查汇总表,不扫描原始问答记录。
4. 页面逻辑拆解
结合当前页面,用户选择条件是:
- 品牌
- 关键词
- 模型
- 时间范围,最多近 30 天
页面展示内容包括:
- 提及率
- 首位提及率
- 首选推荐率
- 正面提及率
- 日趋势折线
- 高频问题
- 引用排行榜
这些数据不应该直接从原始问答明细实时计算,而应该来自预聚合结果。
5. 数据采集逻辑
5.1 问题来源
问题来源于品牌词库中的关键词问题集。
例如:
- 品牌:12
- 关键词:数字时尚
- 问题:
- 数字时尚品牌有哪些值得关注的新趋势
- 如何选择适合自己的数字时尚单品
- 数字时尚和传统时尚的主要区别是什么
这些问题是监控任务的输入,不是单纯展示内容。
5.2 采集单元
建议将一次监控采集定义为:
- 一个问题
- 一个模型
- 一次执行时间
即:
1 question x 1 model x 1 run_time = 1 条原始监控记录
5.3 指标来源
每次模型回答返回后,系统进行解析,得到结构化结果:
- 是否提及品牌
- 是否首位提及品牌
- 是否首选推荐品牌
- 情感倾向是否正向
- 是否引用系统文章
- 引用了哪篇文章
然后再做日级汇总。
6. 推荐数据模型
6.1 主数据表
brand_keywords
idbrand_idnamestatuscreated_at
brand_questions
idbrand_idkeyword_idcurrent_version_idstatuscreated_at
说明:
- 一条问题属于一个品牌和一个关键词
- 数据监控页中的“高频问题”和“问题集”都可以从这里关联出来
brand_question_versions
idquestion_idquestion_textquestion_hashversion_nois_activecreated_at
说明:
- 问题文案一旦修改,新增版本,不覆盖历史版本
- 历史监控记录绑定到具体
question_version_id
6.2 原始采集表
question_monitor_runs
idbrand_idkeyword_idquestion_idquestion_version_idmodel_idrun_daterun_attask_batch_idstatusraw_answer_storage_keycreated_at
说明:
- 一次执行一条记录
- 模型原始回答建议放对象存储,数据库只存引用路径和摘要字段
question_monitor_parse_results
idrun_idmentioned_brandtop1_mentionedfirst_recommendedpositive_mentionedcitation_article_countcitation_platform_countparser_versioncreated_at
说明:
- 解析结果是后续聚合的直接来源
6.3 汇总表
question_model_daily_metrics
粒度:
brand_id + keyword_id + question_id + question_version_id + model_id + date
字段:
ask_countmention_counttop1_mention_countfirst_recommend_countpositive_mention_countcitation_countupdated_at
用途:
- 回看单个问题在每天的表现
- 支撑高频问题排行和问题详情
keyword_model_daily_metrics
粒度:
brand_id + keyword_id + model_id + date
字段:
question_countask_countmention_counttop1_mention_countfirst_recommend_countpositive_mention_countcitation_countmention_ratetop1_mention_ratefirst_recommend_ratepositive_mention_rateupdated_at
用途:
- 直接支撑你截图里的 30 天趋势和顶部指标卡
keyword_model_top_questions_daily
粒度:
brand_id + keyword_id + model_id + date + question_id
字段:
question_text_snapshotask_countmention_countscore
用途:
- 支撑右侧“高频问题”
keyword_model_citation_rank_daily
粒度:
brand_id + keyword_id + model_id + date + cited_article_id
字段:
citation_countcitation_rate
用途:
- 支撑引用排行榜
7. 查询口径定义
7.1 顶部指标卡
以所选:
- 品牌
- 关键词
- 模型
- 时间范围
为过滤条件,从 keyword_model_daily_metrics 汇总得出:
- 提及率 =
sum(mention_count) / sum(ask_count) - 首位提及率 =
sum(top1_mention_count) / sum(ask_count) - 首选推荐率 =
sum(first_recommend_count) / sum(ask_count) - 正面提及率 =
sum(positive_mention_count) / sum(ask_count)
7.2 趋势图
直接查询 keyword_model_daily_metrics 最近 30 天数据。
每个点只是一行日汇总,查询量很小。
7.3 高频问题
从 keyword_model_top_questions_daily 取最近 30 天累计 TopN。
7.4 引用排行榜
从 keyword_model_citation_rank_daily 取最近 30 天累计 TopN。
8. 任务调度设计
8.1 为什么要控制任务量
监控任务量不是由页面访问量决定,而是由:
- 品牌数
- 每个品牌下关键词数
- 每个关键词下问题数
- 模型数
- 每天执行次数
共同决定。
8.2 数据量公式
每日原始采集量近似为:
品牌数 x 每品牌关键词数 x 每关键词问题数 x 模型数 x 每日执行次数
例如:
轻量场景
- 50 个品牌
- 每品牌 10 个关键词
- 每关键词 10 个问题
- 5 个模型
- 每天 1 次
则:
50 x 10 x 10 x 5 x 1 = 25,000 条/天
中等场景
- 100 个品牌
- 每品牌 20 个关键词
- 每关键词 20 个问题
- 5 个模型
- 每天 2 次
则:
100 x 20 x 20 x 5 x 2 = 400,000 条/天
如果每条原始回答按 5KB 估算:
- 每天约 2GB 原始文本
- 30 天约 60GB 原始文本
因此:
- 原始回答不建议长期放 PostgreSQL
- 建议放对象存储
- PostgreSQL 只保存解析结果和汇总结果
8.3 V1 推荐执行频率
为了控制数据量,V1 建议:
- 每个问题每天默认执行
1 次 - 重要品牌或付费用户可提升为
2 次 - 不建议 V1 做高频小时级轮询
8.4 去重策略
如果同一品牌下多个关键词出现完全相同的问题文案,建议:
- 通过
question_hash检测重复问题 - 同一品牌、同一模型、同一天内同 hash 的问题尽量复用一次执行结果
但聚合归属仍可按映射关系分别记账。
这样可以显著减少任务量。
9. 版本与历史口径
9.1 为什么必须版本化
如果用户把问题:
- “数字时尚品牌有哪些值得关注的新趋势”
改成:
- “2026 年数字时尚品牌有哪些值得关注的新趋势”
那前后两者其实已经不是同一个监控口径。
如果直接覆盖旧问题,历史趋势就会失真。
9.2 推荐做法
- 问题修改时新建
question_version - 新运行记录绑定到新版本
- 历史数据仍归属旧版本
- 页面默认展示当前版本问题的聚合数据
如果要做历史总览,可按 question_id 聚合,但需要在文档里说明“跨版本聚合”。
10. 页面查询策略
10.1 页面不查原始表
数据页不能直接查:
question_monitor_runsquestion_monitor_parse_results
这些表只用于:
- 问题详情
- badcase 回放
- 算法验证
10.2 页面只查汇总表
数据页查询路径应为:
- 先查 Redis
- Redis miss 后查 PostgreSQL 汇总表
- 后台异步更新缓存
10.3 Redis Key 建议
可按以下维度缓存:
brand:{brand_id}:keyword:{keyword_id}:model:{model_id}:range:30d:summarybrand:{brand_id}:keyword:{keyword_id}:model:{model_id}:range:30d:trendbrand:{brand_id}:keyword:{keyword_id}:model:{model_id}:range:30d:top_questionsbrand:{brand_id}:keyword:{keyword_id}:model:{model_id}:range:30d:citation_rank
11. V1 性能判断
针对当前页面,如果采用“按问题采集、按天汇总、按 30 天查询”的模型,PostgreSQL 是够用的。
原因是:
- 页面查询只查 30 天汇总数据
- 每次查询最多几十到几百行
- 高频问题和排行榜都可提前汇总
- Redis 可以承接高频重复查询
真正可能变大的不是页面查询,而是:
- 原始问题执行任务量
- 原始模型回答存储量
所以要重点控制的是任务调度和原始结果留存,而不是页面 SQL 本身。
12. V1 推荐保留策略
建议:
- 原始运行记录保留
30 ~ 90 天 - 原始回答正文放对象存储
- PostgreSQL 解析结果保留
90 天 - 日级汇总表保留
180 天或更久
如果页面只看 30 天,也建议汇总表多保留一段时间,方便后续扩展。
13. 升级边界
当出现以下情况时,再升级架构:
- 问题量暴涨
- 模型数明显增加
- 每日执行次数明显增加
- 页面支持自由筛选、明细钻取
- 监控维度从 30 天扩展到半年、一年
此时可以逐步升级为:
- 独立分析库
- 更大规模任务调度
- 更复杂的去重复用逻辑
- 专用分析引擎
14. V1 最终建议
结合你当前页面和品牌词库结构,V1 最合理的设计是:
- 以
问题为最小监控采集单元。 - 以
关键词作为问题的聚合容器。 - 以
问题版本保证历史口径稳定。 - 以
日级汇总表支撑页面查询。 - 以
对象存储保存原始回答,避免 PostgreSQL 膨胀。 - 以
问题 hash 去重控制执行量。 - 默认每题每天执行 1 次,避免 V1 数据量失控。
这样做,逻辑、数据量、页面性能三者才能对齐。