Files
Cyrene_v2/plugins/tools/io/search/provider.go
T
AskaEth ce9af42945
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(search): 新增腾讯云·元宝搜索与秘塔搜索两家 provider
接入位表六家扩到八家,端点形状与错误码一字未改(契约 v0.87,仅两处名单)。

- tencent_wsa:POST {base}/SearchPro + Authorization Bearer,零签名。
  两条官方口径决定实现:HTTP 状态码恒 200(判失败只能读 Response.Error.Code);
  Pages 是「JSON 字符串数组」需二次解析。错误码按官方表分类
  (UnauthorizedOperation/AuthFailure.* = 凭据、ResourceUnavailable = 欠费、
  RequestLimitExceeded* = 限流),文案只带 code 不带上游 Message。
- metaso:POST {base}/api/v1/search,响应 schema 仅第三方来源且互相打架,
  实现走宽松解析(数组候选与字段名回退),逐处标「未实测」。
- 零新 env 键:两家只走 $DATA_DIR/config/plugins/web-search.json,
  .env / .env.example 未动;组合根 websearch_providers.go 零功能改动。
- 契约 v0.87:values.provider 合法闭集与 plugin.providers 候选各加两条,
  计数不变(14 + 25 + 40 = 79)。
2026-10-07 13:42:41 +08:00

82 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}