用gxl_paperclipPython SDK 将 Paperclip 科研检索编程化:安装、连接、批量检索与流式 map/reduce 实战指南
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
导读
本指南以 python-sdk.md 为骨架,系统讲解 Paperclip 官方 Python 客户端gxl_paperclip的完整用法:它是对 CLI 所连接的同一服务端的类型化客户端,适合把 Paperclip 工作放进脚本、notebook 或后台服务——例如批量检索、将结果与自有数据 join、把 map 进度流式渲染进 UI。读完本文你将掌握 SDK 的安装与鉴权策略、ExecuteResult返回契约、语义检索与元数据 SQL、虚拟文件系统读写、流式 map/reduce、图片分析、错误处理以及可复用的批量处理模式,并清楚哪些能力在 0.7.14–0.7.15 版本上是确认不可用的坑。
为什么需要 SDK:CLI 之外的编程入口
在 SKILL.md 中,Paperclip 被描述为一座只读虚拟文件系统:约 1100 万篇全文论文、21.7 万+ 监管文档、11 万+ 临床试验方案、57.4 万+ 蛋白条目,统一以 Unix 风格的目录和命令导航,背后由服务端语义检索与 LLM reader 支撑。CLI 适合交互式的一次性查询,而gxl_paperclip则面向需要代码化编排的场景。
两种入口的取舍(源自 python-sdk.md 开篇):
- CLI:交互式一次性查询更快,适合在终端里随手检索、grep、读取。
- SDK:批量检索、把结果 join 进自己的数据管线、把 map 进度流式推送进 UI、在服务或 notebook 中调用。
SDK 签名基于已安装包在0.7.14上通过inspect读取、并在0.7.15上复核;其中search、health、papers.head、results.list、execute被实际运行验证过,而 repo、library 和 map 相关调用是按其签名转写、未在本仓库内实际执行(写作时请在真实环境用--help或实测复核)。
安装:从 wheel URL 安装,而不是包名
curl 安装器会把 SDK 捆绑进自己的私有目录~/.paperclip/lib/,但它不在你的sys.path上。因此真实使用时应把它装进你掌控的环境:
uv pip install https://paperclip.gxl.ai/paperclip.whl # 或(作为项目依赖) uv add https://paperclip.gxl.ai/paperclip.whl三条硬性注意事项:
- 必须用完整 URL 安装,不能用包名:
gxl-paperclip没有发布到 PyPI,而 PyPI 上确实存在的paperclip包是无关软件——执行uv pip install paperclip会装上错误的东西(这一点在 installation.md 中被同样强调)。 - wheel URL 不带版本号:重建环境时可能拿到更新的 SDK,行为变化时先查
gxl_paperclip.__version__。 - 安装器捆绑副本只应急:如果只是在装有 CLI 的机器上跑个快速脚本,可以把
~/.paperclip/lib临时插进sys.path借用,但它与安装器布局强耦合,布局一变就会失效:
import sys sys.path.insert(0, "/Users/you/.paperclip/lib") from gxl_paperclip import PaperclipClient安装完成后验证版本:
import gxl_paperclip print(gxl_paperclip.__version__) # 写作时为 0.7.15连接与鉴权:从环境变量到显式 Auth 策略
最简连接方式是from_env(),它会优先读取PAPERCLIP_API_KEY环境变量,回退到paperclip login写入的 OAuth 凭据(~/.paperclip/credentials.json):
from gxl_paperclip import PaperclipClient client = PaperclipClient.from_env()from_env()还接受可选关键字参数:base_url、timeout(默认 120.0 秒)、user_agent、session。
当环境变量不够用时,可以显式选择鉴权策略:
from gxl_paperclip import PaperclipClient, APIKeyAuth, BearerAuth, FileCredentialsAuth client = PaperclipClient(APIKeyAuth("gxl_..."), timeout=300.0)连接检查:
status = client.health() # HealthStatus(reachable=True, output='Health: healthy\nInit: True', exit_code=0, elapsed_ms=15)与 CLI 鉴权机制相互印证
SDK 的from_env()解析顺序在 installation.md 中有对应描述:PAPERCLIP_BEARER_TOKEN→PAPERCLIP_API_KEY→~/.paperclip/credentials.json。而 CLI 侧的解析优先级是--api-key标志 →PAPERCLIP_API_KEY环境变量 →~/.paperclip/credentials.json(OAuth)。关键行为是:环境中的 key 会短路 OAuth 完全——即使存在已登录账号,也会被导出的 key 静默覆盖。另外 Paperclip 没有 dotenv 支持(config.py只读os.getenv("PAPERCLIP_API_KEY", "")),.env文件必须由 shell 先导出到环境里。SDK 场景下同理:确保 key 真正进入了进程环境,而不是躺在某个.env文件里。
ExecuteResult:几乎所有调用的统一返回契约
SDK 中几乎所有方法都返回ExecuteResult,其字段含义如下:
| 字段 | 含义 |
|---|---|
output | 渲染后的文本,即 CLI 会打印的内容 |
exit_code | 成功时为 0 |
elapsed_ms | 服务端延迟 |
result_id | s_*格式 id,传给map_/reduce/results |
result_data | 服务端产生结构化负载时提供 |
download_url、download_filename | 命令产生文件时设置 |
cwd | 调用后的虚拟工作目录 |
raw | 未解析的服务端响应 |
注意 ANSI 颜色码
search的output携带 ANSI 颜色码,解析或落日志前需要剥离:
import re ANSI = re.compile(r"\x1b\[[0-9;]*m") clean = ANSI.sub("", result.output)而papers.*的输出是纯文本,不需要剥离。这与 search-and-retrieval.md 中「cat、head、grep输出是稳定纯文本」的观察一致。
检索与读取:search、lookup、sql
语义检索search
result = client.search("CRISPR delivery", limit=5, source="pmc") print(result.result_id) # s_5e9cc4f4完整签名:
client.search( query, limit=None, source=None, exact=False, since=None, sort=None, author=None, journal=None, year=None, type=None, category=None, mode=None, min_embedding_similarity=None, min_bm25_score=None, all=False, timeout=None, )参数与 CLI 的对应关系(可对照 cli-reference.md 的search一节):
source等价于 CLI 的-s,行为一致——必须传。可传逗号分隔的多个来源,如"pmc,biorxiv"。mode对应--ranking,取值hybrid(默认,语义+关键词)、bm25(纯词法,适合基因符号、目录号)、vector(纯语义)、analogical(跨领域结构类比)。min_embedding_similarity和min_bm25_score是没有 CLI 等价物的分数下限,用于批量任务中压制弱的尾部匹配。- 查询措辞比参数更重要:embedding 模型基于论文摘要微调,给它摘要形状的文本(完整摘要,或一两句描述方法与问题的句子)效果最好,裸关键词在
analogical模式下几乎无效。
精确查找与 SQL
client.lookup("doi", "10.1073/pnas.2307796121", limit=None) client.sql("SELECT source, COUNT(*) FROM documents GROUP BY source") client.sql("SELECT COUNT(*) FROM uniprot_v.proteins", source="proteins")lookup是精确元数据匹配(doi/pmc/pmid/arxiv/author/journal/title等字段),无排序、无语义。sql仅允许SELECT,15 秒超时、200 行上限;它只能看到标题和摘要,不是全文检索——Methods/Results 里出现的词它看不到,ILIKE '%X%'是未索引扫描。要回答「哪些论文提到 X」请用 grep。
读取文档:client.papers虚拟文件系统
Paperclip 的每个文档都有统一结构(content.lines每行带L<n>:前缀、meta.json存放元数据、sections/存放分节文件、figures/与supplements/存放附属内容)。SDK 通过client.papers暴露文件类操作:
client.papers.ls("/papers/PMC10945750/") client.papers.cat("/papers/PMC10945750/meta.json") client.papers.head("/papers/PMC10945750/content.lines", lines=40) client.papers.tail("/papers/PMC10945750/content.lines", lines=20) client.papers.grep("lipid nanoparticle", "/papers/PMC10945750/content.lines", ignore_case=True, extended=False) client.papers.scan("/papers/PMC10945750/content.lines", ["IC50", "EC50", "dose"])head返回的文本保留L<n>:前缀,引用行号可以直接从输出里拿:
out = client.papers.head("/papers/PMC10945750/content.lines", lines=3).output # 'L1: Targeted nonviral delivery of genome editors in vivo\nL2: Proceedings of the National...'把元数据解析为 dict:
import json meta = json.loads(client.papers.cat("/papers/PMC10945750/meta.json").output) meta["doi"], meta["journal"], meta["pub_year"]对应 CLI 侧的等价操作可参考 search-and-retrieval.md 的 grep/scan 一节:grep是真正的全文搜索工具(能命中正文、方法、数据可用性、参考文献列表),scan用一次请求扫描多个模式并分组返回。注意 CLI 中head/tail只对.lines文件有效,meta.json要用cat——SDK 中papers.cat读meta.json与此同源。
map 与 reduce:跨论文的流式读取与综合
map_是流式的——它返回事件迭代器而非单一结果:
from gxl_paperclip import MapProgressEvent, MapResultEvent for event in client.map_("What delivery vector and efficiency were reported?", from_results="s_abc123"): if isinstance(event, MapProgressEvent): print("progress:", event) elif isinstance(event, MapResultEvent): print("result:", event)reduce则把各论文的答案综合成一个输出:
client.reduce( "Compare vector, cell type, and efficiency", from_map="m_def456", strategy="table", # summarize | table | themes | consensus | bullet_points | extract columns=["paper", "vector", "efficiency"], )结合 map-reduce.md 的实测细节,有几条重要的实战纪律:
- 给
map_充足的timeout:一个 LLM reader 读十篇论文通常会超过 120 秒默认值,要么在调用处传大 timeout,要么在 client 构造时全局设置。 - map 查询要枚举字段并要求显式 "not reported":worker 只返回你问的内容,不要求缺省说明就无法区分「论文没报」和「reader 漏了」。
--strategy table返回的是散文而不是表格(0.7.14 与 0.7.15 均已验证,带不带--columns都是如此);需要对比表时,用results取回每篇论文输出自己构建。- reduce 输出内嵌的 citation marker 的文档 id 被截断到 8 个字符且无法解析(例如
PMC12388实际是PMC12388858),据此构造的引用 URL 是死链——真实 id 从search、results或meta.json获取。SDK 侧同样不要直接从 reduce 的 output 里提取 citation 用 id。 - map 的结果 id 前缀是
m_,reduce 产物是r_(对应 CLI 中s_/m_/r_的 id 体系)。
Results:找回丢失的 result id
for row in client.results.list(limit=10): print(row.result_id, row.command, row.created_at) # s_5e9cc4f4 search 2026-07-28T01:05:31.651263+00:00 data = client.results.get("s_5e9cc4f4") # ResultData: result_id, output, command, latency_ms, ...results.list相当于 CLI 的paperclip results --list,展示最近的 result id 与产生它的命令,是丢失 id 后的恢复通道。CLI 侧还支持results <id> --save out.csv(稳定表头title,authors,id,source,date,url,abstract),SDK 中结构化行数据优先看result_data。
图片、文件与任意命令
图片分析:ask_image
图文件名由出版社决定,不是fig1.jpg,所以先papers.ls解析出真实文件名:
client.papers.ls("/papers/PMC10945750/figures/") # pnas.2307796121fig01.gif pnas.2307796121fig01.jpg fig = "/papers/PMC10945750/figures/pnas.2307796121fig01.jpg" client.ask_image(fig, "What is on each axis?") client.ask_image(fig, fn="extract-data")重要限制(0.7.14–0.7.15 实测):
pull()对二进制不起作用:返回exit_code 0、download_url None、不写文件。当前没有可行的本地图片下载路径,图片分析请全程在服务端ask_image完成;用户确实需要原图时,给出meta.json里的出版社 URL。- CLI 侧的二进制读取同样不可用:
cat fig.jpg > out.jpg会把每个非 UTF-8 字节替换成U+FFFD,cp到本地被拒,也没有 CLI 版pull(详见 cli-reference.md 的「Binary files cannot be retrieved」)。
上传文档
client.upload_document("analysis.json", data_bytes, folder_path="analyses/my-topic", content_type="application/json")逃生门:execute与stream
SDK 未包装的任何命令都可以通过逃生门调用:
client.execute("search", ["-s", "pmc", "CRISPR delivery", "-n", "5"]) for event in client.stream("map", ["--from", "s_abc123", "question"]): ...已知缺陷:client.bash()不可用
client.bash()在 0.7.14–0.7.15 上不可用——它把整段脚本当作单个命令名传过去,所以连普通命令都会失败,管道和重定向自然一起挂掉:
r = client.bash("grep -c CRISPR /papers/PMC10945750/content.lines") r.output # 'ERR: vsh: grep -c CRISPR /papers/PMC10945750/content.lines: command not found. ... [exit 126]'正确做法是execute()传参数列表,管道逻辑在 Python 侧完成:
client.execute("grep", ["-c", "CRISPR", "/papers/PMC10945750/content.lines"])这与 cli-reference.md 记录的 CLI 侧行为完全一致:paperclip bash '...'和|/>到达grep时都会变成字面量参数——这是服务端虚拟 shell 的行为,不是 CLI 或 SDK 单方面的问题。
exit_code不报告沙箱失败
上面的例子揭示了重要语义:调用返回exit_code == 0,但沙箱在output里报告了[exit 126]。ExecuteResult.exit_code描述的是 HTTP 层的命令分发结果,而不是vsh内部实际执行的命令的结果。因此要同时检查 output:
r = client.execute("cat", ["/papers/PMC99999999/meta.json"]) failed = r.exit_code != 0 or r.output.lstrip().startswith("ERR:")CLI 确实能暴露真实状态——paperclip search -s pmc "x"配坏 key 会以退出码 1 结束——所以 shell 调用方可以依赖$?,而 SDK 调用方不行。
Repos 与 Library:底层接口需要先解析名字
client.repos和client.library比 CLI 更底层:它们接受repo id 和 entry id而非名字,所以要先按名字解析。
repo = client.repos.get_repo_by_name("my-review") client.repos.create_repo("my-review", "Delivery vectors") client.repos.add_papers(repo["id"], [{"document_id": "PMC10945750"}]) client.repos.annotate_paper(repo["id"], entry_id, "Claim text", lines="L45-L52") client.repos.commit(repo["id"], "Initial citations") client.repos.get_status(repo["id"]) client.repos.create_branch(repo["id"], "safety") client.repos.merge_branches(repo["id"], "safety", "main") bibtex = client.repos.export(repo["id"], "bibtex") # 返回 bytesclient.library.list_papers(page=1, per_page=50, search="fine-tuning") client.library.upload_pdfs([("paper.pdf", pdf_bytes)]) job = client.library.poll_import_job(job_id, timeout_s=900) client.library.delete_paper(paper_id)两条与 CLI 相同的工作纪律(源自 SKILL.md 与 repos-and-workspace.md):
- 仓库是 opt-in 的:除非用户明确要求建立受追踪的文献集合或做声明验证,否则不要创建 repo。默认做法是直接读行并引用。
- 仓库里
commit会并行对照全文验证每条声明,标记[OK]或[X];最终作答前运行repo status,只引用[OK]的声明。repo commit存的是声明元数据而非文件——持久化生成的文件要用upload_document。
错误处理:统一的异常层级
所有异常都继承自PaperclipError:
from gxl_paperclip import ( PaperclipError, AuthError, RateLimitError, NotFoundError, ServerError, RequestTimeoutError, NetworkError, ) try: result = client.search("query", source="pmc", limit=5) except AuthError: ... # 凭据缺失或过期——重新登录,或检查 PAPERCLIP_API_KEY except RateLimitError: ... # 退避后重试 except RequestTimeoutError: ... # 调大 timeout,尤其围绕 map_ except PaperclipError: ... # 其他一切exit_code与异常是两条独立的信号通道:一个调用可能成功返回但带着非零exit_code(例如全库 grep 无匹配),两个都要检查。
批量模式:把整套能力串成管线
文档末尾给出一个可直接扩展的批处理模板——搜索 → 逐篇读元数据 → grep 关键值 → 汇总为行:
import json, re from gxl_paperclip import PaperclipClient ANSI = re.compile(r"\x1b\[[0-9;]*m") client = PaperclipClient.from_env(timeout=300.0) hits = client.search( "lipid nanoparticle mRNA delivery to hematopoietic stem cells", source="pmc", limit=10, ) rows = [] for doc_id in extract_ids(ANSI.sub("", hits.output)): # 你的解析函数 meta = json.loads(client.papers.cat(f"/papers/{doc_id}/meta.json").output) ic50 = client.papers.grep("IC50", f"/papers/{doc_id}/content.lines", ignore_case=True) rows.append({ "id": doc_id, "title": meta["title"], "doi": meta.get("doi"), "year": meta.get("pub_year"), "ic50_lines": ic50.output, })两条增强建议:
- 优先用
result_data而非解析output:渲染文本是 UI 表面,其格式不是稳定契约;服务端填充result_data时,用它。 - 解析 result id 用正则
s_[a-f0-9]{8}:由于 search-and-retrieval.md 记录了 search 输出形状不确定(渲染文本与原始 JSON 交替出现,--json也强制不了),不要对 search 的output写完整解析器;可靠的做法是正则提取 id、用papers.cat读meta.json、用results.list恢复 id。
版本与适用前提
- 本文所有行为依据 Paperclip0.7.14 与 0.7.15,SDK 签名用
inspect读取并复核;运行环境要求 macOS 或 Linux(installation.md 说明原生安装器不支持 Windows)。 search、health、papers.head、results.list、execute为本仓库写作时实际执行过;repos、library、map相关调用按签名转写,大规模使用前请先实测或查阅paperclip <cmd> --help。- SDK 与 CLI 共享同一服务端与同一缺陷面:
bash()不可用、pull()写不出二进制、exit_code不反映沙箱失败、reduce 的 citation marker id 截断——这些在 CLI 与 SDK 两侧表现一致。
参考文件
| 文件 | 内容 |
|---|---|
| references/python-sdk.md | gxl_paperclipPython 客户端(本文主体) |
| SKILL.md | 虚拟文件系统、操作规则、引用契约、已知缺陷总览 |
| references/map-reduce.md | map workers、reduce 策略、results 导出、ask-image |
| references/search-and-retrieval.md | 来源列表、排序模式、查询技巧、SQL 模式 |
| references/cli-reference.md | 全部命令与标志、沙箱限制、二进制与/.gxl/细节 |
| references/repos-and-workspace.md | repo、claims、分支、clipboard、import、library |
| references/installation.md | 安装器、鉴权优先级、MCP 配置、排障 |
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考