causal-memory

2026-09-29

系列·叁。做事件驱动的朋友都知道,新闻管线的难点不在"抓",而在"抓完之后":同一条快讯被五个媒体转一遍怎么办?几千条里哪条和你持仓相关?这篇用 news-mcp 跑一条真实管线。

三层架构:Bronze / Silver / Gold

Bronze(采集)        Silver(去重)         Gold(事件化)
六个源独立抓取   →   SimHash 近重复判定  →   事件分类 + 板块 + 情绪
新浪/财联社/华尔街    汉明距离 ≤ 3 判重        规则法(可升级 LLM)
见闻/龙虎榜/热榜/公告  24h 指纹窗口

设计上的两个刻意选择:

  • 零第三方依赖:整个服务只用 Python 标准库(urllib + sqlite3),Docker 镜像极小,也永远不用担心依赖冲突
  • 与 Go 实现的语义逐字节对齐:SimHash 连"只取 rune 低 8 位"这种 Go 怪癖都照搬了,为的是和主系统(Athena)的指纹一致——跨系统判重时才不比错

真实跑一遍(2026-09-29 盘后)

import sources, core

raw  = sources.fetch_sina(num=30)        # 新浪滚动
arts = sources.fetch_cls(limit=30)       # 财联社快讯
== 新浪采集 == 30 条
== 财联社采集 == 0 条        ← 财联社源这次不可达,后面细说

拿 30 条新浪新闻进 Silver 去重:

== SimHash 去重 ==
{"input": 30, "output": 30, "deduped": 0, "seen_total": 30}

0 条重复——单源抓取时这个结果是健康的(新浪自己的滚动列表已经去重过了)。去重的威力在多源合并时才体现:同一个"央行降息"快讯,新浪、财联社、华尔街见闻各转一遍,标题只差几个标点,SimHash 汉明距离 ≤ 3 就吸收掉。seen_hashes 由调用方传入传出,服务本身无状态,你想用 Redis 做跨进程去重也行。

再进 Gold 事件分类:

== 事件分类 ==
{"input": 30, "events": 12, "dropped_no_type": 18, "llm_used": false}

[MACRO_POLICY] GTCFX:实际利率上行考验黄金韧性 9月29日,黄金回调再次把持有成本推到
               板块: -           情绪: +0.00
[INDUSTRY]     Anthropic招股书披露宏大AI愿景,成本激增
               板块: AI算力,电力  情绪: +0.00
[CORPORATE]    里昂:康师傅控股重申"跑赢大市"目标价13.70港元
               板块: -           情绪: +0.50
[CORPORATE]    解禁潮下,谁在卖出GPU四小龙?
               板块: -           情绪: +0.67
[INDUSTRY]     M4Markets:算力扩张提升稳定电源价值
               板块: AI算力,电力  情绪: +0.50

分类是规则法(关键词表 + 判定顺序对齐 Athena 的 Go 实现):公司/宏观/贸易/地缘/行业/市场六大类,同时抽出 sh60*/sz00* 形态的股票代码、映射板块("算力"→AI算力、"储能"→光伏),情绪分 = (正面词数 − 负面词数)/(总词数+1)。18 条没有命中任何关键词的被丢弃——这是和 Go 版一致的行为,宁可漏判不可错判。

每个源都会挂:财联社案例

这次 fetch_cls 返回 0 条。逐源失败是常态(接口改版、反爬升级、网络抖动),所以每个源的 fetcher 都做成可注入的:

def fetch_cls(limit=30, fetcher=_http_json):
    ...

测试时传假 fetcher,生产挂了也不影响其他源。这个"单源失败静默跳过"的策略对采集是对的,但对 Gold 层要小心——如果某天六个源全挂,输出是"今天没有新闻"而不是"采集失败",这是所有新闻管线共同的坑,监控时要对采集量为零报警,而不是对错误报警。

已知的三个优化点(写给你参考,也是贡献机会)

  1. 去重是 O(n²):每篇文章要和全部历史指纹比汉明距离,seen_hashes 只增不减。量不大没关系,量上来要换 LSH 分桶(64 位指纹切 4 段,近重复必同桶,桶内再比对)
  2. 24h 窗口没实现:SIMHASH_RETENTION_SEC 常量定义了,但过期淘汰留给调用方做——用 Redis 的话记得加 TTL
  3. 分类升级路径已留好:gold_process(..., llm_classifier=...) 参数就是给 LLM 分类器留的位,传进去就切换(LLM 优先、失败自动降级规则法),这是下一篇要讲的决策模型天然的位置

采集侧的并行化(多 channel/多 code 同时抓)刚合入不久,30 条新闻的全流程现在秒级完成。

下一篇进因子挖掘:怎么让 AI 帮你挖因子,又不让它把未来函数埋进你的回测里。

项目地址:github.com/JingxuanC/news-mcp

Comments (0)

Sign in with GitHubto leave a comment.