技术热点落地: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 调整使用场景
参考资源:
- OpenHands 官方仓库:https://github.com/All-Hands-AI/OpenHands
- kanemx「Serverless Multi-Tenant OpenHands on AWS with Fargate」:https://kane.mx
- OpenHands Action(GitHub Marketplace):https://github.com/marketplace/actions/open-hands-action
- AWS Spot 实例最佳实践:https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-best-practices.html