# 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 归因与多次重复采样校准