内容
<!-- markdownlint-disable MD033 MD041 -->
<p align="center">
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:F97316,100:7C3AED&height=220§ion=header&text=omnifeed&fontSize=82&fontColor=ffffff&animation=fadeIn&fontAlignY=36&desc=One%20self-hosted%20gateway%20-%20web%20search%20%2B%20crawling%20for%20AI%20agents&descAlignY=56&descSize=17" alt="omnifeed" width="100%"/>
</p>
<p align="center">
<img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=700&size=27&color=F97316¢er=true&vCenter=true&repeat=false&width=820&height=50&lines=%E6%90%9C%E7%B4%A2+%E2%86%92+URL+%E2%86%92+%E5%86%85%E5%AE%B9%EF%BC%8C%E5%9C%A8%E4%B8%80%E4%B8%AA%E9%83%A8%E9%96%80%E4%B8%AD" alt="search → URLs → content, in one gateway"/>
</p>
<p align="center">
<a href="https://github.com/kinorai/omnifeed/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/kinorai/omnifeed/ci.yml?branch=main&label=CI&style=flat-square" alt="CI"/></a>
<a href="https://github.com/kinorai/omnifeed/releases"><img src="https://img.shields.io/github/v/release/kinorai/omnifeed?style=flat-square&color=F97316" alt="Release"/></a>
<a href="https://hub.docker.com/r/kinorai/omnifeed"><img src="https://img.shields.io/docker/pulls/kinorai/omnifeed?style=flat-square&logo=docker&logoColor=white&color=2496ED" alt="Docker pulls"/></a>
</p>
<p align="center">
omnifeed 提供给 AI 代理完整的研发循环 — <b>搜索 → URL → 内容</b> — 针对自我托管的
<a href="https://github.com/searxng/searxng">SearXNG</a> + <a href="https://github.com/unclecode/crawl4ai">crawl4ai</a>,
具有专用的 Reddit 引擎,返回 <b>完整的评论树作为 <a href="https://github.com/toon-format/toon">TOON</a></b>
(比 JSON 少约 40% 的令牌,无损)且 <b>无需 Reddit API 密钥</b>。
</p>
- **`web_search`** 查询 SearXNG 实例(包括 Google/Bing/DDG、Reddit)并返回带有标题和摘要的排名 URL。
- **`fetch_url`** 通过 crawl4ai 将任何 URL 渲染为干净的 Markdown — 而 Reddit URL 返回为完整的评论树,编码为 TOON。
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Activities/Sparkles.png" width="26" height="26" /> 为什么选择 omnifeed
| | omnifeed | 其他 Reddit MCP |
|---|---|---|
| 在一个自我托管的服务中进行网络搜索 → 爬取 | ✅ SearXNG + crawl4ai | ❌ 仅限搜索或仅限爬取 |
| 完整的评论树(/api/morechildren 扩展) | ✅ 最多 40 轮(~4k 评论) | ❌ |
| 令牌高效输出 | ✅ TOON,比 JSON 小约 40% | ❌ 冗长的 JSON 或截断的正文 |
| 非 Reddit URL 的通用爬取备用 | ✅ 通过 crawl4ai | ❌ |
| 前端 | MCP **+** Open WebUI **+** REST | 仅限 MCP(大多数) |
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Clapper%20Board.png" width="26" height="26" /> 演示
<p align="center">
<img src="assets/demo.gif" alt="omnifeed demo — web_search then fetch_url returning a Reddit thread as TOON (render assets/demo.tape with vhs)" width="100%"/>
</p>
<p align="center"><sub><code>web_search</code> → 选择 URL → <code>fetch_url</code> → 完整的 Reddit 评论树为 TOON。使用 <code>vhs assets/demo.tape</code> 生成此剪辑。</sub></p>
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Travel%20and%20places/Rocket.png" width="26" height="26" /> 快速开始
```bash
# 无需克隆 — 获取 compose 文件 + SearXNG 设置,然后启动:
curl -fsSL https://raw.githubusercontent.com/kinorai/omnifeed/main/docker-compose.yml -o docker-compose.yml
curl -fsSL --create-dirs https://raw.githubusercontent.com/kinorai/omnifeed/main/searxng/settings.yml -o searxng/settings.yml
docker compose up
```
启动 omnifeed + SearXNG + crawl4ai — **无需令牌开箱即用**(compose 文件设置 `OMNIFEED_DEV_NO_AUTH=true`),因此 `docker compose up` 直接生效。将 Open WebUI 指向 `http://localhost:8080`,并使用 `WEB_LOADER_ENGINE=external`。 (SearXNG 挂载 `searxng/settings.yml`,启用 `web_search` 所需的 `json` 格式。)请参阅下面的 **身份验证** 以要求 bearer 令牌。
### <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Electric%20Plug.png" width="22" height="22" /> 作为 MCP 服务器(Claude Code、Cursor、Windsurf、...)
**HTTP — 推荐。** `docker compose up` 已经在 `:8081` 上运行 MCP 服务器(开发模式下无需令牌),因此最简单的设置是根本不需要额外的容器 — 将您的客户端指向该 URL:
```jsonc
{ "mcpServers": { "omnifeed": { "url": "http://localhost:8081/mcp" } } }
```
**标准输入/输出 — 仅适用于仅支持标准输入/输出的客户端。** 标准输入/输出服务器由您的客户端启动和拥有(它通过进程的标准输入/输出传输 JSON-RPC),因此它不能是长时间运行的 compose 服务 — 但是您可以从此 compose 文件启动 `mcp` 配置文件,该配置文件保留了所有设置(上游、网络、镜像):
```jsonc
{
"mcpServers": {
"omnifeed": {
"command": "docker",
"args": ["compose", "-f", "/abs/path/to/docker-compose.yml", "run", "-T", "--rm", "mcp"]
}
}
}
```
`run -T` 禁用 TTY 以便 JSON-RPC 管道干净地传输;容器加入堆栈的网络并重用 crawl4ai/SearXNG。 首先启动堆栈(`docker compose up -d`),以便上游服务保持健康。
<details>
<summary><b>独立的标准输入/输出 — 无需 compose 堆栈</b></summary>
直接启动容器,并告诉它 crawl4ai/SearXNG 的可访问位置(如果没有 `OMNIFEED_CRAWL4AI_URL`,omnifeed 将在启动时退出):
```jsonc
{
"mcpServers": {
"omnifeed": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "OMNIFEED_CRAWL4AI_URL=http://host.docker.internal:11235/crawl",
"-e", "OMNIFEED_SEARXNG_URL=http://host.docker.internal:8080",
"kinorai/omnifeed:latest", "--mcp-stdio"
]
}
}
}
```
在 Linux 上,添加 `"--add-host=host.docker.internal:host-gateway"` 到参数中,以便 `host.docker.internal` 解析。
</details>
工具:**`fetch_url`**(始终)和 **`web_search`**(仅当设置了 `OMNIFEED_SEARXNG_URL` 时)。预期的循环是 `web_search` → 选择 URL → `fetch_url`。
`/crawl` 返回 `[{"page_content": "...", "metadata": {...}}]` — 已经是 LangChain / LlamaIndex `Document` 的形状,因此只需几行代码即可将其包装为自定义文档加载器。
### <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Locked%20with%20Key.png" width="22" height="22" /> 身份验证
Compose 堆栈在本地使用时运行 **无需令牌**(`OMNIFEED_DEV_NO_AUTH=true`)。要改为需要 bearer 令牌,请生成一个 — 这是客户端发送的 `Authorization: Bearer <token>` 的值,因此复制它:
```bash
openssl rand -hex 32 # ← 您的令牌;复制此
```
然后在 `docker-compose.yml` 中将 `OMNIFEED_API_KEY` 设置为该值,并删除 `OMNIFEED_DEV_NO_AUTH`。如果没有密钥(并且没有 `OMNIFEED_DEV_NO_AUTH=true`),代理 **拒绝启动**,因此无法意外地将其打开。标准输入/输出 MCP 不需要令牌 — 它继承启动它的进程的信任。
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Gear.png" width="26" height="26" /> 配置
所有内容都使用带有 `OMNIFEED_` 前缀的环境变量进行配置。实际上,您只需要设置 **三个** — `OMNIFEED_API_KEY`、`OMNIFEED_CRAWL4AI_URL` 和(可选)`OMNIFEED_SEARXNG_URL`。其余的都有合理的默认值。
| 变量 | 默认值 | 目的 |
|---|---|---|
| `OMNIFEED_API_KEY` | _(未设置)_ | `/crawl`、`/search`、`/mcp` 的 bearer 令牌。如果未设置,则代理拒绝启动,除非 `OMNIFEED_DEV_NO_AUTH=true`。标准输入/输出 MCP 不受影响。 |
| `OMNIFEED_CRAWL4AI_URL` | _(必需)_ | 上游 crawl4ai 端点。每个引擎都通过它进行抓取;如果为空,则代理在启动时退出。 |
| `OMNIFEED_SEARXNG_URL` | _(未设置)_ | 上游 SearXNG 基本 URL(例如 `http://searxng:8080`)。当未设置时,`web_search` / `/search` 不被公开。实例必须启用 `json` 格式。 |
| `OMNIFEED_DEV_NO_AUTH` | `false` | 在未设置令牌时,以 **无** 身份验证运行 HTTP 传输(本地/开发仅限)。如果设置了令牌,则被忽略。 |
| `OMNIFEED_LISTEN_ADDR` | `:8080` | HTTP 监听地址(`/crawl`、`/search`) |
| `OMNIFEED_MCP_LISTEN_ADDR` | `:8081` | MCP HTTP/SSE 监听地址 |
| `OMNIFEED_MCP_STDIO` | `false` | 通过标准输入/输出运行 MCP(也通过 `--mcp-stdio`) |
| `OMNIFEED_METRICS_ADDR` | `:9090` | Prometheus + 健康监听地址 |
| `OMNIFEED_CRAWL4AI_TIMEOUT` | `90s` | 每次调用 crawl4ai 的超时时间 |
| `OMNIFEED_SEARXNG_TIMEOUT` | `15s` | 每个查询到 SearXNG 的超时时间 |
| `OMNIFEED_SEARCH_MAX_RESULTS` | `25` | 搜索 `limit` 参数的上限(1-100) |
| `OMNIFEED_REDDIT_TIMEOUT` | `4m` | Reddit 线程扩展的时钟上限 |
| `OMNIFEED_REDDIT_MAX_ROUNDS` | `3` | 默认 `/api/morechildren` 轮数(最大 40 轮,`?expand=full`) |
| `OMNIFEED_REDDIT_FORMAT` | `toon` | 默认 Reddit 输出:`toon` 或 `json` |
| `OMNIFEED_REDDIT_FETCH_LIMIT` | `500` | Reddit `limit`:初始树中抓取的最大评论数 |
| `OMNIFEED_REDDIT_DEPTH` | `20` | Reddit `depth`:初始树的嵌套深度 |
| `OMNIFEED_REDDIT_SORT` | `top` | Reddit `sort`:`confidence`(=best)、`top`、`new`、`controversial`、`old`、`random`、`qa`、`live`之一 |
| `OMNIFEED_REDDIT_MAX_COMMENTS` | `0` | 扩展后发出的评论总数上限(0 = 无限) |
| `OMNIFEED_REDDIT_MAX_TOP_LEVEL` | `0` | 顶级评论线程的上限,包括回复(0 = 无限) |
| `OMNIFEED_REDDIT_KEEP_CREATED` | `true` | 包括每个评论的 `created` 时间戳 |
| `OMNIFEED_REDDIT_KEEP_DEPTH` | `false` | 包括每个评论的 `depth` 字段 |
| `OMNIFEED_MAX_URLS_PER_REQUEST` | `30` | `urls[]` 长度上限 |
| `OMNIFEED_PER_DOMAIN_CONCURRENCY` | `2` | 每个域的最大并发请求数 |
| `OMNIFEED_PER_DOMAIN_DELAY` | `1500ms` | 相同域请求之间的最小延迟 |
| `OMNIFEED_BLOCK_PRIVATE_IPS` | `true` | SSRF 保护(在生产中保持开启) |
| `OMNIFEED_LOG_LEVEL` | `info` | `debug`/`info`/`warn`/`error` |
| `OMNIFEED_LOG_FORMAT` | `json` | `json` 或 `text` |
| `OMNIFEED_ENABLE_PPROF` | `false` | 暴露 `/debug/pprof/*`(按需) |
### 控制 Reddit 响应大小
Reddit 线程的评论树可能非常庞大。大小控制共有两种 — 了解哪种是关键:
- **上游 Reddit 参数** — 直接转发到 Reddit 的 API,因此 Reddit 拥有它们的行为:`OMNIFEED_REDDIT_FETCH_LIMIT` → `limit`,`OMNIFEED_REDDIT_DEPTH` → `depth`,`OMNIFEED_REDDIT_SORT` → `sort`。它们决定了 *Reddit 返回的内容*(延迟较少,令牌较少),但 **近似**,并且 `limit`/`depth` 仅绑定 **初始** 获取。语义是 Reddit 的,而不是我们的 — 请参阅 <https://www.reddit.com/dev/api/> → `GET [/r/subreddit]/comments/article`(`limit` = “返回的最大评论数”,`depth` = “子树的最大深度”)。
- **omnifeed 引擎上限** — 我们自己的,应用在获取 + 扩展 **之后**,因此它们是 **精确且独立于 Reddit** 的:`OMNIFEED_REDDIT_MAX_COMMENTS`(截断扁平评论列表)和 `OMNIFEED_REDDIT_MAX_TOP_LEVEL`(保留前 N 个顶级线程,按 `sort` 顺序,包括它们的回复)。
经验法则:优先使用 **上游参数** 以减少从 Reddit 获取的内容;当您需要一个有保证的上限时,请使用 **引擎上限** — `OMNIFEED_REDDIT_MAX_ROUNDS` 扩展在 `limit` 之上添加评论,因此只有上限绑定最终总数。所有五个都是 `fetch_url` MCP 工具的每个请求参数(`limit`、`depth`、`sort`、`max_comments`、`max_top_level`);一个正值覆盖环境默认值。
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Travel%20and%20places/Building%20Construction.png" width="26" height="26" /> 架构
两个端口回答两个问题。`Searcher` 端口回答 *查询 → URL*;`Engine` 端口回答 *URL → 内容*。MCP 工具和 REST 处理程序组成它们;传输保持薄。
```mermaid
%%{init: {"theme":"base","themeVariables":{"background":"transparent","mainBkg":"#161b22","primaryColor":"#161b22","primaryTextColor":"#e6edf3","primaryBorderColor":"#F97316","lineColor":"#8b949e","secondaryColor":"#161b22","tertiaryColor":"#161b22","fontFamily":"Inter, system-ui, sans-serif"},"flowchart":{"curve":"basis","htmlLabels":true}}}%%
flowchart TB
crawl["POST /crawl"] e1@--> owt["Open WebUI<br/>transport"]
search["POST /search"] e2@--> sat["SearchAPI<br/>transport"]
mcpStdio["MCP stdio"] e3@--> mcp["MCP server"]
mcpHTTP["MCP HTTP /mcp"] e4@--> mcp
owt e5@--> reg["Engine Registry"]
mcp -- crawl tools --> reg
sat e6@--> searcher["Searcher<br/>(SearXNG)"]
mcp -- search tool --> searcher
reg e7@--> reddit["Reddit engine<br/>(TOON)"]
reg e8@--> generic["Generic fallback<br/>(markdown)"]
reddit e9@--> c4["crawl4ai upstream<br/>(headless browser)"]
generic e10@--> c4
searcher e11@--> sx["SearXNG upstream<br/>(Google / Bing / DDG)"]
classDef box fill:#161b22,stroke:#30363d,stroke-width:1px,color:#e6edf3;
classDef accent fill:#0d1117,stroke:#F97316,stroke-width:2px,color:#ffd9b3;
classDef animate stroke:#F97316,stroke-width:2px,stroke-dasharray:10 6,stroke-dashoffset:900,animation:dash 14s linear infinite;
class crawl,search,mcpStdio,mcpHTTP,owt,sat box;
class mcp,reg,searcher,reddit,generic,c4,sx accent;
class e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11 animate;
```
### <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Shield.png" width="22" height="22" /> Reddit 反机器人处理
Reddit 的边缘 403 阻止非浏览器 HTTP 客户端(它会指纹识别 TLS/JA3 握手),因此 Reddit 引擎永远不会直接调用 Reddit。它驱动 crawl4ai 的无头 Chromium 进入 `www.reddit.com` 页面(清除机器人墙),然后在该页面内运行 **同源 `fetch()`** 获取 `.json` 和 `/api/morechildren` 端点,无需 Reddit 授权、cookie 或 API 密钥。每个线程的 crawl4ai `session_id` 在扩展轮之间重复使用,以保持一个温暖的上下文。
> 持续抓取可能会提高您的源 IP 的风险评分。如果获取开始返回阻止页面,请减慢速度,保持 `expand` 谦逊,或通过住宅代理路由 crawl4ai。
### <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Activities/Puzzle%20Piece.png" width="22" height="22" /> 扩展
新的引擎(Hacker News、Stack Overflow、...)、搜索引擎(Brave、Tavily、...)、MCP 工具和传输都插入到一个小的端口中,无需触及其余部分。参见 **[AGENTS.md → 添加内容](AGENTS.md#添加内容)** 获取架构和一步一步的指南。
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Hammer%20and%20Wrench.png" width="26" height="26" /> 开发
```bash
git clone https://github.com/kinorai/omnifeed.git && cd omnifeed
make check # vet + lint + test — 密封的,无需上游或令牌
docker compose up # 在本地运行完整的堆栈(无需令牌:端口 8080 / 8081 / 9090)
```
参阅 [CONTRIBUTING.md](CONTRIBUTING.md) 获取完整的工作流程,[SECURITY.md](SECURITY.md) 获取漏洞报告,[AGENTS.md](AGENTS.md) 如果您是编写此代码库的编码代理。
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Travel%20and%20places/Star.png" width="26" height="26" /> 星级历史
<a href="https://star-history.com/#kinorai/omnifeed&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kinorai/omnifeed&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kinorai/omnifeed&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kinorai/omnifeed&type=Date" width="70%" />
</picture>
</a>
<img src="https://user-images.githubusercontent.com/74038190/212284100-561aa473-3905-4a80-b561-0d28506553ee.gif" width="100%">
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Hand%20gestures/Handshake.png" width="26" height="26" /> 贡献
<div align="center">
**如果它对您有帮助,请给它点赞 — 这有助于其他 AI 构建者找到 omnifeed。**
[](https://github.com/kinorai/omnifeed)
[](https://github.com/kinorai/omnifeed/issues/new)
[](https://github.com/kinorai/omnifeed/pulls)
</div>
新引擎、搜索引擎、MCP 工具和传输都欢迎 — 从 [AGENTS.md](AGENTS.md#添加内容) 和 [CONTRIBUTING.md](CONTRIBUTING.md) 开始。
## <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Page%20Facing%20Up.png" width="26" height="26" /> 许可证
[MIT](LICENSE) © kinorai
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:7C3AED,100:F97316&height=120§ion=footer" width="100%"/>
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器