AWS 기술 블로그

AI 에이전트를 개발하지 않고 조립한다 — Amazon Bedrock AgentCore 기반 엔터프라이즈 에이전트 플랫폼

들어가며: 에이전트를 한 명이 만들던 시대의 끝

지난 1년 동안 많은 엔지니어가 자기만의 AI 에이전트를 만들어 봤습니다. CloudWatch 지표를 조회하는 작은 도구, EKS 클러스터 상태를 점검하는 스크립트, 장애 로그를 요약하는 프롬프트. LLM과 몇 개의 도구를 엮으면 놀랍도록 빠르게 동작하는 에이전트가 나옵니다. 여기까지는 한 사람의 생산성 향상 이야기입니다.

문제는 그다음입니다. 10명, 20명이 일하는 클라우드 운영 조직에서 각자가 자기 에이전트를 만들기 시작하면 세 가지 벽에 부딪힙니다. 첫째, 중복 개발 — 옆 팀이 이미 만든 CloudWatch 조회 도구를 또 만듭니다. 둘째, 신뢰 불가 — 누가 만든 에이전트가 무슨 도구를 어떤 권한으로 호출하는지 아무도 전체 그림을 모릅니다. 셋째, 거버넌스 부재 — 에이전트가 운영 환경에서 무엇을 했는지 추적할 방법이 없어 프로덕션에 올리기가 두렵습니다.

개인의 도구가 조직의 자산이 되려면, 에이전트를 매번 새로 개발하는 방식에서 이미 검증된 부품을 조립하는 방식으로 넘어가야 합니다. 이 글은 Amazon Bedrock AgentCore를 기반으로 그런 플랫폼을 어떻게 구성하는지를 보여주는 레퍼런스 아키텍처를 소개합니다. 전체 코드는 aws-samples 에 공개되어 있으며, deploy-all.sh한 번으로 약 15분 만에 배포됩니다.

이 글은 세 부분으로 이어집니다. 1부에서는 “개발하지 않고 조립한다”가 무슨 뜻이고 그 플랫폼이 어떻게 구성되는지를 사용자 관점에서 봅니다. 2부에서는 그렇게 조립된 카드가 어떤 코드 위에서 도는지 — 모든 에이전트가 공유하는 단 하나의 베이스 이미지(agent_runner.py, 약 400줄)를 해부합니다. 3부에서는 그렇게 직접 지은 것들이 이제 AgentCore의 관리형 서비스로 어떻게 대체·단순화되는지를 before/after로 짚습니다. 결론을 먼저 말하면 — 직접 지어보면 그 가치를 알게 되고, 관리형으로 넘기면 직접 지을 필요가 없어진 부분이 꽤 많아졌습니다.

1부 · 조립 — 개발하지 않고 부품으로 에이전트를 만든다

[1.1] 왜 ‘개발’이 아니라 ‘조립’인가

“조립한다”는 말은 비유가 아니라 이 플랫폼의 동작 방식 그 자체입니다. 새 에이전트가 필요할 때 엔지니어가 코드를 작성하지 않습니다. 대신 웹 UI에서 세 가지 부품을 조립합니다.

  • Context Boundary — 이 에이전트가 무엇을 책임지는지, 어디까지가 그 일의 경계인지. 시스템 프롬프트와 역할 정의에 해당합니다.
  • Gateway — 에이전트가 사용할 도구 묶음. CloudWatch, EKS, 교차 계정 조회 같은 운영 도구를 Gateway 단위로 연결합니다(도구는 Lambda로 구현되고, Gateway가 이를 MCP 표준 인터페이스로 노출합니다).
  • Delegation — 복잡한 작업을 하위 도메인 에이전트에게 위임하는 구조. Supervisor 에이전트가 작업을 쪼개 전문 에이전트에게 넘깁니다.

조립이 끝나면 에이전트는 코드 파일이 아니라 카드(Card) 라는 자산으로 등록됩니다. 카드는 조직 누구나 조회하고, Playground에서 테스트하고, 다시 설계 화면으로 들어가 수정할 수 있는 공유 단위입니다. 한 사람이 만든 “EKS 헬스체크 에이전트”가 조직 레지스트리에 카드로 올라가면, 옆 팀은 그것을 다시 만들지 않고 그대로 가져다 쓰거나 변형합니다.

이 전환의 핵심은 재사용 가능한 경계입니다. 코드로 만든 에이전트는 만든 사람만 고칠 수 있지만, 부품으로 조립한 에이전트는 누구나 같은 부품을 다시 꺼내 쓸 수 있습니다. 개발이 아니라 조립이기 때문에 가능한 일입니다.

[1.2] 전체 아키텍처

플랫폼은 네 개의 레이어로 구성됩니다.

  • Presentation Layer — CloudFront + Next.js 14 UI. CloudFront는 VPC Origin을 통해 프라이빗 서브넷의 내부 ALB로 직접 연결되며, 트래픽은 AWS 백본을 벗어나지 않습니다. 인증은 Cognito 기반 JWT를 애플리케이션 레벨에서 검증합니다.
  • Control Plane — ECS Fargate 위의 FastAPI. 에이전트 카드의 CRUD, AgentCore 연동, 세션 관리, 관측성 쿼리를 담당합니다. 조립한 에이전트를 실제 런타임으로 만들어내는 오케스트레이터입니다.
  • Agent Runtime — Bedrock AgentCore 위에서 동작하는 Strands SDK 컨테이너. 서버를 직접 관리하지 않는 관리형 런타임으로, “Deploy” 버튼을 누르면 AgentCore가 ECR에서 이미지를 받아 실행합니다.
  • Tool Layer — boto3/AWS CLI 기반 코드를 담은 Lambda 함수들을 AgentCore Gateway로 묶은 계층. Gateway가 이 도구들을 MCP(Model Context Protocol) 엔드포인트로 노출하면, 에이전트는 표준화된 방식으로 운영 도구를 발견하고 호출합니다.

