라이브러리로 돌아가기
A2A

Hermes Agent에 A2A 배포하기: Docker Gateway 참조 스택

최종 업데이트: 2026年7月23日

프로덕션에서 A2A를 실행한다는 것은 서로 통신하도록 설계되지 않은 두 프로토콜을 연결하고 — 토큰을 떨어뜨리지 않고 인증, 테넌트 격리 및 스트리밍을 처리하는 게이트웨이 뒤에서 그렇게 하는 것을 의미합니다. 이 참조 스택은 A2A 배포를 차단하는 세 가지 문제를 해결합니다: 프로토콜 변환(JSON-RPC 2.0에서 OpenAI 호환 chat completions), 스트리밍 조정(A2A 아티팩트 SSE에서 Hermes 실행 이벤트 SSE), 다중 테넌트 보안(모든 쿼리에서 PostgreSQL Row-Level Security). 다른 프레임워크의 에이전트가 서로 작업을 위임해야 하는 경우, 여기의 패턴이 배포 경로입니다.

핵심 요약

  • 3계층, 1개 Docker Compose 스택 — SilvaEngine Gateway가 전송과 인증을 처리하고, A2A Daemon Engine이 프로토콜 로직을 처리하며, HermesAgentHandler가 Hermes Agent의 OpenAI 호환 API로 브리징합니다. docker-a2a-hermes-agent-gateway 저장소가 세 가지를 모두 패키징합니다.
  • 한 포트에 5개 프로토콜 표면 — JSON-RPC 2.0, GraphQL, SSE 스트림, SSE 푸시, Agent Card 발견이 모두 포트 8765의 단일 게이트웨이 뒤에서 JWT 또는 AWS Cognito 인증과 함께 제공됩니다.
  • PostgreSQL 행 수준 보안이 테넌트 격리를 강제partition_key = "{endpoint_id}#{Part-Id}" 복합 키가 4개 A2A 테이블 모두에 RLS 정책을 통해 데이터베이스 수준에서 강제되며, 애플리케이션 코드에서만 처리되지 않습니다.
  • A2A SDK v2의 단일 메시지 제약이 스트리밍 아키텍처를 형성 — 브리지는 토큰 청크를 SSE에 실시간으로 내보내고 스트림 완료 후 누적된 단일 Message를 SDK EventQueue로 내보내어 InvalidAgentResponseError를 회피합니다.
  • 5개 스크립트에 걸친 15개 E2E 테스트 검사 — 비스트리밍 스모크 테스트부터 HTTP 폴백 검증을 포함한 전체 SSE 스트리밍 파이프라인까지, 모두 pip install requests로 실행 가능합니다.

Agent2Agent Protocol (A2A)은 AI 에이전트가 JSON-RPC 2.0을 통해 서로를 발견하고, 위임하고, 작업을 스트리밍하는 방법을 정의합니다. Hermes Agent(Nous Research 제작)는 /v1/chat/completions에서 OpenAI 호환 API 서버를, /v1/runs/v1/runs/{id}/events에서 runs 기반 SSE 스트리밍 인터페이스를 노출합니다. 둘은 같은 언어를 사용하지 않습니다. A2A는 구조화된 파트와 함께 message/send를 보내고, Hermes는 채팅 완성 페이로드를 받습니다. A2A는 SSE를 통해 작업 아티팩트를 스트리밍하고, Hermes는 run 이벤트를 통해 토큰 델타를 스트리밍합니다.

docker-a2a-hermes-agent-gateway 저장소는 단일 컨테이너 이미지와 docker compose 스택에서 그 간극을 브리징합니다. a2a_daemon_engine 모듈만 등록된 SilvaEngine Gateway를 실행하여 전체 A2A 프로토콜 표면을 노출하고 HTTP + SSE를 통해 Hermes Agent API 서버 인스턴스로 A2A 작업을 브리징합니다. 상태는 테넌트 격리를 위한 행 수준 보안을 갖춘 번들 PostgreSQL 백엔드에 영속화됩니다.

이 글은 3계층 아키텍처, A2A 클라이언트에서 Hermes로 그리고 되돌아오는 요청 라이프사이클, 구성 및 배포 패턴, 그리고 프로덕션에서 Hermes Agent에 A2A를 운영하기 위한 운영 관심사를 매핑합니다. 이는 참조 배포 워크스루입니다 — 일반적인 패턴(임의 에이전트 프레임워크로의 게이트웨이 매개 A2A 브리지)이 주제이며, Docker 스택이 구현된 사례입니다.

