# 文献阅读价值评分系统方案

## 一、定位与目标

**定位：** 成为肿瘤学文献领域的权威价值标尺——每篇文献一个分，用户信任这个分来做阅读决策。评分的价值不在于一个数字，而在于它每天帮医生决定"哪篇值得先读"。

**目标：** 评分直接驱动每日推送排序。用户每天早上打开 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一线NSCLC（n=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 产出
