让每个 Agent 改动都有独立试验场:理解 Worker Previews

2026-09-22 30 预计阅读时间: 1 分钟
来源: blog.cloudflare.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

当开发 Agent 能在短时间内连续修改多个分支时,传统的共享测试环境很快会变成瓶颈:配置互相覆盖、测试状态彼此污染,日志也难以对应到某一次改动。Worker Previews 的核心变化,是为每个分支提供独立的 URL、配置、状态和可观测性,让开发者与 Agent 可以并行验证改动,而不触碰生产环境。

隔离的不只是访问地址

“每个分支一个 URL”只是预览环境最直观的部分。真正有价值的是四个维度同时隔离:

  • URL:每次改动都有明确入口,可以交给自动化测试、产品人员或另一个 Agent 检查。
  • 配置:环境变量、功能开关和依赖地址不会与其他分支共用。
  • 状态:某个分支写入的测试数据不会改变其他预览或生产环境。
  • 可观测性:日志、指标和错误可以追溯到具体分支,而不是全部混在共享测试环境里。

这几个条件缺一不可。只有独立 URL、却仍然连接共享数据库的环境,并不是真正隔离;同样,如果日志中没有分支或预览标识,并行运行多个 Agent 后仍然很难定位故障。

对于 Agent 工作流,这种隔离尤其重要。Agent 不只生成代码,还可能执行部署、调用接口、写入状态并根据结果继续修改。如果所有任务共享同一套环境,一个任务产生的数据就可能误导另一个任务。

从“提交代码”转向“提交可验证环境”

接入预览环境后,一次变更的交付物不再只有 commit 或 diff,还应该包括一个可以访问和诊断的运行实例。典型流程可以设计成:

  1. Agent 在独立分支上完成修改。
  2. CI 为该分支创建预览环境,并返回唯一 URL。
  3. 自动化测试针对这个 URL 执行接口、页面或回归检查。
  4. Agent 根据独立日志和测试结果继续修复。
  5. 人工评审通过后合并代码,并销毁预览环境。

这里的关键是让预览 URL 成为流水线中的一等输出。例如,CI 可以把它写入 Pull Request 评论、任务记录或 Agent 上下文:

PREVIEW_URL="https://fix-login-timeout.preview.example.com"

curl --fail --show-error --silent \
  "${PREVIEW_URL}/health"

curl --fail --show-error --silent \
  -H 'Content-Type: application/json' \
  -d '{"email":"preview-user@example.com"}' \
  "${PREVIEW_URL}/api/session/check"

实际使用时,需要把域名和接口路径替换为项目自己的地址,并确保测试账号与生产账号完全分离。

用 Kubernetes 模拟分支级隔离

Worker Previews 的具体平台接口并未在摘要中展开。为了理解其运行模型,可以在 Kubernetes 中用“每个分支一个 Namespace”模拟相似的隔离方式。下面示例假设:

  • 集群已经安装 Ingress Controller;
  • 应用镜像监听 8080 端口;
  • DNS 支持 *.preview.example.com
  • 应用把临时状态写入 /data

将以下内容保存为 preview.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: preview-${SLUG}
  labels:
    app.kubernetes.io/part-of: branch-preview
    preview.branch: "${SLUG}"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: preview-${SLUG}
data:
  APP_ENV: preview
  PREVIEW_ID: "${SLUG}"
  FEATURE_EXPERIMENTAL_UI: "true"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-state
  namespace: preview-${SLUG}
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: preview-${SLUG}
  labels:
    app: preview-app
    preview.branch: "${SLUG}"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: preview-app
  template:
    metadata:
      labels:
        app: preview-app
        preview.branch: "${SLUG}"
    spec:
      containers:
        - name: app
          image: ${IMAGE}
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: app-config
          volumeMounts:
            - name: state
              mountPath: /data
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 3
            periodSeconds: 5
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
      volumes:
        - name: state
          persistentVolumeClaim:
            claimName: app-state
---
apiVersion: v1
kind: Service
metadata:
  name: app
  namespace: preview-${SLUG}
spec:
  selector:
    app: preview-app
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app
  namespace: preview-${SLUG}
spec:
  rules:
    - host: ${SLUG}.preview.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80

可以使用下面的命令从当前 Git 分支生成安全的预览标识并部署:

export SLUG="$(git branch --show-current \
  | tr '[:upper:]' '[:lower:]' \
  | sed -E 's/[^a-z0-9-]+/-/g; s/^-+|-+$//g' \
  | cut -c1-40)"
export IMAGE="registry.example.com/my-app:$(git rev-parse --short HEAD)"

envsubst < preview.yaml | kubectl apply -f -

kubectl rollout status \
  deployment/app \
  -n "preview-${SLUG}"

echo "Preview: https://${SLUG}.preview.example.com"

日志也可以按预览环境单独读取:

kubectl logs \
  -n "preview-${SLUG}" \
  deployment/app \
  --all-containers=true \
  --follow

变更合并或分支关闭后,删除整个 Namespace 即可回收该预览的计算、配置和状态:

kubectl delete namespace "preview-${SLUG}"

这不是 Worker Previews 官方接口,而是一个可改造的隔离模型。实际平台如果原生提供分支 URL、状态和日志,就不需要自行维护这些 Kubernetes 资源。

落地时最容易遗漏的边界

独立预览环境并不自动等于安全环境。接入 Agent 与 CI 时,至少要检查以下事项:

  • 密钥分级:预览环境只能使用测试密钥,不应获得生产数据库或支付系统凭证。
  • 外部依赖隔离:邮件、短信、Webhook 和队列可能仍指向真实系统,应使用沙箱、模拟服务或明确的预览前缀。
  • 数据库迁移策略:破坏性迁移不应从预览环境直接作用于共享数据库。
  • 访问控制:分支 URL 可能包含尚未发布的功能和数据,需要身份认证或短期访问令牌。
  • 资源回收:为关闭的分支设置自动销毁和最大存活时间,避免 Agent 高频创建环境导致成本失控。
  • 统一关联标识:在日志、指标、Trace 和测试报告中写入同一个 preview ID,才能形成完整诊断链路。
  • 环境一致性:预览环境应尽量复用生产构建产物和启动方式,但数据与权限必须保持隔离。

Worker Previews 的价值,不只是省去手工部署测试分支的步骤。它把每次代码修改变成一个独立、可访问、可验证、可回收的运行单元。对并行工作的开发者和 Agent 来说,这正是避免环境争用、状态污染和错误归因的基础设施。


相关推荐