联网搜索
很多模型自己不会上网搜索,但客户端(Claude Code 等)会在请求里声明联网搜索工具。原样转发时,这类模型要么无视搜索工具、要么直接报错。
网关搜索解决这个问题:当路由目标模型不支持原生搜索时,网关替模型完成搜索,把结果整理成参考资料交给模型回答,再把搜索结果包装成"模型自己搜的"原生形状还给客户端。网关代搜后必定调用上游模型一次——搜索结果(包括厂商自己合成的答案)作为标注清楚的参考资料注入用户消息,由模型基于资料组织回答;网关绝不直接把厂商文本当模型响应返回。客户端零感知。
入口:控制台 → 设置 → 搜索(仅 admin 可见)。
快速开始
- 添加搜索引擎:点击"添加搜索引擎",类型选
serper(首批支持的厂商,serper.dev),粘贴 API Key,保存。 - 连通性测试:列表行内点"测试",网关会发起一次真实查询(短超时),返回结果条数与耗时。
- 设默认引擎(可选):系统配置区选择默认搜索引擎;不设置时按优先级自动选择。
- 之后,凡客户端声明了搜索工具、而路由模型无原生搜索能力的请求,自动走网关搜索。
劫持开关:三态与优先链
每个模型可在模型表单设置"网关搜索"三态开关(写入 extra_config.search.enabled):
| 取值 | 行为 |
|---|---|
| 默认 · 跟随能力 | 模型无原生搜索 → 网关劫持代搜;有原生搜索 → 让位原生路径 |
| 强制开启 | 即使模型有原生搜索,也由网关劫持代搜(搜索工具会被剥离,上游不会双搜) |
| 强制关闭 | 任何情况下都不劫持 |
判定按优先链解析:模型配置 > 上游配置 > 能力声明。上游(upstream)同样可以在 extra_config.search 配置开关,模型级配置整体覆盖上游级配置;两者都未配置时,才回落到元数据能力声明(模型是否原生支持搜索)。
常见疑问:qwen 等模型在元数据中声明了原生搜索能力(如 DashScope
enable_search),默认模式下这类模型走原生路径、不会被网关劫持。若希望统一由网关代搜,把该模型的开关设为"强制开启"。
引擎选择:三级回落
一次劫持使用哪个引擎,按三级选择:
- 模型/上游在
extra_config.search.provider指定的引擎名; - 系统默认引擎(设置 → 搜索 → 默认搜索引擎);
- 优先级最高的启用引擎。
系统配置与调优
设置 → 搜索页下半部分提供全局调优参数(保存即热生效):
| 参数 | 默认 | 含义 |
|---|---|---|
| 每查询页数 | 2 | 每次劫取向厂商取几页结果(每页约 10 条) |
| 单条摘要最大字符 | 500 | 注入给模型的单条摘要截断长度 |
| 注入总字符上限 | 16384 | 参考资料文本总封顶,超出从尾部丢弃 |
| 查询提取最大字符 | 400 | 从用户消息提取的查询长度上限 |
| 单引擎超时 | 10000 ms | 单个引擎的查询超时 |
| 劫持总预算 | 15000 ms | 含降级重试在内的整体时间预算 |
| 并发闸门容量 | 64 | 同时进行的搜索数上限 |
| 闸门排队超时 | 2000 ms | 排队等闸门的时限,超时减载 |
| 最大响应字节 | 512 KB | 厂商响应硬上限,超限断流 |
内存天花板:常驻占用 ≈ 闸门容量 × 每查询页数 × 最大响应字节(默认 64 × 2 × 512KB ≈ 64MB)。三个旋钮联动即可匹配实例内存预算。
降级行为(搜索失败不影响请求)
搜索是尽力而为的旁路能力,任何失败都不阻塞请求,且不剥离客户端声明的搜索工具:
- 引擎超时/HTTP 错误/解析失败/无结果 → 请求体原样转发(web_search 工具保留),响应里带一条降级通知;
- 内存压力高或并发闸门排队超时 → 减载,请求体同样原样转发;
- 未配置任何引擎 → 不劫持,请求按现状转发。
代搜失败时,保留的 web_search 工具会交给上游:有原生搜索能力的路由(如 qwen 的 enable_search)由上游模型继续搜索兜底;无原生能力的路由则按协议转换层既有规则处理(可能被静默丢弃、模型本轮无法搜索——与代搜失败前行为一致)。搜索工具仅在搜索成功、且结果成功注入用户消息后才被剥离,避免"搜索失败却把模型搜索能力一并剥夺"的情况。
降级时客户端视角:代搜失败的轮次响应没有网关搜索块(等价于"模型自己搜或没搜"),回答仍正常返回。运营侧可在请求日志的翻译通知中看到降级原因。
劫持请求的完整数据流
- 网关从用户最后一条消息提取查询;
- 向引擎发起搜索(分页取回标题/链接/摘要,不抓取正文);厂商若返回自己合成的答案文本,一并保留;
- 结果渲染成带编号的参考资料,追加到用户最后一条消息。注入文本开头明确标注"这是网关代搜的搜索结果,回答时引用来源 URL"——让模型清楚这是搜索资料而非用户输入;厂商合成答案作为"搜索厂商摘要"单独一段置于结果列表前,同样标注清楚;
- 搜索成功且注入成功后,网关才剥离客户端声明的 web_search 工具(避免上游重复搜索);注入失败则保留工具、请求体原样转发(见降级行为);
- 上游模型只看到"问题 + 标注清楚的参考资料",正常作答(必定一次调用,网关绝不替模型回答);
- 网关把保存的搜索结果按客户端协议合成原生搜索块拼进响应:
- Anthropic →
server_tool_use+web_search_tool_result内容块; - Chat Completions →
url_citation引用标注; - Responses →
web_search_call输出项。
- Anthropic →
已知代价:上游前缀缓存与必调上游
网关代搜后必定调用上游模型一次——这是不变量。即便搜索厂商返回了现成的合成答案,网关也只把它作为资料注入、由模型基于资料组织回答,绝不直接把厂商文本当模型响应返回。
注入的参考资料只有上游看得见,客户端下一轮请求不会携带它。对无状态协议(Chat Completions / Anthropic),上游前缀缓存的命中范围会收缩到上一个被劫持轮次之前;有状态协议(Responses + store)不受影响。这是"客户端零感知"的固有代价。每轮劫持还会消耗厂商 2 次请求配额(默认 2 页)。
计费口径
劫持搜索不计入用量与计费:响应体 usage.server_tool_use.web_search_requests 只是协议伪装(与真实厂商响应同形),内部用量账本不记录搜索次数。搜索成本是网关运营者向厂商的采购支出,与上游计费体系分离。
