93 lines
4.6 KiB
Markdown
93 lines
4.6 KiB
Markdown
---
|
|
name: research
|
|
description: 查 paper_server 文献库(基于 OpenAlex 元数据 + Sci-Hub 下载的内部部署)。用户要查文献、找 DOI、拉 PDF、做文献综述、写带引文的申报书 / 研究方案 / 调研报告时使用。
|
|
---
|
|
|
|
# Research
|
|
|
|
paper_server 是内部部署的 Django 文献库:元数据来自 OpenAlex,PDF 由 Sci-Hub 异步抓取。本 skill 给你三个 helper(`search` / `get_paper` / `fetch_pdf`),用 `run_python` 调用,**不要**自己 `httpx` 裸调 API。
|
|
|
|
## 何时用
|
|
|
|
- 用户要查 / 找 / 看 / 推荐文献
|
|
- 要 DOI、要某篇 PDF、要 abstract
|
|
- 写申报书 / 研究方案 / 调研报告的"国内外现状"段需要真实文献支撑
|
|
- 配合 `proposal` skill 的「立项依据」起草 —— 先 `search` 拿候选,`get_paper` 看 abstract 决定要不要引
|
|
|
|
## 何时不用
|
|
|
|
- 用户只问通识(直接答即可,不需要文献支撑)
|
|
- 用户已经给了具体文献清单(直接用,不要二次校验)
|
|
- paper_server 没覆盖的领域(本库主打中文工程 / 材料 / 土木 / 化学;前沿 AI / 数学 / 纯理论 paper 命中率低 —— 命中 0 条直接告诉用户,不要瞎编)
|
|
|
|
## 准备
|
|
|
|
```python
|
|
from skills.research.paper import search, get_paper, fetch_pdf
|
|
```
|
|
|
|
(import 路径由 `run_python` 注入的 `PYTHONPATH` 提供,直接写就行,不必折腾 `sys.path`)
|
|
|
|
## 三个函数
|
|
|
|
### `search(keyword="", year=None, doi="", has_pdf=None, limit=10) -> list[dict]`
|
|
|
|
搜文献,返回精简列表(每条只含 id / doi / title / first_author / publication_year / publication_name / has_fulltext_pdf / has_abstract / type)。
|
|
|
|
- `keyword`: paper_server 的 search 字段,匹配 title / first_author / first_author_institution
|
|
- `year`: 精确年份(目前只支持 exact)
|
|
- `doi`: 精确 DOI(命中 0 / 1 条)
|
|
- `has_pdf=True` 仅返已下好 PDF 的;`False` 仅返没 PDF 的;`None` 都返
|
|
- `limit`: 默认 10,上限 50
|
|
|
|
```python
|
|
# 找最近 5 篇关于"水泥水化"且已下好 PDF 的
|
|
papers = search(keyword="水泥水化", has_pdf=True, limit=5)
|
|
for p in papers:
|
|
print(p["title"], p["doi"], p["publication_year"])
|
|
```
|
|
|
|
### `get_paper(id_or_doi) -> dict`
|
|
|
|
取单条完整 metadata + abstract。`id_or_doi` 既接受 paper_server 内部 id,也接受 DOI(自动解析)。
|
|
|
|
```python
|
|
paper = get_paper("10.1016/j.cemconres.2020.106156")
|
|
print(paper["title"])
|
|
print(paper["abstract"]) # 没 abstract 时是空串,不会抛
|
|
```
|
|
|
|
### `fetch_pdf(id_or_doi, working_dir) -> str`
|
|
|
|
下载 PDF 到 `<working_dir>/papers/<safe_doi>.pdf`,返回相对路径 `papers/<safe_doi>.pdf`(safe_doi 把 `/` 换成 `_`)。已存在跳过下载直接复用。
|
|
|
|
```python
|
|
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"
|
|
```
|
|
|
|
`has_fulltext_pdf=False` 时抛 `RuntimeError` —— 这时该 paper 服务器侧还没下到 PDF,告诉用户不要硬等。
|
|
|
|
## 标准工作流
|
|
|
|
1. **search**:按关键词 / 年份缩窄候选(`limit=10` 起,不够再扩)
|
|
2. **筛选**:扫返回列表,挑相关的(看 title + first_author + year),`has_fulltext_pdf=True` 的优先
|
|
3. **get_paper**:对每篇候选拿 abstract,确认确实切题再继续
|
|
4. **fetch_pdf**:确定要全文细读时才下载(用户没说要全文就别下,abstract 已足够覆盖大多数引用场景)
|
|
5. **read PDF**:`fetch_pdf` 返回相对路径,用主 agent 的 `read` 工具读 PDF 提取关键段(zcbot 已内置 PDF 文本提取)
|
|
|
|
## 错误处理
|
|
|
|
- 网络超时 / paper_server 不可达:`httpx.ConnectError` / `httpx.TimeoutException` —— 直接告诉用户"paper_server 暂时连不上",不要重试堆栈刷屏
|
|
- `doi 未命中` / `doi 命中多条`:`get_paper` / `fetch_pdf` 内部 `_resolve_to_id` 抛 `ValueError` —— DOI 拼写错或库里真没收录,改 keyword 重搜
|
|
- `has_fulltext_pdf=False`:`fetch_pdf` 抛 `RuntimeError(reason=...)` —— 服务器还没下到 PDF;告诉用户"这篇 paper_server 还没下到 PDF,可读 abstract 或换一篇"
|
|
- `abstract` 字段为空字符串:正常情况,不是错;告诉用户"这篇没收录摘要"即可
|
|
|
|
## 反模式
|
|
|
|
- 用 `httpx` / `requests` 裸调 paper_server API(走 helper,免得 base_url / auth / 字段名漂移时四处改)
|
|
- `search(limit=50)` 一次拉满后扔给 LLM 全文 dump(只 print 前 5-10 条精简就够,要全部让用户自己 `print(papers)`)
|
|
- 没看 abstract 就 `fetch_pdf`(80% 场景 abstract 够用,下载 PDF 慢且费带宽)
|
|
- 编造 DOI / title / 作者 —— 不在 paper_server 库里就**明确告诉用户"未命中"**,不要凭训练数据脑补
|
|
- 把 `fetch_pdf` 返回的相对路径当绝对路径用(它是相对 `working_dir` 的)
|