主人原话:「返回五条结果会不会太少了?」。裁定:**上限抬高到 10,且它是真上限** (模型要 50 也只给 10);模型要更少(3)时**尊重模型**;缺省/非法(≤0/非数)⇒ 上限。 - plugins/tools/io/web_search.go:`n := resultLimit(s.maxResults, m)` 一处收口 (原来是 `if count>0 { n = count }` ⇒ 模型的 count 能顶掉配置上限,与 .env.example 写的「条数上限」不符 —— 那是一条真口径缺陷,本次一并修正)。 - internal/config/websearch.go:`WEB_SEARCH_MAX_RESULTS` 代码默认值 5 → 10 (三处口径现已一致:env 默认 10 / io 包 defaultMaxResults 10 / 各家发给上游的 请求区间是另一回事,未动)。 - 生产:`data/config/plugins/web-search.json` 的 `maxResults` 5 → 10 + 一次 saveProvider 触发**热重载**(applied+registered),盘上已生效。 验收(主会话复跑 + 亲自打两个变异): - io 包 / internal/config / cmd/cyrene 相关用例全绿; - 变异 A(拆掉上限、恒取 50)⇒ **4 条用例变红**(含「默认上限 10」「count 非数值 ⇒ 上限」「count=0 ⇒ 上限」「既有 MaxResultsOption」); - 变异 B(默认值退回 5)⇒ internal/config 的默认值断言变红。 🔴 顺带挖出一条真缺陷(**本片只报不修,等派**):`maxResults` **在面板 UI 上 根本改不了** —— ① `search/panel.go` 的字段表里没有这个字段(输入框不显示); ② `saveProvider` 只并 provider/apiKey/baseUrl/enabled;③ `mergeSelectionPatch` 只处理 primary/fallback ⇒ **手工塞进 values 也会被静默丢弃**(「我改了它却没变」 成立)。修它要给面板加一个数字字段 + 三态落 `f.MaxResults`,属**管理面板对外契约 变更**(须同步 Docs/api-client.md),留给主会话。
主人定调「先把原生的做进插件,MCP 路线等后端 MCP 完善后再接」。 - 新包 plugins/tools/io/search/:一 provider 一文件 + 共享 http/提取器 + 归一化 Result;域名过滤/去重/排序**归一化后统一做一次** - 配置 15 键:WEB_SEARCH_PROVIDER(默认 searxng)/_FALLBACK/_MAX_RESULTS + 五家 ×(API_KEY/BASE_URL);WEB_SEARCH_TIMEOUT 语义改为「**每次上游尝试**」(降级链才有意义) - 硬语义:未注册/未配置的主 provider ⇒ **启动期报错**(不许静默回落);off ⇒ 不注册工具;缺 key 的家不进可用清单;降级链**如实标实际应答的 provider**(变异:拿主力冒充 ⇒ 测试红「provider = zhipu, want bocha」) - 密钥绝不进日志/响应(有断言);错误分类(鉴权/限流/额度/网络)+ **空结果 ≠ 成功**(变异暴露过一个死分支⇒下移到 searchWith 修掉) - searxng 真机 e2e 通过(provider=searxng 5 条);**其余五家无 key ⇒ 如实标未实测**;契约无改动(无新端点、工具 schema 不变) - 注册表留好 B 档接入位(tavily/exa/firecrawl/serper/serpapi),并有「未登记⇒启动报错」的防回归断言