# EarthFeed 新闻坐标与异步精修计划 ## 目标 EarthFeed 的每条新闻都直接携带巡航可用坐标。初始响应使用新闻所属大区的锚点坐标,后台通过消息队列异步推理更精确的目标地址,完成后用实时补丁替换原新闻坐标,实现前端无感更新。 ## 返回结构 `GET /api/v1/news/earth-feed` 返回: ```json { "generated_at": "2026-05-15T03:16:43Z", "focus": { "lat": null, "lon": null, "region": "global", "label": "全球焦点", "accent": "#d6e6ff" }, "sources": [ { "id": "bbc-world", "name": "BBC World", "region": "global", "homepage_url": "https://www.bbc.com/news/world" } ], "items": [ { "id": "bbc-world:af01519ba7dd", "title": "Flattery and fanfare as Trump welcomed to China - but thorny issues remain", "summary": "The leaders of the world's two superpowers were all smiles...", "url": "https://www.bbc.com/news/articles/cdxpypg9dgeo", "source": "BBC World", "feed_name": "BBC World", "region": "global", "homepage_url": "https://www.bbc.com/news/world", "published_at": "2026-05-14T13:02:13Z", "latitude": 39.9057136, "longitude": 116.3912972, "location_label": "北京市, 中国", "location_source": "headline_location_hint", "verified": true, "location_meta": { "resolution_stage": "headline_location_hint", "ai_attempted": false, "ai_status": "skipped_text_hint", "ai_error": null, "debug_note": "text hint matched 北京市, 中国", "target": { "latitude": 39.9057136, "longitude": 116.3912972, "label": "北京市, 中国", "source": "headline_location_hint", "confidence": 0.78, "country": "中国", "city": "Beijing" }, "anchor": { "region": "global", "label": "全球", "latitude": 20.0, "longitude": 0.0 } }, "is_focus_match": true } ], "errors": [], "stale": false } ``` 字段规则: - `latitude` / `longitude`:前端巡航唯一读取的坐标。 - `location_label`:当前坐标展示名。 - `location_source`:`region_anchor`、`headline_location_hint`、`headline_country_hint`、`ai_inferred_target` 等。 - `verified`:`false` 表示仍是大区锚点;`true` 表示已经由标题规则、国家规则或 AI 得到目标地址。 - `location_meta`:调试、诊断、AI 状态、目标地址和锚点详情都放这里,不再展开成 `t_*` 主字段。 ## 后台队列 当前使用 Redis Streams: - stream:`earth_news:target_location:jobs` - consumer group:`earth_news_target_location` - result cache:`earth_news:target_location:result:{item_id}` - dedupe key:`earth_news:target_location:queued:{item_id}` 请求流程: 1. RSS 拉取并排序。 2. 每条新闻先生成大区锚点坐标,`verified=false`。 3. 若 Redis 已有该新闻的精修结果,则合并结果返回。 4. 若没有精修结果,则把新闻 job 入队,接口立即返回。 Worker 流程: 1. 从队列消费新闻 job。 2. 先跑标题/国家规则,再视情况调用 AI。 3. 写入 result cache。 4. 广播 WebSocket 补丁: ```json { "type": "data_frame", "channel": "earth_news", "timestamp": "2026-05-15T03:17:00Z", "payload": { "item_id": "bbc-world:af01519ba7dd", "patch": { "latitude": 39.9057136, "longitude": 116.3912972, "location_label": "北京市, 中国", "location_source": "headline_location_hint", "verified": true, "location_meta": {} } } } ``` ## 可迁移性 业务代码只调用队列接口,不直接依赖 Redis Streams 细节。以后迁移 Kafka 时新增 Kafka adapter,保持 job payload、result patch 和 worker 推理逻辑不变。 ## 前端规则 新闻面板和巡航都只读取新闻项内的 `latitude` / `longitude`。实时补丁到达后按 `item_id` 合并到现有 `payload.items`,重新渲染并触发 `earth:news-payload-updated`,巡航下一轮自然使用精修坐标。