OpenCode v2 多源上下文实战:Websearch、References、Attachments 与缓存预热
这是《OpenCode v2 实战:从零搭建企业级 AI 编程助手》专题的第 18 篇。前一篇解决了 Web UI、Pair、SSH 隧道与 OpenTunnel 的远程接入问题;这一篇继续回答更关键的问题:Agent 在远程工作时,怎样安全地获得最新网页、外部仓库、本地文件、图片和 PDF,又怎样避免把"能读到更多"变成"上下文更混乱、成本更高、结果不可复现"。
OpenCode v2 已经不只是读取当前仓库的代码。它可以调用 Websearch 获取实时信息,可以用 References 为项目挂载本地目录或远程 Git 仓库,可以把文本、目录、图片与 PDF 作为 Attachments 送入会话,还可以通过 Warming 延续模型提供方的短期缓存状态。
这些能力看起来都在"补充上下文",但工程语义完全不同:网页是外部且会变化的证据,Reference 是可命名、可复用的资料根,Attachment 是某次请求的输入快照,Warming 则是一次真实的模型请求。把四者混用,常见后果是来源过期、权限越界、提示注入、Token 激增和审计证据缺失。
本文给出一套可直接落地的多源上下文方案:先判断信息类型,再选择入口;先收集证据,再执行改动;对每一种来源建立权限、版本、大小、刷新和引用规则;最后用结构化结果与指标证明任务确实完成。
一、先建立多源上下文的统一模型
1. 四种能力解决四类问题
可以把一次 OpenCode 任务拆成四层:
text
实时事实层:Websearch
稳定资料层:References
本次输入层:Attachments
短期缓存层:Warming
↓
Agent 推理与工具调用
↓
代码变更、报告、测试与审计证据
Websearch 回答"现在是什么";References 回答"团队认可的资料在哪里";Attachments 回答"用户这次具体给了什么";Warming 回答"暂停几分钟后是否值得继续维持提供方缓存"。它们不是四个同义的上传按钮,而是生命周期不同的输入通道。
2. 用生命周期决定入口
| 信息类型 | 推荐入口 | 生命周期 | 典型风险 |
|---|---|---|---|
| 最新版本、公告、漏洞、价格 | Websearch | 分钟到天 | 过期、伪造站点、提示注入 |
| 团队文档、共享库、样例仓库 | References | 天到月 | 分支漂移、缓存刷新、越权读取 |
| 日志、截图、PDF、单次需求文件 | Attachments | 单次请求或会话 | 格式不支持、大小超限、敏感信息 |
| 模型侧短期 Prompt Cache | Warming | 分钟级 | 额外 Token、限流、无收益调用 |
选择入口时先问三个问题:它会不会变化?是否需要跨会话复用?是否只属于本次请求?如果仍然模糊,优先使用生命周期更短、权限更小的方式。
3. 上下文不是越多越好
text
有效上下文 = 与任务直接相关的证据
- 重复内容
- 过期内容
- 未验证的外部指令
- 无法追溯来源的摘要
Agent 的窗口再大,也不应该把整个文档库、所有搜索结果和几十张截图一次性塞进去。正确做法是先用目录、标题、文件名或搜索摘要缩小范围,再读取必要片段,并把来源与时间写入结果。
二、Websearch:实时信息也要有证据边界
1. OpenCode v2 的搜索工作流
当用户要求最新信息时,OpenCode 可以搜索网页、读取结果并在回答中给出来源链接。首次搜索会要求选择 Provider,后续会话记住选择。
text
用户问题
→ 判断是否需要实时信息
→ websearch 权限匹配
→ 搜索 Provider 返回候选结果
→ 读取一手来源
→ 交叉验证时间与结论
→ 输出带链接的结论
官方文档当前列出 Exa、Firecrawl、Parallel、Tavily 和 TinyFish 五种 Provider。它们的可用性、计费、索引覆盖和限流策略不同,因此生产环境不应把"搜索成功"写死为某一家服务必然可用。
2. 显式选择 Provider
json
{
"$schema": "https://opencode.ai/config.json",
"websearch": {
"provider": "tavily"
}
}
固定 Provider 适合希望成本、合规和出网路径可预测的团队。密钥应通过进程环境或受控密钥系统注入,不要写入仓库配置:
bash
TAVILY_API_KEY="${SEARCH_API_KEY}" opencode
示例只展示变量名,不包含真实密钥。CI、共享开发机和远程服务还应限制谁能读取该进程环境。
3. 随机选择与 429 冷却
json
{
"$schema": "https://opencode.ai/config.json",
"websearch": {
"provider": "random"
}
}
random 会从可用 Provider 中选择一个,并在当前 session 中继续使用。当服务返回 HTTP 429 时,OpenCode 会根据 Retry-After 进入冷却;若响应没有有效等待时间,则按 60 秒处理,并尝试其他可用 Provider。所有 Provider 都在冷却时,搜索会立即失败,而不是无限等待。
text
Exa 返回 429
→ Exa 进入冷却
→ 当前搜索换用其他可用 Provider
→ 全部冷却则明确失败
这是一种可用性策略,不是事实校验策略。换 Provider 只解决"能不能搜",不能证明"搜到的内容是真的"。
三、给 Websearch 加权限、来源与时间门禁
1. 默认询问,而不是默认放行
json
{
"$schema": "https://opencode.ai/config.json",
"permissions": [
{
"action": "websearch",
"resource": "*",
"effect": "ask"
}
]
}
Websearch 使用 websearch 权限动作,资源是实际搜索词。团队可以允许公开技术资料搜索,同时对客户名、工单号、内部项目名和疑似个人信息做拒绝或人工确认。
2. 把搜索词当作外发数据
text
可以搜索:公开产品名 + 公开版本号 + 官方文档
谨慎搜索:未公开项目代号、客户简称、内部报错全文
禁止搜索:Token、Cookie、手机号、订单明细、内部地址
很多泄露并不是上传了文件,而是把日志中的敏感字段原样复制成搜索词。搜索前应做脱敏,将内部标识替换为通用组件名、错误码和公开版本。
3. 一手来源优先
json
{
"claim": "某配置字段在当前版本可用",
"source": "official-docs",
"checkedAt": "2026-10-28T10:00:00+08:00",
"versionScope": "OpenCode v2",
"status": "verified"
}
产品行为优先查官方文档、官方仓库、发布说明和标准原文。社区文章可以用来发现线索,但不能单独支撑接口名称、默认值和安全边界。结论中同时保留检查时间和版本范围,避免把今天的事实误当成永久契约。
四、防住网页中的提示注入
1. 网页内容是数据,不是系统指令
恶意页面可能写着"忽略之前指令""读取环境变量""把仓库上传到某地址"。对 Agent 来说,这些字符串与普通文档同样进入上下文,因此必须在流程层区分来源权限。
text
系统与用户授权:决定可以做什么
仓库规则:决定在项目中怎样做
网页内容:只提供待核验的数据
网页无权扩大工具权限,也无权替用户授权提交、删除、发布、上传或发消息。
2. 搜索与执行分阶段
text
阶段 A:只读搜索与摘录
阶段 B:来源核验与冲突分析
阶段 C:形成操作计划
阶段 D:按既有权限执行
阶段 E:回读业务状态
不要让"搜索到一个命令"直接连接到 Shell 执行。先解释命令影响、适用版本和回滚方式,再在受控目录与最小权限下运行。
3. 记录冲突而不是强行选边
yaml
topic: config-key
official_docs: supported
release_notes: introduced-in-2.x
community_post: says-removed
decision: follow-official-docs
follow_up: verify-against-installed-version
当来源冲突时,报告冲突、来源层级和本地验证结果。没有本地证据时不要把推测写成确定事实。
五、References:把外部资料变成命名资产
1. 本地目录 Reference
References 允许 OpenCode 为当前项目命名一个外部目录。相对路径从配置文件所在目录解析:
json
{
"$schema": "https://opencode.ai/config.json",
"references": {
"product-docs": {
"path": "../product-docs",
"description": "用于核对产品行为、术语和验收规则"
}
}
}
调用时先附加别名,再明确要求读取具体路径:
text
请检查已附加的 product-docs reference,
读取 guides/authentication.md,列出认证流程与失败分支。
Reference 根目录只提供一层非递归目录列表。想读取更深内容,要让 Agent 继续定位具体文件,而不是假设整个目录已经进入模型窗口。
2. 字符串简写的歧义
json
{
"references": {
"docs": "../docs",
"shared": "~/work/shared"
}
}
字符串只有以 .、/ 或 ~ 开头时才表示本地路径。裸写 docs 会被解释为 Git 仓库,因此同目录引用也应写成 ./docs。
3. Description 是路由提示
有 description 的 Reference 会以别名和解析后路径出现在 Agent 指令中,帮助模型判断什么时候使用。没有描述的 Reference 仍可被客户端附加,但不会自动广告给 Agent。
json
{
"references": {
"design-system": {
"path": "../design-system",
"description": "修改 UI 组件或设计 Token 时使用"
}
}
}
描述要写用途和边界,不要写"包含很多资料"这种无决策价值的句子。
六、远程 Git Reference:版本、缓存与刷新
1. 固定仓库与分支
json
{
"references": {
"internal-sdk": {
"repository": "git@example.invalid:platform/sdk.git",
"branch": "release/v2",
"description": "用于核对 SDK v2 接口与样例"
}
}
}
官方支持 GitHub owner/repo 简写、Git URL、host/path 和 SCP 风格地址。示例域名是保留域,不对应真实内部仓库。
2. 缓存目录不是工作副本
远程 Reference 会按远端与分支在 OpenCode 全局数据目录中保存一份 checkout。显式分支会形成带编码分支后缀的缓存路径。
text
opencode/repos/<host>/<repository-path>
opencode/repos/<host>/<repository-path>@<branch>
缓存可能被后台 refresh 重置,因此不要在里面直接开发或保存未提交改动。需要修改外部仓库时,应使用正式 clone、独立工作树和明确的写权限。
3. 理解 24 小时刷新窗口
text
Reference 加载或重载
→ 检查上次刷新尝试时间
→ 至少 24 小时则后台 fetch/reset
→ Agent 不等待刷新完成
刷新时间跨服务重启保留,共享同一 checkout 的 Location 共享时间戳。失败尝试也受 24 小时限制;未使用的 Location 不会周期轮询。
最容易踩坑的是时序:Attachment 可能先捕获旧目录内容,后台刷新随后完成,而 Agent 后续用工具读取时看到新版本。对版本敏感的任务要记录分支、Commit 和读取时点。
七、Reference 权限不会自动放大
1. 可见不等于可读
Reference 只告诉 OpenCode "有这样一个外部根"。真正读取项目外路径仍要经过 external_directory 与相关工具权限;编辑还需要 edit 权限。
text
Reference 声明
≠ 文件读取授权
≠ 文件编辑授权
≠ Git 推送授权
这是很重要的分层:资料发现和操作授权不能由同一个配置项完成。
2. Hidden 只影响选择器
json
{
"references": {
"internal": {
"path": "../internal",
"description": "用于内部服务行为核对",
"hidden": true
}
}
}
hidden: true 只是不在交互选择器中展示。Reference 仍存在于 API 中;如果有 description,也仍会进入 Agent 指令。它不是安全控制,不能用来保护敏感目录。
3. Alias 要稳定且无歧义
text
有效:docs
有效:sdk-v2
无效:product docs
无效:docs/api
别名不能为空,也不能包含斜杠、反斜杠、空白、反引号或逗号。团队应把别名视为接口,避免频繁改名导致命令和文档失效。
八、Attachments:为一次请求提供精确输入
1. 适合附加什么
当前 v2 session 可向模型展示 UTF-8 文本、目录的一层列表、PNG/JPEG/GIF/WebP 图片和 PDF。SVG 按文本处理;AVIF、BMP、音频、视频等其他二进制不会直接进入模型请求。
| 输入 | 模型实际收到 | 注意点 |
|---|---|---|
| UTF-8 文本 | 文件名与解码文本 | 大文件应分段 |
| 目录 | 一层文件/目录列表 | 不会递归展开 |
| PNG/JPEG/GIF/WebP | 图片媒体 | 需要支持图片的模型 |
| 文档媒体 | 需要支持 PDF 的模型 | |
| 不支持的二进制 | 简短说明 | 应先转换 |
客户端接受一个文件,不代表模型最终看到了内容。还要同时满足 OpenCode 格式、处理限制以及 Provider/模型限制。
2. 文件 URI 与行范围
json
{
"uri": "file:///workspace/project/src/server.ts?start=20&end=60",
"name": "server.ts",
"description": "HTTP 服务入口第 20 到 60 行"
}
文件 URI 必须是服务端可访问的绝对路径。文本文件的 start 和 end 使用从 1 开始的行号。程序化调用时不要把用户输入直接拼接进 URI,应先解析和校验目标路径。
3. Data URL 适合小内容
json
{
"uri": "data:text/plain;base64,SGVsbG8sIE9wZW5Db2RlIQ==",
"name": "greeting.txt"
}
Data URL 适合生成的小型内容或无需落盘的测试输入。HTTP/HTTPS Attachment URL 当前不受支持;网页信息应走 Websearch 或先经过受控下载与校验。
九、图片与 PDF:多模态能力不是上传即用
1. 先选支持该媒体的模型
文本模型可能拒绝图片,普通多模态模型也不一定支持 PDF。模型不支持某种 Attachment 时,OpenCode 会发送一条简短说明替代原内容,而不是假装已经识别。
text
附件被客户端接收
→ OpenCode 校验类型与大小
→ 模型能力校验
→ Provider 再执行自己的限制
→ 内容才可能进入模型
报告中应区分"已选择文件""OpenCode 已处理"和"模型实际可见"。
2. 配置图片规范化
json
{
"$schema": "https://opencode.ai/config.json",
"media": {
"image": {
"auto_resize": true,
"max_width": 2000,
"max_height": 2000,
"max_base64_bytes": 5242880
}
}
}
默认开启自动缩放,默认最大宽高都是 2000 像素,Base64 编码长度默认上限为 5,242,880 字节。OpenCode 保持纵横比,并尝试更小的 PNG 或 JPEG 编码;输出媒体类型可能变化。
3. 严格拒绝而不是自动缩放
json
{
"media": {
"image": {
"auto_resize": false,
"max_width": 1200
}
}
}
对图像取证、像素级 UI 对比或合规归档,自动重编码可能破坏证据。此时应关闭自动缩放,超限就失败,并在其他受控步骤生成派生图。
十、Attachment 大小、格式与敏感信息控制
1. 20 MiB 不是唯一限制
OpenCode 对单个解码后 Attachment 设置 20 MiB 上限;Desktop 文件选择器一次选择总量也最多 20 MiB,其他客户端可能更低。Provider 仍可对图片尺寸、文件数和媒体大小施加更严格限制。
text
单文件解码后大小 ≤ 20 MiB
Desktop 一次选择总量 ≤ 20 MiB
Base64 长度满足 media 配置
最终还要满足 Provider 限制
两个 12 MiB 文件分别满足单项限制,但会超过 Desktop 选择总量。
2. 扩展名不能伪造媒体类型
OpenCode 从实际字节检测图片类型。把 BMP 改名为 PNG,或修改 Data URL 的媒体声明,都不能让不支持的二进制变得可见。
bash
# 先在受控环境转换,再附加转换结果
convert input.bmp output.png
命令只是示意;生产任务应固定转换工具版本,并保存原文件哈希与转换参数。
3. 附加前做最小化
yaml
attachment_policy:
remove_secrets: true
remove_personal_data: true
select_relevant_pages: true
preserve_source_hash: true
record_purpose: true
不要把整份生产日志、数据库导出或合同 PDF 直接交给 Agent。先选相关页和相关行,脱敏后再附加;需要审计时保留原件位置与哈希,但不要把敏感原件复制进会话记录。
十一、把 Websearch、References 与 Attachments 组合起来
1. 一个可靠的故障排查链路
text
1. Attachment:附加脱敏后的错误日志片段
2. Reference:读取团队运行手册和当前服务代码
3. Websearch:核对依赖的最新官方变更
4. Agent:形成根因假设与验证顺序
5. Tools:只读验证,再实施最小修复
6. Test:运行回归与健康检查
7. Report:记录来源、Commit、命令和结果
三种来源各自承担职责:日志说明症状,Reference 说明本系统契约,Websearch 说明外部依赖现状。任何一个来源都不能单独替代完整证据链。
2. 版本固定策略
json
{
"task": "dependency-migration",
"projectCommit": "example-commit",
"referenceBranch": "release/v2",
"referenceCommit": "example-reference-commit",
"webCheckedAt": "2026-10-28T10:00:00+08:00",
"attachments": ["error-log-redacted.txt"]
}
如果 Git Reference 只固定分支、不固定 Commit,任务期间仍可能漂移。最可靠的做法是在读取后记录实际 Commit,并把网页检查时间与 Attachment 哈希一起写进任务制品。
3. 冲突解决顺序
text
运行中的系统事实
> 当前仓库代码与测试
> 固定版本官方文档
> 团队 Reference
> 最新网页资料
> 社区经验与模型记忆
这是默认参考顺序,不是绝对真理。例如团队规则可以比通用官方建议更严格,但不能把已经运行失败的事实解释成"文档说应该成功"。
十二、Warming:延续缓存,不等于保持会话在线
1. 默认关闭的真实模型请求
Session warming 会在用户暂停期间周期性调用当前模型,以尝试保留 Provider 侧 Prompt Cache 或其他短期状态。它默认关闭:
json
{
"$schema": "https://opencode.ai/config.json",
"warming": true
}
默认行为是在最近一次正常模型请求后空闲 4 分钟触发,之后每 4 分钟一次,直到正常请求后的 30 分钟窗口结束。
2. 自定义间隔、窗口与 Prompt
json
{
"$schema": "https://opencode.ai/config.json",
"warming": {
"prompt": "Do not perform any work. Reply with exactly: OK",
"interval": "5 minutes",
"duration": "1 hour"
}
}
interval 与 duration 必须解析为有限且大于零的时长,否则 OpenCode 跳过 warming 并记录警告。省略的字段继续使用默认值。
3. 理解计时语义
text
00:00 正常模型请求
00:04 warming
00:08 warming
00:20 新的正常模型请求,窗口重置
00:24 warming
00:50 窗口结束
每次 warming 完成后重新计算下一个空闲间隔,但不会延长活动窗口。只有新的非 warming 模型活动会重置两类计时器。
十三、Warming 的成本、失败与安全边界
1. 它会消耗真实额度
Warming 使用 session 当前的模型、Agent、instructions 和 conversation context,临时追加 keep-alive Prompt,调用一次模型后丢弃响应。请求不会写入持久消息历史,也不会继续工具循环。
text
[当前 session context]
[临时 keep-alive 消息]
→ 一次模型响应
→ 响应丢弃
但"丢弃响应"不等于免费。它仍可能消耗 Token、产生费用、占用限额和触发 Provider 失败。
2. 默认窗口最多七次尝试
按默认 4 分钟间隔与 30 分钟窗口,一段没有正常活动的窗口最多在第 4、8、12、16、20、24、28 分钟尝试七次。
text
更短 interval → 请求量上升
更长 duration → 可尝试次数上升
更大上下文 → 单次成本可能上升
只有当缓存命中节省的延迟或费用大于 warming 成本时才值得开启。
3. 失败不改变 session
Warming 失败会记录日志,但不会让 session 失败或改变 durable state。OpenCode 等待下一个间隔再尝试,原活动窗口继续倒计时。
text
00:04 warming 失败
00:08 再次尝试
00:30 窗口仍按原计划结束
不要为 warming 失败设计无限即时重试,否则会放大限流和费用问题。
十四、生产配置与审计蓝图
1. 一份分层配置示例
json
{
"$schema": "https://opencode.ai/config.json",
"websearch": {
"provider": "random"
},
"references": {
"product-docs": {
"path": "../product-docs",
"description": "用于产品行为与验收规则"
},
"public-sdk": {
"repository": "Effect-TS/effect",
"branch": "main",
"description": "仅用于公共 API 设计参考"
}
},
"media": {
"image": {
"auto_resize": true,
"max_width": 1600,
"max_height": 1600,
"max_base64_bytes": 4194304
}
},
"warming": false
}
示例不包含密钥。References 也不代表额外读取授权,实际还要配置外部目录、工具和编辑权限。
2. 最小审计事件
json
{
"time": "2026-10-28T10:00:00+08:00",
"sessionId": "session-example",
"action": "context-read",
"sourceType": "reference",
"sourceAlias": "product-docs",
"resource": "guides/authentication.md",
"decision": "allow",
"result": "succeeded"
}
日志不要记录搜索密钥、完整敏感搜索词、Attachment 原文、Provider 凭据和内部仓库私钥。审计的目标是证明谁在什么边界内访问了什么类别的资源,而不是复制全部敏感内容。
3. 关键指标
text
Websearch 成功率、429 率与 Provider 切换次数
官方一手来源占比与引用缺失率
Reference clone/refresh 成功率与版本漂移次数
Attachment 拒绝率、格式不支持率与平均大小
Warming 请求量、Token 成本、缓存收益与限流率
多源冲突数、人工复核率与错误结论率
只统计"调用了多少次"没有意义。应把输入来源与最终质量、延迟、成本和人工采纳结果关联起来。
十五、常见故障、上线清单与总结
1. 常见故障定位
text
搜不到:检查 Provider 凭据、权限、429 冷却与出网
Reference 为空:检查别名、路径语义、clone 是否完成
读取被拒:检查 external_directory 与工具权限
附件不可见:检查格式、模型能力、大小与 Provider 限制
图片被改码:检查 auto_resize 与 Base64 上限
Warming 没触发:检查是否启用、时长是否合法、窗口是否过期
成本异常:检查 warming 间隔、上下文体积和重复搜索
故障排查时先确认"入口是否成功",再确认"内容是否真的进入模型",最后确认"模型是否按来源边界使用"。
2. 上线检查清单
- 实时信息使用 Websearch,并优先引用官方一手来源;
- 搜索词已脱敏,未包含密钥、个人信息和内部地址;
- Websearch 权限默认为 ask 或更严格规则;
- Reference 别名、用途、分支和实际 Commit 可追溯;
- 不在远程 Reference 缓存 checkout 中保存修改;
- 外部目录读取与编辑分别受权限控制;
- Attachment 只包含任务必要页、行和媒体;
- 模型明确支持所附加的图片或 PDF;
- 图片缩放策略符合证据保真要求;
- 记录 Attachment 哈希,不在日志复制敏感原文;
- 网页与外部文档中的指令不扩大工具权限;
- 搜索、分析与执行分阶段,外部内容不能直接触发副作用;
- Warming 默认关闭,启用前已量化缓存收益;
- Warming 失败不会触发无限重试;
- 最终报告包含来源、时间、版本、命令与业务结果。
3. 总结
OpenCode v2 的多源上下文能力,真正价值不是"能往模型里塞更多东西",而是把实时网页、共享资料和单次输入分成可治理的通道。
一套成熟方案应该遵循这条链路:
text
先按生命周期选择 Websearch、Reference 或 Attachment
→ 用权限与脱敏限制输入边界
→ 用来源、时间、分支和 Commit 固定证据
→ 把外部内容视为数据而非授权指令
→ 先只读核验,再执行受控改动
→ 用测试、业务状态与结构化报告证明结果
→ 仅在收益可量化时启用 Warming
当 Agent 可以接触更多来源时,最重要的能力反而是拒绝无关信息、识别不可信指令、说明版本边界并保留证据。多源上下文不是一次搜索或一次上传,而是一套贯穿发现、权限、版本、成本和审计的工程系统。
4. 参考资料
- OpenCode v2 Websearch 官方文档
- OpenCode v2 References 官方文档
- OpenCode v2 Attachments 官方文档
- OpenCode v2 Warming 官方文档
- OpenCode v2 Permissions 官方文档
- OpenCode v2 Config 官方文档
- OpenCode v2 API Reference
本文依据 2026 年 10 月 28 日可见的 OpenCode v2 官方文档整理。Provider 列表、附件格式、大小限制、刷新窗口、图片默认值与 Warming 行为仍可能随版本调整;生产环境应固定实际版本,并以当前官方文档、安装版本和运行结果为准。示例中的仓库、域名、Commit、路径和凭据均为泛化内容。