Files
backend/docs/07-文献阅读价值评分系统.md
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

16 KiB
Raw Permalink Blame History

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

一、定位与目标

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

目标: 评分直接驱动每日推送排序。用户每天早上打开 Feed,看到的第一篇不是最新的一篇,而是今天最重要、最值得读的一篇。

核心理念: 医生不需要从 60 篇里自行筛选。排序就是筛选,评分就是排序的依据。


二、分层定价

功能 免费 专业版 团队版
系统评分(整数分+排序)
Feed 按评分排序
评分标签(文献卡片上)
5 维明细(hover 卡片)
雷达图展示(详情页)
预设方案(临床/科研/综合)
自定义权重 (管理员设)
团队共用评分方案

三、评分算法

3.1 五维度模型

每个维度的满分即体现其权重——证据强度 30 分天然就是影响力 10 分的 3 倍重要,无需额外权重系数。

维度 满分 数据来源 计算方式
证据强度 30 study_design.sub + rct_detection 研究类型映射表(下详)
期刊信誉 25 journal_issnglobal_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_regNCT/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),输出:

{
  "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 中:

{
  "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 新增字段

reading_value: Mapped[dict | None] = mapped_column(JSON)

免费版存储:

{"score": 82, "version": 1}

专业版/团队版存储(含 5 维明细):

{
  "score": 82,
  "version": 1,
  "dims": {
    "evidence": 28,
    "journal": 22,
    "recency": 12,
    "clinical": 16,
    "impact": 4
  }
}

为什么用 JSON 而非整数字段?

  1. 可扩展性——后期加维度不减字段,老数据兼容
  2. 专业版直接展示——dimensions 明细无需额外查询
  3. 版本控制——score 旁带 version,算法更新时可区分新旧

5.2 索引

CREATE INDEX ix_gl_reading_value_score
  ON global_literature(((reading_value->>'score')::int) DESC)
  WHERE reading_value IS NOT NULL;

六、计算时机

6.1 全量回算(仅上线时跑一次)

scripts/compute_reading_values.py

扫描全库(或 reading_value IS NULL 的记录),一次性打分写入。

6.2 每日增量

每条文献入库时,在 _process_article() 末尾附带计算一次:

# 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 脚本版本

# 全量重算
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 表新增字段:

reading_score: Mapped[int | None] = mapped_column(Integer, default=0)

batch_generate_feeds() 中,从 GlobalLiterature.reading_value 取出 score 写入 UserFeed.reading_score

Feed 查询改为:

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 个性化排序(团队版)

团队版自定义权重不落库,查询时纯内存实时算

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 产出