一次输入,同时打到多个搜索来源,结果统一成同一种格式、跨来源去重合并,再按来源分组交给你。 本体是一个命令行工具;这一页把其中三个不需要登录态的公开源接上了服务端出口,你可以直接在下面试。
同一个问题,答案分散在互不相通的几个站里。于是你开五个标签页,同一串关键词敲五遍, 再自己在脑子里合并——哪些是同一篇、哪个先看、哪个站今天登录掉了。metasearch 把这一整套动作压成一次输入。
「有没有现成的库」这件事,代码仓库、社区讨论、问答站各知道一半。同时问过去,你拿到的是一张合并过的表, 而不是几个标签页。
预印本、引用图谱、DOI 元数据分散在不同库里。同一个术语一次打过去,同一篇论文若在多个库同时命中, 会被识别成一条并标注出「哪几个来源都收录了它」。
某个源超时、限流、掉登录,只让它自己降级并把原因摆出来,其余源照常返回——你知道这次少看了什么, 而不是默默少了一块。这条在下面的演示里可以直接点出来看。
本网页版接了三个源:GitHub、arXiv、Hacker News。 它们都是公开检索接口,不需要任何登录态,所以能由服务端代发。
本地完整版还有小红书、知乎、个人知识库等来源,公网版接不了—— 它们要么依赖浏览器登录态,要么依赖只存在于本机的私域凭证与本地索引文件。 网页托管别人的登录凭据这件事不该做,也做不对,所以这一页就是三个源,不是缩水的完整版,是公开源那一半。
三个源同时发出(不是排队轮问),谁先回来先渲染谁,总耗时取决于最慢的那个源而不是三者相加。 每个源单独显示耗时;某个源失败只标它自己失败,其余照常显示。
合并视图用的就是下面这套逻辑(URL 归一化 + 跨源合并),在你的浏览器里当场算,不联网。
改改试试:把 utm_source 换个值、把参数顺序调一下、加个 # 锚点,看它还认不认得这是同一条。
| 来源 | 领域 | 后端 | 开箱状态 | 网页版 |
|---|---|---|---|---|
| github | 代码仓库 | GitHub 公开检索接口(按 star 数给分) | 本地需 gh 已登录 | 已接 |
| arxiv | 学术预印本 | arXiv 官方 Atom API | 零配置 | 已接 |
| hackernews | 技术趋势 | Hacker News 全文检索 API | 零配置 | 已接 |
| web | 通用网页 | DuckDuckGo;配了环境变量则改用 SearXNG / Brave / Google 自定义搜索 | 零配置 | 未接 |
| openalex | 开放学术图谱 | OpenAlex API(可选密钥仅用于提额) | 零配置 | 未接 |
| crossref | DOI / 引文元数据 | Crossref REST(带 mailto 走礼貌池) | 零配置 | 未接 |
| semantic_scholar | 学术(摘要 / 引用) | Semantic Scholar Graph API(可选密钥更稳) | 零配置 | 未接 |
| europepmc | 生物医学 | Europe PMC REST | 零配置 | 未接 |
| stackexchange | 技术问答 | Stack Exchange API(默认站点 Stack Overflow) | 零配置 | 未接 |
| npm | JavaScript 包 | npm registry 搜索端点 | 零配置 | 未接 |
| cratesio | Rust 包 | crates.io API | 零配置 | 未接 |
| wikipedia | 百科 | MediaWiki Action API(默认英文站,改一个常量即切语种) | 零配置 | 未接 |
| gdelt | 全球新闻(65 语种) | GDELT DOC 2.0(限速每 5 秒 1 次) | 零配置 | 未接 |
| xhs | 小红书 | xhs 命令行工具,复用浏览器登录态(按点赞数给分) | 需装 CLI 并登录 | 要登录态 |
| kb | 本地知识库 | 本地 SQLite 全文检索(FTS5)命令行 | 需本地知识库 | 私域本机 |
| notion | 个人文档 | Notion 官方搜索 API | 需个人 token | 私域凭证 |
| tavily | 通用网页(面向 AI) | Tavily Search API | 需 API 密钥 | 私域凭证 |
| youtube | 视频 | YouTube Data API v3 | 需 API 密钥 | 私域凭证 |
| 论坛 | Reddit 搜索;未授权时公开端点会被拒,需 OAuth 凭据 | 需 OAuth 凭据 | 私域凭证 | |
| tieba | 百度贴吧 | aiotieba;贴吧无全局搜索,只能吧内搜,须指定吧名(可多吧并发) | 需指定吧名 | 要登录态 |
| bili | 哔哩哔哩 | bili 命令行工具 | 需装 CLI | 要登录态 |
# 看看当前注册了哪些源 metasearch --list-sources # 只问三个源,每源最多 5 条,JSON 输出到屏幕 metasearch "claude code" --sources web,github,arxiv --limit 5 # 不指定源 = 全部启用的源一起上 metasearch "vector database" # 单源超时收紧到 8 秒(慢源自动降级,不等它) metasearch "vector database" --sources web,hackernews --timeout 8 # 出 Markdown / 自包含 HTML,写到文件 metasearch "transformer" --format md -o out.md metasearch "transformer" --format html -o out.html
核心是一份只有两个字段要求的适配器契约:每个源实现 async search(query, limit),
把自家返回映射成统一的结果对象(标题 / 链接 / 来源 / 摘要 / 分值 / 时间,外加一份原始负载兜底,不丢信息)。
派发层拿到的是一组同构对象,因此它只认契约不认源——21 个源里既有原生异步 HTTP 调用,也有包成线程的同步子进程命令行,
在派发层眼里没有区别。所有源用一次 asyncio.gather 并发出去,每个源套一层独立的超时与异常隔离壳:
它只会返回「结果」或「降级原因」,绝不向上抛——这是「单源失败不拖垮整体」在代码层面的落点,而不是一句设计意图。
聚合层做两件事:归一化 URL 后跨源去重合并(保留高分那条,累计多源标记),以及组内排序。
去重主键是归一化后的 URL:剥掉 utm_* 一类跟踪参数与 fbclid / gclid /
spm / ref 等分享参数、丢掉锚点、把剩余参数排序、统一去掉末尾斜杠;
没有 URL 的结果(本地知识库、部分社交内容)退回用「来源 + 标题」作主键,不会被误合。
上面那个搜索框走的是同一套规则的 JavaScript 复刻版,逐条对齐。
网页版这一层只多做一件事:把三个公开源的请求挪到服务端出口
(GET /api/fetch?src=<github|arxiv|hackernews>&q=<关键词>,
返回 {src, items:[{title,url,meta,snippet}], ms}),
浏览器侧三个请求一起发、各自渲染、各自计时。加来源在命令行版是往目录里丢一个文件;
在网页版则要先回答「这个源需不需要登录态」——需要的那些,答案就是不接。
example.org 链接,不是真实抓取结果,其中「arXiv 502」是故意造的失败,用来演示单源隔离);
②「搜索」按钮会真的向本站 /api/fetch 发三个请求,结果来自 GitHub / arXiv / Hacker News 的公开接口;
③ 来源表描述的是本地命令行版的实际能力。去重演示完全在你的浏览器里计算,输入不离开本机。
本地完整版跑在你自己的机器上,搜索词与结果不经过第三方服务器。