post cover

技术热点落地:AI Coding Agent 自托管:OpenHands + Spot 实例省 70% 成本(2026-05-09)


适用场景与目标

谁适合自托管 AI Coding Agent?

  • 团队每月在 GitHub Copilot / Claude Code / Cursor 等服务上花费超过 $500
  • 对代码数据有隐私要求(不能把代码发给第三方 API)
  • 需要深度定制 Agent 行为(Prompt、工具链、审批流)
  • 已有 Kubernetes 或 AWS 基础设施

本文目标: 用 OpenHands 搭建一个生产级 AI Coding Agent,单机起步,Spot 实例托底,月成本从 ~$200 降至 ~$60,且数据完全自控。

为什么选 OpenHands? 开源、成熟(2026 年已有多篇生产部署案例)、支持 Docker 一键启动、默认集成 Code Interpreter + Browser + File tools,生态活跃。相比自建 Devin 类系统,省去 2 周基础设施工作。


最小可行方案(MVP)单机 Docker 部署

前提条件

  • Linux / macOS(建议 Ubuntu 22.04+,至少 4 核 8G)
  • Docker 24+
  • 目标大模型:OpenAI API(GPT-4o)或 Anthropic API(Claude Sonnet 4)

步骤 1:一键启动 OpenHands

# 克隆官方仓库
git clone https://github.com/All-Hands-AI/OpenHands.git
cd OpenHands

# 复制环境配置
cp .env.example .env

# 编辑 .env,填入你的 API Key
# 编辑 .env,填入你的 API Key
nano .env
# 必须配置:
# OPENAI_API_KEY=sk-xxxx
# OPENAI_BASE_URL=https://api.openai.com/v1
# 或使用 Anthropic:
# ANTHROPIC_API_KEY=sk-ant-xxxx
# ANTHROPIC_BASE_URL=https://api.anthropic.com

# 用 Docker 启动(默认前端 + 后端 + Agent 进程)
docker compose up -d

# 验证服务
curl http://localhost:3000/api/agent/status
# 返回 {"status": "idle"} 即成功

关键配置项说明:

# .env 核心配置
OPENAI_API_KEY=sk-xxx              # 必填
OPENAI_MODEL=gpt-4o                # 可选,默认 gpt-4o
MAX_TOKENS=4096                    # 单次回复上限
SANDBOX_MODE=docker                # docker / local / kubernetes
AGENT_MODE=default                 # default / security / readonly

步骤 2:配置持久化存储

# 建立工作目录,Agent 在此读写代码
mkdir -p /data/openhands/workspace
mkdir -p /data/openhands/state    # Agent 状态、session 持久化

# 修改 docker-compose.yml,追加 volumes
vim docker-compose.yml
# docker-compose.yml 关键片段
services:
  openhands:
    volumes:
      - /data/openhands/workspace:/workspace      # 代码工作区
      - /data/openhands/state:/app/state          # 状态持久化
      - /var/run/docker.sock:/var/run/docker.sock # Agent 可启动子容器

步骤 3:验证 Agent 基本能力

# 通过 API 触发一个简单任务
curl -X POST http://localhost:3000/api/agent/run \
  -H "Content-Type: application/json" \
  -d '{
    "task": "在 /workspace 中创建一个 hello.py,输出 Hello from OpenHands",
    "mode": "default"
  }'

# 查看运行状态
curl http://localhost:3000/api/agent/status

进阶:AWS Spot 实例部署(省 70% 成本)

为什么用 Spot?

按需 EC2 t3.xlarge(4 核 16G)= $0.167/h ≈ $120/月 Spot t3.xlarge = $0.05/h ≈ $36/月,节省约 70%

⚠️ Spot 可能被 AWS 中断,OpenHands 是无状态设计(状态在 PostgreSQL),中断影响可控,配合 RDS 多 AZ 方案可做到生产级 SLA。

架构概览

用户  →  CloudFront  →  ALB  →  ECS Fargate (OpenHands Backend)

                        RDS Aurora PostgreSQL (Multi-AZ)

                        EFS (workspace 共享存储)

CDK 部署核心片段(参考 kanemx 的方案)

// infrastructure/lib/openhands-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';

export class OpenHandsStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC
    const vpc = new ec2.Vpc(this, 'OpenHandsVPC', {
      maxAzs: 2,
      natGateways: 1,
    });

    // RDS Aurora PostgreSQL(状态持久化)
    const cluster = new rds.ServerlessCluster(this, 'DBCluster', {
      engine: rds.DatabaseEngine.AURORA_POSTGRESQL,
      vpc,
      scaling: { minCapacity: 1, maxCapacity: 4 },
    });

    // ECS Cluster(Spot 实例)
    const cluster = new ecs.Cluster(this, 'OpenHandsCluster', {
      vpc,
      capacityProviders: ['FARGATE_SPOT'],
      defaultCapacityStrategy: {
        type: ecs.CapacityProviderStrategyType.CAPACITY_PROVIDER,
        base: 0,
        weight: 100, // 100% 用 Spot
      },
    });

    // OpenHands Task Definition
    const taskDef = new ecs.FargateTaskDefinition(this, 'Task', {
      memoryLimitMiB: 16384,
      cpu: 4096,
    });

    taskDef.addContainer('OpenHands', {
      image: ecs.ContainerImage.fromRegistry('allhandsai/openhands:latest'),
      environment: {
        DB_URL: cluster.clusterSecret.secretValueFromJson('cluster_arn'),
        REDIS_URL: 'redis://redis:6379',
      },
      portMappings: [{ containerPort: 3000 }],
    });
  }
}

