AI 工具

如何写好 Consensus 研究问题:检查问题范围并转换筛选条件

Consensus 的结果好不好,很大程度取决于问题怎么问。 这篇做 questionlint.py:检查问题是否笼统、是否带预设立场、有没有写明人群和结果,并把“近 5 年”“随机对照试验”等要求转换成 Consensus API 的筛选参数。

BINBHO · 图文教程

Consensus 的结果好不好,很大程度取决于问题怎么问。 这篇做 question_lint.py:检查问题是否笼统、是否带预设立场、有没有写明人群和结果,并把“近 5 年”“随机对照试验”等要求转换成 Consensus API 的筛选参数。

先看清成品

  • “咖啡好不好”给出 3 条建议;“间歇性禁食是否能改善成年人的胰岛素敏感性?”通过。
  • “近 5 年随机对照试验:…”转换为 {"year_min": 2021, "study_types": ["rct"], "human": "true"}。
  • 发现并修复:英文 “improve” 中包含 “prove”,被误判为带预设立场。pytest 6 项通过。

合规提醒:Consensus 的回答和要点摘录是 AI 生成的,可能遗漏或误读论文;引用时必须引用原论文,并阅读原文核对。医疗、健康、法律问题请咨询专业人士,不要把检索结果当成诊疗建议。本文只使用官方功能:Consensus 网页版、官方 API(x-api-key),以及 Crossref 公开 REST API。不抓取网页,不共享账号。调用次数和套餐以官方页面为准(本文不列价格)。

运行说明:本篇是纯本地规则,不调用任何接口。规则是我按 PICO 思路总结的经验,不是 Consensus 官方规则。所有测试都在 box 上真实运行过。

做完之后你会得到什么?

成果 说明
question_lint.py 检查问题并生成筛选参数
test_lint.py 6 项测试

需要准备哪些环境?

项目 说明
Python 3.9 以上,pip install pytest

关键事实

事实 来源
study_types 的取值包括 rct、meta-analysis、systematic review 等 Consensus Search 接口参考
human=true 只返回人类研究;year_min 排除更早的论文 同上

文件结构

71-consensus-C-src/
├── question_lint.py
└── test_lint.py

第 1 步:脚本

"""提问前检查研究问题(PICO 思路:人群 Population、干预 Intervention、对照 Comparison、结果 Outcome),
并把要求翻译成 Consensus API(GET /v1/search)的筛选参数。规则是我总结的经验,不是 Consensus 官方规则。"""
import json, re, sys

VAGUE = ["好不好", "有没有用", "怎么样", "好吗", "is it good", "does it work", "worth it"]
LEADING = ["证明", "证实", "为什么.*有效", "为什么.*有害", "危害有多大", r"\bprove\b", r"\bwhy does\b.*\bwork\b"]   # 英文要加词边界:prove 会匹配 improve
POP = ["成人", "成年", "儿童", "老年", "孕妇", "患者", "学生", "大学生", "青少年", "女性", "男性", "adults?", "children", "older", "patients", "students", "women", "men"]
OUTCOME = ["风险", "发生率", "死亡率", "成绩", "体重", "血压", "血糖", "胰岛素", "敏感性", "睡眠", "焦虑", "抑郁", "risk", "mortality", "weight", "blood pressure", "insulin", "sleep", "anxiety", "depression", "score"]
STUDY = {"rct": ["随机对照", "rct", "randomi[sz]ed"], "meta-analysis": ["荟萃", "meta"], "systematic review": ["系统综述", "systematic review"]}

def has(text, words): return any(re.search(w, text, re.I) for w in words)

def lint(q):
    tips = []
    if has(q, VAGUE): tips.append("太笼统:把“好不好”换成具体结果,例如“与 2 型糖尿病风险的关系”")
    if has(q, LEADING): tips.append("带有预设立场:改成中性问法“X 是否影响 Y”,否则容易只看到支持的证据")
    if not has(q, POP): tips.append("没写研究人群(例如成年人、儿童、2 型糖尿病患者)")
    if not has(q, OUTCOME): tips.append("没写关注的结果(例如死亡率、血压、睡眠质量)")
    if len(q) > 120: tips.append("太长:一次只问一个问题")
    return tips

