feat(v1.8.0): 稳定性修复 + AI Agent 体验增强
稳定性修复: - 修复 cache.py SQLite 连接泄漏(contextlib.closing 包装) - 修复 fetch.py requests stream=True 连接泄漏(try/finally resp.close()) - RETRYABLE_STATUS 新增 403,激活 UA fallback 切换逻辑 - --cache-stats 移至实例解析前,无需实例即可查询 - classify_error 从错误消息提取 HTTP 状态码,正确分类 E_AUTH/E_RATE_LIMIT - --stream 与 --queries-file 互斥检查,违规报 E_INPUT - batch 退出码语义统一(0=有结果 / 1=全部错误 / 2=全部空结果) AI Agent 体验增强: - 错误码体系完善:E_CONFIG/E_AUTH/E_NETWORK/E_RATE_LIMIT/E_PARSE/E_EMPTY/E_INPUT/E_INTERNAL - recovery_hint 恢复提示字段,AI Agent 可程序化决策恢复策略 - stream 模式新增 error 事件类型(含 error_code + recovery_hint) - 进度事件扩展:instance_try/instance_ok/instance_fail - batch 模式统一 schema(status 字段区分 success/failed) - JSON 输出含 schema_version 字段确保版本兼容 测试与文档: - 测试覆盖:330 -> 352 - SKILL.md / README.md 同步更新
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: searxng-use-cli
|
||||
description: Use when you need to search the web via your OWN SearXNG instance (no public-instance discovery). 3 CLI scripts + a shared common.py module — execute privacy-respecting searches against a user-supplied instance (with multi-instance failover, 5xx/429 retry, auto-fetch) or via SEARXNG_INSTANCE env / config file, fetch/extract readable text or markdown from web pages. Zero-config replacement for proprietary search APIs.
|
||||
version: 1.7.0
|
||||
version: 1.8.0
|
||||
author: Metona Team
|
||||
license: MIT
|
||||
platforms: [linux, macos, windows]
|
||||
@@ -188,23 +188,24 @@ In `--format json` mode, errors are emitted as structured JSON on stdout:
|
||||
{
|
||||
"error": "All 2 instances failed. Last error: connection refused",
|
||||
"error_code": "E_NETWORK",
|
||||
"recovery_hint": "Retry with backoff, or try a different SearXNG instance. Check network connectivity, proxy settings, and instance uptime.",
|
||||
"exit_code": 1,
|
||||
"query": "search term"
|
||||
}
|
||||
```
|
||||
|
||||
AI agents can use `error_code` to programmatically decide recovery strategy:
|
||||
AI agents can use `error_code` to programmatically decide recovery strategy, and `recovery_hint` for a ready-to-use actionable suggestion:
|
||||
|
||||
| Code | Meaning | AI Recovery Strategy |
|
||||
|------|---------|---------------------|
|
||||
| `E_CONFIG` | Configuration error (no instance resolved) | Prompt user to set `-i` / `SEARXNG_INSTANCE` / config file |
|
||||
| `E_AUTH` | Authentication failed (401/403) | Check token/credentials, prompt user to re-authenticate |
|
||||
| `E_NETWORK` | Network error (connection refused, timeout, 5xx, all instances failed) | Retry with backoff, switch instance or proxy |
|
||||
| `E_RATE_LIMIT` | Rate limited (429) | Wait and retry with reduced frequency |
|
||||
| `E_PARSE` | Parse error (JSON/HTML parsing failed) | Check if instance supports JSON, try HTML fallback |
|
||||
| `E_EMPTY` | Empty results (exit code 2) | Adjust query terms or time range |
|
||||
| `E_INPUT` | Input error (bad parameters, file not found) | Fix CLI arguments or file paths |
|
||||
| `E_INTERNAL` | Internal error (unexpected exception) | Report bug with full error message |
|
||||
| Code | Meaning | recovery_hint (abridged) |
|
||||
|------|---------|--------------------------|
|
||||
| `E_CONFIG` | Configuration error (no instance resolved) | Provide `-i`/`SEARXNG_INSTANCE`/config file |
|
||||
| `E_AUTH` | Authentication failed (401/403) | Verify credentials, check token expiry & permissions |
|
||||
| `E_NETWORK` | Network error (connection refused, timeout, 5xx, all instances failed) | Retry with backoff, switch instance, check proxy |
|
||||
| `E_RATE_LIMIT` | Rate limited (429) | Wait and retry, reduce frequency, distribute load |
|
||||
| `E_PARSE` | Parse error (JSON/HTML parsing failed) | Try different instance, switch `--method` |
|
||||
| `E_EMPTY` | Empty results (exit code 2) | Refine query, broaden `--time-range`, add `--categories` |
|
||||
| `E_INPUT` | Input error (bad parameters, file not found) | Check query syntax, flag combinations, file paths |
|
||||
| `E_INTERNAL` | Internal error (unexpected exception) | Re-run with `--verbose`, report bug |
|
||||
|
||||
### JSON Lines Streaming (`--stream`)
|
||||
|
||||
@@ -221,14 +222,19 @@ Output format (each line is a separate JSON object):
|
||||
```
|
||||
{"type": "result", "result": {"title": "...", "url": "...", "content": "..."}}
|
||||
{"type": "result", "result": {"title": "...", "url": "...", "content": "..."}}
|
||||
{"type": "done", "count": 2, "query": "large topic"}
|
||||
{"type": "done", "schema_version": "1.0", "count": 2, "query": "large topic"}
|
||||
```
|
||||
|
||||
- `type: "result"` — one per search result, emitted as soon as available
|
||||
- `type: "done"` — terminal event with total count, always emitted last
|
||||
- Exit code 0 on success, 2 on empty results (done event still emitted)
|
||||
- `type: "done"` — terminal event with total count and `schema_version`, always emitted last
|
||||
- `type: "error"` — emitted when the search fails, includes `error_code` and `recovery_hint`:
|
||||
```
|
||||
{"type": "error", "error": "All 2 instances failed. Last error: HTTP Error 403", "error_code": "E_AUTH", "recovery_hint": "Verify credentials...", "query": "..."}
|
||||
```
|
||||
- Exit code 0 on success, 1 on error, 2 on empty results (done event still emitted)
|
||||
|
||||
Only valid with `--format json` (the default).
|
||||
Only valid with `--format json` (single query mode). Using `--stream` with
|
||||
`--queries-file` or non-json formats raises `E_INPUT` immediately.
|
||||
|
||||
### Progress Events (`--progress`)
|
||||
|
||||
@@ -243,6 +249,9 @@ Event types (each on its own line, JSON Lines format on stderr):
|
||||
|
||||
```jsonl
|
||||
{"event": "start", "query": "research topic", "instances": 2}
|
||||
{"event": "instance_try", "url": "https://instance1.example.com", "attempt": 1}
|
||||
{"event": "instance_ok", "url": "https://instance1.example.com", "latency": 0.342, "results": 10}
|
||||
{"event": "instance_fail", "url": "https://instance2.example.com", "error": "HTTP 503", "error_code": "E_NETWORK"}
|
||||
{"event": "cache_hit", "query": "research topic", "ttl": 30}
|
||||
{"event": "cache_store", "query": "research topic", "ttl": 30}
|
||||
{"event": "fetch_start", "count": 3}
|
||||
@@ -264,10 +273,19 @@ logs are human-readable text.
|
||||
|
||||
### Output JSON Schema
|
||||
|
||||
The default `--format json` output shape (for reference, not enforced):
|
||||
The default `--format json` output includes a `schema_version` field so AI
|
||||
agents can detect breaking changes. Run `--dump-schema` to get the full JSON
|
||||
Schema document programmatically:
|
||||
|
||||
```bash
|
||||
python scripts/search.py --dump-schema
|
||||
```
|
||||
|
||||
Single query output shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"query": "search term",
|
||||
"number_of_results": 10,
|
||||
"results": [
|
||||
@@ -294,13 +312,28 @@ The default `--format json` output shape (for reference, not enforced):
|
||||
"text_length": 12345,
|
||||
"truncated": false,
|
||||
"final_url": "https://example.com/final",
|
||||
"user_agent_used": "searxng-cli/1.7.0"
|
||||
"user_agent_used": "searxng-cli/1.8.0"
|
||||
}
|
||||
],
|
||||
"fetched_source": "json"
|
||||
}
|
||||
```
|
||||
|
||||
Batch mode (`--queries-file`) wraps results in a unified schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"queries": [
|
||||
{"query": "term1", "status": "ok", "results": {...}},
|
||||
{"query": "term2", "status": "error", "error": "...", "error_code": "E_NETWORK"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Each batch entry has a `status` field (`"ok"` or `"error"`). Successful entries
|
||||
contain `results`; failed entries contain `error` and `error_code`.
|
||||
|
||||
Fields marked as optional may be absent. The `fetched` and `fetched_source`
|
||||
fields only appear when `--fetch N` is used.
|
||||
|
||||
@@ -431,7 +464,7 @@ usage: search.py [-h] [--query QUERY] [--instance URL]
|
||||
[--exclude-domain DOMAINS] [--queries-file FILE]
|
||||
[--cache-ttl MINUTES] [--clear-cache] [--cache-stats]
|
||||
[--sort-by {score,date,engine,none}] [--no-dedup]
|
||||
[--config FILE] [--verbose] [--quiet] [--version]
|
||||
[--config FILE] [--dump-schema] [--verbose] [--quiet] [--version]
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
@@ -445,14 +478,14 @@ usage: search.py [-h] [--query QUERY] [--instance URL]
|
||||
8. **Stable auto-fetch:** `--fetch 3` concurrently downloads top 3 result pages with retry, browser-UA fallback, CAPTCHA detection, and **`fetch.py`'s higher-quality text extractor** (the same engine `fetch.py` uses)
|
||||
9. **Health-check mode:** `--verify` probes each instance (reachability / JSON-API support / latency / POST support / engine list / auth status) and prints a report, then exits without searching — use it to validate your instance list
|
||||
10. **Result caching:** `--cache-ttl 30` stores results for 30 min; identical queries within the TTL skip the network entirely. Cache lives at `$SEARXNG_CACHE_DIR` or `~/.cache/searxng-cli/cache.db` (SQLite, WAL mode). `--clear-cache` / `--cache-stats` manage it without searching
|
||||
11. **Batch mode:** `--queries-file FILE` reads one query per line (blank/`#` lines skipped) and runs them in sequence; output is a JSON array (or one block per query in brief/urls). A failed query is recorded but does not abort the batch
|
||||
11. **Batch mode:** `--queries-file FILE` reads one query per line (blank/`#` lines skipped) and runs them in sequence; JSON output is `{"schema_version": "1.0", "queries": [{"query":..., "status": "ok"|"error", ...}]}` (or one block per query in brief/urls). A failed query is recorded but does not abort the batch. Exit codes: 0 if any query returned results, 1 if all errored, 2 if all empty
|
||||
12. **Dedup + sort + domain filter:** After search (and cache), duplicate URLs are collapsed (default; `--no-dedup` disables), results are sorted (`--sort-by`; default: score descending), and then `--include-domain`/`--exclude-domain` filter by domain. Matching is case-insensitive and ignores a leading `www.`; when a domain is in both lists, exclude wins
|
||||
13. **Proxy & auth:** `--proxy URL` routes both search and fetch through a proxy; `--auth-bearer` / `--auth-basic` (plus `*-file` variants, `searxng.toml` `auth_basic`/`auth_bearer` fields, and `SEARXNG_BEARER_TOKEN` / `SEARXNG_BASIC_AUTH` env vars) supply credentials. Priority: CLI flag > file > config file > env var
|
||||
14. **Config defaults:** `searxng.toml` may pre-set most flags (engines, categories, language, safesearch, time_range, method, format, sort_by, timeout, max_retries, proxy, cache_ttl, fetch, fetch_timeout, fetch_retries, max_size, auth_basic, auth_bearer); explicit CLI flags always win
|
||||
15. **Structured errors:** in `--format json` mode, failures print a JSON object `{"error": "...", "exit_code": N, "query": "..."}` to stdout so agents can parse them
|
||||
15. **Structured errors:** in `--format json` mode, failures print a JSON object `{"error": "...", "error_code": "E_*", "recovery_hint": "...", "exit_code": N, "query": "..."}` to stdout so agents can parse them and decide recovery strategy
|
||||
|
||||
**Key options:**
|
||||
- `--query "your search"` — **required unless** `--verify`, `--queries-file`, `--clear-cache`, or `--cache-stats` is used
|
||||
- `--query "your search"` — **required unless** `--verify`, `--queries-file`, `--clear-cache`, `--cache-stats`, or `--dump-schema` is used
|
||||
- `--instance https://searx.example.org` — **required unless** `SEARXNG_INSTANCE` env var or a config file supplies it; comma-separated list enables failover
|
||||
- `--queries-file FILE` — read queries from a file (one per line; blank/`#` skipped) and run them in sequence; overrides `--query`
|
||||
- `--engines google,duckduckgo` — restrict to specific search engines (whitespace around commas is auto-stripped; see default list above)
|
||||
@@ -487,6 +520,7 @@ usage: search.py [-h] [--query QUERY] [--instance URL]
|
||||
- `--cache-ttl MINUTES` — cache results for N minutes (default: `0` = disabled); identical queries within the TTL skip the network
|
||||
- `--clear-cache` — delete all cached entries and exit (no search)
|
||||
- `--cache-stats` — print cache statistics (entries, age, size, path) and exit
|
||||
- `--dump-schema` — print the JSON Schema for `--format json` output and exit; lets AI agents programmatically discover field names and types without parsing prose docs
|
||||
- `--auth-bearer TOKEN` — `Authorization: Bearer` header for private instances
|
||||
- `--auth-bearer-file FILE` — read Bearer token from a file (first non-empty, non-`#` line); also honors `SEARXNG_BEARER_TOKEN` env var
|
||||
- `--auth-basic USER:PASS` — `Authorization: Basic` header (auto base64-encoded)
|
||||
|
||||
Reference in New Issue
Block a user