
직접 에이전트 루프를 작성하지 않은 이유: AgentCore Harness의 필요성
요약
AWS Bedrock AgentCore Harness를 사용하여 에이전트 루프를 직접 구현하는 대신 관리형 프레임워크를 활용한 사례를 설명합니다. 이를 통해 설정 비용, 반복 속도, 도구 통합 문제를 해결하고 프롬프트 튜닝 및 도구 연결 효율을 극대화하는 방법을 다룹니다.
핵심 포인트
- 직접 구현한 루프 대비 빠른 반복 속도 확보 (재배포 없이 약 20초 내 업데이트)
- 모델, 프롬프트, 도구, 메모리 등 구성 요소의 유연한 연결 가능
- 도구 통합(Tool integration)을 코드가 아닌 설정(Configuration)으로 처리
- 보안을 위해 모델의 쉘 접근 권한과 파일 시스템 스킬을 의도적으로 분리
요약: 저는 직접 에이전트 루프를 작성하는 대신 이 프로젝트를 AWS Bedrock AgentCore Harness에 구현했습니다. 그 이유는 수동으로 만든 루프는 판매 자격 심사와는 아무 관련 없는 설정 작업, 느린 반복 속도, 도구 통합 배관(plumbing) 비용을 발생시켰을 것이기 때문입니다. Harness는 이름과 실행 역할이라는 두 가지 필수 매개변수만 필요하며, 나머지 모든 구성 요소(모델, 프롬프트, 도구, 스킬, 메모리)는 나중에 연결되어 약 20초 만에 제자리에 업데이트됩니다. 가장 중요한 발견은 다음과 같습니다: 파일 시스템용 Skill을 위해 harness를 컨테이너 백킹하는 것이 모델에게 쉘 접근 권한을 부여해야 함을 의미하지 않으며, 저는 이 두 가지 결정을 의도적으로 분리했습니다.
이 글은
하네스 (Harness)가 해결하는 문제
에이전트 루프 (Agent loop) 자체는 주말 동안 할 수 있는 프로젝트 수준입니다. 대화 내용과 도구 정의 (tool definitions)를 모델에 보내고, 텍스트나 도구 호출 (tool call)을 읽어 들인 뒤, 도구를 실행하고, 그 결과를 추가하며, 모델이 더 이상 도구를 요청하지 않을 때까지 반복하는 것이죠. 저는 이전에 그런 루프를 작성해 본 적이 있습니다. 만약 그것이 작업의 전부였다면, 이 글은 존재하지 않았을 것입니다.
비용은 루프를 둘러싼 모든 요소에서 발생합니다. 관리형 런타임 (managed runtime) 없이 이 데모에 무엇이 필요할지 가격을 산출해 보았을 때, 목록은 세 가지 범주로 나뉘었습니다.
설정 (Setup). 루프가 단 한 번 실행되기 전에 모델 배선 (model wiring), 오케스트레이션 프레임워크 (orchestration framework), 루프를 호스팅할 런타임 환경 (runtime environment), 해당 런타임의 배포 경로 (deployment path), 그리고 그 위에 메모리 통합 (memory integration)이 필요합니다.
반복 속도 (Iteration speed). 직접 만든 (hand-rolled) 루프는 구성 (configuration)이 소유하고 있는 서비스에 내장되므로, 모든 변경 사항은 해당 서비스의 재배포를 의미합니다. 새로운 도구, 수정된 시스템 프롬프트 (system prompt), 다른 모델 등이 모두 마찬가지입니다. 이 빌드는 실제 테스트 결과에 따라 시스템 프롬프트를 반복적으로 재작업했는데, 프롬프트가 애플리케이션 코드 (application code)가 아닌 하네스 설정 (harness configuration)이기 때문에, 각 재작업은 재배포 없이 약 20초 만에 완료되는 업데이트 적용 과정이었습니다. 이 차이는 복리로 쌓입니다. 시도당 20초가 걸리는 프롬프트 튜닝 (Prompt tuning)은 시도당 컨테이너 빌드 한 번이 걸리는 프롬프트 튜닝과는 차원이 다른 활동입니다.
도구 통합 (Tool integration). 메모리, 검색 (retrieval), 브라우저, 코드 실행 (code execution), 그리고 외부 API는 모두 서로 다르게 연결되며, 각 소스는 고유한 전송 (transport) 및 인증 (auth) 방식을 가집니다. 직접 만든 루프에서는 공통 커넥터 계층 (common connector layer)을 무료로 얻을 수 없습니다. 이 데모는 4개의 타겟에 걸쳐 Gateway가 전면에 배치된 6개의 도구, 파일 시스템 스킬 (filesystem Skill), 그리고 메모리 저장소 (memory store)를 연결합니다. 이러한 통합 작업은 제가 유지 관리해야 하는 코드가 아니라 하네스의 설정 (configuration)일 뿐이며, 존재하는 코드는 Gateway 뒤에 위치하며 루프 내부에는 절대 존재하지 않습니다.
구체적으로 말하자면, 하네스 (harness)가 없었다면 이 프로젝트는 에이전트가 단 하나의 BANT (budget, authority, need, timeline) 자격 확인 질문에 답하기 전에, 자체적인 에이전트 루프 (agent loop), 자체적인 도구 호출 로직 (tool-dispatch logic), 자체적인 세션 및 메모리 연결 (session and memory wiring), 그리고 자체적인 컨테이너 오케스트레이션 (container orchestration)을 모두 갖추어야 했을 것입니다. 데모의 가치는 도구 (tools), 가드레일 (guardrails), 그리고 프롬프트 (prompt)에 있습니다. 이 중 그 어느 것도 루프 (loop) 안에 존재하지 않습니다.
이 세 가지 범주는 재사용 가능한 핵심 교훈이기도 합니다. 여러분이 계획 중인 빌드에 대해 이 항목들을 체크리스트로 실행해 보십시오. 만약 도구가 하나뿐이고, 메모리 요구 사항이 없으며, 루프를 호스팅할 수 있는 기존 서비스가 있다면, 직접 구현 (hand-rolling)하는 것이 정당화될 수 있으며 루프의 내부 동작을 완전히 제어할 수 있습니다. 하지만 이 빌드는 세 가지 항목 모두에서 체크리스트를 통과하지 못했으며, 바로 그 점이 현재 일반적으로 사용 가능(GA) [1]한 AgentCore Harness를 제 첫 번째 아티클 아키텍처의 중심에 두게 된 이유입니다.
하네스 (harness)의 실제 정의
하네스 (harness)는 완전히 관리되는 에이전트 루프 (agent loop)입니다. 여러분은 AgentCore에 설정 (configuration)을 전달하기만 하면 됩니다: 어떤 모델을 사용할지, 어떤 시스템 프롬프트 (system prompt)를 사용할지, 어떤 도구와 기술 (tools and skills)을 사용할지, 그리고 어떤 메모리 (memory)를 사용할지 말입니다. AgentCore는 언제 답변하고 언제 도구를 호출할지 [2] 결정하는 루프를 실행합니다. 작성해야 할 프레임워크 (framework) 코드도 없고, 계속 유지해야 할 자체 서비스도 없습니다.
AWS Management Console에서 하네스는 Amazon Bedrock AgentCore 콘솔의 Build 항목 아래에 Runtime, Gateways, Memory, Policy, Identity와 함께 위치하며, 별도의 Test 섹션에는 이 아티클의 뒷부분에서 사용될 하네스 플레이그라운드 (harness playground)가 마련되어 있습니다.
API는 정확히 두 가지, 즉 harness 이름과 실행 역할 (execution role) ARN을 요구하며, 콘솔의 생성 양식도 이 분할을 그대로 반영합니다. 해당 양식의 다른 모든 항목은 선택 사항입니다: 모델 (model), 시스템 프롬프트 (system prompt), 도구 (tools), 기술 (skills), 메모리 (memory), 타임아웃 (timeouts), 환경 설정 (environment settings). 이 모든 것은 나중에 업데이트를 통해 추가할 수 있으며, 대부분은 호출 (invocation) 시마다 재정의 (override) 할 수도 있습니다 [2]. 관리 콘솔 (Management console)에서 AWS는 harness 이름을 기반으로 실행 역할 생성을 처리합니다. 실제로 시작하는 데는 harness의 이름만 있으면 됩니다.
이 두 가지 파라미터만 필요하다는 주장은 콘솔 외부에서도 유효합니다. aws_bedrockagentcore_harness에 대한 Terraform 프로바이더 (provider) 스키마는 harness_name과 execution_role_arn 단 두 개의 최상위 속성만을 필수(required)로 표시하며, boto3 호출은 다음과 같이 간단합니다 [2]:
import boto3
client = boto3.client("bedrock-agentcore-control")
...
이 프로젝트에서 사용하는 선택적 블록 (optional blocks)들을 포함한 boto3 대응 방식의 전체적인 형태는 다음과 같습니다:
harness = client.create_harness(
harness_name="MyFirstAgentWithHarness",
executionRoleArn="<your_harness_execution_role>",
...
거기에 사용된 모델 ID는 이 프로젝트가 배포하는 모델인 eu.anthropic.claude-sonnet-5로, 단순한 모델 ID가 아닌 EU 교차 리전 추론 프로필 (EU cross-region inference profile)입니다. 제가 배포한 harness는 결과적으로 이 API가 제공하는 주요 선택적 블록 대부분을 사용했습니다:
- 모델 (model) 블록
- 규칙 번호가 매겨진 긴 시스템 프롬프트 (system prompt)
- Gateway 도구 바인딩 (tool binding)
- 기술 (Skill)
- 메모리 부착 (memory attachment)
- 명시적인 도구 허용 목록 (tool allowlist)
첫 번째 하네스 (harness)를 실행하는 데 이 중 그 어떤 것도 필요하지 않았습니다. 저는 두 개의 파라미터(parameter) 형태에서 시작하여 설정을 하나씩 적용하며 확장해 나갔으며, 다음과 같은 순서를 권장합니다: 첫 배포 시 이름(name)과 역할(role)을 설정하고, 이후 빌드 요구 사항에 따라 각 선택적 블록(optional block)을 추가하는 방식입니다. 또한 CLI를 통한 중간 경로도 있습니다: agentcore create는 새로운 에이전트 프로젝트의 스캐폴딩 (scaffolding)을 수행하며, 마법사(wizard)가 제공하는 유형 중 하나로 하네스 프로젝트를 포함합니다. 이는 플래그(flags)를 사용하여 비대화형으로 생성할 수 있습니다 [3].
제어 평면 (Control plane) 대 데이터 평면 (Data plane)
AgentCore는 하네스 API를 두 가지로 분리합니다. 다섯 가지 제어 평면 (control-plane) 작업은 리소스로서 하네스를 관리합니다: CreateHarness, GetHarness, ListHarnesses, UpdateHarness, DeleteHarness. 그리고 이를 실행하는 하나의 데이터 평면 (data-plane) 작업이 있습니다: InvokeHarness [4]. Boto3는 이 분리를 bedrock-agentcore-control과 bedrock-agentcore라는 두 개의 별도 클라이언트(client)로 제공합니다. 저는 빌드 시작 시 문서(documentation)를 신뢰하는 대신 설치된 SDK(boto3 1.43.56)의 사용 가능한 서비스 목록을 나열하여 이를 매핑했습니다. 해당 서비스가 매우 최신이었기 때문에 SDK와 문서의 내용이 다를 수 있었기 때문입니다.
이러한 분리는 제 빌드 과정에서 물리적으로 나타납니다. Terraform의 aws_bedrockagentcore_harness 리소스는 제어 평면을 래핑 (wrap)합니다: plan 및 apply 단계는 Create, Get, Update 호출로 변환됩니다. 첫 번째 기사의 Dynamic content에 등장했던 스트리밍 릴레이 (streaming relay) Lambda는 요청 시점에 JavaScript SDK의 InvokeHarnessCommand를 통해 데이터 평면만 사용합니다. 두 개의 SDK 인터페이스, 두 개의 클라이언트, 그리고 겹치는 부분은 없습니다.
이러한 분리는 보안 특성 (security property)을 확보해 줍니다. 릴레이의 실행 역할 (execution role)은 데이터 평면 호출 권한 (invoke grant)만 보유하며 제어 평면의 권한은 전혀 가지고 있지 않으므로, 최종 사용자를 위해 에이전트를 실행하는 컴포넌트가 에이전트를 재설정(reconfigure)할 수 없습니다. Terraform은 제어 평면 자격 증명 (credentials)을 보유하며 요청 시점에는 실행되지 않습니다. 하네스를 구성하는 것과 하네스를 실행하는 것은 두 개의 서로 다른 API에 대한 두 개의 별개 작업입니다. 이것이 바로 Terraform 코드와 릴레이 Lambda가 서로의 호출에 관여하지 않는 이유이며, 더 유용하게는, 서로 관여할 수 없는 이유입니다.
설정에서 실행 중인 에이전트로

왼쪽에서 오른쪽으로 읽어보세요. 에이전트 설정 (agent configuration)에는 모델 (model), 시스템 프롬프트 (system prompt), 도구 및 기술 (tools and skills), 그리고 그 외 모든 것 (타임아웃, 반복 제한, 환경)의 네 가지 행이 포함됩니다. 도구 (tools) 행에는 여섯 가지 종류의 도구 소스 (tool source)가 공급될 수 있습니다: 원격 MCP 서버, AgentCore 게이트웨이 (Gateway), 관리형 브라우저 (managed Browser), 관리형 코드 인터프리터 (managed Code Interpreter), 클라이언트 측에서 실행되는 인라인 도구 (inline tool), 그리고 기술 (Skill)입니다. 설정된 하네스 (harness)는 그런 다음 오른쪽에 있는 플랫폼 기능 (platform capabilities)을 활용합니다: 런타임 (Runtime)은 이를 호스팅하고, 메모리 (Memory)는 세션 간에 유지되며, 아이덴티티 (Identity)는 인바운드 인증 (inbound auth)을 처리하고, 관찰성 (Observability)은 이를 추적 (trace)합니다. 이 모든 것의 밑바탕에는 하네스가 상속받는 런타임 기능 (runtime features)들이 자리 잡고 있습니다: 장기 실행 세션 (long-running sessions), 연결 가능한 스토리지 (attachable storage), 셸 명령 실행 (shell command execution), 마이크로VM 격리 (microVM isolation), 그리고 사용자 정의 컨테이너 이미지 (bring your own container image) 옵션입니다 [2].
이 다이어그램을 본 프로젝트에 매핑하면 정직하게 유지할 수 있습니다. 여섯 가지 도구 소스 중 저는 두 가지를 사용합니다: 첫 번째 기사(article 1)의 여섯 가지 게이트웨이 대상 (Gateway targets) 전면에 배치된 하나의 게이트웨이 바인딩 (Gateway binding) (네 번째 기사(article 4)에서 이를 확장합니다), 그리고 하나의 기술 (Skill)입니다. 기능 (capabilities) 열의 경우, 런타임 (Runtime)과 메모리 (Memory)는 핵심적인 역할을 하며 (세 번째 기사(article 3)에서 두 가지 모두를 설정합니다), 관찰성 (Observability)은 제가 나중에 프롬프트 준수 (prompt-compliance) 문제를 측정하는 데 사용한 도구 호출 추적 (tool-call traces)을 제공했습니다 (다섯 번째 기사(article 5)에서 해당 이야기를 수치화합니다). 그리고 아이덴티티 (Identity)는 사용되지 않습니다: 첫 번째 기사(article 1)의 인바운드 인증 (inbound-auth) 조사에서 왜 토큰 검증 릴레이 (token-validating relay)가 이를 이겼는지 설명합니다. 마지막 행의 컨테이너 옵션은 기본값이 아니라 단 하나의 특정한 이유를 위해 이 빌드에 포함되었습니다.
그 이유는 바로 스킬 (Skills)입니다. 스킬 (Skill)은 지침 마크다운 (instruction markdown)과 지원 파일들의 디렉토리이며, 때로는 스크립트 (scripts)를 포함하기도 합니다. 하네스 (harness)는 모델을 위해 이를 필요할 때마다 로드하며, 이 프로젝트가 사용하는 경로 형식의 skills 파라미터 (parameter)는 이미 하네스 파일 시스템 (filesystem)에 존재하는 콘텐츠를 가리킵니다. 하네스 자체는 아무것도 업로드하거나 설치하지 않습니다 [5]. 순수하게 선언적인 (declarative) 하네스는 가리킬 파일 시스템이 없으므로, 스킬 디렉토리는 다른 방식으로 전달되어야 하며, 이 프로젝트는 이를 커스텀 컨테이너 이미지 (custom container image)에 포함하여 배포하는 방식을 사용합니다. 전체 Dockerfile은 FROM과 COPY로 구성되어 있습니다. 즉, 이미지는 순수하게 파일 시스템 레이어 (filesystem layer)일 뿐이며, 결코 저의 실행 프로세스 (running process)가 아닙니다 [6].
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
