让 AI Agent 直接修改代码、执行测试并创建 Pull Request,已经从实验性工作流变成了可以纳入 CI 的工程实践。但 Agent 需要读写仓库、安装依赖、启动服务,权限过大时会带来明显的供应链和凭据风险。
Docker Sandboxes 提供了一个更适合这类任务的边界:把 Agent 放进隔离环境,让它在沙箱中查看代码、运行 Testcontainers 测试和修改工作区,GitHub Actions 只负责调度、收集结果以及创建草稿 Pull Request。
为什么要把 Agent 放进沙箱
一个能够自主修复代码的 Agent,通常需要完成以下操作:
- 读取整个仓库,理解项目结构和测试约定。
- 修改源代码、测试文件或配置文件。
- 执行构建、单元测试和集成测试。
- 启动数据库、消息队列等临时依赖。
- 根据失败日志继续分析并尝试修复。
如果这些操作直接发生在 GitHub Actions Runner 上,Agent 可能接触到不必要的环境变量、缓存目录或宿主机资源。尤其是工作流中存在第三方 Action、外部脚本或不受信任的输入时,风险会进一步扩大。
Docker Sandboxes 的价值不只是“再启动一个容器”。它的目标是为 Agent 提供一个相对独立的开发环境,并把工作区、网络和凭据暴露控制在明确范围内。这样可以把 Agent 的职责限定为:在指定目录中修改代码,并用允许的工具验证修改结果。
需要注意,沙箱不是自动完成的安全证明。工作流仍然需要遵循最小权限原则,避免把长期有效的云凭据、生产环境密钥或高权限 GitHub Token 直接交给 Agent。
一个可改造的 GitHub Actions 工作流
下面是一个最小化示例。它假设 Runner 已经安装了支持 Docker Sandboxes 的 Docker CLI,并且本地可以使用 docker sandbox run。不同 Docker 版本或 Agent 镜像的参数可能略有差异,实际使用时应以当前 Docker Sandboxes CLI 文档为准。
这个示例的关键点有三个:
- 只在拥有写权限的事件上运行 Agent。
- 把仓库挂载到沙箱内的固定目录。
- 让 Agent 先运行测试,再输出修改结果,由后续步骤决定是否创建草稿 PR。
name: AI repair proposal
on:
workflow_dispatch:
inputs:
task:
description: "修复任务,例如:修复失败的订单服务测试"
required: true
type: string
permissions:
contents: write
pull-requests: write
jobs:
repair:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
TASK: ${{ inputs.task }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare workspace
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "agent/${GITHUB_RUN_ID}"
- name: Run agent in Docker Sandbox
run: |
docker sandbox run \
--mount-workspace "$GITHUB_WORKSPACE:/workspace" \
--network none \
agent-image:latest \
sh -lc '
cd /workspace
printf "%s\n" "$TASK" > /tmp/task.txt
agent run \
--workspace /workspace \
--task "$(cat /tmp/task.txt)" \
--run-tests "./gradlew test"
'
- name: Show changes
run: |
git status --short
git diff --check
git diff --stat
- name: Run verification outside the agent
run: ./gradlew test
- name: Commit and push proposal
run: |
if git diff --quiet; then
echo "Agent did not change any files."
exit 0
fi
git add -A
git commit -m "chore: propose AI-assisted fix"
git push --set-upstream origin "agent/${GITHUB_RUN_ID}"
- name: Open draft pull request
env:
GH_TOKEN: ${{ github.token }}
run: |
if git diff HEAD^ HEAD --quiet; then
exit 0
fi
gh pr create \
--draft \
--title "AI-assisted repair proposal" \
--body "由隔离的 Docker Sandbox Agent 生成。请人工检查代码变更、测试覆盖率和依赖影响。" \
--base "${GITHUB_REF_NAME}" \
--head "agent/${GITHUB_RUN_ID}"
示例中的 agent-image:latest 和 agent run 是占位名称,需要替换成团队实际使用的 Agent 镜像与启动命令。--network none 适合不需要访问外部服务的任务。如果 Agent 必须下载依赖或连接测试服务,应改为更受控的网络策略,而不是直接开放全部网络。
Testcontainers 与沙箱边界
Testcontainers 测试通常需要启动 Docker 容器,例如 PostgreSQL、Redis 或 Kafka。此时要区分两层能力:
- Agent 所在的 Docker Sandbox,负责隔离 Agent 进程和工作区。
- Testcontainers 使用的容器运行时,负责启动测试依赖。
如果沙箱禁止访问 Docker daemon,Testcontainers 可能无法创建依赖容器。工程上可以采用两种方式:
- 在沙箱内提供受限的 Docker socket 或专用 Docker daemon。
- 让 Agent 只负责修改代码,在沙箱外由一个独立验证步骤运行 Testcontainers 测试。
第二种方式通常更容易审计。Agent 可以先运行不依赖外部容器的快速测试,提交修改后,GitHub Actions 再执行完整的集成测试。这样即使 Agent 生成了危险的容器参数,也不会直接控制长期运行的宿主机 Docker daemon。
一个典型的分层流程如下:
# 沙箱内:快速反馈,限制网络和工具范围
agent run --task "$TASK" --run-tests "./gradlew test"
# Runner 上:由固定脚本执行,不把控制权交给 Agent
./gradlew integrationTest
./gradlew testcontainersTest
如果项目确实要求 Agent 在迭代过程中运行 Testcontainers,应明确限制以下内容:
- 可访问的镜像仓库和网络域名。
- Docker daemon 的权限范围。
- 测试容器的 CPU、内存和运行时长。
- 工作区之外的挂载目录。
- Agent 是否能够读取 GitHub、云服务或包管理器凭据。
凭据、分支和审查流程
最容易被忽略的是 GitHub 权限。一个能够创建 PR 的 Agent 不应该同时拥有发布、删除仓库内容或访问生产资源的权限。建议把工作流设计成一次性、可追踪的修复提案:
- 使用
workflow_dispatch或受信任的 Issue 命令触发。 - 只授予
contents: write和pull-requests: write等必要权限。 - 让 Agent 推送到独立分支,不允许直接写入默认分支。
- 一律创建 draft PR,要求人工完成最终审查。
- 将测试结果、变更统计和 Agent 日志作为 PR 检查项保存。
- 对任务描述、Issue 内容和用户输入设置长度与字符限制。
如果需要访问私有包仓库,优先使用短期 Token,并把凭据注入到沙箱内的专用环境变量。不要把 ${{ secrets.* }} 写进 Agent 的提示词,也不要让 Agent 负责生成或上传新的长期凭据。
什么时候值得采用
Docker Sandboxes 比普通容器多了一层运行时和权限管理成本,因此并非每个自动化脚本都需要它。以下任务比较适合:
- 根据失败测试自动提出修复方案。
- 批量升级依赖并生成变更说明。
- 对多个模块执行格式化、迁移或兼容性修复。
- 在隔离环境中分析仓库并生成草稿 PR。
如果任务只是固定的代码格式化或版本替换,普通 GitHub Actions 步骤通常更简单、更可预测。Agent 适合处理需要阅读上下文、尝试多个方案并根据测试反馈调整的工作,但它的输出仍然应当被视为未审核的代码变更。
落地时可以从一个手动触发的工作流开始:限制权限,使用独立分支,在沙箱内执行快速测试,然后在 Runner 上执行完整验证。确认日志、网络和凭据边界都符合预期后,再考虑接入定时任务或 Issue 自动触发。这样既能获得 Agent 的修复能力,也能保留现有 CI 的可审查性和失败回滚路径。