Dify 实现:基于 SearXNG MCP 的 Exa 免费替代方案 参考文章https://www.cnblogs.com/suknna/p/19695813有幸看到这篇文章我决定将它实现一下。这篇文章的设计哲学很清晰两步流水线 输出裁剪。尝试设计 Dify agent的具体实现。未来计划workflow的工作流实现。Dify 实现基于 SearXNG MCP 的 Exa 免费替代方案文章的核心思想把噪声挡在工具侧而不是让模型硬吞。在 Dify 中这个设计通过 “Agent 参数约束 System Prompt 编排” 来实现不需要额外写代码。整体架构用户提问 ↓ Dify Agent按 System Prompt 执行两步流水线 ↓ ① searxng_web_search搜索→ 输出裁剪为 Markdown 表格 ↓ 模型选链接 ② web_url_read读取→ 干净正文设硬上限 ↓ 模型综合多源 → 生成带引用的回答第一步添加 MCP 服务器Dify → 工具 → MCP → 添加 MCP 服务器 (HTTP)字段值名称SearXNG MCP服务器标识searxngURLhttp://192.168.31.184:3456/mcp导入成功后4 个工具会出现searxng_web_searchsearxng_search_suggestionssearxng_instance_infoweb_url_read示例图片如下第二步创建 Agent 应用新建 → Agent → 选择模型。系统提示词这是关键——直接复刻文章思路## 搜索研究协议 ### 步骤一搜索获取候选集 调用 searxng_web_search 搜索。 输出紧凑候选表最多 5 条 | # | 标题 | URL | 摘要 | |---|------|-----|------| 首轮结果不理想时改写 query 重试最多 2 轮。 ### 步骤二读取获取正文 从候选表中按权威性从高到低依次调用 web_url_read 获取完整内容。 **快速失败规则** - 连续 2 个不同域名的 URL 读取失败 → 立即停止读取不要继续尝试剩余候选 - 读取失败时记录失败原因如 安全策略拦截、超时、404 - maxLength 固定 8000 ### 步骤三降级策略新增 如果所有读取均失败 1. 直接用 searxng_web_search 的精简 query 提取关键信息 - what is topic best practice → 搜索 - 从搜索结果 snippet 中提取要点 2. 输出时明确标注 ⚠️ 以下答案基于搜索摘要snippet未经过完整页面读取验证。建议开发者在当前环境配置 MCP_HTTP_ALLOW_PRIVATE_URLStrue 以启用完整读取能力。 3. 务必列出所有候选来源 URL供用户手动验证 ### 步骤四综合输出读取成功时 交叉验证 2-3 个来源后输出带引用 URL。 ### 禁止行为 - 不要在搜索后直接用摘要回答除非步骤三降级触发 - 不要连续尝试 3 个以上失败读取 - 不要在 URL 读取全部失败时编造引用第三步工具参数配置将 SearXNG 的 4 个工具添加到 Agent示例图片如下第四步验证测试在 Agent 中测试以下问题“Go 语言中 context 的最佳实践是什么”预期执行过程Agent 调用searxng_web_search(querygolang context cancellation best practices, num_results5)Agent 输出 Markdown 表格展示 5 个候选Agent 挑选 2-3 个官方文档链接调用web_url_readAgent 综合读取内容生成带引用的回答SearXNG MCP安全策略拦截解决方案web_url_read 被 MCP 服务的安全策略拦截导致步骤二全部失败。官方文档 https://github.com/ihor-sokoliuk/mcp-searxng/blob/main/CONFIGURATION.md明确说明https://github.com/ihor-sokoliuk/mcp-searxng/security/advisories/GHSA-q87f-qc2r-2gw4GitHub 安全公告 GHSA-q87f-qc2r-2gw4 记录了关键版本变更v1.1.0 及之前私有地址防护仅在 MCP_HTTP_HARDENtrue 时生效默认配置下 web_url_read 无任何拦截v1.1.0 之后修复后私有地址拦截在所有模式下默认开启我的是1.11.1版本所以符合推理curl http://192.168.31.184:3456/health {status:healthy,server:ihor-sokoliuk/mcp-searxng,version:1.11.1,transport:http}在启动searXNG-mcp中加入MCP_HTTP_ALLOW_PRIVATE_URLStrue参数我是github安装的版本给出我的启动例子如果是docker启动类推即可echo offsetSEARXNG_URLhttp://localhost:8888setMCP_HTTP_PORT3456setMCP_HTTP_HOST0.0.0.0setMCP_HTTP_ALLOW_PRIVATE_URLStruesetNODEE:\software\nvm\v122\v24.13.1\node.exesetAPP_DIRD:\code\local\learning\mcp-searxngcd/d%APP_DIR%%NODE%dist\cli.js pause启用后 web_url_read 可以读取内网地址127.0.0.1、192.168.x.x 等请仅在信任的网络环境中使用测试 宿主机通过MCPModel Context Protocol服务器调用web_url_read 工具来读取指定网页$initBody{jsonrpc:2.0,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:test,version:1.0}},id:1}$responseInvoke-WebRequest-Urihttp://192.168.31.184:3456/mcp-MethodPost-ContentTypeapplication/json-Headers{Acceptapplication/json, text/event-stream}-Body$initBody$sessionId$response.Headers[Mcp-Session-Id]Write-HostSession ID:$sessionId$readBody{jsonrpc:2.0,method:tools/call,params:{name:web_url_read,arguments:{url:https://pkg.go.dev/context,maxLength:2000}},id:2}$resultInvoke-WebRequest-Urihttp://192.168.31.184:3456/mcp-MethodPost-ContentTypeapplication/json-Headers{Acceptapplication/json, text/event-stream;Mcp-Session-Id$sessionId}-Body$readBody$result.Content上面测试在配置MCP_HTTP_ALLOW_PRIVATE_URLStrue后应正常返回网页抓取结果