Files
Aq-Accounting-Spring/docs/SearXNG搭建指南.md
T
2026-09-24 14:27:12 +08:00

15 KiB
Raw Blame History

SearXNG 自建搜索引擎搭建指南

用途:给 AI 助手提供联网搜索能力(后端 web_search 工具的后端服务) 环境:Docker 29.7.2 / SearXNG 2026.9.23-3cd69d30e / Ubuntu(腾讯云 231) 验证状态:已上线运行,实测 14–17 条结果、零异常、约 1.5 秒


0. 这套东西解决什么问题

大模型自己不能上网。你问它"今天的天气"或"Flutter 最新版有什么变化",它只能靠训练数据里的旧信息编——这叫做幻觉。

正确做法是给它一个工具:

你提问  →  LLM 判断"我需要联网查一下"
        →  它让后端调用 web_search("Flutter 3.47 新特性")
        →  后端去搜索引擎拿结果
        →  把结果喂回给 LLM
        →  LLM 基于真实结果回答

问题在于中间那个搜索引擎从哪来:

方案 问题
Google / Bing 官方 API 要钱、要申请 key、按次计费
直接爬搜索引擎页面 很快被封 IP,还要处理反爬
SearXNG(本方案) 开源自托管、免费、有现成的 JSON 接口

1. 先理解原理(不然后面调优会瞎猜)★

这是全文最重要的一节。

SearXNG 自己不会搜索,它是元搜索引擎(聚合器):

                 ┌─→ Google      ─┐
你的查询 ─→ SearXNG ─→ DuckDuckGo  ─┼─→ 汇总 / 去重 / 排序 ─→ 返回
                 ├─→ Brave       ─┤
                 └─→ 百度        ─┘

推论(记住这一条,能省你一小时):

SearXNG 的可用性 = 它底下那些引擎在你服务器上通不通。

所以:

  • 装在国外服务器上 → 默认配置开箱即用(Google/Bing 都通)
  • 装在国内服务器上 → 默认配置返回 0 条结果,因为默认开的全是墙外的

这不是 SearXNG 装坏了,是它的引擎连不上。第 4 节专门讲怎么解决。


2. 安装

2.1 前置

docker --version        # 需要 Docker

2.2 建配置目录

SearXNG 容器里以 uid 977 的用户运行,目录权限必须给对,否则它写不了配置:

mkdir -p /home/nzy/searxng
chown -R 977:977 /home/nzy/searxng

第一次启动时 SearXNG 会自动生成 settings.yml(含随机 secret_key)。预先把目录 chown 好,避免它因为没权限而静默失败。

2.3 启动容器

docker run -d --name searxng --restart unless-stopped \
  -p 127.0.0.1:8888:8080 \
  -v /home/nzy/searxng:/etc/searxng:rw \
  -e BASE_URL=http://127.0.0.1:8888/ \
  -e INSTANCE_NAME=qiangbao-search \
  searxng/searxng

逐项说明:

参数 作用
-p 127.0.0.1:8888:8080 只绑本机。外面访问不到,降低攻击面和运维负担
-v .../searxng:/etc/searxng:rw 配置持久化,容器删了配置还在
--restart unless-stopped 开机自启 / 挂了自动拉起来
BASE_URL 影响 SearXNG 生成的链接,即使本机用也建议设对

2.4 为什么只绑 127.0.0.1

后端和 SearXNG 在同一台机器上,走 localhost 就够了。不对外暴露的好处:

  • 不用管 limiter(防爬限流)—— 本项目就是关掉的
  • 不用担心被当成公开实例滥用、被扫
  • 不用配 HTTPS、不用备案

如果哪天你需要从别的机器访问它,不要直接把端口开到公网。至少加上 limiter: true + 反代 + 鉴权。

2.5 验证起来了

docker ps --filter name=searxng --format "  {{.Names}}  {{.Status}}  {{.Ports}}"
# 期望:searxng  Up xx seconds  127.0.0.1:8888->8080/tcp

