zcbot/skills/research/SKILL.md

8.1 KiB

name description
research 查 paper_server 文献库(基于 OpenAlex 元数据 + Sci-Hub 下载的内部部署)。用户要查文献、找 DOI、拉 PDF、做文献综述、写带引文的申报书 / 研究方案 / 调研报告时使用。

Research

paper_server 是内部部署的 Django 文献库:元数据来自 OpenAlex,PDF / XML 由 Sci-Hub / OpenAlex 异步抓取。库里主语料是英文(OpenAlex 主索引英文文献),少量中文。本 skill 给你四个 helper(search / get_paper / fetch_pdf / fetch_xml),用 run_python 调用,不要自己 httpx 裸调 API。

何时用

  • 用户要查 / 找 / 看 / 推荐文献
  • 要 DOI、要某篇 PDF、要 abstract
  • 写申报书 / 研究方案 / 调研报告的"国内外现状"段需要真实文献支撑
  • 配合 proposal skill 的「立项依据」起草 —— 先 search 拿候选,看 abstract 决定要不要引

何时不用

  • 用户只问通识(直接答即可,不需要文献支撑)
  • 用户已经给了具体文献清单(直接用,不要二次校验)

准备

from skills.research.paper import search, get_paper, fetch_pdf, fetch_xml

(import 路径由 run_python 注入的 PYTHONPATH 提供,直接写就行,不必折腾 sys.path)

关键:keyword 优先用英文

search(keyword=...) 走 paper_server SearchFilter,模糊匹配 title / first_author / first_author_institution(目前不含 abstract)。库里 95%+ 文献 title 是英文,中文 keyword 命中率很低。

用户输入中文 → 先转成专业英文术语再 search:

用户原话 不要这样 这样
水泥水化 search("水泥水化") search("cement hydration")
钢筋锈蚀 search("钢筋锈蚀") search("steel reinforcement corrosion")
混凝土碳化 search("混凝土碳化") search("concrete carbonation")
锂离子电池电解液 search("锂离子电池电解液") search("lithium-ion battery electrolyte")

转译策略:

  • 用领域内标准英文术语(查不准就先英文 keyword 试一次看返回 title 是不是相关)
  • 多词术语用空格分隔(SearchFilter 默认空格视作 AND,要更宽用单词)
  • 不确定时同义词都试一遍:search("CO2 absorption") 没结果 → 试 search("carbon dioxide capture")
  • 中文期刊 paper 也可中文 keyword 单独搜一次(search("水泥") + filter 中文期刊 命中率不算低,但远不如英文主搜)

四个函数

search(keyword="", year=None, year_gte=None, year_lte=None, doi="", first_author="", publication_name="", has_pdf=None, is_oa=None, limit=10) -> list[dict]

搜文献,返回精简列表(每条含 16 字段:id / doi / title / first_author / first_author_institution / publication_year / publication_date / publication_name / has_fulltext_pdf / has_fulltext_xml / has_abstract / is_oa / type / abstract / pdf_url / xml_url)。

  • keyword: SearchFilter,匹配 title / first_author / first_author_institution(英文为主,见上节)
  • year / year_gte / year_lte: 精确年份 / 范围(做"近 5 年文献"用 year_gte=2020)
  • doi: 精确 DOI(命中 0 / 1 条)
  • first_author / publication_name: 精确作者 / 期刊
  • has_pdf=True 仅返 PDF 已下好的;False 仅返没 PDF 的;None 都返
  • is_oa=True 仅返开放获取(OA);False 仅返非 OA;None 都返
  • limit: 默认 10,上限 50
# 找近 5 年水泥水化研究,且 PDF 已下好
papers = search(keyword="cement hydration", year_gte=2020, has_pdf=True, limit=10)
for p in papers:
    print(p["title"], p["publication_year"])
    if p["abstract"]:
        print(p["abstract"][:200])  # 看摘要前 200 字判断是否切题

get_paper(id_or_doi) -> dict

取单条完整 metadata + abstract。id_or_doi 既接受 paper_server 内部 id,也接受 DOI(自动解析)。

list 端点已带 abstract,正常工作流不需要调本函数 —— 仅在用户给单个 id / DOI 想拿全字段(含 OpenAlex 原始字段如 o_keywords 等)时用。

paper = get_paper("10.1016/j.cemconres.2020.106156")
print(paper["title"])
print(paper["abstract"])

fetch_pdf(id_or_doi, working_dir) -> str

