Files
planet/docs/technical/zh/earth-news-sources.md
linkong 899e3bce43
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
release / images (push) Has been cancelled
ci / delivery (push) Has been cancelled
release: bump version to 0.71.0
2026-06-11 16:47:24 +08:00

238 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Earth 新闻源配置
Earth 态势新闻使用 `/api/v1/news/earth-feed` 输出给前端。新闻源配置存放在 `SystemSetting.category = "earth_news_sources"`;没有数据库配置时,后端使用内置默认源作为 fallback seed。
## 默认源
默认源包含四类:
- **新闻源**BBC World、DW Top Stories、CNBC Business、BBC Business、Guardian Business、NPR Business、MarketWatch、TechCrunch、Retail Dive、PR Newswire Retail、36氪、亿邦动力、国家统计局数据发布。
- **行业洞察源**McKinsey Retail、Deloitte Retail。
- **官方数据源**国家统计局数据发布、US Census Retail / E-Commerce、商务数据中心、商务部电商动态、电商物流指数。
- **线索源**BusinessWire Electronic CommerceGoogle News 作为一个聚合 source下面挂 global / americas / europe / middle-east-africa / asia-pacific 五个区域 Feed 子项。
配置型数据源默认保留在 Admin 配置中,但若不是稳定 RSS/Atom则默认不参与自动抓取。管理员可以在 `Earth 内容 -> 新闻源` 中改成可抓取 RSS、启用或禁用。
| 来源 | 类型 | 默认状态 | 默认主类型 | 主要标签 | 用途 |
| --- | --- | --- | --- | --- | --- |
| BBC World | RSS | 启用 | 政治 | `official_media`, `global` | 全球公共新闻基线 |
| DW Top Stories | RSS | 启用 | 政治 | `official_media`, `europe` | 欧洲与国际新闻基线 |
| CNBC Business | RSS | 启用 | 商业 | `business_news`, `us`, `global` | 国际商业新闻 |
| BBC Business / Guardian Business / NPR Business / MarketWatch | RSS | 启用 | 商业 / 金融 | `business_news`, `finance` | 英美商业与金融基线 |
| TechCrunch / Retail Dive / PR Newswire Retail | RSS | 启用 | 科技 / 商业 | `business_news`, `ecommerce`, `retail`, `press_release` | 科技、电商、零售和企业公告 |
| 36氪 | RSS | 启用 | 商业 | `business_news`, `ecommerce`, `china` | 国内商业、创投和快讯;主页是 `https://www.36kr.com/`Feed 信息页是 `https://www.36kr.com/rss-center`Feed 子项是综合资讯、文章资讯、最新快讯、动态内容 |
| 亿邦动力 | RSS | 启用 | 电商 | `ecommerce`, `business_news`, `china`, `retail` | 国内电商行业新闻;主页是 `https://www.ebrun.com/`Feed 信息页是 `https://www.ebrun.com/rss/`Feed 子项是 B2C、B2B、零售、O2O、服务、数据、政策 XML |
| 国家统计局数据发布 | RSS | 启用 | 电商 | `official_data`, `ecommerce`, `retail`, `china` | 官方数据发布 RSS社零和网上零售条目由分类/重要度规则识别 |
| Google News | Aggregated | 启用 | 政治 | `aggregated`, `low_stability` | 一个聚合 sourceFeed 子项为全球、美洲、欧洲、中东与非洲、亚太区域兜底 RSS优先级低于真实 RSS |
| BusinessWire Electronic Commerce | Reference | 禁用 | 电商 | `press_release`, `ecommerce`, `low_stability` | 企业公告线索 |
| McKinsey Retail Insights | Reference | 禁用 | 商业 | `industry_insight`, `retail` | 零售行业洞察 |
| Deloitte Retail | Reference | 禁用 | 商业 | `industry_insight`, `retail` | 零售行业洞察 |
| US Census Retail / E-Commerce | Reference | 禁用 | 电商 | `official_data`, `ecommerce`, `retail`, `us` | 美国零售和电商官方数据 |
| 商务数据中心 | Reference | 禁用 | 商业 | `official_data`, `china` | 国内商务数据 |
| 商务部电商动态 | Reference | 禁用 | 电商 | `official_data`, `ecommerce`, `china` | 国内电商政策与动态 |
| 电商物流指数 | Reference | 禁用 | 电商 | `official_data`, `ecommerce`, `logistics`, `china` | 物流履约与电商景气度 |
`Reference` 源表示参考链接/未来采集器线索,只记录官网、报告页或数据页,不参与 RSS/Atom 抓取。这样可以把商业与官方数据源先纳入后台治理,同时避免不可抓取页面拖垮新闻 feed。
新闻源模型是两层结构:
- `source` 表示来源品牌或聚合器,例如 36氪、亿邦动力、Google News、BBC。
- `homepage_url` 表示来源官网、栏目页或报告页。
- `feed_directory_url` 表示 Feed 信息页,也就是 RSS 订阅中心或 Feed 聚合页,只用于人工查看,不参与抓取。
- `feeds` 表示该来源下真正抓取的 RSS、Atom 或 Aggregated 子项。每个 Feed 子项都有 `id / name / url / type / enabled / default_category / tags / priority`
后端会遍历同一 source 下所有启用的 Feed 子项,逐个抓取、合并去重,并把单个子项的检测结果写入 `health.feed_results`。这不是“备用地址”逻辑36氪的四个订阅地址、亿邦的多个分类 XML、Google News 的五个区域 RSS 都可以同时启用,并且每个 Feed 可以单独配置默认新闻类型和启用状态。HTML 订阅中心或聚合页只能放在 `feed_directory_url`,不能放进 Feed 地址。默认启用的可抓 Feed 已逐项连通性检测RSS/Atom/Aggregated Feed 必须解析到条目Reference 源只保留参考地址和后续采集器线索。
当前仍保留为 Reference 的项不是“坏源”,而是没有找到稳定、可直接消费的 RSS/Atom
- BusinessWire 官方说明支持可定制 RSS/Atom但公开页面未暴露稳定行业 feed URL当前保留电子商务行业页作为公告线索。
- McKinsey / Deloitte 的零售洞察页是报告和文章集合,不是公开 RSS。
- US Census 的 press release RSS 可访问但条目链接为空Quarterly E-Commerce 页面保留为官方数据参考链接。
- 商务部数据、电商物流指数目前未找到稳定 RSS后续应做专用 collector 或人工配置可抓 feed。
## 源属性标签与新闻类型
新闻源有 `source_tags`,在 Admin 中显示为“源属性标签”。它用于描述 source 的属性,不是媒体来源名,也不是新闻条目的内容类型。例如:
- `official_data`:官方数据
- `business_news`:商业新闻
- `ecommerce`:电商
- `finance`:金融
- `retail`:零售
- `logistics`:物流
- `industry_insight`:行业洞察
- `press_release`:企业公告
- `china``global``us`
- `aggregated``low_stability`
单条新闻有一个主类型 `category`,默认类型包括:政治、商业、电商、金融、体育、科技、军事、灾害、能源、社会、文化、其他。`item_tags` 是条目级补充标签例如跨境电商、直播电商、零售数据、物流履约、平台治理、AI、半导体、选举、油价、足球、供应链。
主类型优先由规则引擎根据标题、摘要、来源名打分生成;规则未命中时优先使用 Feed 子项的默认类型,再回退 source 默认类型。AI enrichment 不阻塞新闻展示。
## 重要度
每条新闻输出:
- `importance_score`
- `importance_level`
- `importance_reasons`
- `market_impact`
官方数据源、电商指标、平台型公司、量化指标会提高重要度;企业公告基础权重较低,只有命中大平台、金额、并购、监管等信号时提升。
重要度等级固定为:`low` 034、`medium` 3559、`high` 6079、`critical` 80100。分类、重要度和 Breaking 的计算集中在 `earth_news_classification.py`;分类 key 和标签仍可配置,重要度与 Breaking 协议状态使用统一枚举,数据库和 API 继续保存兼容的小写字符串。
## 配置与缓存
`GET /api/v1/earth/news-sources` 返回默认或已保存配置。`PUT /api/v1/earth/news-sources` 保存配置并递增 `cache_version`,同时清理进程内 region cache。`POST /api/v1/earth/news-sources/reset` 恢复默认源。`POST /api/v1/earth/news-sources/test` 只测试单个 RSS/Atom/Aggregated 源,不写入新闻表。
## 手动新闻内容
手动新闻是内容管理能力,不是 RSS 源配置。Admin 入口是 `智能星球内容 -> 新闻内容`,左栏按 RSS 来源和手动新闻组聚合;进入手动组详情后可以按条添加新闻或上传 JSON 数组批量导入。手动新闻写入 `earth_news_items`,并标记 `feed_type/source_type = manual`;前台展示文案是“手动添加”。它不参与 RSS 连通性测试,也不会进入 RSS 抓取流程。
手动新闻采用“先展示,再精修”的策略:
1. 保存后立即写入 `earth_news_items`
2. 没有人工坐标时使用所选区域锚点,`verified=false`
3. 有人工坐标时使用 `location_source=manual_location``verified=true`,后续 AI 精修不会覆盖该坐标。
4. 创建或重新处理后进入新闻增强队列后台补清洗、翻译、分类、重要度、Breaking 和目标位置推断。
5. 精修完成后更新同一条新闻,并通过 Earth news reload / patch 让前端无感替换。
后台接口挂在 `/api/v1/earth/news-items`
- `GET /earth/news-groups`:返回 RSS 虚拟来源组和手动新闻组。
- `POST /earth/news-groups`:新建手动新闻组。
- `PUT /earth/news-groups/{group_id}`:重命名手动新闻组,并同步组内新闻 meta。
- `GET /earth/news-items`:分页查询 RSS 与手动新闻,支持来源类型、区域、类型和状态过滤。
- `POST /earth/news-items`:新增一条手动新闻。
- `POST /earth/news-items/import`:上传 JSON 数组批量导入,`group_id` 指定当前手动新闻组。
- `PUT /earth/news-items/{id}`编辑手动新闻RSS 新闻只读。
- `DELETE /earth/news-items/{id}`:删除手动新闻,并触发 Earth 新闻重载。
- `POST /earth/news-items/{id}/reprocess`:重新进入清洗、翻译和定位队列。
JSON 导入首版只支持数组:
```json
[
{
"title": "必填标题",
"summary": "可选摘要",
"content": "可选正文",
"url": "https://example.com/story",
"source": "手动添加",
"region": "global",
"published_at": "2026-05-15T03:00:00Z",
"category": "business",
"tags": ["manual", "analysis"],
"location": {
"label": "北京市, 中国",
"latitude": 39.9057,
"longitude": 116.3913
}
}
]
```
重复导入使用稳定 ID 去重ID 由标题、发布时间、URL 和来源生成,格式为 `manual:{hash}`。同一条手动新闻再次导入会更新原记录,不会重复出现在 EarthFeed。
## Feed 查询与类型过滤
星球端和 UE 端统一使用 `GET /api/v1/news/earth-feed` 获取新闻。接口支持服务端过滤,不要求客户端拿全量列表后自行筛选。
- `lat` / `lon`:按当前视角推断区域,适合 Web 星球端。
- `region`:显式指定区域,适合 UE 端或服务端集成;可选值包括 `global``americas``europe``asia-pacific``middle-east-africa``global` 是全局聚合视图,会展示所有区域来源;其它区域只展示该区域和 `global` 来源。
- `categories`:逗号分隔的新闻类型 key例如 `business,ecommerce`。全选时可以不传。
- `locale`:展示语言,支持 `zh-CN``en-US`,默认 `zh-CN`。中文 RSS 会以中文原文入库,并由后台补 `en-US`;英文 RSS 则由后台补 `zh-CN`
示例:
```http
GET /api/v1/news/earth-feed?region=europe&categories=business,ecommerce
GET /api/v1/news/earth-feed?lat=48&lon=10&categories=technology
GET /api/v1/news/earth-feed?region=global&categories=business,ecommerce&locale=zh-CN
```
非法新闻类型或语言会返回 `422`,响应中包含允许值。响应体会带 `filters`,用于确认后端实际应用的区域、类型和语言过滤。`items``cruise_items` 使用同一套类型过滤规则。
Web 星球端的新闻类型按钮只保存当前浏览器的显示偏好偏好变化后会重新请求接口。UE 端应直接把类型选择拼到 `categories` 参数里,不需要再做主过滤。
未指定 `sources` 时,服务层会优先选择当前 `locale` 已有可展示标题和摘要的新闻,并从当前启用来源补齐候选后做来源轮转,避免一个来源的最新待处理条目占满默认 12 条。指定 `sources` 时保持精确来源过滤,不做跨来源补齐。数据库查询层只负责区域、类型、来源和排序条件,不包含语言展示策略。
源测试只证明当前 RSS/Atom/XML 能解析到条目,不等于这些条目已经入库展示。展示链路还会检查区域、类型过滤和数据库新鲜度。保存或重置新闻源会递增配置版本并清理缓存;如果当前启用的 Feed 子项在库里没有近期条目,下一次 `earth-feed` 请求会补抓,避免新启用的 36氪、亿邦被旧 Google News 缓存挡住。
## Breaking News 插队
新闻体系里有三套互不替代的判断:
- **分类**:新闻是什么,例如商业、军事、灾害。
- **重要度**:长期是否值得关注,写入 `importance_score / importance_level`
- **Breaking**:短时间内是否必须插队,写入 `location_meta.news_meta.breaking_*`
Breaking 不新增表字段,继续保存在 `location_meta.news_meta`
- `breaking_level``none / watch / breaking / critical`
- `breaking_scope``regional / global`
- `breaking_reasons`:触发原因。
- `breaking_source``rules / ai / manual / multi_source`
- `breaking_confidence`0 到 1。
- `breaking_expires_at`:过期时间。
排序由服务端完成,客户端和 UE 不需要自己重排。未过期的 `critical``breaking``watch` 会依次排在普通新闻前面;过期后只回到普通排序,不删除新闻,也不改变长期重要度。
`breaking_scope = global` 的新闻会无视当前区域,进入所有区域的 feed`regional` 只遵守当前区域加 `global` 的普通区域规则。接口响应的 `filters` 会返回 `has_breaking``highest_breaking_level`,星球端据此给新闻面板和卡片加克制的背景/边框状态。
## 连通性监测
`POST /api/v1/earth/news-sources/test` 会测试单个源并把结果写入 `earth_news_sources.health[source_id]`。实际 RSS/Atom 抓取也会更新同一份健康状态。
健康结果包含:
- `status``ok``empty``format_error``http_error``timeout``network_error``reference`
- `status_code``content_type``item_count``latency_ms``error``fetched_at`
- `feed_results`:多 Feed source 的逐 Feed 子项检测结果,包含 `feed_id``feed_name``feed_type``feed_url`、状态、条数和错误。
常见诊断:
- 返回 HTML 页面:说明配置 URL 不是 RSS/Atom feed例如把网页中心页当成 feed。
- HTTP 403通常是 CDN、反爬或源站拒绝抓取。
- Reference参考链接不参与抓取需要改为 RSS、Atom 或 Aggregated 后才可测试抓取。
Admin 入口是 `Earth 内容 -> 新闻源`。界面不是整包 JSON 编辑,而是两层:
- **新闻源**:左侧逐个 source 列表支持按启用、停用、参考链接、RSS/Atom/Aggregated、区域和源属性标签筛选右侧编辑当前 source 字段和 Feed 子项列表。
- **策略规则**:保留源属性标签、新闻类型、条目标签规则、默认健康策略等全局规则。高级 JSON 只用于排障,不作为默认编辑路径。
单源表单分为“来源信息”和“Feed 子项”:
- 来源信息包括名称、ID、区域、主页 URL、Feed 信息页、源类型、启用开关、源属性标签、重要度权重、抓取间隔、超时、失败阈值和熔断开关。
- Feed 子项包括 Feed ID、名称、真实 Feed URL、类型、启用开关、默认新闻类型、优先级和 Feed 标签。Feed 子项底部的 `+` 只新增一个前端草稿;保存 source 后才写入配置,取消会销毁草稿。
单源“测试源”会测试当前 source 下全部启用 FeedFeed 子项上的测试按钮只测试当前 Feed。测试请求仍发送到 `/api/v1/earth/news-sources/test`,但 payload 里只带当前 source 和选中的 Feed 子项。
参考链接会显示“只记录官网、报告页或未来采集器线索,不参与 RSS/Atom 抓取”。它可作为商业或官方数据线索保留在配置中,但启用抓取前必须改成 RSS、Atom 或 Aggregated并提供可抓取的 Feed 地址。
```mermaid
flowchart LR
Admin["Admin: Earth 内容 / 新闻源"] --> Source["Source 配置"]
ManualAdmin["Admin: Earth 内容 / 新闻内容"] --> ManualAPI["/api/v1/earth/news-items"]
Source --> Feed["Feed 子项"]
Feed --> ConfigAPI["/api/v1/earth/news-sources"]
ConfigAPI --> Config["SystemSetting: earth_news_sources"]
ManualAPI --> Store
Earth["Earth 新闻面板"] --> NewsAPI["/api/v1/news/earth-feed"]
NewsAPI --> Resolver["Source Resolver"]
Resolver --> Config
Resolver --> Cache["Region Feed Cache"]
Resolver --> Fetcher["RSS / Atom Fetcher"]
Fetcher --> Parser["Feed Parser"]
Parser --> Classifier["Classifier: category + item_tags + importance"]
Classifier --> Store["earth_news_items"]
Fetcher --> Health["source health"]
Health --> Config
Store --> EnrichQueue["Location / Localization Queue"]
EnrichQueue --> AI["AI Provider"]
Store --> NewsAPI
UE["UE Client"] --> NewsAPI
```