Files
backend/docs/07-文献阅读价值评分系统.md
T

491 lines
16 KiB
Markdown
Raw Normal View History

# 文献阅读价值评分系统方案
## 一、定位与目标
**定位:** 成为肿瘤学文献领域的权威价值标尺——每篇文献一个分,用户信任这个分来做阅读决策。评分的价值不在于一个数字,而在于它每天帮医生决定"哪篇值得先读"。
**目标:** 评分直接驱动每日推送排序。用户每天早上打开 Feed,看到的第一篇不是最新的一篇,而是今天最重要、最值得读的一篇。
**核心理念:** 医生不需要从 60 篇里自行筛选。排序就是筛选,评分就是排序的依据。
---
## 二、分层定价
| 功能 | 免费 | 专业版 | 团队版 |
|---|---|---|---|
| 系统评分(整数分+排序) | ✅ | ✅ | ✅ |
| Feed 按评分排序 | ✅ | ✅ | ✅ |
| 评分标签(文献卡片上) | ✅ | ✅ | ✅ |
| 5 维明细(hover 卡片) | ❌ | ✅ | ✅ |
| 雷达图展示(详情页) | ❌ | ✅ | ✅ |
| 预设方案(临床/科研/综合) | ❌ | ✅ | ✅ |
| 自定义权重 | ❌ | ❌ | ✅(管理员设) |
| 团队共用评分方案 | ❌ | ❌ | ✅ |
---
## 三、评分算法
### 3.1 五维度模型
每个维度的满分即体现其权重——证据强度 30 分天然就是影响力 10 分的 3 倍重要,无需额外权重系数。
| 维度 | 满分 | 数据来源 | 计算方式 |
|---|---|---|---|
| **证据强度** | 30 | `study_design.sub` + `rct_detection` | 研究类型映射表(下详) |
| **期刊信誉** | 25 | `journal_issn``global_journals.tier` | Tier 1-4 对应分值 |
| **时效性** | 15 | `pub_date` | 分段基准,同年度内比较 |
| **临床相关性** | 20 | PICO + trial_reg + AI 判断 + 其他信号 | 多个信号叠加,无数据时给基础分 10 |
| **学术影响力** | 10 | `cited_by_count` | 同一年发表的文献之间按被引分位排名 |
| **合计** | **100** | | |
### 3.2 各维度详细规则
#### 证据强度(0-30 分)
| 研究类型 | 分值 | 说明 |
|---|---|---|
| Practice Guideline / Consensus | 30 | 直接影响临床决策 |
| Meta-Analysis | 27 | 综合证据最高形式 |
| Systematic Review | 27 | |
| RCT Phase III | 28 | 随机对照金标准 |
| RCT(未分期) | 26 | |
| RCT Phase II | 24 | 探索性疗效证据 |
| RCT Phase I | 22 | |
| Non-Randomized / Single-Arm | 20 | |
| Cohort / Case-Control | 18 | 观察性研究 |
| Cross-Sectional | 14 | |
| Narrative Review | 12 | |
| Case Report / Case Series | 8 | |
| Editorial / Letter / Comment | 2(×20%折扣) | 排除类型,打 20% |
| 无分类 | 5 | 默认值 |
多条 pub_type 冲突时取最高分。RCT Detection 模块检测到 RCT 但未标注时补偿 +5。
#### 期刊信誉(0-25 分)
| 等级 | 分值 | 期刊示例 |
|---|---|---|
| Tier 1 | 25 | NEJM, Lancet, JAMA, BMJ |
| Tier 2 | 22 | JCO, Lancet Oncol, JAMA Oncol, Cancer Discov, Nat Med |
| Tier 3 | 18 | Cancer Res, Ann Oncol, Clin Cancer Res, EJC... |
| Tier 4 | 12 | 其他同行评议期刊 |
| 未识别 | 8 | 无 ISSN 或未收录 |
#### 时效性(0-15 分)
按发表年份分段,**仅在同一年份内比较**。即 2026 年的文献之间经其他维度拉开差距,不因年份新旧系统性吃亏。
| 发表年份 | 基准分 |
|---|---|
| 当年 | 15 |
| 前 1 年 | 13 |
| 前 2 年 | 11 |
| 前 3 年 | 9 |
| 前 4 年 | 7 |
| 前 5 年 | 5 |
| 6-10 年 | 3 |
| >10 年或无日期 | 1 |
> 设计说明:同一年发表的文献之间,时效性的影响是相同的。真正体现区分度的是**被引次数分位**(学术影响力)、**研究类型**(证据强度)等维度。
#### 临床相关性(0-20 分)
采用**信号叠加**模式,逐步丰富:
| 信号 | 加分 | 当前覆盖率 |
|---|---|---|
| 有 `pico.population` | +4 | 低(仅 Meta 等已处理文献) |
| 有 `pico.intervention` | +4 | 低 |
| 有 `pico.sample_size` > 100 | +3 | 低 |
| 有 `pico.outcome` | +2 | 低 |
| 有 `trial_reg`NCT/EudraCT | +3 | 中 |
| `pub_types` 含 Guideline/RCT | +2 | 高 |
| 有 PMC 全文(`is_oa` | +1 | 中 |
| 有 AI 摘要(`ai_summary` | +1 | 中 |
| AI 临床相关性判断(第二阶段) | 覆盖基础分 | 仅高分文献 |
| **无任何信号** | **基础分 10** | 新文献默认 |
> 设计说明:初期数据不足时,大部分文献走基础分 10,区分度由其他 4 个维度提供。后续数据工程推进后,这个维度自然产生区分度。进入 AI 评估阶段的高分文献,临床相关性由 AI 重新评估并覆盖基础分。
#### 学术影响力(0-10 分)
基于 `cited_by_count`,只在**同一年发表的文献之间**排名:
| 分位 | 分值 |
|---|---|
| 同年前 10% | 10 |
| 同年前 25% | 8 |
| 同年前 50% | 6 |
| 其他 | 3 |
| `cited_by_count` = 0 | 1(刚发表,尚未被引) |
### 3.3 打折因子
| 条件 | 处理 |
|---|---|
| Editorial / Letter / Comment | 证据强度分 ×0.2 |
| 撤稿(`retracted = true` | 总分 ×0.1,上限 5 |
| 阴性结果 | 不扣分,标注 `is_negative` |
### 3.4 计算公式
```
总分 = 证据强度得分 + 期刊信誉得分 + 时效性得分 + 临床相关性得分 + 学术影响力得分
满分 = 30 + 25 + 15 + 20 + 10 = 100
```
---
## 四、两阶段评分流水线
### 4.1 设计原则
规则分提供**稳定基础**,AI 分提供**增量信号**。AI 挂了,规则分仍然能跑。
**只对高分文献跑 AI** 规则分 ≥60 的文献才进 AI 评估阶段。每日新文献 200-300 篇,规则筛选后约 30-50 篇进入 AI 阶段,token 成本 ≈ 15K tokens/天 ≈ 0.2 元/天。
### 4.2 流水线流程
```
每日新文献 200-300 篇
┌──────────────────────┐
│ 第一阶段:规则分 │ ← 5 维规则打分,零成本
│ (evidence + journal │
│ + recency + clin │
│ + impact) │
└──────────┬───────────┘
规则分 ≥ 60?──── 否 ──→ 存储规则分,结束
┌──────────────────────┐
│ 第二阶段:AI 评估 │ ← DeepSeek,仅高分文献
│ │
│ ├─ 临床相关性修正 │ ← 覆盖规则分的临床维度
│ ├─ landmark 标记 │ ← +5 bonus
│ ├─ 一句话推荐理由 │ ← 展示在 Feed 卡片上
│ └─ 研究要点摘要 │ ← MDT/汇报可直接使用
│ │
└──────────┬───────────┘
合并为 reading_value
```
### 4.3 AI 评估内容
对于每条规则分 ≥60 的文献,调用 DeepSeek 一次,输入标题 + 摘要(约 300 tokens),输出:
```json
{
"clinical_relevance": 16,
"clinical_reason": "III期RCT,含PFS/OS数据",
"is_landmark": true,
"landmark_reason": "首个针对EGFR Exon20ins的一线III期数据",
"landmark_bonus": 5,
"one_liner": "首个对比化疗的III期研究,mPFS 9.2 vs 5.8mo,可能改变一线标准",
"key_points": "人群:EGFR Exon20ins一线NSCLCn=423\n干预:Amivantamab + 化疗 vs 化疗\n结果:mPFS 9.2 vs 5.8mo (HR=0.58)"
}
```
| 字段 | 用途 |
|---|---|
| `clinical_relevance` | 覆盖规则分的临床相关性基础分(0-20) |
| `is_landmark` | 是否为 landmark 研究,是则加 `landmark_bonus` |
| `one_liner` | 展示在 Feed 卡片上,用户直接看到理由 |
| `key_points` | 结构化要点,可用于 MDT 摘要、导出 |
### 4.4 最终分值计算
```
最终分 = 规则 5 维总分(规则分 mode:临床相关性维使用 AI.clinical_relevance 覆盖基础分)
+ is_landmark ? landmark_bonus : 0
例如:
规则分 = evi(28) + jnl(22) + rec(12) + clin_base(10) + imp(4) = 76
AI 修正临床分为 16(覆盖基础 10)
is_landmark = true → +5
最终分 = (76 - 10 + 16) + 5 = 87
```
### 4.5 边缘情况:阈值外的重要文献
Tier 4 期刊上的 breakthrough 研究,规则分可能不到 60(期刊分低)。设一个**缓冲阈值** 50-59 之间的文献进入 AI 做一次快速筛查:
```
规则分 50-59?── 是 ──→ AI 快速筛查(只判断"是否重要")
│ │
│ 重要?── 是 ──→ 进入完整 AI 评估
│ │
│ 否 ──→ 存储规则分,结束
否(<50
存储规则分,结束
```
### 4.6 AI 产出存储
AI 产出缓存在 `reading_value` 中:
```json
{
"score": 87,
"version": 2,
"dims": {
"evidence": 28,
"journal": 22,
"recency": 12,
"clinical": 16,
"impact": 4
},
"ai": {
"clinical_relevance": 16,
"is_landmark": true,
"bonus": 5,
"one_liner": "首个对比化疗的III期研究,mPFS 9.2 vs 5.8mo",
"key_points": "人群:EGFR Exon20ins一线NSCLC..."
}
}
```
---
## 五、数据存储
### 5.1 GlobalLiterature 新增字段
```python
reading_value: Mapped[dict | None] = mapped_column(JSON)
```
免费版存储:
```json
{"score": 82, "version": 1}
```
专业版/团队版存储(含 5 维明细):
```json
{
"score": 82,
"version": 1,
"dims": {
"evidence": 28,
"journal": 22,
"recency": 12,
"clinical": 16,
"impact": 4
}
}
```
**为什么用 JSON 而非整数字段?**
1. **可扩展性**——后期加维度不减字段,老数据兼容
2. **专业版直接展示**——dimensions 明细无需额外查询
3. **版本控制**——score 旁带 version,算法更新时可区分新旧
### 5.2 索引
```sql
CREATE INDEX ix_gl_reading_value_score
ON global_literature(((reading_value->>'score')::int) DESC)
WHERE reading_value IS NOT NULL;
```
---
## 六、计算时机
### 6.1 全量回算(仅上线时跑一次)
```python
scripts/compute_reading_values.py
```
扫描全库(或 `reading_value IS NULL` 的记录),一次性打分写入。
### 6.2 每日增量
每条文献入库时,在 `_process_article()` 末尾附带计算一次:
```python
# pubmed_api.py _process_article() 末尾
lit.reading_value = compute_reading_value(lit)
# 如果规则分 ≥60,异步触发 AI 评估(不阻塞入库)
if (lit.reading_value or {}).get("score", 0) >= 60:
await schedule_ai_evaluation(lit.id)
```
### 6.3 脚本版本
```bash
# 全量重算
python scripts/compute_reading_values.py
# 指定文献重算
python scripts/compute_reading_values.py --pmids 12345678,23456789
```
### 6.4 算法版本升级
`reading_value.version` 字段用于版本标识。新版本上线时用脚本重算所有旧版本数据。
---
## 七、Feed 引擎整合
### 7.1 当前流程
```
文献入库 → 标签匹配 → UserFeed (priority: must_read/recommended/related)
→ Feed 查询:先按 priority 分组,组内按 created_at 倒序
```
### 7.2 改动方案
**保留 priority 三档结构**——must_read / recommended / related 解决"用户订阅场景",评分解决"同一场景内哪篇更重要",两个维度不冲突。
**UserFeed 表新增字段:**
```python
reading_score: Mapped[int | None] = mapped_column(Integer, default=0)
```
`batch_generate_feeds()` 中,从 `GlobalLiterature.reading_value` 取出 score 写入 `UserFeed.reading_score`
**Feed 查询改为:**
```python
order_by(UserFeed.priority, UserFeed.reading_score.desc().nullslast())
```
用户看到的 Feed
```
must_read ──────────────
[92] 肺癌新辅助III期... ← 同must_read内按评分倒序
[88] EGFR新药I期结果...
[75] 肺癌免疫微环境综述...
recommended ────────────
[80] 胃癌腹腔镜对比...
[72] 食管癌放疗新方案...
```
### 7.3 个性化排序(团队版)
团队版自定义权重不落库,查询时**纯内存实时算**:
```python
for feed in user_feeds:
dims = (feed.literature.reading_value or {}).get("dims", {})
feed._user_score = sum(
dims.get(dim, 0) * weights.get(dim, 0) / max_score[dim]
for dim in ALL_DIMS
)
```
N 篇文献 × 5 次乘加 = 毫秒级,对服务器接近零压力。
---
## 八、前端展示
### 8.1 LiteratureCard(免费版)
```
[Oncotarget] Title...
期刊信誉: tier2 阅读价值 82 ▲
```
- 分数在卡片右下角/右上角
- 颜色梯度:≥85 绿色,70-84 蓝色,50-69 灰色,<50 不显示
- Feed 默认按评分倒序排列
- 若该文献有 AI 产出(`one_liner`),在标题下方显示一行推荐理由
### 8.2 LiteratureCard(专业版 hover
```
┌──────────────────────────┐
│ 阅读价值 82/100 │
│ │
│ 证据强度 ██████████░ 28/30 │
│ 期刊信誉 ████████░░ 22/25 │
│ 时效性 ████████░░ 12/15 │
│ 临床相关 ██████░░░░ 16/20 │
│ 学术影响 ██░░░░░░░░ 4/10 │
│ │
│ 当前方案: 临床优先 │
│ │
│ 💡 首个对比化疗的III期 │
│ 研究,mPFS 9.2 vs 5.8mo│
└──────────────────────────┘
```
### 8.3 详情页(专业版)
在文献详情页新增「价值评估」区域,横向展示五维条形图。
右上角选择器可切换预设方案(临床优先 / 科研优先 / 综合)。团队版用户可在此调整权重。
---
## 九、不做的改动
- **不存用户级评分**——所有用户共享一份维度分,个性化只在查询时实时算
- **不改变免费版页面结构**——只多加一个数字标签
- **不改变 Feed 引擎的标签匹配逻辑**——priority 仍是 must_read/recommended/related
- **不支持用户级自定义权重**——放在团队版,由管理员统一设
- **临床相关性维度不等数据完备再上线**——骨架先搭进去,当前无信号给基础分 10,后续信号逐步加权
---
## 十、执行顺序
```
Phase 1 ─── GlobalLiterature 新增 reading_value 字段 + 索引
读写:单次 Alembic 迁移
Phase 2 ─── 实现规则评分算法 + 全量回算脚本
读写:新建 backend/app/services/reading_value.py
新建 backend/scripts/compute_reading_values.py
Phase 3 ─── Feed 引擎整合(加 reading_score 字段 + 排序逻辑)
读写:feed_engine.py + 各导入路径
Phase 4 ─── AI 评估集成(DeepSeek 调用 + 异步任务)
读写:reading_value.py + ARQ worker 任务
Phase 5 ─── 前端展示(文献卡片评分标签 + 推荐理由)
读写:LiteratureCard.vue + LiteratureDetailView.vue
Phase 6 ─── 专业版功能(5 维展示、预设方案选择)
独立,可与 Phase 5 并行
Phase 7 ─── 团队版自定义权重
不紧急,有团队客户后再做
```
Phase 1-3 上线后用户无感知,但排序已优化。Phase 4 增加 AI 评估(后端任务,不影响用户操作)。Phase 5 用户才看到分数和推荐理由。
---
## 十一、验证
1. 运行 `scripts/compute_reading_values.py`,检查全库 `reading_value IS NOT NULL` 比例
2. 查看分数分布曲线(大部分文献应在 50-85,不应过于集中或离散)
3. 抽样 5-10 篇人工判断:高分的确实应该高,低分的确实低
4. Feed 列表中观察排序顺序:同 priority 的文献是否按分倒序
5. 检查 AI 评估覆盖率:规则分 ≥60 的文献是否都有 AI 产出