| name | web-search |
| description | 网络信息获取唯一决策入口。三层能力:
① Layer 1 — OpenCLI 100+ 站点直采(含 External CLI 桥接 + 本机扩展 CLI)
② Layer 2 — 通用搜索降级链(WebSearch → Tavily → DuckDuckGo)
③ Layer 3/4 — 已知 URL 读取 + 浏览器 CDP 兜底
触发场景:搜索、查询、调研、读取网页、抓 SPA、登录后内容、采集前置「先搜后采」
不触发场景:URL 已明确且已确认走 OpenCLI(如 ob-collect 拿到具体视频 URL 后直接执行)
特殊指令:/web-search setup(注册 Tavily + 扫描本机 CLI)
沉淀机制:搜索完成后按规则更新 experience.local.md
|
Web Search 工具决策
完整 Tradeoff 矩阵和经验沉淀参考 Obsidian 文章 OBA-w8s3k7p2。
${CLAUDE_SKILL_DIR} 指本 SKILL.md 所在目录。
0. 一句话决策(90% 场景)
拿到信息获取请求
│
├─ 有 URL?─────────────→ Layer 1(OpenCLI 路由)
│ ├─ 内置 100+ 站点
│ ├─ External CLI 桥接(gh/lark/notion/...)
│ └─ 本机扩展 CLI(yuque-cli/linear/...)
│ ↓ 全部未命中
│ Layer 3(已知 URL 读取)
│ ↓ 需要登录/SPA 失败
│ Layer 4(浏览器 CDP 兜底)
│
└─ 没有 URL ───────────→ Layer 2(通用搜索降级链)
WebSearch → Tavily → DuckDuckGo
核心原则:零成本优先、按需升级、一工具失败立即换下一级,不要在同一工具反复重试。
1. 首次使用提示(仅出现一次)
进入 skill 时先读 ${CLAUDE_SKILL_DIR}/experience.local.md 顶部。如果没有 setup-completed: 标记:
💡 提示:跑 /web-search setup 可一次性完成 Tavily API Key 配置 + 扫描本机的搜索/采集 CLI 工具(语雀/Linear/Notion 等)。配好后下次自动用上,不会有打断。
跑过一次后,setup 命令会在 experience.local.md 顶部写入 setup-completed: {ISO 时间},从此不再提示。
2. Layer 1:OpenCLI 站点路由(URL 已知)
2.1 依赖检查
which opencli >/dev/null && opencli doctor 2>&1 | head -3
期望三项全绿(Daemon / Extension / Connectivity)。未安装时跳过 Layer 1,不引导安装。
2.2 高频站点清单(覆盖 80% 场景)
域名命中此表 → 必须用 OpenCLI 对应命令;不命中 → 进 2.4/2.5/2.6。
| 类别 | 域名 | 命令 | 认证 |
|---|
| 视频/播客 | youtube.com / youtu.be | youtube search/transcript/video/download | 🔐 |
| bilibili.com | bilibili search/subtitle/video/favorite | 🔐 |
| xiaoyuzhou.fm | xiaoyuzhou transcript/episode/search | 🌐 |
| apple-podcasts | apple-podcasts episodes/search/top | 🌐 |
| spotify | spotify ... | 🌐 |
| 社交 | twitter.com / x.com | twitter search/thread/bookmarks | 🔐 |
| reddit.com | reddit search/read | 🔐 |
| xiaohongshu.com | xiaohongshu search/note | 🔐 |
| weibo.com | weibo search/post | 🔐 |
| bsky.app | bluesky ... | 🌐 |
| jike (即刻) | jike ... | 🔐 |
| 技术社区 | news.ycombinator.com | hackernews search/read | 🌐 |
| stackoverflow.com | stackoverflow search | 🌐 |
| v2ex.com | v2ex topic/search | 🌐/🔐 |
| linux.do | linux-do ... | 🔐 |
| dev.to | devto ... | 🌐 |
| huggingface.co | hf search/dataset/model | 🌐 |
| 资讯/内容 | zhihu.com | zhihu answer/search | 🔐 |
| 36kr.com | 36kr article/search/hot | 🌐 |
| medium.com | medium ... | 🔐 |
| substack.com | substack ... | 🌐/🔐 |
| mp.weixin.qq.com | weixin article/account | 🔐 |
| douban.com | douban subject/search | 🔐 |
| 学术 | arxiv.org | arxiv search/paper | 🌐 |
| wikipedia.org | wikipedia ... | 🌐 |
| scholar.google.com | google-scholar search | 🌐 |
| 财经 | eastmoney.com | eastmoney ... | 🌐/🔐 |
| xueqiu.com | xueqiu ... | 🔐 |
| AI 平台 | chatgpt.com | chatgpt ... | 🔐 |
| gemini.google.com | gemini ... | 🔐 |
| doubao.com | doubao ... | 🔐 |
| deepseek.com | deepseek ... | 🔐 |
| 通用兜底 | 任意 HTTP URL(含 SPA) | opencli web read <url> | — |
| Google 搜索(无登录) | opencli google search "<keyword>" | 🌐 |
认证标识:🌐 public(无需登录) / 🔐 cookie(需本机 Chrome 已登录该站点) / 🔧 ui(需本机已装对应桌面应用)
2.3 重操作确认
执行前必须询问用户:字幕提取(youtube transcript / bilibili subtitle)、下载媒体(<site> download)、批量搜索(多个 opencli 组合)。
不需要确认:单次 search、获取详情、热门趋势。
2.4 未命中 → grep 兜底
URL 域名不在 2.2 表里时:
opencli list 2>&1 | grep -i <域名前缀或关键词>
命中 → 用该命令;未命中 → 进 2.5 / 2.6。
2.5 External CLI 桥接
OpenCLI 主命令下挂载了第三方工具,与内置站点同等优先级:
| 命令 | 适用域名 / 用途 |
|---|
opencli gh | github.com(repo/PR/issue/release/gist) |
opencli lark-cli | feishu.cn / larksuite.com(消息/文档/表格/日历,200+ 命令) |
opencli dws | dingtalk.com(钉钉 Workspace) |
opencli wecom-cli | wecom.com(企业微信) |
opencli obsidian | 本地 Obsidian vault(笔记/搜索/标签) |
opencli vercel | vercel.com(部署/域名/env/日志) |
碰到对应域名时优先用这些,而不是 Layer 3 通用读取。
2.6 本机扩展 CLI(用户安装的非 OpenCLI 工具)
URL 域名既不在内置清单也没有 External 桥接时:
第一步:读 experience.local.md 的「本机扩展站点」表(最权威)
如果表里已有该域名 → 直接用记录的 CLI。
第二步:懒汉式探测本机是否有相关工具
which yuque-cli yuque 2>/dev/null
which linear 2>/dev/null
找到 → 调用该 CLI → 追加到 experience.local.md 的「本机扩展站点」表:
| yuque.com | yuque-cli | yuque-cli doc <id> | 2026-05-11 |
找不到 → 回到 Layer 3。
这张表是 skill 的知识扩展点:用户安装新 CLI 后被发现一次,之后所有会话都自动用上。
2.7 OpenCLI 命令失败的降级
| 报错信号 | 处理 |
|---|
| Authentication required / 401 / 403 | 提示用户在浏览器登录该站点后重试(不引导扫码,OpenCLI 不支持) |
| Daemon not running | 跑 opencli doctor,按提示修复 |
| 超时(>120s) | 退到 Layer 4 |
3. Layer 2:通用搜索降级链(无 URL)
3.1 决策链
WebSearch(内置,零配置)
├─ 结果够 → 完成
└─ 不够 → 检查 Tavily 配置
├─ 已配置 → Tavily(AI answer,~1.3s)
└─ 未配置 → 智能降级(见 3.3)
3.2 Trade-off 对照
| 工具 | 速度 | 要 Key | 免费额度 | 适用 |
|---|
| WebSearch(内置) | 中 | 否 | 共享 Z.ai | 日常通用首选 |
| Tavily | 1.3s | 是 | 1000/月 | 需要 AI 摘要 |
DuckDuckGo(ddgs) | ~10s | 否 | 无限 | 兜底 |
mcp__web-search-prime | 中 | 否 | 周/月有限 | 仅限定 domain/recency 时用 |
mcp__zread__search_doc | 快 | 否 | 无限 | GitHub 仓库内搜索 |
3.3 Tavily 未注册时的智能降级(LLM 自数会话搜索次数)
进入 Layer 2 前回顾本会话历史,估算已搜索次数:
if 已搜索 < 3 次:
静默跳 DDG,不弹提示
elif 已搜索 ≥ 3 次 AND 本会话未提示过:
仅提示一次:
"本会话已搜索 N 次。注册 Tavily 可减少等待(10s → 1.3s)。
执行 /web-search setup(1 分钟),或继续用 DDG。"
用户选择后本会话不再提示
else (用户已跳过提示):
静默 DDG
计数完全靠 LLM 上下文记忆,不写文件,避免污染 experience.local.md。
3.4 Tavily 用法
TAVILY_KEY="${TAVILY_API_KEY:-}"
[ -z "$TAVILY_KEY" ] && [ -f "${CLAUDE_SKILL_DIR}/config.local.json" ] && \
TAVILY_KEY=$(python3 -c "import json; print(json.load(open('${CLAUDE_SKILL_DIR}/config.local.json'))['tavily']['apiKey'])" 2>/dev/null)
[ -z "$TAVILY_KEY" ] && { echo "Tavily 未配置,跳到 DDG"; } || {
curl -s -X POST "https://api.tavily.com/search" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"$TAVILY_KEY\",\"query\":\"<keyword>\",\"max_results\":5,\"include_answer\":true}"
curl -s -X POST "https://api.tavily.com/extract" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"$TAVILY_KEY\",\"urls\":[\"<url>\"]}"
}
3.5 DuckDuckGo (ddgs) 用法
python3 -c "from ddgs import DDGS" 2>/dev/null || pip3 install --break-system-packages ddgs
python3 -c "from ddgs import DDGS; [print(r['title'], r['href']) for r in DDGS(proxy='http://127.0.0.1:10802').text('<keyword>', max_results=5)]"
python3 -c "from ddgs import DDGS; [print(r['title']) for r in DDGS().text('<keyword>', max_results=5, timelimit='w')]"
python3 -c "from ddgs import DDGS; [print(r['title'], r['date']) for r in DDGS().news('<keyword>', max_results=5)]"
3.6 知识扩展点
发现新搜索后端(Serper / SearXNG / Brave)适合补充进降级链时:
- 验证免费额度、速度、是否需要 Key
- 追加到 3.2 Trade-off 表
- 在 experience.local.md「有效模式」记录使用经验
4. Layer 3:已知 URL 的页面读取
适用:URL 已知,但 Layer 1 没有专属命令、External 桥接、本机扩展都没匹配。
| 工具 | JS 渲染 | 速度 | 适合 |
|---|
opencli web read <url> | ✅ | 中 | SPA 但无需登录的公开页 |
mcp__web_reader__webReader | ❌ | 快 | 静态博客、文档站 |
mcp__zread__read_file | — | 快 | GitHub 仓库内文件 |
WebFetch | ❌ | 快 | 最后兜底 |
经验规则:
- SPA + 公开 →
opencli web read
- SPA + 登录 → Layer 4
- 静态页 →
mcp__web_reader 先试,慢再换 WebFetch
- GitHub 文件 →
mcp__zread__read_file(不用 clone)
5. Layer 4:浏览器兜底(CDP)
进入条件:
- Layer 1 OpenCLI 命令报需要登录但当前未登录
- Layer 3 全部读取失败(SPA + 登录 / 反爬 / 复杂交互)
5.1 两种 CDP 入口的 Trade-off
| 维度 | opencli browser | /web-access CDP |
|---|
| 定位 | OpenCLI 内置浏览器子命令 | 通用 CDP 直连 |
| 启动 | OpenCLI daemon 自动管理 | 需 Chrome 远程调试 + cdp-proxy |
| 适合 | 已装 OpenCLI 时的首选 | 临时一次性交互 / 需要更精细控制 |
| 反检测 | 依赖 daemon 设置 | 内置 Port Guard |
| 学习成本 | 低 | 高 |
默认建议:已装 OpenCLI → opencli browser navigate/click/extract;否则用 /web-access。
详细 API 参考各自 skill 文档,本 skill 只做入口决策。
6. /web-search setup 命令(一次性配置)
用户执行 /web-search setup 时按顺序完成。核心是 Tavily Key 配置;CLI 扫描是可选扩展。
6.1 Tavily Key 配置(核心步骤)
先检测是否已配置
CFG="${CLAUDE_SKILL_DIR}/config.local.json"
TAVILY_KEY=""
[ -n "$TAVILY_API_KEY" ] && TAVILY_KEY="$TAVILY_API_KEY"
[ -z "$TAVILY_KEY" ] && [ -f "$CFG" ] && \
TAVILY_KEY=$(python3 -c "import json,sys; d=json.load(open('$CFG')); print(d.get('tavily',{}).get('apiKey',''))" 2>/dev/null)
if [ -n "$TAVILY_KEY" ]; then
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "https://api.tavily.com/search" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"$TAVILY_KEY\",\"query\":\"ping\",\"max_results\":1}")
case "$STATUS" in
200) echo "✓ Tavily 已配置且 Key 有效" ;;
401|403) echo "✗ Tavily Key 无效(HTTP $STATUS),请重新配置" ; TAVILY_KEY="" ;;
*) echo "⚠ Tavily 探测异常(HTTP $STATUS),但 Key 已配置" ;;
esac
else
echo "✗ Tavily 未配置"
fi
未配置时的注册引导
如果上一步报「未配置」或「Key 无效」:
-
访问 https://app.tavily.com 注册账号
-
Dashboard → API Keys → 复制 Key(格式 tvly-xxx)
-
写入配置(二选一):
方式 A:环境变量(推荐,跨 skill 复用)
把下面这行加到 ~/.zshrc 或 ~/.bashrc,然后 source 一下:
export TAVILY_API_KEY="tvly-粘贴你的 Key"
方式 B:skill 本地配置(隔离作用域)
cat > "${CLAUDE_SKILL_DIR}/config.local.json" << 'EOF'
{
"tavily": {
"apiKey": "<粘贴你的 Key>",
"enabled": true
}
}
EOF
-
配置后重跑 /web-search setup 验证。
Layer 2 中读取 Key 的代码(3.4 节)也按 环境变量优先 → config.local.json 兜底 的顺序读取,与本节一致。
6.2 扫描本机 CLI 工具(可选扩展,按需添加)
当你装了网站专用 CLI(如语雀 yuque-cli、Linear linear 等)时,把它加进下面的扫描清单,下次进 Layer 1 时遇到对应域名会自动用上。当前清单只列 1 个示例,按需扩展。
EXP="${CLAUDE_SKILL_DIR}/experience.local.md"
DATE=$(date +%Y-%m-%d)
grep -q "^## 本机扩展站点" "$EXP" 2>/dev/null || cat >> "$EXP" << 'EOF'
| 域名 | CLI 命令 | 用法示例 | 发现日期 |
|------|---------|---------|---------|
EOF
for tool in \
"yuque.com|yuque-cli|yuque-cli doc <id>"; do
domain="${tool%%|*}"; rest="${tool#*|}"; cmd="${rest%%|*}"; usage="${rest##*|}"
if which "$cmd" >/dev/null 2>&1 && ! grep -q "| $domain |" "$EXP"; then
echo "| $domain | $cmd | $usage | $DATE |" >> "$EXP"
echo "✓ 发现 $cmd → 已登记 $domain"
fi
done
扩展示例(用户按需添加新行到 for 列表):
"linear.app|linear|linear issue list"
"notion.so|notion-cli|notion-cli page <id>"
"<新域名>|<新 CLI 命令>|<用法示例>"
6.3 写入 setup-completed 标记
if ! grep -q "^setup-completed:" "$EXP"; then
TMP=$(mktemp)
echo "setup-completed: $(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$TMP"
echo "" >> "$TMP"
cat "$EXP" >> "$TMP"
mv "$TMP" "$EXP"
fi
7. 硬约束
- 禁止首选
mcp__web-search-prime 做通用搜索(配额有限)
- 禁止用
mcp__web_reader 读已知 SPA(直接 Layer 4)
- 一个工具失败立即升级,不在同一工具反复重试
- Layer 1 命中 OpenCLI 时禁止跳 Layer 3(domain-specific 永远更准)
- 子 Agent 任务分发用「获取/了解/调研」等中性词,避免暗示特定工具
- Tavily 注册引导仅本会话提示一次
- 本机扩展站点表必须先读、后探测(已记录的域名直接用,不重复探测)
8. 经验沉淀机制
经验存储在 ${CLAUDE_SKILL_DIR}/experience.local.md,由 LLM 自动维护。
8.1 读取规则
搜索开始前必须先读取 experience.local.md,作为工具选择的先验。优先级:
本机扩展站点 > 失败模式 > 有效模式 > Trade-off 矩阵
8.2 写入规则
搜索完成后按下表写入:
| 触发条件 | 写入位置 | 内容 |
|---|
| 首次发现本机有非内置 CLI 能搞定某域名 | 本机扩展站点表 | | 域名 | CLI | 用法 | 日期 | |
| 工具在新场景表现特别好 | 有效模式表 | | 结论 | 日期 | 场景 | |
| 工具在特定场景失败 | 失败模式表 | | 陷阱 | 日期 | 正确做法 | |
不写:按预期工作的常规调用 / 未经验证的猜测。
8.3 回退策略
按经验选择工具后结果不达标 → 立即回退到下一级 → 将旧经验改为失败模式。
8.4 写入示例
## 本机扩展站点
| yuque.com | yuque-cli | yuque-cli doc <id> | 2026-05-11 |
## 有效模式
| Tavily 在编程问答比 WebSearch 命中率高 | 2026-05-11 | 编程概念解释 |
## 失败模式
| web_reader 读小红书返回空 | 2026-05-11 | 改用 Layer 4 opencli browser |