ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SearXNG自托管元搜索引擎:部署配置与OpenWebUI联网问答

SearXNG自托管元搜索引擎:部署配置与OpenWebUI联网问答 SearXNG 这个名字第一次被我认真记下来是因为一个很具体的麻烦本地跑着一套问答系统模型对训练数据之外的事情几乎一问三不知接官方检索接口又按量计费用量一上来账单完全不可控。我需要一个自己能掌握、随时可调、结果还能自己动手排序的检索入口。翻了一圈资料落点就是 SearXNG 这个元搜索引擎。元搜索听起来玄乎说白了它就是搜索的中间层你提交一次关键词它同时向多个公开搜索引擎发出请求把各自返回的原始结果收回来做归一化、去重、加权重排最后用一个统一页面或 JSON 接口吐给你。它本身不建索引、不爬全互联网价值全在聚合和可控这四个字上。这篇内容我会把自己从零搭起来、接到 OpenWebUI 做联网问答、再到长期维护踩过的坑按实际顺序讲一遍。适合自己折腾过容器、想给本地模型补上时效性检索能力的人也适合单纯想把搜索行为收回自己手里的人。1. 元搜索不是多开几个搜索框SearXNG 在中间做了什么1.1 一次查询从提交到出结果的完整链路很多人第一次看 SearXNG 的架构会觉得它只是个套壳页面把几个搜索框塞在一个输入框后面。真跑起来你会发现中间那段处理逻辑才是它全部的价值所在。一次查询的完整走向大致是这样浏览器或调用方向/search发起请求携带q、categories、engines、language、pageno、time_range、safesearch、format这些参数。引擎调度层根据categories和engines筛选出这次要用的引擎集合然后开线程池并发向每个引擎发请求谁先回谁先处理不会傻等最慢的那个。每个引擎配一个独立的解析器用 XPath 或 CSS 选择器从返回的页面里抠出标题、链接、摘要、发布时间。抠出来之后是最关键的一步归一化。不管上游返回的结构差多远全部整理成同一套字段——url、title、content、publishedDate、engine、score、category。接着按 URL 去重这一步会顺手剥掉utm_source、fbclid这类跟踪参数统一域名大小写避免同一个页面因为参数不同被算成两条。最后是打分排序分数来自引擎权重、结果在原始列表里的位置、有没有摘要、有没有发布时间这几个因子。提示weight这个字段是可以自己调的。默认所有引擎权重相同但如果你发现某类内容某个引擎质量明显更高把它调到 1.3 到 1.5排序结果会立刻不一样。1.2 自托管之后变的是数据流向而不是搜索能力必须先说清楚一件事SearXNG 不会让你搜到原本搜不到的东西它用的是公开搜索引擎的结果检索能力和上游一致。自建带来的变化在于数据流向和调用方式。查询从你自己的服务器发出多个引擎各拿到查询的一个片段而不是所有搜索行为集中堆在同一家。默认配置下它不设账户体系不写搜索历史。但这里有个绝大多数人都会忽略的细节你以为自建就不留痕结果入口层把带q参数的完整 URL 全记进了访问日志。Nginx 的 access log 默认会记录完整请求行包括查询词。真要干净得在日志格式里把查询串去掉或者干脆降低访问日志级别。另一个实际变化是可编程。开启 JSON 输出后/search?qxxxformatjson就是一个标准接口任何脚本、任何问答系统都能直接消费。这一点在使用价值上远比隐私来得直观——它把搜索从人用页面变成了机器调接口。1.3 谁适合自建谁装了会后悔说句实在话这个工具不是所有人都需要。适合的典型场景有几类在做本地问答或知识库需要给模型接一个可控的外部检索源手上有一堆脚本需要统一检索能力不想挨个对接不同平台的接口团队内部想有一个统一的搜索入口结果排序规则自己定以及单纯在意自己的查询记录由谁保管的人。会后悔的情况也很典型。指望它比直接用某个搜索引擎更快的人会失望——聚合意味着要等一批并发请求回来延迟天然更高尤其引擎配得多的时候。完全不想维护的人也会难受上游页面一改版对应的解析器就可能失效需要跟着升级。还有一类是把它当成内容抓取工具的抱歉它只返回摘要片段不搬运正文。2. 部署形态怎么选容器、裸机还是内网小主机2.1 我为什么最终固定在容器方案裸机直装不是不行Python 依赖、uwsgi 配置、系统服务托管这一套走下来能跑但升级和回滚都很痛苦。我前后试过三种装法最后稳定在 Docker Compose理由很朴素配置目录挂载出来镜像换版本就一句docker compose pull出问题回退旧 tag 就行不动宿主机环境。我的docker-compose.yml大概是这个形态去掉了一些和本文无关的服务services: searxng: image: searxng/searxng:2024.11.1-abc1234 container_name: searxng restart: unless-stopped ports: - 127.0.0.1:8080:8080 volumes: - ./searxng:/etc/searxng:rw environment: - SEARXNG_BASE_URLhttp://192.168.1.10:8080/ - SEARXNG_SECRET替换成openssl_rand_hex_32生成的字符串 cap_drop: - ALL cap_add: - CHOWN - SETGID - SETUID logging: driver: json-file options: max-size: 2m max-file: 2几个关键点值得单独讲。端口映射我写的是127.0.0.1:8080:8080只监听本机回环外面由统一入口转发不要图省事直接8080:8080暴露到公网——一个没有访问控制的搜索接口被扫到很快就会被脚本刷爆。SEARXNG_SECRET别用默认值用openssl rand -hex 32生成一串它参与会话和 CSRF 相关处理。cap_drop: ALL后面再单独加回三个必要能力是最小权限原则的常规操作能省掉一堆潜在风险。镜像 tag 我习惯写具体版本号而不是latest。latest看着省事某天自动拉下来一个带 breaking change 的新版配置解析失败服务起不来排查半天才发现是版本问题。2.2 入口地址和 BASE_URL 不一致会出哪些怪问题SEARXNG_BASE_URL这个环境变量是新手上手最容易翻车的地方它必须和你实际访问实例时用的地址完全一致包括协议、域名、端口、末尾斜杠。不一致会出现什么现象表单提交后跳到错误地址、静态资源 404 导致页面样式全丢、分页链接指向不存在的路径、API 返回里的相对路径拼不对。这些问题看起来像前端坏了实际根因都在这个变量上。如果你的实例挂在子路径下比如https://example.com/searxng/那BASE_URL就得写成带/searxng/的完整形式同时入口转发规则里proxy_pass末尾的斜杠必须写对。少了那个斜杠路径拼接会少一段或者多一段静态资源照样 404。我在这上面浪费过整整一个下午最后发现就是斜杠的问题。注意改完BASE_URL一定要重启容器光改文件不重启是不生效的这个坑我踩过不止一次。2.3 资源估算1 核 1G 到底能不能用实测数据放这儿空闲状态下容器内存占用在 200MB 到 300MB 之间主要是 Python 运行时加上 uwsgi 进程。磁盘占用方面镜像解压后大约几百 MB配置和数据目录很小除非你启用了持久化缓存。CPU 才是真正的瓶颈而且瓶颈出现在并发请求引擎的那一瞬间。1 核 1G 的机器如果只启用五六个响应快的引擎日常使用完全够单次查询两三秒出结果。但如果把默认引擎列表全开——那通常是几十个——再叠加高并发CPU 会直接被拉满请求排队表现就是越用越慢。我的经验值是1 核 2G 配 8 到 12 个精选引擎是舒适区2 核 4G 可以放开到 20 个左右并且扛住小团队并发再往上就没必要堆硬件了先把引擎列表裁剪干净收益更大。3. settings.yml 才是整套系统的神经中枢3.1 引擎全开是最常见的性能自杀SearXNG 的配置文件默认开启use_default_settings: true意思是先加载内置的完整引擎清单再用你写的配置做增量覆盖。很多人没搞懂这一点以为在engines里写几个引擎就等于只启用这几个结果内置的几十个引擎一个没少全都在跑。正确的裁剪姿势是在engines列表里对不想要的引擎显式写disabled: true对想要的引擎调权重需要新引擎时直接追加定义use_default_settings: true engines: - name: google engine: google disabled: false weight: 1.4 - name: bing engine: bing disabled: false weight: 1.2 - name: wikipedia engine: wikipedia disabled: false weight: 0.8 - name: 某个你根本用不上的引擎 engine: xxx disabled: true选引擎的取舍逻辑很简单留响应快、结果质量高、解析长期没坏过的。中文检索场景下通用网页引擎留两到三个足够百科类留一个代码和技术文档类按需留一个。分类太杂反而会拉低整体结果质量因为低质量来源的摘要会混进排序里。3.2 search 段的参数组合与含义search这一段控制的是搜索行为和输出格式参数不多但每个都影响使用体验。参数作用我的建议值safe_search内容过滤等级0 关闭 1 中等 2 严格自用填 0共享实例填 1autocomplete输入联想来源留空即关闭留空少一次外部请求default_lang默认语言zh-CN或allformats允许的输出格式至少包含html和jsonlimiter请求频率限制防滥用内网自用false对外truemax_page最大翻页数3 到 5 之间autocomplete我建议直接留空每敲一个字都要发一次请求收益不明显还增加延迟。default_lang设成zh-CN能让中文结果的排序更贴合预期但如果你经常混着搜英文资料填all更省事。3.3 超时、连接池与结果数量的三角平衡outgoing这一段是性能调优的核心绝大多数查询慢的问题都能在这里找到解法。outgoing: request_timeout: 5.0 max_request_timeout: 12.0 pool_connections: 100 pool_maxsize: 20 enable_http2: truerequest_timeout是单个引擎请求的耐心值。设太大一个慢引擎就能拖垮整次查询设太小本来能回来的结果被砍掉。5 秒是我试出来比较均衡的值。max_request_timeout是整次查询的总上限超过就返回已经拿到的部分结果12 秒足够覆盖绝大多数场景。pool_connections和pool_maxsize控制的是连接复用。前者是连接池能缓存的连接总数后者是单主机并发连接上限。这两个值调太大没意义反而占内存调太小会导致请求排队等连接。100 和 20 是社区里比较常见的组合我照着用了很久没出过问题。enable_http2打开是有收益的多个请求复用同一个连接在引擎数量多的时候能明显减少握手开销。3.4 打开 JSON 输出同时把接口关进笼子formats里加上json之后接口地址就变成了/search?q关键词formatjson。这一步是后面接问答系统的前提不加的话返回的是 HTML 页面程序没法直接解析。但接口一开安全问题就来了。一个任何人都能匿名调用、还不限速的搜索接口就是给别人准备的免费检索资源。limiter打开后会有基于请求来源的频率控制能挡掉一部分滥用但它的防护强度有限。我的做法是分两层实例本身只监听内网或者回环地址外部完全够不着需要跨网络访问的场景在入口层加身份校验再做频率限制。把安全和易用性分开考虑比在一个配置文件里纠结参数要清爽得多。4. 把 SearXNG 接进 OpenWebUI 做联网问答4.1 本地知识库为什么需要一个外部检索兜底本地部署的问答系统有个天然短板它只知道两件事——模型训练时见过的东西和你喂给它的文档。训练数据有时间截止点本地文档库覆盖不了天底下所有话题。用户问一件上个月刚发生的事模型要么说不知道要么编一个看起来很像的答案后者更麻烦。给问答系统接一个检索环节让它在回答前先查一遍外部资料把查到的片段塞进上下文这是现在最通用的做法。SearXNG 在这个位置上的优势是接口标准、返回结构化、费用为零、结果来源可自己控制。4.2 对接参数与返回结构的逐字段说明OpenWebUI 的联网检索设置里把搜索引擎类型选成 SearXNG然后填接口地址。地址形式是http://your-searxng-host:8080/search?qqueryquery是占位符调用时会被替换成实际的查询词。这里有个几乎每个人都会踩的坑OpenWebUI 如果跑在容器里localhost指向的是它自己那个容器不是宿主机。填http://localhost:8080必然连不上。要么用容器名两个服务在同一个自定义网络里要么用宿主机的内网地址要么在启动 OpenWebUI 容器时加--add-hosthost.docker.internal:host-gateway然后用这个主机名。返回的 JSON 结构长这样{ query: 关键词, number_of_results: 0, results: [ { url: https://example.com/article, title: 文章标题, content: 这里是搜索结果摘要文本, engine: google, score: 3.2, category: general, publishedDate: 2024-11-01T00:00:00 } ] }注意number_of_results这个字段在很多版本里是 0 或者不准确别拿它做逻辑判断直接数results数组的长度更靠谱。score是排序依据越大越靠前做结果筛选时可以用它卡阈值。4.3 检索片段塞进提示词前的清洗工序拿到results数组直接往提示词里塞是很常见的做法但效果往往不好。原因是摘要文本质量参差不齐有的只有半句话有的是导航栏文字有的来自内容农场。我在中间加了一道清洗工序规则不复杂但收益明显。先按score降序排列取前 5 到 8 条content字段统一截断到 300 到 500 个字符太长会挤占上下文窗口标题或摘要里命中典型低质特征的直接丢掉最后把保留下来的条目拼成序号 标题 来源链接 摘要的固定格式。固定格式这件事比想象中重要。模型对结构化输入的利用率明显高于一堆散乱文本而且带了来源链接之后回答里可以标注出处可信度完全不同。4.4 实测里的三类翻车空结果、超时、模型无视第一类是搜出空结果。最常见的原因是formats里没加json接口返回的是一整页 HTML调用方解析失败日志里看到的却是请求成功非常误导人。另一个原因是被上游限流了短时间内请求太密集。第二类是超时。这个基本可以定位到引擎数量上启用二三十个引擎其中总有那么几个响应慢整次查询被拖到十几秒。解决办法就是裁剪引擎把/stats里响应时间明显偏高的挑出来禁掉。第三类最有意思检索明明成功了模型在回答时却完全无视检索到的内容继续按自己的记忆答。这不是接口问题是提示词问题。提示词里必须明确写优先依据以下资料回答资料未涉及的再使用你自己的知识并标注引用来源。不写这句话模型很可能把检索结果当成可有可无的参考。5. 某个引擎突然没结果了按这个顺序查5.1 先看 /stats用数据替代猜测引擎出问题的时候第一反应容易是去翻配置文件、改参数、重启服务这套动作下来往往什么问题都没解决。正确姿势是先看数据。访问/stats页面能看到每个引擎的成功率、错误率、平均响应时间这些指标。哪个引擎坏了、坏到什么程度、是从什么时候开始坏的一目了然。我有一次遇到某引擎连续几天零结果看 stats 才发现它的错误率是 100%而问题的根因是上游页面结构改了解析器匹配不到任何元素。同时可以在/preferences页面确认当前实际启用的引擎列表验证配置有没有按预期生效。很多配置改了没反应的情况都能在这里找到答案。5.2 常见故障的分类对照表现象可能原因处理方向单个引擎持续零结果上游页面改版解析规则失效升级镜像版本或临时设为 disabled大面积返回 429请求频率过高触发限流减少引擎数量启用缓存降低并发请求整体超时某个引擎响应极慢调低request_timeout剔除慢引擎返回内容变成验证页面被识别为自动化流量暂时禁用该引擎观察一段时间结果重复率异常高URL 归一化没覆盖某些参数检查是否新增了跟踪参数接口返回 HTML 而非 JSONformats未包含json修改配置并重启这张表是这两年陆陆续续攒下来的基本覆盖了我遇到过的所有情况。有一点要提醒引擎状态是动态变化的今天好的明天可能坏今天坏的过段时间可能又好了所以处理方式上临时禁用往往比彻底删除配置更合适。5.3 引擎是活的配置也得跟着走上游搜索引擎的页面结构、反爬策略、接口参数一直在变这意味着 SearXNG 的引擎解析器需要持续维护。官方版本更新里相当一部分内容就是修引擎。我的维护节奏大概是每月抽十几分钟看一次/stats把连续表现差的引擎清出去升级前先扫一眼更新说明里有没有引擎相关的改动settings.yml纳入版本管理每次改动都留记录。听起来麻烦实际做起来很快最怕的是放着不管半年某天发现一半引擎都废了再回头逐个排查就很累。提示排查引擎问题时先临时禁用再观察比直接删配置安全得多。禁用是可逆的删掉之后想恢复就得翻文档重写。6. 长期运行中值得打磨的几处细节6.1 缓存能省掉多少重复请求SearXNG 支持外接缓存后端配置里加上一段就能启用valkey: url: redis://redis:6379/0打开之后相同或相似查询在一段时间内会直接命中缓存不再向所有引擎重新发一遍请求。收益在两个场景下特别明显一是团队多人在查相似话题二是问答系统在短时间内反复检索同类关键词。缓存带来的另一个好处是降低被上游限流的概率。请求总量下去了触发频率控制的几率自然就小。代价是要多维护一个缓存服务内存占用看查询量和过期策略一般给个几百 MB 绰绰有余。6.2 实例标识与界面调整配置里有一组 branding 相关的字段可以设置实例名称、主题风格、默认主题、联系方式。自己的实例改个名字共享给同事用的时候一眼就能区分。还有一个容易被忽略的设置是默认分类和默认语言的组合。共享实例上给不同用途的人用默认值设得不对每次都要手动切体验会很差。中文为主的团队把default_lang设成zh-CN把默认分类设成通用网页基本就不用再动了。6.3 升级和配置备份别让一次 pull 毁掉半年配置最后一个话题也是我觉得最值得强调的。升级这件事我的三条规矩镜像 tag 写具体版本号不写latest升级前把settings.yml单独备份一份或者直接在 Git 里打个 tag升级后先看/stats和搜几个平时常用的关键词确认引擎正常再收工。配置漂移是长期运行中最隐蔽的问题。半年时间里你东改一点西改一点配置和最初的样子早就不同了这时候如果配置文件丢了没人能凭记忆复原。纳入版本管理每次改动一句话说明成本极低真出事的时候能救命。我个人在实际操作中的体会是这套东西的维护成本主要集中在引擎这一层而不是 SearXNG 本身。把它当成一个需要定期照看的小服务每个月花十分钟它就能一直跑得住。反过来装完就不管几个月后打开发现搜不出东西那时候要处理的问题会多得多。
返回列表