argo-search 2.9.7 → 2.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +73 -150
- package/README.es.md +73 -150
- package/README.ja.md +79 -156
- package/README.ko.md +78 -155
- package/README.md +80 -159
- package/SKILL.md +3 -3
- package/assets/readme/hero.svg +2 -2
- package/backends/domain_profiles.json +48 -0
- package/backends/engine_registry.yaml +118 -4
- package/backends/quota_profiles.json +92 -12
- package/backends/source_types_cn.json +27 -1
- package/bin/argo +78 -5
- package/bin/argo.js +33 -2
- package/config.yaml +269 -63
- package/docs/ARGO_INTRO.md +5 -3
- package/docs/ENGINE_CATALOG.md +50 -39
- package/docs/MCP_SETUP.md +3 -1
- package/docs/RELEASE_NOTES_v2.9.8.md +73 -0
- package/docs/RELEASE_NOTES_v2.9.9.md +78 -0
- package/engines/specs/cfets.yaml +41 -0
- package/engines/specs/defillama.yaml +38 -0
- package/engines/specs/firecrawl.yaml +1 -1
- package/engines/specs/fxtwitter.yaml +5 -0
- package/engines/specs/gleif.yaml +55 -0
- package/engines/specs/hkex.yaml +40 -0
- package/engines/specs/keenable_free.yaml +36 -0
- package/engines/specs/shfe.yaml +39 -0
- package/engines/specs/stackexchange.yaml +2 -2
- package/engines/specs/tavily_free.yaml +56 -0
- package/engines/specs/yuanbao.yaml +93 -0
- package/mcp/clients.yaml +32 -0
- package/package.json +7 -2
- package/packages/dsh-plugin/README.md +25 -6
- package/packages/dsh-plugin/dsh/client.js +402 -47
- package/packages/dsh-plugin/dsh/diagnostics.js +97 -0
- package/packages/dsh-plugin/dsh/index.js +135 -1876
- package/packages/dsh-plugin/dsh/native-tools.mjs +7 -1
- package/packages/dsh-plugin/dsh/runner.js +666 -0
- package/packages/dsh-plugin/dsh/wide-research.js +878 -0
- package/packages/dsh-plugin/package.json +3 -3
- package/packages/dsh-plugin/scripts/sync-local.sh +70 -0
- package/packages/dsh-plugin/test/client.test.js +177 -2
- package/packages/dsh-plugin/test/diagnostics.test.js +28 -0
- package/packages/dsh-plugin/test/index.test.js +44 -0
- package/scripts/adaptive.py +126 -43
- package/scripts/article_meta.py +131 -0
- package/scripts/bench_tail_ab.py +281 -0
- package/scripts/bounded_run.py +91 -49
- package/scripts/budget.py +247 -0
- package/scripts/budget_cli.py +93 -0
- package/scripts/build_release_assets.sh +65 -0
- package/scripts/cache.py +60 -72
- package/scripts/ci_drift_gate.py +90 -0
- package/scripts/clock_bridge.py +22 -0
- package/scripts/content_boundary.py +46 -0
- package/scripts/content_security.py +119 -4
- package/scripts/content_signals.py +26 -5
- package/scripts/cross_lingual_trace.py +293 -0
- package/scripts/dispatch_result.py +105 -0
- package/scripts/engine_add.py +602 -0
- package/scripts/engine_dispatch.py +58 -86
- package/scripts/engine_env.py +5 -0
- package/scripts/engine_families.py +6 -0
- package/scripts/engine_policy.py +7 -7
- package/scripts/engines.py +21 -0
- package/scripts/engines_base.py +3 -3
- package/scripts/engines_builders.py +12 -0
- package/scripts/engines_builders_batch10.py +48 -3
- package/scripts/engines_builders_batch14.py +656 -0
- package/scripts/engines_builders_batch15.py +249 -0
- package/scripts/engines_builders_keyless.py +111 -0
- package/scripts/engines_builders_tech.py +0 -1
- package/scripts/evidence.py +31 -3
- package/scripts/evidence_loop.py +58 -22
- package/scripts/fetch_quality.py +13 -0
- package/scripts/fetch_v3.py +28 -3
- package/scripts/gen_routing_snapshot.py +11 -0
- package/scripts/http_client.py +24 -38
- package/scripts/install.ps1 +18 -0
- package/scripts/install.sh +16 -1
- package/scripts/job.py +3 -3
- package/scripts/local_seek.py +41 -5
- package/scripts/macro_countries.py +15 -0
- package/scripts/matrix_search_eval.py +192 -0
- package/scripts/mcp_compact.py +234 -0
- package/scripts/mcp_handlers.py +60 -642
- package/scripts/mcp_launch.sh +10 -8
- package/scripts/mcp_local.py +241 -0
- package/scripts/mcp_server.py +19 -14
- package/scripts/mcp_setup.py +33 -9
- package/scripts/mcp_tools.py +1 -0
- package/scripts/mcp_transport.py +1 -1
- package/scripts/mcp_warm.py +127 -0
- package/scripts/net_proxy.py +27 -0
- package/scripts/output_transparency.py +200 -0
- package/scripts/paid_policy.py +98 -0
- package/scripts/quota.py +158 -13
- package/scripts/recovery.py +7 -3
- package/scripts/recovery_runner.py +150 -0
- package/scripts/replay_eval.py +18 -0
- package/scripts/research.py +37 -21
- package/scripts/research_expand.py +24 -1
- package/scripts/retry_policy.py +76 -0
- package/scripts/route.py +6 -6
- package/scripts/route_cache.py +18 -0
- package/scripts/route_combo.py +26 -5
- package/scripts/route_domains.py +170 -31
- package/scripts/safe_conn.py +52 -0
- package/scripts/search.py +15 -27
- package/scripts/search_attribution.py +100 -3
- package/scripts/search_cli.py +24 -11
- package/scripts/search_entry.py +8 -1
- package/scripts/search_output.py +99 -15
- package/scripts/search_pipeline.py +62 -195
- package/scripts/search_rank.py +6 -0
- package/scripts/search_types.py +107 -9
- package/scripts/semantic_evidence.py +179 -3
- package/scripts/serp_guard.py +138 -7
- package/scripts/serp_kill_log.py +25 -2
- package/scripts/stats_cli.py +13 -1
- package/scripts/tail_completion.py +212 -0
- package/scripts/url_safety.py +126 -3
- package/scripts/usage_log.py +45 -0
- package/scripts/wide_research.py +45 -0
- package/scripts/zhihu_quota.py +139 -0
- package/sub-skills/local-search/bm25.py +27 -3
- package/sub-skills/local-search/cli_runner.py +331 -0
- package/sub-skills/local-search/parsers.py +328 -0
- package/sub-skills/local-search/search_v3.py +31 -674
- package/sub-skills/local-seek/scripts/seek_rank.py +7 -3
- package/assets/readme/settings-keys.png +0 -0
- package/assets/readme/settings-tools.png +0 -0
- package/sub-skills/ego-search/references/browser-runtime.md +0 -43
- package/sub-skills/ego-search/references/install.md +0 -67
- package/sub-skills/ego-search/references/original-ego-upgrade.md +0 -51
- package/sub-skills/ego-search/tests/test_query_normalize.py +0 -68
- package/sub-skills/js-run/tests/fixtures/challenge_like.js +0 -27
- package/sub-skills/js-run/tests/test_jsrun.py +0 -165
- package/sub-skills/local-seek/references/auto-behaviors.md +0 -24
- package/sub-skills/local-seek/references/fd-mdfind-recipes.md +0 -62
- package/sub-skills/local-seek/references/git-integration.md +0 -54
- package/sub-skills/local-seek/references/remote-handoff.md +0 -46
- package/sub-skills/local-seek/references/rg-recipes.md +0 -96
- package/sub-skills/local-seek/references/strategies.md +0 -52
- package/sub-skills/local-seek/references/structural-search.md +0 -59
- package/sub-skills/local-seek/tests/test_content_rank.py +0 -222
- package/sub-skills/local-seek/tests/test_determinism_20261006.py +0 -145
- package/sub-skills/local-seek/tests/test_noise_tier.py +0 -101
- package/sub-skills/local-seek/tests/test_pcre2_probe.py +0 -136
- package/sub-skills/local-seek/tests/test_rank_pinyin.py +0 -77
- package/sub-skills/local-seek/tests/test_regression_20261003.py +0 -169
- package/sub-skills/local-seek/tests/test_regression_20261006.py +0 -146
- package/sub-skills/local-seek/tests/test_seek_degradation.py +0 -111
- package/sub-skills/local-seek/tests/test_seek_fts.py +0 -313
package/scripts/http_client.py
CHANGED
|
@@ -36,11 +36,22 @@ from contextlib import contextmanager
|
|
|
36
36
|
from typing import Any
|
|
37
37
|
|
|
38
38
|
try:
|
|
39
|
-
from url_safety import check_url
|
|
39
|
+
from url_safety import check_url, check_redirect_target
|
|
40
|
+
from url_safety import max_redirects as _max_redirects
|
|
41
|
+
from url_safety import resolve_redirect_chain
|
|
40
42
|
except ImportError: # pragma: no cover
|
|
41
43
|
def check_url(url: str) -> tuple[bool, str]:
|
|
42
44
|
return True, ""
|
|
43
45
|
|
|
46
|
+
def check_redirect_target(url: str, start: float) -> dict | None:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def _max_redirects() -> int:
|
|
50
|
+
return 5
|
|
51
|
+
|
|
52
|
+
def resolve_redirect_chain(url, headers, timeout, limit=None):
|
|
53
|
+
return None
|
|
54
|
+
|
|
44
55
|
|
|
45
56
|
# ─── User-Agent 轮换池 ───────────────────────────────────────────────────────
|
|
46
57
|
|
|
@@ -525,7 +536,7 @@ class HttpClient:
|
|
|
525
536
|
"""实际执行 GET 请求(使用 http.client,不自动解压)。"""
|
|
526
537
|
start = time.time()
|
|
527
538
|
current_url = url
|
|
528
|
-
redirects_left =
|
|
539
|
+
redirects_left = _max_redirects() if follow_redirects else 0
|
|
529
540
|
|
|
530
541
|
while True:
|
|
531
542
|
# 构建请求头
|
|
@@ -593,6 +604,11 @@ class HttpClient:
|
|
|
593
604
|
ch if ord(ch) < 128 else urllib.parse.quote(ch)
|
|
594
605
|
for ch in current_url
|
|
595
606
|
)
|
|
607
|
+
# 每跳重定向目标过 SSRF 校验(与 _safe_follow_redirects 对齐):
|
|
608
|
+
# 旧实现只校验首跳,重定向到内网即可绕过(SSRF)
|
|
609
|
+
blocked = check_redirect_target(current_url, start)
|
|
610
|
+
if blocked:
|
|
611
|
+
return blocked
|
|
596
612
|
redirects_left -= 1
|
|
597
613
|
continue
|
|
598
614
|
|
|
@@ -664,7 +680,7 @@ class HttpClient:
|
|
|
664
680
|
start = time.time()
|
|
665
681
|
headers = _random_headers(extra_headers)
|
|
666
682
|
|
|
667
|
-
final_url =
|
|
683
|
+
final_url = resolve_redirect_chain(url, headers, self.timeout)
|
|
668
684
|
if final_url is None:
|
|
669
685
|
return {"status": 0, "headers": {}, "text": "", "url": url,
|
|
670
686
|
"elapsed_ms": int((time.time() - start) * 1000),
|
|
@@ -868,7 +884,7 @@ class HttpClient:
|
|
|
868
884
|
"""
|
|
869
885
|
start = time.time()
|
|
870
886
|
current_url = url
|
|
871
|
-
redirects_left =
|
|
887
|
+
redirects_left = _max_redirects() if follow_redirects else 0
|
|
872
888
|
while True:
|
|
873
889
|
headers = _random_headers(extra_headers)
|
|
874
890
|
cookie_str = self._cookies.get_cookie_header(current_url)
|
|
@@ -914,6 +930,10 @@ class HttpClient:
|
|
|
914
930
|
if not loc:
|
|
915
931
|
break
|
|
916
932
|
current_url = urllib.parse.urljoin(current_url, loc)
|
|
933
|
+
# 每跳重定向目标过 SSRF 校验(与 _do_get/_safe_follow_redirects 对齐)
|
|
934
|
+
blocked = check_redirect_target(current_url, start)
|
|
935
|
+
if blocked:
|
|
936
|
+
return blocked
|
|
917
937
|
redirects_left -= 1
|
|
918
938
|
continue
|
|
919
939
|
raw_body = resp.read()
|
|
@@ -978,40 +998,6 @@ class HttpClient:
|
|
|
978
998
|
return {"status": 0, "headers": {}, "text": "", "url": url,
|
|
979
999
|
"elapsed_ms": 0, "error": str(e)[:200]}
|
|
980
1000
|
|
|
981
|
-
def _safe_follow_redirects(self, url: str, headers: dict,
|
|
982
|
-
max_redirects: int = 5) -> str | None:
|
|
983
|
-
"""逐跳安全跟随重定向:每跳目标都过 SSRF 校验。
|
|
984
|
-
|
|
985
|
-
返回最终 URL;任一跳被拦截或超过跳数返回 None。
|
|
986
|
-
"""
|
|
987
|
-
current = url
|
|
988
|
-
for _ in range(max_redirects):
|
|
989
|
-
ok, _reason = check_url(current)
|
|
990
|
-
if not ok:
|
|
991
|
-
return None
|
|
992
|
-
parsed = urllib.parse.urlparse(current)
|
|
993
|
-
if parsed.scheme not in ("http", "https"):
|
|
994
|
-
return None
|
|
995
|
-
try:
|
|
996
|
-
from net_proxy import open_connection, request_selector, resolve_proxy
|
|
997
|
-
proxy_url = resolve_proxy(current)
|
|
998
|
-
conn, via_proxy = open_connection(parsed, self.timeout, proxy_url)
|
|
999
|
-
path = parsed.path or "/"
|
|
1000
|
-
if parsed.query:
|
|
1001
|
-
path += "?" + parsed.query
|
|
1002
|
-
conn.request("HEAD", request_selector(parsed, path, via_proxy),
|
|
1003
|
-
headers=headers)
|
|
1004
|
-
resp = conn.getresponse()
|
|
1005
|
-
status = resp.status
|
|
1006
|
-
location = resp.getheader("Location")
|
|
1007
|
-
conn.close()
|
|
1008
|
-
except Exception:
|
|
1009
|
-
return None
|
|
1010
|
-
if status not in (301, 302, 303, 307, 308) or not location:
|
|
1011
|
-
return current
|
|
1012
|
-
current = urllib.parse.urljoin(current, location)
|
|
1013
|
-
return None
|
|
1014
|
-
|
|
1015
1001
|
|
|
1016
1002
|
# ─── 便捷函数 ────────────────────────────────────────────────────────────────
|
|
1017
1003
|
|
package/scripts/install.ps1
CHANGED
|
@@ -77,6 +77,15 @@ if (Test-Path (Join-Path $InstallDir ".git")) {
|
|
|
77
77
|
Write-Host "==> 克隆仓库…"
|
|
78
78
|
New-Item -ItemType Directory -Force -Path (Split-Path $InstallDir) | Out-Null
|
|
79
79
|
git clone --depth 1 --branch $Branch $Repo $InstallDir
|
|
80
|
+
if ($LASTEXITCODE -ne 0 -and $Repo -like "*github.com*") {
|
|
81
|
+
$MirrorRepo = "https://ghfast.top/" + $Repo
|
|
82
|
+
Write-Host "[warn] 直连 GitHub 失败,尝试加速镜像: $MirrorRepo …"
|
|
83
|
+
git clone --depth 1 --branch $Branch $MirrorRepo $InstallDir
|
|
84
|
+
if ($LASTEXITCODE -ne 0) {
|
|
85
|
+
Write-Error "克隆失败。若处于受限网络环境,可尝试手动克隆或设置 ARGO_REPO 环境变量。"
|
|
86
|
+
exit 1
|
|
87
|
+
}
|
|
88
|
+
}
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
# 供应链加固:固定 commit 校验(ARGO_PIN),与 install.sh 对齐
|
|
@@ -94,11 +103,20 @@ if ($Pin) {
|
|
|
94
103
|
if (-not $SkipPip) {
|
|
95
104
|
Write-Host "==> 安装依赖 (PyYAML)…"
|
|
96
105
|
& $script:py -m pip install pyyaml
|
|
106
|
+
if ($LASTEXITCODE -ne 0) {
|
|
107
|
+
& $script:py -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyyaml
|
|
108
|
+
}
|
|
97
109
|
if ($LASTEXITCODE -ne 0) { Write-Host "[warn] pip 安装 PyYAML 失败,可手动: pip install pyyaml" }
|
|
98
110
|
Write-Host "==> 安装可选增强 (curl_cffi: TLS 指纹伪造;rapidfuzz: 去重模糊近重复复核;缺失不影响核心功能)…"
|
|
99
111
|
& $script:py -m pip install curl_cffi
|
|
112
|
+
if ($LASTEXITCODE -ne 0) {
|
|
113
|
+
& $script:py -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple curl_cffi
|
|
114
|
+
}
|
|
100
115
|
if ($LASTEXITCODE -ne 0) { Write-Host "[warn] pip 安装 curl_cffi 失败(可选依赖)" }
|
|
101
116
|
& $script:py -m pip install rapidfuzz
|
|
117
|
+
if ($LASTEXITCODE -ne 0) {
|
|
118
|
+
& $script:py -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple rapidfuzz
|
|
119
|
+
}
|
|
102
120
|
if ($LASTEXITCODE -ne 0) { Write-Host "[warn] pip 安装 rapidfuzz 失败(可选依赖,缺失时去重回退纯 minhash)" }
|
|
103
121
|
}
|
|
104
122
|
|
package/scripts/install.sh
CHANGED
|
@@ -104,7 +104,19 @@ elif [[ -d "$INSTALL_DIR" ]] && [[ -f "$INSTALL_DIR/scripts/search.py" ]]; then
|
|
|
104
104
|
else
|
|
105
105
|
echo "==> 克隆仓库…"
|
|
106
106
|
mkdir -p "$(dirname "$INSTALL_DIR")"
|
|
107
|
-
git clone --depth 1 --branch "$BRANCH" "$REPO" "$INSTALL_DIR"
|
|
107
|
+
if ! git clone --depth 1 --branch "$BRANCH" "$REPO" "$INSTALL_DIR"; then
|
|
108
|
+
if [[ "$REPO" == *"github.com"* ]]; then
|
|
109
|
+
MIRROR_REPO="https://ghfast.top/${REPO}"
|
|
110
|
+
echo "[warn] 从 GitHub 直连克隆失败,尝试加速镜像: $MIRROR_REPO …"
|
|
111
|
+
if ! git clone --depth 1 --branch "$BRANCH" "$MIRROR_REPO" "$INSTALL_DIR"; then
|
|
112
|
+
echo "!! 克隆失败。若处于受限网络环境,可尝试手动克隆或设置 ARGO_REPO 环境变量。" >&2
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
115
|
+
else
|
|
116
|
+
echo "!! 克隆失败,请检查网络连接。" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
108
120
|
fi
|
|
109
121
|
|
|
110
122
|
# 供应链加固:校验 HEAD 与预期 commit 一致(ARGO_PIN 固定)
|
|
@@ -122,13 +134,16 @@ if [[ "$SKIP_PIP" != "1" ]]; then
|
|
|
122
134
|
echo "==> 安装依赖 (PyYAML)…"
|
|
123
135
|
python3 -m pip install --user -q pyyaml 2>/dev/null \
|
|
124
136
|
|| python3 -m pip install -q pyyaml 2>/dev/null \
|
|
137
|
+
|| python3 -m pip install --user -q -i https://pypi.tuna.tsinghua.edu.cn/simple pyyaml 2>/dev/null \
|
|
125
138
|
|| echo "[warn] pip 安装 PyYAML 失败,可手动: pip install pyyaml"
|
|
126
139
|
echo "==> 安装可选增强 (curl_cffi: TLS 指纹伪造;rapidfuzz: 去重模糊近重复复核;缺失不影响核心功能)…"
|
|
127
140
|
python3 -m pip install --user -q curl_cffi 2>/dev/null \
|
|
128
141
|
|| python3 -m pip install -q curl_cffi 2>/dev/null \
|
|
142
|
+
|| python3 -m pip install --user -q -i https://pypi.tuna.tsinghua.edu.cn/simple curl_cffi 2>/dev/null \
|
|
129
143
|
|| echo "[warn] pip 安装 curl_cffi 失败(可选依赖)。需要反爬 TLS 指纹伪造时手动: pip install curl_cffi"
|
|
130
144
|
python3 -m pip install --user -q rapidfuzz 2>/dev/null \
|
|
131
145
|
|| python3 -m pip install -q rapidfuzz 2>/dev/null \
|
|
146
|
+
|| python3 -m pip install --user -q -i https://pypi.tuna.tsinghua.edu.cn/simple rapidfuzz 2>/dev/null \
|
|
132
147
|
|| echo "[warn] pip 安装 rapidfuzz 失败(可选依赖)。缺失时去重回退纯 minhash,需要模糊近重复复核时手动: pip install rapidfuzz"
|
|
133
148
|
fi
|
|
134
149
|
|
package/scripts/job.py
CHANGED
|
@@ -895,13 +895,13 @@ def _is_quota_exhausted(exc: Exception) -> bool:
|
|
|
895
895
|
的模块静默生效。403/429 各有归属:engine_failure.classify 分 auth /
|
|
896
896
|
blocked / rate_limited,健康问题交熔断器自愈,本函数不接手。
|
|
897
897
|
|
|
898
|
-
|
|
899
|
-
|
|
898
|
+
关键词表真源是 cache_guard 的 `_QUOTA_ERROR_KEYWORDS`(配额分类与自适应
|
|
899
|
+
跳过共用),不另起一份。
|
|
900
900
|
真欠费的文案(`You do not have enough money or package quota`)含
|
|
901
901
|
`quota`,仍然照常命中。
|
|
902
902
|
"""
|
|
903
903
|
try:
|
|
904
|
-
from
|
|
904
|
+
from cache_guard import _QUOTA_ERROR_KEYWORDS
|
|
905
905
|
except ImportError:
|
|
906
906
|
return False
|
|
907
907
|
text = str(exc).lower()
|
package/scripts/local_seek.py
CHANGED
|
@@ -151,6 +151,37 @@ def _seek_disk_cache_put(key: str, results: list[dict[str, Any]]) -> None:
|
|
|
151
151
|
pass
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
# 本地代码/工程/配置意图正则模式
|
|
155
|
+
_LOCAL_INTENT_PATTERN = re.compile(
|
|
156
|
+
r"(?i)(?:\b(?:def|class|function|fn|import|export|const|var|struct|enum)\b|"
|
|
157
|
+
r"\.(?:py|js|ts|json|yaml|yml|md|rs|go|c|cpp|h|sh|sql)\b|"
|
|
158
|
+
r"(?:readme|config|package\.json|cargo\.toml|dockerfile|makefile)|"
|
|
159
|
+
r"(?:本工程|本项目|代码库|本地文件|架构实现|函数|方法|源码|接口定义))"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def has_local_code_intent(query: str) -> bool:
|
|
164
|
+
"""是否包含本地工程/代码/配置检索意图。"""
|
|
165
|
+
if not query:
|
|
166
|
+
return False
|
|
167
|
+
return bool(_LOCAL_INTENT_PATTERN.search(query))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def resolve_include_local(include_local: bool | None, mode: str, query: str = "") -> bool:
|
|
171
|
+
"""include-local 三态解析:True/False 显式,None=自动。
|
|
172
|
+
|
|
173
|
+
自适应感知:除 fast/budget 模式外,当查询具有明显的本地代码/文件特征
|
|
174
|
+
且处于非宽泛根项目目录时,自动开启本地命中。
|
|
175
|
+
"""
|
|
176
|
+
if include_local is not None:
|
|
177
|
+
return bool(include_local)
|
|
178
|
+
if mode in ("fast", "budget"):
|
|
179
|
+
return True
|
|
180
|
+
if has_local_code_intent(query) and _local_seek_dir() is not None:
|
|
181
|
+
return True
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
|
|
154
185
|
def _is_broad_local_root(path: str) -> bool:
|
|
155
186
|
"""path 是否是「不能当作本地检索范围」的宽泛根。
|
|
156
187
|
|
|
@@ -200,7 +231,8 @@ def _local_seek_dir() -> str | None:
|
|
|
200
231
|
|
|
201
232
|
|
|
202
233
|
def _run_local_seek(query: str, max_n: int = 5,
|
|
203
|
-
search_dir: str | None = None
|
|
234
|
+
search_dir: str | None = None,
|
|
235
|
+
warn: bool = True) -> list[dict[str, Any]]:
|
|
204
236
|
"""本机文件命中(include-local 用):进程内调 seek.run_query,JSON 并入。
|
|
205
237
|
|
|
206
238
|
fast/budget 模式下由 super_search 自动调用(_resolve_include_local),
|
|
@@ -215,10 +247,14 @@ def _run_local_seek(query: str, max_n: int = 5,
|
|
|
215
247
|
# 静默跳过会让用户以为本地没匹配,而事实是根本没查。返回一个
|
|
216
248
|
# 占位「结果」又会污染 results(它只是个说明,不是命中),所以
|
|
217
249
|
# 说明走 stderr:与 CLI 既有的 include-local 异常提示同一条通道。
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
250
|
+
# 只在**显式开启**时提示(warn=False 是 fast/budget 的自动路径):
|
|
251
|
+
# 自动路径在 home 等宽根下每次 search 都会打印,Agent 会把它当成
|
|
252
|
+
# 输出的一部分,属于噪音而非归因。
|
|
253
|
+
if warn:
|
|
254
|
+
sys.stderr.write(
|
|
255
|
+
" [include-local] 当前目录不适合本地检索(home / 根目录过宽,或位于 argo "
|
|
256
|
+
"自身安装树内——只会命中自身源码),已跳过;"
|
|
257
|
+
"要搜本机文件请 cd 到目标目录后重试,或用 local-seek 的 seek.py --path <目录>\n")
|
|
222
258
|
return []
|
|
223
259
|
|
|
224
260
|
# 缓存:同 query+target 在 TTL 内直接命中,避免重复冷启动 seek.py 子进程。
|
|
@@ -66,6 +66,12 @@ _NON_US_COUNTRIES: dict[str, str] = {
|
|
|
66
66
|
name: code for name, code in WORLDBANK_COUNTRIES.items() if code != "USA"
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
# 美国子集:给「统计局/国内」这类中国语境别名做前置排除。
|
|
70
|
+
# 同一张主表派生,避免「美国 统计局」被当成中国口径。
|
|
71
|
+
_US_COUNTRIES: dict[str, str] = {
|
|
72
|
+
name: code for name, code in WORLDBANK_COUNTRIES.items() if code == "USA"
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
|
|
70
76
|
def is_foreign_macro_query(query: str) -> bool:
|
|
71
77
|
"""查询是否明确指向非美国国家/地区。
|
|
@@ -78,5 +84,14 @@ def is_foreign_macro_query(query: str) -> bool:
|
|
|
78
84
|
英文词——实测 `focus on growth`、`consensus 预测`、`business survey`
|
|
79
85
|
全被判成「外国宏观查询」,FRED 因而被无谓地排除。同一个概念(国家词识别)
|
|
80
86
|
在同一模块里有两套行为不同的实现,正是这类漏判的来源。
|
|
87
|
+
|
|
88
|
+
补充(2026-10-23):「国家统计局」「国内」「我国」是明确的中国语境,
|
|
89
|
+
但不在国家名表里。实测「国家统计局 一季度 GDP」因此没让位,FRED 领队
|
|
90
|
+
返回美国 GDP。这几个词只指向中国,直接判为 foreign(非美国)。
|
|
91
|
+
但「美国 统计局」「美国国内 CPI」同时含美国词,必须先排除美国口径。
|
|
81
92
|
"""
|
|
93
|
+
q = query or ""
|
|
94
|
+
if (not match_country(q, _US_COUNTRIES)
|
|
95
|
+
and any(term in q for term in ("国家统计局", "统计局", "国内", "我国"))):
|
|
96
|
+
return True
|
|
82
97
|
return bool(match_country(query, _NON_US_COUNTRIES))
|
|
@@ -254,6 +254,65 @@ ROUTE_MATRIX: list[dict[str, Any]] = [
|
|
|
254
254
|
"domain": "org_entity", "primary": "wikidata", "scenario": "org"},
|
|
255
255
|
{"id": "R_zh_org2", "q": "清华大学 创办年份", "lang": "zh",
|
|
256
256
|
"domain": "org_entity", "primary": "wikidata", "scenario": "org"},
|
|
257
|
+
# 中文企业总部不是地理问题(2026-10-22 修:geo_places 的方位词 unless 增补
|
|
258
|
+
# 「总部|母公司|子公司|注册地|办公地」后落到本域;此前落 geo_places,用 OSM+维基
|
|
259
|
+
# 只拿到广汽/新凯来/五粮液等无关结果)
|
|
260
|
+
{"id": "R_zh_org_hq", "q": "华为 总部在哪", "lang": "zh",
|
|
261
|
+
"domain": "org_entity", "primary": "wikidata", "scenario": "org"},
|
|
262
|
+
|
|
263
|
+
# ── 企业/工商(company_search)──
|
|
264
|
+
# 2026-10-22 补:此前 ROUTE_MATRIX 无本域用例,导致「中国企业股权/工商信息」
|
|
265
|
+
# 被路由到本域、而该域 combo 只有国际法人库(gleif/opencorporates)+ 百科的
|
|
266
|
+
# 问题长期无人发现——实测「长亭科技 股东 持股比例」返回 5 条同名「XX科技
|
|
267
|
+
# 有限公司」干扰。元宝进 combo 第 1 位后(a5a46ad)才具中文交付能力。
|
|
268
|
+
{"id": "R_zh_company_shareholder", "q": "长亭科技 股东 持股比例", "lang": "zh",
|
|
269
|
+
"domain": "company_search", "primary": "opencorporates", "scenario": "company"},
|
|
270
|
+
{"id": "R_zh_company_regcap", "q": "字节跳动 注册资本 变更", "lang": "zh",
|
|
271
|
+
"domain": "company_search", "primary": "opencorporates", "scenario": "company"},
|
|
272
|
+
# 2026-10-25 补(企业主体查询 MECE 类目):尽调风险(注销/经营异常)、开票
|
|
273
|
+
# (开票信息/税号)、人查公司(担任法定代表人/名下公司)三类此前落
|
|
274
|
+
# org_entity 的 wikidata 组合——wikidata 对中文企业 7 天空结果率 82%,
|
|
275
|
+
# company_search 的元宝才是交付源。config.yaml 两域词表成对改动
|
|
276
|
+
# (org_entity 让位 unless + company_search 收口 match/unless),改一处
|
|
277
|
+
# 必须同步另一处。企业名/人名一律合成名(对外脱敏纪律,v2.9.8 同款)。
|
|
278
|
+
{"id": "R_zh_company_risk", "q": "晨曦合成科技有限公司 注销了吗", "lang": "zh",
|
|
279
|
+
"domain": "company_search", "primary": "opencorporates", "scenario": "company"},
|
|
280
|
+
{"id": "R_zh_company_invoice", "q": "晨曦合成科技有限公司 开票信息 税号", "lang": "zh",
|
|
281
|
+
"domain": "company_search", "primary": "opencorporates", "scenario": "company"},
|
|
282
|
+
{"id": "R_zh_company_person", "q": "李合成 担任法定代表人的公司", "lang": "zh",
|
|
283
|
+
"domain": "company_search", "primary": "opencorporates", "scenario": "company"},
|
|
284
|
+
# 双向锁(v2.9.8 纪律):新词表不得误吸非主体查询——办事指南是 howto、
|
|
285
|
+
# 招聘是求职,都不该进尽调域
|
|
286
|
+
{"id": "R_zh_company_not_howto", "q": "公司注销流程 怎么办", "lang": "zh",
|
|
287
|
+
"domain_not": ["company_search", "org_entity"], "scenario": "company"},
|
|
288
|
+
{"id": "R_zh_company_not_job", "q": "晨曦合成科技有限公司 招聘", "lang": "zh",
|
|
289
|
+
"domain_not": ["company_search"], "scenario": "company"},
|
|
290
|
+
# ── 2026-10-25 六族 MECE 扫描(学术/金融财务/法律/医药)──
|
|
291
|
+
# 全部为真实场景类目,企业名/人名保持合成名(脱敏纪律)。每处 config 改动
|
|
292
|
+
# 都有成对的 unless 让位,反例见 tests/test_domain_rule_schema.py。
|
|
293
|
+
{"id": "R_zh_stock_financials", "q": "贵州茅台 2025 年报 营收", "lang": "zh",
|
|
294
|
+
"domain": "stock_query", "primary": "sina_quote", "scenario": "finance"},
|
|
295
|
+
{"id": "R_zh_stock_valuation", "q": "贵州茅台 市盈率 估值", "lang": "zh",
|
|
296
|
+
"domain": "stock_query", "primary": "sina_quote", "scenario": "finance"},
|
|
297
|
+
# zh 财务词形不得落美股域(sec_companyfacts 只认 CIK,A 股全废)
|
|
298
|
+
{"id": "R_zh_usfund_yields_cn", "q": "宁德时代 毛利率 现金流", "lang": "zh",
|
|
299
|
+
"domain_not": ["us_fundamentals"], "scenario": "finance"},
|
|
300
|
+
# 民事实务(劳动/借贷)此前落 chinese_general,flk_law 才是交付源
|
|
301
|
+
{"id": "R_zh_legal_labor", "q": "劳动合同解除 赔偿 N+1", "lang": "zh",
|
|
302
|
+
"domain": "legal", "primary": "anysearch", "scenario": "legal"},
|
|
303
|
+
{"id": "R_zh_legal_lending", "q": "民间借贷 利率 司法保护上限", "lang": "zh",
|
|
304
|
+
"domain": "legal", "primary": "anysearch", "scenario": "legal"},
|
|
305
|
+
# 药品说明书此前被 code_search 的「用法」子串抢走(GitHub/MDN 答不了药)
|
|
306
|
+
{"id": "R_zh_medical_drug_label", "q": "布洛芬 说明书 用法用量", "lang": "zh",
|
|
307
|
+
"domain": "medical", "primary": "clinicaltrials", "scenario": "medical"},
|
|
308
|
+
# 综述语境不被模型目录抢(models_dev 答不了综述)
|
|
309
|
+
{"id": "R_zh_survey_not_modelcard", "q": "大模型 长上下文 综述", "lang": "zh",
|
|
310
|
+
"domain_not": ["ai_model"], "scenario": "academic"},
|
|
311
|
+
# 论文写作 howto / 年报模板 howto 不进学术域与行情域
|
|
312
|
+
{"id": "R_zh_academic_not_howto", "q": "怎么写论文 查重 降重", "lang": "zh",
|
|
313
|
+
"domain_not": ["academic"], "scenario": "academic"},
|
|
314
|
+
{"id": "R_zh_stock_not_howto", "q": "年报 模板 怎么写", "lang": "zh",
|
|
315
|
+
"domain_not": ["stock_query", "us_fundamentals"], "scenario": "finance"},
|
|
257
316
|
|
|
258
317
|
# ── 媒体/音乐 ──
|
|
259
318
|
{"id": "R_en_media", "q": "Taylor Swift album", "lang": "en",
|
|
@@ -384,6 +443,81 @@ ROUTE_MATRIX: list[dict[str, Any]] = [
|
|
|
384
443
|
"worldbank", "nbs_stats", "un_comtrade", "endoflife",
|
|
385
444
|
"deps_dev", "openalex", "europepmc"],
|
|
386
445
|
"forbid_cn": True, "scenario": "negative"},
|
|
446
|
+
|
|
447
|
+
# ── 域覆盖(2026-10-22):补 53 个此前零用例的域 ────────────────────────
|
|
448
|
+
# 零用例 = 路由回归无人挡。company_search 零用例,「中国企业股权/工商信息」
|
|
449
|
+
# 被路由到本域却无中文能力的问题因此潜伏 8 天;geo_places 的「在哪」劫持
|
|
450
|
+
# 企业总部查询同理(两者都是靠人工撞出来的)。lang 取路由实测 primary_lang,
|
|
451
|
+
# 与 _route_ok 的 lang 断言对齐;primary 取域声明的主引擎。
|
|
452
|
+
{"id": "R_ai_model", "q": "models.dev claude 价格 上下文", "lang": "en", "domain": "ai_model", "primary": "models_dev", "scenario": "tech"},
|
|
453
|
+
{"id": "R_aviation_weather", "q": "北京首都机场 航空气象 METAR", "lang": "zh", "domain": "aviation_weather", "primary": "aviation_weather", "scenario": "weather"},
|
|
454
|
+
{"id": "R_chinese_tech_deep", "q": "中文 编程 算法 架构 深度对比", "lang": "zh", "domain": "chinese_tech_deep", "primary": "byted", "scenario": "tech"},
|
|
455
|
+
{"id": "R_claim_check", "q": "辟谣 传言 事实核查", "lang": "zh", "domain": "claim_check", "primary": "factcheck_org", "scenario": "factcheck"},
|
|
456
|
+
{"id": "R_cn_encyclopedia", "q": "长征 百度百科 词条解释", "lang": "zh", "domain": "cn_encyclopedia", "primary": "baidu_baike", "scenario": "media"},
|
|
457
|
+
{"id": "R_cn_tech_community", "q": "掘金 前端文章 技术博客", "lang": "zh", "domain": "cn_tech_community", "primary": "juejin", "scenario": "tech"},
|
|
458
|
+
{"id": "R_code_search", "q": "github 代码 实现 用法", "lang": "zh", "domain": "code_search", "primary": "github", "scenario": "code"},
|
|
459
|
+
{"id": "R_crypto_search", "q": "比特币 以太坊 加密货币 价格", "lang": "zh", "domain": "crypto_search", "primary": "coingecko", "scenario": "finance"},
|
|
460
|
+
{"id": "R_dataset_search", "q": "科研数据集 开放数据 下载", "lang": "zh", "domain": "dataset_search", "primary": "datacite", "scenario": "academic"},
|
|
461
|
+
{"id": "R_dictionary_search", "q": "英文单词 词典 意思", "lang": "zh", "domain": "dictionary_search", "primary": "free_dictionary", "scenario": "media"},
|
|
462
|
+
{"id": "R_earth_science", "q": "地震 震级 余震 震中", "lang": "zh", "domain": "earth_science", "primary": "usgs", "scenario": "science"},
|
|
463
|
+
{"id": "R_em_news_search", "q": "东财 全球资讯 市场", "lang": "zh", "domain": "em_news_search", "primary": "em_global_news", "scenario": "news"},
|
|
464
|
+
{"id": "R_energy_grid", "q": "电网 发电结构 发电量", "lang": "zh", "domain": "energy_grid", "primary": "energy_charts", "scenario": "science"},
|
|
465
|
+
{"id": "R_entity_search", "q": "维基数据 知识图谱 实体查询", "lang": "zh", "domain": "entity_search", "primary": "wikidata", "scenario": "media"},
|
|
466
|
+
{"id": "R_fact_check", "q": "长江 长度 是多少", "lang": "zh", "domain": "fact_check", "primary": "wikipedia", "scenario": "factcheck"},
|
|
467
|
+
{"id": "R_futures_market", "q": "沪铜 期货 行情", "lang": "zh", "domain": "futures_market", "primary": "shfe", "scenario": "finance"},
|
|
468
|
+
{"id": "R_fx_query", "q": "人民币 汇率 中间价", "lang": "zh", "domain": "fx_query", "primary": "fx_rate", "scenario": "finance"},
|
|
469
|
+
{"id": "R_game_search", "q": "steam 游戏推荐 好玩的游戏", "lang": "zh", "domain": "game_search", "primary": "steam", "scenario": "media"},
|
|
470
|
+
{"id": "R_global_event", "q": "国际事件 地缘政治 局势", "lang": "zh", "domain": "global_event", "primary": "google_news", "scenario": "news"},
|
|
471
|
+
{"id": "R_hk_filings", "q": "港股 港交所 披露易 公告", "lang": "zh", "domain": "hk_filings", "primary": "hkex", "scenario": "finance"},
|
|
472
|
+
{"id": "R_hot_trending", "q": "微博热搜 今日热搜", "lang": "zh", "domain": "hot_trending", "primary": "baidu_hot", "scenario": "community"},
|
|
473
|
+
{"id": "R_image_search", "q": "雪景 壁纸 图片", "lang": "zh", "domain": "image_search", "primary": "openverse", "scenario": "media"},
|
|
474
|
+
{"id": "R_intl_news_flash", "q": "国际新闻 外媒 国际要闻", "lang": "zh", "domain": "intl_news_flash", "primary": "guardian_rss", "scenario": "news"},
|
|
475
|
+
{"id": "R_japan_law", "q": "日本 法令 民法 条文", "lang": "zh", "domain": "japan_law", "primary": "egov_law", "scenario": "legal"},
|
|
476
|
+
{"id": "R_kor_law", "q": "韩国 法令 民法 条文", "lang": "zh", "domain": "kor_law", "primary": "kor_law", "scenario": "legal"},
|
|
477
|
+
{"id": "R_law_text", "q": "法条 法律条文 全文", "lang": "zh", "domain": "law_text", "primary": "flk_law", "scenario": "legal"},
|
|
478
|
+
{"id": "R_legal", "q": "刑法 司法解释 判例", "lang": "zh", "domain": "legal", "primary": "anysearch", "scenario": "legal"},
|
|
479
|
+
{"id": "R_meme_slang", "q": "网络梗 流行语 黑话", "lang": "zh", "domain": "meme_slang", "primary": "itotii", "scenario": "media"},
|
|
480
|
+
{"id": "R_ml_models", "q": "huggingface 预训练模型 开源模型", "lang": "zh", "domain": "ml_models", "primary": "huggingface", "scenario": "tech"},
|
|
481
|
+
{"id": "R_news_realtime", "q": "最新新闻 今天发生 突发", "lang": "zh", "domain": "news_realtime", "primary": "byted", "scenario": "news"},
|
|
482
|
+
{"id": "R_outbreak_health", "q": "疫情 暴发 流行病 通报", "lang": "zh", "domain": "outbreak_health", "primary": "who_don", "scenario": "science"},
|
|
483
|
+
{"id": "R_patent_search", "q": "专利 权利要求 技术方案", "lang": "zh", "domain": "patent_search", "primary": "google_patents", "scenario": "academic"},
|
|
484
|
+
{"id": "R_prediction_market", "q": "预测市场 事件合约", "lang": "zh", "domain": "prediction_market", "primary": "polymarket", "scenario": "finance"},
|
|
485
|
+
{"id": "R_protein_search", "q": "蛋白质 基因 氨基酸序列", "lang": "zh", "domain": "protein_search", "primary": "uniprot", "scenario": "academic"},
|
|
486
|
+
{"id": "R_redskill_search", "q": "小红书 技能 排行榜", "lang": "zh", "domain": "redskill_search", "primary": "redskill", "scenario": "community"},
|
|
487
|
+
{"id": "R_scholar_search", "q": "google scholar 学术论文 期刊", "lang": "zh", "domain": "scholar_search", "primary": "google_scholar", "scenario": "academic"},
|
|
488
|
+
{"id": "R_sec_filings", "q": "SEC EDGAR 招股书 财报申报", "lang": "zh", "domain": "sec_filings", "primary": "sec_edgar", "scenario": "finance"},
|
|
489
|
+
{"id": "R_semantic_discovery", "q": "discover landscape ecosystem emerging", "lang": "en", "domain": "semantic_discovery", "primary": "exa", "scenario": "tech"},
|
|
490
|
+
{"id": "R_shopping", "q": "购物 评测 推荐 踩坑", "lang": "zh", "domain": "shopping", "primary": "zhihu", "scenario": "media"},
|
|
491
|
+
{"id": "R_skill_search", "q": "agent skills 市场 目录", "lang": "en", "domain": "skill_search", "primary": "skillsmp", "scenario": "community"},
|
|
492
|
+
{"id": "R_species_search", "q": "物种 学名 生物分类 拉丁名", "lang": "zh", "domain": "species_search", "primary": "gbif", "scenario": "academic"},
|
|
493
|
+
{"id": "R_stackoverflow_search", "q": "stackoverflow 编程问题 代码问题", "lang": "zh", "domain": "stackoverflow_search", "primary": "stackoverflow", "scenario": "code"},
|
|
494
|
+
{"id": "R_standards", "q": "国家标准 国标 标准号", "lang": "zh", "domain": "standards", "primary": "std_samr", "scenario": "academic"},
|
|
495
|
+
{"id": "R_tech_deep", "q": "deep dive 架构 实践", "lang": "zh", "domain": "tech_deep", "primary": "arxiv", "scenario": "tech"},
|
|
496
|
+
# 2026-10-25 约定更新:zh 词形财务查询统一走 stock_query(sina/eastmoney
|
|
497
|
+
# 双地覆盖,与「XX 股价」分流同构);本域收英文词形与美股语境。代价是
|
|
498
|
+
# zh 美股财务查询损失 sec 结构化精度,换来 A 股企业不再落只认 CIK 的
|
|
499
|
+
# sec_companyfacts 全空——两害取其轻,成对改动注释见 config us_fundamentals。
|
|
500
|
+
# 英文方向的域锁定另见 R_us_fundamentals_en。
|
|
501
|
+
{"id": "R_us_fundamentals", "q": "营收 净利润 财务数据 苹果", "lang": "zh",
|
|
502
|
+
"domain": "stock_query", "primary": "sina_quote", "scenario": "finance"},
|
|
503
|
+
{"id": "R_us_fundamentals_en", "q": "apple annual revenue net income", "lang": "en",
|
|
504
|
+
"domain": "us_fundamentals", "primary": "sec_companyfacts", "scenario": "finance"},
|
|
505
|
+
{"id": "R_us_legal", "q": "supreme court 判例 precedent", "lang": "en", "domain": "us_legal", "primary": "federal_register", "scenario": "legal"},
|
|
506
|
+
{"id": "R_web_archive", "q": "网页快照 历史网页 wayback", "lang": "zh", "domain": "web_archive", "primary": "wikipedia", "scenario": "general"},
|
|
507
|
+
{"id": "R_web_docs", "q": "mozilla developer fetch 属性", "lang": "en", "domain": "web_docs", "primary": "mdn", "scenario": "code"},
|
|
508
|
+
{"id": "R_wechat_search", "q": "微信公众号 文章 搜狗微信", "lang": "zh", "domain": "wechat_search", "primary": "wechat_sogou", "scenario": "community"},
|
|
509
|
+
{"id": "R_world_news", "q": "noticias 国际 新闻", "lang": "zh", "domain": "world_news", "primary": "anysearch", "scenario": "news"},
|
|
510
|
+
{"id": "R_zhihu_content", "q": "知乎 怎么看待 如何评价", "lang": "zh", "domain": "zhihu_content", "primary": "zhihu", "scenario": "media"},
|
|
511
|
+
{"id": "R_zhihu_hot_list", "q": "知乎热榜 知乎热搜", "lang": "zh", "domain": "zhihu_hot_list", "primary": "zhihu_hot", "scenario": "community"},
|
|
512
|
+
{"id": "R_zhihu_user_data", "q": "我的知乎 我的创作 收藏", "lang": "zh", "domain": "zhihu_user_data", "primary": "zhihu_user", "scenario": "community"},
|
|
513
|
+
|
|
514
|
+
# ── 域冲突让位修复(2026-10-22)────────────────────────────────────────
|
|
515
|
+
# 这三个域的触发词此前被更宽泛的域覆盖,无法通过自动路由命中(因此曾登记在
|
|
516
|
+
# test_route_matrix_coverage 的 _NO_CASE_DEBT 里)。在宽泛侧加 unless 让位后
|
|
517
|
+
# 各补一条钉住;宽泛侧的真查询锁在 test_domain_pattern_precision 的既有用例。
|
|
518
|
+
{"id": "R_jin10_flash", "q": "金十 7x24 快讯", "lang": "zh", "domain": "jin10_flash", "primary": "jin10", "scenario": "news"},
|
|
519
|
+
{"id": "R_v2ex_search", "q": "v2ex 技术社区", "lang": "zh", "domain": "v2ex_search", "primary": "v2ex", "scenario": "code"},
|
|
520
|
+
{"id": "R_wenshu_query", "q": "裁判文书 判决书", "lang": "zh", "domain": "wenshu_query", "primary": "wenshu", "scenario": "legal"},
|
|
387
521
|
]
|
|
388
522
|
|
|
389
523
|
|
|
@@ -1049,7 +1183,65 @@ def _summarize(c: Checker) -> dict[str, Any]:
|
|
|
1049
1183
|
}
|
|
1050
1184
|
|
|
1051
1185
|
|
|
1186
|
+
def _reexec_with_yaml() -> bool:
|
|
1187
|
+
"""缺 PyYAML 时改用带依赖的解释器重入;exec 成功则不返回。
|
|
1188
|
+
|
|
1189
|
+
why:config 在无 yaml 时静默回落内置默认引擎集,route 段整片假红
|
|
1190
|
+
(2026-10-25 实测:同机无 yaml 的解释器跑出 94 FAIL、带 yaml 的全绿)。
|
|
1191
|
+
bin/argo 启动器的 _pick_python 会自动挑解释器,但分享场景里用户会直接
|
|
1192
|
+
`python3 scripts/matrix_search_eval.py`——解释器版本不该卡住人。候选序:
|
|
1193
|
+
ARGO_PYTHON(与启动器同一权威覆盖)→ PATH 上的 python3.14/3.13/3.15/
|
|
1194
|
+
3.12 → python3;每个候选真跑一次 `import yaml` 验证(探测约 100ms,
|
|
1195
|
+
评测不是热路径)。已重入过仍缺(_ARGO_MATRIX_REEXEC 标记)则放弃,
|
|
1196
|
+
避免套娃。
|
|
1197
|
+
"""
|
|
1198
|
+
import os
|
|
1199
|
+
import shutil
|
|
1200
|
+
import subprocess
|
|
1201
|
+
|
|
1202
|
+
if os.environ.get("_ARGO_MATRIX_REEXEC") == "1":
|
|
1203
|
+
return False
|
|
1204
|
+
cands = []
|
|
1205
|
+
env_py = os.environ.get("ARGO_PYTHON", "").strip()
|
|
1206
|
+
if env_py:
|
|
1207
|
+
cands.append(env_py)
|
|
1208
|
+
cands += [shutil.which("python3.%d" % m) or "" for m in (14, 13, 15, 12)]
|
|
1209
|
+
cands.append(shutil.which("python3") or "")
|
|
1210
|
+
for cand in cands:
|
|
1211
|
+
if not cand:
|
|
1212
|
+
continue
|
|
1213
|
+
if os.path.realpath(cand) == os.path.realpath(sys.executable):
|
|
1214
|
+
continue # 当前解释器已证明缺 yaml,跳过
|
|
1215
|
+
try:
|
|
1216
|
+
probe = subprocess.run([cand, "-c", "import yaml"],
|
|
1217
|
+
capture_output=True, timeout=30)
|
|
1218
|
+
except Exception:
|
|
1219
|
+
continue
|
|
1220
|
+
if probe.returncode == 0:
|
|
1221
|
+
os.environ["_ARGO_MATRIX_REEXEC"] = "1"
|
|
1222
|
+
print("[matrix] 当前解释器缺 PyYAML,改用 %s 重入" % cand,
|
|
1223
|
+
file=sys.stderr)
|
|
1224
|
+
os.execv(cand, [cand, sys.argv[0], *sys.argv[1:]])
|
|
1225
|
+
return False
|
|
1226
|
+
|
|
1227
|
+
|
|
1052
1228
|
def main() -> int:
|
|
1229
|
+
# PyYAML 缺失时 config 会**静默**回落到内置默认引擎集(垂直域引擎全不在),
|
|
1230
|
+
# route 段整片假红(2026-10-25 实测:无 yaml 的解释器跑出 94 FAIL,带 yaml
|
|
1231
|
+
# 重跑 197 全绿——错误根因是解释器不是路由)。先自愈换解释器重入
|
|
1232
|
+
# (3.15/3.14 等版本差异不再卡用户),换无可换才硬失败退出。
|
|
1233
|
+
try:
|
|
1234
|
+
import yaml # noqa: F401
|
|
1235
|
+
except ImportError as exc:
|
|
1236
|
+
_reexec_with_yaml()
|
|
1237
|
+
print(
|
|
1238
|
+
f"[matrix] PyYAML 不可用({exc})且候选解释器(ARGO_PYTHON 与 PATH "
|
|
1239
|
+
"上的 python3.x)均缺:config 将静默降级到内置默认引擎集,route 段"
|
|
1240
|
+
"会整片假红。请 pip install pyyaml,或设 ARGO_PYTHON 指向带依赖的"
|
|
1241
|
+
"解释器。",
|
|
1242
|
+
file=sys.stderr,
|
|
1243
|
+
)
|
|
1244
|
+
return 2
|
|
1053
1245
|
ap = argparse.ArgumentParser(description="多语言×引擎×场景 矩阵测试")
|
|
1054
1246
|
ap.add_argument("--offline", action="store_true")
|
|
1055
1247
|
ap.add_argument("--live", action="store_true")
|