658 lines
20 KiB
Markdown
658 lines
20 KiB
Markdown
# SearXNG 私有搜索服务部署与使用
|
||
|
||
> 2026-09-16 记录。为 [[AI桌面陪伴助手方案]] 部署一个不绑定 LLM 厂商的联网搜索后端。服务运行在 `cadadmin-server`,只监听宿主机回环地址 `127.0.0.1:18080`,由同机助手调用,不对局域网或公网开放。
|
||
|
||
---
|
||
|
||
## 一、结论与当前状态
|
||
|
||
当前方案是:**DeepSeek 负责判断何时搜索和组织回答,SearXNG 负责查询互联网。**
|
||
|
||
```text
|
||
用户问题
|
||
↓
|
||
DeepSeek 判断是否需要实时资料
|
||
↓ Function Calling: web_search(query)
|
||
AI Companion 调用 SearXNG JSON API
|
||
↓
|
||
SearXNG 聚合 360 搜索 / 搜狗等上游结果
|
||
↓ 标题、摘要、URL
|
||
结果作为 tool 消息回灌 DeepSeek
|
||
↓
|
||
生成正文 + 来源 URL
|
||
├─ 网页:显示正文和来源
|
||
└─ TTS:只朗读正文,不读网址
|
||
```
|
||
|
||
截至 2026-09-16:
|
||
|
||
| 项目 | 状态 |
|
||
|---|---|
|
||
| 容器 | `ai-companion-searxng`,运行中 |
|
||
| 宿主机地址 | `http://127.0.0.1:18080` |
|
||
| 暴露范围 | 仅服务器回环地址 |
|
||
| 重启策略 | `unless-stopped` |
|
||
| JSON API | 已开启 |
|
||
| 可用上游 | 360 搜索、搜狗 |
|
||
| 暂不可用 | DuckDuckGo、Brave、Wikipedia 超时;Bing 暂时返回空结果 |
|
||
| 助手接入 | 已完成并通过真实工具调用验收 |
|
||
| 镜像 | 固定到已验收的 OCI digest,未使用浮动 `latest` |
|
||
|
||
真实闭环验收结果:DeepSeek 主动调用 `web_search('DeepSeek 最新消息 2025')`,SearXNG 返回 5 条结果,结果回灌后模型生成带两个来源 URL 的最终回答。
|
||
|
||
---
|
||
|
||
## 二、SearXNG 是什么
|
||
|
||
SearXNG 是开源的**元搜索引擎**。它本身不维护网页索引,而是把查询转发给多个搜索服务和数据库,再统一整理结果。官方项目强调不追踪、不画像用户,代码采用 AGPL-3.0 许可证。
|
||
|
||
这意味着:
|
||
|
||
- 自建 SearXNG 不需要维护爬虫和海量网页索引;
|
||
- 搜索质量、时延和可用性仍依赖上游搜索引擎;
|
||
- 同一个配置换到不同网络环境,结果可能完全不同;
|
||
- 自建的主要价值是统一 API、集中控制搜索源、减少对单一商业 API 的绑定,而不是“完全脱离外部搜索服务”。
|
||
|
||
官方资料:
|
||
|
||
- [SearXNG GitHub](https://github.com/searxng/searxng)
|
||
- [容器部署文档](https://docs.searxng.org/admin/installation-docker.html)
|
||
- [`settings.yml` 文档](https://docs.searxng.org/admin/settings/settings.html)
|
||
- [搜索引擎配置](https://docs.searxng.org/admin/settings/settings_engines.html)
|
||
- [Search API](https://docs.searxng.org/dev/search_api.html)
|
||
|
||
### 为什么选择它
|
||
|
||
| 方案 | 优点 | 缺点 | 本项目结论 |
|
||
|---|---|---|---|
|
||
| **SearXNG** | 开源、自建、统一 JSON API、可更换上游、无按次 API Key | 需要运维;上游会限流、验证码或封禁 | **采用** |
|
||
| DeepSeek 官方搜索 | 模型接入简单 | DeepSeek API 当前没有可直接使用的内置网页搜索,仍需自行实现工具 | 不可单独解决 |
|
||
| DDGS/DuckDuckGo 库 | 代码少、原型快 | 单一来源,服务器网络下 DuckDuckGo 实测超时 | 不采用 |
|
||
| Tavily / Brave / Exa | API 稳定,面向 Agent | 需要 Key,可能按量收费,绑定厂商 | 可作为未来备用 |
|
||
| Vane/Perplexica | 完整开源 AI 搜索应用 | 自带 UI、LLM 编排,与现有助手架构重叠,集成过重 | 只作参考 |
|
||
|
||
选择 SearXNG 的核心原因不是“绝对免费”,而是把搜索后端抽象成独立服务:以后更换 DeepSeek、本地模型或其他 LLM,不需要重写搜索层。
|
||
|
||
---
|
||
|
||
## 三、部署布局
|
||
|
||
代码仓库中的配置:
|
||
|
||
```text
|
||
~/ai-companion/deploy/searxng/
|
||
├── docker-compose.yml
|
||
├── settings.yml.example # 可提交的模板,密钥是占位符
|
||
└── settings.yml # 服务器实际配置,含随机密钥,不进 Git
|
||
```
|
||
|
||
Docker 数据:
|
||
|
||
```text
|
||
容器名:ai-companion-searxng
|
||
配置挂载:./settings.yml → /etc/searxng/settings.yml:ro
|
||
缓存卷:searxng_searxng-cache → /var/cache/searxng
|
||
端口:127.0.0.1:18080 → 容器 8080
|
||
```
|
||
|
||
官方文档列出的两个持久化位置是 `/etc/searxng` 和 `/var/cache/searxng`。本项目把配置作为只读单文件挂载,缓存放 named volume。配置必须备份,缓存丢失可以重建。
|
||
|
||
---
|
||
|
||
## 四、从零部署
|
||
|
||
### 1. 前提检查
|
||
|
||
```bash
|
||
docker version
|
||
docker compose version
|
||
ss -ltn | grep ':18080 ' || true
|
||
```
|
||
|
||
本次服务器环境:Docker 28.4.0、Docker Compose v2.39.4,`18080` 原先未占用。
|
||
|
||
### 2. Compose 文件
|
||
|
||
`deploy/searxng/docker-compose.yml`:
|
||
|
||
```yaml
|
||
services:
|
||
searxng:
|
||
# 固定到 2026-09-16 已通过真实搜索验收的镜像。
|
||
image: docker.io/searxng/searxng@sha256:f1aca3f61c7ff4552b8d54c0ab7fb458e907b78f51c503210cf450c900adca2c
|
||
container_name: ai-companion-searxng
|
||
restart: unless-stopped
|
||
ports:
|
||
- "127.0.0.1:18080:8080"
|
||
volumes:
|
||
- ./settings.yml:/etc/searxng/settings.yml:ro
|
||
- searxng-cache:/var/cache/searxng
|
||
environment:
|
||
- SEARXNG_BASE_URL=http://127.0.0.1:18080/
|
||
|
||
volumes:
|
||
searxng-cache:
|
||
```
|
||
|
||
为什么不写 `0.0.0.0:18080:8080`:当前服务没有对公网用户设计认证和限流,只供同机助手调用。绑定 `127.0.0.1` 能从网络边界直接阻止其他机器访问。
|
||
|
||
为什么固定 digest:`latest` 会在下次拉取时静默改变版本,可能带来配置兼容或搜索引擎行为变化。应先在新镜像上完成 API 和真实工具调用验收,再更新 digest。
|
||
|
||
### 3. `settings.yml` 模板
|
||
|
||
```yaml
|
||
use_default_settings:
|
||
engines:
|
||
keep_only:
|
||
- bing
|
||
- bing news
|
||
- 360search
|
||
- sogou
|
||
|
||
general:
|
||
instance_name: AI Companion Search
|
||
debug: false
|
||
|
||
search:
|
||
safe_search: 1
|
||
formats:
|
||
- html
|
||
- json
|
||
|
||
server:
|
||
secret_key: "__SEARXNG_SECRET__"
|
||
bind_address: "0.0.0.0"
|
||
limiter: false
|
||
public_instance: false
|
||
image_proxy: false
|
||
|
||
engines:
|
||
- name: bing
|
||
disabled: false
|
||
- name: bing news
|
||
disabled: false
|
||
- name: 360search
|
||
disabled: false
|
||
- name: sogou
|
||
disabled: false
|
||
```
|
||
|
||
关键解释:
|
||
|
||
- `use_default_settings`:继承官方完整默认配置,只覆盖本项目关心的部分,减少升级维护量;
|
||
- `keep_only`:只加载指定引擎,避免每次等待当前网络必然超时的搜索源;
|
||
- `disabled: false`:必须显式启用。**只写 `keep_only` 不等于启用引擎**,它会保留默认的禁用状态;
|
||
- `formats: [html, json]`:JSON API 必须显式开放。官方文档说明,未启用的输出格式会返回 HTTP 403;
|
||
- `safe_search: 1`:默认使用中等级别安全搜索;
|
||
- `limiter: false`:仅因为宿主机端口只绑定回环地址且是单用户内部调用;如果对外暴露,不能照抄;
|
||
- 容器内部 `bind_address` 为 `0.0.0.0`,是为了让 Docker 端口映射能访问;真正的外部边界由宿主机的 `127.0.0.1:18080` 控制。
|
||
|
||
### 4. 生成实际配置
|
||
|
||
不要把真实 `secret_key` 写进仓库:
|
||
|
||
```bash
|
||
cd ~/ai-companion/deploy/searxng
|
||
|
||
search_secret=$(openssl rand -hex 32)
|
||
sed "s/__SEARXNG_SECRET__/${search_secret}/" \
|
||
settings.yml.example > settings.yml
|
||
chmod 600 settings.yml
|
||
```
|
||
|
||
仓库 `.gitignore` 已包含:
|
||
|
||
```gitignore
|
||
deploy/searxng/settings.yml
|
||
```
|
||
|
||
### 5. 启动
|
||
|
||
```bash
|
||
cd ~/ai-companion/deploy/searxng
|
||
docker compose pull
|
||
docker compose up -d
|
||
docker compose ps
|
||
```
|
||
|
||
预期端口应显示为:
|
||
|
||
```text
|
||
127.0.0.1:18080->8080/tcp
|
||
```
|
||
|
||
首次拉取镜像在本次环境中比较慢,但下载一直有进度;不要因为几十秒没有完成就重复启动多个 `docker pull`。
|
||
|
||
---
|
||
|
||
## 五、验证与使用
|
||
|
||
### 1. 最小健康验证
|
||
|
||
SearXNG 没有必要为本项目额外暴露公开健康接口,直接查询 JSON API:
|
||
|
||
```bash
|
||
curl -GsS --max-time 20 http://127.0.0.1:18080/search \
|
||
--data-urlencode 'q=DeepSeek' \
|
||
--data-urlencode 'format=json' \
|
||
| jq '.results | length'
|
||
```
|
||
|
||
返回大于零的数字才算真正可用。**HTTP 200 或容器处于 Up 状态不代表上游搜索可用。**
|
||
|
||
查看前三条结果:
|
||
|
||
```bash
|
||
curl -GsS --max-time 20 http://127.0.0.1:18080/search \
|
||
--data-urlencode 'q=DeepSeek 最新消息' \
|
||
--data-urlencode 'format=json' \
|
||
--data-urlencode 'language=zh-CN' \
|
||
| jq -r '.results[0:3][] | .title + " | " + .url'
|
||
```
|
||
|
||
官方 API 支持 `/` 和 `/search` 两个端点、GET/POST 两种方法;常用参数包括:
|
||
|
||
| 参数 | 用途 |
|
||
|---|---|
|
||
| `q` | 必填,搜索词 |
|
||
| `format` | `json` / `csv` / `rss`,必须在配置中启用 |
|
||
| `language` | 搜索语言,如 `zh-CN` |
|
||
| `categories` | 搜索分类 |
|
||
| `engines` | 临时指定引擎,逗号分隔 |
|
||
| `pageno` | 页码 |
|
||
| `time_range` | `day` / `month` / `year`,仅对支持的引擎有效 |
|
||
| `safesearch` | `0` / `1` / `2` |
|
||
|
||
指定单一引擎诊断:
|
||
|
||
```bash
|
||
curl -GsS http://127.0.0.1:18080/search \
|
||
--data-urlencode 'q=DeepSeek' \
|
||
--data-urlencode 'format=json' \
|
||
--data-urlencode 'engines=sogou' \
|
||
| jq '.results | length'
|
||
```
|
||
|
||
### 2. 从其他电脑临时打开 Web UI
|
||
|
||
服务没有直接暴露到局域网。需要人工查看时,用 SSH 隧道:
|
||
|
||
```bash
|
||
ssh -L 18080:127.0.0.1:18080 \
|
||
yonglin@cadadmin-server.taild0f91d.ts.net
|
||
```
|
||
|
||
然后在本机浏览器打开:
|
||
|
||
```text
|
||
http://localhost:18080/
|
||
```
|
||
|
||
不要为了偶尔看 UI 就把 Compose 改成 `0.0.0.0:18080:8080`。
|
||
|
||
### 3. Python 调用示例
|
||
|
||
```python
|
||
import aiohttp
|
||
|
||
async def search(query: str):
|
||
timeout = aiohttp.ClientTimeout(total=6)
|
||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||
async with session.get(
|
||
"http://127.0.0.1:18080/search",
|
||
params={
|
||
"q": query,
|
||
"format": "json",
|
||
"language": "zh-CN",
|
||
"safesearch": "1",
|
||
},
|
||
) as response:
|
||
response.raise_for_status()
|
||
return await response.json(content_type=None)
|
||
```
|
||
|
||
生产代码还应限制查询长度、结果数量、摘要长度,并只接受 `http` / `https` URL。本项目的实现见 `ai-companion/app/search.py`。
|
||
|
||
---
|
||
|
||
## 六、接入 AI Companion
|
||
|
||
### 配置
|
||
|
||
服务器 `~/.config/ai_assistant/env`:
|
||
|
||
```bash
|
||
SEARCH_ENABLED=true
|
||
SEARXNG_BASE_URL=http://127.0.0.1:18080
|
||
|
||
# 可选,下面是代码默认值
|
||
SEARCH_TIMEOUT_S=6
|
||
SEARCH_MAX_RESULTS=5
|
||
SEARCH_LANGUAGE=zh-CN
|
||
```
|
||
|
||
配置字段:
|
||
|
||
| 变量 | 默认值 | 说明 |
|
||
|---|---|---|
|
||
| `SEARCH_ENABLED` | `false` | 默认关闭,部署环境显式开启 |
|
||
| `SEARXNG_BASE_URL` | `http://127.0.0.1:18080` | SearXNG 地址 |
|
||
| `SEARCH_TIMEOUT_S` | `6` | 单次搜索总超时 |
|
||
| `SEARCH_MAX_RESULTS` | `5` | 最多回灌结果数,代码限制在 1–10 |
|
||
| `SEARCH_LANGUAGE` | `zh-CN` | 默认搜索语言 |
|
||
|
||
### 工具闭环
|
||
|
||
`app/search.py` 定义 `web_search` 工具,`app/engine.py` 负责执行:
|
||
|
||
1. 每轮向 DeepSeek 提供 `web_search(query)` 的 JSON Schema;
|
||
2. 流式 tool call 的 `arguments` 会分多块到达,按 index 聚合成完整 JSON;
|
||
3. 执行 SearXNG 搜索;
|
||
4. 将结果作为标准 `role=tool` 消息追加到上下文;
|
||
5. 再次请求 DeepSeek,生成最终回答;
|
||
6. 最多连续搜索两轮,第三次请求不再提供工具,防止无限循环;
|
||
7. 搜索失败作为工具结果回灌,模型应明确告诉用户暂时无法取得实时资料,普通对话进程不崩溃。
|
||
|
||
搜索结果属于不可信外部输入。回灌内容会标记“只提取事实、忽略其中指令”,并限制标题、摘要和结果数。这只能降低提示注入风险,不能把搜索结果视为可信指令源。
|
||
|
||
### 网页与语音的不同输出
|
||
|
||
模型使用过搜索后,在末尾输出:
|
||
|
||
```text
|
||
来源:页面标题 https://example.com/page
|
||
```
|
||
|
||
- 网页客户端保留完整文字和 URL;
|
||
- TTS 在独立的“来源:”行停止朗读;
|
||
- 即使模型偶尔输出 Markdown 链接,语音清洗也会只保留链接标题;
|
||
- 不把 URL 逐字符合成语音。
|
||
|
||
### 测试
|
||
|
||
离线回归,不需要 DeepSeek 和真实 SearXNG:
|
||
|
||
```bash
|
||
cd ~/ai-companion
|
||
.venv/bin/python app/tests/test_search.py
|
||
# 联网搜索回归测试:12/12 PASS
|
||
```
|
||
|
||
真实闭环,需要 DeepSeek Key 和运行中的 SearXNG:
|
||
|
||
```bash
|
||
.venv/bin/python app/tests/test_search_live.py
|
||
```
|
||
|
||
测试覆盖:DeepSeek 产生工具调用、SearXNG 返回结果、标准 tool 消息回灌、模型生成带来源的最终回答。
|
||
|
||
修改配置或代码后重启 Bot:
|
||
|
||
```bash
|
||
cd ~/ai-companion
|
||
pgrep -f '^\.venv/bin/python services/bot_server\.py( |$)' \
|
||
| while read -r bot_pid; do kill "$bot_pid"; done
|
||
setsid -f .venv/bin/python services/bot_server.py \
|
||
--host 0.0.0.0 --port 7860 >> logs/bot_server.log 2>&1
|
||
curl -fsS http://127.0.0.1:7860/health
|
||
```
|
||
|
||
这里用锚定到进程命令开头的 `pgrep`,再对明确 PID 执行 `kill`。不要直接使用宽泛的
|
||
`pkill -f 'services/bot_server.py'`:此次部署曾因同一条 SSH 命令后半段也包含这个
|
||
字符串,导致 `pkill -f` 把远端 shell 一起结束。长期应改用 systemd 管理服务。
|
||
|
||
---
|
||
|
||
## 七、本次部署过程与踩坑
|
||
|
||
### 1. 初始状态:容器正常,但搜索结果为零
|
||
|
||
首次部署后:
|
||
|
||
```text
|
||
container: Up
|
||
HTTP API: 200
|
||
results: 0
|
||
```
|
||
|
||
日志显示默认引擎全部超时:
|
||
|
||
```text
|
||
wikipedia: Timeout
|
||
brave: Timeout
|
||
duckduckgo: Timeout
|
||
wikidata: Timeout
|
||
google cse: Timeout
|
||
```
|
||
|
||
结论:只检查容器状态和 HTTP 状态码会产生“服务正常”的假象,必须检查 `results` 数量及实际标题、URL。
|
||
|
||
### 2. 分宿主机与容器测试上游
|
||
|
||
宿主机实测:
|
||
|
||
| 上游 | 结果 |
|
||
|---|---|
|
||
| 百度 | 能连接,但搜索请求跳验证码 |
|
||
| Bing 中国 | HTTP 200,但通过 SearXNG 暂时无结果 |
|
||
| 360 搜索 | HTTP 200,SearXNG 有结果 |
|
||
| 搜狗 | HTTP 200,SearXNG 有结果 |
|
||
| DuckDuckGo HTML | 8 秒超时 |
|
||
|
||
容器内访问百度成功,说明不是 Docker bridge 完全断网,而是特定境外上游不可达或受限。因此没有改成 host 网络,而是调整搜索源。
|
||
|
||
### 3. `keep_only` 不会自动启用引擎
|
||
|
||
第一次只配置:
|
||
|
||
```yaml
|
||
use_default_settings:
|
||
engines:
|
||
keep_only:
|
||
- bing
|
||
- 360search
|
||
- sogou
|
||
```
|
||
|
||
逐个指定 `engines=360search` 或 `engines=sogou` 时有结果,但不指定 `engines` 的聚合查询仍返回 0。
|
||
|
||
原因:`keep_only` 只限制加载范围,仍继承这些引擎在默认配置中的 `disabled` 状态。补上以下配置后聚合查询恢复:
|
||
|
||
```yaml
|
||
engines:
|
||
- name: 360search
|
||
disabled: false
|
||
- name: sogou
|
||
disabled: false
|
||
```
|
||
|
||
最终聚合测试先后返回 8、14、5 条结果;数量会随查询和上游状态变化,判断标准应是“大于零且内容合理”,不能写死固定数量。
|
||
|
||
### 4. JSON API 默认不一定开放
|
||
|
||
SearXNG 的默认输出格式通常只有 HTML。如果没有在 `search.formats` 中加入 `json`:
|
||
|
||
```text
|
||
/search?q=test&format=json → 403 Forbidden
|
||
```
|
||
|
||
这是配置行为,不是路由不存在。
|
||
|
||
### 5. 搜索源会动态失效
|
||
|
||
元搜索无法消除上游限制。常见变化:
|
||
|
||
- 某引擎开始返回验证码;
|
||
- 服务器 IP 被限流;
|
||
- 引擎页面结构变化,SearXNG 解析器暂时失效;
|
||
- 境外域名在当前网络超时;
|
||
- 语言参数或复杂搜索词导致特定引擎零结果。
|
||
|
||
所以必须保留多引擎、超时、日志和失败降级,不能假定“一次配置永久可用”。
|
||
|
||
---
|
||
|
||
## 八、日常运维
|
||
|
||
### 状态和日志
|
||
|
||
```bash
|
||
cd ~/ai-companion/deploy/searxng
|
||
docker compose ps
|
||
docker compose logs --tail=100 searxng
|
||
docker stats --no-stream ai-companion-searxng
|
||
```
|
||
|
||
确认端口没有意外对外暴露:
|
||
|
||
```bash
|
||
docker inspect --format '{{(index (index .HostConfig.PortBindings "8080/tcp") 0).HostIp}}:{{(index (index .HostConfig.PortBindings "8080/tcp") 0).HostPort}}' \
|
||
ai-companion-searxng
|
||
# 必须是 127.0.0.1:18080
|
||
```
|
||
|
||
### 重启与停止
|
||
|
||
```bash
|
||
docker compose restart searxng
|
||
docker compose stop searxng
|
||
docker compose up -d searxng
|
||
```
|
||
|
||
`docker compose down` 会删除容器和 Compose 网络,但默认不会删除 named volume;不要随意加 `-v`。
|
||
|
||
### 升级
|
||
|
||
当前 Compose 固定 digest。升级步骤:
|
||
|
||
```bash
|
||
docker pull docker.io/searxng/searxng:latest
|
||
docker image inspect docker.io/searxng/searxng:latest \
|
||
--format '{{index .RepoDigests 0}}'
|
||
```
|
||
|
||
把得到的 `searxng/searxng@sha256:...` 更新到 Compose,然后:
|
||
|
||
```bash
|
||
docker compose up -d
|
||
|
||
# API 验收
|
||
curl -GsS --max-time 20 http://127.0.0.1:18080/search \
|
||
--data-urlencode 'q=DeepSeek' \
|
||
--data-urlencode 'format=json' \
|
||
| jq '.results | length'
|
||
|
||
# DeepSeek 工具闭环验收
|
||
cd ~/ai-companion
|
||
.venv/bin/python app/tests/test_search_live.py
|
||
```
|
||
|
||
两个测试都通过后再提交新的 digest。官方文档建议升级时检查 Compose 模板和环境变量变化;固定 digest 不意味着永不升级,而是把升级变成显式、可验证的操作。
|
||
|
||
### 备份与恢复
|
||
|
||
需要备份:
|
||
|
||
```text
|
||
deploy/searxng/docker-compose.yml
|
||
deploy/searxng/settings.yml.example
|
||
服务器实际的 deploy/searxng/settings.yml(包含 secret,放安全备份)
|
||
```
|
||
|
||
缓存卷不属于关键数据,可以重新创建。恢复时也可以从模板生成新的随机 secret,不要求保留旧值,因为当前没有外部登录会话依赖它。
|
||
|
||
---
|
||
|
||
## 九、故障排查
|
||
|
||
| 现象 | 可能原因 | 检查与处理 |
|
||
|---|---|---|
|
||
| 连接拒绝 | 容器没启动或端口错误 | `docker compose ps`、`ss -ltn | grep 18080` |
|
||
| HTTP 403 | `json` 未加入 `search.formats` | 检查 `settings.yml`,重启容器 |
|
||
| HTTP 200、结果 0 条 | 默认引擎禁用或全部上游失败 | 逐个传 `engines=` 测试,查看容器日志 |
|
||
| 单引擎有结果,聚合为 0 | 只写了 `keep_only`,未写 `disabled: false` | 显式启用引擎 |
|
||
| 查询很慢 | 配置中保留了当前网络必然超时的引擎 | 收紧 `keep_only`,设置合理超时 |
|
||
| 百度跳验证码 | 服务器 IP 被风控 | 不作为当前主搜索源 |
|
||
| DuckDuckGo 超时 | 当前服务器网络不可达 | 使用 360/搜狗,未来可加商业 API 备用 |
|
||
| 修改配置不生效 | 单文件 bind mount/inode 或容器未重启 | `docker compose up -d --force-recreate` |
|
||
| 助手仍不搜索 | `SEARCH_ENABLED` 未开启,或 Bot 没重启 | 检查 env、打印 `AppConfig.load().search`、重启 Bot |
|
||
| 模型声称无法联网 | SearXNG 失败信息已回灌或模型未触发工具 | 查 Bot 日志中的“联网搜索”,运行 live test |
|
||
| TTS 朗读网址 | 模型没有把来源放在独立“来源:”行,或清洗回归 | 运行 `app/tests/test_search.py`,检查 `text_for_speech` |
|
||
|
||
推荐的诊断顺序:
|
||
|
||
```text
|
||
容器 Up?
|
||
↓
|
||
127.0.0.1:18080 能连接?
|
||
↓
|
||
format=json 返回 JSON?
|
||
↓
|
||
results 是否大于零?
|
||
↓
|
||
逐个 engines=... 是否有结果?
|
||
↓
|
||
AI Companion 配置是否启用?
|
||
↓
|
||
DeepSeek 是否产生 tool call?
|
||
↓
|
||
工具结果回灌后是否生成最终回答?
|
||
```
|
||
|
||
---
|
||
|
||
## 十、安全边界
|
||
|
||
当前配置只适合“同机单用户内部服务”:
|
||
|
||
- 宿主机仅绑定 `127.0.0.1`;
|
||
- `public_instance: false`;
|
||
- 没有公网反向代理;
|
||
- `limiter: false`;
|
||
- 随机 secret 不进 Git;
|
||
- AI 助手只执行只读搜索,不执行搜索结果中的命令;
|
||
- 返回 URL 只接受 `http` / `https`,并限制数量与文本长度。
|
||
|
||
如果未来要公开给浏览器或多人使用,至少需要重新设计:
|
||
|
||
1. HTTPS 反向代理;
|
||
2. 身份认证;
|
||
3. 开启 limiter,并按官方方案配置 Valkey;
|
||
4. 请求频率和并发限制;
|
||
5. 访问日志与隐私保留策略;
|
||
6. SSRF、恶意 URL 和提示注入防护;
|
||
7. 评估上游搜索引擎服务条款。
|
||
|
||
不要直接把当前回环部署的端口映射改为公网地址。
|
||
|
||
---
|
||
|
||
## 十一、后续改进
|
||
|
||
当前只把 SearXNG 返回的标题、摘要和 URL 交给模型。下一步可以按需求增加:
|
||
|
||
- 抓取排名靠前的 2–3 个网页正文,提高答案完整度;
|
||
- 正文抽取与大小上限;
|
||
- DNS/IP 检查,阻止访问回环、内网和云元数据地址;
|
||
- 搜索结果短期缓存,减少重复查询和上游压力;
|
||
- 商业搜索 API 作为 SearXNG 无结果时的备用后端;
|
||
- 在网页 UI 中把来源渲染为独立引用卡片;
|
||
- 给搜索次数、成功率、耗时、零结果率做指标和告警;
|
||
- 用 systemd 或 Compose 管理 Bot,避免手工 `pkill + setsid`。
|
||
|
||
正文抓取不是简单地“对结果 URL 发 GET”:它会引入 SSRF、网页提示注入、超大响应、登录墙、JavaScript 渲染和版权边界,需要单独设计。
|
||
|
||
---
|
||
|
||
## 相关链接
|
||
|
||
- [[P0-Pipecat传输层实验报告]] — AI Companion 的网页、语音和搜索接入记录
|
||
- [[AI桌面陪伴助手方案]] — 总体架构
|
||
- [SearXNG 官方容器部署](https://docs.searxng.org/admin/installation-docker.html)
|
||
- [SearXNG Search API](https://docs.searxng.org/dev/search_api.html)
|
||
- [SearXNG `settings.yml`](https://docs.searxng.org/admin/settings/settings.html)
|
||
|
||
---
|
||
|
||
*最后更新:2026-09-16*
|