Skip to main content

AI Agent Relay Protocol

이 문서는 외부 ai-node-agentAWS public relay와 통신하는 표준 계약을 정의합니다.

목표는 다음 세 가지입니다.

  • 외부 노드가 backend 주소나 AWS private Redis 주소를 직접 알지 않도록 한다.
  • local backend, dev backend가 기존처럼 동일한 shared Redis를 보면서도, 외부 노드는 relay 하나만 바라보게 만든다.
  • 고빈도 실시간 이벤트와 저빈도 확정 상태를 분리하여, 네트워크 비용과 구현 복잡도를 동시에 낮춘다.

1. 전제

  • 외부 ai-node-agent는 shared Redis에 직접 연결하지 않는다.
  • 외부 ai-node-agent는 단일 public relay 주소만 알고 있다.
  • relay는 외부 노드로부터 받은 메시지를 shared Redis의 기존 키/채널 모델에 맞게 정규화한다.
  • local backend, dev backend는 기존처럼 shared Redis를 읽는다.

핵심 구조는 아래와 같습니다.

ai-node-agent
-> public relay (WebSocket + HTTP)
-> shared Redis
-> local/dev backend

2. 역할 분리

relay와의 통신은 성격에 따라 세 가지 plane으로 나눕니다.

2.1 Realtime Plane

  • 대상: heartbeat, progress, executing, node.monitor
  • 특성: 고빈도, 최신값 위주, 일부 중복 허용
  • 권장 transport: WebSocket

2.2 Durable Status Plane

  • 대상:
    • 1차: post_processing, failed, processing_failed
    • 2차: completed, artifact_registered
  • 특성: 저빈도, 유실되면 안 됨, 멱등성 중요, DB authoritative
  • 권장 transport: HTTP

중요한 점은 durable status plane이 단순히 Redis에 상태를 쓰는 통로가 아니라, backend 도메인 서비스가 DB 상태 전이를 확정하는 plane이라는 점입니다.

여기서 artifact_registered는 개념적 이름이고, 현재 코드베이스의 wire-level 이벤트명은 creation_result_added입니다. 즉 문서에서 말하는 artifact_registered결과물 1개가 DB/스토리지 기준으로 등록된 순간을 뜻하며, 현재 구현에서는 creation_result_added로 fan-out 됩니다.

2.3 Control Plane

  • 대상: CANCEL_JOB, WARMUP_UPDATE, 향후 DRAIN_NODE, PAUSE_NODE
  • 특성: relay가 agent에게 push 해야 함
  • 권장 transport: WebSocket

3. 단일 허브 원칙

현재 단계에서는 Redis나 relay를 물리적으로 여러 개로 나누지 않습니다.

  • Redis: 단일 shared Redis 유지
  • Relay: 단일 public relay 유지

대신 논리적으로만 분리합니다.

  • node presence
  • job events
  • control commands

이유는 다음과 같습니다.

  • 외부 노드 설정을 단순하게 유지할 수 있다.
  • local/dev backend가 동일한 중앙 상태를 그대로 볼 수 있다.
  • heartbeat/progress/control을 서로 다른 처리 우선순위로 다룰 수 있다.

4. Transport 선택 기준

4.1 WebSocket으로 보내는 것

  • heartbeat
  • progress
  • executing
  • node.monitor
  • relay -> agent 방향의 command
  • agent -> relay 방향의 command_ack

4.2 HTTP로 보내는 것

  • post_processing
  • failed
  • processing_failed
  • completed
  • artifact_registered (creation_result_added와 동일 개념)
  • 재시도와 멱등성이 필요한 저빈도 이벤트

5. WebSocket 계약

5.1 연결

agent는 부팅 시 relay에 agent 전용 WebSocket 1개를 엽니다.

연결 시 최소한 아래 정보를 식별 가능하게 전달해야 합니다.

  • nodeId
  • agentVersion
  • capabilities
  • environments
  • 인증 토큰

예시 개념:

{
"type": "hello",
"nodeId": "api-node-agent-01",
"agentVersion": "2026.04.08",
"capabilities": ["image", "video", "monitoring"],
"environments": ["local", "dev"]
}

relay는 연결 성공 시 ack를 반환합니다.

{
"type": "hello_ack",
"nodeId": "api-node-agent-01",
"sessionId": "relay-session-uuid"
}

5.2 WebSocket 메시지 타입

heartbeat

노드 생존 여부 및 현재 기본 상태를 나타냅니다.

{
"type": "heartbeat",
"nodeId": "api-node-agent-01",
"status": "waiting",
"occurredAt": "2026-04-08T10:15:30.000Z"
}

job_event

고빈도 실시간 작업 이벤트입니다.

{
"type": "job_event",
"eventType": "progress",
"env": "dev",
"creationId": "uuid",
"userId": "uuid",
"nodeId": "api-node-agent-01",
"sequence": 12,
"occurredAt": "2026-04-08T10:15:31.200Z",
"payload": {
"progress": 42,
"max": 100,
"comfyPromptId": "uuid",
"node": "ksampler"
}
}

eventType 후보:

  • progress
  • executing
  • execution_success
  • preview

