Appearance
第 18 章 外部上下文:搜索与网页
本章要解决的问题
第 17 章解决了外部能力怎样通过 MCP、Skill 或插件接入,但“能接入”不等于“任何外部内容都可以直接交给模型”。搜索和网页是最常见的外部信息入口,也是把上下文、权限和网络风险放到一起的具体例子。
第 6、11~13 章已经处理了本地上下文:怎样组装、怎样按需加载、怎样从代码库检索,以及会话过长时怎样压缩。但本地代码不一定包含框架的最新文档、依赖的 changelog 或报错对应的 issue。
本章讨论 web_search 和 web_fetch:怎样返回可用结果,怎样限制 SSRF 和超长内容,以及为什么网页只能作为不可信数据进入上下文。
18.1 问题:搜索和读网页为什么不能合成一个工具
web_search(搜索) 返回的应该是结构化的候选列表——标题、URL、摘要片段,而不是把网页全文拼成一坨。让模型先浏览候选,再决定深入读哪一条;结果数设上限(超出就标记截断,提示模型细化查询)。
web_fetch(读网页) 则根据 URL 主动发起网络请求,取回全文并转成 markdown。它同时扩大了三种风险:目标可能是内网地址,响应可能大到撑满上下文,页面还可能藏有诱导 Agent 操作的指令。所以两类工具需要不同的返回契约和权限策略。
18.2 社区思路:开放网页读取前,先补齐三道边界
网络边界: 读网页工具可以被诱导访问任意 URL,包括回环、内网和云服务元数据地址。这就是 SSRF(Server-Side Request Forgery,服务端请求伪造):Agent 借用宿主机的网络权限读取用户本来无意暴露的信息。自建时至少要在 DNS 解析后检查地址,每次重定向都重新检查,并用域名白名单限制可访问范围。只检查 URL 字符串不够,因为域名解析和重定向都可能改变最终目标。
内容边界: 外部内容比命令输出更不可控。搜索只返回有上限的候选列表;网页全文则要限制响应体大小和进入上下文的长度,必要时把剩余内容写入文件,同时明确告诉模型“结果被截断”。被截断的结果不应当作全文写入缓存,否则下次命中时会把不完整内容误当成完整证据。
执行边界: 网页可以藏有“忽略之前指示”之类的提示注入。来源标记和角色分离能降低模型误信的概率,但不是强制安全边界。真正的限制仍在第 9 章的沙箱、网络出口和人工审批:私有数据、不可信内容和对外通信三者同时存在时,注入才能形成完整攻击路径。
搜索和读网页通常不直接改文件,比 Shell 更适合并行和短期缓存;但只有查询彼此独立,且不会超过时效、权限、费用和服务限流时,才应该并行。“只读”不等于“无限制”。
18.3 各家怎样划定能力边界
搜索方面,codex、grok-build 和 dsh 都选择服务端托管:codex 把 web_search 作为 Responses API 的服务端工具放进请求(默认 Cached 模式优先用缓存结果,不保证实时),也可经独立的 web.run 扩展请求搜索后端;grok-build 使用 xAI 的服务端搜索;dsh 使用 DeepSeek 的服务端搜索,结果最多 8 条。查询内容会发给搜索服务,来源、费用和权限仍要自己处理。
网页读取上,四家的选择分开了:
- codex 不在本地抓取网页。 外部内容带来源标记;看过网页的回合不写入长期记忆,避免把注入内容保存成“经验”。
- grok-build 在本地实现了较完整的网页读取保护。
web_fetch校验 URL、强制 HTTPS,在 DNS 解析后拒绝回环、内网和云服务元数据地址;每次重定向都重新检查,跨域时不自动跟随。HTML 转 Markdown 前移除导航、广告和脚本;响应体上限是 10MB,超出上下文预算的部分写入文件。被截断的结果不写缓存,内存缓存时长为 15 分钟。默认白名单包含约 90 个文档站,两个工具默认都关闭。 - dsh 的网页读取默认关闭。
web_fetch有实现,但 preset 默认不加载。配置注释指出 fetch provider 没有 SSRF 防护,而目标 URL 由模型选择,因此当前不应默认开放。 - pi 核心不内置搜索或网页读取。 需要外部资料时,可由扩展注册工具,或在允许网络的外部沙箱中运行命令。扩展拥有与宿主相同的系统能力,因此“核心不内置”不自动等于安全;SSRF、出口限制、内容预算、来源标记和项目信任都要由扩展处理。
这些选择对应不同的攻击面:要么不在本地提供 fetch,要么默认关闭,要么把 SSRF、输出和缓存边界补齐后再开放。抽查的内置网页路径没有形成统一的“不可信数据”输出协议,所以输出格式仍不能当作安全边界。
【对比小结】
| codex | grok-build | dsh | pi | |
|---|---|---|---|---|
| 搜索 | 服务端托管/扩展 | xAI 服务端搜索 | DeepSeek 服务端搜索 | 核心不内置 |
| 读网页 | 不提供本地 fetch | SSRF 检查+白名单+转 markdown | 有实现,默认关 | 核心不内置 |
| 注入对策 | 外部内容不写入长期记忆 | 剥离脚本,无统一来源协议 | 无统一来源协议 | 由扩展负责 |
| 默认状态 | 按模式配置 | 双工具默认关闭 | 搜索开、fetch 关 | 无网络工具 |
核心取舍是便利与攻击面。 搜索的风险通常小于直接读取任意网页,但查询内容仍会发送给搜索服务,也要处理来源、费用和权限。自建时可以先接入受控搜索;网页读取应等 SSRF 检查、域名策略和输出限制完成后再开放。
来源标记和提示可以降低模型误信网页内容的概率,却不能强制阻止文件、命令和网络操作——这道边界由第 9 章的沙箱和审批把守。
到这里,第 17 章的“外部能力”已经有了一个具体例子:能力要有接入协议,外部内容要有信任和输出边界。但外部服务会超时、限流和中断,本地长任务也会跨压缩和重启。下一章把这些失败收进同一套可恢复、可观测的生产运行规则。