
千笔-AIWritePaper · https://www.aiwritepaper.com
挂上 WebSearchTool 或 FileSearchTool,并不等于「Agent 会自己做完可靠检索」。它们是 OpenAI 托管 工具:在服务器侧执行网页或向量库检索,再把命中回灌给模型。启用条件绑在 Responses 模型路径上;结果默认进入对话上下文,不自动等于已抽检的终裁事实。租户向量库挂错、截断丢掉反证、空命中仍自信作答,都会在生产里变成静默事故。
本文在 openai-agents 0.23.1 下核对 SDK 类形状,并用离线假检索模拟回灌与抽检(不打真实托管 API)。伪造 URL/题名漂移、空命中自信答、跨租户向量库、超大片段截断、非 Responses 混用,各留日志与 CSV(_w/hosted-search-smoke/)。

图:上排为 WebSearch / FileSearch 与自建检索分工;中部为启用与回灌路径;底部为五条生产禁区。
目标说明
读完你应能独立完成五件事。
- 说明
WebSearchTool/FileSearchTool的托管含义,以及为何文档写明当前主要支持 OpenAI Responses。 - 为 FileSearch 显式传入本租户
vector_store_ids,并限制max_num_results。 - 在答案落地前做引用抽检:URL/题名必须能回溯到本轮命中集。
- 空命中时拒绝自信结论;截断策略不得静默丢掉关键反证。
- 把 5 条失败样本写进禁区表与 CI。
适用与边界
适合:已在 Agents SDK 里挂托管检索,需要分清「谁执行、结果怎么回灌、何处终裁」;多租户共用同一 OpenAI 项目、多个向量库并存。
不讲:tool 循环与超时调参、as_tool、MCP、guardrails、lifecycle、ModelSettings、RunContext、streaming、ModelProvider、LocalShell、needs_approval、tool_use_behavior、SQLiteSession、AgentsException 族(见既有工程笔记)。CodeInterpreterTool / HostedMCPTool 等同族托管工具只作对照,不展开。
失败含义:smoke 用假命中固定抽检与截断行为;真实托管检索的排序、计费与延迟以平台为准。类名与字段来自本机 openai-agents 0.23.1。
机制:启用、回灌、与自建检索分工
| 工具 | 执行位置 | 关键参数 | 工程含义 |
|---|---|---|---|
WebSearchTool |
OpenAI 托管 | search_context_size、user_location、filters、search_content_types |
name=web_search;网页命中回灌模型 |
FileSearchTool |
OpenAI 托管 | 必填 vector_store_ids;max_num_results(1-50);include_search_results |
name=file_search;只搜你挂上的库 |
自建 @function_tool 检索 |
你的进程 | 自定 schema / 审计日志 | 可强制租户过滤、引用落库、拒绝空答 |
启用条件可以记成三句。第一,Agent 的 tools 列表挂上上述托管工具。第二,模型路径需走支持托管工具的 OpenAI Responses。第三,FileSearch 的向量库 ID 必须在构造时绑死本租户,禁止运行期从用户输入直接拼进 vector_store_ids。
回灌路径:模型发起 web_search / file_search 调用,托管侧执行后,命中作为工具结果回到 Runner,再进入后续模型上下文。include_search_results=True 时,FileSearch 可把检索细节一并纳入输出侧可见结果,便于你做抽检;它仍然不是「已人工核验」标记。
python
from agents import Agent, FileSearchTool, WebSearchTool
agent = Agent(
name="Assistant",
tools=[
WebSearchTool(search_context_size="medium"),
FileSearchTool(
vector_store_ids=["vs_tenant_A"],
max_num_results=3,
include_search_results=True,
),
],
)
与自建检索的分工:托管适合广搜与公司向量库的第一跳;终裁引用、跨库权限、合规审计应落在自建工具或应用层闸门。把托管命中直接写进对外答复,等于把平台检索当公证处。
离线 smoke:5 条通过 + 5 条失败
text
P1 PASS WebSearchTool 托管名与 Responses 专用声明 -> name=web_search context=medium
P2 PASS FileSearchTool 需 vector_store_ids 且可限制条数 -> name=file_search stores=['vs_tenant_A'] max=3 include=True
P3 PASS 托管工具与自建 function_tool 检索分工探针 -> hosted=OpenAI服务器执行 / custom=本地on_invoke;生产须分审计与租户边界
P4 PASS 引用抽检:题名与 URL 均可回溯到命中集 -> issues=-
P5 PASS 空命中策略:禁止自信作答 -> injected_chars=0 refuse=True
F1 FAIL 把托管命中当终裁:伪造 URL/题名漂移未抽检 -> issues=['title_drift:FORGED: 年度系统综述 2025', 'title_drift:真题名(漂移)']
F2 FAIL 空命中仍自信作答 -> hits=0 answer='根据全面检索,可以确认产品已通过全部合规审计。' blind=True
F3 FAIL 跨租户向量库误挂:FileSearch 命中他户文档 -> agent_store=vs_tenant_A hit_store=vs_tenant_B leaked=True title=B租户薪资制度
F4 FAIL 超大片段静默截断丢掉关键反证 -> truncated=True dropped=['填充段2', '关键反证'] critical_lost=True kept_chars=156
F5 FAIL 与非 Responses 后端混用托管检索(文档约束) -> mixed=True constraint=WebSearchTool/FileSearchTool currently only supported with O...
CSV rows: 10
逐条读法。
- P1/P2:SDK 类名与属性可用;WebSearch 默认
search_context_size=medium;FileSearch 必须带vector_store_ids。 - P3:托管在 OpenAI 侧执行,自建在本地
on_invoke,审计与租户边界策略不同。 - P4:答案中的 URL 都能在命中集找到,抽检通过。
- P5:空注入时策略选择拒绝作答,而不是让模型自由发挥。
- F1:伪造题名与漂移题名进入答案,抽检报
title_drift。 - F2:零命中仍输出「全面检索...确认」,典型幻觉话术。
- F3:Agent 绑定
vs_tenant_A,命中却来自vs_tenant_B的薪资文档。 - F4:填充段耗尽预算后截断,
关键反证进入dropped且critical_lost=True。 - F5:在非 Responses 后端混挂托管检索,触碰文档约束(本条记录混用意图,不发起真实错误请求)。
生产禁区表
| 编号 | 禁区 | 实测证据 | 替代做法 |
|---|---|---|---|
| D1 | 把托管命中当终裁事实 | F1 伪造/漂移题名 | 答案发布前引用抽检;高风险句人工复核 |
| D2 | 空命中仍自信作答 | F2 | 命中数为 0 时固定拒答或转人工 |
| D3 | 跨租户 / 跨库串搜 | F3 | 向量库 ID 来自服务端租户映射,禁止请求方直传 |
| D4 | 截断静默丢证据 | F4 | 截断必打日志;关键命中优先保留或分页 |
| D5 | 与非 Responses 能力混用 | F5 | 托管检索仅挂在支持的 Responses 配置上 |
| D6 | 密钥或项目级权限过宽 | (配置审计) | 分项目或分向量库 ACL;最小权限 |
配置对照:四个问题定检索形态
| 问题 | 是 | 否 |
|---|---|---|
| 是否必须走 OpenAI 托管网页/向量检索? | 用 WebSearch/FileSearch | 自建 function_tool 对接自有搜索 |
| 是否多租户? | 每租户独立 vector_store,服务端绑定 | 仍要防 F3 |
| 对外答复是否需可追溯引用? | 强制抽检 + 落库命中快照 | 仅内部草稿可放宽 |
| 上下文是否紧张? | 限 max_num_results / context_size,并监控截断 |
会走上 F4 |
最小复现:断言「截断不得丢掉反证」
python
kept = search.inject(max_chars=budget, stop_on_truncate=True)
assert "关键反证" not in search.dropped_titles or fail_run()
# 生产里改为:截断则升采样关键命中,或拒绝生成最终答案
把「关键」定义为带 must_keep 标签的命中(合规条款、否证、权限边界)。CI 用假检索固定预算,断言标签命中永不进 dropped。
常见误判
- 「FileSearch 已经挂了公司库,就不会串租户」:挂错
vector_store_ids或允许客户端传库 ID 时,F3 立刻出现。 - 「有链接就等于可引用」:F1 的伪造题名同样能带着链接进入答案。
- 「context_size=high 更安全」:更大窗口仍可能截断;安全靠保留策略,不靠只调大。
- 「空结果时让模型凭训练知识补全」:那是把托管检索失败藏进幻觉。
命中快照与抽检字段
对外答复前,建议把本轮托管命中落成一张只读快照,再对答案做字符串级抽检。最小字段如下。
| 字段 | 来源 | 用途 |
|---|---|---|
tool |
web_search / file_search | 分工具审计 |
title |
命中题名 | 防漂移 |
url 或 file_id |
命中定位 | 防伪造链接 |
vector_store_id |
FileSearch | 防跨租户 |
rank / score |
若平台返回 | 截断优先级 |
must_keep |
应用标签 | F4 保留策略 |
抽检规则两条就够硬:答案里出现的每个 URL 必须落在快照;答案里复述的题名若带年份/「系统综述」等强主张词,必须与快照题名逐字一致。F1 的 title_drift 就是第二条红灯。
空命中与截断的产品话术
空命中不要让模型说「根据全面检索」。固定三选一:明确「未检索到可用来源」、改写查询重试一次、转人工。截断时若 must_keep 命中被丢,应阻断最终答案,而不是用剩余填充段生成「看起来完整」的回复。F2 与 F4 在用户侧表现都很像「模型很自信」,根因却是检索层策略缺失。
多租户上线检查清单可以贴在发布页:向量库映射表是否按租户单测;预发环境是否仍指向 vs_demo;日志是否打印 vector_store_id(注意脱敏)。这三项能消掉大半 F3 事故。
验收清单
- 生产 Agent 仅在 Responses 兼容配置下挂 WebSearch/FileSearch。
- FileSearch 的
vector_store_ids来自服务端租户映射。 - 对外答案有引用抽检:URL/题名 ∈ 本轮命中快照。
- 空命中拒答;截断打点且关键命中不丢。
- CI 跑
_w/hosted-search-smoke/smoke.py,P 系列 PASS,F 系列可复现。
自建检索何时必须上
出现下列任一条件,应在托管之外再挂自建检索或网关抽检,而不是只加一个 WebSearch。
- 答案进入客户合同、监管报送或对外知识库。
- 语料含多租户隔离要求,向量库 ACL 无法单独证明。
- 必须保留「谁在何时检索了哪条命中」的审计行。
- 需要与内部权限系统联查(单据号、员工号、病例号)。
这四条与 F1至F3 直接对应:托管负责找得到,自建或网关负责证明找对了、找的是本租户、并且可追溯。
当天 30 分钟落地
- 5 分钟:列出所有挂了 WebSearch/FileSearch 的 Agent,核对模型后端。
- 10 分钟:把向量库 ID 改为服务端映射,禁止请求体直传。
- 5 分钟:加上命中快照落盘与答案 URL 抽检。
- 5 分钟:空命中与截断告警接入值班看板。
- 5 分钟:把 CSV 进仓库,升级 SDK 时复跑。
踩坑
- 演示环境用共享
vs_demo,上线忘记换成租户库。 - 只抽检答案里的域名白名单,不核对题名漂移。
- 为省 token 把命中按长度排序再硬切,短反证被排到后面丢掉。
- 在 Chat Completions 实验分支误留托管工具,排障时以为是「偶发无结果」。
- 把
include_search_results当成已审计标志,跳过人工抽检。
与前序主题的咬合
| 相邻主题 | 关系 | 一句话 |
|---|---|---|
| AgentsException(10-03 A) | 失败出口 | 本篇管检索结果是否可终裁,不管异常分桶 |
| SQLiteSession(10-02 A) | 历史落盘 | 命中快照可另存,勿只靠会话文本 |
| 自建 function_tool 检索 | 分工 | 托管做第一跳,自建做租户与审计 |
把托管检索当「搜索引擎插件」还是「证据管道」,决定你敢不敢把答案交给客户。插件心态会停在能搜到;管道心态会要求快照、抽检、拒答与截断告警四件套齐全。本篇 smoke 的五条失败,对应的都是管道缺段,而不是模型偶发胡言。
上线前用本目录假检索再跑一遍:改 max_chars 应能稳定复现 F4;把 Agent 绑定库改成 vs_tenant_B 应能复现 F3。谁在预发关掉抽检「先看效果」,等于把 F1/F2 的失败样本直接送给客户。值班手册里应写明:检索类事故先查命中快照与 vector_store_id,再查模型温度与提示词。
密钥轮换时同步检查向量库 ACL,避免旧项目密钥仍能读到已迁移租户的库。预发与生产的库 ID 表要分文件,禁止共用一份「临时」配置。
总结
WebSearchTool 与 FileSearchTool 提供的是托管检索与回灌 ,不是公证过的事实库。启用绑在 Responses 路径与正确的向量库 ID 上;生产能不能扛住,取决于引用抽检、空命中策略、租户隔离与截断纪律。把五条失败写进禁区表,升级 SDK 或改检索参数时复跑同一份 smoke。字段名以你安装的 openai-agents 版本为准;若托管工具可选参数有变,先对照官方 Tools 文档与本目录 CSV。
检索抽检清单与禁区表归档可用千笔-AIWritePaper 整理;边界与脚本不依赖产品名。