def filters(q, since=None):
    f = {}
    m = re.search(r"(近|最近|过去)\s*(\d+)\s*年|last (\d+) years", q, re.I)
    if m and since: f["year_min"] = since - int(m.group(2) or m.group(3))
    types = [k for k, ws in STUDY.items() if has(q, ws)]
    if types: f["study_types"] = types
    if has(q, ["人类", "人群", "成人", "成年", "儿童", "患者", "humans?", "adults?", "patients"]): f["human"] = "true"
    return f

def main(argv=None):
    argv = argv or sys.argv[1:]; q = argv[0]; year = int(argv[1]) if len(argv) > 1 else None
    tips = lint(q)
    print("问题:", q); print("建议:" if tips else "通过检查"); [print("  -", t) for t in tips]
    print("API 筛选参数:", json.dumps(filters(q, year), ensure_ascii=False))
    return 1 if tips else 0

if __name__ == "__main__":
    sys.exit(main())

第 2 步:测试

import question_lint as Q

def test_vague():
    t = Q.lint("咖啡好不好")
    assert len(t) == 3 and t[0].startswith("太笼统")

def test_good_question():
    q = "间歇性禁食是否能改善成年人的胰岛素敏感性?"
    assert Q.lint(q) == []

def test_leading():
    assert any("预设立场" in t for t in Q.lint("请证明维生素 D 能降低成年人的抑郁风险"))

def test_filters():
    f = Q.filters("近 5 年随机对照试验:间歇性禁食是否改善成年人胰岛素敏感性", since=2026)
    assert f == {"year_min": 2021, "study_types": ["rct"], "human": "true"}

def test_english():
    assert Q.lint("Does creatine supplementation improve sleep in older adults?") == []
    assert Q.filters("meta-analysis of exercise and depression in adults") == {"study_types": ["meta-analysis"], "human": "true"}

def test_improve_is_not_prove():
    # 调试时发现:“improve” 中包含 “prove”,被误判为带预设立场
    assert not any("预设立场" in t for t in Q.lint("Does exercise improve sleep in adults?"))
    assert any("预设立场" in t for t in Q.lint("Prove that exercise improves sleep in adults"))

第 3 步:在 Consensus 中使用

把通过检查的问题粘贴到 Consensus 搜索框;网页版中用年份、研究类型等筛选器实现同样的限制。用 API 时把输出的参数传给 71A 的脚本。

第 4 步:运行

$ python question_lint.py "咖啡好不好"
问题: 咖啡好不好
建议:
  - 太笼统:把“好不好”换成具体结果,例如“与 2 型糖尿病风险的关系”
  - 没写研究人群(例如成年人、儿童、2 型糖尿病患者)
  - 没写关注的结果(例如死亡率、血压、睡眠质量)
API 筛选参数: {}
$ python question_lint.py "近 5 年随机对照试验:间歇性禁食是否改善成年人胰岛素敏感性" 2026
问题: 近 5 年随机对照试验:间歇性禁食是否改善成年人胰岛素敏感性
通过检查
API 筛选参数: {"year_min": 2021, "study_types": ["rct"], "human": "true"}
$ python -m pytest -q
6 passed

怎么检查结果?

  • 通过检查的问题应该能回答:研究谁?做了什么?和什么比较?看什么结果?
  • 带预设立场的问题(“证明…”)要改成中性问法。
  • 关键词列表是示例,要按你的领域补充。

调试示例

英文 “improve” 被判为带预设立场。 测试 “Does creatine supplementation improve sleep in older adults?” 时,脚本提示“带有预设立场”。原因是规则中的 prove 匹配到了 improve 的后半部分。修复:英文规则加词边界 \bprove\b。中文没有这个问题,因为“证明”不会出现在常见词的中间。

常见问题

现象 原因 处理
合理的问题也被提示缺少人群 关键词表里没有你的人群词 在 POP 中补充
年份没有转换 没有提供当前年份参数 运行时传入年份
英文问题误报 规则没有加词边界 英文规则统一用 \b

模板规则

  • 一次只问一个问题。
  • 写清人群、干预和结果。
  • 用中性问法。
  • 规则只是提示,最终由你判断。

项目资源

用途 地址
Consensus 官网 https://consensus.app/
Consensus API 入门(官方) https://docs.consensus.app/api-get-started
Consensus Search 接口参考(官方) https://docs.consensus.app/api-reference/search

问题问清楚了,Consensus 才能找到对的论文。