内容
# refkit
中立、轻依赖的**创意作品引用检索工具包** — 搜索图片 / 视频 / 音频 / 文本作为创意参考,每个结果都带有**许可归一化**,因此每个结果都包含`来源 + 许可证 + 署名 + 标准URL`。

> Apache-2.0 · `v0.2.0` — 添加了一个可选的、零依赖的重新排序器(`lexicalReranker`)。API 接口(`createRefkit`)是稳定的;提供者覆盖范围正在增加。
## 为什么
多媒体创作者经常会“搜索 X 张图片作为参考”或“查找 Y 篇文章来了解结构”。然而,现有的库中没有一个能够结合以下五个功能:**多源聚合 × 每个结果的许可归一化 × 可调用代理 × 可嵌入的 BYOK SDK × 视觉和文本**。refkit 填补了这一空白。
其核心竞争力在于**不是**简单的多源扩展(一种商品),而是**许可归一化 + 严格拒绝使用门控 + 双模态契约**,以及将结果流入生成管道作为具有溯源的资产。
## 安装
```bash
pnpm add @refkit/core @refkit/provider-openverse @refkit/provider-met
```
`@refkit/core` 是核心;每个来源都是一个薄的 `@refkit/provider-*` 卫星,您可以根据需要添加。
## 快速入门
```ts
import { createRefkit } from '@refkit/core'
import { openverse } from '@refkit/provider-openverse'
import { met } from '@refkit/provider-met'
import { unsplash } from '@refkit/provider-unsplash'
const refkit = createRefkit({
providers: [
openverse(), // 无密钥
met(), // 无密钥
unsplash({ accessKey: process.env.UNSPLASH_KEY! }), // BYOK
],
// fetch 默认使用 globalThis.fetch — timeouts/retries/caching 内置(见下文)
})
// 扩展、合并(倒数排名融合)+ 去重;每个结果都带有权利。
const refs = await refkit.search({ query: 'cyberpunk alley at night', modalities: ['image'], limit: 12 })
for (const r of refs) {
// 意图:'internal-moodboard' | 'commercial-product' | 'ai-generation-input' | 'redistribution'
const verdict = refkit.evaluateUse(r, 'commercial-product')
// 'allowed' | 'allowed-with-attribution' | 'denied' | 'needs-review'
if (verdict.decision === 'allowed-with-attribution') {
console.log(r.canonicalUrl, refkit.buildAttribution(r).text)
}
}
// 或在搜索时设置门控 - 只返回商业上可用的结果:
const safe = await refkit.search({ query: 'forest', modalities: ['image'], gateFor: 'commercial-product' })
```
## 搜索控制
对主要路径使用提供者中立的`controls`。refkit 仅将每个控制路由到声明支持的提供者,并且`searchWithMeta()`会解释哪些提供者应用或忽略了每个控制:
```ts
await refkit.search({
query: 'brutalist library interior',
modalities: ['image'],
controls: {
orientation: 'landscape',
color: 'blue',
language: 'en-US',
sort: 'relevance',
safety: 'strict',
license: { commercial: true, modification: true },
media: { minWidth: 1200, minHeight: 800 },
},
})
```
## 排名和重新排序
默认情况下,结果通过**倒数排名融合**跨来源融合 - 跨来源可排序,但不适用于查询。对于更清晰的相关性,请传递一个**重新排序器**:
```ts
import { createRefkit, lexicalReranker } from '@refkit/core'
const refs = await refkit.search({
query: 'cyberpunk alley at night',
modalities: ['image'],
rerank: lexicalReranker(), // 零依赖,无模型,无网络
})
```
`lexicalReranker(opts?)` 是默认配置:它通过查询↔(标题+摘录)术语覆盖率、分辨率质量和许可宽容度来评估每个结果,然后使用 MMR-lite 传播来源,以便一个提供者不会主导。所有权重都是可调的:
```ts
rerank: lexicalReranker({ qualityWeight: 0.3, licenseWeight: 0.2, sourceDiversity: 0.15 })
```
对于**语义**排名,请自带 - `Reranker`钩子接收 `{ query, refs, signal }`并返回重新排序的 refs,因此您可以将 CLIP/embedding/LLM 重新排序器连接到您自己的 API。`core`不附带模型;这是唯一的接口:
```ts
rerank: async ({ query, refs }) => myEmbeddingRerank(query, refs)
```
重新排序是**可选的** - 省略它以获取默认 RRF 顺序。它在合并后运行,在`gateFor`许可过滤器和限制之前。
## 提供者
| 包名 | 来源 | 模态 | 认证 | 许可 |
| --- | --- | --- | --- | --- |
| `@refkit/provider-openverse` | Openverse (CC 聚合器) | 图片 · 音频 | 无密钥 | 每项 CC / PD |
| `@refkit/provider-wikimedia-commons` | Wikimedia Commons | 图片 | 无密钥 | 每项 CC / PD |
| `@refkit/provider-met` | 大都会艺术博物馆 | 图片 | 无密钥 | CC0 |
| `@refkit/provider-artic` | 芝加哥艺术学院 | 图片 | 无密钥 | CC0 |
| `@refkit/provider-smithsonian` | 史密森尼开放获取 | 图片 | API 密钥 | CC0 |
| `@refkit/provider-flickr` | Flickr | 图片 | API 密钥 | 每项 CC / PD |
| `@refkit/provider-unsplash` | Unsplash | 图片 | API 密钥 | Unsplash |
| `@refkit/provider-pexels` | Pexels | 图片 · 视频 | API 密钥 | Pexels |
| `@refkit/provider-pixabay` | Pixabay | 图片 · 视频 | API 密钥 | Pixabay |
| `@refkit/provider-gutendex` | 古腾堡计划 | 文本 | 无密钥 | 每项 PD |
| `@refkit/provider-poetrydb` | PoetryDB | 文本 | 无密钥 | PD |
| `@refkit/provider-brave` | Brave 网络搜索(发现) | 图片(网络) | API 密钥 | 未知 → 需要审查 |
| `@refkit/provider-rijksmuseum` | Rijksmuseum | 图片 | 无密钥 | CC0 / PD |
| `@refkit/provider-polyhaven` | Poly Haven + ambientCG | 图片 | 无密钥 | CC0 |
| `@refkit/provider-freesound` | Freesound | 音频 | API 密钥 | 每项 CC / CC0 |
| `@refkit/provider-jamendo` | Jamendo | 音频 | API 密钥 | 每项 CC |
| `@refkit/provider-europeana` | Europeana | 图片 | API 密钥 | 每项 CC / PD / 权利声明 |
| `@refkit/provider-internet-archive` | Internet Archive | 视频 · 文本 | 无密钥 | 每项 CC(脏)→ 未知 |
音频/视频是现有软件包的额外工厂:`openverseAudio()`、`pexelsVideo()`、`pixabayVideo()`。模态路由是自动的 - `['audio']`搜索仅会命中支持音频的提供者。
## 架构
```
@refkit/core 中立的大脑 — 零网络,零提供者,只有 zod
参考契约 · 权利模型 + 许可事实 · 严格拒绝使用门控 ·
RRF 跨源合并/去重 · 参考提供者接口 · 双模态信封
@refkit/provider-* 薄卫星 - 每个源一个;商品层
@refkit/mcp 代理面 - 通过 MCP 暴露 search_references
```
(主机绑定)将 Reference 映射到主机的资产/生成模型;
注入密钥(BYOK),获取,缓存。生活在消费应用程序中,不在这里。
**依赖方向是单向的:** `provider-*` → `core`;主机 → `core`。`core`仅依赖于 `zod`,而不依赖于任何主机或编排框架。
## 核心不变性(由`@refkit/core`中的测试强制执行)
- **核心中没有网络** - 没有`fetch`调用,没有硬编码的端点。主机注入`ProviderContext.fetch`。
- **不重新托管** - 仅保留`canonicalUrl` + 缩略图;绝不存储原件。
- **严格拒绝** - 当无法确定权利时,拒绝/需要审查(绝不开放失败)。未知、非商业和“没有已知的版权限制”绝不映射到商业上可用的许可证;NoDerivatives 允许逐字商业重用(带有署名)但绝不允许派生/AI 使用。
## 代理用法
代理可以通过两种方式使用 refkit:
1. **主机工具中的 SDK** - 您的应用程序定义自己的`search`工具,连接`createRefkit({ providers, fetch, cache })`,并控制密钥、缓存、重试、重新排序器、过滤器和提供者特定的选项。
2. **MCP 适配器** - `@refkit/mcp`通过`search_references`暴露相同的许可归一化搜索,对于您想要的零胶水工具,可以跨 MCP 代理工作。
## MCP
`@refkit/mcp`通过[模型上下文协议](https://modelcontextprotocol.io)公开`search_references`,因此任何 MCP 代理都可以使用零胶水代码搜索许可归一化参考。
**零配置** - 指向一个 MCP 客户端:
```bash
npx -y @refkit/mcp
```
它使用无密钥源(Met,Art Institute,Wikimedia,Openverse,Project Gutenberg,PoetryDB,Rijksmuseum,Poly Haven,ambientCG,Internet Archive)启动,并自动添加任何 BYOK 源,其密钥在环境中(`REFKIT_UNSPLASH_KEY`,`REFKIT_PEXELS_KEY`,`REFKIT_BRAVE_KEY`,... - 旧名称如`UNSPLASH_KEY`,`PEXELS_KEY`,`BRAVE_TOKEN`仍然作为后备)。传递`intent`以注释每个结果的使用判决(我可以使用这个吗?是否需要署名);`gateFor`返回仅允许的结果。除了搜索外,`evaluate_use`和`build_attribution`将相同的许可判决和归属逻辑暴露为独立的无状态工具,当代理已经有一个许可 ID并且只需要判决或信用额度时。
## 不是法律建议
`evaluateUse`返回基于来源声明的许可/ToS事实的**保守启发式**。它**不是法律建议**,不确定法律权利。每个判决都带有`disclaimer`和`confidence`。对于真正的法律立场(尤其是将参考资料输入 AI 生成时),请咨询律师。
## 开发
```bash
pnpm install
pnpm typecheck # 所有软件包
pnpm test:run # 所有软件包
pnpm build # tsup → dist 适用于所有软件包
```
`REFKIT_LIVE=1 pnpm test:run`针对真实的提供者 API 运行实时烟雾测试(BYOK 需要设置密钥环境变量);CI 中的每周 cron 运行相同的检查。
发布版本通过 [changesets](https://github.com/changesets/changesets) 自动进行:运行 `pnpm changeset` 记录更改;合并 CI 生成的“版本包”PR 发布到 npm。