OpenCode v2 CI 与自动化实战:无交互运行、权限收口与制品闭环
这是《OpenCode v2 实战:从零搭建企业级 AI 编程助手》专题第 10 篇。上一篇解决了长会话中的 Compaction、Snapshots 与任务续跑;本篇把 Agent 放进真正的工程流水线:使用
opencode run无交互执行审查与分析,用独立服务和最小权限缩小影响面,把模型输出、测试结果和差异证据保存为制品,并为超时、失败、重试和人工审批建立明确边界。
把 AI 命令复制进 CI 配置并不难,难的是让这条流水线长期可信。交互式使用时,人可以看到权限弹窗、修正含糊指令、阻止危险操作;无人值守环境没有这些缓冲。一次过宽的授权、一个来自不可信 PR 的提示注入、一条把密钥打印到日志的命令,都会把"自动化提效"变成真实事故。
因此,CI 中的 Agent 不应该被设计成一个拥有仓库全部权限的"自动程序员",而应该被设计成受约束的流水线步骤:输入固定、能力可枚举、输出可归档、失败可解释、写操作可审批。
本文将完成七件事:
- 讲清
opencode run与交互式 TUI 的边界; - 用
--standalone隔离 CI 会话、配置和工具执行; - 设计适合无人值守环境的 Permissions 与 Policies;
- 给出可直接改造的 GitHub Actions 审查流水线;
- 把回复、差异、测试和元数据整理成可追溯制品;
- 建立超时、失败、重试与幂等策略;
- 给出从只读审查到受控修复的分阶段落地方案。
一、先建立正确模型:CI Agent 是一个受控步骤
1. 交互式会话与无交互运行的差异
日常开发时,直接执行 opencode 会打开全屏终端界面。用户能够选择 Agent、继续对话、查看工具调用,并在权限为 ask 时作出决定。
自动化场景应使用:
bash
opencode run "Explain this repository"
opencode run 不打开交互界面,而是提交一条消息并把回复打印到终端。它适合脚本、CI Job、定时任务和批处理,但"没有界面"不等于"没有风险":模型仍可能读取文件、调用 Shell、启动子 Agent 或访问已配置的外部工具,实际边界仍由运行账户、配置、权限和组织 Policy 共同决定。
2. 流水线不是聊天窗口
CI 任务应当满足下面的约束:
text
固定输入
→ 明确 Agent 与模型
→ 受限工具和目录
→ 有时限的执行
→ 结构化或可归档输出
→ 机器门禁与人工复核
不要让一个 Job 同时"自由理解需求、修改任意文件、执行任意命令、推送分支、发布生产"。职责越多,失败后越难判断是模型、工具、代码、网络还是权限出了问题。
3. 最适合先自动化的任务
优先选择只读、结果可验证、失败不改变外部状态的场景:
| 场景 | 输入 | 输出 | 推荐风险级别 |
|---|---|---|---|
| PR 代码审查 | 当前差异、仓库规则 | 审查报告 | 低 |
| 测试失败归因 | 日志、测试代码 | 原因与候选修复 | 低 |
| 依赖升级影响分析 | Lockfile、变更日志 | 影响清单 | 低 |
| 文档一致性检查 | API、README、示例 | 不一致项 | 低 |
| 自动生成补丁 | 明确目录与测试 | Patch 制品 | 中 |
| 自动提交或推送 | 补丁、写入凭证 | 远程分支 | 高 |
| 部署或生产写入 | 构建物、环境凭证 | 外部副作用 | 极高 |
第一阶段只让 Agent 输出建议。第二阶段允许它在临时工作区生成补丁,但不推送。只有当审查、测试、审计和回滚都成熟后,才考虑受控写入。
二、opencode run 的关键参数怎样组合
1. 显式选择模型和 Agent
自动化不应依赖某台机器的临时默认值:
bash
opencode run \
--model anthropic/claude-sonnet-5-5 \
--agent reviewer \
"Review the current changes for correctness, security, and missing tests."
--model 使用 provider/model 形式;--agent 选择已经配置并评审过的角色。示例模型只用于说明参数,生产环境应锁定团队验证过的模型和 OpenCode 版本。
2. 用文件参数收窄上下文
当任务只涉及少量文件时,可显式附加:
bash
opencode run \
--agent reviewer \
--file src/server.ts \
--file src/client.ts \
"Review these files for concurrency bugs and missing error handling."
显式文件并不自动撤销 Agent 的其他读取能力,因此仍需配置 Permissions。它的价值是让输入意图更清晰,减少模型先扫描整个仓库的成本和噪声。
3. 为脚本输出 NDJSON
需要机器消费事件时,可以使用:
bash
opencode run --format json "List the TODO comments" > opencode-events.ndjson
官方将该格式定义为 newline-delimited JSON。NDJSON 适合流式处理和逐行归档,但解析器不应猜测未承诺的字段。升级 OpenCode 后,应先在测试流水线保存一份真实输出,回归内部解析器,再推广新版本。
如果只需要给人阅读的报告,保持默认文本输出更简单:
bash
opencode run --agent reviewer "Review the pull request" > opencode-review.md
4. 谨慎理解 --continue 与 --session
继续最近会话:
bash
opencode run --continue "Now check the expired-token case"
继续指定会话:
bash
opencode run --session ses_4f2a1c "Check edge cases"
这两个能力适合人工驱动的分阶段任务,但普通 CI 更推荐"每次运行输入完整、会话独立"。复用历史会话会引入隐藏状态:上次运行的分支、文件、批准规则和结论可能已经过期。只有当任务确实需要跨 Job 延续时,才保存会话 ID,并把 Commit SHA、配置版本和输入摘要一并记录。
5. --auto 不是安全开关
bash
opencode run --auto "Fix the failing test"
--auto 会自动批准没有被明确拒绝的权限请求。它解决的是"无人点击 ask"问题,而不是"自动识别危险动作"。如果默认规则过宽,--auto 会扩大风险。
正确顺序是:
- 先用 Permissions 明确允许、询问和拒绝;
- 再用 Policies 写不可被项目放宽的硬拒绝;
- 让 CI 运行账户本身只有最小系统权限;
- 最后才决定特定 Job 是否需要
--auto。
只读审查通常不需要 --auto;受控修复可以在严格 deny 基线上使用,但必须把所有外部副作用排除在外。
三、为什么 CI 应优先使用 --standalone
OpenCode 默认会发现或启动用户级共享后台服务。该服务持有会话、配置、集成、权限和工具执行状态。开发机上共享服务很方便,CI 中却会带来不必要的状态耦合。
bash
opencode run --standalone "Review this repository"
--standalone 为当前命令使用私有服务,适合 CI 的原因包括:
- 不复用开发者本地的会话与保存批准;
- 不与其他并行 Job 共享运行状态;
- 环境变量和 Provider Key 只进入本次私有服务;
- Job 结束后更容易销毁整个执行环境;
- 故障可以归因到当前 Commit、配置和输入。
可以把一次 CI 运行理解为一次短生命周期沙箱:
text
Runner 启动
→ Checkout 固定 Commit
→ 安装锁定版本
→ 注入最小 Secret
→ 启动 standalone 私有服务
→ 执行单一任务
→ 保存报告与证据
→ 销毁 Runner
需要强调:--standalone 隔离的是 OpenCode 服务状态,不是操作系统安全边界。Shell 仍继承 Runner 用户的文件、进程和网络权限。真正的隔离仍要依赖临时 Runner、容器、只读凭证、网络策略和最小化令牌。
四、为无人值守环境设计权限基线
1. ask 在 CI 中通常意味着无法继续
Permissions 的 effect 有 allow、deny 和 ask。交互客户端可以处理 ask,无人值守客户端必须预先决定如何处理请求。不能把所有 ask 粗暴变成 allow,否则审批边界会在最需要它的地方消失。
CI 规则应该尽量确定化:允许什么、拒绝什么都写清楚,未覆盖能力让任务明确失败并输出原因。
2. 只读审查 Agent 示例
jsonc
{
"$schema": "https://opencode.ai/config.json",
"permissions": [
{ "action": "external_directory", "resource": "*", "effect": "deny" },
{ "action": "read", "resource": "*", "effect": "allow" },
{ "action": "glob", "resource": "*", "effect": "allow" },
{ "action": "grep", "resource": "*", "effect": "allow" },
{ "action": "edit", "resource": "*", "effect": "deny" },
{ "action": "shell", "resource": "*", "effect": "deny" },
{ "action": "shell", "resource": "git status *", "effect": "allow" },
{ "action": "shell", "resource": "git diff *", "effect": "allow" },
{ "action": "shell", "resource": "git log *", "effect": "allow" },
{ "action": "subagent", "resource": "*", "effect": "deny" },
{ "action": "webfetch", "resource": "*", "effect": "deny" },
{ "action": "websearch", "resource": "*", "effect": "deny" }
],
"agents": {
"ci-reviewer": {
"description": "Review checked-out code without modifying it",
"mode": "primary",
"system": "Review only. Cite file paths and evidence. Never edit files or request credentials.",
"permissions": [
{ "action": "edit", "resource": "*", "effect": "deny" }
]
}
}
}
规则按顺序合并,最后一个匹配项生效,因此 Shell 的通配拒绝放在前面,精确的只读 Git 例外放在后面。一次操作如果检查多个资源,其中任何一个为 deny,整个操作都会被拒绝。
3. 默认外部目录必须显式收口
官方文档指出,默认策略没有要求 external_directory 询问,因此 Location 和工作树之外的路径不会自动弹出审批。CI 配置应主动添加:
jsonc
{ "action": "external_directory", "resource": "*", "effect": "deny" }
同时要记住:Shell 对命令中目录的推断是尽力而为,不能把路径匹配当成完整沙箱。与其维护一长串"危险命令黑名单",不如为只读 Job 建立极窄的 Shell 白名单。
4. 用 Policies 写不可被仓库放宽的红线
Permissions 属于 Agent 工具规则;Policies 只会进一步收紧,不能授予能力。系统级或组织级配置可以拒绝仓库自行放宽的操作:
jsonc
{
"$schema": "https://opencode.ai/config.json",
"policies": [
{ "action": "tool.use", "resource": "shell:git push *", "effect": "deny" },
{ "action": "tool.use", "resource": "shell:git tag *", "effect": "deny" },
{ "action": "tool.use", "resource": "shell:gh pr merge *", "effect": "deny" },
{ "action": "tool.use", "resource": "external_directory:*", "effect": "deny" },
{ "action": "integration.use", "resource": "mcp:*", "effect": "deny" }
]
}
Policy 在权限规则和已保存批准之后执行;命中 deny 时会直接阻止,不会进入审批。企业环境应把这类红线放在系统配置、受管配置或 Console,而不是只放在仓库内。
5. 三层边界缺一不可
| 层级 | 控制对象 | 典型措施 |
|---|---|---|
| Runner/容器 | 文件、进程、网络、系统凭证 | 临时 Runner、非特权用户、网络出口限制 |
| OpenCode Policy | 组织级不可放宽红线 | 禁止 push、生产工具、外部目录 |
| Agent Permissions | 当前角色能做什么 | 只读 Git、禁编辑、禁子 Agent |
只做其中一层都不够。提示词中的"请不要推送"更不能替代强制规则。
五、一个可落地的 GitHub Actions 只读审查流程
下面示例把 OpenCode 作为 PR 的只读审查步骤。它不写仓库、不推送分支,也不使用默认 GITHUB_TOKEN 做外部变更。
yaml
name: OpenCode Review
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
concurrency:
group: opencode-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout exact revision
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install pinned OpenCode CLI
env:
OPENCODE_VERSION: "2.0.21"
run: npm install --global "@opencode/cli@${OPENCODE_VERSION}"
- name: Capture change metadata
shell: bash
run: |
mkdir -p artifacts
git status --short > artifacts/git-status.txt
git diff --stat "${{ github.event.pull_request.base.sha }}"...HEAD \
> artifacts/diff-stat.txt
- name: Run read-only review
shell: bash
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -o pipefail
opencode run \
--standalone \
--agent ci-reviewer \
--model anthropic/claude-sonnet-5-5 \
"Review the diff from ${BASE_SHA} to ${HEAD_SHA}. Focus on correctness, security, regressions, and missing tests. Cite file paths and explain evidence. Do not modify files." \
2>&1 | tee artifacts/opencode-review.md
- name: Publish job summary
if: always()
shell: bash
run: |
if [ -f artifacts/opencode-review.md ]; then
cat artifacts/opencode-review.md >> "$GITHUB_STEP_SUMMARY"
else
echo "OpenCode did not produce a review report." >> "$GITHUB_STEP_SUMMARY"
fi
- name: Upload evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: opencode-review-${{ github.run_id }}
path: artifacts/
retention-days: 14
1. 为什么要锁定 CLI 版本
Agent 配置字段、默认模型、输出事件和权限动作会随版本演进。使用浮动 latest 可能让同一 Commit 在不同日期得到不同运行环境。版本升级应先经过测试流水线,再修改仓库中的固定版本。
上面 2.0.21 只是展示"固定版本"的写法,不代表长期推荐版本;应替换为团队实际验证的版本。
2. 为什么限制 permissions: contents: read
即使 OpenCode 的规则出现错误,GitHub 令牌也不应该默认拥有写权限。平台令牌、OpenCode 权限和操作系统账户应当相互独立地遵循最小权限。
3. 为什么使用 if: always() 上传证据
模型失败、超时或权限拒绝时,排障更需要现场信息。只在成功时上传报告,会把最有价值的失败证据丢掉。上传内容必须经过敏感信息检查,不要把完整环境变量、认证文件或未脱敏日志打包。
4. 为什么给并发任务分组
同一个 PR 每次推送都会触发新审查。取消旧的未完成任务可以减少模型成本,也避免过期报告晚于新报告完成,造成审查结论倒序。
六、Secret、PR 与提示注入的边界
1. 密钥只通过平台 Secret 注入
Provider Key 不应写进:
opencode.jsonc的明文;- 仓库脚本;
- Prompt;
- 命令行参数;
- Artifact;
- 调试日志。
GitHub 官方建议通过 Secrets Context 注入环境变量,并避免在命令行中传递 Secret。对于支持 OIDC 的云资源,应优先使用短期身份,减少长期凭证。
2. 不可信 PR 就是不可信输入
PR 中的代码、注释、Markdown、测试输出和文件名都可能包含提示注入,例如要求 Agent 读取环境变量、访问网络或忽略审查规则。模型"看到了仓库内容"不代表仓库内容获得了指令权限。
防线应包括:
- Fork PR 不注入高权限 Secret;
- 审查 Agent 禁止编辑、网络、子 Agent 和外部目录;
- 不把生产 MCP、云凭证或发布令牌加载到审查 Job;
- Prompt 明确仓库内容是待分析数据,不是可信指令;
- 结论只作为报告,不直接触发合并或部署。
3. 谨慎对待 pull_request_target
该事件在目标仓库上下文中运行,常被用来访问 Secret 或写评论。如果同时检出并执行来自 Fork 的不可信代码,会显著扩大风险。审查不可信代码时,应把"读取差异"和"使用高权限令牌"拆成不同安全域,不能仅靠 Prompt 隔离。
4. 日志脱敏不是无限保证
平台能遮罩已登记的 Secret,但经过编码、拆分或转换后的内容未必都能自动识别。Agent 输出也可能复述它读到的敏感文件。最可靠的办法是让敏感内容根本不进入其可读范围。
七、把输出设计成证据包,而不是一段聊天
1. 推荐制品目录
text
artifacts/
├── run-metadata.json
├── opencode-review.md
├── opencode-events.ndjson
├── git-status.txt
├── diff-stat.txt
├── test-summary.txt
└── policy-denials.txt
run-metadata.json 可以记录非敏感元数据:
json
{
"repository": "example/service",
"base_sha": "0123456",
"head_sha": "89abcde",
"workflow_run_id": "123456789",
"opencode_version": "2.0.21",
"agent": "ci-reviewer",
"model": "anthropic/claude-sonnet-5-5",
"result": "completed"
}
不要记录 API Key、完整认证头、用户凭证或可直接复用的会话令牌。
2. 人读报告与机器事件分开
一份 Markdown 报告适合 PR 页面和人工评审;NDJSON 适合平台统计、事件追踪和自动化解析。不要为了"结构化"而把原始内部事件全部公开到评论区,也不要让后续脚本依赖自然语言中某个不稳定句式。
3. Artifact 需要生命周期和完整性
GitHub Actions 可以为 Artifact 设置 retention-days。保留期应按审计和成本要求设定,而不是永久堆积。官方上传动作还会返回 Artifact 的 SHA-256 digest,下载动作会重新计算并验证;关键流程可以把 digest 记录到审计系统。
4. 把验证结果放在模型结论旁边
模型说"测试应该通过"不是证据。制品中应保存真实命令的退出状态和摘要:
text
Agent 结论:怀疑缓存失效条件错误
代码位置:src/cache.ts:87
真实验证:npm test -- cache.spec.ts
退出状态:0
测试数量:18 passed
Commit:89abcde
Agent 负责提出假设和解释证据,确定性工具负责证明构建、测试、格式和静态检查是否通过。
八、失败、超时与重试怎样设计
1. 先按失败类型分类
| 类型 | 示例 | 是否自动重试 |
|---|---|---|
| 临时基础设施错误 | 网络抖动、Provider 5xx | 可有限重试 |
| 速率限制 | 429、额度瞬时不足 | 按退避策略重试 |
| 权限拒绝 | Shell/目录/Policy deny | 不重试,修正规则或任务 |
| 输入错误 | 文件不存在、参数错误 | 不重试,修复配置 |
| 测试失败 | 代码行为不符合预期 | 不重复同一动作,进入诊断 |
| 已发生外部写入后连接中断 | 评论、提交、部署状态不明 | 先查实时状态,禁止盲重放 |
2. 使用有界指数退避
只对明确可重试、最好是只读且幂等的请求使用重试:
text
第 1 次失败 → 等待 5 秒
第 2 次失败 → 等待 15 秒
第 3 次失败 → 等待 45 秒
仍失败 → 保存证据并结束
每次重试都应保留尝试编号和错误类型。不要无限循环,也不要在一次昂贵请求失败后立即并发十次相同请求。
3. Job 和命令都要有时间预算
GitHub Actions 的 timeout-minutes 是外层保险。内部脚本还应限制模型调用、测试和构建各自的时间。这样才能区分"Agent 没有结束""测试卡住"和"整个 Runner 超时"。
4. 外部副作用必须使用幂等键
如果后续流程要写评论或创建工单,使用稳定键避免重复:
text
idempotency_key = 仓库 + PR 编号 + Head SHA + 检查类型
重试前先查询该键对应的结果是否已经存在。对于 Git push、发布、付款、数据库写入等不可安全重放的动作,结果不明时必须先读实时状态。
5. 不要让模型自行决定无限重试
重试属于工作流控制逻辑,应由外层脚本或 CI 平台管理。Agent 可以解释失败并建议下一步,但不应通过含糊 Prompt 自行循环调用同一外部系统。
九、从只读审查升级到受控生成补丁
1. 在临时工作区允许编辑
受控修复 Job 可以允许 Agent 编辑指定目录,但仍拒绝 Push、外部目录和生产集成:
jsonc
{
"permissions": [
{ "action": "edit", "resource": "*", "effect": "deny" },
{ "action": "edit", "resource": "src/*", "effect": "allow" },
{ "action": "edit", "resource": "tests/*", "effect": "allow" },
{ "action": "shell", "resource": "*", "effect": "deny" },
{ "action": "shell", "resource": "npm test *", "effect": "allow" },
{ "action": "shell", "resource": "npm run lint *", "effect": "allow" },
{ "action": "shell", "resource": "git diff *", "effect": "allow" }
]
}
注意这里的 src/* 使用整值通配匹配;应根据真实目录结构和规范化路径验证规则是否覆盖预期文件。
2. 输出 Patch,不直接推送
bash
git diff --binary > artifacts/opencode-fix.patch
随后由独立 Job 完成:
- 在干净工作树应用 Patch;
- 运行完整测试和安全检查;
- 生成变更摘要;
- 请求 Code Owner 或维护者批准;
- 批准后由受控机器人身份提交。
生成与提交拆开后,即使 Agent 获得编辑权限,也没有远程仓库写权限。
3. 写入流程要跨越明确门禁
text
Agent 生成补丁
→ 确定性测试
→ 静态与依赖扫描
→ Patch 制品
→ 人工审批
→ 独立机器人提交
每个箭头都应能回答:输入是什么、由谁批准、证据在哪里、失败后如何恢复。
十、成本控制不是"少用模型"这么简单
1. 只在有信息增量时触发
文档拼写变化不一定需要完整安全审查。可以根据文件路径、标签或变更规模选择是否运行,以及选择不同 Agent 和模型。
2. 先用确定性工具缩小范围
text
lint/test/scan 先运行
→ 提取失败摘要
→ 只把相关文件与日志交给 Agent
这比把整个仓库和全部构建日志都塞入 Prompt 更便宜,也更容易复核。
3. 取消过期运行
同一 PR 新 Commit 到来后,旧审查继续运行通常没有价值。使用 Concurrency 取消过期任务,是最直接的成本控制之一。
4. 记录版本、模型与用量
OpenCode 的 stats 命令可以查看模型、成本和 Token 统计;团队还应把 Workflow、Commit、Agent、模型和结果关联起来。没有维度的总账无法回答"哪个自动化检查最贵、是否带来了可验证收益"。
十一、常见反模式
反模式 1:给 Agent 一个仓库写令牌再要求"只审查"
Prompt 不是访问控制。只读任务就不应拥有写令牌。
反模式 2:在 Fork PR 中加载生产 Secret
仓库内容属于不可信输入。高权限 Secret 与不可信代码不应出现在同一执行域。
反模式 3:为了无人值守全局使用 --auto
先收口权限和 Policy,再决定是否自动批准。否则只是把审批消失包装成自动化。
反模式 4:把共享后台服务当作 CI 隔离
共享服务可能携带历史会话、批准和集成。CI 应优先使用 --standalone 和短生命周期 Runner。
反模式 5:只保存最后一段模型回答
没有 Commit、配置版本、测试摘要和权限拒绝信息,报告很难复现。
反模式 6:遇到失败就无条件重跑
权限错误、输入错误和已发生副作用后的未知状态都不应盲目重试。
反模式 7:让 Agent 既生成又发布
补丁生成、确定性验证、审批和远程写入应拆成不同阶段与身份。
十二、分阶段落地路线
阶段 1:建议模式
- 只读仓库;
- 禁止网络、编辑和子 Agent;
- 输出 Markdown 报告;
- 不作为合并硬门禁;
- 人工统计误报和有效发现。
阶段 2:证据模式
- 保存 NDJSON、测试摘要和差异元数据;
- 固定 CLI、Agent、模型与配置版本;
- 对权限拒绝、超时和 Provider 错误分类;
- 为报告建立保留期和脱敏检查。
阶段 3:补丁模式
- 只允许编辑指定目录;
- 在临时工作区生成 Patch;
- 完整测试与扫描;
- 不直接 Push;
- 人工审批后由独立身份提交。
阶段 4:受控闭环
- 只对低风险、可回滚任务自动提交;
- 使用幂等键和实时状态校验;
- 组织级 Policy 固化红线;
- 持续审计权限、成本、命中率和回滚率。
不要跨过前一阶段的证据直接追求"全自动修复"。自动化成熟度来自可重复和可解释,而不是来自省掉了多少确认按钮。
十三、上线检查清单
执行环境
- 使用临时 Runner、容器或隔离账户;
- CI 使用
opencode run --standalone; - CLI、Action 与关键依赖版本已固定;
- Job、模型调用和测试都有超时;
- 同一 PR 的过期运行会被取消。
身份与 Secret
- Provider Key 来自 CI Secret,不在仓库和参数中;
- 平台令牌为最小权限;
- Fork PR 不接触高权限 Secret;
- 日志与 Artifact 不包含认证材料;
- 可用 OIDC 的云资源不继续使用长期密钥。
OpenCode 权限
-
external_directory已显式 ask 或 deny; - Shell 使用窄白名单,而不是危险命令黑名单;
- 编辑、网络、MCP 和子 Agent 按任务关闭;
- 组织红线写入高优先级 Policies;
-
--auto只在严格权限基线上使用。
输出与验证
- 报告包含 Commit、Agent、模型和配置版本;
- 模型结论旁边有真实测试或扫描证据;
- 失败时仍上传经过脱敏的诊断制品;
- Artifact 有保留期,必要时记录 digest;
- NDJSON 解析器经过目标版本回归。
副作用与恢复
- 生成补丁与远程提交使用不同 Job 和身份;
- 写操作前有明确门禁;
- 重试只覆盖可识别的临时错误;
- 外部写入有幂等键;
- 结果不明时先查询实时状态,不盲目重放。
十四、故障排查顺序
1. opencode run 一直不结束
先检查 Job 超时、Provider 响应、工具调用是否卡住,再检查是否遇到无人处理的权限请求。不要直接放宽所有权限。
2. 本地成功,CI 失败
依次核对 CLI 版本、工作目录、配置来源、模型名称、环境变量、文件大小写、系统 Shell 和 Runner 网络。使用 opencode debug config 查看实际合并配置,避免凭印象判断。
3. Agent 读到了工作区外文件
检查 external_directory 规则是否缺失,Runner 账户可见范围是否过大,以及 Shell 是否通过命令间接访问。补上 OpenCode 规则后,也要缩小操作系统文件权限。
4. 报告内容与当前 Commit 不一致
确认 Checkout 的 SHA、Prompt 中的 Base/Head SHA、Artifact 元数据和审查触发时间。不要复用没有绑定 Commit 的旧会话。
5. 流水线频繁被限流
先取消过期任务、收窄触发条件和上下文,再设置有界退避。并发堆积通常比单次请求更容易放大限流。
6. Patch 通过局部测试但完整构建失败
Agent 生成阶段只是一项候选变更。必须在干净工作树重新应用 Patch,并运行完整的确定性门禁,不能把 Agent 自己执行过的局部命令当作最终验收。
十五、总结
OpenCode v2 进入 CI 后,价值不在于"让模型替代一条 Shell 命令",而在于把复杂代码理解、故障归因和候选修复接入已有工程治理。真正可靠的流水线应满足:
text
独立服务与短生命周期环境
→ 固定版本、模型和 Agent
→ 最小权限与组织 Policy
→ 明确输入和时间预算
→ 报告、事件、测试与差异制品
→ 有界重试和幂等写入
→ 人工或确定性门禁后再产生副作用
opencode run 提供了无交互入口,--standalone 提供服务级隔离,Permissions 和 Policies 提供工具边界,CI 平台提供身份、超时、并发和制品能力。它们组合起来,才能让 Agent 从"偶尔给出好答案"变成"在工程系统里可重复、可审计、可安全失败的一步"。
下一篇将继续专题第 11 篇:深入 OpenCode v2 的可观测性、成本治理与质量评估,讨论怎样记录模型、Token、工具调用、失败原因与人工采纳结果,并用可量化指标判断 Agent 自动化到底有没有产生净收益。
参考资料
- OpenCode v2 CLI 官方文档
- OpenCode v2 Commands 与 CI 示例
- OpenCode v2 Permissions 官方文档
- OpenCode v2 Policies 官方文档
- OpenCode v2 Config 官方文档
- GitHub Actions 使用 Secrets
- GitHub Actions 存储与共享 Workflow Artifacts
本文依据 2026 年 10 月 24 日可访问的 OpenCode v2 与 GitHub Actions 官方文档整理。OpenCode v2、模型 ID、CLI 参数、权限动作和输出事件仍可能演进;生产环境应以实际安装版本的
opencode run --help、配置 Schema、官方文档和回归验证结果为准。文中的版本号与模型名用于演示固定版本和显式选择的做法,不构成长期版本推荐。