package search import ( "context" "net/http" ) // Provider 是一种搜索后端的**窄接口**(design/27 §4)。实现必须并发安全: // 工具循环可能从多个 goroutine 调它。 type Provider interface { // Name 返回该 provider 的配置名(即 WEB_SEARCH_PROVIDER / WEB_SEARCH_FALLBACK 里的取值)。 Name() string // Search 执行一次检索,返回**已归一化**的结果(可能为空)。 // // 返回空切片(len==0)**不是错误**:它表示「上游正常应答但什么都没查到」, // 由调用方按「空结果 ≠ 成功」处置(见 ErrNoResults 的注释)。 Search(ctx context.Context, q Query) ([]Result, error) } // Query 是一次检索的输入。工具 schema 仍是 `query`/`count`(design/27 §4:不破坏契约)。 // // 域名过滤**不在这里**:按 design/27 §4,域名过滤/去重/排序统一在归一化之后做一次 // (见 Finalize 的 Options),各家 provider 一律不许自己过滤。 type Query struct { // Text 是检索词。 Text string // Count 是调用方期望的结果条数(>0 才有意义)。provider 支持就透传, // 不支持(如阿里云 IQS 默认 10 条不可调)就忽略 —— 本地截断仍然生效。 Count int } // Result 是所有 provider **统一归一化**后的结果(design/27 §4)。 type Result struct { Link string Title string Snippet string Content string Published string Score float64 } // 本期内置的 provider 名(对外配置值 WEB_SEARCH_PROVIDER 的合法取值之一)。 const ( // NameSearXNG 是自建 SearXNG(A 档、零 key、本地兜底)。 NameSearXNG = "searxng" // NameZhipu 是智谱 BigModel 联网搜索(design/27 §2.1 国内首选)。 NameZhipu = "zhipu" // NameAliyunIQS 是阿里云信息查询服务 IQS(HTTP 免签名免 SDK)。 NameAliyunIQS = "aliyun_iqs" // NameBocha 是博查 Bocha(中文优先)。 NameBocha = "bocha" // NameVolcengine 是火山引擎·豆包搜索 SearchInfinity。 NameVolcengine = "volcengine" // NameBaiduQianfan 是百度千帆 AI 搜索。 NameBaiduQianfan = "baidu_qianfan" // NameTencentWSA 是腾讯云·元宝搜索(联网搜索 API / WSA,服务 API KEY 方式)。 NameTencentWSA = "tencent_wsa" // NameMetaso 是秘塔搜索 Metaso(开放平台 /api/v1/search)。 NameMetaso = "metaso" ) // Config 是构造一家 provider 所需的一切接线(密钥/地址/客户端/响应上限/URL 守卫)。 // // 它由组合根(plugins/tools/io 的 searchWires)填好 —— provider 自己不读配置、 // 不读环境变量、不开新的 HTTP 客户端(单二进制、零新依赖:design/27 §1.5)。 type Config struct { // APIKey 是上游凭据。**绝不进日志/响应/提示词**。 APIKey string // BaseURL 是上游基地址(不含各家自己的路径),可被各自的环境变量覆盖。 BaseURL string // Client 是这一套工具共用的 HTTP 客户端(已带 SSRF 重定向守卫与超时)。 Client *http.Client // MaxBytes 是单次响应体读取上限(字节);≤0 用包内默认。 MaxBytes int64 // Guard 是**组合根注入的 SSRF 守卫**:发请求前对最终地址做一次判定 // (scheme / host / 解析结果)。它复用 plugins/tools/io 的既有守卫与本工具自己 // 那份白名单,**不改** http / web-fetch 共用的白名单。 // // 为 nil 时不做判定(search 包的夹具测试直接用 httptest 客户端)。 Guard func(ctx context.Context, rawURL string) error }