Bedrock AgentCore 에이전트에 관측성과 트레이싱 추가하기
AWS AgentCore에서 프로덕션용 AI 에이전트를 운영하고, Weave에 연동해 단계별 관측 가능성을 구현하는 실용 가이드입니다. 설정, 계측, 트레이스 기반 디버깅 예제를 포함합니다. 이 글은 AI 번역본입니다. 오역이 의심되면 댓글로 알려주세요.
Created on September 12|Last edited on September 12
Comment
AgentCore 프로덕션에서 AI 에이전트를 구축·운영·확장하기 위한 AWS의 프레임워크입니다. 인프라를 관리하지 않고도 개발자가 에이전트 로직에 집중할 수 있도록 런타임 환경, 아이덴티티 및 메모리 서비스, Browser·Code Interpreter·Gateway 같은 내장 도구를 제공합니다. 또한 여러 에이전트 프레임워크와 모델을 지원하고, 보안이 강화된 격리 실행을 제공하며, 인증·데이터 액세스·배포를 위해 AWS 서비스와 통합됩니다.
Weave는 Weights & Biases에서 제공하는 AI 애플리케이션용 모니터링 및 관측 가능성 플랫폼입니다. 모든 함수 호출의 입력, 출력, 메타데이터, 예외를 포함한 상세 실행 트레이스를 기록하고, 이를 탐색 가능한 트리로 연결합니다. 많은 LLM 라이브러리와 바로 연동되며, 데코레이터를 통해 커스텀 애플리케이션 로직도 추적할 수 있습니다. 이러한 트레이스를 지연 시간, 토큰 수, 비용과 같은 지표와 결합해, Weave는 개발자가 AI 시스템을 디버그하고 최적화하며 평가하도록 돕습니다.
함께 사용하면, AgentCore는 프로덕션 에이전트를 실행하는 환경을 제공하고, Weave는 그 에이전트가 실제로 어떻게 동작하는지를 관찰하는 렌즈를 제공합니다. 즉, AgentCore 내부에서 에이전트가 수행하는 모든 추론 단계, 모든 도구 호출, 모든 API 상호작용을 Weave의 트레이스 뷰에 모두 기록해 확인할 수 있습니다. 여러 도구를 호출하고, 추론 단계를 체인으로 연결하며, 외부 API와 상호작용하는 복잡한 에이전트를 만드는 팀에게 이 조합은 운영 측 신뢰성과 개발 측 가시성을 동시에 제공합니다.
이제부터는 Weave를 AgentCore 에이전트 런타임에 통합하는 방법을 단계별로 살펴보겠습니다. 에이전트 내부에서 Weave를 초기화하고, 내장 도구와 커스텀 로직을 모두 계측하며, 실행 트레이스를 전송해 현대적인 AI 애플리케이션에서 기대하는 것과 같은 세밀한 수준으로 AgentCore 기반 에이전트를 모니터링하고 디버그하는 방법을 확인하게 됩니다.

