README
🚀 Search Stack
Search Stack 是 AI Agent 专用的 Web 搜索与抓取中间层,为 OpenClaw、Claude Code、Dify 等 AI 智能体提供统一的 Web 访问 API。它具备多引擎搜索自动 fallback、无头 Chrome 反爬渲染、Cookie 注入登录态、正文精准提取等功能,一次部署,所有 Agent 均可共用。
🚀 快速开始
Search Stack 为 AI 智能体提供了强大的 Web 搜索与抓取能力。以下是快速部署和使用的步骤:
前置要求
不配 Tavily / Serper 也能用,会自动 fallback 到 SearXNG(完全免费)。
1. 克隆项目
git clone https://github.com/pinkpills/search-stack.git
cd search-stack
2. 配置环境变量
cp .env.example .env
编辑 .env:
# ====== 搜索引擎 API Key(可选)======
TAVILY_API_KEY=your_tavily_key
SERPER_API_KEY=your_serper_key
# ====== 内部服务密钥(必填,每个值必须不同)======
SEARXNG_SECRET=
PROXY_API_KEY=
BROWSERLESS_TOKEN=
REDIS_PASSWORD=
一键生成随机密钥:
python3 -c "
import secrets
for name in ['SEARXNG_SECRET', 'PROXY_API_KEY', 'BROWSERLESS_TOKEN', 'REDIS_PASSWORD']:
print(f'{name}={secrets.token_hex(16)}')
" >> .env
3. 配置 SearXNG
必做! 不做这步 SearXNG 的 JSON API 会返回 403。
cp searxng/settings.yml.example searxng/settings.yml
编辑 searxng/settings.yml,确保包含:
search:
formats:
- html
- json # ← 必须有这行,否则 JSON API 返回 403
如果你之前已经启动过 SearXNG(它会自动生成 settings.yml),需要手动加上 formats 配置后重启容器。
4. 启动服务
docker compose -f search-stack.yml up -d
等待所有容器健康(约 30 秒):
docker compose -f search-stack.yml ps
全部显示 healthy 即完成。
5. 验证
# 健康检查
curl -s -H "X-API-Key: YOUR_PROXY_API_KEY" http://127.0.0.1:17080/health | python3 -m json.tool
# 搜索测试(自动选择引擎)
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: YOUR_PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "hello world", "count": 3}' | python3 -m json.tool
# 指定 SearXNG 搜索(验证 SearXNG 是否正常)
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: YOUR_PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "hello world", "count": 3, "provider": "searxng"}' | python3 -m json.tool
# 抓取测试
curl -s -X POST http://127.0.0.1:17080/fetch \
-H "X-API-Key: YOUR_PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "render": false}' | python3 -m json.tool
提示: 如果 SearXNG 返回 502 或空结果,大概率是缺少
formats: [html, json]配置,参见步骤 3。
✨ 主要特性
- 多引擎 Fallback — Tavily → Serper → SearXNG 按优先级自动切换,单引擎挂不影响服务。
- 搜索 + 抓取一体 —
/search搜索,/fetch抓取正文,enrich=true搜索后自动抓取全文。 - 抗反爬 — Browserless Stealth 模式,绕过 Cloudflare / JS Challenge。
- 正文提取 — trafilatura + BeautifulSoup + readability 三引擎,精准提取正文。
- Cookie 管理 — API 动态增删 Cookie,自动注入 Chrome 渲染,支持直接粘贴浏览器 Cookie。
- 登录/反爬检测 — 多维度启发式检测:HTTP 状态码(401/403)、文本关键词(中英日)、页面标题、HTML 结构(密码框、CAPTCHA 嵌入、meta refresh 重定向)、SPA 登录墙,返回
needs_login标记引导 Cookie 更新。 - Cookie Catcher — 浏览器内远程登录:通过 WebSocket + CDP Screencast 在 Web UI 中操控远程 Chrome 完成登录,一键保存 Cookie。
- SSRF 防护 — 拒绝访问私网 IP(127/10/172.16/192.168/169.254)。
- URL 去重 — 自动去除追踪参数(utm_*、fbclid 等),同域名结果限制。
- Redis 缓存 — 15 分钟 TTL,重复查询即时返回。
- API Key 鉴权 + 限流 — 滑动窗口限流。
- MCP Server — stdio 模式 MCP Server(
mcp-server.ts),可通过 mcporter 注册供 OpenClaw 等 Agent 使用。 - TikHub 社交媒体 API — 可选集成,代理 TikHub 803 个社交平台工具(抖音、TikTok、微博等),内置自动回退。
- HTTP 代理 — 支持 HTTP / SOCKS5 代理,用于反爬固定 IP 或翻墙访问被墙网站(YouTube 等)。
- 全异步 — async Redis + 共享 httpx 连接池,高并发低延迟。
📦 安装指南
前置要求
克隆项目
git clone https://github.com/pinkpills/search-stack.git
cd search-stack
配置环境变量
cp .env.example .env
编辑 .env 并可一键生成随机密钥:
python3 -c "
import secrets
for name in ['SEARXNG_SECRET', 'PROXY_API_KEY', 'BROWSERLESS_TOKEN', 'REDIS_PASSWORD']:
print(f'{name}={secrets.token_hex(16)}')
" >> .env
配置 SearXNG
cp searxng/settings.yml.example searxng/settings.yml
编辑 searxng/settings.yml 确保包含 formats: [html, json],若之前启动过需手动添加并重启容器。
启动服务
docker compose -f search-stack.yml up -d
等待容器健康:
docker compose -f search-stack.yml ps
验证
# 健康检查
curl -s -H "X-API-Key: YOUR_PROXY_API_KEY" http://127.0.0.1:17080/health | python3 -m json.tool
# 搜索测试
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: YOUR_PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "hello world", "count": 3}' | python3 -m json.tool
# 指定 SearXNG 搜索
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: YOUR_PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "hello world", "count": 3, "provider": "searxng"}' | python3 -m json.tool
# 抓取测试
curl -s -X POST http://127.0.0.1:17080/fetch \
-H "X-API-Key: YOUR_PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "render": false}' | python3 -m json.tool
💻 使用示例
基础用法
# 基础搜索
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: KEY" -H "Content-Type: application/json" \
-d '{"query": "Docker best practices", "count": 5}'
# 搜索 + 抓取全文(深度研究)
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: KEY" -H "Content-Type: application/json" \
-d '{"query": "Python asyncio", "count": 3, "enrich": true}'
# 强制使用 SearXNG(免费,不消耗 API 额度)
curl -s -X POST http://127.0.0.1:17080/search \
-H "X-API-Key: KEY" -H "Content-Type: application/json" \
-d '{"query": "AI news", "count": 5, "provider": "searxng"}'
高级用法
# 抓取网页正文
curl -s -X POST http://127.0.0.1:17080/fetch \
-H "X-API-Key: KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com", "render": false}'
📚 详细文档
与 Brave Search 对比
| 维度 | Search Stack | Brave Search(OpenClaw 内置) |
|------|-------------|-------------------------------|
| 搜索速度 | 0.8 - 1.5s(Tavily/Serper)| ~1 - 2s |
| 缓存命中 | 13ms(Redis 缓存 15 分钟)| 无缓存,每次重新请求 |
| 中文搜索 | 结果丰富(掘金、知乎、什么值得买等)| 中文结果偏少,偏英文源 |
| 英文搜索 | 优秀 | 优秀 |
| 高可用 | 三引擎自动 fallback | 单点,挂了就没了 |
| 全文抓取 | enrich=true 搜索+正文一步到位 | 只返回摘要,需额外抓取 |
| 反爬站点 | Browserless Chrome 渲染 | 无法抓取 |
| 需登录站点 | Cookie 注入 + 自动检测引导 | 不支持 |
| 免费额度 | SearXNG 无限量兜底 | 免费 Key 有严格限制 |
结论:搜索速度持平,中文质量更好,功能远超 Brave。
架构
+-----------+
AI Agent ──────────>| search- |──> Tavily API
(OpenClaw / Claude) | proxy |──> Serper API (Google)
POST /search | (FastAPI) |──> SearXNG (self-hosted)
POST /fetch +-----+-----+
|
+-------------+-------------+
| |
+-----+-----+ +--------+--------+
| Redis | | Browserless |
| (cache + | | (headless Chrome |
| rate-limit)| | anti-bot render)|
+------------+ +-----------------+
四个容器,一键启动: | 服务 | 作用 | |------|------| | search-proxy | FastAPI 核心代理,统一搜索/抓取接口 | | Redis | 结果缓存(15 分钟 TTL)+ API 限流计数 | | SearXNG | 自托管元搜索引擎(聚合 Google、DuckDuckGo、Brave 等,免费无限量) | | Browserless | 无头 Chrome,渲染 JS 页面,Stealth 模式绕过反爬 |
集成 OpenClaw
方式一:原生插件(推荐)
- 步骤 1:安装插件
# 用 --link 符号链接安装(更新时无需重装)
openclaw plugins install --link /opt/search-stack/plugin/
- 步骤 2:配置
编辑
~/.openclaw/openclaw.json,在plugins.entries中添加:
{
"plugins": {
"entries": {
"search-stack": {
"enabled": true,
"config": {
"apiUrl": "http://127.0.0.1:17080",
"apiKey": "your_proxy_api_key",
"tikhubApiKey": "your_tikhub_key"
}
}
}
}
}
apiKey的值就是.env中的PROXY_API_KEY。tikhubApiKey可选,填入 TikHub API Key 可启用社交媒体 API。 注意: 配置必须放在config嵌套对象内,不能直接放在search-stack下。
- 步骤 3:禁用内置 Brave 搜索
编辑
~/.openclaw/openclaw.json:
{
"tools": {
"web": {
"search": {
"enabled": false
}
}
}
}
- 步骤 4:创建 Skill 文件
mkdir -p ~/.openclaw/workspace/skills/web-search/
cp /opt/search-stack/skill-template/SKILL.md ~/.openclaw/workspace/skills/web-search/SKILL.md
- 步骤 5:重启并验证
sudo systemctl restart openclaw
# 验证插件加载
openclaw plugins list # 应显示 search-stack: loaded
# 验证工具注册(通过 AI 对话或 gateway API)
# AI 应能直接看到 web_search 等工具,无需 exec
重要: 如果 AI 仍在使用旧方式,需要归档旧 session。OpenClaw 的会话上下文会缓存之前的工具模式,即使配置已更新,旧 session 仍会沿用旧行为。详见下方「常见问题 → AI 不使用 search-stack」。
方式二:MCP + mcporter(备选)
- 步骤 1:安装 MCP Server 依赖
# 安装 Bun(如果没有)
curl -fsSL https://bun.sh/install | bash
# 安装 MCP SDK
bun add -g @modelcontextprotocol/sdk zod
- 步骤 2:注册到 mcporter
编辑
~/.mcporter/mcporter.json,添加 search-stack:
{
"mcpServers": {
"search-stack": {
"command": "/home/your_user/.bun/bin/bun",
"args": ["run", "/opt/search-stack/proxy/mcp-server.ts"],
"keepAlive": true,
"env": {
"SEARCH_STACK_URL": "http://127.0.0.1:17080",
"SEARCH_STACK_API_KEY": "your_proxy_api_key",
"TIKHUB_API_KEY": "your_tikhub_key"
}
}
}
}
验证注册:
mcporter daemon restart
mcporter list
# 应显示 search-stack (6 tools) healthy
- 步骤 3:创建 Skill 并重启
创建
~/.openclaw/workspace/skills/web-search/SKILL.md(使用 mcporter exec 调用格式),禁用 Brave 搜索,重启 OpenClaw。
注意: MCP+mcporter 方式中,AI 通过
exec工具执行mcporter call search-stack.*命令。超时时 SIGKILL 会导致零输出,AI 可能认为"搜索引擎挂了"。推荐使用原生插件方式避免此问题。
异地部署(OpenClaw 和 Search Stack 在不同机器)
- 步骤 1:服务器 A — 配置反向代理(HTTPS)
location /search-stack/ {
proxy_pass http://127.0.0.1:17080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# Cookie Catcher WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
安全提醒: API Key 会在请求头中明文传输,必须使用 HTTPS。建议用 Certbot 自动申请 Let's Encrypt 证书。
- 步骤 2:服务器 B — 获取插件代码
# 方法一:克隆完整仓库(简单)
git clone https://github.com/pinkpills/search-stack.git /opt/search-stack
cd /opt/search-stack/plugin && npm install
# 方法二:只下载需要的目录(轻量)
mkdir -p /opt/search-stack && cd /opt/search-stack
# 下载 plugin/ 和 skill-template/
git clone --depth 1 --filter=blob:none --sparse \
https://github.com/pinkpills/search-stack.git .
git sparse-checkout set plugin skill-template
cd plugin && npm install
- 步骤 3:服务器 B — 安装插件到 OpenClaw
openclaw plugins install --link /opt/search-stack/plugin/
- 步骤 4:服务器 B — 配置远程 API 地址
编辑
~/.openclaw/openclaw.json:
{
"plugins": {
"entries": {
"search-stack": {
"enabled": true,
"config": {
"apiUrl": "https://search.example.com/search-stack",
"apiKey": "your_proxy_api_key",
"publicUrl": "https://search.example.com/search-stack",
"tikhubApiKey": "your_tikhub_key"
}
}
}
},
"tools": {
"web": {
"search": {
"enabled": false
}
}
}
}
配置说明:
| 字段 | 说明 |
|------|------|
| apiUrl | 服务器 A 的 Search Stack API 地址(通过 Nginx 代理后的 HTTPS URL) |
| apiKey | 服务器 A .env 中的 PROXY_API_KEY |
| publicUrl | Cookie Catcher 链接中使用的公网 URL(用户浏览器需要能访问),通常与 apiUrl 相同 |
| tikhubApiKey | (可选)TikHub API Key |
- 步骤 5:服务器 B — 创建 Skill 并重启
mkdir -p ~/.openclaw/workspace/skills/web-search/
cp /opt/search-stack/skill-template/SKILL.md ~/.openclaw/workspace/skills/web-search/SKILL.md
sudo systemctl restart openclaw
验证
在 OpenClaw 中对话测试:
用户: "搜索一下 Claude Opus 4.6 评测"
AI: 调用 web_search → 返回结果(来自远程 Search Stack)
用户: "打开第一条链接看看全文"
AI: 调用 page_fetch → 返回全文(远程 Browserless 渲染)
如果工具调用失败,检查:
- 服务器 B 能否访问
apiUrl:curl -H "X-API-Key: KEY" https://search.example.com/search-stack/health - 插件是否加载:
openclaw plugins list - 旧 session 缓存:归档旧 session 后重启(详见「常见问题 → AI 不使用 search-stack」)
多机并发与资源控制
| 客户端数 | MAX_CONCURRENT_SESSIONS | memory | 服务器内存建议 |
|----------|--------------------------|----------|---------------|
| 1 - 2 台 | 5 | 2g | 4GB+ |
| 3 - 5 台 | 10 | 4g | 8GB+ |
| 5 - 10 台 | 20 | 8g | 16GB+ |
超出并发上限时,Browserless 会将请求排队等待;队列也满时返回 429,
page_fetch会报错但不影响搜索功能(web_search不依赖 Chrome)。Cookie Catcher 另有 2 会话硬限制,多人同时登录需排队。
工具列表
| 工具 | 说明 |
|------|------|
| web_search | 多引擎搜索,支持 enrich 全文抓取 |
| page_fetch | 抓取网页正文,支持 Cookie 注入 + Chrome 渲染 + 登录检测 |
| cookies_list | 列出已配置 Cookie 的域名 |
| cookies_update | 添加/更新域名 Cookie(支持 raw 字符串粘贴) |
| cookies_delete | 删除域名 Cookie |
| cookie_catcher_link | 生成远程浏览器登录链接(Cookie Catcher) |
| tikhub_call | 调用 TikHub 社交媒体 API(需配置 Key,按需使用) |
注意: 抓取工具名为
page_fetch而非web_fetch。这是为了避免与 OpenClaw 内置的web_fetch工具名冲突。内置web_fetch不支持 Cookie 注入和 Chrome 渲染,使用同名会导致 AI 调用错误的工具。
Cookie 工作流实战
方式一:手动复制粘贴(适用于桌面端)
用户: "帮我看看这个网页 https://zhuanlan.zhihu.com/p/xxxx"
AI: 调用 page_fetch → 正文不完整(只有标题/摘要)
AI: "这个网站的反爬比较严格,正文没有完整抓到。
如果你需要完整内容,可以提供该网站的 Cookie:
1. 在浏览器中打开该网址并登录
2. 按 F12 → Network 标签 → 刷新页面
3. 找到请求头中的 Cookie: 一行
4. 复制整个值发给我"
用户: "z_c0=xxx; _xsrf=yyy; d_c0=zzz ..."
AI: 自动提取域名 zhihu.com → cookies_update → 保存成功
AI: 用 bypass_cache:true 重新抓取 → 拿到完整文章内容
方式二:Cookie Catcher 远程登录(适用于复杂登录流程)
1. 浏览器打开:http://YOUR_HOST:17080/cookie-catcher?key=API_KEY&url=https://threads.net
2. 在远程 Chrome 画面中完成登录(支持鼠标/键盘/触屏操作)
3. 点击 "Save Cookies" → 自动保存到 cookies.json
4. 后续 /fetch 请求自动注入 Cookie
适合需要 OAuth 跳转、二维码扫码、手机验证码等复杂登录场景。
🔧 技术细节
API 文档
所有请求需携带 X-API-Key 头部。
GET /health
健康检查。
{
"ok": true,
"redis": true,
"order": ["tavily", "serper", "searxng"],
"browserless_configured": true,
"dedupe": { "enabled": true, "max_per_host": 2 }
}
POST /search
Web 搜索。
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| query | string | 必填 | 搜索关键词 |
| count | int | 5 | 返回结果数(1 - 10) |
| provider | string | 自动 | 强制指定:tavily / serper / searxng |
| enrich | bool | false | 抓取每条结果的网页全文 |
| max_chars | int | 8000 | enrich 时每页最大字符数 |
| render | bool | true | 用无头浏览器渲染 |
| concurrency | int | 3 | enrich 并发抓取数 |
POST /fetch
抓取网页正文。
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| url | string | 必填 | 目标 URL |
| render | bool | true | 用无头浏览器渲染 |
| max_chars | int | 20000 | 最大提取字符数 |
| timeout | float | 25 | 超时秒数 |
| headers | object | null | 自定义请求头 |
| bypass_cache | bool | false | 跳过缓存(更新 Cookie 后用) |
当页面需要登录或被反爬拦截时,返回 needs_login: true:
{
"needs_login": true,
"has_cookies": false
}
has_cookies: true 时表示已有 Cookie 但已过期,需要重新导出。
检测规则覆盖以下信号(按优先级排列):
| 信号 | 条件 | 示例 |
|------|------|------|
| HTTP 401 | 直接判定 | API 端点未认证 |
| HTTP 403 + 短内容 | text < 2000 字符 | 访问被拒绝页面 |
| 文本登录关键词 | 1 hit + < 500 字符,或 2+ hits | "请登录"、"sign in to continue"、"verify you are human" |
| 页面标题含登录词 | + text < 2000 字符 | <title>Sign In - Example</title> |
| HTML 密码输入框 | + text < 3000 字符 | <input type="password"> |
| Meta refresh → 登录 URL | 直接判定 | <meta http-equiv="refresh" content="0;url=/login"> |
| CAPTCHA 嵌入 | + text < 1000 字符 | reCAPTCHA、hCaptcha、Cloudflare Turnstile |
| 空壳备案页 | 2+ hits + < 800 字符 | 仅含 ICP 备案号的页面(小红书等) |
支持中文、英文、日文登录关键词,以及 OAuth 提示("continue with Google")、付费墙("subscribe to continue")、Cloudflare 验证("checking your browser")等。
Cookie 管理
# 列出所有域名
GET /cookies
# 添加/更新 — Raw 字符串(直接从浏览器复制粘贴)
PUT /cookies/zhihu.com
{"raw": "z_c0=xxx; _xsrf=yyy; d_c0=zzz"}
# 添加/更新 — JSON 数组
PUT /cookies/zhihu.com
{"cookies": [{"name":"z_c0","value":"xxx"}, {"name":"_xsrf","value":"yyy"}]}
# 删除
DELETE /cookies/zhihu.com
# 从 cookies.json 重新加载
POST /cookies/reload
Cookie Catcher(远程浏览器登录)
浏览器访问:GET /cookie-catcher?key=YOUR_API_KEY[&url=https://target-site.com]
工作流程:
- 浏览器打开
/cookie-catcher?key=API_KEY,建立 WebSocket 连接 - 在地址栏输入目标网站 URL,点击 Go
Scan to join WeChat group