Files
backend/docs/07-文献阅读价值评分系统.md
T
34047007@qq.com a6cd99a4ca
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
feat: initial commit - oncology literature search platform
OncoLit: a multi-tenant oncology literature search, feed, and
collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL.
Includes PubMed pipeline, drug approvals, AI summaries, and
systematic review tools.
2026-07-27 07:59:18 +08:00

491 lines
16 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.
# 文献阅读价值评分系统方案
## 一、定位与目标
**定位:** 成为肿瘤学文献领域的权威价值标尺——每篇文献一个分,用户信任这个分来做阅读决策。评分的价值不在于一个数字,而在于它每天帮医生决定"哪篇值得先读"。
**目标:** 评分直接驱动每日推送排序。用户每天早上打开 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 产出