From 05933d168f2f30c234804cfaf29f35df67d611e4 Mon Sep 17 00:00:00 2001 From: ylhan Date: Wed, 16 Sep 2026 11:14:06 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=20SearXNG=20=E7=A7=81?= =?UTF-8?q?=E6=9C=89=E6=90=9C=E7=B4=A2=E6=9C=8D=E5=8A=A1=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../P0-Pipecat传输层实验报告.md | 10 + 自建服务/SearXNG私有搜索服务部署与使用.md | 657 ++++++++++++++++++ 2 files changed, 667 insertions(+) create mode 100644 自建服务/SearXNG私有搜索服务部署与使用.md diff --git a/有趣的项目/AI桌面陪伴助手/P0-Pipecat传输层实验报告.md b/有趣的项目/AI桌面陪伴助手/P0-Pipecat传输层实验报告.md index e8c176b..bcfa915 100644 --- a/有趣的项目/AI桌面陪伴助手/P0-Pipecat传输层实验报告.md +++ b/有趣的项目/AI桌面陪伴助手/P0-Pipecat传输层实验报告.md @@ -100,10 +100,20 @@ metrics 与 interruption,不重写已有的记忆、身份、LLM/TTS 适配。 - 已实现:SmallWebRTC 信令端点 `/api/offer`、prebuilt 网页客户端挂载 `/client/`、 Playground 启动端点 `/start`、`EngineProcessor`(自写能量 VAD + 引擎调用) - 已改用 `PipelineWorker`,保留 RTVI;启动/超时/错误有显式日志 +- 已适配 RTVI `send-text` 产生的 `LLMMessagesAppendFrame`,网页文字输入与 + Conversation 区回复走自有引擎,不依赖框架原生 LLM service;Playground 1.7 + 需补发完整 `bot-transcription` / `bot-output`,token 级事件不足以稳定渲染 - 音频缓冲加入 1 秒预卷、60 秒上限和短噪声丢弃,连接结束释放会话资源 - VAD 是**极简能量判定**,不是最终方案(方案 §5 的说完判定还没选型) - 当前没有唤醒门控:网页连接后每段有效语音都会触发回复。正式唤醒仍在 P2 的 N5095 本地实现,待机时不上传音频;本次按用户决定暂不处理 +- 已接入联网搜索:DeepSeek 通过 Function Calling 请求 `web_search`,执行层调用 + 仅绑定 `127.0.0.1:18080` 的私有 SearXNG,结果作为 `tool` 消息回灌后继续生成; + 网页保留来源 URL,TTS 不朗读来源段 +- 2026-09-16 真实闭环验收通过:模型主动搜索,SearXNG 返回 5 条结果,最终回答带 + 两个来源;服务器网络下 360 搜索、搜狗可用,DuckDuckGo/Brave/Wikipedia 超时 +- SearXNG 的完整选型、部署、API、运维和排错记录见 + [[SearXNG私有搜索服务部署与使用]] **客户端** `client/test_client.py`: diff --git a/自建服务/SearXNG私有搜索服务部署与使用.md b/自建服务/SearXNG私有搜索服务部署与使用.md new file mode 100644 index 0000000..2d0a739 --- /dev/null +++ b/自建服务/SearXNG私有搜索服务部署与使用.md @@ -0,0 +1,657 @@ +# 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*