DigitalOcean

DigitalOcean

DigitalOcean을 자체 호스팅 샌드박스 제공자로 사용하는 방법을 안내하는 가이드예요. 웹훅 관리 또는 애플리케이션 관리 프로비저닝으로 DigitalOcean 샌드박스를 시작하고 executor를 연결하는 방법을 배워요.

출처: 문서

본문

OpenAI Cookbook의 application-managed 및 webhook-managed 예시를 참고하세요.

동작 방식

DigitalOcean의 Managed Agents Runtime Services(M.A.R.S.)가 codex-agentapi 이미지를 사용해 Firecracker microVM을 시작해요. 이 이미지는 Codex를 포함하고 executor를 시작하며, executor가 Agents API로 아웃바운드 연결해요.

OpenAI 이벤트에서 샌드박스를 시작하거나 재개하려면 webhook-managed 프로비저닝을 선택하고, 애플리케이션에서 제어하려면 application-managed 프로비저닝을 선택하세요. 대화형 퀵스타트에는 선택적 DigitalOcean CLI flow를 사용하세요. 연결 및 복구 동작은 Sandbox lifecycle을 참고하세요.

DigitalOcean Managed Agents는 공개 미리 보기(public preview) 상태예요. 접근 및 설정은 DigitalOcean's documentation을 참고하세요.

시작하기 전에

codex-agentapi에 접근할 수 있는 샌드박스 활성화 DigitalOcean 계정과 Agents API 접근이 있는 OpenAI 프로젝트가 필요해요.

애플리케이션 또는 CLI에는 OPENAI_API_KEY를 사용하세요. OPENAI_EXECUTOR_API_KEY를 environment key로 설정하세요. 환경 키만 CODEX_API_KEY로 샌드박스에 전달하세요.

웹훅 컨트롤러나 Python 애플리케이션에는 DIGITALOCEAN_TOKEN을 설정하고 비동기 지원(pydo[aio])이 있는 PyDo SDK 0.41.0 이상을 설치하세요. Agents API 요청에는 OpenAI SDK를 사용하세요. CLI 설치는 CLI 흐름에서만 필요해요.

웹훅 관리

  1. 저장된 에이전트를 만들고 그 ID를 OPENAI_AGENT_ID로 저장하세요. DigitalOcean App Platform에 이 ID, 세션 읽기용 OPENAI_API_KEY, DIGITALOCEAN_TOKEN, OPENAI_EXECUTOR_API_KEY로 HTTPS 웹훅 컨트롤러를 배포하세요.
  2. OpenAI 프로젝트에 컨트롤러의 /webhook 엔드포인트를 등록하세요. agent.session.action_required와 agent.session.failed를 활성화한 뒤 서명 시크릿을 OPENAI_WEBHOOK_SECRET으로 저장하고 컨트롤러를 재배포하세요.
  3. 동일한 OPENAI_AGENT_ID와 작업 디렉토리 /workspace로 session steps을 따르세요. 이벤트 스트림을 열고 입력을 보내세요. OpenAI가 environment_connection을 요청하면 컨트롤러가 서명을 검증하고 현재 세션을 검색하며 에이전트 ID와 필수 액션을 확인해요. DigitalOcean에서 mars-{session_id}를 조회하고, 일시 중지된 샌드박스를 재개하거나 활성 샌드박스가 없으면 만듭니다.
  4. agent.session.failed가 발생하면 세션을 다시 검색하고, 현재 세션 상태가 여전히 failed일 때만 그 샌드박스를 삭제하세요.

이미지가 executor를 세션의 환경에 연결해요. 여러분의 애플리케이션은 Agents API를 통해 입력을 보내고 결과를 스트리밍하며, 컨트롤러가 프로비저닝과 재연결을 처리해요. 중복 및 동시 전달을 처리하려면 세션별로 프로비저닝을 직렬화하세요. 컨트롤러 요구 사항은 webhook-managed lifecycle guidance를 참고하세요.

DigitalOcean CLI로 시도하기

CLI는 두 리소스를 모두 만들고 터미널에서 에이전트와 상호작용할 수 있게 해줘요. 웹훅 컨트롤러 없이 샌드박스를 직접 프로비저닝해요.

harness-runtime이 포함된 doctl 1.170.0 이상을 설치한 뒤 인증하세요:

doctl auth init

이 매니페스트를 environment.yaml로 저장하세요:

name: openai-codex-session
agent: codex-agentapi
config:
  agent:
    model: gpt-5.6-sol
    instructions: Work from the files in /workspace.
  environment:
    type: self_hosted
    workspace_directory: /workspace
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}

config 블록은 OpenAI create-session 요청이에요. CLI는 그 요청을 OPENAI_API_KEY로 인증하고, 응답에서 ${ENV_ID}를 채우며, 환경 키만 샌드박스에 전달해요. 해결된 매니페스트를 로그와 소스 컨트롤에 남기지 마세요. 도구가 필요한 모든 대상 위치를 egress에 추가하세요.

세션과 샌드박스를 만드세요:

doctl harness-runtime create --spec environment.yaml

이 명령은 기본적으로 최대 300초 동안 준비를 기다려요. 세션 세부 사항에서 OpenAI 세션 ID와 DigitalOcean 세션 ID를 저장한 뒤 연결하세요:

