Omi Crossref App:为 Omi 对话工具打造的无认证学术文献检索集成
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
本篇技术指南围绕开源仓库GitHub_Trending/fr/Friend中 plugins/omi-crossref-app 插件展开,系统讲解如何基于公共 Crossref REST API 构建一套免认证(no-auth)的 Omi chat tools 集成服务。读完本文,你将掌握该插件的三个核心工具(文献搜索、DOI 详情查询、按作者检索)的接口契约、底层调用链与部署方式,并能直接本地运行或发布到 Railway/Heroku 类平台。
插件定位:无认证的学术元数据查询服务
omi-crossref-app是 Omi 插件体系中的独立子应用,其目标是在对话场景中为 AI 提供学术论文元数据检索能力。与仓库内其他需要 OAuth 或 API Key 的插件(如 plugins/omi-google-calendar-app、plugins/omi-dropbox-app)不同,它直接使用公共的 Crossref REST API(https://api.crossref.org),不依赖任何环境变量、OAuth 流程或密钥,天然适合作为轻量 chat tool 接入。
从 README 的定位描述"A no-auth Crossref integration app for Omi chat tools"与 main.py 中 FastAPI 应用的标题Crossref Omi Integration可以确认:该插件只承担"查询、清洗、返回文本结果"这一件事,不涉及数据库、鉴权与用户态存储。
三个核心工具及其请求契约
README 声明了三个工具,其参数契约完整定义在 main.py 的/tools端点以及 models.py 的 Pydantic 模型中:
| 工具名 | 功能 | 输入参数 | 默认值 |
|---|---|---|---|
search_crossref_works | 按关键词搜索学术文献 | query(必填)、max_results(1–10) | max_results=5 |
get_crossref_work | 按 DOI 获取单篇文献详情 | doi(必填) | — |
get_crossref_works_by_author | 按作者名列出近期文献 | author(必填)、max_results(1–10) | max_results=5 |
三个输入模型SearchWorksInput、GetWorkInput、AuthorWorksInput均继承pydantic.BaseModel,其中max_results默认为 5。所有工具响应统一使用ChatToolResponse(result/error二选一的可选字段),这保证了 Omi 侧解析结果时接口形状一致。
1. search_crossref_works:关键词检索
对应实现位于 main.py。它先将query去除首尾空白,并校验长度至少 2 个字符;随后调用crossref_get("/works", {"query": query, "rows": limited, "sort": "relevance", "order": "desc"}),即按相关度降序向 Crossref 拉取最多limited条结果,最后逐条输出序号. 标题 (年份)与DOI: xxx两行文本。
注意max_results并非直接透传:clamp_max_results(main.py)会将其钳制在max(1, min(10, value))区间内,防止越界请求。
2. get_crossref_work:按 DOI 查详情
对应实现位于 main.py。入参doi需满足两个校验:必须包含/(否则返回Invalid DOI format. Example: 10.1038/nphys1170),且不得包含..(防路径注入类异常值)。合法后将 DOI 通过urllib.parse.quote编码后请求/works/{doi},输出Title / DOI / Year / Publisher / URL五行,并在存在摘要时追加Abstract: {abstract[:1200]}(截断至 1200 字符以控制 token 消耗)。
3. get_crossref_works_by_author:按作者检索
对应实现位于 main.py。逻辑与关键词检索对称,但请求参数改为query.author、sort: "published"、order: "desc",即按出版时间降序返回该作者最近的作品列表。
底层实现要点:JATS 清洗与年份提取
Crossref 的abstract字段经常携带 JATS XML 标记(如<jats:p>、<jats:italic>),而 chat tools 需要纯文本。clean()函数(main.py)按三层正则处理:
- 无条件剥离
<jats:...>开闭标签(_JATS_TAG); - 无条件剥离通用闭合标签(
_CLOSE_TAG); - 对开标签要求其前一个字符不是字母/数字/下划线(
_OPEN_TAG使用负向后行断言),从而保留a<b这类数学不等式。
同时clean()会先执行html.unescape,将&还原为&。这一行为有专门的单测佐证:test_clean.py 中的四个用例分别验证了 JATS 段落标签剥离(<jats:p>Sleep quality improved <jats:italic>slightly</jats:italic>.</jats:p>→Sleep quality improved slightly.)、HTML 实体反转义、不等式操作符保留(If a<b, then c>d.原样输出)以及闭合标签清理。
年份提取由extract_year()(main.py)完成:按published-print→published-online→issued的优先级读取date-parts[0][0],全部缺失时返回空字符串,保证输出行格式稳定。
HTTP 调用层与超时控制
所有上游请求统一走crossref_get()异步封装(main.py):每次调用创建httpx.AsyncClient(timeout=20.0),拼接CROSSREF_BASE(https://api.crossref.org)发起 GET,异常时由上层捕获并转为ChatToolResponse(error=...)返回。20 秒超时对 Crossref 公共 API 的典型响应时间留有余量,同时避免工具调用长期挂起拖慢对话。
依赖版本钉死在 requirements.txt:fastapi==0.104.1、uvicorn==0.24.0、httpx==0.27.0、pydantic==2.5.2,保证构建可复现。
本地运行
README 给出了最简启动方式:
pip install -r requirements.txt uvicorn main:app --reload --host 0.0.0.0 --port 8080启动后服务提供四类端点:
GET /health:健康检查,返回{"status": "ok"}(main.py);GET /tools:人类可读的工具清单(名称、描述、参数);GET /.well-known/omi-tools.json:Omi 平台约定的工具清单端点(main.py),声明每个工具的endpoint、method: POST、参数properties/required、auth_required: false及status_message(如Searching Crossref...),供 Omi 侧自动发现与挂载;POST /tools/{tool_name}:三个工具的实际执行端点。
可以先用 curl 做端到端自测:
# 搜索文献 curl -X POST http://localhost:8080/tools/search_crossref_works \ -H "Content-Type: application/json" \ -d '{"query":"graph neural network", "max_results":3}' # 按 DOI 查详情 curl -X POST http://localhost:8080/tools/get_crossref_work \ -H "Content-Type: application/json" \ -d '{"doi":"10.1038/nphys1170"}' # 按作者查近期作品 curl -X POST http://localhost:8080/tools/get_crossref_works_by_author \ -H "Content-Type: application/json" \ -d '{"author":"Geoffrey Hinton", "max_results":3}'同一仓库中的 plugins/omi-arxiv-app 遵循完全相同的架构(/health+/.well-known/omi-tools.json+POST /tools/*),可作为对照参考。
部署到 Railway / Heroku
插件为平台即服务(PaaS)部署做了两重准备:
- Procfile:
web: uvicorn main:app --host 0.0.0.0 --port ${PORT:-8080},兼容 Heroku 等按PORT环境变量注入端口的平台,本地缺省回退 8080; - railway.toml:Railway 部署配置,
builder = "NIXPACKS"自动识别 Python 项目,startCommand同样读取$PORT,并配置healthcheckPath = "/health"、healthcheckTimeout = 100、restartPolicyType = "ON_FAILURE"、restartPolicyMaxRetries = 10。
由于服务无状态且不读写本地文件,--reload仅用于开发环境;生产部署时直接以 Procfile/railway.toml 中不带--reload的启动命令运行即可。
在 Omi 生态中的角色与适用边界
从 plugins/main.py 的聚合入口可见,Omi 插件体系把各集成作为独立 FastAPI 子应用挂载;而omi-crossref-app这类"工具型"插件则更倾向于独立部署,通过/.well-known/omi-tools.json清单被对话运行时发现,属于 Omi 的 chat tools 能力通道。
需要明确两点适用边界:其一,本插件是只读的元数据查询服务,不提供文献全文下载、引用导出或账号体系;其二,结果质量完全取决于 Crossref 公共索引的收录与字段完整度——缺失title/date-parts的记录会回退为Untitled/空年份(见clean与extract_year的防御逻辑),摘要字段仅在有值时输出且截断至 1200 字符。基于这些源码级事实,本插件最适合作为学术场景对话助手的轻量检索后端,与 plugins/omi-pubmed-app(NCBI E-utilities)、plugins/omi-arxiv-app(arXiv Atom feed)共同构成免认证学术检索工具矩阵。
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考