fix(search): 「上游没给链接」不再被当成「没有结果」(真机 no results 的根因)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

真机现象:她自主思考的 4 次 web-search 全部报
`search: no results (provider=zhipu)`,而同一查询直连上游返回 10 条。

根因:`Finalize` 把「link 为空」当成「不是一条结果」整条丢弃。而智谱
`search_std` 的 `search_result[].link` 在真机上**经常整批是空串**(实测同一
payload 连打 6 次:10 条全空 link,title/content 齐全;换查询也见过 6/10 空)。
⇒ 10 条被整批丢光 ⇒ 工具层如实报「一家都没查到」——**那是谎报**。

为什么面板探针是 ok:探针走 `search.Probe → p.Search`,**不过 Finalize**;
工具走 `Execute → p.Search → Finalize`。同一份上游、同一份解析代码,两条路
给出相反结论 —— 探针对这一类缺陷**结构性失明**,以后不能拿探针通不通推断
工具通不通。

修法:无链接条目**照原样保留**(title/content 对模型仍有用,链接空着就空着,
**绝不编一个出来**);判据改为「link/title/content/snippet **全空**的空壳行才
不算结果」;无链接条目不参与按 link 的去重(没有链接就没有判同身份,宁可重复
也不静默吞)。「空结果 ≠ 成功」**未放宽**:真·空 search_result 仍报 ErrNoResults。

验收(主会话复跑 + 亲自变异):go build ./... + vet 绿;io 与 search 两包绿;
我用 -overlay 把判据退回缺陷形态 ⇒ TestWebSearch_ZhipuLinklessResultsAreNotNoResults
变红,报错串与真机逐字相同。子代理另用真 key 走 in-process 工具路径实测:
三条当时失败的查询现在各回 10 条。

⚠️ 本修复要等下次 build + restart 才真机生效(现在跑的还是老产物)。
⚠️ 其他四家是否也有空 link 形态未验(无 key);修复是通用的。
This commit is contained in:
AskaEth committed 2026-10-07 10:28:36 +08:00
1 parent bc1dee3f56
commit 2c56bc9704
5 files changed
+296 -16

No files matched your search

+6 -1
View File
@@ -170,6 +170,8 @@ func (s *webSearchTool) Execute(ctx context.Context, args any) (any, error) {
return nil, err
}
// 归一化之后**统一做一次**后处理:域名过滤/去重/排序/截断(design/27 §4)。
// ⚠️ 这里判的是「**归一化之后**确实一条都没有」,不是「上游没给链接」—— 后者曾被
// Finalize 当成没有结果(2026-10-07 智谱真机缺陷),现在无链接的条目照原样保留。
results := search.Finalize(raw, search.Options{Limit: n})
if len(results) == 0 {
// 空结果 ≠ 成功(design/27 §1.4):如实报「一家都没查到」,绝不冒充成功。
@@ -271,7 +273,10 @@ func searchUnavailable(err error) bool {
// searchResult 是一条结果的**线格式**(既有字段一字不改;published/score 是本期追加的
// 可选字段 —— design/27 §4:只许追加,不许改名)。
type searchResult struct {
Title string `json:"title"`
Title string `json:"title"`
// URL 是结果链接。⚠️ **可能是空串**:上游(如智谱 `search_std`)真机上经常不给 link,
// 此时如实回空串(`title`/`content` 仍可用),**绝不**编一个 URL 出来。
// 客户端按「空 url = 这条没有可点链接」处理,不要把它当成失败或省略字段。
URL string `json:"url"`
Content string `json:"content"`
Snippet string `json:"snippet"`