데이터는 DynamoDB(에이전트 메타데이터·인시던트)와 S3(보고서)에 저장되고, CloudWatch 알람은 EventBridge를 거쳐 자동 인시던트 대응 흐름으로 이어집니다. 전체 인프라는 Terraform 8개 모듈로 코드화되어 있어 한 번의 terraform apply로 약 70개 리소스가 생성됩니다.

[1.3] 조립의 핵심 ① — Agent Builder

Agent Builder는 “개발하지 않고 조립한다”가 실제로 일어나는 화면입니다. 엔지니어는 자연어로 요구사항을 적습니다.

“Pod 상태와 노드 용량을 모니터링하고 실패한 배포를 보고하는 EKS 클러스터 헬스체크 에이전트를 만들어 줘.”

그러면 Builder가 이 요구를 세 부품으로 분해해 조립안을 제시합니다. Context Boundary로 “EKS 클러스터 상태 점검”이라는 역할과 책임 경계를 잡고, 필요한 Gateway(EKS·CloudWatch MCP 도구 묶음)를 선택하고, 작업이 복잡하면 하위 에이전트로의 Delegation 구조를 제안합니다. 엔지니어는 생성된 시스템 프롬프트, 도구 선택, 모델 선택을 검토하고 조정합니다.

확정하면 에이전트는 카드로 레지스트리에 등록됩니다. 카드를 클릭하면 바로 Playground에서 테스트하거나 Design 화면으로 들어가 부품을 다시 조립할 수 있습니다. 조직 단위로 쌓인 카드는 “우리 조직이 보유한 운영 에이전트 목록” 그 자체가 됩니다.

여기서 실제 배포가 흥미롭습니다. “Deploy” 버튼을 누르면 Control Plane API가 미리 빌드된 베이스 이미지 URI로 AgentCore Runtime을 생성하고, AgentCore가 ECR에서 이미지를 받아 런타임을 READY 상태로 만듭니다. 엔지니어는 컨테이너도, 서버도 다루지 않습니다. 부품을 고르고 버튼을 누르는 것이 전부입니다.

시리즈 노트 — 현재 이 카드 레지스트리는 플랫폼이 자체 구현한 것입니다. AgentCore에 추가되는 관리형 Agent Registry로 이 부분을 대체할 수 있으며, 3부 [3.2]에서 다룹니다.

[1.4] 조립의 핵심 ② — Gateway로 도구를 표준화하고 재사용한다

조립이 성립하려면 조립할 부품이 표준화되어 있어야 합니다. 이 플랫폼은 그 표준화를 구현과 인터페이스를 분리하는 방식으로 풉니다.

먼저 구현(implementation) — 도구의 실체는 AWS Lambda 함수입니다. Lambda 안에서 boto3(또는 AWS CLI) 기반 코드가 실제 운영 작업을 수행합니다. CloudWatch 지표를 조회하고, EKS 클러스터 상태를 점검하고, STS AssumeRole로 교차 계정 리소스에 접근합니다. 이 레퍼런스에는 aws_cloudwatch_mcp(지표·알람·로그 조회), aws_eks_mcp(클러스터·Pod 상태), cross_account(교차 계정 접근 헬퍼) 세 가지 샘플이 포함되어 있고, 같은 패턴으로 도구를 계속 확장할 수 있습니다.

다음으로 인터페이스 — 이 Lambda들은 AgentCore Gateway에 target으로 등록됩니다. 그리고 Gateway는 MCP(Model Context Protocol)로 생성됩니다. 여기가 핵심입니다. MCP는 에이전트가 도구를 발견(discover)하고 호출하는 방식을 표준화한 오픈 프로토콜로, “AI를 위한 USB-C”에 비유됩니다. AgentCore Gateway는 Lambda·API·OpenAPI 스펙을 MCP 호환 도구로 변환(translate)하고, 여러 도구를 하나의 MCP 엔드포인트로 합성(compose)해 에이전트에게 제공합니다. 즉 도구의 구현은 Lambda 위 boto3/CLI 코드이지만, 에이전트가 보는 인터페이스는 일관된 MCP입니다. 이 데모도 이 패턴을 그대로 따라, 도구를 도메인별로 묶은 여러 Gateway(network·container·data·monitoring·cost 등)를 MCP 엔드포인트로 노출합니다.

이 분리가 앞 절의 “중복 개발” 문제를 직접 해소합니다. CloudWatch 조회 도구를 한 번 Lambda로 만들어 Gateway에 등록하면, EKS 헬스체크 에이전트도, 인시던트 RCA 에이전트도, 비용 분석 에이전트도 모두 같은 MCP 도구를 재사용합니다. 도구를 만든 사람과 쓰는 사람이 분리되고, 도구는 조직 공용 부품이 됩니다. 새 도구가 필요하면 Lambda 하나를 추가해 Gateway에 등록하는 것으로 조직 전체가 그 도구를 MCP로 쓸 수 있게 됩니다 — 도구는 늘어날수록 가치가 커지는 공유 자산입니다. AgentCore Gateway의 시맨틱 검색은 도구가 수백 개로 늘어도 에이전트가 작업 맥락에 맞는 도구를 골라 쓰게 해줍니다.