Bedrock AgentCore란 무엇인가요?
AgentCore는 보안성, 확장성, 유연성을 갖춘 방식으로 AI 에이전트를 호스팅하고 운영하도록 설계된 AWS 플랫폼입니다. 단일 모놀리식 서비스가 아니라, 필요에 따라 함께 또는 개별적으로 사용할 수 있는 구성 요소들의 집합으로 이루어져 있습니다. 중심에는 에이전트 또는 도구를 격리된 컨테이너에서 실행하는 관리형 실행 환경인 AgentCore Runtime이 있습니다. 이러한 격리는 각 에이전트 세션의 보안을 보장하는 동시에, 서버 관리에 신경 쓰지 않고도 최대 8시간에 이르는 장시간 작업을 수행할 수 있게 해 줍니다.
AgentCore는 LangGraph, CrewAI, Strands Agents 등 여러 에이전트 프레임워크를 지원하며, 사용자가 선택하는 어떤 대규모 언어 모델과도 함께 작동합니다. 런타임은 대용량 페이로드와 멀티모달 입력을 처리하고, AWS 인증과 자연스럽게 통합되어 에이전트가 AWS 리소스와 외부 API에 안전하게 접근하고 상호작용할 수 있도록 합니다.
런타임을 넘어, AgentCore는 아이덴티티, 메모리, 관측 가능성 관리를 위한 서비스도 포함합니다. 아이덴티티 서비스는 인증과 권한 부여를 제어해 최소 권한 접근을 쉽게 적용할 수 있게 합니다. 메모리 서비스는 에이전트에 단기 및 장기 컨텍스트 저장소를 제공하여, 대화 전반에 걸쳐 상태를 유지하거나 에이전트 간 지식을 공유할 수 있도록 합니다.
또한 웹에서 데이터를 가져오는 Browser 도구, 격리된 환경에서 코드를 실행하는 Code Interpreter, API를 에이전트가 호출 가능한 함수로 바꿔 주는 Gateway 같은 내장 도구도 함께 제공합니다. 관측 기능을 통해 에이전트를 트레이스하고 디버그하며, 모니터링 시스템과 연동하고, 시간에 따른 성능을 추적할 수 있습니다. 요약하면, AgentCore는 지원 시스템을 직접 구축하고 유지하지 않아도 AI 에이전트를 프로토타입에서 프로덕션으로 옮기는 데 필요한 인프라와 서비스를 제공합니다.
에이전트 관측 가능성이 중요한 이유
관측 가능성은 시스템이 생성하는 데이터를 살펴봄으로써 시스템 내부에서 정확히 무엇이 일어나는지 이해할 수 있는 능력을 의미합니다. AI 에이전트의 경우, 이는 에이전트가 취하는 모든 단계, 내리는 모든 결정, 외부 시스템과 수행하는 모든 상호작용을 가시화하는 것을 뜻합니다. 관측 가능성이 없으면 문제는 진단하고 해결하기 어려운 장애를 일으킬 때까지 숨겨진 채 남아 있을 수 있습니다. AgentCore 환경에서는 에이전트가 복잡해져 추론 단계를 체인으로 연결하고, 여러 도구를 호출하며, 다양한 출���의 데이터를 처리하고, 여러 API와 상호작용할 수 있습니다. 문제가 발생했을 때는 에이전트가 무엇을 했고 왜 그렇게 했는지에 대한 명확한 기록이 필요하며, 관측 가능성은 입력, 출력, 타이밍, 오류, 기타 메타데이터를 수집하여 그 기록을 제공합니다.
이러한 가시성은 디버깅을 더 효율적으로 만들어 문제를 찾고 수정하는 데 걸리는 시간을 줄여 줄 뿐만 아니라, 성능과 비용 최적화에도 유용합니다. 에이전트가 도구를 어떻게 사용하는지, 각 단계가 얼마나 오래 걸리는지, 특정 패턴이 얼마나 자주 나타나는지를 모니터링하면 워크플로를 간소화하거나 비용이 큰 모델에 대한 불필요한 호출을 줄일 기회를 파악할 수 있습니다. 프로덕션 시스템에서는 관측 가능성이 신뢰의 기반이기도 합니다. 에이전트가 사용자나 중요한 비즈니스 프로세스에 영향을 주는 결정을 내린다면, 그 행동을 투명하게 들여다볼 수 있어야 하며, 실행을 추적할 수 있어야 의도한 대로 동작하는지와 컴플라이언스 또는 거버넌스 요구 사항을 충족하는지를 보장할 수 있습니다.
AgentCore의 내장 관측 기능과 Weave 같은 외부 도구를 함께 사용하면, 런타임 수준의 메트릭과 풍부한 실행 트레이스를 결합해 에이전트 동작을 전체적으로 파악할 수 있으며, 이를 통해 신속한 문제 해결과 지속적인 개선을 모두 달성할 수 있습니다.
AgentCore 시작하기
이 섹션에서는 Strands 프레임워크와 몇 가지 기본 도구를 사용해 기본적인 AgentCore 에이전트를 만든 뒤, 이를 AgentCore에 배포합니다. 이를 통해 AWS의 관리형 환경에서 에이전트를 실행하기 위한 최소한이지만 실용적인 시작점을 마련할 수 있습니다. AWS 설정과 에이전트를 AgentCore에 배포하는 단계별 안내는 튜토리얼을 참고하세요. 여기.
먼저 관측 기능을 켜지 않은 상태로 에이전트를 만들어, 기본 설정을 확인해 보겠습니다:
from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom typing import Dict, Anyfrom datetime import datetimefrom strands import Agent, toolfrom strands.models import BedrockModel### --- Define your tools@tooldef word_count(text: str) -> int:"""Returns the number of words in the text."""return len(text.split())@tooldef reverse(text: str) -> str:"""Reverses the input string."""return text[::-1]### --- Configure your Bedrock model providerbedrock_model = BedrockModel(model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",temperature=0.3,streaming=False, # Set to True if you want streaming outputregion_name="us-east-1", # <--- your AWS region)### --- Initialize the agent WITH toolsstrands_agent = Agent(model=bedrock_model,tools=[word_count, reverse],system_prompt="You are a helpful assistant who uses tools when they help.")### --- FastAPI app setupapp = FastAPI(title="Strands Agent Server",version="1.0.0")class InvocationRequest(BaseModel):input: Dict[str, Any]class InvocationResponse(BaseModel):output: Dict[str, Any]@app.post("/invocations", response_model=InvocationResponse)async def invoke_agent(request: InvocationRequest):try:user_message = request.input.get("prompt", "")if not user_message:raise HTTPException(status_code=400,detail="No prompt found in input. Please provide a 'prompt' key in the input.")# Call the strands agent synchronouslyresult = strands_agent(user_message)response = {"message": result.message, # agent reply object"timestamp": datetime.utcnow().isoformat(),"model": bedrock_model.config["model_id"],}return InvocationResponse(output=response)except Exception as e:raise HTTPException(status_code=500, detail=f"Agent processing failed: {str(e)}")@app.get("/ping")async def ping():return {"status": "healthy"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8080)
전반적으로, 이 코드는 두 가지 도구를 갖춘 AI 에이전트를 래핑하는 API를 정의합니다. 해당 도구는 다음과 같습니다 word_count, 문자열에 포함된 단어 수를 반환하며, 그리고 reverse, 문자열을 뒤집습니다. Bedrock에서 Anthropic Claude 3.5 Sonnet 모델을 구성한 다음, 해당 도구들을 호출할 수 있는 Strands 에이전트를 빌드합니다. FastAPI 서버는 두 개의 엔드포인트를 노출합니다. POST /invocations 는 request.input.prompt를 읽고 에이전트를 실행한 뒤 에이전트의 응답, 타임스탬프, 모델 ID를 반환합니다. 프롬프트가 없으면 400을, 그 외 오류에는 500을 발생시킵니다. GET /ping 간단한 상태 확인을 반환합니다. 메인 블록에서는 uvicorn을 실행합니다.
이제 에이전트에 관측 기능을 추가하겠습니다. AWS도 에이전트 모니터링을 위한 통합 옵션을 제공하지만, 저는 세밀한 트레이스 수집, 인터페이스, 전반적인 개발자 경험 측면에서 Weave를 선호합니다. 대시보드는 완성도가 높고 사용자화가 가능하며, 트레이스를 탐색하고 실행을 비교하며 세부 내용을 빠르게 파고들 수 있도록 설계되었습니다.
Weave SDK를 사용하면 Anthropic, OpenAI 같은 LLM 제공자와 바로 연동되어 즉시 동작하며, 로컬 개발에서의 워크플로가 Agent Runtime 내부에 배포했을 때와 동일하기 때문에 개발에서 프로덕션으로의 전환이 쉽습니다. 입력, 출력, 메타데이터, 토큰 사용량, 예외를 자동으로 수집해 벤더에 종속되지 않은 대시보드로 기록하는 반면, AWS 로그는 주로 CloudWatch 콘솔에서 확인하게 됩니다.
Agent Runtime에 배포했을 때 Weave는 유연성을 제공합니다. 원한다면 네이티브 Weave SDK를 사용할 수 있고 @weave.op 및 해당 UI를 그대로 활용하거나, AgentCore가 내부적으로 이미 사용하는 OpenTelemetry를 통해 통합할 수도 있습니다. 이 이중 옵션 덕분에 처음에는 Weave의 자체 대시보드로 시작한 뒤, 이후 필요에 따라 다른 OpenTelemetry 기반 관측 시스템과 결합할지 결정할 수 있습니다. 또한 AWS 전용 도구에 비해 과도한 벤더 종속을 피할 수 있어, GenAI 애플리케이션을 더 이식성 높고 사용자 친화적인 방식으로 모니터링할 수 있습니다. 제 말만 믿지 마세요. 어떤 솔루션이 가장 적합한지 확실히 알 수 있는 훌륭한 방법은 두 옵션을 모두 실제 환경에 배포해 일정 기간 운영해 보고, 장기적으로 하나를 선택하기 전에 경험을 비교해 보는 것입니다.
AgentCore 에이전트에 Weave 를 추가하는 방법은 몇 가지가 있습니다. 가장 직접적인 방법은 추적하려는 함수나 메서드에 데코레이터를 붙이는 것입니다 @weave.op, 에이전트 코드에서 Weave를 초기화하고, 런타임에 weave Python 패키지가 포함되어 있는지 확인하세요. AgentCore는 Docker 위에 구축되어 있으므로 컨테이너로 배포한다면 다음을 추가하기만 하면 됩니다 uv add weave 그래서 런타임에 weave 패키지가 추가되도록 합니다 uv.lock 에 해당하는 Dockerfile 배포 전에 이미지에 Weave가 설치되었는지 확인하기 위해서입니다.
import osimport jsonfrom datetime import datetimefrom fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom typing import Dict, Anyimport weavefrom strands import Agent, toolfrom strands.models import BedrockModel# Export WANDB key for Weave authos.environ["WANDB_API_KEY"] = "your_api_key"WEAVE_PROJECT = os.getenv("WEAVE_PROJECT", "your_wandb_username/wand_project_name")weave.init(WEAVE_PROJECT)@weave.op()def word_count_op(text: str) -> int:return len(text.split())@weave.op()def reverse_op(text: str) -> str:return text[::-1]def get_agent():@tooldef word_count(text: str) -> int:return word_count_op(text)@tooldef reverse(text: str) -> str:return reverse_op(text)bedrock_model = BedrockModel(model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",temperature=0.3,streaming=False,region_name="us-east-1",)return Agent(model=bedrock_model,tools=[word_count, reverse],system_prompt="You are a helpful assistant who uses tools when they help.",)@weave.op()def run_agent(agent: Agent, user_message: str) -> Dict[str, Any]:result = agent(user_message)return {"message": result.message,"model": agent.model.config["model_id"],}app = FastAPI()class InvocationRequest(BaseModel):input: Dict[str, Any]class InvocationResponse(BaseModel):output: Dict[str, Any]@app.post("/invocations", response_model=InvocationResponse)async def invoke_agent(body: InvocationRequest):agent = get_agent()user_message = body.input.get("prompt", "")if not user_message:raise HTTPException(400, "No prompt found; provide 'prompt' key.")result = run_agent(agent, user_message)response = {"message": result["message"],"timestamp": datetime.utcnow().isoformat(),"model": result["model"],}return InvocationResponse(output=response)@app.get("/ping")async def ping():return {"status": "healthy"}
여기서는 Weave가 다음으로 초기화됩니다 weave.init, 그리고 함수에는 @weave.op 호출을 자동으로 캡처합니다. 모든 입력, 출력, 그리고 도구 호출이 Weave에 기록되므로, 스팬을 수동으로 만들지 않아도 각 실행에서 무슨 일이 있었는지 정확히 확인할 수 있습니다. 이 방식은 계측이 더 간단하고, 기본 설정만으로도 더 풍부하고 구조화된 트레이스를 제공합니다.
Weave는 애플리케이션에서 다양한 백엔드로 트레이스와 메트릭을 전송하기 위한 표준인 OpenTelemetry와도 함께 사용할 수 있습니다. 이를 통해 Weave의 상세한 실행 트레이스를 기존에 사용 중인 다른 관측 시스템이나 대시보드와 연결할 수 있습니다.
import functoolsimport base64import osimport jsonfrom datetime import datetimefrom fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom typing import Dict, Anyfrom opentelemetry import tracefrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk.trace.export import SimpleSpanProcessorfrom strands import Agent, toolfrom strands.models import BedrockModel# ─── Weave OTLP setup ────────────────────────────────────────────────────────────WANDB_API_KEY = os.getenv("WANDB_API_KEY", "your_api_key")WEAVE_PROJECT = "your_wandb_username/wand_project_name"auth_b64 = base64.b64encode(f"api:{WANDB_API_KEY}".encode()).decode()exporter = OTLPSpanExporter(endpoint="https://trace.wandb.ai/otel/v1/traces",headers={"Authorization": f"Basic {auth_b64}", "project_id": WEAVE_PROJECT},)provider = TracerProvider()provider.add_span_processor(SimpleSpanProcessor(exporter))trace.set_tracer_provider(provider)tracer = trace.get_tracer("strands-agent")# ────────────────────────────────────────────────────────────────────────────────def tool_logger(tool_calls):def decorator(fn):@functools.wraps(fn)def wrapper(*args, **kwargs):result = fn(*args, **kwargs)tool_calls.append({"tool_name": fn.__name__,"tool_input": {"args": args, "kwargs": kwargs},"tool_output": result,})return resultreturn wrapperreturn decoratordef get_agent(tool_calls):@tooldef word_count(text: str) -> int:return tool_logger(tool_calls)(lambda t: len(t.split()))(text)@tooldef reverse(text: str) -> str:return tool_logger(tool_calls)(lambda t: t[::-1])(text)bedrock_model = BedrockModel(model_id="anthropic.claude-3-5-sonnet-20240620-v1:0",temperature=0.3,streaming=False,region_name="us-east-1",)return Agent(model=bedrock_model,tools=[word_count, reverse],system_prompt="You are a helpful assistant who uses tools when they help.",)app = FastAPI()class InvocationRequest(BaseModel):input: Dict[str, Any]class InvocationResponse(BaseModel):output: Dict[str, Any]@app.post("/invocations", response_model=InvocationResponse)async def invoke_agent(body: InvocationRequest):tool_calls = []agent = get_agent(tool_calls)user_message = body.input.get("prompt", "")if not user_message:raise HTTPException(400, "No prompt found; provide 'prompt' key.")with tracer.start_as_current_span("invoke_agent") as span:span.set_attribute("input.value", json.dumps({"prompt": user_message}))result = agent(user_message)span.set_attribute("output.value", json.dumps({"message": result.message,"model": agent.model.config["model_id"],"tool_calls": tool_calls,}))response = {"message": result.message,"timestamp": datetime.utcnow().isoformat(),"model": agent.model.config["model_id"],"tool_calls": tool_calls,}return InvocationResponse(output=response)@app.get("/ping")async def ping():return {"status": "healthy"}
이 버전은 OpenTelemetry를 설정하여 스팬을 Weave의 OTLP 엔드포인트로 직접 내보냅니다. W&B API 키는 `OTLPSpanExporter`를 위해 Base64로 인코딩되며, 스팬 태깅을 위한 트레이서는 별도로 생성합니다. 각 도구는 `tool_logger`로 래핑되어 이름, 입력, 출력이 리스트에 저장됩니다. 내부에서는 invoke_agent, 스팬은 요청의 입력과 출력을 기록하여 Weave에서 검색할 수 있게 합니다. 로깅은 특정 백엔드에 종속되지 않으면서 AgentCore와의 호환성을 유지할 수 있습니다. 즉, 에이전트는 AgentCore 내부에서 정상적으로 실행되지만, 텔레메트리는 AWS의 CloudWatch에 고정되지 않습니다.
통합이 완료되면 Weave의 UI에서 에이전트를 직접 시각화할 수 있습니다. 각 호출, 도구 호출, 추론 단계가 트리 뷰로 표시되며, 입력, 출력, 지연 시간, 토큰 사용량 같은 세부 정보를 펼쳐서 볼 수 있습니다. 호출 체인을 따라가며 에이전트가 어떻게 결론에 도달했는지 정확히 확인하거나, 과거 실행이나 실시간 흐름에서 예기치 않은 출력을 디버그할 수 있습니다. 이러한 가시성 덕분에 프롬프트를 반복 개선하고, 도구 동작을 튜닝하며, 에이전트 로직 변경의 영향을 이해하기가 훨씬 쉬워집니다.
다음은 Weave 내부의 관측 대시보드 스크린샷 일부입니다.