ls -la /home/nzy/searxng/
# 期望:看到自动生成的 settings.yml

3. 两个必踩的坑 ★

坑一:JSON 接口默认是关的

现象:请求 ?format=json 返回 403,而且报错信息完全看不出原因。

原因:SearXNG 官方镜像默认只开启 html 格式,JSON 需要显式打开。

修法:在 settings.yml 里加

search:
  formats:
    - html
    - json          # ← 没有这行,你的程序永远拿不到数据

改完必须重启容器:

docker restart searxng

排查技巧:如果你看到 403 但浏览器打开首页是正常的,先怀疑这一条。

坑二:国内服务器上默认引擎全军覆没

现象:接口返回 200,但 results 是空数组。

原因:默认启用的通用引擎是 google cse / duckduckgo / brave / wikipedia / wikidata —— 在国内服务器上全部连不上。

实测数据(2026-09-24,腾讯云国内机器):

结果数: 0
无响应引擎: [['brave','timeout'], ['duckduckgo','timeout'], ['google cse','timeout'],
            ['wikidata','timeout'], ['wikipedia','timeout']]

更糟的是:这些引擎不是立刻失败,而是各等 3 秒超时。所以你还会觉得"搜索怎么这么慢"。

修法:见下一节。


4. 引擎调优(国内服务器重点)

4.1 第一步:学会看诊断信息

SearXNG 的 JSON 返回里有个关键字段 unresponsive_engines,它会告诉你哪些引擎挂了、为什么挂。这是排错的第一入口:

curl -s --get --data-urlencode "q=Flutter 状态管理" --data "format=json" \
  http://127.0.0.1:8888/search | python3 -c "
import json, sys
d = json.load(sys.stdin)
print('结果数:', len(d.get('results', [])))
print('异常引擎:', d.get('unresponsive_engines', []))
eng = {}
for r in d.get('results', []):
    eng[r.get('engine')] = eng.get(r.get('engine'), 0) + 1
print('各引擎贡献:', eng)
"

三个指标都要看:

指标 含义
结果数 0 就是没搜到,说明引擎全挂
异常引擎 谁挂了、挂的原因(timeout / CAPTCHA / crash)
各引擎贡献 谁真的在干活。贡献 0 的引擎可以直接关掉,省它的等待时间

4.2 第二步:拿到引擎的准确名字

这一步千万别跳过。 SearXNG 是按 name 覆盖配置的,名字写错不会报错,只会静默失效——你会以为改了,其实没生效。

# 看镜像里装了哪些引擎模块
docker exec searxng ls /usr/local/searxng/searx/engines/