[1.5] 조립한 것을 신뢰하기 — 관측성

부품으로 빠르게 조립할 수 있다는 것만으로는 프로덕션에 올릴 수 없습니다. “이 에이전트가 운영 환경에서 실제로 무엇을 했는가”를 추적할 수 없다면, 조립의 편리함은 오히려 통제 불능의 위험이 됩니다. 그래서 이 플랫폼은 조립한 에이전트를 신뢰할 수 있게 만드는 관측성 계층을 함께 제공합니다.


모든 에이전트 호출은 OTEL 스팬과 X-Ray 분산 트레이싱으로 기록됩니다. Trace Viewer는 이를 워터폴 형태로 보여줍니다 — Supervisor 에이전트가 요청을 받아 Domain 에이전트에게 위임하고, Domain 에이전트가 MCP Tool을 호출하는 전 과정이 하나의 스팬 트리로 펼쳐집니다. 각 스팬은 하나의 도구 호출 또는 LLM 추론에 대응하며, 지연 시간과 토큰 사용량까지 분해해서 볼 수 있습니다.

이 가시성이 조립을 안전하게 만듭니다. 에이전트가 예상과 다르게 동작하면 어느 스팬에서 무슨 도구를 어떤 인자로 호출했는지 그대로 들여다볼 수 있습니다. CloudWatch 알람이 EventBridge를 통해 RCA 에이전트를 자동 실행하는 무인 인시던트 대응 흐름도, 모든 단계가 트레이스로 남기 때문에 사후에 검증할 수 있습니다. 조립의 속도와 운영의 신뢰가 양립합니다.

시리즈 노트 — 현재 이 관측성 계층은 플랫폼이 자체 구현한 것입니다. 그 내부 구조는 2부 [2.6]에서 해부하고, AgentCore의 관리형 Observability/Evaluations로 어떻게 대체·증강되는지는 3부 [3.4]에서 다룹니다.

[1.6] 배포 — 15분 one-shot

레퍼런스 아키텍처는 읽는 것으로 끝나면 의미가 절반입니다. 이 플랫폼은 환경변수 두 개만 설정하면 deploy-all.sh 한 번으로 전체가 배포됩니다. 커스텀 도메인 없이 CloudFront 기본 도메인을 쓰므로 사전 준비물이 사실상 없습니다.

export AWS_REGION=us-west-2
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
./scripts/deploy-all.sh

배포는 6단계로 진행됩니다.

Phase 내용 소요
1 Terraform Apply — VPC·ECR·DynamoDB·ECS·CloudFront 등 ~70 리소스 ~5분
2 Container Image Build — CodeBuild로 4개 이미지 빌드 후 ECR push ~5분
3 Seed Data — DynamoDB에 에이전트·Gateway 메타데이터 시드 ~10초
4 ECS Redeployment — 새 이미지로 서비스 재시작 ~3분
5 AgentCore Agents — Runtime 등록 (선택) ~2분
6 MCP Gateways — Lambda 기반 Gateway 연결 (선택) ~1분

인프라는 Terraform 8개 모듈(network·data·auth·registry·iam·compute·cdn·build)로 분리되어 있어 각 계층을 독립적으로 이해하고 변경할 수 있습니다. 데모가 끝나면 terraform destroy 한 번으로 정리됩니다. NAT Gateway·ALB·Fargate·CloudFront는 시간당 과금되므로 반드시 정리하세요.

1부가 보여준 것은 현재의 빌딩블록으로 직접 조립한 버전입니다. 카드 레지스트리도, 경계 강제도, 관측성도 플랫폼이 자체 구현했습니다. 그렇다면 조립이 끝난 그 카드는 — 도대체 어떤 코드 위에서 실행될까요? 2부에서 그 심장을 해부합니다.

2부 · 해부 — 하나의 코드가 서로 다른 에이전트가 되기까지

1부에서 우리는 에이전트를 개발하지 않고 조립한다고 했습니다. Agent Builder에서 Context Boundary·Gateway·Delegation 세 부품을 골라 카드로 등록하면 에이전트가 만들어집니다. 그런데 놀랍게도, 모든 에이전트가 단 하나의 동일한 컨테이너 이미지를 공유합니다. EKS 헬스체크 에이전트도, 비용 분석 에이전트도, 인시던트 RCA Supervisor도 전부 같은 이미지입니다. 이제 그 베이스 이미지의 심장인 agent_runner.py(약 400줄)를 해부해, 1부가 “신뢰할 수 있다”고 말한 그 기계장치를 우리가 어떻게 직접 지었는지 봅니다.

[2.1] 핵심 발상 — 코드가 아니라 설정으로 에이전트를 구분한다

“EKS 헬스체크 에이전트”와 “비용 분석 에이전트”는 전혀 다른 일을 합니다. 보통이라면 각각 다른 코드를 작성하겠지만, 이 플랫폼은 그렇게 하지 않습니다.