여기에서는 입력과 출력뿐만 아니라, 에이전트가 답을 생성하기까지 거친 각 중간 단계의 상세한 내역까지 모두 확인할 수 있습니다. 도구 호출, 그 인자, 각 도구가 반환한 정확한 출력이 포함됩니다. 각 호출의 지연 시간 메트릭도 보이므로 느린 구성 요소를 식별하기가 쉬워집니다. 단계별 토큰 사용량도 제공되어 비용 관리와 프롬프트 최적화에 도움이 됩니다. 이러한 세부 정보를 결합하면 에이전트의 실행 흐름을 처음부터 끝까지 파악할 수 있어 정밀한 디버깅과 성능 튜닝이 가능합니다.
결론
Weave를 AgentCore 워크플로에 통합하면 관측성이 에이전트 운영의 자연스러운 일부가 됩니다. 대시보드에 트레이스가 나타나면 에이전트가 요청을 처리하고 도구를 호출하며 결과를 생성하는 과정을 정확히 확인할 수 있습니다. 각 단계는 입력, 출력, 그리고 소요 시간과 함께 기록되므로, 디버깅은 흩어진 로그를 뒤지는 일이 아니라 핵심에 집중한 과정이 됩니다. 이러한 가시성은 각 변경의 영향을 추적하고 실행 결과를 나란히 비교할 수 있게 해 주어, 튜닝 또한 더욱 정밀해집니다.
시간이 지나면서 이러한 트레이스는 에이전트가 동작하는 패턴을 드러냅니다. 도구 실행의 병목, 출력의 불일치, 비용을 높이는 불필요한 호출 등을 발견할 수 있습니다. Weave를 사용하면 문제가 발생한 정확한 순간까지 파고들어 자신 있게 로직을 조정할 수 있습니다. 단독으로 사용하든 AWS의 모니터링과 함께 사용하든, 에이전트의 동작을 명확하고 지속적으로 보여 주어, 기능을 발전시키는 동안 신뢰성을 유지하기가 한층 쉬워집니다.
Add a comment