# 用 /config 接口拿准确名字和当前启用状态(推荐)
curl -s http://127.0.0.1:8888/config | python3 -c "
import json, sys
keys = ['baidu', '360', 'quark', 'sogou', 'bing']
for e in json.load(sys.stdin).get('engines', []):
    if any(k in e['name'].lower() for k in keys):
        print(f\"{e['name']:<20} enabled={e['enabled']}\")
"

4.3 第三步:实测结论(本项目)

逐个开关测出来的结果:

引擎 分类 实测 处置
360search general ✅ 可用 开启
quark(夸克) general ✅ 可用 开启
bing general,web ⚠️ 没报错但贡献 0 条 保留(无害)
baidu general ❌ CAPTCHA(被风控拦) 关闭
sogou general ❌ unexpected crash 关闭
google cse general,web ❌ timeout 关闭
duckduckgo general ❌ timeout 关闭
brave general ❌ timeout 关闭
wikipedia general ❌ timeout 关闭
wikidata general ❌ timeout 关闭

注意:这个结论只对国内服务器成立。如果你的服务器在香港/海外,Google 系反而应该开着。别抄结论,抄方法。

为什么百度也不行? 搜狗、百度这类国内引擎的反爬很激进,SearXNG 的对应实现经常被拦(返回验证码页)。所以"国内引擎"不等于"国内服务器上能用"——必须实测。

4.4 最终配置

/home/nzy/searxng/settings.yml 完整内容:

# SearXNG —— 给强宝小助手用
#
# 关键一条:search.formats 必须包含 json。官方镜像默认只开 html,
# 不开的话 /search?format=json 直接返回 403,且报错信息看不出原因。
use_default_settings: true

server:
  secret_key: "<首次启动自动生成的随机串,保持原样别改>"
  image_proxy: true
  limiter: false          # 本机自用,限流会让 JSON 接口返回 429
  public_instance: false

search:
  formats:
    - html
    - json

# 引擎取舍:SearXNG 自己不会搜索,它只是聚合各家引擎。
# 这台机器在国内,google/duckduckgo/brave/wikipedia 全部连不上,
# 而且每个都要白等 3 秒超时 —— 必须关掉,否则搜索又慢又空。
engines:
  # 国内能通的
  - name: 360search
    disabled: false
  - name: quark
    disabled: false
  - name: bing
    disabled: false

  # 实测不行的:baidu 被 CAPTCHA 拦、sogou 报 unexpected crash
  - name: baidu
    disabled: true
  - name: sogou
    disabled: true

  # 实测 timeout 的(2026-09-24 测出来 5 个全挂)
  - name: google cse
    disabled: true
  - name: duckduckgo
    disabled: true
  - name: brave
    disabled: true
  - name: wikipedia
    disabled: true
  - name: wikidata
    disabled: true

改完必做:

chown 977:977 /home/nzy/searxng/settings.yml    # 权限别丢
docker restart searxng
sleep 7                                          # 给它点启动时间
docker logs searxng 2>&1 | grep -iE "error|invalid|unknown" | tail -5

日志里如果有 can't register engine 说明引擎名写错了,配置那行会静默失效——这就是 4.2 要你先拿准确名字的原因。

4.5 调优后的效果

查询「Flutter 状态管理」  → 12 条结果,1.5 秒,异常 []
查询「MySQL 主从复制 搭建」→ 14 条结果,异常 []
查询「深度求索 DeepSeek」  → 17 条结果,异常 []
各引擎贡献: {'360search': 6, 'quark': 8}

5. 验证清单

装完之后按顺序验三件事:

# ① 服务活着 + JSON 格式已开(这一步只验格式,不验引擎)
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
  "http://127.0.0.1:8888/search?format=json&q=test"
# 200 = 格式开了;403 = 回去看 3.1

# ② 引擎真的能搜到东西
curl -s --get --data-urlencode "q=测试" --data "format=json" \
  http://127.0.0.1:8888/search | python3 -c "
import json,sys; d=json.load(sys.stdin)
print('结果数:', len(d.get('results',[])), '异常:', d.get('unresponsive_engines',[]))
"
# 结果数 >0 且 异常=[] 才算通

# ③ 中文查询也正常(很多引擎对中文支持差)
curl -s --get --data-urlencode "q=今天有什么新闻" --data "format=json" \
  http://127.0.0.1:8888/search | python3 -c "
import json,sys; print(len(json.load(sys.stdin).get('results',[])))
"

6. 接入后端(本项目)

6.1 配置

后端通过环境变量读地址(见 application.yml 的 ai.search.base-url):

# /home/nzy/.env
SEARCH_BASE_URL=http://127.0.0.1:8888

manage.sh 会在启动 java 之前自动 source 这个文件。改完重启:

cd /home/nzy && ./manage.sh restart
# 确认变量真的进到进程里了
tr '\0' '\n' < /proc/$(cat app.pid)/environ | grep SEARCH_

不配这个变量会怎样:后端不注册 web_search 工具——模型不知道自己能联网,也就不会瞎编"我查到了"。这是刻意的设计。

6.2 后端怎么用它

LLM 决定联网
   ↓ tool_calls: web_search({"query": "..."})
SearxngApiClient.search(query, maxResults=5)
   ↓ GET {SEARCH_BASE_URL}/search?format=json&q=<urlencoded>
取前 5 条 → 拼成 JSON(title/url/snippet)→ 回填给模型
   ↓
模型基于真实结果回答,并标注来源

代码在 src/main/java/com/accounting/service/SearxngApiClient.java。

6.3 ⚠️ 一个安全提醒

脱敏层保护的是「发给 LLM 的内容」,不包含搜索词。

搜索词会原样发给 360search / 夸克(以及它们的上游)。所以:

  • ✅ 「帮我查一下 Flutter 3.47 有什么新特性」
  • ❌ 「帮我查一下 155.103.159.109 这台机器」

第二句会把你的服务器 IP 送到第三方搜索引擎。别在联网搜索里带敏感信息。


7. 日常维护与排错

7.1 常用命令

docker logs searxng 2>&1 | tail -30        # 看日志
docker restart searxng                     # 改配置后重启
docker stats searxng --no-stream           # 资源占用

7.2 排错对照表

现象 原因 处置
?format=json 返回 403 search.formats 没加 json 见 3.1
返回 200 但结果数为 0 引擎全连不上 见 3.2 / 4.3
结果里有 CAPTCHA 该引擎被风控拦 关掉它
结果里有 unexpected crash 该引擎实现有 bug 关掉它
结果里有 timeout 网络不通 关掉它(别留着白等)
返回 429 limiter 开着 设 limiter: false 并重启
日志报 can't register engine 引擎名写错 用 /config 拿准确名字(4.2)
容器反复重启 配置目录权限不对 chown -R 977:977 /home/nzy/searxng
本机 curl 不通 端口没发布 / 绑错地址 检查 -p 127.0.0.1:8888:8080
搜索很慢(3 秒以上) 有引擎在等超时 用 unresponsive_engines 找出并关掉

7.3 升级 SearXNG

docker pull searxng/searxng
docker stop searxng && docker rm searxng
# 重新执行 2.3 的 docker run(配置在挂载目录里,不会丢)

升级后重新跑一遍第 5 节的验证——新版本可能改了默认引擎列表。


8. 附录

8.1 命令速查

# 启动
docker start searxng

# 看哪些引擎启用中
curl -s http://127.0.0.1:8888/config | python3 -c "
import json,sys
for e in json.load(sys.stdin)['engines']:
    if e['enabled']: print(e['name'], '|', ','.join(e.get('categories',[])))
"

# 完整搜索测试(三个指标一起看)
curl -s --get --data-urlencode "q=关键词" --data "format=json" \
  http://127.0.0.1:8888/search | python3 -c "
import json,sys; d=json.load(sys.stdin)
print('结果数:', len(d.get('results',[])))
print('异常引擎:', d.get('unresponsive_engines',[]))
eng={}
for r in d.get('results',[]): eng[r['engine']]=eng.get(r['engine'],0)+1
print('各引擎贡献:', eng)
"

# 浏览器里直接用(图形界面,方便手工试)
# 把 8888 端口临时转发出来,或直接在服务器上用 lynx/curl 看 html

8.2 本项目的部署信息

项 值
服务器 腾讯云 231(国内)
容器名 searxng
监听 127.0.0.1:8888(不对外)
配置目录 /home/nzy/searxng/
版本 SearXNG 2026.9.23-3cd69d30e
后端配置项 SEARCH_BASE_URL(在 /home/nzy/.env)
可用引擎 360search、quark(bing 保留)
停用方式 docker stop searxng(后端会自动不给模型 web_search 工具)

8.3 一句话总结

SearXNG 装起来只要三行命令,难的是「它在你的服务器上能不能真的搜到东西」——而那 100% 取决于底层引擎的网络可达性。先测、再配,别抄别人的引擎清单。


文档创建:2026-09-24 · 所有数据均为在本项目服务器上实测