下载 PDF 到 <working_dir>/papers/<safe_doi>.pdf,返回相对路径 papers/<safe_doi>.pdf(safe_doi 把 / 换成 _)。走 paper_server media 静态直链(从 list/retrieve 返回的 pdf_url 字段),跟 fetch_xml 同范式。已存在跳过下载直接复用。

has_fulltext_pdf=Falsepdf_url 空(publication_date 缺失)→ 抛 RuntimeError

rel = fetch_pdf("10.1016/j.cemconres.2020.106156", working_dir=r"D:/projects/zcbot/workspace/users/<uid>/<wd>")
# rel == "papers/10.1016_j.cemconres.2020.106156.pdf"

fetch_xml(id_or_doi, working_dir) -> str

下载 XML 到 <working_dir>/papers/<safe_doi>.xml,对称 fetch_pdf走 paper_server 的 media 静态直链(由 list/retrieve 返回的 xml_url 字段提供),paper_pdf_view 只覆盖 PDF,XML 没对应 API。已存在跳过下载直接复用。

has_fulltext_xml=Falsexml_url 空(publication_date 缺失时会空)→ 抛 RuntimeError

为什么 XML 优先 PDF:XML 已结构化 —— 章节标题 / 摘要 / 段落 / 参考文献 / 图表 caption 都有标签,LLM 读取无需 OCR 或 PDF 文本抽取的不确定性;文献综述 / 引文清单 / 章节定位场景比 PDF 友好得多。能拿 XML 就别拿 PDF;只有需要看具体公式 / 图表内容 / 表格数据时才下 PDF。

标准工作流

  1. (若用户输入中文)转专业英文术语
  2. search:按 keyword + filter(年份范围 / OA / has_pdf 等)缩窄候选,limit=10
  3. 直接看返回里的 abstract(list 端点已带):
    • abstract 非空 → 看前 200-400 字判断切题
    • abstract 空(has_abstract=False)→ 仅凭 title + 期刊 + 年份判断,信号弱时下条候选
  4. 下全文(若需要) —— 优先级:
    • has_fulltext_xml=Truefetch_xml(LLM 友好,结构化)
    • has_fulltext_pdf=Truefetch_pdf(回退,需要 PDF 文本抽取)
    • 两者都 False → 仅凭 abstract 写综述,告诉用户哪几篇没全文
  5. read 全文:fetch 返回相对路径,用主 agent 的 read 工具读取(zcbot 已内置 PDF 文本抽取;XML 直接当文本读)

错误处理

  • 网络超时 / paper_server 不可达:httpx.ConnectError / httpx.TimeoutException —— 告诉用户"paper_server 暂时连不上",不要重试堆栈刷屏
  • doi 未命中 / doi 命中多条:get_paper / fetch_pdf / fetch_xml 内部 _resolve_to_idValueError —— DOI 拼写错或库里没收录,改 keyword 重搜
  • has_fulltext_pdf=False / has_fulltext_xml=False:fetch_pdf / fetch_xmlRuntimeError —— 服务器还没下到对应格式;若另一格式存在则换用,都没就只能用 abstract
  • xml_url 空(publication_date 缺失,paper 落到 unknown 目录):fetch_xmlRuntimeError(xml_url unavailable...) —— 改试 fetch_pdf
  • 文件 disk 缺失(has_fulltext_pdf=True 但 paper_server 侧文件丢了 → HTTP 404):helper 透传 httpx.HTTPStatusError,告诉用户换一篇
  • abstract 字段为空字符串:正常情况,不是错;告诉用户"这篇没收录摘要"即可
  • search 命中 0 条:先尝试同义词 / 缩短 keyword / 放宽 filter,3 次还是 0 条告诉用户库里没覆盖,不要凭训练数据脑补文献

反模式

  • httpx / requests 裸调 paper_server API(走 helper,免得 base_url / auth / 字段名漂移时四处改)
  • 用户输中文直接 search(中文) —— 转英文术语,见上节
  • search(limit=50) 一次拉满后 dump 给 LLM 全文(只 print 前 5-10 条精简就够,要全部让用户自己 print(papers))
  • 已经看到 abstract 还 get_paper 一遍(list 已带 abstract,重复调白费 roundtrip)
  • 没看 abstract 就 fetch_pdf / fetch_xml(80% 场景 abstract 够用,下载全文慢且费带宽)
  • has_fulltext_xml=True 时盲走 fetch_pdf(XML 对 LLM 更友好,先试 fetch_xml)
  • 编造 DOI / title / 作者 —— 不在 paper_server 库里就明确告诉用户"未命中",不要凭训练数据脑补
  • fetch_pdf 返回的相对路径当绝对路径用(它是相对 working_dir 的)