feat: Introduce AI Brand Monitoring System V5 technical design document

- Added comprehensive technical design document for AI Brand Monitoring System V5, outlining system architecture, data models, sampling strategies, and monitoring protocols.
- Key changes include a shift to a sampling-based trend monitoring approach, updated data collection and storage strategies, and new metrics for performance evaluation.
- Implemented migration scripts to support the flattening of brand questions and versioning of question texts, ensuring historical data integrity and version control.
This commit is contained in:
2026-04-09 14:43:20 +08:00
parent 41f8e0621e
commit 36451a613d
18 changed files with 2709 additions and 234 deletions
File diff suppressed because it is too large Load Diff
+960
View File
@@ -0,0 +1,960 @@
# AI 品牌曝光监测系统技术方案 V5
## 1. 文档信息
| 项目 | 内容 |
| --- | --- |
| 文档名称 | AI 品牌曝光监测系统技术方案 V5(采样型趋势监测 + 异步解耦) |
| 文档版本 | V5.1 |
| 文档状态 | 待评审 |
| 创建日期 | 2026-04-08 |
| 基线文档 | `docs/ai-brand-monitoring-tech-design-v4.md` |
| 适用范围 | 数据追踪模块全部页面 |
| 关联文档 | `docs/geo-platform-prd-v1.md`PRD 8.4 数据追踪) |
| 关联文档 | `docs/question-driven-monitoring-design-v1.md`(问题驱动模型) |
| 核心约束 | 单租户仅 1 个监测插件实例,插件所在设备不保证全天在线 |
## 2. V5 核心结论
V5 不再把该能力定义为“全量、精确、准实时监控”,而是明确收敛为:
1. **采样型趋势监测系统**,不是审计系统。
2. **单实例插件 + 非全天在线设备**前提下,只承诺趋势参考,不承诺全量覆盖。
3. **页面百分比全部按实际采样成功样本计算**,不再暗示“全量真实占比”。
4. **页面必须展示采样覆盖信息**,包括计划采样数、实际采样数、覆盖率、最近更新时间。
5. **引入 RabbitMQ 和独立 Monitoring PostgreSQL 隔离写链路**,但聚合仍以日批为主,不承诺准实时展示。
### 2.1 相比 V4 的根本变化
| 维度 | V4 | V5 |
| --- | --- | --- |
| 产品定位 | 全量品牌曝光监测 | 采样型趋势参考 |
| 采集目标 | 尽量覆盖全部问题 × 全平台 | 每日仅覆盖活跃监测题池 |
| 插件节点假设 | 大量用户端节点可互补 | 单租户单主插件实例 |
| 数据时效 | 准实时 + T+1 | T+1 为主,允许延迟 |
| 数据精度承诺 | 接近真实全量 | 趋势可参考,精度受覆盖率影响 |
| 任务处理 | PG + Redis 锁 + RabbitMQ | Monitoring PG 任务表 + PG 租约状态机 + RabbitMQ 结果异步化 |
| 存储边界 | 单业务库承载配置与监测结果 | 主业务 PG 存配置,Monitoring PG 存监测数据 |
| 页面表达 | 指标卡直接展示百分比 | 指标卡 + 采样覆盖信息 + 置信度提示 |
### 2.2 非目标
V5 明确不做以下承诺:
1. 不承诺所有问题每天都有结果。
2. 不承诺所有 AI 平台每天都有样本。
3. 不承诺分钟级实时更新。
4. 不承诺百分比可用于财务、法务、投放结算或精确 KPI 审计。
---
## 3. 产品口径与页面表达
### 3.1 页面定位
数据追踪页展示的是:
- 在“当前监测题池”上的 AI 曝光趋势
- 在“实际采样成功样本”上的品牌表现
- 在“可归因引用样本”上的文章引用趋势
不是:
- 品牌在全网 AI 场景中的绝对真实份额
- 所有问题、所有平台、所有时间点的完整监控
### 3.2 页面必须新增的展示项
为避免误导,数据追踪页和 Dashboard composite 接口必须返回并展示:
1. `planned_sample_count`:计划采样数
2. `actual_sample_count`:实际采样成功数
3. `coverage_rate`:覆盖率 = `actual / planned`
4. `last_sampled_at`:最近成功采样时间
5. `confidence_level``high / medium / low`
推荐前端呈现方式:
- 顶部右侧信息条:`采样模式`
- 趋势图上方副文案:`已采样 18/30,覆盖率 60%,更新于 04-08 09:20`
- 覆盖率过低时展示 `样本不足,仅供参考`
### 3.3 低覆盖场景的展示规则
| 覆盖率 | 置信度 | 页面表现 |
| --- | --- | --- |
| >= 70% | high | 正常展示百分比和趋势 |
| 40% - 69% | medium | 展示百分比,并提示“样本偏少” |
| < 40% | low | 保留原始计数,百分比弱化展示或标记“仅供参考” |
`actual_sample_count < 5` 时:
1. 不展示“精确到整数”的百分比变化解释。
2. 趋势线可以显示,但需打低置信度标记。
3. 引用排行允许为空,不用补零。
---
## 4. 采样配额与容量模型
### 4.1 套餐定义
V5 中配额控制的不是“题库总量”,而是“每日活跃监测问题数”。
| 配额项 | Free | Pro | Enterprise |
| --- | --- | --- | --- |
| 最大品牌数 | 1 | 3 | 自定义 |
| 题库总量 | 不限于监测能力,只做配置资产 | 不限于监测能力,只做配置资产 | 自定义 |
| **每日活跃监测问题数** | **3-5** | **5-10** | 自定义 |
| 采集频率 | 每天 | 每天 | 每天 / 自定义 |
| 平台范围 | 当前已接入且已登录的平台 | 当前已接入且已登录的平台 | 同左 |
| 数据承诺 | 趋势参考 | 趋势参考 | 趋势参考 / 专属增强 |
### 4.2 活跃监测题池
系统将问题分成两层:
1. **问题题库**:品牌下维护的全部问题,仅做配置和运营资产。
2. **活跃监测题池**:每日实际参与采样的问题集合,数量受套餐限制。
推荐实现:
- `brand_questions` 保留全部题库
- 新增 `monitor_priority``monitor_enabled`
- 调度器每日只选 Top N 问题进入任务生成
### 4.3 单实例前提下的目标任务量
按首批 6 个平台测算:
| 套餐 | 活跃问题数 | 平台数 | 目标计划任务/天 |
| --- | --- | --- | --- |
| Free | 3-5 | 6 | 18-30 |
| Pro | 5-10 | 6 | 30-60 |
说明:
1. 这是 **计划采样量**,不是保证完成量。
2. 实际完成量取决于:
- 主监测插件是否在线
- 插件机器当日在线时长
- 对应 AI 平台是否已登录
- 平台风控和采集成功率
### 4.4 核心调度原则
V5 的调度器不追求“补齐所有漏采任务”,而追求“稳定输出可比较样本”。
因此:
1. 每天只生成当天计划任务,不补历史大积压。
2. 超过 48 小时未完成的任务自动过期,不回补到新一天的分母中。
3. 读侧按 `planned_sample_count``actual_sample_count` 共同解释结果。
### 4.5 主插件在线约束
单租户只指定 1 个 `primary_monitoring_installation` 参与自动监测。
调度前先判断该插件是否活跃:
- `last_seen_at <= 24h`:生成当天任务
- `last_seen_at > 24h`:不生成新任务,页面沿用最近一次成功数据并提示“近期无新采样”
这条规则的目的:
1. 防止设备长期离线时任务无限堆积
2. 防止“未执行任务”污染覆盖率统计
3. 让系统行为和单实例现实约束一致
---
## 5. 数据口径定义
### 5.1 顶部指标口径
所有百分比的分母统一为 **实际采样成功回答数**,不是计划任务数,也不是题库总数。
| 指标 | 定义 |
| --- | --- |
| 提及率 | `mentioned_count / actual_sample_count` |
| 首位提及率 | `top1_mentioned_count / actual_sample_count` |
| 首选推荐率 | `first_recommended_count / actual_sample_count` |
| 正面提及率 | `positive_mentioned_count / actual_sample_count` |
| 品牌可见率 | 与提及率同源,可作为 UI 别名展示 |
| 文章引用率 | `cited_answer_count / actual_sample_count` |
### 5.2 趋势图口径
趋势图展示的是按天聚合后的样本表现,不是平台真实总体份额。
每个点必须同时具备:
- `metric_value`
- `actual_sample_count`
- `coverage_rate`
可选增强:
- 默认展示 7 天滚动均值
- 用户切换后可看原始日值
### 5.3 高频问题口径
高频问题区不再解释为“全站高频触发问题”,而是:
> 在当前时间窗口内,被实际采样成功次数最多的问题
推荐展示字段:
- 问题文案
- 成功采样次数
- 提及率
- 最近采样时间
### 5.4 引用排行口径
引用排行按模型平台展示:
- 有过可归因引用的回答数
- 引用文章数
- 引用率
若平台当日无样本或平台未登录:
- 展示为 `未采样`
- 不强行按 `0` 并入分母
---
## 6. 数据模型修订
### 6.1 双 PostgreSQL 存储边界
V5.1 将数据按“业务配置”和“监测结果”拆到两个 PostgreSQL
**主业务 PostgreSQL**
- `tenants`
- `brands`
- `brand_keywords`
- `brand_questions`
- `articles`
- `publish_records`
- `plugin_installations`
- `tenant_monitoring_quotas`
**Monitoring PostgreSQL**
- `monitoring_collect_tasks`
- `question_monitor_runs`
- `question_monitor_parse_results`
- `monitoring_citation_facts`
- `monitoring_article_url_aliases`
- `monitoring_*_daily`
设计原则:
1. 不做跨库物理外键,只保留逻辑 ID 引用。
2. 监测链路的高频写入、重试、归档不影响主业务 PG。
3. 读侧监测查询优先从 Monitoring PG 获取。
### 6.2 `question_version_id` 移除后的替代方案
V5 接受原系统不再维护 `question_version_id`,但仍然保留“输入快照不可变”原则。
替代方案:
1. 在任务表和运行表写入 `question_text_snapshot`
2. 同时写入 `question_hash`
3. 历史汇总按 `question_hash` 区分口径
这样即使 `question_id` 不变、文案发生修改,历史数据也不会被新文案污染。
### 6.3 `tenant_monitoring_quotas`
```sql
CREATE TABLE tenant_monitoring_quotas (
tenant_id BIGINT PRIMARY KEY REFERENCES tenants(id),
max_brands INT NOT NULL DEFAULT 1,
daily_active_question_limit INT NOT NULL DEFAULT 5,
collect_frequency VARCHAR(20) NOT NULL DEFAULT 'daily',
enabled_platforms JSONB NOT NULL DEFAULT '["deepseek","qwen","doubao"]',
primary_installation_id BIGINT REFERENCES plugin_installations(id),
task_daily_hard_cap INT NOT NULL DEFAULT 36,
plan_tier VARCHAR(20) NOT NULL DEFAULT 'free',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
建议默认值:
- Free`daily_active_question_limit = 5``task_daily_hard_cap = 36`
- Pro`daily_active_question_limit = 10``task_daily_hard_cap = 72`
说明:
1. `tenant_monitoring_quotas` 放在主业务 PostgreSQL。
2. `primary_installation_id` 继续引用主业务库中的 `plugin_installations`
### 6.4 `monitoring_collect_tasks`
V5.1 使用 Monitoring PG 单表状态机进行任务租约,并用 RabbitMQ 解耦结果写入。
```sql
CREATE TABLE monitoring_collect_tasks (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
brand_id BIGINT NOT NULL,
question_id BIGINT NOT NULL,
question_text_snapshot TEXT NOT NULL,
question_hash VARCHAR(64) NOT NULL,
ai_platform_id VARCHAR(30) NOT NULL,
run_mode VARCHAR(30) NOT NULL DEFAULT 'plugin_standard',
business_date DATE NOT NULL,
planned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
lease_token_hash VARCHAR(128),
leased_to_installation BIGINT,
leased_at TIMESTAMPTZ,
lease_expires_at TIMESTAMPTZ,
callback_received_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
skip_reason VARCHAR(50),
error_message TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT uk_collect_task_idempotent
UNIQUE (tenant_id, brand_id, question_id, question_hash, ai_platform_id, run_mode, business_date)
);
CREATE INDEX idx_collect_tasks_lease
ON monitoring_collect_tasks(tenant_id, status, business_date, lease_expires_at);
```
状态流转:
- `pending`
- `leased`
- `received`
- `completed`
- `skipped`
- `expired`
- `failed`
说明:
1. 任务租约状态在 Monitoring PG 中维护。
2. `received` 表示 API 已接收插件结果并成功投递 MQ,但 worker 尚未完成最终入库。
### 6.5 `question_monitor_runs`
在 V5 中至少补充:
```sql
ALTER TABLE question_monitor_runs
ADD COLUMN IF NOT EXISTS tenant_id BIGINT,
ADD COLUMN IF NOT EXISTS business_date DATE,
ADD COLUMN IF NOT EXISTS run_mode VARCHAR(30),
ADD COLUMN IF NOT EXISTS question_text_snapshot TEXT,
ADD COLUMN IF NOT EXISTS question_hash VARCHAR(64),
ADD COLUMN IF NOT EXISTS provider_model VARCHAR(100),
ADD COLUMN IF NOT EXISTS request_id VARCHAR(100),
ADD COLUMN IF NOT EXISTS raw_answer_storage_key TEXT;
```
建议幂等键:
```sql
CREATE UNIQUE INDEX uk_monitor_run_idempotent
ON question_monitor_runs(tenant_id, brand_id, question_id, question_hash, ai_platform_id, run_mode, business_date);
```
建议:
1. `question_monitor_runs` 落在 Monitoring PostgreSQL。
2. 若原始回答文本不超过阈值,可直接存库;超大原文再转对象存储。
### 6.6 文章 URL 归因表
为支撑“总引用数 / 文章引用率 / 引用文章数”,新增 URL 归因别名表:
```sql
CREATE TABLE monitoring_article_url_aliases (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
article_id BIGINT NOT NULL,
publish_record_id BIGINT,
platform_id VARCHAR(64),
external_article_id VARCHAR(128),
original_url TEXT NOT NULL,
normalized_url TEXT NOT NULL,
last_path_segment VARCHAR(255),
confidence VARCHAR(20) NOT NULL DEFAULT 'high',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX uk_article_url_alias
ON monitoring_article_url_aliases(tenant_id, normalized_url);
```
---
## 7. 解析标准化与 SoV 统计模型
### 7.1 解析分层
V5.1 的解析分为 3 层:
1. **回答层**:品牌提及、首位提及、首选推荐、情感倾向
2. **引用层**:从回答、citation JSON、search_results 中提取 URL 事实
3. **来源层**:基于域名清洗和站点聚合生成 SoV 统计结果
### 7.2 基于 `tldextract` 的顶级域名清洗
为避免 `www`、二级域名、国家后缀等问题导致同一站点重复计数,引用归因与来源排行统一采用 **Public Suffix List + `tldextract`** 口径。
处理目标:
1. 从原始 URL 中提取 `subdomain / domain / suffix`
2. 生成 `registrable_domain`
3. 生成统一站点主键 `site_key`
4. 为页面级和站点级聚合提供稳定维度
示例:
| 原始 URL | `registrable_domain` | 说明 |
| --- | --- | --- |
| `https://www.zhihu.com/question/123` | `zhihu.com` | `www` 折叠 |
| `https://zhuanlan.zhihu.com/p/123` | `zhihu.com` | 子域归并到站点 |
| `https://mp.weixin.qq.com/s/abc` | `weixin.qq.com` | 基于 PSL 提取可注册域 |
| `https://finance.sina.com.cn/roll/123.html` | `sina.com.cn` | 正确处理 `com.cn` |
| `https://sub.example.co.uk/a` | `example.co.uk` | 正确处理 `co.uk` |
推荐清洗流程:
1. URL 小写化主机名
2. 去掉默认端口
3. 去掉 fragment
4. path 做基础规范化
5. host 通过 `tldextract` 计算 `registrable_domain`
6. 保存:
- `normalized_url`
- `host`
- `registrable_domain`
- `subdomain`
- `suffix`
### 7.3 `monitoring_citation_facts` 推荐字段
为支撑域名清洗和来源排行,建议引用事实表至少包含:
```sql
CREATE TABLE monitoring_citation_facts (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
run_id BIGINT NOT NULL,
brand_id BIGINT NOT NULL,
ai_platform_id VARCHAR(30) NOT NULL,
business_date DATE NOT NULL,
cited_url TEXT NOT NULL,
normalized_url TEXT NOT NULL,
host VARCHAR(255) NOT NULL,
registrable_domain VARCHAR(255) NOT NULL,
subdomain VARCHAR(255),
suffix VARCHAR(50),
site_key VARCHAR(255) NOT NULL,
article_id BIGINT,
publish_record_id BIGINT,
resolution_status VARCHAR(20) NOT NULL DEFAULT 'resolved',
resolution_confidence VARCHAR(20) NOT NULL DEFAULT 'high',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_citation_facts_domain
ON monitoring_citation_facts(tenant_id, business_date, registrable_domain);
```
约定:
1. `normalized_url` 用于页面级去重和文章归因
2. `registrable_domain` 用于站点级来源排行
3. `site_key` 默认等于 `registrable_domain`
4. 如业务需要区分站群,可对特定域名做覆写映射
### 7.4 `site_key` 映射规则
默认规则:
- `site_key = registrable_domain`
可选增强规则:
1.`mp.weixin.qq.com``weixin.qq.com` 统一到 `weixin.qq.com`
2. 将业务上需要区分的站群写入 `site_domain_mapping`
3. 对同集团但不同产品是否合并,必须通过配置显式决定,不靠临时规则猜测
推荐新增配置表:
```sql
CREATE TABLE site_domain_mappings (
id BIGSERIAL PRIMARY KEY,
registrable_domain VARCHAR(255) NOT NULL,
site_key VARCHAR(255) NOT NULL,
site_name VARCHAR(255) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
### 7.5 SoV 统计模型
V5.1 中的 SoV 统一解释为:
> 在当前采样窗口内,某品牌、某站点或某文章在全部有效样本中的相对占比
必须明确分母,禁止不同口径混用。
### 7.6 品牌 SoV
品牌 SoV 适用于品牌曝光类页面。
定义:
```text
brand_sov = brand_mentioned_answers / actual_sample_count
```
其中:
- `brand_mentioned_answers`:成功采样回答中,提及目标品牌的回答数
- `actual_sample_count`:成功采样回答总数
这和页面上的“提及率”同源,可视为品牌在样本回答中的 `Share of Voice`
### 7.7 引用来源 SoV
引用来源 SoV 适用于“引用来源排行”“来源平台占比”。
定义:
```text
source_sov = source_citation_count / total_resolved_citation_count
```
其中:
- `source_citation_count`:某 `site_key` 在窗口内被引用的次数
- `total_resolved_citation_count`:全部已成功解析并完成站点归类的引用次数
补充口径:
- unresolved 引用不进入分子
- unresolved 是否进入分母,要固定为“不进入”
- unresolved 单独展示占比,作为质量指标
### 7.8 文章 SoV
文章 SoV 用于内部文章在可归因引用中的占比。
定义:
```text
article_sov = article_citation_count / total_article_resolved_citation_count
```
其中:
- `article_citation_count`:某篇内部文章被 AI 引用的次数
- `total_article_resolved_citation_count`:全部成功归因到内部文章的引用次数
注意:
1. 文章 SoV 不是品牌 SoV
2. 文章 SoV 的分母不能拿全部回答数
3. 若用户看的是“文章引用率”,仍然应使用 `cited_answer_count / actual_sample_count`
### 7.9 覆盖率与 SoV 的关系
覆盖率和 SoV 必须分开解释:
1. `coverage_rate` 反映“今天采样够不够”
2. `SoV` 反映“在已采样样本里占比多少”
因此页面解释顺序应为:
1. 先看覆盖率
2. 再看 SoV / 提及率 / 引用率
`coverage_rate < 40%` 时:
- 允许展示 SoV
- 但必须附带低置信度提示
### 7.10 聚合输出建议
建议在 `monitoring_citation_source_daily``monitoring_citation_page_daily` 中增加:
1. `citation_count`
2. `citation_sov`
3. `resolved_count`
4. `unresolved_count`
5. `confidence_level`
这样前端可以同时展示:
- 引用次数
- SoV
- 数据质量
---
## 8. 任务协议与插件侧流程
### 8.1 统一为租约式协议
V5 删除 `GET tasks -> POST claim` 两步式协议,统一改为:
1. 插件请求租约
2. 服务端原子分配任务并返回 `lease_token`
3. 插件执行采集
4. 插件带 `lease_token` 回传结果
5. 服务端校验归属、先写任务状态为 `received` 并投递 RabbitMQ
6. Monitor Worker 异步写入运行结果、解析结果并完成任务
### 8.2 插件 API
```text
POST /api/plugin/monitoring/tasks/lease
POST /api/plugin/monitoring/tasks/{id}/result
POST /api/plugin/monitoring/tasks/{id}/skip
```
请求头:
- `X-Geo-Installation-Token`
- `X-Geo-Installation-Id`
### 8.3 租约接口行为
租约接口建议按 PG 原子领取:
```sql
SELECT id
FROM monitoring_collect_tasks
WHERE tenant_id = $1
AND status = 'pending'
AND business_date = $2
ORDER BY planned_at ASC, id ASC
FOR UPDATE SKIP LOCKED
LIMIT $3;
```
服务端完成以下动作:
1. 选出任务
2. 生成短期 `lease_token`
3. 更新 `status = leased`
4. 写入 `lease_expires_at = now() + interval '15 minutes'`
### 8.4 插件执行策略
V5 继续复用现有插件基础设施:
- [background.ts](/Users/liangxu/Documents/test/geo-rankly/apps/browser-extension/entrypoints/background.ts)
- [runtime.ts](/Users/liangxu/Documents/test/geo-rankly/apps/browser-extension/src/runtime.ts)
- [storage.ts](/Users/liangxu/Documents/test/geo-rankly/apps/browser-extension/src/storage.ts)
但采样策略改为“小批量、固定预算”:
| 项目 | 建议值 |
| --- | --- |
| 心跳频率 | 15 分钟 |
| 单次租约任务数 | 2-3 |
| 单任务超时 | 60 秒 |
| 租约时长 | 15 分钟 |
| 每日硬上限 | 按套餐 36 / 72 |
### 8.5 任务过期与回收
定时任务每 30 分钟处理一次:
1. `lease_expires_at < now()` 且状态仍为 `leased` 的任务改为 `expired`
2. `received` 状态超过阈值未被 worker 完成时,进入异常告警
3. 同一 `business_date``expired` 任务最多重试 1 次
4. 超过 `business_date + 2 days` 的未完成任务不再重试
---
## 9. 采集、解析、聚合架构
### 9.1 首期简化版链路
```text
每日 00:10 调度器生成当天任务
插件后台 15 分钟心跳租约任务
插件执行提问并回传结果
tenant-api:校验租约 -> 标记 received -> 投递 RabbitMQ
Monitor Worker:消费结果 -> 写 Monitoring PG -> 解析结果 -> 完成任务
每日 06:30 聚合 Job 汇总到 monitoring_*_daily
读侧 API + Redis 缓存
```
### 9.2 RabbitMQ 的职责边界
V5.1 中 RabbitMQ 只承担“结果异步化”,不承担“任务分发”。
使用 RabbitMQ 的原因:
1. 插件回调接口可以快速返回,降低超时和重试风险
2. 解析、引用归因、写库失败不会直接影响插件侧体验
3. 方便把高频写入隔离到独立 worker
4. 后续若企业版放大量级,不需要重做写链路
但 RabbitMQ 不参与以下事项:
1. 不做任务租约
2. 不做读侧查询
3. 不强制引入准实时前端刷新
推荐队列:
- `monitor.result.ingest`
- `monitor.result.dlq`
- `monitor.aggregate.trigger`
### 9.3 Monitoring PostgreSQL 的职责
独立 Monitoring PG 的目的不是“多一套库”,而是隔离监测链路的写放大。
承载内容:
1. 任务表
2. 原始回答
3. 解析结果
4. 引用事实
5. 日聚合结果
收益:
1. 主业务 PG 不承受高频插入和大文本更新
2. Monitoring PG 可独立做分区、归档、VACUUM 策略
3. 监测表膨胀不会影响文章、品牌、发布主链路
### 9.4 原始回答存储
V5.1 中抓取结果优先进入 Monitoring PG
1. 原始回答文本
2. 平台响应元数据
3. 引用与搜索结果原始 JSON
4. 解析后的结构化字段
可选归档策略:
1. 小于 64 KB 的原文直接存 Monitoring PG
2. 超大原文或完整调试负载转存对象存储
3. Monitoring PG 仅保留摘要、关键字段和存储路径
---
## 10. 文章引用归因策略
### 10.1 为什么不能只看 URL 最后一段
仅按最后一段匹配存在以下问题:
1. 不同平台可能出现相同 path segment
2. 同一平台 PC / H5 / 分享页 URL 可能不同
3. AI 引用结果可能带参数、重定向或 canonical URL
4. 同一篇文章可能存在多个可访问别名
因此 V5 的归因策略是 **分层匹配 + 置信度标记**
### 10.2 归因顺序
1. `normalized_url` 精确匹配
2. `(platform_id, external_article_id)` 精确匹配
3. `(domain, last_path_segment)` 唯一匹配
4. 无法唯一命中时,记为 `unresolved`
### 10.3 低置信度处理
当仅通过 `last_path_segment` 命中时:
1. 给归因结果打 `low_confidence`
2. 可以进入趋势参考视图
3. 不建议进入需要精确计费或精确归属的报表
这与 V5 的“趋势参考”定位是一致的。
---
## 11. 平台接入范围
### 11.1 V5 交付范围
数据模型支持 6 平台扩展,但首期交付只承诺首批 3 平台:
1. DeepSeek
2. 通义千问
3. 豆包
### 11.2 页面与套餐表达
页面和套餐都必须按“当前已接入且已登录的平台”解释:
- 已接入且已登录:进入采样计划
- 已接入未登录:展示 `未登录`
- 未接入:展示 `未接入`
不得再用“全 6 平台稳定可用”作为首期对外承诺。
---
## 12. 读侧与汇总表
V5 读侧继续沿用 V3 的聚合表和缓存思路,但口径增加采样字段:
1. `planned_sample_count`
2. `actual_sample_count`
3. `coverage_rate`
4. `confidence_level`
推荐所有 `monitoring_*_daily` 汇总表统一增加以上字段。
### 12.1 Dashboard composite
`/api/tenant/monitoring/dashboard/composite` 继续保留,但返回结构需扩展:
```json
{
"overview": {
"mention_rate": 0.42,
"citation_rate": 0.18,
"planned_sample_count": 30,
"actual_sample_count": 21,
"coverage_rate": 0.70,
"confidence_level": "high",
"last_sampled_at": "2026-04-08T09:20:00Z"
}
}
```
### 12.2 缓存策略
读侧缓存策略可继续沿用 V3
- composite 聚合缓存
- singleflight
- TTL 抖动
- stale-while-revalidate
但缓存失效触发点改为:
1. 每日聚合完成
2. 用户切换品牌 / 时间窗口
不需要为单条采样结果做准实时逐条失效。
---
## 13. 降级、告警与风险
### 13.1 降级策略
| 场景 | V5 行为 |
| --- | --- |
| 插件 24h 未活跃 | 暂停生成新任务,页面展示上次数据 |
| 平台未登录 | 该平台不进入当天计划样本 |
| 当日覆盖率低 | 页面提示“样本不足,仅供参考” |
| 某平台 adapter 失效 | 该平台展示“暂不可用”,其余平台继续 |
| URL 无法归因 | 计入 unresolved,不强行挂到文章 |
### 13.2 关键告警
| 指标 | 阈值 |
| --- | --- |
| 主插件离线时长 | > 24h |
| 日覆盖率 | < 40% |
| 单平台连续失败 | > 20 次 / 天 |
| unresolved 引用占比 | > 30% |
| 每日聚合延迟 | > 08:00 未完成 |
### 13.3 风险接受说明
V5 明确接受以下风险:
1. 单日样本偏少导致波动大
2. 插件离线导致当天无新数据
3. 部分 URL 只能低置信度归因
但不接受以下风险:
1. 任务重复执行导致统计翻倍
2. 输入口径变更污染历史数据
3. 未授权回调写入虚假监测结果
---
## 14. 实施计划
### 14.1 Phase 分期
| Phase | 内容 | 估时 |
| --- | --- | --- |
| A | 双库方案落地:主业务 PG / Monitoring PG schema 与连接配置 | 4 天 |
| B | RabbitMQ 基础设施 + Monitor Worker 骨架 + DLQ | 4 天 |
| C | 插件:AI adapter 框架 + 3 平台 + 租约式任务执行 | 6 天 |
| D | 后端:租约 API、结果回调、MQ 消费、解析入库、日聚合 | 7 天 |
| E | 前端:数据追踪页补覆盖率、更新时间、置信度提示 | 4 天 |
| F | 联调、坏样本验证、口径验收 | 4 天 |
| **合计** | | **29 天** |
### 14.2 验收标准
1. 页面可展示 `planned_sample_count / actual_sample_count / coverage_rate`
2. 页面可正常展示趋势图、高频问题、引用排行
3. 当覆盖率不足时,页面有明确提示
4. 同一任务不会被重复计入统计
5. 问题文案修改后,历史趋势不被污染
6. 引用文章数可通过 URL alias 规则稳定归因,无法归因时进入 unresolved
---
## 15. V4 → V5 变更摘要
| 编号 | V4 | V5 |
| --- | --- | --- |
| C1 | 全量监测叙事 | 采样趋势叙事 |
| C2 | Free 1 品牌 / 40 问题 | Free 3-5 个每日活跃问题 |
| C3 | Pro 3 品牌 / 40 问题 | Pro 5-10 个每日活跃问题 |
| C4 | GET + claim 两步任务协议 | 一步租约式任务协议 |
| C5 | `question_version_id` | `question_hash + question_text_snapshot` |
| C6 | RabbitMQ + 增量聚合 | RabbitMQ 仅做结果异步化,不参与任务分发或前端准实时 |
| C7 | 准实时可见 | T+1 / 延迟可接受 |
| C8 | 仅展示百分比 | 百分比 + 采样覆盖信息 |
| C9 | 首期 6 平台叙事 | 首期只承诺 3 平台 |
| C10 | 单一业务库承载全部数据 | 主业务 PG + Monitoring PG 双库隔离 |
## 16. 最终建议
V5 适合当前阶段的原因不是“它最强”,而是“它和当前约束一致”:
1. 和单实例插件现实相符
2. 和 PRD 的 V1 边界相符
3. 和用户对“趋势参考”的接受度相符
如果未来要升级到 V6,再考虑:
1. 专用在线采集节点
2. 企业版更高采样量
3. 更细粒度的增量聚合和近实时刷新
4. 更强的 URL 归因与多次重复采样校准