컨테이너는 환경변수 AGENT_ID 하나로만 구분됩니다. 에이전트의 성격을 결정하는 모든 것 — 시스템 프롬프트, 사용할 도구, 위임 대상, 안전 정책 — 은 코드가 아니라 DynamoDB에 저장된 Config에 들어 있고, 컨테이너는 기동 시 이 Config(PK=AGENT#{id}, SK=CONFIG)를 읽어 자기 자신을 조립합니다. 1부에서 Builder가 만들어 카드로 등록한 그 설정이, 바로 여기서 실행 가능한 에이전트로 살아납니다.

이 한 줄짜리 발상이 운영상 엄청난 차이를 만듭니다. 이미지를 한 번만 빌드해 ECR에 올려두면, 새 에이전트를 100개 추가해도 빌드도 배포 파이프라인도 늘어나지 않습니다. 새 에이전트 = 새 Config 한 건일 뿐입니다.

실제 Config 레코드는 이렇게 생겼습니다.

{
  "PK": "AGENT#eks-healthcheck",
  "SK": "CONFIG",
  "name": "EKS Cluster Healthcheck Agent",
  "contextBoundary": "EKS 클러스터 상태 점검과 실패 배포 보고에만 집중합니다.",
  "systemPrompt": "You are an EKS cluster health monitoring agent...",
  "modelId": "global.anthropic.claude-sonnet-4-6",
  "delegations": [
    {"targetAgent": "cost-analyzer", "purpose": "비용 이상 감지 시 위임"}
  ],
  "mcpGateways": ["arn:aws:...gateway/eks-tools"],
  "internalTools": ["query_dynamodb", "invoke_agent"],
  "hooks": {"pre": ["persona-injection"], "post": ["side-channel"]}
}

에이전트의 정체성(systemPrompt·contextBoundary), 능력(mcpGateways·internalTools), 관계(delegations), 안전 장치(hooks)가 모두 이 하나의 레코드에 선언형으로 담깁니다. 코드는 이 선언을 읽어 자기 자신을 조립할 뿐입니다.

[2.2] 5초 안에 깨어나되, 무거운 일은 미룬다 — Lazy Initialization

AgentCore Runtime은 컨테이너가 빠르게 헬스체크에 응답하기를 기대합니다(콜드 스타트를 최소화하도록 /ping에 즉시 READY로 응답). 그런데 LLM 모델 핸들 생성, MCP Gateway 연결, 도구 등록은 시간이 걸리는 작업입니다. 이 둘을 어떻게 양립시킬까요?

답은 지연 초기화(Lazy Init) 입니다. FastAPI 서버는 즉시 떠서 /ping에 응답하고, 무거운 조립은 첫 번째 실제 요청이 들어온 순간 딱 한 번 수행합니다. 동시에 여러 요청이 몰려도 조립이 두 번 일어나지 않도록 락(double-checked locking)으로 보호합니다.

def _initialize_agent():
    global _initialized
    if _initialized:
        return
    with _init_lock:          # 동시 요청이 몰려도 조립은 1회만
        if _initialized:
            return
        ...

여기에 워밍업 장치도 있습니다. type: "wake-up" 요청이 오면 LLM을 호출하지 않고 초기화만 마친 뒤 즉시 ready를 반환합니다. 사용자가 첫 질문을 던지기 전에 미리 깨워둘 수 있어, 콜드 스타트 체감을 없앱니다.

[2.3] 조립의 실제 — Config 한 건이 살아 있는 에이전트가 되기까지

초기화 루틴은 Config를 받아 다음 순서로 에이전트를 짜 맞춥니다.

각 단계를 더 자세히 보겠습니다.

  1. Config 로드 — DynamoDB에서 AGENT#{id}/CONFIG를 읽습니다. 배포 직후엔 Config 쓰기와 컨테이너 기동이 경합할 수 있어, 지수 백오프로 3회 재시도(2**attempt초)합니다. 이런 작은 내성이 “방금 만든 에이전트를 눌렀더니 실패”를 막습니다. Config는 immutable — 시작 시 1회만 로드합니다.
  2. 위임을 도구로 변환 — Config의 delegations[](다른 에이전트에게 일을 넘기는 관계)을 각각 delegate_{대상}이라는 호출 가능한 도구(scoped_agent_invoke 타입)로 바꿉니다. Supervisor는 하위 에이전트를 “함수처럼” 부르게 됩니다.
  3. 내부 도구 등록 — DynamoDB 조회/쓰기, 에이전트 호출(A2A), 파이썬 함수 등을 Strands @tool로 래핑합니다. 이때 한 도구가 실패해도 그 도구만 건너뛰고(tool_errors에 기록) 나머지로 기동을 이어갑니다.
  4. MCP Gateway 연결 — 1부의 Tool Layer에서 만든 Gateway에 SigV4 서명 transport로 접속합니다. toolFilter로 “이 에이전트는 이 Gateway의 이 도구들만” 골라 바인딩할 수 있습니다. 연결 실패는 BaseException까지 잡아 skip — Gateway가 죽어 있어도 에이전트 기동 자체는 막지 않습니다.
  5. Strands Agent 조립 — 모델(BedrockModel, 기본 apac.anthropic.claude-sonnet-4-..., max_tokens=32768) + 도구(내부 도구 + MCP) + 시스템 프롬프트 + 훅을 하나의 Agent 객체로 묶습니다.

모든 단계는 DynamoDB의 디버그 채널에 init_start → init_tools → init_complete 이벤트로 남아, “이 에이전트가 어떤 도구를 들고 깨어났는지”를 나중에 그대로 들여다볼 수 있습니다.

[2.4] 경계를 코드가 아니라 훅으로 강제한다 — persona-injection

1부에서 강조한 Context Boundary(에이전트의 책임 경계)는 단순한 프롬프트 문구가 아닙니다. 런타임이 이를 자동으로 강제합니다.

Config에 persona-injection pre-hook이 켜져 있으면, 시스템 프롬프트 끝에 contextBoundary 기반 범위 강제 규칙이 기계적으로 덧붙습니다. 실제 주입되는 문구는 이런 식입니다.

scope_rule = (
    f"\n\n[SCOPE ENFORCEMENT] Your Context Boundary is: {boundary}. "
    f"You MUST only handle requests within this boundary. "
    f"For any request outside this boundary, do NOT delegate to other agents "
    f"and do NOT use any tools. Instead, respond: "
    f"'This request is outside my scope ({boundary}). ...'"
)
system_prompt = system_prompt + scope_rule

엔지니어가 프롬프트에 이 문장을 깜빡 빠뜨려도, 베이스 이미지가 빠짐없이 주입합니다. 경계는 사람의 성실함이 아니라 플랫폼의 기본 동작으로 보장됩니다. 이것이 1부에서 말한 “신뢰할 수 있는” 에이전트의 한 축입니다. (이 “프롬프트에 부탁하는” 방식이 3부에서 어떻게 바뀌는지 기억해 두세요.)

[2.5] 무한 위임을 차단한다 — depth 가드

에이전트가 다른 에이전트를 부르고, 그 에이전트가 또 다른 에이전트를 부르면 — 잘못하면 무한 루프입니다. 런타임은 모든 요청에 delegationDepth를 실어 전파하고, 위임할 때마다 1씩 올립니다. 요청 처리 초입에서 _runtime.check_depth(depth)가 정해진 최대 깊이(기본 max_depth=2)를 넘는지 검사해, 초과하면 그 자리에서 거부합니다. A2A(Agent-to-Agent) 위임이 통제 불능으로 번지는 것을 구조적으로 막는 안전핀입니다.

[2.6] 모든 행동을 세 겹으로 관측한다

1부의 Trace Viewer(스팬 워터폴)는 마법이 아닙니다. 베이스 이미지가 세 가지 관측 계층을 동시에 돌리기 때문에 가능합니다.

계층 무엇을 보는가 어떻게
분산 추적(OTEL) Supervisor→Domain→MCP 호출의 시간 워터폴 커스텀 AgentSpanProcessor가 모든 스팬에 agent.id/session.id/phase를 자동 주입
실시간 이벤트(Hook) 어떤 도구를 언제 호출/완료했는가 Strands의 도구 호출 전/후 이벤트를 ObservabilityHook이 가로채 기록
세션 타임라인(Side-Channel) 한 세션에서 벌어진 모든 일의 시간순 기록 DynamoDB(PK=SESSION#{id})에 ULID 정렬로 이벤트 적재(TTL 7일)

특히 세션 타임라인은 fire-and-forget 입니다. 기록에 실패해도 에이전트 실행은 멈추지 않습니다. 관측은 “있으면 좋은 것”이지 “실행을 인질로 잡는 것”이 아니라는 설계 철학이 드러나는 대목입니다.

Side-Channel에 실제로 쌓이는 이벤트 레코드는 이렇습니다.

{
  "PK": "SESSION#sess_01J5KX...",
  "SK": "EVENT#01J5KXMR7N...",
  "type": "tool_call_complete",
  "data": {"tool": "describe_pods", "duration_ms": 340, "status": "success"},
  "agentId": "eks-healthcheck",
  "expiresAt": 1752000000
}

ULID 정렬 키 덕분에 시간순 조회가 바로 되고, TTL 7일(expiresAt)이 지나면 DynamoDB가 자동 삭제합니다. Trace Viewer는 이 레코드를 SESSION#{id}로 쿼리해 워터폴을 렌더링합니다.

[2.7] 관통하는 설계 원칙 — Graceful Degradation

2부를 한 문장으로 요약하면 “부품 하나가 고장 나도 전체는 선다” 입니다. 도구 등록 실패, MCP 연결 실패, 관측 기록 실패 — 어느 것도 에이전트 기동이나 응답을 막지 못합니다. 각 실패는 격리되고, 건너뛰어지고, 흔적만 남습니다.

조직의 자산으로 운영되는 에이전트에게 이것은 사치가 아니라 필수입니다. Gateway 하나가 일시적으로 죽었다고 모든 에이전트가 함께 쓰러진다면, 누구도 이것을 프로덕션에 올리지 못할 테니까요.

3부 · 전환 — 직접 지은 것을 관리형으로 갈아 끼우기

[3.1] 이 400줄이 한 일을 다시 보자

방금 해부한 agent_runner.py는 작지만 많은 일을 합니다. 단일 이미지를 Config로 차별화하고(조립 루프), 경계를 프롬프트에 주입하고(persona-injection), 위임 깊이를 검사하고(depth 가드), 모든 행위를 기록합니다(3중 관측). 이 모든 안전 장치가 코드 한 덩어리 안에 응축되어 있습니다.

AgentCore는 이제 에이전트 플랫폼의 횡단 기능을 모듈형 관리형 서비스로 제공합니다. 2부에서 해부한 자체 구현을 거기에 포개면 이렇게 정리됩니다.

2부에서 직접 지은 것 대체할 AgentCore 관리형 서비스 핵심 변화
카드 레지스트리 (DynamoDB + Control Plane API) AgentCore Registry (Preview) 조회·승인 카탈로그를 관리형으로. 시맨틱 + 키워드 하이브리드 검색, MCP-native 엔드포인트
persona-injection + depth 가드 (경계 강제) AgentCore Policy 경계 규칙을 코드 밖 Cedar 정책으로. Gateway에서 도구 호출(tool call)을 결정적으로 가로채 검증
agent_runner.py 조립/실행 루프 AgentCore Harness 모델·프롬프트·도구를 인라인 선언하면 오케스트레이션·도구 실행을 관리형이 담당
OTEL + Hook + Side-Channel 3중 관측 AgentCore Observability + Evaluations OTEL 표준 트레이스 통합 뷰 + 자동화된 품질 평가

가장 흥미로운 변화는 우리가 코드 안에 한 덩어리로 짠 안전 로직이, 관리형에서는 성격이 다른 두 서비스로 갈라진다는 점입니다. persona-injection·depth 가드(경계 강제)는 Policy로, 조립·실행 루프는 Harness로 내려갑니다. 한 파일에 섞여 있던 두 가지 책임이, 관리형 서비스의 경계선을 통해 비로소 분리되어 보입니다.

[3.2] 대체 ① — 카드 레지스트리 → AgentCore Registry

Before — 2부의 에이전트는 “카드”로 등록됩니다. 카드의 실체는 DynamoDB 항목(PK=AGENT#{id})이고, 조회·공유·수정은 Control Plane FastAPI가 직접 구현한 CRUD입니다. 검색은 테이블 스캔에 가깝고, “이 에이전트를 프로덕션에 써도 되는가”라는 승인 절차는 별도 장치 없이 운영 규율에 맡겨졌습니다. 조직이 커지면 두 가지가 아픕니다 — 발견성(원하는 에이전트를 못 찾아 또 만듦)과 거버넌스(검증되지 않은 카드가 그대로 노출됨).

After — AgentCore Registry는 바로 이 문제를 정조준한 완전관리형 디스커버리 서비스입니다. 에이전트뿐 아니라 MCP 서버·도구·스킬·커스텀 리소스를 하나의 검색 가능한 카탈로그에 등록합니다.

  • 거버넌스 워크플로publish → review → approve가 서비스에 내장됩니다. 퍼블리셔가 레코드를 제출하면 큐레이터가 승인/반려하고, 승인된 것만 발견 대상이 됩니다. 우리가 “운영 규율”에 맡겼던 부분이 플랫폼의 기본 동작이 됩니다.
  • 하이브리드 검색 — 시맨틱 이해와 키워드 매칭을 결합해, 자연어 질의(“EKS 상태 점검 에이전트”)와 정확한 이름 조회를 모두 처리합니다. 테이블 스캔이 검색 엔진으로 바뀝니다.
  • MCP-native 엔드포인트 — Registry 자체가 원격 MCP 엔드포인트로 노출됩니다. 즉 사람만이 아니라 에이전트가 직접 Registry를 검색해 적절한 도구·하위 에이전트를 찾아 쓸 수 있습니다. 2부의 Supervisor가 delegations[]로 위임 대상을 미리 알고 있어야 했다면, 이제는 레지스트리에 물어볼 수 있습니다.
  • 유연한 구성 — 조직 전체 단일 레지스트리든, 리소스 타입별/개발 단계별로 나누든 자유롭고, 인가는 IAM 또는 IdP의 JWT 중 선택합니다.

실제 마이그레이션에서는 기존 Control Plane의 카드 CRUD가 Registry API 호출로 바뀝니다.

# Before: Control Plane이 직접 DynamoDB에 카드 저장
table.put_item(Item={"PK": f"AGENT#{agent_id}", "SK": "CONFIG", ...})

# After: Registry에 레코드로 게시
agentcore.publish_registry_record(
    record_type="AGENT",
    name="eks-healthcheck",
    metadata={"contextBoundary": "...", "mcpGateways": [...]},
    mcp_endpoint="arn:aws:...gateway/eks-tools"
)

마이그레이션 메모 — Control Plane의 카드 CRUD를 걷어내는 대신, 에이전트 메타데이터를 Registry 레코드로 게시하도록 바꿉니다. UI의 “레지스트리” 화면은 Registry 검색 API(또는 MCP 엔드포인트)를 호출하는 얇은 클라이언트가 됩니다. Registry는 현재 Preview이므로, GA 전까지는 자체 구현과 병행하거나 단계적으로 전환하는 것을 권장합니다.

[3.3] 대체 ② — persona-injection·depth 가드 → Policy, 조립 루프 → Harness

2부에서 본 베이스 이미지의 “실행 루프” 격 로직은 두 가지 일을 했습니다. (a) 에이전트가 자기 책임 경계를 벗어나지 못하게 막고([2.4]의 SCOPE ENFORCEMENT 주입, [2.5]의 depth 가드), (b) Config를 읽어 모델·도구·프롬프트를 조립해 실행 루프를 돌렸습니다([2.2]~[2.3]). 관리형으로 옮기면 이 둘이 갈라집니다.

②-a 경계 강제 → AgentCore Policy

Before — 경계는 프롬프트 문구코드 분기로 강제됐습니다. persona-injection 훅이 프롬프트 끝에 “범위 밖 요청은 거부하라”를 붙이고, depth 가드가 요청마다 delegationDepth를 검사했습니다. 문제는 이것이 에이전트 코드 안에 있다는 점입니다 — LLM이 프롬프트 지시를 무시하거나, 프롬프트 주입(prompt injection)으로 우회되면 경계가 무너질 수 있습니다.

After — AgentCore Policy는 경계를 에이전트 코드 밖, Gateway 경계선에서 결정적으로 강제합니다. 정책 엔진을 만들어 Gateway에 연결하면, 모든 tool call이 실행 전에 정책에 의해 평가됩니다. 규칙은 Cedar(AWS 오픈소스 정책 언어)로 쓰거나 자연어로 기술하면 Cedar로 변환·검증됩니다. 자연어 작성 시에는 자동 추론으로 “지나치게 허용적/제한적이거나 영원히 만족 불가능한 조건”까지 사전에 잡아줍니다.

Cedar 정책은 이렇게 생겼습니다.

// EKS 헬스체크 에이전트는 eks-tools Gateway의 read 도구만 허용
permit (
  principal == Agent::"eks-healthcheck",
  action in [Action::"tool_call"],
  resource in Gateway::"eks-tools"
) when {
  resource.category == "read"
};

// 다른 Gateway 접근은 묵시적 deny

persona-injection이 “범위 밖이면 거부하라”고 프롬프트에 부탁했다면, Cedar 정책은 Gateway에서 tool call 자체를 차단합니다. LLM이 프롬프트를 무시하더라도 도구 실행이 일어나지 않는 구조입니다.

게다가 사용자 신원과 도구 입력 파라미터 단위의 fine-grained 제어가 가능하고, 모든 판정은 CloudWatch에 로깅되어 보안·컴플라이언스 팀이 감사할 수 있습니다. “신뢰할 수 있는 에이전트” 의 경계가, 사람의 성실함이나 프롬프트의 견고함이 아니라 정책 엔진의 결정적 보증으로 올라섭니다.

②-b 실행 루프 → AgentCore Harness

Beforeagent_runner.py가 Config 로드 → 위임을 도구로 변환 → 내부 도구 등록 → MCP 연결 → Strands Agent 조립 → 스트리밍 응답까지 직접 오케스트레이션했습니다([2.2]~[2.3]에서 해부한 바로 그 400줄).

After — AgentCore Harness는 모델·시스템 프롬프트·도구를 인라인으로 선언하면 Harness 정의와 호출 등 몇 번의 API 호출만으로 에이전트를 정의·실행해주는 관리형 에이전트 루프입니다. 오케스트레이션, 도구 실행, 메모리 관리, 응답 생성을 서비스가 담당합니다. 각 세션은 파일시스템·셸 접근이 가능한 격리 microVM에서 돌고, 필요하면 커스텀 컨테이너 이미지를 가져올 수도 있습니다. AgentCore Memory·Gateway·Browser·Code Interpreter·Observability와 통합됩니다.

다만 트레이드오프가 있습니다. 2부의 베이스 이미지는 “단일 이미지 × Config”라는 우리 고유의 조립 모델을 코드로 구현한 것이라, 그 동적 조립 UX(UI에서 부품을 고르면 즉시 카드가 됨)를 그대로 유지하려면 Harness 위에 여전히 얇은 어댑터가 필요할 수 있습니다. Harness는 “실행 루프를 직접 짜지 않아도 된다”를 해결하지, “우리 플랫폼의 조립 경험”까지 대체하지는 않습니다. 무엇이 남는지는 [3.5]에서 다룹니다.

[3.4] 대체 ③ — 자체 3중 관측성 → Observability + Evaluations

Before — 2부는 관측을 세 겹으로 직접 돌렸습니다([2.6]) — OTEL 스팬에 agent.id/session.id/phase를 주입하는 커스텀 SpanProcessor, Strands 도구 훅 이벤트, DynamoDB Side-Channel 세션 타임라인. Trace Viewer도 이 데이터를 직접 워터폴로 렌더링했습니다.

After — 두 관리형 서비스가 이를 받습니다.

  • AgentCore Observability — OTEL 호환 텔레메트리를 표준 형식으로 받아 에이전트 워크플로의 각 단계를 추적·디버그·모니터링하는 통합 뷰를 제공합니다. 우리가 직접 만든 SpanProcessor·Side-Channel·Trace Viewer가 하던 일을, 표준 OTEL 파이프라인과 CloudWatch 기반 통합 뷰로 대체합니다. 자체 계측을 유지하더라도 백엔드와 뷰어를 직접 운영할 필요가 사라집니다.
  • AgentCore Evaluations — 단순 추적을 넘어, 에이전트가 작업을 얼마나 잘 수행하고 엣지 케이스를 어떻게 다루는지를 자동·일관·데이터 기반으로 평가합니다. 세션·트레이스·도구 호출(tool call) 단위 평가를 지원하며 결과는 Observability에 통합됩니다. 2부에는 없던 계층입니다 — “조립한 에이전트를 배포 전후로 검증”하는 일을 운영 규율이 아니라 서비스로 수행하게 됩니다.

즉 관측성은 대체(Observability)에 더해 증강(Evaluations)까지 일어납니다. “추적 가능하게 만들어 신뢰한다”는 명제가, “추적 + 정량 평가로 신뢰한다”로 한 단계 올라갑니다.

[3.5] 그래서 무엇이 남는가 — 여전히 직접 지어야 하는 것

관리형으로 갈아 끼운 뒤에도 플랫폼이 통째로 사라지지는 않습니다. AgentCore는 이런 플랫폼을 위한 “paved path” 를 제공하지만, 조직 고유의 경험은 여전히 우리 몫입니다.

  • Control Plane / Builder UX — “자연어 요구 → 세 부품(Context Boundary·Gateway·Delegation)으로 분해 → 카드 조립”이라는 1부의 핵심 경험은 우리 제품의 정체성입니다. 이제 그 백엔드가 Registry·Policy·Harness를 호출하도록 바뀔 뿐, 조립 UX 자체는 직접 만듭니다.
  • Presentation Layer — CloudFront + VPC Origin + 내부 ALB + Cognito 인증 구성은 그대로 우리가 운영합니다.
  • 도구의 구현 — Gateway에 등록할 Lambda(boto3/CLI 기반 운영 도구)는 여전히 조직이 작성합니다. 관리형이 표준화·노출·거버넌스를 맡고, 무엇을 하는 도구인지는 우리가 만듭니다.

정리하면, 관리형 전환의 효과는 “차별화되지 않는 무거운 일(undifferentiated heavy lifting)을 걷어내는 것” 입니다. 레지스트리 백엔드, 정책 엔진, 실행 루프, 관측 백엔드 — 누가 만들어도 비슷하고 직접 운영하면 부담스러운 것들을 AgentCore에 넘기고, 우리는 조직 고유의 조립 경험과 도구 자산에 집중합니다.

[3.6] before/after 한눈에

전환은 한 번에 다 할 필요가 없습니다. 위험이 낮고 효과가 큰 것부터 단계적으로 옮기는 편이 현실적입니다 — 예를 들어 Policy(경계 강제를 코드 밖으로 — 거버넌스가 즉시 강화)Registry(GA 추적하며 병행)Observability/Evaluations(표준 OTEL이라 결합이 느슨해 안전하게 통합)Harness(조립 UX 어댑터 설계 후) 순서가 한 가지 합리적인 경로입니다. (전환 절차의 단계별 체크리스트는 글 끝의 [부록]에 정리했습니다.)

[3.7] 마무리

개인의 생산성 도구였던 AI 에이전트가 조직의 공유 자산이 되는 길목에는 세 가지 전환이 있습니다. 에이전트를 개발하지 않고 조립하고(Agent Builder), 도구를 표준 부품으로 재사용하고(MCP Gateway), 조립한 것을 추적 가능하게 만들어 신뢰합니다(관측성).

1부에서 우리는 그 조립의 청사진을 봤고, 2부에서는 “조립한 카드가 무슨 코드 위에서 도는가”를 따라 베이스 이미지를 해부했습니다 — 단일 이미지×Config, 지연 초기화, 경계 강제, depth 가드, 3중 관측, Graceful Degradation. 직접 지어봤기에 각 부품이 무슨 문제를 푸는지 알게 됐습니다.

3부에서는 그중 차별화되지 않는 부분을 AgentCore 관리형 서비스로 갈아 끼워 더 적은 코드로 같은 — 더 강한 — 보증을 얻는 길을 봤습니다. 카탈로그는 Registry로, 경계는 Policy로, 실행 루프는 Harness로, 관측은 Observability와 Evaluations로 내려갑니다. 우리에게 남는 것은 조직 고유의 조립 경험과 도구 자산 — 정확히 차별화되는 부분입니다.

개인의 도구가 조직의 자산이 되는 여정에서, 직접 지어 증명한 패턴을 관리형 기반 위에 다시 세우는 것이 다음 한 걸음입니다. 전체 코드는 aws-samples GitHub에 있으며, 클론한 뒤 deploy-all.sh를 실행하면 15분 안에 직접 에이전트를 조립하고 Playground에서 테스트해 볼 수 있습니다. 관리형 전환 버전은 각 서비스의 GA 진행에 맞춰 별도 브랜치로 업데이트할 예정입니다.

부록 · 관리형 전환 절차 개요

AgentCore 공식 문서가 제공하는 각 서비스별 Getting Started를 따르면, 전환은 크게 세 단계로 진행됩니다.

1. 서비스 생성 — 콘솔 또는 CLI/SDK로 Registry, Policy Engine, Harness를 각각 생성합니다.
2. 기존 로직 마이그레이션 — 자체 구현 코드를 관리형 API 호출로 교체합니다.

  • DynamoDB 카드 CRUD → Registry API (publish_registry_record)
  • persona-injection + depth 가드 → Policy (Cedar 정책 + Gateway 강제)
  • 직접 구현한 실행 루프 → Harness (격리 microVM + create/invoke API)
  • 3중 관측 + Side-Channel → Observability (통합 뷰) + Evaluations (자동 평가)

3. 병행 운영 → 절체 — Preview 상태 서비스(Registry)는 기존 구현과 병행하면서 점진적으로 트래픽을 이전합니다.

권장 전환 순서(거버넌스 우선): Policy(즉시 차단 가능) → Registry(검색/발견) → Observability+Evaluations(통합 뷰) → Harness(실행 루프 단순화). Policy부터 시작하면 기존 코드 대부분을 유지하면서도 거버넌스가 즉시 강화됩니다. (본문 [3.6]도 같은 순서를 제시합니다. 거버넌스 강화(Policy 우선)와 결합도(Observability/Evaluations의 느슨한 결합)라는 두 관점이 같은 결론으로 수렴합니다.)

서비스별 공식 가이드

참고

Youngmin Joung

Youngmin Joung

정영민 솔루션스 아키텍트는 클라우드에 기반한 대 규모 서비스의 DevOps 및 SRE 엔지니어 경험을 통해 엔터프라이즈 고객이 비즈니스 목표를 달성하기위해 적절한 아키텍트를 제안하고, 고객의 기술적 문제를 해결하는데 도움을 드리는 역할을 수행합니다. 특히 Observability 도메인에 대한 전문성을 통해 고객 비즈니스의 문제 해결을 지원합니다.