news 2026/9/12 13:45:53

用 `gxl_paperclip` Python SDK 将 Paperclip 科研检索编程化:安装、连接、批量检索与流式 map/reduce 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 `gxl_paperclip` Python SDK 将 Paperclip 科研检索编程化:安装、连接、批量检索与流式 map/reduce 实战指南

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上复核;其中searchhealthpapers.headresults.listexecute被实际运行验证过,而 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

三条硬性注意事项:

  1. 必须用完整 URL 安装,不能用包名gxl-paperclip没有发布到 PyPI,而 PyPI 上确实存在的paperclip包是无关软件——执行uv pip install paperclip会装上错误的东西(这一点在 installation.md 中被同样强调)。
  2. wheel URL 不带版本号:重建环境时可能拿到更新的 SDK,行为变化时先查gxl_paperclip.__version__
  3. 安装器捆绑副本只应急:如果只是在装有 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_urltimeout(默认 120.0 秒)、user_agentsession

当环境变量不够用时,可以显式选择鉴权策略:

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_TOKENPAPERCLIP_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_ids_*格式 id,传给map_/reduce/results
result_data服务端产生结构化负载时提供
download_urldownload_filename命令产生文件时设置
cwd调用后的虚拟工作目录
raw未解析的服务端响应

注意 ANSI 颜色码

searchoutput携带 ANSI 颜色码,解析或落日志前需要剥离:

import re ANSI = re.compile(r"\x1b\[[0-9;]*m") clean = ANSI.sub("", result.output)

papers.*的输出是纯文本,不需要剥离。这与 search-and-retrieval.md 中「catheadgrep输出是稳定纯文本」的观察一致。

检索与读取: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_similaritymin_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.catmeta.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 的实测细节,有几条重要的实战纪律:

  1. map_充足的timeout:一个 LLM reader 读十篇论文通常会超过 120 秒默认值,要么在调用处传大 timeout,要么在 client 构造时全局设置。
  2. map 查询要枚举字段并要求显式 "not reported":worker 只返回你问的内容,不要求缺省说明就无法区分「论文没报」和「reader 漏了」。
  3. --strategy table返回的是散文而不是表格(0.7.14 与 0.7.15 均已验证,带不带--columns都是如此);需要对比表时,用results取回每篇论文输出自己构建。
  4. reduce 输出内嵌的 citation marker 的文档 id 被截断到 8 个字符且无法解析(例如PMC12388实际是PMC12388858),据此构造的引用 URL 是死链——真实 id 从searchresultsmeta.json获取。SDK 侧同样不要直接从 reduce 的 output 里提取 citation 用 id。
  5. 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 0download_url None、不写文件。当前没有可行的本地图片下载路径,图片分析请全程在服务端ask_image完成;用户确实需要原图时,给出meta.json里的出版社 URL。
  • CLI 侧的二进制读取同样不可用:cat fig.jpg > out.jpg会把每个非 UTF-8 字节替换成U+FFFDcp到本地被拒,也没有 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")

逃生门:executestream

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.reposclient.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") # 返回 bytes
client.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, })

两条增强建议:

  1. 优先用result_data而非解析output:渲染文本是 UI 表面,其格式不是稳定契约;服务端填充result_data时,用它。
  2. 解析 result id 用正则s_[a-f0-9]{8}:由于 search-and-retrieval.md 记录了 search 输出形状不确定(渲染文本与原始 JSON 交替出现,--json也强制不了),不要对 search 的output写完整解析器;可靠的做法是正则提取 id、用papers.catmeta.json、用results.list恢复 id。

版本与适用前提

  • 本文所有行为依据 Paperclip0.7.14 与 0.7.15,SDK 签名用inspect读取并复核;运行环境要求 macOS 或 Linux(installation.md 说明原生安装器不支持 Windows)。
  • searchhealthpapers.headresults.listexecute为本仓库写作时实际执行过;reposlibrarymap相关调用按签名转写,大规模使用前请先实测或查阅paperclip <cmd> --help
  • SDK 与 CLI 共享同一服务端与同一缺陷面:bash()不可用、pull()写不出二进制、exit_code不反映沙箱失败、reduce 的 citation marker id 截断——这些在 CLI 与 SDK 两侧表现一致。

参考文件

文件内容
references/python-sdk.mdgxl_paperclipPython 客户端(本文主体)
SKILL.md虚拟文件系统、操作规则、引用契约、已知缺陷总览
references/map-reduce.mdmap workers、reduce 策略、results 导出、ask-image
references/search-and-retrieval.md来源列表、排序模式、查询技巧、SQL 模式
references/cli-reference.md全部命令与标志、沙箱限制、二进制与/.gxl/细节
references/repos-and-workspace.mdrepo、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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 13:45:16

Unity脚本性能优化实战:从Profiler定位到GC与对象池

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:42:05

TradingAgents-CN Docker 容器化部署指南:一键构建股票分析环境

TradingAgents-CN Docker 容器化部署指南&#xff1a;一键构建股票分析环境 【免费下载链接】TradingAgents-CN 基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版 项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN TradingAgents-CN 是…

作者头像 李华
网站建设 2026/9/12 13:41:03

CEF、Electron、Tauri桌面开发硬边界深度对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:39:48

物联网毕设选题全指南:100个可落地题目与技术选型避坑清单

每年到了做毕业设计的季节&#xff0c;后台和私信里就堆满了类似的提问&#xff1a;"学长&#xff0c;物联网毕设题目怎么选啊""有没有那种看起来不水、但又不会把自己做崩的题目""我单片机基础很弱&#xff0c;敢选智能家居吗"。说实话&#xf…

作者头像 李华