3계층 아키텍처

이 스택은 관심사를 각각 별도 컴포넌트가 소유하는 세 계층으로 분리합니다:

Hermes Agent의 A2A — 3계층 스택 docker-a2a-hermes-agent-gateway 참조 배포 1 A2A 클라이언트 JSON-RPC 2.0을 사용하는 임의 에이전트 또는 애플리케이션 message/send · tasks/get · SSE 2 SilvaEngine Gateway 전송 · 인증 · 라우팅 · SSE 클라이언트 라이프사이클 JWT / Cognito 인증 Part-Id 테넌트 라우팅 SSE 클라이언트 레지스트리 속도 제한 3 A2A Daemon Engine 프로토콜 로직 · 작업 상태 머신 · 핸들러 디스패치 Agent Card 서빙 작업 라이프사이클 HermesAgentHandler 이중 경로 스트리밍 Hermes Agent API 서버 OpenAI 호환 · SSE runs /v1/chat/completions /v1/runs + /events PostgreSQL 영속성 · RLS 테넌트 격리 a2a_agents / tasks messages / settings 게이트웨이가 유일한 상시 실행 서비스 — Hermes와 PostgreSQL은 프로파일 게이트 형제 — ideabosque.com/library

게이트웨이가 유일한 상시 실행 서비스입니다. Hermes와 PostgreSQL 모두 프로파일 게이트 형제입니다 — 자체 완결형 스택을 위해 번들로 포함하거나, 프로파일을 끄고 HERMES_API_URLPG_HOST를 외부 인스턴스로 지정하세요. 이는 프로덕션에서 중요합니다: VPC에서 게이트웨이를 실행하고 관리형 Postgres(RDS, Cloud SQL)와 다른 GPU 노드에서 실행되는 Hermes 인스턴스를 가리키도록 할 수 있습니다.

계층 1: SilvaEngine Gateway — 전송과 인증

SilvaEngine Gateway는 설치된 모듈에 인증된 인-프로세스 접근을 제공하는 FastAPI 게이트웨이입니다. 구성 가능한 YAML 라우트 매니페스트를 통해 모듈 GraphQL과 REST 라우트를 노출합니다 — 새 모듈 추가는 매니페스트 변경만으로 가능하며 게이트웨이 Python 코드는 전혀 필요 없습니다. 이 스택에서는 A2A Daemon Engine만 등록되어 있습니다.

게이트웨이가 소유하는 것:

  • 인증GATEWAY_AUTH_PROVIDER로 선택되는 로컬 JWT(HS256) 또는 AWS Cognito(RS256 + JWKS)
  • 라우팅 — YAML 매니페스트가 URL 경로를 모듈 디스패치 함수로 매핑
  • SSE 클라이언트 라이프사이클sse_manager가 모듈별로 해결되고 장기 연결 클라이언트 연결을 관리
  • 속도 제한 — IP당 인메모리 속도 제한(GATEWAY_RATE_WINDOW초당 GATEWAY_RATE_LIMIT 요청)
  • 스레드 풀 디스패치 — 동기 모듈 디스패치 함수가 구성 가능한 스레드 풀에서 실행(GATEWAY_DISPATCH_WORKERS, Docker 이미지에서 기본값 32)

게이트웨이는 URL 경로 세그먼트와 Part-Id 요청 헤더에서 partition_key = "{endpoint_id}#{Part-Id}"를 구성합니다. 모든 테넌트 범위 요청은 해당 헤더를 필요로 합니다. /{ep}/.well-known/agent-card.json의 Agent Card 엔드포인트는 A2A 스펙에 따라 공개(인증 없음)이지만, 카드가 파티션별로 해결되므로 여전히 Part-Id가 필요합니다.

계층 2: A2A Daemon Engine — 프로토콜 로직

a2a_daemon_engine은 독립 실행형 서비스가 아닙니다. main.pydeploy()를 통해 등록된 게이트웨이 모듈로 로드되며, 세 개의 게이트웨이 대면 엔트리 포인트를 선언합니다:

