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