OpenAI Agents SDK RunState: 중복 부작용 없이 도구 승인 재개하기
요약
OpenAI Agents SDK의 RunState를 사용하여 인간의 승인이 필요한 실행을 지속하고 재개하는 방법을 다룹니다. RunState가 내구성은 제공하지만 '정확히 한 번 실행'을 보장하지 않으므로, 중복 부작용을 방지하기 위한 설계 전략을 제시합니다.
핵심 포인트
- RunState는 실행 스냅샷일 뿐 정확히 한 번 실행(exactly-once)을 보장하지 않음
- 동일한 승인 상태를 재실행할 경우 중복 부작용이 발생할 수 있음
- 상태 직렬화 시 토큰 등 민감 정보가 포함되지 않도록 엄격한 직렬화 도구 사용 권장
- 프로덕션 환경에서는 실행 스냅샷, 권한 부여, 멱등성 원장을 분리하여 설계해야 함
OpenAI Agents SDK의 RunState는 인간의 승인을 기다리는 실행(run)을 지속(persist)할 수 있으며, 원래 프로세스를 종료한 뒤 다른 워커에서 승인되거나 거부된 실행을 재개할 수 있습니다.
이는 내구성(durability) 문제를 해결합니다. 하지만 정확히 한 번 실행(exactly-once execution) 문제를 해결하지는 않습니다.
재현 가능한 openai-agents==0.18.3 실험 환경에서:
- 승인 전 부작용 (side effects): 0
- 프로세스 간 승인 및 재개 후 부작용: 1
- 거부 후 부작용: 0
- 동일한 승인된 상태를 두 개의 워커에서 재실행(replaying)한 후 부작용: 2
- 멱등성 원장 (idempotency ledger) 추가 후 부작용: 1
재현 설정 (Reproduction setup)
Python 3.10.2
openai-agents 0.18.3
RunState schema 1.12
...
결정론적 모델(deterministic model)은 항상 하나의 deploy_release 도구 호출(tool call)을 생성한 다음, 도구 결과(tool result)를 받은 후 최종 메시지를 반환합니다. 이를 통해 SDK 중단, 직렬화(serialization) 및 재개 동작을 모델의 변동성 및 네트워크 동작으로부터 격리합니다.
부작용 발생 전 일시 중지
from agents import function_tool
@function_tool(needs_approval=True)
...
result = await Runner.run(
agent,
"Deploy release 2026.07.24",
...
needs_approval=True는 도구 실행 시점에 러너(runner)에 의해 강제됩니다. 모델은 이미 호출을 제안하고 인자(arguments)를 생성했지만, Python 함수는 아직 실행되지 않은 상태입니다.
지속 가능한 컨텍스트만 직렬화하기
RunContextWrapper.context는 RunState 직렬화에 참여합니다. 따라서 토큰을 포함하는 일반 매핑(plain mapping)은 해당 토큰을 상태 블롭(state blob)에 기록할 수 있습니다.
식별자(identifiers)를 유지하고 나중에 런타임 의존성(runtime dependencies)을 재구성하는 엄격한 직렬화 도구(strict serializer)를 사용하세요:
def context_serializer(context: AppContext):
return {"tenant_id": context.tenant_id}
...
복구(restore) 시에는 블롭(blob) 대신 비밀 관리자(secret manager)에서 자격 증명(credentials)을 가져오십시오.
애플리케이션은 여전히 승인 기록이 필요합니다
RunState는 SDK 실행 스냅샷(snapshot)입니다. 이는 다음과 같은 사항에 대한 유일한 기록이 되어서는 안 됩니다:
- 누가 작업을 요청했는지
- 누가 이를 승인할 권한이 있는지
- 테넌트(tenant) 및 리소스 경계
- 만료 및 취소
- 불변의 도구 인자 다이제스트 (immutable tool argument digest)
- 큐 전달(queue delivery) 및 워커 임대(worker leases)
- 외부 부작용 (external side effect)이 이미 발생했는지 여부
프로덕션 설계에서는 다음을 분리합니다:
run_state_blob SDK 실행 스냅샷 (execution snapshot)
approval_request 권한 부여 (authorization) 및 생명주기 (lifecycle)
idempotency_ledger 외부 부작용 소유권 및 결과 재사용
승인 UI는 불변의 검토 스냅샷을 표시해야 합니다. 도구 인자(tool arguments)를 정규화(canonicalize)하고 SHA-256 다이제스트를 저장합니다. 실행 전, 워커는 RunState에서 인자를 추출하고 다이제스트를 다시 계산합니다. 불일치가 발생하면 승인은 무효화됩니다.
다른 프로세스에서 재개하기
state = await RunState.from_json(
initial_agent,
stored_payload,
...
그런 다음 워커는 호환 가능한 에이전트를 다시 구축하고 실행을 재개합니다:
agent = AGENT_FACTORIES[record.app_state_version](runtime_dependencies)
state = await load_state(agent)
result = await Runner.run(agent, state)
from_json()은 여전히 초기 에이전트(initial Agent)를 필요로 합니다. 왜냐하면 JSON은 Python 함수, 도구 구현(tool implementations), 클라이언트, 데이터베이스 연결 또는 완전한 실행 가능한 그래프(executable graph)를 재생성할 수 없기 때문입니다.
중복 전달 실패 사례
워커 A가 승인된 상태를 소비하여 도구를 실행했습니다. 그 후 저는 워커 A의 새로운 결과를 사용하는 대신, 원래의 승인된 상태를 워커 B로 다시 재생(replay)했습니다.
{
"worker_a": "deployed:2026.07.24",
"worker_b": "deployed:2026.07.24",
...
SDK는 이것이 큐의 재전달(redelivery)인지, 확인 응답(acknowledgement)의 유실인지, 운영자의 재시도(retry)인지, 아니면 두 번째 정당한 재개(resume)인지 알 수 없습니다. 스냅샷에는 호출이 승인되었고 완료되지 않았다고 되어 있으므로, 이를 다시 실행하는 것은 일관성이 있습니다.
멱등성 원장(idempotency ledger)을 통한 실행 주장
connection.execute("BEGIN IMMEDIATE")
existing = connection.execute(
"SELECT result FROM idempotency_ledger WHERE idempotency_key = ?",
...
작업 키(operation key)는 애플리케이션에 의해 생성되어야 합니다. 예를 들어:
tenant_id + approval_id + logical_operation + target_resource
만약 다운스트림 API (downstream API)가 멱등성 키 (idempotency key)를 지원한다면, 동일한 값을 전달하십시오. 로컬 원장 (local ledger)만으로는 원격 작업은 성공했지만 로컬 결과는 기록되지 않은 충돌 구간 (crash window)을 완전히 닫을 수 없습니다. 그런 경우에는 다운스트림 시스템을 조회하거나, 해당 작업을 uncertain (불확실) 상태로 표시하고 조정 (reconciliation) 절차를 거쳐야 합니다.
프로덕션 체크리스트 (Production checklist)
- 승인 전 부작용 (side effects)이 없는지 확인
- 승인 권한 (approval authorization)을 RunState와 분리하여 유지
- 도구 인자 다이제스트 (tool argument digest)를 영구 저장
- 상태 블롭 (state blobs)을 암호화하고 테넌트 ACL (tenant ACLs) 적용
- 오래된 상태를 버전 관리되는 Agent 팩토리 (Agent factories)를 통해 라우팅
- 최소 한 번 전달 (at-least-once delivery)을 위한 큐 (queues) 설계
- 부작용을 일으키는 도구들을 멱등하게 (idempotent) 설계
- 거부 (rejection), 만료 (expiry), 취소 (revocation), 재전송 (redelivery), ACK 유실 (lost ACKs), 그리고 손상된 블롭 (corrupted blobs) 테스트
전체 기사에는 실험 파일, 상태 형태 (state shapes), SQL 스키마 (SQL schema), 실패 로그 (failure logs), 그리고 아키텍처 다이어그램 (architecture diagrams)이 포함되어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기