node_monitor

노드 자원 상태를 전달합니다.

{
"type": "node_monitor",
"nodeId": "api-node-agent-01",
"occurredAt": "2026-04-08T10:15:32.000Z",
"payload": {
"cpuPercent": 21.5,
"ramPercent": 43.1,
"gpuUtilPercent": 88,
"gpuMemoryPercent": 61.2,
"diskPercent": 57.0
}
}

command_ack

relay가 내려준 명령을 agent가 수신/처리했음을 알립니다.

{
"type": "command_ack",
"commandId": "uuid",
"nodeId": "api-node-agent-01",
"result": "accepted",
"occurredAt": "2026-04-08T10:15:35.000Z"
}

command

relay가 agent에게 보내는 제어 메시지입니다.

{
"type": "command",
"commandId": "uuid",
"commandType": "CANCEL_JOB",
"env": "dev",
"creationId": "uuid",
"payload": {}
}

6. HTTP 계약

HTTP는 저빈도 확정 상태를 위해 사용합니다.

현재 권장 구현은 다음 두 단계로 나뉩니다.

  • 1차: ai-node-agent가 직접 생성하는 durable 상태를 relay HTTP로 전송
    • post_processing
    • failed
    • processing_failed
  • 2차: post-processing/backend가 확정하는 상태를 동일한 durable plane으로 통합
    • completed
    • artifact_registered (creation_result_added)

권장 엔드포인트는 다음과 같습니다.

POST /v1/agent-relay/job-status

작업의 durable 상태 전이를 relay에 전달합니다.

{
"env": "dev",
"creationId": "uuid",
"userId": "uuid",
"nodeId": "api-node-agent-01",
"status": "post_processing",
"eventType": "post_processing",
"occurredAt": "2026-04-08T10:16:02.000Z",
"idempotencyKey": "creationId:status:sequence"
}

status 후보:

  • post_processing
  • failed
  • processing_failed
  • completed

eventTypestatus보다 더 세부적인 실패/후처리 단계를 표현할 때 사용합니다. 예:

  • processing_failed
  • post_processing_upload
  • post_processing_sync

POST /v1/agent-relay/artifacts

S3 원본 경로나 결과 metadata를 relay에 전달할 때 사용합니다.

{
"env": "dev",
"creationId": "uuid",
"nodeId": "api-node-agent-01",
"artifactType": "raw_output",
"s3Path": "s3://bucket/key",
"occurredAt": "2026-04-08T10:16:03.000Z",
"idempotencyKey": "creationId:artifactType:s3Path"
}

7. Durable Status 처리 원칙

durable status plane은 Redis 업데이트용 HTTP endpoint가 아닙니다. 최종 목표는 relay -> backend domain service -> DB -> Redis projection 흐름을 고정하는 것입니다.

7.1 1차 구현 원칙

  • ai-node-agentpost_processing, failed, processing_failed를 HTTP로 relay에 전송합니다.
  • relay는 backend durable service로 전달합니다.
  • backend durable service는 기존 도메인 서비스와 연결하여 DB 상태 전이를 수행합니다.
  • DB 반영 후에만 Redis prompt_progress:*ai-comfyui-events를 projection으로 갱신합니다.

7.2 2차 구현 원칙

  • completed는 결과 개수/후처리 완료 조건을 기준으로 backend가 확정합니다.
  • artifact_registered는 post-processing worker 또는 backend 내부 서비스가 durable plane으로 전달합니다.
  • 현재 구현에서는 artifact_registered라는 새 wire 이벤트를 추가하지 않고, 기존 creation_result_added를 canonical event로 유지합니다.
  • 결과 일부가 등록되는 동안 Creation.statusPOST_PROCESSING을 유지하고, 필요한 결과가 모두 등록되었을 때만 COMPLETED로 전이합니다.
  • completed는 1차부터 ai-node-agent가 직접 전송하는 상태로 가정하지 않습니다.

7.3 DB authoritative 원칙

  • Creation.status가 최종 상태의 authoritative source입니다.
  • Redis의 prompt_progress:*는 프론트 실시간 표시용 projection입니다.
  • Redis Pub/Sub는 fan-out 수단이지 durable source가 아닙니다.

7.4 DB 과부하 회피 원칙

  • 모든 progress tick을 DB에 쓰지 않습니다.
  • heartbeat를 DB에 주기적으로 쓰지 않습니다.
  • 같은 상태에 대한 중복 HTTP 요청은 멱등하게 처리합니다.
  • updatedAt touch는 상태 전이가 필요한 시점에만 최소화합니다.
  • 결과물 저장은 이미 필요한 creation_results insert/update로 한정합니다.

7.5 현재 구현 상태

  • 1차 durable plane:
    • post_processing, failed, processing_failedai-node-agent -> relay HTTP -> backend domain service -> DB -> Redis projection으로 구현되었습니다.
  • 2차 durable plane:
    • completed는 backend CreationResultsService가 결과 개수 기준으로 확정합니다.
    • 결과 일부 등록 시 상태는 PROCESSING으로 되돌아가지 않고 POST_PROCESSING을 유지합니다.
    • 결과 등록 fan-out 이벤트는 현재 creation_result_added 이름으로 유지합니다.
  • 남은 작업:
    • post-processing worker를 포함한 end-to-end 검증

