refactor(skills): 统一论文与书籍检索路由

This commit is contained in:
caoqianming 2026-08-25 15:02:33 +08:00
parent c8a823c50c
commit 5d9ee823b5
3 changed files with 64 additions and 228 deletions

View File

@ -1,7 +1,7 @@
# zcbot Skill 清单
服务对象:中国建筑材料科学研究总院 —— 无机非金属材料 R&D(水泥 / 混凝土 / 玻璃 / 陶瓷 / 耐火 / 新型建材)
最后更新:2026-08-21(coding 增加 Node.js/TypeScript 优先的技术栈路由与静态多文件 Web 预览)
最后更新:2026-08-25(research / documents 统一支持论文、书籍与章节查阅,并精简检索路由约束)
Skill 总数:18
zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` + 配套 templates / scripts / Python helper),模型在识别用户意图后挂载对应 skill,按其内置的阶段化流程产出可交付物。本文档面向**使用方 / 协作方**,按"做什么、什么时候用、什么时候别用、典型产物"组织。
@ -22,8 +22,8 @@ zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` +
| 科研写作 | [rebuttal](#rebuttal) | 回复审稿意见(修回):逐点回复信 + 稿件修改清单 + 修回 cover letter |
| 演示出图 | [ppt](#ppt) | 生成可编辑 PowerPoint(SVG-first:逐页手写 SVG → 原生 DrawingML;总院红品牌模板可选) |
| 演示出图 | [plot_pub](#plot_pub) | 出版级 matplotlib 学术图(中文 + viridis + 矢量 + 投稿级复合图设计纪律) |
| 文献检索 | [research](#research) | 查 paper_server(OpenAlex 元数据 + Sci-Hub 下载) |
| 文献检索 | [documents](#documents) | 查内部 7 学科材料知识库(100W+ 论文,跨语言检索;host-side tool 持 key) |
| 文献检索 | [research](#research) | 查 paper_server 中的论文、书籍与章节(题录 / 摘要 / 可用全文) |
| 文献检索 | [documents](#documents) | 查内部 7 学科材料知识库(论文 / 书籍 / 全文语义检索) |
| 文献检索 | [brief](#brief) | 科研方向简报:三路检索(research + 内部库取文献 / web 取动向)→ 重要论文列表(带摘要概述)+ 内容总结,只描述不给建议 |
| 科研计算 | [pymatgen](#pymatgen) | 晶体结构 / XRD 模拟 / 相图 / Materials Project(host-side tool 持 key) |
| 科研计算 | [stats_ml](#stats_ml) | 配方-性能建模与机器学习(三库分工) |
@ -254,36 +254,22 @@ zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` +
## 文献检索
### research
**查 paper_server 文献库(基于 OpenAlex 元数据 + Sci-Hub 下载的内部部署)。**
**检索独立 `paper_server` 项目中的论文、书籍和书籍章节。**
paper_server 是内部 Django 文献库:元数据来自 OpenAlex,PDF / XML 由 Sci-Hub / OpenAlex 异步抓取。**库里主语料是英文**(OpenAlex 主索引英文文献),少量中文
`research` 是 zcbot 对 `paper_server` 的客户端适配可取得题录、DOI、摘要及记录实际提供的 PDF/XML。适合跨学科发现、精确题名或 DOI 查找、出版类型识别和引文核验;材料领域任务可与 `documents` 并查
**何时用**:
- 查 / 找 / 看 / 推荐文献
- 要 DOI、要某篇 PDF、要 abstract
- 写申报书 / 研究方案 / 调研报告的"国内外现状"段需要真实文献支撑
- 配合 `proposal` skill 的「立项依据」起草
按任务所需证据深度选择题录、摘要或全文:结构化段落适合 XML页码、公式、图表与版式核验适合 PDF。输出明确区分“仅题录”“摘要已核对”“全文已核对”。
**何时不用**:
- 只问通识 → 直接答
- 已经给了具体文献清单 → 直接用,不要二次校验
**四个 helper**:`search` / `get_paper` / `fetch_pdf` / `fetch_xml`
**关键规则**:`keyword` 优先用英文(库里 95%+ 文献 title 是英文,中文 keyword 命中率很低)。中文术语先转专业英文术语再搜。
**四个 helper**:
- `search(keyword, year, year_gte, year_lte, doi, first_author, publication_name, has_pdf, is_oa, limit)` —— SearchFilter 匹配 title / first_author / first_author_institution
- `get_paper(id_or_doi)` —— 精准取一篇
- `fetch_pdf(id_or_doi)` —— 拉 PDF 文件
- `fetch_xml(id_or_doi)` —— 拉 OpenAlex 全文 XML
**典型产物**:候选文献清单(16 字段) → 选定文献的 abstract / PDF / 引用条目。
**典型产物**:论文、书籍和章节候选清单,以及可核验的摘要、全文和引用条目。
---
### documents
**查内部材料学科知识库(document_search API)。**
**检索内部 7 个材料学科知识库中的论文、书籍及其他学术文档。**
部署在 `https://ai.ctc-zc.com:8100/api`。后端按 `kb_name` 分库存 7 个材料学科,共 **100W+ 英文学术论文**(Elsevier 期刊为主,DOI 前缀文件名)。每个文档带 `md_content`(整篇 Markdown,LLM 友好)+ 可选原 PDF 下载。
`kb_name` 分为胶凝、陶瓷、玻璃、晶体、复合、耐火和检验检测 7 个材料学科库提供跨语言语义检索、Markdown 正文片段和原件下载。
**7 大学科库**(`classification_id` 1-7):
| 学科 | 内容 |
@ -297,14 +283,14 @@ paper_server 是内部 Django 文献库:元数据来自 OpenAlex,PDF / XML 由 S
| 检验检测 | 表征方法 / 标准 / 仪器 |
**核心特色**:
- **跨语言语义检索** —— 中文 query 也能命中英文论文(API 后端处理)
- **已 Markdown 化** —— LLM 直接读,免 OCR / XML 解析
- **跨语言语义检索** —— 中英文查询均可用于发现学术文档
- **Markdown 正文** —— 可直接筛选论点、数据和章节,必要时再读取原件
**何时用**:
- 查材料领域文献 / 特定材料性能 / 工艺数据
- 写申报书 "国内外现状" 段(本库 Markdown 比 research 拿到的裸 PDF/XML 更直接可用)
- 查材料领域论文、书籍、性能数据或工艺信息
- 需要全文语义检索,或为申报书、方案、报告和综述寻找可核验证据
**关系**:与 research 互补 —— research 搜全网,documents 是本地预收的材料学科子集。**找材料类文献优先 documents,找其他学科或要 DOI 走 research,两者命中不重叠时可并用**
**关系**:与 `research` 互补。`documents` 擅长材料领域全文语义检索,`research` 擅长跨学科题录和 DOI 检索;重要任务可并查,并按 DOI 或规范化题名去重。论文或书籍的来源类型不作为排他路由条件
**三个 host-side tool**:`document_list_kb` / `document_search` / `document_download`。只有宿主配置 `DOCUMENT_SEARCH_API_KEY` 时注册;key 不进入 sandbox。

View File

@ -1,99 +1,41 @@
---
name: documents
description: 查内部材料学科知识库(document_search API,7 个学科:胶凝 / 陶瓷 / 玻璃 / 晶体 / 复合 / 耐火 / 检验检测,100W+ 英文学术论文 Markdown 化,跨语言语义检索)。用户找材料领域文献、特定学科论文、材料性能数据时使用;与 research(OpenAlex 外部库)互补,可并用 / 同时试
description: 检索内部 7 个材料学科知识库中的论文、书籍及其他学术文档。适合材料领域主题检索、全文语义检索、性能或工艺数据查找;可与 research(paper_server)并用并按 DOI 或题名去重
---
# Documents
部署在 `https://ai.ctc-zc.com:8100/api` 的文档检索 API。后端按 `kb_name` 分库存储 7 个材料学科库(中文命名:胶凝 / 陶瓷基 / 玻璃基 / 晶体材料 / 复合材料 / 耐火材料 / 检验检测,共 100W+ 文件),**文档主体是英文学术论文**(Elsevier 期刊为主,DOI 前缀文件名),每个文档带 `md_content`(整篇 Markdown,LLM 友好)+ 可选的原 PDF 下载。**API 后端有跨语言语义检索**,中英文 query 都能命中英文文档。本 skill 使用三个 host-side tool:`document_list_kb` / `document_search` / `document_download`,**不要**自己 `httpx` 裸调,也不要在 `run_python` 里读 `DOCUMENT_SEARCH_API_KEY`
`documents` 是内部材料知识库的客户端 skill。知识库覆盖胶凝、陶瓷、玻璃、晶体、复合、耐火和检验检测提供跨语言语义检索、Markdown 正文片段和原件下载
> ⚠️ **配置条件**:只有宿主后端配置了 `DOCUMENT_SEARCH_API_KEY` 时,上述 tool 才会出现在可用工具列表里。若没有 `document_*` tool,降级走 `research` skill(OpenAlex + Sci-Hub,不受影响,中文 query 先转专业英文术语)/ 用户自己导出文档落 task 目录后用 `read` 工具读。**别让 LLM 误推**:research 跟本 skill 不同范式,research 不持 secret,任何模式都能用。
## 选择与协作
## 何时用
- 材料领域主题、性能数据或全文语义检索:优先使用 `documents`
- 跨学科发现、精确 DOI 或题录检索:优先使用 `research`
- 系统调研、综述或重要引用:可同时查询两者;按 DOI 去重,无 DOI 时按规范化题名、作者和年份去重。
- 用户已提供文件或个人知识库路径:直接读取该来源。
- 用户要查材料领域文献(7 个学科:胶凝 / 陶瓷 / 玻璃 / 晶体 / 复合 / 耐火 / 检验检测)
- 用户要查特定材料性能 / 工艺数据(实验数据 / 表征结果 / 公式 / 表格)
- 写申报书 / 方案 / 报告需要"国内外现状"段,本库 Markdown 化的论文比 research 拿到的裸 PDF/XML 更直接可用
- **跟 research 并列**:都是文献检索 —— research 走 OpenAlex 元数据 + Sci-Hub PDF,搜全网;documents 是本地预收的材料学科子集,**已转 Markdown**(LLM 直接读,免 OCR / XML 解析)+ **跨语言语义检索**(中文 query 也能命中英文论文)。找材料类文献优先 documents,找其他学科或要 DOI 走 research,**两者命中不重叠时可并用**
论文、书籍和书籍章节都可作为候选,来源类型本身不决定使用哪个 skill以相关性、元数据完整度和可核验正文为准。
## 何时不用
## 工具
- 用户只问通识(直接答)
- 用户已经给了具体内部文档路径(直接读,不要二次校验)
- 用户问的是**自己上传的自建资料**(规范 / 报告 / 内部文档)→ 走个人知识库(system prompt「个人知识库」段注入的 `.kb/` 索引,fs 工具直接 read/grep),不在本库搜
本 skill 使用宿主工具API Key 不进入 sandbox
## 三个 tool
- `document_list_kb()`:列出当前有效知识库。用户未指定材料方向且需要缩窄库范围时调用。
- `document_search(queries, kb_names=None, classification_ids=None, max_documents=6, content_chars_per_doc=1200)`:批量搜索,返回文件元数据与截断的 `md_content`。中文和英文均可,专业术语可同时准备中英文表达。
- `document_download(items)`:把选定原件下载到当前任务的 `documents/` 目录。`items` 使用搜索结果中的 `file_name``kb_name`
### `document_list_kb()`
工具会自动限制批量大小、去重并控制返回体积。需要更多正文时,对少量高相关候选增加 `content_chars_per_doc`,或下载原件后读取。
列所有有效知识库(分类 1-7)。每条含 `id` / `kb_name` / `ch_name` / `kb_info` / `file_count` 等。
## 工作流
**用途**:用户没指定库 → 先 `document_list_kb` 看有哪些库(中文名 `ch_name` 看分类),再选 `kb_names` / `classification_ids` 缩窄 search 范围。
1. 根据用户问题形成少量有区分度的查询;需要分类时先列知识库。
2. 搜索并依据题名、文件名、正文片段和库来源筛选候选。
3. 相关性判断可使用片段;论点、数据、公式、表格、章节或页码核验应读取足够正文或原件。
4. 与 `research` 并查时合并去重,优先保留正文更完整、元数据更可靠的记录。
5. 输出时区分“仅题录”“片段已核对”“全文已核对”并标注可追溯的文件名、DOI 或其他真实标识。
### `document_search(queries, kb_names=None, classification_ids=None, max_documents=6, content_chars_per_doc=1200)`
## 失败与真实性
搜文档,**一次可传多个 query 并发搜**,返回精简列表,每条带 **截断后的 `md_content`**
- `queries`:**搜索词列表**(1-8 条)。把你这一轮想搜的所有不同 query 一次性传进来(`queries=["alkali-activated slag strength", "fly ash cement hydration", ...]`),**别一个 query 一轮 tool call** —— 反复来回每轮重发整段上下文,轮数是 token 体量的线性乘数。**中英文均可** —— 文档主体是英文学术论文,但 API 后端有跨语言语义检索;复杂技术术语用**英文**更精准(`cement hydration` > `水泥水化`),日常概念中文 OK。**批内自动去重**;**别堆一堆只差几个词的近义 query**(边际递减),先想清楚一组互不重叠的 query 再批量发
- `kb_names`:知识库白名单(从 `document_list_kb` 选,对所有 query 生效);`None` 走 server 默认(单库 `mu_34_1740625285897` 胶凝)。**多库联查就显式传**,如 `kb_names=["mu_34_1740625285897", "mu_34_1740625303475"]`
- `classification_ids`:分类 ID 白名单(1-7,对应 7 个学科库,对所有 query 生效);`None` 不过滤
- `max_documents`:每个 query 返回几篇,1-20,默认 6(**批量多 query 时自动缩量**控制总输出)
- `content_chars_per_doc`:每篇返回多少 Markdown 字符,默认 1200,最大 5000(**批量多 query 时自动缩量**);不要一上来拉满
**学科库 → kb_name 速查**(`document_list_kb` 拿全量,这里只列常用):
| 学科 | kb_name |
|---|---|
| 胶凝材料(水泥 / 混凝土 / 砂浆) | `mu_34_1740625285897` |
| 陶瓷基材料 | `mu_34_1740625303475` |
| 玻璃基材料 | `mu_34_1740625318986` |
| 晶体材料 | `mu_34_1740625346474` |
| 复合材料 | `mu_34_1740625355308` |
| 耐火材料 | `mu_34_1740625365079` |
| 检验检测 | `mu_34_1740625376621` |
### `document_download(items)`
下载原始文档(PDF / Word / ...)到 `<working_dir>/documents/<safe_file_name>`,返回各自相对路径。**一次可传多个文档并发下载**,单条失败不连坐其余。已存在跳过下载直接复用。
- `items`:文档列表(1-10 条),每条 `{file_name, kb_name, preview?}`,如 `items=[{"file_name":"a.pdf","kb_name":"mu_34_1740625285897"}, {"file_name":"b.pdf","kb_name":"mu_34_1740625285897"}]`。**要下几篇就一次性列进来,别一篇一轮 tool call**
- `file_name` 支持原始文件名(`example.pdf`)或 Markdown 名(`example.md`),server 自动回退
## 标准工作流
1. **(可选)`document_list_kb`** —— 用户没指定库 / 不确定分类时看一下有哪些
2. **`document_search(queries=[...])`** —— 先规划好一组互不重叠的 query 一次性批量搜,中英文均可,专业技术术语优先英文
3. **看返回**:
- 用 `file_name + character_count + md_content` 判断切题
- 切题 → 直接用返回的 Markdown 摘要给 LLM 引用;需要更多上下文时对**少数**命中文档提高 `content_chars_per_doc` 单独重搜
- 需要看图表 / 表格原貌 / 给用户附件 → `document_download(items=[...])` 一次性批量拿原文档,然后用主 agent 的 `read` 工具读(zcbot 已内置 PDF/Word 文本抽取)
4. **写产出**:把 md_content 关键段落引到申报书 / 方案里,标注来源文件名
## md_content 优先 vs 原件下载
- **绝大多数场景用 md_content 就够** —— API 已把原文档转成 Markdown,LLM 直接读,无需 OCR 或 PDF 抽取
- **仅以下场景下载原件**:
- 用户明说"要原文件"(给客户附件 / 存档 / 引用页码)
- md_content 里图表 / 表格 / 公式信息丢失(Markdown 转换无损不了)
- 文档过大(`character_count` > 10 万),想用 PDF reader 跳页抽取局部
## 错误处理
- 没有 `document_*` tool:`DOCUMENT_SEARCH_API_KEY` 未在宿主配置,改走降级路径
- 401 / 403 `Invalid API key`:`httpx.HTTPStatusError` —— key 错或失效,告诉用户检查 env
- 404 `未找到知识库`:`kb_names` 拼写错或库已下线,改 `document_list_kb` 看当前有效列表
- 404 `文件不存在: xxx`:`document_download` 时常见,可能 server 侧文件丢失或 `file_name` 拼写错
- search 命中 0 条:同义词 / 切换中英文 / 缩短 query / 放宽 `classification_ids` 再试 2-3 次,还是 0 条就明确告诉用户"本库没覆盖,改走 research 或换关键词",**不要凭训练数据脑补文献**
- 网络超时 / server 不可达:`httpx.ConnectError` / `httpx.TimeoutException` —— 告诉用户"document_search 暂时连不上",不要重试堆栈刷屏
## 反模式
- 用 `httpx` / `requests` 裸调 API(走 host tool,免得 base_url / auth / 字段名漂移时四处改,也避免 key 进入 sandbox)
- `document_search(max_documents=20, content_chars_per_doc=5000)` 一次拉满(20 条直接爆 LLM 上下文)—— 先用默认值判断切题,只对少数命中文档加大 `content_chars_per_doc`
- **一个 query 一轮 tool call 地反复搜**(同一意图换着措辞搜十几遍)—— 这是最烧 token 的反模式:每轮重发整段上下文。改成**先列一组去重 query 一次 `queries=[...]` 批量发**;一批结果看完不够再发下一批,而不是一条一条挤
- `document_download` 一篇一轮 tool call —— 把要下的都列进 `items=[...]` 一次下完
- 看到 md_content 切题还 `download` 一遍原件(md_content 已是 LLM 友好的 Markdown,大多数引用场景够用)
- 凭 `ch_name`("胶凝材料学科知识库")就以为 query 要用中文 —— 文档主体是英文,复杂术语用英文更精准
- 编造 file_name / kb_name —— 不在 `document_list_kb` / `document_search` 返回里就**明确告诉用户"未命中"**,不要瞎传 ID
- 把 `document_download` 返回的相对路径当绝对路径用(它是相对 task_dir 的)
- 尝试给 `document_download``working_dir`(tool 已绑定当前 task_dir,`items` 里只放 `file_name` / `kb_name`,不要让模型指定路径)
- 工具不可用或认证失败时,说明 `document_search` 当前不可用,并改用 `research` 或用户提供的文件。
- 未命中时可调整术语、语言或库范围;没有新增检索思路时如实说明覆盖不足。
- 只使用工具返回的 `file_name`、`kb_name` 和来源信息不推测缺失的作者、DOI、ISBN、页码或正文内容。

View File

@ -1,138 +1,46 @@
---
name: research
description: 查 paper_server 文献库(基于 OpenAlex 元数据 + Sci-Hub 下载的内部部署)。用户要查文献、找 DOI、拉 PDF、做文献综述、写带引文的申报书 / 研究方案 / 调研报告时使用。
description: 检索 paper_server 中的论文、书籍和书籍章节获取题录、DOI、摘要及可用的 PDF/XML 全文。适合跨学科文献发现、精确文献查找和引文核验;材料领域可与 documents 并用。
---
# Research
paper_server 是内部部署的 Django 文献库:元数据来自 OpenAlex,PDF / XML 由 Sci-Hub / OpenAlex 异步抓取。**库里主语料是英文**(OpenAlex 主索引英文文献),少量中文。本 skill 给你四个 helper(`search` / `get_paper` / `fetch_pdf` / `fetch_xml`),用 `run_python` 调用,**不要**自己 `httpx` 裸调 API
`research` 是 zcbot 对独立 `paper_server` 项目的客户端适配。`paper_server` 以 OpenAlex 元数据为基础并按记录实际可用性提供摘要、PDF 或 XML当前语料以英文为主也包含中文文献
## 何时用
## 选择与协作
- 用户要查 / 找 / 看 / 推荐文献
- 要 DOI、要某篇 PDF、要 abstract
- 写申报书 / 研究方案 / 调研报告的"国内外现状"段需要真实文献支撑
- 配合 `proposal` skill 的「立项依据」起草 —— 先 `search` 拿候选,看 abstract 决定要不要引
- 跨学科发现、题名或 DOI 检索、出版类型识别:优先使用 `research`
- 材料领域全文语义检索、性能或工艺数据:优先使用 `documents`
- 系统调研、综述或重要引用:可同时查询两者;按 DOI 去重,无 DOI 时按规范化题名、作者和年份去重。
- 用户已提供具体文件:直接读取;用户只提供题录而任务要求核验论断时,继续获取摘要或全文。
## 何时不用
论文、书籍和书籍章节均可检索。是否读取摘要、XML 或 PDF由用户问题所需的证据深度决定。
- 用户只问通识(直接答即可,不需要文献支撑)
- 用户已经给了具体文献清单(直接用,不要二次校验)
## 调用方式
## 准备
通过 `run_python` 使用 helper
```python
from skills.research.paper import search, get_paper, fetch_pdf, fetch_xml
```
(import 路径由 `run_python` 注入的 `PYTHONPATH` 提供,直接写就行,不必折腾 `sys.path`)
- `search(keyword="", year=None, year_gte=None, year_lte=None, doi="", first_author="", publication_name="", has_pdf=None, is_oa=None, limit=10)`搜索题录返回类型、题名、作者、年份、期刊或出版物、DOI、摘要及全文可用状态。
- `get_paper(id_or_doi)`:按 `paper_server` ID 或 DOI 获取单条完整记录。
- `fetch_xml(id_or_doi, working_dir)` / `fetch_pdf(id_or_doi, working_dir)`:把可用全文下载到当前任务的 `papers/` 目录并返回相对路径。
> **本 skill 持一把低价值 key**。paper_server 的 GET 接口要 `PAPER_SERVER_API_KEY`(helper 自动以 `?api_key=` 查询参数带上,你不用管);它是唯一刻意放进 sandbox 的凭证 —— `run_python` 的 env 过滤器对它放行,docker 模式由宿主透传进容器 → host / docker 任何模式都能用。`PAPER_SERVER_URL` 可选覆盖(默认 `http://paper.xxhhcty.xyz:8080`)。**别因 documents / pymatgen 报"key 没配"就连带放弃 research**,这俩不可用时它就是降级首选。
helper 自动使用 `PAPER_SERVER_API_KEY` 和可选的 `PAPER_SERVER_URL`。通过 helper 访问 `paper_server`由它处理认证、URL 和下载路径
## 关键:keyword 优先用英文
## 工作流
`search(keyword=...)` 走 paper_server SearchFilter,模糊匹配 **title / first_author / first_author_institution**(目前不含 abstract)。库里 95%+ 文献 title 是英文,中文 keyword 命中率很低。
1. 将用户概念转换为常用专业术语;英文题名占多数时优先使用英文,并可用中文或同义词补充。
2. 用主题、年份、作者、出版物或 DOI 搜索并筛选候选;`type` 可帮助识别论文、书籍和章节。
3. 相关性初筛可使用题名和摘要;精确论断、数据、章节、图表或页码应获取足够正文。
4. 结构化段落和参考文献通常适合读取 XML版式、页码、公式和图表通常适合读取 PDF。只获取记录实际提供的格式。
5. 与 `documents` 并查时合并去重,输出中区分“仅题录”“摘要已核对”“全文已核对”。
**用户输入中文 → 先转成专业英文术语再 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
```python
# 找近 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` 等)时用。
```python
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`
```python
rel = fetch_pdf("10.1016/j.cemconres.2020.106156", working_dir="<task_dir>") # <task_dir> = system prompt 给的工作目录绝对路径(docker 沙盒里形如 /workspace/<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。
## 标准工作流
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=True``fetch_xml`(LLM 友好,结构化)
- 仅 `has_fulltext_pdf=True``fetch_pdf`(回退,需要 PDF 文本抽取)
- 两者都 False → 仅凭 abstract 写综述,告诉用户哪几篇没全文
5. **read 全文**:fetch 返回相对路径,用主 agent 的 `read` 工具读取(zcbot 已内置 PDF 文本抽取;XML 直接当文本读)
## 错误处理
- 网络超时 / paper_server 不可达:`httpx.ConnectError` / `httpx.TimeoutException` —— 告诉用户"paper_server 暂时连不上",不要重试堆栈刷屏
- `paper_server auth failed (HTTP 400/401/403, not_authenticated/...)`:`PAPER_SERVER_API_KEY` 未配置或无效 —— 告诉用户找管理员在 `.env` 配 key,换 keyword 重试没用
- `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` —— 服务器还没下到对应格式;若另一格式存在则换用,都没就只能用 abstract
- `xml_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 条告诉用户库里没覆盖,**不要凭训练数据脑补文献**
## 反模式
- 用任何 HTTP 客户端(`httpx` / `requests` / `urllib` / `aiohttp` / shell `curl`)裸调 paper_server API —— 一律走 helper。裸调跳过本 skill "中文转英文术语" / `has_pdf` / `year_gte` filter 教学,典型坑:`search=cement+based` 字符级模糊匹配返几千条横跨无人机 / 锂电池 / 热界面,LLM 还以为搜对了
- 用户输中文直接 `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` 的)
- 认证或网络失败时,明确说明 `paper_server` 当前不可用;认证失败需要管理员检查 zcbot 的 `PAPER_SERVER_API_KEY` 配置。
- DOI 未命中、全文格式缺失或服务器文件不存在时,可改查其他候选或使用现有摘要,不把“已收录”写成“已读全文”。
- 未命中时可调整术语和过滤条件;没有新增检索思路时如实说明覆盖不足。
- 只引用返回记录中的真实题名、作者、DOI 和正文,不推测缺失的 ISBN、出版社、版本、页码或内容。