trend-radar-mcp
# trend-radar-mcp
[](https://github.com/ngwddd/trend-radar-mcp/actions/workflows/ci.yml)
把 [trend-radar](https://github.com/ngwddd/trend-radar) 那个 skill 的**确定性部分**拆出来,做成一个有状态、可并发、按需读取的 **MCP 工具**。
原版 skill 保留不动,作为「判断协议 + 采集引擎」的历史版本;这个仓库是分支重写版。
> 一句话:原版让模型**照着 1,800 行说明书跑八个阶段**;这一版把取数、打分、分桶、记忆交给工具,
> 模型只负责判断与写作——**两次调用完成一份简报**。
---
## v0.4(2026-10-21)· 潜力新闻(opt-in 支路)
默认路径不变:`radar_scan` / `radar_pack` / `radar_check` 的参数、返回、耗时都不动。
新增第 4 个工具 `radar_potential`:
- **复用**同一个 `scan()` 与 5 分钟缓存;5 分钟内有同 scope 的 run 就直接读它(**0 网络**)
- **不建新表**:每个事件的“新增独立源”从已有 `runs/*.json` 的差集算
- 只做数值预排:新增源速度、突发率、源层级 → 8–12 条候选
- 语义判断交给模型;`candidates` 独立命名空间,**永不与 core/watch 混排**
- 异常只降级:无历史按层级+新鲜度、超时回退、`try/except` 不外溢
凌晨热度未起时用它;它不是“已确认新闻”,每条都带 `caveat: 潜力(未验证)`。
**自动触发(任何时间)**:只要 `radar_scan` 的 `notice` 提示 `core` 为空、或 `core` 里只有论文/单源硬证据,就说明今天没有热点新闻,模型会**自动补一次** `radar_potential`,不需要用户开口。
---
## v0.3(2026-10-19)· 两轮完成
| 改动 | 之前 | 现在 |
|---|---|---|
| 模型轮次(典型) | 判类 → scan → entries → 写(4 轮) | **scan → 写(2 轮)** |
| 工具数 | 6 | **3**(scan / pack / check) |
| 判 scope | 模型先判,判不了才 broad | **工具内自动判**;判不进/跨类 → broad |
| 摘要 | 单独再调一次 entries | **scan 内联 core/watch 前 3 条**(briefs) |
| 传播分 | 按独立源**数量**阶梯 | **按独立源质量加权**(低质转载不抬分) |
| 真值分 | corroboration 同时加 truth 和 comm | **只加 comm**(去掉双重计数) |
| 边界样本 | 0.59 / 0.61 处置相反 | **borderline 带**(0.55–0.65)标出来 |
| arXiv API | 每轮都抓(18 s 量级) | **默认跳过**,`deep=true` 才抓 |
实测:单类冷启 5–9 s(墙钟 = 最慢的源)、broad 冷启 ~13 s、缓存命中 **0.07 s**;扫描响应 5.9–7.3 KB(含 3 条摘要)。
**没有后台任务**:缓存只在请求路径上读写,超过 5 分钟就重新抓;不做定时刷新、不做启动预热。
完整设计说明:[docs/design-v0.3.md](docs/design-v0.3.md) · 上一版:[docs/design-v0.2.md](docs/design-v0.2.md)
---
## 架构

```
radar_scan(query)
│
├─ 自动判 scope(三类 / broad)· 选源(跳过 dead / 空源 / 慢源)
├─ 16 线程并发 + 按主机限速(arXiv 3s / Crossref 0.35s)
├─ 源级缓存 5 分钟 → 命中 0.07s(无后台刷新)
├─ 归一化 → 时间窗 → 事件级去重 → 勘误过滤
├─ SQLite:first_seen / times_seen / source_health
├─ truth = 0.4·源层级 + 0.6·证据层级
│ comm = 独立源质量加权 → 0.6 闸门 → 5 桶(含 borderline 标记)
└─ digest ≤8 KB + 内联 briefs(core/watch 各前 3 条摘要)
```
状态与缓存都在 `~/.trend-radar/`(可用 `TREND_RADAR_HOME` 覆盖):
```
~/.trend-radar/
├── state.db # items(first_seen) + runs + source_health
├── runs/<run_id>.json# 一次扫描的全部条目与统计
└── cache/ # HTTP 响应 + 源级解析结果
```
---
## 工具(4 个)+ 资源
| 工具 | 作用 | 硬限制 |
|---|---|---|
| `radar_scan` | 自动判 scope + 采集 + 评分 + 分桶 + digest + 内联摘要 | 响应 ≤8 KB;`briefs` ≤8;`top` ≤10/桶 |
| `radar_pack` | 按 id / bucket 取正文与细节 | `ids` ≤12;`limit` ≤20;摘要 ≤600 字 |
| `radar_check` | 发布前关联度 + 补搜条目打分 | 条目 ≤30;关键词 ≤8;补搜 ≤10 |
| `radar_potential` | 潜力预排:新增源速度 / 突发 / 层级 → 8–12 条未验证候选 | `top` ≤12;复用 5 分钟内的 run |
资源:`radar://registry`、`radar://history`、`radar://runs`、`radar://runs/{run_id}`、`radar://runs/{run_id}/entries`。
### 分桶语义(写简报前必须看)
| bucket | 含义 | 怎么用 |
|---|---|---|
| `core` | 双项达标,或单源硬证据(`peer-reviewed`) | 可作结论主体;单源必须标出来 |
| `watch` | 可信但还没传开 | **不是噪声**,往往是刚出来的论文 |
| `suspect` | 传得广但不可信 | 必须标出来,不许当事实 |
| `calibration` | 热榜 | 只校准传播度/发现遗漏,**永不作内容源** |
| `discarded` | 双项未达标 | 可查,不作依据 |
---
## 安装
无第三方依赖,Python ≥ 3.10。
```bash
git clone https://github.com/ngwddd/trend-radar-mcp.git
cd trend-radar-mcp
python -m unittest discover -s tests # 38 tests, no network
```
### 接到 DeepSeek Harness
在 profile 的 `cordis.patch.yml` 里追加(和现有 MCP 服务同一写法):
```yaml
- insert:
- id: trend-radar-mcp
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: trend-radar
transport: stdio
command: 'python'
args:
- '-m'
- 'trend_radar_mcp'
env:
PYTHONIOENCODING: utf-8
PYTHONUNBUFFERED: '1'
TREND_RADAR_HOME: 'C:\Users\<你的用户名>\.trend-radar'
toolCallTimeoutMs: 180000
failOnStartupError: false
```
`radar_scan` 首轮约 5–13 秒(看是否命中缓存),失败源会逐条报出来,不会静默。
### 接到 Claude Desktop / 其他 MCP 客户端
```json
{
"mcpServers": {
"trend-radar": {
"command": "python",
"args": ["-m", "trend_radar_mcp"],
"env": { "PYTHONIOENCODING": "utf-8" }
}
}
}
```
---
## 用法(模型侧契约)
1. 直接调 `radar_scan(query="用户原话")`。**不要先自己判类** —— 工具会判;
判不进会自动 broad,返回 `scope_source` 可审计。显式 `domain` 永远优先。
2. `truth` / `comm` 是引擎算的固定分,**不要自己估**,也不要把两个分合成一个。
3. `borderline=true`(0.55–0.65)必须复核后再引用。
4. 「新」看 `new` / `first_seen`,不要凭印象。
5. 写完用 `radar_check(bucket="core")` 量关联度;`<0.6` 必须换角度重查。
6. 写简报:每条必须有 URL 与日期;预印本不许写成已发表;厂商自述不算独立验证;
**「本次覆盖」必须如实写失败源、降级流、跳过的空源**。
这六条同时写在 MCP `initialize` 的 `instructions` 里,客户端会自动拿到。
---
## 与 v1 的行为差异
只列**有意**的差异:
- **自动判 scope**:关键词分类器命中单一类且置信度 ≥0.7 才用,否则 broad。
- **质量加权传播**:两个低质站互转不再等于“两家印证”;单源永远是 0.30。
- **真值不再叠加印证加成**:印证只影响传播分。
- **borderline 带**:0.55–0.65 标出来,不再一刀切。
- **默认跳过 arXiv API**:`deep=true` 恢复(4 分类 × 3s 礼貌间隔)。
- **状态**:`first_seen` 写入 SQLite;`is_new` = 首次见到发生在窗口内。
- **空源**:连续 3 次 0 条 → 12 小时跳过,恢复自动解除。
判据没有放松:0.6 闸门、单源硬证据通道、撤稿短路、热榜封顶、front-matter、
身份去重全部保留。
---
## 测试
```bash
python -m unittest discover -s tests -v
python tests/smoke_stdio.py # 真启一个 stdio server,跑握手/工具/资源
```
`tests/test_scan.py` 用 fixture 替掉网络层,验证分桶、`is_new`、`first_seen` 稳定性、
broad 与 stream fallback;`tests/test_core.py` 覆盖权重、身份、front-matter、去重、关联度、
质量加权的传播分。
---
## 路线
- [ ] ETag / If-Modified-Since 条件请求
- [ ] `since_run` 增量:只回上次之后的新事件
- [ ] 同一 `event_id` 的演进时间线
- [ ] 状态库样本 ≥20/源后,开 source 权重校准
- [ ] 采集适配器拆成 `sources/`,每个 kind 一个文件
## License
MIT · 与原版 skill 同作者、同许可。判据与权重的出处见源码注释。
TDQS
Scored across 3 tools
The three tools form a clear pipeline: radar_scan for discovery/scoring, radar_pack for detail retrieval when the digest is insufficient, and radar_check for pre-publish verification. Each has a distinct role and explicit usage conditions, so an agent can easily tell them apart.
All tool names follow the same consistent pattern: the `radar_` prefix followed by a verb (`scan`, `pack`, `check`) in snake_case. There is no mixing of conventions or vague naming.
Three tools is well-scoped for a focused trend-scouting pipeline. Each tool corresponds to a distinct stage (collect/score, fetch details, verify) and none feels redundant or extraneous.
The set covers the core workflow: discovering trends, retrieving details, and validating before publication. A minor gap exists in source management (e.g., no explicit tool to list available domains or sources), but this does not block the primary purpose of preparing brief material.