MCP 搜索 + RAG 工具
想让 AI agent 既能搜公网又能查私有文档的开发者必装十件套:Tavily MCP 做面向 AI 的 web 搜索打底 + Exa / Firecrawl 两种 backend 取舍 + omnisearch 多提供商聚合 + Perplexity Sonar API 出有引用的答案 + Citations 渲染源标 + Qdrant / haiku.rag 给私有文档做向量 RAG + lnav 本地日志检索 + Ragas 评测整条链是否真的有据可查。按推荐安装顺序排列。
这个 pack 解决什么
开发者把搜索和 RAG 接进 AI agent,每次都撞同一堵墙。每个 web 搜索 API 返回格式不一样。每个向量库的 embedding 契约不一样。每个引用系统对"什么算源"的口径不一样。而且没人能告诉你,LLM 给出的那个答案,到底有没有从它拿到的 snippet 里得出来。
本 pack 选 10 个 MCP / API,把整条管线正面覆盖 — 公网搜索 → 多提供商路由 → 带引用的答案 → 私有文档检索 → 本地日志检索 → grounding 评测 — 并安排了安装顺序,每装一个解锁下一层。装完你的 agent 能搜实时公网、检索你自己的 corpus,而且每个声明都能审计。
推荐安装顺序
- Tavily MCP / Tavily Search API — 从这里起步。返回格式是为 LLM 设计的:snippet、citation 元数据、可选的生成答案,一次调用全有。比起通用 web 搜索 wrapper,省掉了"解析 SERP"这一步。Tavily 文档公开了请求和响应 schema,还有当下就能接通的免费档。
- Exa MCP Server(远程) — 第二个装的 web 搜索 MCP,不是第一个。Exa 是原生 embedding 的搜索索引 — 返回的是语义相近的结果,不是关键词匹配。把它挂到和 Tavily 同一个工具名后面,让 agent 自己选:关键词式 query 走 Tavily,探索 / 概念式 query 走 Exa。
- Firecrawl MCP — 搜索 + 抓取二合一。Tavily / Exa 返回 snippet,Firecrawl 返回整页清洗后的 markdown。snippet 不够用时换它 — 长文、文档站、任何你要塞回 LLM 上下文窗口的内容。MCP 同时暴露
search和scrape,agent 按调用决定走哪条。 - mcp-omnisearch — 统一搜索 MCP 服务器 — 接了两三个提供商之后,omnisearch 是多路复用器。一个工具名,多个 backend。agent 按 hint 选提供商,或者你设默认值;某 backend 挂了自动降级到下一个。至少有两个提供商时才装它,否则就是空转。
- Perplexity Sonar API — 带搜索的 LLM — 和 Tavily / Exa 形状不一样。你发问题,它回带引用的答案。grounding 这一步在 server 端完成。agent 的任务是"答"而不是"取"时用它。和下一项配对使用做源标渲染。
- Perplexity Citations — 渲染源脚注 — 渲染那一半。Sonar 返回 citation 数组;这个端点产出干净的源脚注,可以塞进 agent 回复或 Markdown UI。两个配对装,不要单独装。Perplexity API 参考里每条 citation 是结构化对象,含 URL / 标题 / snippet — 照原文渲染,不要让 LLM 改写。
- Qdrant MCP — 向量搜索引擎 — 私有文档层。把文档、代码、runbook、转录稿索引成 embedding;MCP 暴露
qdrant-find和qdrant-store给 agent 用。Qdrant 是标准选择 — 有稳定的 MCP wrapper、Apache 2.0 宽松许可。先单机 Docker 起,撑不住再 scale。 - haiku.rag — Agentic RAG CLI + MCP 服务器 — 第二个 RAG MCP。Qdrant MCP 是薄的向量 wrapper,haiku.rag 是有主张的 RAG 管线 — chunking、检索、可选 rerank、citation 表层 — 同时作为本地 CLI 和 MCP 暴露。你不想自己造管线时选它;你想造时选 Qdrant MCP。
- lnav — 日志文件导航器 — 本地搜索的逃生通道。Web 搜索和向量 RAG 覆盖内容层;lnav 覆盖运维层。Agent shell 出去跑 lnav,在本地日志上跑 SQL,拿到带时间戳的匹配。lnav 文档说它自动识别常见日志格式 — agent 不用额外 prompt 就能用。
- Ragas — 评测 RAG & LLM 应用 — 收官。管线接通之后,Ragas 用你的 eval set 给 answer faithfulness / context precision / context recall 打分。这是你发现 omnisearch 路由到错的 provider、Qdrant 取出"语义相近但不对"的 chunk、LLM 把源材料改写成源里根本没说过的话的方式。
它们怎么协同
[ 公网层 ] [ 私有 corpus 层 ] [ 本地运维层 ]
Tavily MCP Qdrant MCP lnav(SQL 跑日志)
Exa MCP haiku.rag
Firecrawl MCP │
│ │
└─── mcp-omnisearch ──┐ │
│ │
Perplexity Sonar API │
│ │
Perplexity Citations │
│ │
└──── agent 答案 ────► Ragas eval set
主干是 Tavily MCP + Perplexity Sonar + Citations + Qdrant MCP + Ragas — 这五件套覆盖单人 agent 80% 的搜索 + RAG 工作。Exa MCP 和 Firecrawl MCP 是备选 web backend,做语义搜索或全页抓取时切换。omnisearch 是菜单变多之后的路由层。haiku.rag 是有主张的 RAG 分支,你不想自己拼管线时用。lnav 是本地运维分支,问题是日志而不是文档时用。
Web 搜索 vs RAG vs 带引用的答案 — 选对层
- Web 搜索 MCP(Tavily / Exa / Firecrawl) — agent 拿 snippet 或整页,自己决定下一步。最适合探索、查事实、找链接。最不适合"用户想要答案而不是研究报告"。
- 带引用的答案 API(Perplexity Sonar + Citations) — server 端做检索和合成,返回一个答案 + 来源。最适合 chatbot 和 copilot。最不适合"我想控制具体用了哪些源"。
- 向量 RAG(Qdrant MCP / haiku.rag) — agent 从你的 corpus 检索。最适合私有文档、内部 runbook、代码考古。最不适合"变化频率快于你重建索引节奏"的内容。
- 本地搜索(lnav) — 在本地日志上跑 SQL。最适合运维问题。最不适合内容问题 — 不要把它当通用检索层。
真实的 agent 通常至少混两个层。常犯的错是只选一个 — 通常是向量 RAG — 然后硬把它套到所有形状上。
引用和 grounding — 不要省评测这一步
把搜索和 RAG 堆在 agent 后面的全部意义,是你能证明每个声明是从哪儿来的。这个承诺只有在你真的去度量时才成立。Ragas 算的三个指标对本 pack 都重要:
- Faithfulness — 答案是不是从检索到的上下文里推出来的,还是 LLM 自己编的?这是抓"模型把源材料改写成源里根本没说的话"的关键指标。
- Context precision — 取出来的 chunk 里有多少是相关的?低 precision = 你的向量库取多了,LLM 在被迫过滤本不该看的噪声。
- Context recall — 应该取出来的 chunk 里取到了多少?低 recall = 你的 chunking 或 embedding 把答案整块漏掉了。
第一天就用一个很小的 eval set 接上 Ragas — 5 个问题、手标正确上下文,就够你在切换 omnisearch 后面的 provider、调整 chunker 时抓回归。
常见踩坑
- 装了两个 web 搜索 MCP 但没有路由逻辑 — agent 会按调用随机挑一个。要么在系统提示里设默认,要么在第二个 backend 之前先装 omnisearch。
- 把 Perplexity Sonar 当搜索 API 用 — Sonar 已经合成了。你想要 snippet 喂自己的 LLM,你想要的是 Tavily 或 Exa。Sonar 的价值是答案 + 引用一起给。
- Qdrant 不带重建索引任务 — 向量 RAG 会静默腐烂。Corpus 漂移、embedding 不更新,agent 自信满满地回答半年前的旧答案。第一天就排重建索引计划。
- haiku.rag 和 Qdrant MCP 都接上 — 它们重叠。选一个做规范 RAG 路径,另一个可以关掉。两层 RAG 比一层更糟 — agent 不知道信哪个。
- Citation 被改写成转述 — 让 LLM 改写 citation 文本就毁掉了审计链。Perplexity Citations 的 payload 照原文 + 链接渲染,不要当散文。
- 跳过 Ragas — 没有 eval set,每次"答案看起来对"都是下一次切 backend 时等爆的回归。
10 个资产打包就绪
常见问题
只来得及装一个,先装哪个?
Tavily MCP。它的返回格式是最接近 LLM 能直接用的形状 — snippet、citation 元数据、可选的生成答案,一次调用全有。本 pack 里其他 web 搜索 MCP 都是在它之上做优化:Exa 做语义搜索、Firecrawl 做整页抓取、omnisearch 做路由。如果你跳过 Tavily 直接上原生 Brave 或通用搜索 wrapper,agent 会花 token 解析一份本来就不是给它做的 SERP 格式。
Perplexity Sonar 和 Tavily 都能返回搜索结果,区别是啥?
它们在不同层。Tavily 返回 snippet,让你的 LLM 合成答案。Perplexity Sonar 在 server 端做检索 + 合成,返回一个答案加结构化 citation。想控制"哪些源进了模型上下文窗口"用 Tavily。想要"答案 + 引用一起给、能直接发出去"用 Sonar。如果你叠用 — Tavily 先收候选、Sonar 再合成 — 你付了两份钱、还把审计链搞糊涂。每次调用挑一个层。
Qdrant MCP 和 haiku.rag 需要都装吗?
不要,同时接通常更糟。Qdrant MCP 是薄的向量 wrapper — 你自己带 chunking 和检索逻辑。haiku.rag 是有主张的 RAG 管线,帮你处理 chunking / 检索 / citation 表层,同时作为 CLI 和 MCP 暴露。已经有自己管线 + 想在前面套个稳定 MCP 壳 → 选 Qdrant MCP。完全不想造管线 → 选 haiku.rag。两个都装的话,在系统提示里指定一个做规范检索路径,agent 不要在两层 RAG 之间仲裁。
怎么保证 agent 给的 citation 是真的不是改写过的?
三层。(1) Perplexity Citations 的 payload 照原文渲染 — URL / 标题 / snippet 一字不改,永远不让 LLM 重写 citation 文本。(2) 你自己的 RAG 层在工具响应里同时返回 chunk 原文和文档 ID;在 prompt 里要求 agent 引用时逐字引而不是 summarize。(3) 用一个小 eval set 跑 Ragas faithfulness;faithfulness 就是专门抓"答案偏离检索到的上下文"的。第三步是你在 omnisearch 后面切 backend、调 chunker 时抓回归的方式。
这些能在离线 / 内网隔离环境跑吗?
部分能。Tavily / Exa / Firecrawl / Perplexity Sonar 都是托管 API,需要出网。Qdrant 本地跑 — 单机 Docker 对大多数个人 corpus 够用了。haiku.rag 本地跑。lnav 完全本地。Ragas 如果你接一个本地 LLM 做 judge,也能本地跑。所以离线子集是 Qdrant + haiku.rag + lnav + Ragas(本地 judge)。本 pack 里 web 搜索和带引用答案这两层没有离线等价物 — 你要换成自托管搜索索引(比如 Hister 或 Elasticsearch MCP),代价是失去 AI 友好的返回格式。