doctl harness-runtime launch openai-codex-session

에이전트에게 hello를 /workspace/hello.txt에 쓰고 다시 읽도록 요청하세요. Ctrl+D를 눌러 세션을 삭제하지 않고 분리하고, 같은 launch 명령을 실행해 다시 연결하세요. 작업이 끝나면 Cleanup을 따르세요.

애플리케이션 관리

세션 생성과 샌드박스 프로비저닝을 애플리케이션이 소유할 때 이 경로를 사용하세요. 먼저 OpenAI 세션을 만드세요:

자체 호스팅 세션 만들기

import OpenAI from "openai";
const client = new OpenAI();

const session = await client.beta.agents.sessions.create({
  agent: {
    model: "gpt-6-astra",
    instructions:
      "You are a helpful coding assistant. Write clean code and verify that it works.",
  },
  environment: {
    type: "self_hosted",
    workspace_directory: "/workspace",
  },
});

console.log(session);
from openai import OpenAI

client = OpenAI()

session = client.beta.agents.sessions.create(
    agent={
        "model": "gpt-6-astra",
        "instructions": "You are a helpful coding assistant. Write clean code and verify that it works.",
    },
    environment={"type": "self_hosted", "workspace_directory": "/workspace"},
)
print(session.to_json())
import (
	"context"
	"fmt"

	"github.com/openai/openai-go/v3"
)

ctx := context.Background()
client := openai.NewClient()
result, err := client.Beta.Agents.Sessions.New(ctx,
	openai.BetaAgentSessionNewParams{
		Agent: openai.BetaAgentSessionNewParamsAgent{
			Model:        openai.String("gpt-6-astra"),
			Instructions: openai.String("You are a helpful coding assistant. Write clean code and verify that it works."),
		},
		Environment: openai.EnvironmentParamUnion{
			OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{WorkspaceDirectory: "/workspace"},
		},
	})
if err != nil {
	panic(err)
}
fmt.Println(result)
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.beta.agents.EnvironmentParam;
import com.openai.models.beta.agents.sessions.SessionCreateParams;

OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var result =
    client
        .beta()
        .agents()
        .sessions()
        .create(
            SessionCreateParams.builder()
                .agent(
                    SessionCreateParams.Agent.builder()
                        .model("gpt-6-astra")
                        .instructions(
                            "You are a helpful coding assistant. Write clean code and verify"
                                + " that it works.")
                        .build())
                .environment(
                    EnvironmentParam.SelfHosted.builder()
                        .workspaceDirectory("/workspace")
                        .build())
                .build());
System.out.println(result);
require "openai"

client = OpenAI::Client.new
result = client.beta.agents.sessions.create(
  agent: {
    model: "gpt-6-astra",
    instructions: "You are a helpful coding assistant. Write clean code and verify that it works."
  },
  environment: {
    type: "self_hosted",
    workspace_directory: "/workspace"
  }
)
puts result

Connect a sandbox에 설명된 대로 session.id와 환경 ID를 저장하세요. 이 샌드박스 전용 매니페스트를 sandbox.yaml로 저장하세요. 에이전트 설정은 이미 OpenAI로 전송됐어요:

agent: codex-agentapi
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}
  1. DIGITALOCEAN_TOKEN으로 pydo.aio.Client를 만들고 client.agents.create_session을 호출하세요. params.openai_session_id를 OpenAI 세션 ID로, body.manifest를 sandbox.yaml의 내용으로, body.variables를 ENV_ID와 OPENAI_EXECUTOR_API_KEY에서 그 값으로의 매핑으로 설정하세요. 반환된 DigitalOcean session_id를 저장하세요.
  2. 이벤트 스트림을 열고 입력을 보내 에이전트에게 /workspace/hello.txt를 쓰고 읽도록 요청하세요. 입력은 executor가 연결될 때까지 기다려요. 연결 이벤트와 완료된 턴을 확인하고, 에이전트의 출력에서 도구 실패를 검사하세요.
  3. 상대 경로 hello.txt로 workspace_download를 사용해 파일을 검색하세요. 후속 턴을 위해 두 리소스를 모두 유지하거나 정리하세요.

애플리케이션에서 제한된 setup 및 실행 타임아웃을 사용하고 연결 실패를 처리하세요. 애플리케이션이나 CLI가 직접 관리하는 세션에는 프로비저닝 웹훅 핸들러를 연결하지 마세요.

정리

필요한 파일을 저장한 뒤 OpenAI 세션을 삭제하고 DigitalOcean 샌드박스를 파괴하세요. 세션 삭제는 웹훅을 발생시키지 않으므로 두 작업을 모두 수행하고 정리 실패를 보고하세요.

PyDo에서는 DigitalOcean 세션 ID로 client.agents.destroy_session을 호출하세요. CLI에서는 그 ID나 샌드박스 이름을 전달하세요:

doctl harness-runtime remove openai-codex-session

웹훅 컨트롤러를 삭제하기 전에 OpenAI 웹훅 등록을 제거하세요.

참고 자료

더 알아보기 (Learn more)

관련 문서: 자체 호스팅 샌드박스와 샌드박스 수명 주기를 참고하세요.