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
- 写申报书 / 研究方案 / 调研报告的"国内外现状"段需要真实文献支撑
- 配合
proposalskill 的「立项依据」起草 —— 先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=False 或 pdf_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=False 或 xml_url 空(publication_date 缺失时会空)→ 抛 RuntimeError。
为什么 XML 优先 PDF:XML 已结构化 —— 章节标题 / 摘要 / 段落 / 参考文献 / 图表 caption 都有标签,LLM 读取无需 OCR 或 PDF 文本抽取的不确定性;文献综述 / 引文清单 / 章节定位场景比 PDF 友好得多。能拿 XML 就别拿 PDF;只有需要看具体公式 / 图表内容 / 表格数据时才下 PDF。
标准工作流
- (若用户输入中文)转专业英文术语
- search:按 keyword + filter(年份范围 / OA / has_pdf 等)缩窄候选,
limit=10起 - 直接看返回里的 abstract(list 端点已带):
- abstract 非空 → 看前 200-400 字判断切题
- abstract 空(
has_abstract=False)→ 仅凭 title + 期刊 + 年份判断,信号弱时下条候选
- 下全文(若需要) —— 优先级:
has_fulltext_xml=True→fetch_xml(LLM 友好,结构化)- 仅
has_fulltext_pdf=True→fetch_pdf(回退,需要 PDF 文本抽取) - 两者都 False → 仅凭 abstract 写综述,告诉用户哪几篇没全文
- 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_id抛ValueError—— DOI 拼写错或库里没收录,改 keyword 重搜has_fulltext_pdf=False/has_fulltext_xml=False:fetch_pdf/fetch_xml抛RuntimeError—— 服务器还没下到对应格式;若另一格式存在则换用,都没就只能用 abstractxml_url空(publication_date 缺失,paper 落到 unknown 目录):fetch_xml抛RuntimeError(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的)