8. 공통 이벤트 envelope

WebSocket/HTTP 모두 아래 공통 필드를 유지하는 것이 좋습니다.

  • env
  • nodeId
  • creationId (job scoped인 경우)
  • userId (가능하면 포함)
  • occurredAt
  • sequence
  • idempotencyKey
  • payload

이 필드를 유지해야 relay가 다음 처리를 안정적으로 수행할 수 있습니다.

  • 중복 제거
  • 순서 역전 방지
  • 늦게 도착한 progress discard
  • env 라우팅
  • 장애 분석

9. Redis Adapter 규칙

relay는 shared Redis의 기존 모델을 최대한 유지합니다.

9.1 Node Presence

  • agent_heartbeat:{nodeId}
  • nodes:known
  • nodes:active:last_seen

9.2 Job State / Lookup

  • prompt_progress:{creationId}
  • comfy_id_map:{comfyPromptId} 필요 시 유지
  • prompt_outputs:{creationId} 필요 시 유지

9.3 Pub/Sub

  • ai-comfyui-events

relay는 외부 agent가 보낸 이벤트를 정규화한 뒤 ai-comfyui-events로 발행합니다. backend는 기존처럼 이 채널을 구독하여 프론트엔드에 fan-out 합니다.

durable status plane의 경우에도 Redis는 projection 역할을 유지합니다. 즉, DB 상태가 먼저 갱신된 뒤 Redis projection이 뒤따르는 것을 원칙으로 합니다.

10. Multi-backend 동작 원칙

이 시스템은 local backenddev backend가 동시에 존재하는 것을 전제로 합니다.

공통으로 보여야 하는 것

  • node presence
  • heartbeat
  • node.monitor
  • 현재 노드가 waiting인지 processing인지

작업별로 분리되어야 하는 것

  • 특정 creationId의 progress
  • completed, failed, post_processing
  • artifact metadata

즉, relay/Redis 모델은 아래 원칙을 지켜야 합니다.

  • node plane: 양쪽 backend가 같이 본다.
  • job plane: env, creationId, userId 기준으로 각 backend가 필터링한다.

11. 신뢰성 규칙

relay는 단순 프록시가 아니라 정규화 계층입니다. 최소한 아래 규칙이 필요합니다.

11.1 Heartbeat

  • latest timestamp 기준 갱신
  • TTL 관리
  • heartbeat path는 가장 가볍고 빠르게 처리

11.2 Progress

  • sequence 또는 occurredAt 기준 최신값만 반영
  • 늦게 도착한 progress는 버린다
  • 동일 progress 값은 dedup 가능

11.3 Durable Status

  • idempotencyKey 기반 재시도 허용
  • completed 이후 늦게 온 progress는 무시
  • failedcompleted의 충돌 규칙을 명시
  • 같은 Creation.status에 대한 반복 업데이트는 no-op 처리
  • DB 상태 변경과 Redis projection 발행의 순서를 고정

11.4 Reconnect

  • agent는 relay 연결이 끊기면 자동 재연결
  • 재연결 후 hello와 현재 상태를 재전송
  • 필요 시 마지막 미반영 durable event를 재시도

12. 보안 원칙

  • relay는 HTTPS/WSS만 허용
  • 최소한 agent 전용 토큰 인증 필요
  • 가능하면 노드별 자격 증명 분리
  • rate limit 적용
  • 필요 시 IP allowlist 또는 mTLS 검토

중요한 점은, relay는 public endpoint이지만 Redis는 여전히 private이어야 한다는 것입니다.

13. 구현 우선순위

Phase 1

  • WebSocket: heartbeat, progress, node.monitor
  • HTTP: post_processing, failed, processing_failed
  • relay -> Redis adapter
  • backend는 기존 Redis 소비 로직 유지
  • realtime plane은 이 단계에서 완료로 본다

Phase 2

  • durable status를 DB authoritative 모델로 고정
  • completed, artifact_registered(creation_result_added)를 backend/post-processing 흐름에 통합
  • Redis는 projection/cache 역할로 정리

Phase 3

  • relay -> agent control command push
  • CANCEL_JOB, WARMUP_UPDATE
  • command_ack

Phase 4

  • dedup / backpressure / batching 고도화
  • preview 이미지 처리 최적화
  • extreme scale 시 relay 내부 worker 분리 검토

14. 결론

이 프로토콜의 핵심은 다음 다섯 가지입니다.

  1. 외부 노드는 backend 주소와 Redis 주소를 알 필요가 없다.
  2. shared Redis는 유지하되, 외부 노드는 Redis에 직접 붙지 않는다.
  3. heartbeat/progress/controldurable status는 transport를 분리한다.
  4. durable status의 최종 진실은 DB이고, Redis는 projection으로 유지한다.
  5. 물리적 분리보다 단일 relay + 단일 shared Redis + 논리적 역할 분리를 우선한다.