Spot 中断应对策略

# 利用 ECS Spot 中断处理(SIGTERM)
# 在应用层捕获,实现优雅退出

# 写一个入口脚本 graceful_shutdown.sh
#!/bin/bash
trap 'echo "Received SIGTERM, saving state..."; curl -X POST http://localhost:3000/api/agent/save-state; exit 0' SIGTERM

# 启动时优先恢复上次状态
curl -X POST http://localhost:3000/api/agent/restore-state
exec /app/openhands-entrypoint.sh

关键实现细节

多租户隔离(团队版)

每个用户独立 workspace + 独立数据库 schema:

# middleware/tenant.py
from starlette.middleware.base import BaseHTTPMiddleware
import uuid

class TenantMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        # 从 Header 或 Cookie 解析 tenant_id
        tenant_id = request.headers.get('X-Tenant-ID', 'default')
        request.state.tenant_id = tenant_id
        response = await call_next(request)
        response.headers['X-Tenant-ID'] = tenant_id
        return response

与现有 GitHub Actions CI 集成

# .github/workflows/openhands-review.yml
name: OpenHands Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.ref }}

      - name: Run OpenHands Review
        uses: allhandsoai/openhands-action@v1
        with:
          task: "Review the code changes in this PR for bugs, security issues, and code quality. Focus on: logic errors, edge cases, and potential bugs."
          api_key: ${{ secrets.OPENAI_API_KEY }}
          workspace: /workspace

常见坑与规避清单

说明规避方法
Agent 权限过大默认 Agent 可执行任意 shell 命令,存在安全风险启动时设置 AGENT_MODE=readonly,或通过 MCP 限制可用工具
API Key 泄露.env 文件提交到 GitHub.env 加入 .gitignore,用 AWS Secrets Manager 存 API Key
Spot 中断丢状态无状态设计没做好,中断后 session 丢失PostgreSQL 持久化 + 中断前自动 save-state
上下文超限大仓库导致 context 爆掉限制 workspace 扫描路径 + 拆分成多个 sub-agent
成本失控Agent 频繁重试、无限循环调用 API设置 MAX_ITERATIONS=50 + TIMEOUT_PER_TASK=600
Docker Sandbox 逃逸Agent 在容器中执行危险操作容器加 security-opt: no-new-privileges,限制网络
并发冲突多个任务同时操作同一文件通过 Redis distributed lock 互斥,或单租户串行

安全加固配置

# docker-compose.security.yml
services:
  openhands:
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=100m
    environment:
      AGENT_MODE: "security"  # 启用安全模式,禁止 rm -rf 等高危操作
    ulimits:
      nproc: 512
      nofile:
        soft: 1024
        hard: 2048

成本 / 性能 / 维护权衡

成本对比(单 Agent / 月)

方案月成本适用规模
GitHub Copilot Business$19/用户 × 5 = $95小团队
Claude Code Pro(云)$100/人 × 5 = $500追求最强模型
OpenHands + 按需 API~$60(Spot)+ API 费中型团队,数据敏感
OpenHands + 开源模型~$36(Spot)+ $0(本地)预算极紧,有 GPU

如果用 Ollama + Llama 4 开源模型,API 成本可以降到 $0,但代码质量会明显下降(对比 GPT-4o 和 Claude Sonnet 4)。建议先用 API 模型验证 ROI,再用开源模型替换非核心场景。

性能注意事项

  • OpenHands Agent 单次任务耗时通常是人工的 3-5 倍(更适合:代码审查、重构、测试生成;不适合:快速 hotfix)
  • Spot 实例启动冷启动约 30-60 秒,比按需实例慢
  • EFS 共享存储有 ~5ms 额外延迟,小文件影响可忽略

维护成本

任务频率工作量
Docker 镜像更新每周10 分钟
Spot 中断恢复按需5 分钟(自动化后自动恢复)
数据库备份每日自动
安全更新按需30 分钟

一周内可执行行动清单

Day 1(30 分钟):本地 Demo

  • docker pull allhandsai/openhands:latest
  • 配置 .env,填入 API Key
  • docker compose up -d,验证 Web UI 可访问
  • 跑一个简单任务(创建文件 / 写测试)

Day 2(1 小时):集成现有代码库

  • 把你的代码仓库映射到 /workspace
  • 跑 Code Review 任务,评估输出质量
  • 调整 system prompt,找到适合你团队的 agent 角色

Day 3(1 小时):安全加固

  • 应用 docker-compose.security.yml
  • 用 AWS Secrets Manager(或 1Password)管理 API Key
  • 配置 AGENT_MODE=security,禁止高危操作

Day 4(2 小时):成本估算

  • 统计本周 API 调用量,计算月成本
  • 对比现有 Copilot/Claude Code 成本
  • 如果省钱 >50%,开始评估 AWS Spot 方案

Day 5(2 小时):Spot 部署

  • 熟悉 CDK 或 Terraform
  • 部署单台 Spot EC2(参考 kanemx/Deploying-OpenHands-AI-Platform-on-AWS)
  • 验证:Spot 中断 → 自动恢复 → session 完整

Day 6(1 小时):GitHub Actions 集成

  • 配置 openhands-action 在 PR 时自动触发 Review
  • 评估:Agent Review 能发现多少人工 Review 漏掉的 bug

Day 7:复盘与优化

  • 统计 Agent 任务成功率 / 成本 / 节省人力
  • 决定:扩大规模 or 调整使用场景

参考资源: