案例 #1784:SearXNG 可行动错误信息
对齐 写作标准 · 错误语义可参考 case-1828 构造期 fail
PR:#1784 | 5 文件 +187/-4 | 2026-06-24 合入
结论先行
设置页 Test Connection 对联网搜索做探针查询时,若 SearXNG 返回 0 条结果,旧逻辑统一提示 「检查 API Key」 — 对 无 Key 的 SearXNG 完全误导。本 PR 新增 EmptyTestResultsError:SearXNG 解析响应里的 unresponsive_engines,在错误里展示 google (timeout) 等;Bing/Google 等仍走 API Key 提示。
适合谁读:改 web_search 集成、设置页 test connection、#1248 POST 错误码 设计。
链 Ch12 Agentic RAG · case-1774(三层 API)· case-1754(可执行 API 契约)。
1. 问题现象(What)
1.1 运维场景
- 管理员配置 SearXNG 为 Agent 联网搜索后端
- 点击 Test Connection
- HTTP 成功但 results 为空
| 修复前 toast | 修复后(有 unresponsive_engines) |
|---|---|
| 笼统「检查 API Key」 | searxng returned 0 results: unresponsive engines: google (timeout), bing (blocked); check upstream... |
| 同上 | 无 diagnostics 时提示 JSON format / URL / 上游连通 |
1.2 修复前 / 修复后
| Provider | 0 results 时消息 |
|---|---|
| SearXNG | |
| DuckDuckGo | 网络/代理提示(PR 新增分支) |
| Bing/Google 等 | 仍 verify API key(不变) |
2. 定位步骤
rg "Test Connection|test connection|EmptyTest" internal/
rg "unresponsive_engines|UnresponsiveEngines" internal/infrastructure/web_search/
rg "EmptyTestResultsError" internal/
阅读顺序:
web_search/test_errors.go— NEW 消息路由web_search/searxng.go— 存lastUnresponsive+EmptyResultDiagnostics()handler/web_search_provider.go— 调用 test 流程test_errors_test.go— 断言 不含 API key(SearXNG)
3. 根因分析(Why)
3.1 类比
SearXNG 像 总机转接:WeKnora 拨通总机(HTTP 200),但 分机(google/bing)全忙。旧 UI 却提示「你是不是 没办电话卡(API Key)」— 问错问题,运维浪费一小时查 Key。
3.2 数据流
flowchart LR
UI[设置 Test Connection] --> H[web_search_provider handler]
H --> P[SearxngProvider.Search]
P --> SX[SearXNG HTTP JSON]
SX --> R{results 空?}
R -->|是| E[EmptyTestResultsError]
E --> D[EmptyResultDiagnostics<br/>unresponsive_engines]
D --> T[Toast 可行动文案]
3.3 根因
- Test 流程 不区分 provider 类型,0 results → 默认 Key 文案
- SearXNG JSON 里
unresponsive_engines字段被丢弃,诊断信息未 surfaced
4. 解决方案(How)
4.1 接口:emptyResultDiagnostics
type emptyResultDiagnostics interface {
EmptyResultDiagnostics() string
}
SearxngProvider 实现;Search 时:
p.lastUnresponsive = data.UnresponsiveEngines
4.2 EmptyTestResultsError
func EmptyTestResultsError(providerType string, provider any) error {
switch types.WebSearchProviderType(providerType) {
case types.WebSearchProviderTypeSearxng:
if detail := provider.(emptyResultDiagnostics).EmptyResultDiagnostics(); detail != "" {
return fmt.Errorf("searxng returned 0 results: %s", detail)
}
return fmt.Errorf("searxng returned 0 results; verify ... JSON format ...")
default:
return fmt.Errorf("search returned 0 results, please verify your API key ...")
}
}
4.3 formatUnresponsiveEngines
将 [["google","timeout"],["bing","blocked"]] 格式化为 google (timeout), bing (blocked)。
4.4 逐文件表(5 文件)
| 文件 | ± | 说明 |
|---|---|---|
test_errors.go |
+58 | NEW 路由 |
test_errors_test.go |
+74 | SearXNG 无 API key 断言 |
searxng.go |
+14/-2 | lastUnresponsive + Diagnostics |
searxng_test.go |
+38 | Search empty 场景 |
web_search_provider.go |
+3/-2 | 接 EmptyTestResultsError |
4.5 验证
go test ./internal/infrastructure/web_search/... -run 'TestEmptyTestResultsError|TestFormatUnresponsive' -v
Staging:可达 SearXNG 但上游引擎全挂 → toast 含 unresponsive engines;Bing 错 Key → 仍 Key 提示。
5. Review 与合入
PR 四段式
| What | Why | How | Test |
|---|---|---|---|
| 0 results 误报 API Key | 未读 SearXNG diagnostics | EmptyTestResultsError + 接口 | go test + 手动 test connection |
维护者 checklist
- SearXNG 消息 never 含 “API key”?
- 其他 provider 行为未回归?
- JSON decode 失败仍提示 settings.yml?
6. 与 #1248
| 模式 | 1784 | #1248 feedback POST |
|---|---|---|
| 错误分类 | 按 provider type | 按业务:重复点赞 / message 不存在 |
| 用户文案 | 可行动(查 JSON/上游) | 可行动(刷新/联系管理员) |
| 实现 | 小文件 test_errors.go |
可仿 feedback_errors.go |
7. 可复用模式
- 0 results ≠ 同一错误 — 先问 provider 是谁
- optional interface 暴露 diagnostics — 不污染所有 provider
- 单测锁文案 —
strings.Contains(msg, "API key")必须 false(SearXNG)
8. 常见误区
错误认知:HTTP 200 就是 test 成功。
正确:业务 0 results 仍应 fail with reason。
错误认知:所有搜索后端都要 API Key。
正确:SearXNG 自建 无 Key — 错误提示必须分型。
错误认知:改 handler 文案就够。
正确:diagnostics 在 Search 响应解析 时捕获。
错误认知:unresponsive_engines 只给日志。
正确:Test Connection 是 运维 UI,应进 toast。
9. 思考点
- Agent 运行时 Search 失败是否也应带
unresponsive_engines?与 test 路径差异? - 设计 #1248 的
feedback_already_submitted错误 JSON 形状。 - case-1828 SSRF 校验 SearXNG URL — 与 1784 运维提示如何同一文档?
章末自检
- 说清为何 SearXNG 不应提示 API Key
- 写出 go test 命令
- 解释
lastUnresponsive生命周期
延伸阅读
- case-1774 · ch-bridge-1248 · Ch12 · 写作标准