엔트리 포인트 게이트웨이 라우트 메서드 목적
a2a_core_graphql POST /{ep}/a2a_core_graphql POST 에이전트, 작업, 메시지, 설정에 대한 GraphQL CRUD
a2a POST /{ep}/a2a POST A2A JSON-RPC 프로토콜(message/send, tasks/get, tasks/cancel, tasks/list)
sse_message POST /{ep}/a2a_sse POST A2A JSON-RPC 메시지 + SSE 클라이언트로 푸시

게이트웨이는 추가로 SSE 스트림을 위해 GET /{ep}/a2a_sse를 노출합니다. 데몬은 자체 포트에서 수신하거나 프로덕션에서 자체 HTTP 서버를 실행하지 않습니다. 모든 전송, 인증, SSE 클라이언트 라이프사이클은 게이트웨이가 소유합니다.

데몬이 제공하는 것:

  • A2A SDK v1.0 — 공식 A2A SDK 서버 패턴 기반의 HTTP를 통한 JSON-RPC
  • 공개 Agent Card — ETag와 Last-Modified 지원을 포함한 /.well-known/agent-card.json
  • 작업 상태 머신submittedworkinginput-required | completed | failed | canceled
  • 이중 백엔드 영속성 — DynamoDB(PynamoDB) 또는 PostgreSQL(SQLAlchemy + Alembic). Docker 이미지는 PostgreSQL을 강제합니다.
  • 다중 테넌트 격리 — PostgreSQL 행 수준 보안을 포함한 복합 파티션 키({endpoint_id}#{part_id})
  • 플러그형 LLM 핸들러 — 에이전트 레지스트리의 에이전트별 module_name / class_name 선택

계층 3: HermesAgentHandler — 브리지

Hermes 브리지 핸들러(a2a_daemon_engine/handlers/a2a_hermes_handler.py)는 유일한 프레임워크별 코드입니다. ask_model() 인터페이스를 구현합니다: A2A 메시지 파트와 컨텍스트를 수락하고, Hermes Agent API 서버를 호출하고, 응답을 다시 A2A 메시지 파트로 변환하며, 스트리밍의 경우 토큰 델타를 SSE 채널로 전달합니다.

핸들러는 두 가지 실행 모드를 지원합니다:

비스트리밍은 Hermes API 서버의 OpenAI 호환 엔드포인트인 POST /v1/chat/completions에 매핑됩니다. 요청은 변환된 A2A 메시지 파트를 채팅 완성 페이로드로 전달합니다. Hermes가 요청을 처리하고 단일 응답을 반환합니다. 핸들러는 응답을 ROLE_AGENT를 가진 A2A Message로 변환하고 SDK EventQueue로 내보냅니다. 클라이언트는 전체 에이전트 텍스트가 포함된 단일 JSON-RPC 응답을 받습니다.

스트리밍은 run을 생성하기 위해 POST /v1/runs에 매핑되고, 이후 GET /v1/runs/{id}/events로 SSE 연결을 엽니다. Hermes는 이벤트가 발생하는 대로 스트리밍합니다: 토큰 델타(message.delta), 추론 메타데이터(reasoning.available), 도구 호출/결과 알림, 승인 요청(approval.required), 라이프사이클 이벤트(run.created, run.completed, run.failed). 핸들러는 백그라운드 스레드에서 드레인 루프를 실행합니다. 각 message.delta 이벤트는 연결된 클라이언트에 실시간 전달을 위해 게이트웨이의 SSE 매니저로 푸시됩니다. run.completed가 도착하면 누적된 텍스트가 단일 A2A Message로 SDK EventQueue로 내보내집니다.

요청 라이프사이클

stream=true를 포함한 단일 message/send는 8단계를 거칩니다:

1. 클라이언트      POST /{ep}/a2a  {jsonrpc, method:"message/send", params}
                   헤더: Authorization: Bearer *** Part-Id: 
2. 게이트웨이      인증(로컬 JWT / Cognito) → routes.yaml에서 라우트 매칭
                   → partition_key = "{ep}#{Part-Id}"
3. a2a_daemon     dispatch_a2a → A2ADaemonExecutor
                   → resolve_agent(): 에이전트 메타데이터(DB) > 설정 딕셔너리 > Config(환경변수)
4. 핸들러          HermesAgentHandler (A2A_AI_AGENT_MODULE / _CLASS)
5. Hermes          POST {HERMES_API_URL}/v1/runs   (Bearer HERMES_API_KEY)
                   GET  {HERMES_API_URL}/v1/runs/{id}/events   (SSE)
6. 브로드캐스트    토큰 청크 → GET /{ep}/a2a_sse의 구독자
7. 영속화          작업 + 메시지 PostgreSQL에 기록(a2a_* 테이블, RLS 범위)
8. 응답            누적된 응답은 HTTP JSON-RPC 결과에도 반환

8단계가 중요합니다: 스트리밍 중에도 HTTP 응답이 전체 응답을 전달합니다. SSE 프레임을 놓친 클라이언트도 HTTP 응답으로 폴백할 수 있습니다. E2E 테스트 스위트(test_hermes_sse_live.py)는 6단계에서 이 폴백을 명시적으로 검증합니다.

에이전트 해결 우선순위

구성 해결은 우선순위 체인을 따릅니다: 에이전트 메타데이터(DB) → 설정 딕셔너리 → Config 기본값(환경변수). 에이전트별 재정의가 전역 기본값보다 우선합니다. 두 에이전트가 다른 프레임워크를 가리킬 수 있습니다 — 추론 중심 작업에는 Hermes로, 워크플로 오케스트레이션 작업에는 다른 핸들러로 — 그리고 A2A 프로토콜 표면은 호출 에이전트에 동일하게 보입니다.

Hermes 기반 에이전트의 경우 a2a_agents 테이블에 저장된 메타데이터는 다음과 같습니다:

{
  "module_name": "a2a_daemon_engine.handlers.a2a_hermes_handler",
  "class_name": "HermesAgentHandler",
  "hermes_api_url": "http://127.0.0.1:8642",
  "hermes_api_key": "hermes-local-key",
  "hermes_model": "hermes-agent",
  "hermes_timeout": 300.0
}

환경변수 기본값(HERMES_API_URL, HERMES_API_KEY, HERMES_MODEL)은 DB 에이전트 레코드 없이 브리지가 Hermes에 도달할 수 있게 합니다 — a2a_ai_agent_utility.pyresolve_agent() 함수는 에이전트 레코드가 없을 때 환경변수로 폴백합니다. 즉, 에이전트를 등록하지 않고도 스택을 시작하고 message/send를 보낼 수 있으며, 환경 기본값을 사용해 Hermes로 라우팅됩니다.

A2A 상태 매핑

브리지는 Hermes SSE 이벤트를 A2A 작업 상태로 매핑합니다. 이 표가 브리지의 핵심입니다 — 모든 프레임워크 통합은 동등한 표를 생성합니다:

Hermes SSE 이벤트 A2A 작업 상태 브리지 동작
run.created(run_id 반환) WORKING 취소 지원을 위해 run_id 등록
message.delta WORKING 토큰 누적; 청크별 SSE로 내보냄
reasoning.available WORKING 추론 메타데이터 — 토큰 내보냄 없음
tool.call / tool.result WORKING 도구 실행 메타데이터만
approval.required INPUT_REQUIRED 승인 청크 내보냄; pending_approval 저장
run.completed COMPLETED 스트림 이벤트 설정; 최종 텍스트 누적
run.failed FAILED 오류 청크 내보냄; FAILED 상태 설정
POST /v1/runs/{id}/stop CANCELED tasks/cancel을 통한 외부 취소
POST /v1/runs/{id}/approval (run 계속) operation="approval_response"로 해결

a2a_daemon_engine 저장소의 HERMES_INTEGRATION.md 문서가 정확한 Hermes 이벤트 형식, 구성 키, 종단 간 흐름 세부 사항을 기록합니다.

에이전트 경계를 넘어선 인간 개입 승인

Hermes는 인간 개입 승인 게이트를 지원합니다 — 에이전트가 민감한 작업을 실행하기 위해 권한이 필요할 때 일시 중지하고 승인 요청을 내보냅니다. 브리지는 이를 A2A INPUT_REQUIRED 상태로 변환하여, 호출 에이전트(또는 인간 운영자)에게 입력이 필요함을 알립니다. 응답은 POST /v1/runs/{id}/approval을 통해 돌아오고 run이 계속됩니다.

여기서 브리지 패턴의 가치가 드러납니다. A2A는 INPUT_REQUIRED을 일급 작업 상태로 정의합니다. Hermes는 자체 승인 메커니즘을 가지고 있습니다. 브리지가 하나를 다른 것으로 매핑하며, 호출 에이전트 — 완전히 다른 프레임워크에서 실행되는 A2A 클라이언트 자체일 수도 있습니다 — 는 Hermes별 세부사항이 아닌 표준 프로토콜 상태 전환을 봅니다. 에이전트 위임 체인은 인간 승인이 필요한 단계(구매 승인, 데이터 접근 결정, 견적 승인)를 포함할 수 있으며, A2A 프로토콜이 그 게이트를 프레임워크 경계를 넘어 투명하게 전달합니다.

A2A SDK v2 제약과 이중 경로 수정

A2A SDK v2(a2a-sdk==1.0.2)는 on_message_send 경로에 두 가지 제약을 부과하여 브리지 구현을 형성했습니다:

  1. 단일 Message만. SDK EventQueue에 여러 Message 객체를 내보내면 InvalidAgentResponseError: Multiple Message objects received.가 발생합니다.
  2. TaskStatusUpdateEvent 불가. 상태 이벤트는 InvalidAgentResponseError: Received TaskStatusUpdateEvent in message mode.를 발생시킵니다.

단순한 브리지는 토큰 델타마다 하나의 Message를 내보낼 것입니다 — 자연스러운 스트리밍 패턴입니다. SDK는 이를 거부합니다. 또한 message/send 경로에서 상태 이벤트(WORKING, COMPLETED)도 거부합니다.

수정은 이중 경로 출력 채널입니다:

  • SSE(게이트웨이 관리): 토큰 청크가 실시간으로 SSE로 푸시됩니다. 연결된 클라이언트는 발생하는 대로 스트리밍 출력을 봅니다. 상태 이벤트(WORKING, COMPLETED, FAILED)도 SSE로만 갑니다.
  • SDK EventQueue: 스트림 완료 후 전체 응답 텍스트가 포함된 단일 누적 Message가 SDK EventQueue로 내보내집니다. 이것이 JSON-RPC message/send 응답이 반환하는 것입니다.

클라이언트는 SSE를 통해 실시간 스트리밍을, SDK를 통해 깔끔한 단일 메시지 JSON-RPC 응답을 받습니다. 두 채널 모두 작동하며 어느 쪽도 SDK 제약을 위반하지 않습니다.

한 포트의 프로토콜 표면

게이트웨이는 단일 포트(기본 8765)에 5개 프로토콜 표면을 노출합니다:

프로토콜 라우트 인증 목적
GraphQL POST /{ep}/a2a_core_graphql A2A 코어 쿼리/뮤테이션(에이전트, 작업, 메시지, 설정)
JSON-RPC 2.0 POST /{ep}/a2a A2A 프로토콜: message/send, tasks/get, tasks/cancel, tasks/list
SSE(스트림) GET /{ep}/a2a_sse 파티션별 장기 A2A 작업 이벤트 스트림
SSE(푸시) POST /{ep}/a2a_sse JSON-RPC 메시지 + 연결된 SSE 클라이언트로 푸시
Agent Card GET /{ep}/.well-known/agent-card.json 공개 A2A 발견 문서(Part-Id 헤더 여전히 필요)

테스트 하니스가 사용하는 JSON-RPC message/send 매개변수 형태:

{
  "message": {
    "role": "ROLE_USER",
    "parts": [{ "text": "Say hello from A2A" }]
  },
  "metadata": {
    "operation": "task_execution",
    "agent_uuid": "a2a-hermes-agent",
    "stream": true,
    "task_data": { "task_id": "my-task-001", "task_type": "hermes_test" },
    "system_prompt": "You are a concise assistant.",
    "conversation_history": []
  }
}

metadata.operation 필드가 실행 경로를 선택합니다: 스트리밍 지원을 포함한 에이전트 run은 task_execution, 비스트리밍 채팅은 message_response. agent_uuid는 레지스트리의 특정 에이전트를 대상으로 합니다. stream 플래그는 SSE 브로드캐스팅을 활성화합니다. task_data.task_idtasks/gettasks/cancel이 사용하는 호출자 제공 ID입니다.

SSE는 작업별이 아닌 파티션별입니다. {ep}#{Part-Id}의 모든 작업이 해당 파티션의 모든 구독자에게 브로드캐스트됩니다. 작업 순서가 중요합니다: 메시지를 보내기 전에 SSE 리스너를 연결하세요, 그렇지 않으면 초기 토큰 청크가 누락됩니다.

영속성과 다중 테넌시

Docker 이미지는 db_backend=postgresql을 강제합니다 — DynamoDB는 지원되지 않습니다. 데몬은 리터럴, 접두사 없는 테이블 이름을 사용합니다:

테이블 보관
a2a_agents 에이전트 레코드 + 에이전트별 핸들러/모델 메타데이터
a2a_tasks 작업 라이프사이클 + 상태
a2a_messages 작업별 메시지 턴
a2a_settings 파티션별 설정 딕셔너리

이름이 접두사가 없으므로 같은 이름을 사용하는 다른 모듈과 PG_DB를 공유하지 마세요.

테넌트 격리는 PostgreSQL 행 수준 보안을 사용합니다. 세션 변수 app.tenant_id가 요청의 partition_key("{endpoint_id}#{Part-Id}")로 설정되고, RLS 정책이 모든 쿼리를 해당 값으로 범위 지정합니다. 테이블과 정책은 initialize_tables=1일 때 게이트웨이 시작 시 자동 생성됩니다. 즉, 애플리케이션 코드에서 잊힌 partition_key 필터가 크로스 테넌트 행을 누출할 수 없습니다 — 데이터베이스가 경계를 강제합니다.

RLS 구현은 a2a_daemon_engine/utils/rls.py(set_rls_contextcreate_rls_policies)와 마이그레이션 0005_enable_rls_policies에 있습니다. set_rls_context 함수가 연결에서 요청별 SET app.tenant_id를 실행하고, create_rls_policies가 4개 A2A 테이블 모두에 tenant_isolation 정책으로 RLS를 활성화하고 강제합니다. RLS는 DynamoDB 모드에서 비활성입니다.

Docker Compose로 배포

이 스택은 하나의 상시 실행 서비스와 두 개의 선택적 프로파일 게이트 형제를 가집니다:

서비스 컨테이너 이름 상시 실행? 프로파일 목적
a2a-gateway a2a-hermes-gateway SilvaEngine Gateway(A2A 전용 라우트) + Hermes 브리지
postgres a2a-postgres 선택 postgres 번들 PostgreSQL 영속성 백엔드
hermes container-hermes 선택 hermes 번들 Hermes Agent(OpenAI 호환 API + 대시보드)

COMPOSE_PROFILES가 두 형제에 대한 단일 스위치입니다:

시작되는 서비스
비어 있음 게이트웨이만(외부 Postgres + 외부 Hermes)
postgres 게이트웨이 + 번들 Postgres
hermes 게이트웨이 + 번들 Hermes(외부 Postgres)
postgres,hermes 게이트웨이 + 번들 Postgres와 Hermes(기본값)

형제가 번들로 포함될 때, 호스트 참조를 서비스 이름으로 유지하세요: PG_HOST=postgresHERMES_API_URL=http://hermes:<API_SERVER_PORT>. 형제가 외부일 때, 자체 인스턴스로 지정하세요(예: PG_HOST=host.docker.internal, HERMES_API_URL=http://host.docker.internal:8642).

빠른 시작

cp .env.example .env

# 입력: JWT_SECRET_KEY, ADMIN_PASSWORD, API_SERVER_KEY,
# HERMES_API_KEY(= API_SERVER_KEY), HERMES_MODEL_PROVIDER + 제공자 키, HERMES_MODEL

mkdir -p www/hermes www/projects

DOCKER_BUILDKIT=1 docker compose build
docker compose up -d           # COMPOSE_PROFILES=postgres,hermes가 기본값

docker compose ps              # (healthy) 대기
curl -f http://localhost:8765/health

pip install requests
python test_hermes_hello.py    # 종단 간 스모크 테스트

silvaengine_gatewaya2a_daemon_engine 모두 git에서 이미지에 pip 설치됩니다(호스트 소스 마운트 없음). 이미지는 일반적이고 완전히 환경 기반입니다 — 시크릿이 포함되지 않습니다. 모듈은 git+https를 통해 ideabosque의 공개 GitHub 저장소에서 복제됩니다 — 자격 증명이나 SSH 배포 키가 필요 없습니다.

.env 인라인 주석 함정

Docker Compose의 env_file 파서는 인라인 주석을 제거하지 않습니다. 다음과 같은 줄은:

HERMES_API_KEY=hermes-local-key   # token for Hermes

HERMES_API_KEY를 리터럴 문자열 hermes-local-key # token for Hermes(주석 포함)로 설정하여, 조용히 인증을 깨뜨립니다. 규칙: KEY=value 줄의 값 뒤에 아무것도 두지 마세요. 메모는 변수 위의 자체 # 주석 줄에 두세요.

검증: 5개 스크립트에 걸친 15개 E2E 검사

이 스택은 독립형 Python 테스트 하니스를 제공합니다(유일한 종속성: requests). ./.env를 로드하고, 게이트웨이 JWT를 해결하거나 발행하고, 실행 중인 스택과 통신합니다:

스크립트 종류 수행 작업
test_hermes_hello.py 스모크 비스트리밍 message/send, 응답 출력
test_hermes_hello_sse.py 스모크 한 프롬프트를 SSE로 스트리밍하여 반환
test_hermes_gateway_live.py E2E 스위트 9개 검사: Hermes 헬스, 게이트웨이 헬스, 에이전트 카드, GraphQL 핑, message/send, tasks/get, tasks/list, tasks/cancel, 실패 경로
test_hermes_sse_live.py E2E 스위트 6개 검사: 헬스 x2, SSE 연결, 실시간 토큰 청크, COMPLETED 상태, HTTP 폴백
test_hermes_chatbot.py 인터랙티브 실시간 SSE 스트리밍을 포함한 A2A 표면에 대한 REPL

모든 비인터랙티브 스크립트는 단계별로 PASS/FAIL을 출력하고 실패 시 0이 아닌 종료 코드를 반환하여, CI 게이트로 작동합니다. 단위 테스트 스위트(test_hermes_handler.py)는 httpx.MockTransport를 통해 모킹된 HTTP로 24개 테스트를 실행합니다 — 서비스가 필요 없습니다.

운영 패턴

재빌드 없는 라우트 변경

routes.yaml이 컨테이너에 읽기 전용으로 바인드 마운트됩니다. 호스트 파일을 편집하고 게이트웨이 프로세스를 재시작하세요 — 재빌드가 필요 없습니다:

make restart

업스트림 변경 후

silvaengine_gatewaya2a_daemon_engine가 빌드 시 git에서 pip 설치되므로, 업스트림 변경은 git 레이어가 최신 @main을 다시 복제하도록 --no-cache로 재빌드가 필요합니다:

DOCKER_BUILDKIT=1 docker compose build --no-cache
docker compose up -d --force-recreate

버전 고정이 없습니다 — @main은 움직이는 대상입니다. 재현성이 필요하면 requirements-modules.txt에 태그나 커밋을 고정하세요.

한 워커 이상으로 스케일링

인메모리 작업 상태, 속도 제한 카운터, SSE 클라이언트 레지스트리는 프로세스별입니다. GATEWAY_WORKERS > 1일 때, 공유 백엔드로 전환하세요(GATEWAY_TASK_BACKEND=dynamodb, GATEWAY_RATE_LIMIT_BACKEND=dynamodb, 추가로 region_nameaws_* 자격 증명)하고 SSE에 sticky 세션을 사용하세요. 기본 구성은 하나의 Uvicorn 프로세스를 시작합니다.

변경해야 할 보안 기본값

  • JWT_SECRET_KEY=change-me-in-productionopenssl rand -hex 32로 교체
  • ADMIN_PASSWORD=change-me — 실제 비밀번호로 교체
  • POSTGRES_PASSWORD=silvaengine — 실제 비밀번호로 교체
  • GATEWAY_CORS_ORIGINS=*는 자격 증명 없이 모든 출처를 허용합니다. 쿠키/자격 증명이 필요하면 명시적 목록을 설정하세요.
  • 번들 Hermes는 호스트 Docker 소켓(/var/run/docker.sock)을 마운트합니다. 이는 해당 컨테이너 내부의 모든 것에 대해 사실상 호스트의 root입니다. 제어하는 호스트에서만 hermes 프로파일을 실행하고, 에이전트가 컨테이너를 시작할 필요가 없으면 마운트를 제거하세요.
  • Hermes 대시보드는 기본적으로 포트 9119에서 빈 basic-auth 자격 증명으로 활성화됩니다. 호스트를 노출하기 전에 HERMES_DASHBOARD_BASIC_AUTH_*를 설정하거나 포트를 localhost에 바인드하세요.
  • RLS가 테넌트 경계입니다. 임의 Part-Id를 설정할 수 있는 호출자는 해당 파티션의 데이터를 읽습니다 — 이 앞에 두는 모든 프론트엔드에서 Part-Id를 인증 관련 입력으로 취급하세요.

이것이 가능하게 하는 것

3계층 스택은 처음부터 조립하기 어려운 세 가지 역량을 제공합니다:

1. Hermes 재작성 없는 A2A 프로토콜 준수. 모든 A2A 클라이언트가 Agent Card를 통해 Hermes 기반 에이전트를 발견하고, message/send로 작업을 보내고, SSE로 응답을 스트리밍하고, 표준 A2A 상태로 작업 라이프사이클을 추적할 수 있습니다. 클라이언트는 원격 에이전트가 Hermes를 실행한다는 것을 모릅니다 — JSON-RPC 인터페이스를 가진 A2A 엔드포인트로 보입니다.

2. 하나의 게이트웨이에서 다중 테넌트 에이전트 서빙. Part-Id 헤더와 PostgreSQL RLS의 결합은 하나의 게이트웨이 인스턴스가 하드 데이터베이스 수준 격리로 여러 테넌트를 서빙함을 의미합니다. 각 테넌트는 자체 에이전트 레지스트리, 작업 기록, 메시지 저장소를 갖습니다 — 모두 partition_key로 범위 지정된 동일한 4개 테이블에 있습니다.

3. 에이전트 경계를 넘어선 인간 개입 승인. Hermes 승인 게이트가 A2A INPUT_REQUIRED 상태로 매핑됩니다. 에이전트 위임 체인이 인간 승인이 필요한 단계를 포함할 수 있으며, A2A 프로토콜이 해당 상태 전환을 원래 에이전트나 운영자에게 되돌려 전달합니다 — 체인의 각 에이전트가 어떤 프레임워크에서 실행되든 관계없이.

참조 구현은 또한 서버리스 A2A를 위한 AWS Lambda 디스패치, 양방향 스트리밍을 포함한 실험적 gRPC 전송, 이중 백엔드 영속성(DynamoDB 또는 PostgreSQL)을 지원합니다. a2a_daemon_engine 저장소Hermes 통합 가이드가 전체 구현, 구성 참조, 상태 매핑 세부 사항을 포함합니다. SilvaEngine Gateway 저장소가 라우트 매니페스트 시스템, 인증 제공자, 모듈 자동 초기화를 문서화합니다.

관련 읽기


중견 유통업체는 카탈로그 에이전트와 대화하는 견적 에이전트가, 인벤토리 에이전트와 대화하는 카탈로그 에이전트가 필요합니다 — 각각 다른 프레임워크로 뒷받침되고, 각각 다른 팀이 소유합니다. A2A가 그 에이전트들에게 공유 프로토콜을 제공합니다. 브리지 계층은 Hermes Agent가 내부를 재작성하지 않고 참여할 수 있게 합니다. docker-a2a-hermes-agent-gateway가 그 브리지를 PostgreSQL 영속성, RLS 다중 테넌시, 15개 E2E 테스트 검사를 포함한 단일 컨테이너 이미지로 패키징합니다.

범위 지정 빌드 요청

1주 발견. 시스템 인벤토리, 워크플로 맵, 고정 범위를 받습니다 — 우리와 함께 빌드하든 아니든.

귀하의 시스템을 위해 이것을 구축하고 싶으신가요?

여기의 각 문서는 실제 프로덕션 작업에서 나왔습니다. 대상 시스템과 워크플로가 있다면, 1주 내에 빌드를 범위 정의할 수 있습니다.

범위 정의 빌드 요청

1주 발견. 시스템 인벤토리, 워크플로 맵, 고정 범위를 받습니다 — 우리와 빌드할지 여부와 관계없이.