Strands TypeScript SDK
Node.js와 브라우저에서 타입 안전한 에이전트
TypeScript SDK
Node.js와 브라우저에서 타입 안전한 에이전트
에이전트를 만드는 가장 빠른 방법
3줄 코드로 시작하는 프로덕션 에이전트 SDK
에이전틱 루프
Reason → Act → Observe · 시퀀스 · 종료 조건
Reason → Act → Observe
목표 달성까지 추론·실행·관찰을 반복하는 핵심 사이클
발표자 노트
루프는 이 세 단계의 반복입니다. 사람이 일하는 방식과 똑같다고 보시면 돼요. 상황을 파악하고, 행동하고, 결과를 확인해서 다음을 정하는 겁니다. 중요한 건 이 사이클을 몇 번 돌지 개발자가 미리 정하지 않는다는 점입니다. 목표가 달성됐다고 모델이 판단할 때까지 돌아갑니다.
루프의 구조 — Agent가 허브
Prompt가 들어와 Result가 나올 때까지 — Agent가 Model·Tools와 양방향으로 교환
모델은 도구를 직접 실행하지 않습니다 — 도구 결정만 반환하고 실행은 Agent 허브의 몫이라, 실행 앞뒤에 검증·로깅(Hooks)이 끼어들 자리가 생깁니다.
발표자 노트
이 그림에서 Agent가 가운데 허브라는 점을 봐주세요. 모델이 도구를 직접 실행하는 게 아닙니다. 모델은 어떤 도구를 쓰겠다는 결정만 반환하고, 실제 실행은 Agent가 맡습니다. 그래서 도구 실행 앞뒤에 검증이나 로깅을 끼워 넣을 수 있는 거고, 잠시 뒤에 볼 Hooks가 걸리는 지점도 바로 이 허브입니다. 점선 박스 안이 agent 호출 한 번 안에서 벌어지는 일 전체입니다.
요청에서 답변까지 — 루프가 도는 모습
한 번의 호출 안에서 루프가 2회전하는 실제 시퀀스 — 사용자 · Agent · Model · Tools
발표자 노트
이번에는 실제 시간 순서로 따라가 보시죠. 첫 회전에서 모델이 tool_use를 반환하면 Agent가 도구를 실행하고, 그 결과를 대화에 추가해서 다시 모델에게 보냅니다. 두 번째 회전에서 모델이 도구 호출 없이 텍스트로만 답하면 그게 곧 종료 신호예요. 별도의 종료 API가 있는 게 아니라, 응답의 형태 자체가 계속과 종료를 구분한다는 게 포인트입니다.
루프 종료 조건
기본 동작은 자연 종료 — 반복 상한은 limits 내장, 그 외 중단은 보조 수단
자연 종료
모델이 도구 호출 없이 최종 응답 — stop_reason: end_turn
- 도구 호출(tool_use)이 유일한 "계속" 신호
- 반복 상한이 필요하면 내장 limits 파라미터 — 가운데 카드
limits — 내장 반복 상한
agent(prompt, limits={"turns": 10, ...}) — turns · output_tokens · total_tokens 예산 지정
- 초과 시 stop_reason: limit_turns · limit_output_tokens · limit_total_tokens
- 각 루프 반복 시작 시 검사 — 대화 상태를 보존한 채 종료
보조 수단 — Hooks · cancel
BeforeToolCallEvent에서 event.cancel_tool = "사유" — 도구 실행 차단
- 취소 사유가 도구 결과로 모델에 전달 → 모델이 마무리 응답
- 외부 중단은 agent.cancel() — stop_reason: cancelled
반복 상한은 limits 파라미터가 내장 — 초과 시 stop_reason: limit_turns 등으로 종료되어 폭주 비용을 방어합니다.
발표자 노트
그럼 모델이 계속 도구만 부르면 어떻게 될까요? 이럴 때를 위해 limits 파라미터가 내장되어 있습니다. 호출할 때 turns, output_tokens, total_tokens 예산을 지정하면 각 루프 반복이 시작될 때 검사해서, 초과하면 limit_turns 같은 stop_reason으로 대화 상태를 보존한 채 종료됩니다. 보조 수단도 두 가지 있는데요, 훅에서 cancel_tool로 도구 실행을 차단하면 그 사유가 모델에 전달되어 모델이 마무리 응답을 하고, 사용자 취소나 타임아웃 같은 외부 신호는 agent.cancel()로 처리합니다. 프로덕션에서는 비용 방어선으로 limits를 기본으로 걸어두시는 걸 권합니다.
도구 생태계
@tool · 빌트인 도구 · MCPClient
도구의 세 가지 공급 경로
직접 제작 · 가져다 쓰기 · 외부 연결
발표자 노트
도구를 확보하는 경로는 세 가지입니다. 사내 API처럼 우리 회사에만 있는 기능은 @tool로 직접 만들고, 파일 읽기나 HTTP 호출처럼 흔한 기능은 카테고리별 빌트인에서 가져다 쓰고, 이미 MCP 서버로 공개된 기능은 MCPClient로 연결합니다. 실무에서는 세 경로를 섞어 쓰게 되는데, 새 도구를 만들기 전에 빌트인과 MCP 생태계부터 검색해 보시는 게 시간을 아끼는 길입니다. 이제 하나씩 코드로 보겠습니다.
@tool 데코레이터
Python 함수에 붙이면 에이전트가 호출 가능한 도구로 변환
# 임포트 — 데코레이터 하나면 충분
from strands import tool
# ① @tool 데코레이터 — 함수를 도구로 변환
@tool
# ② 타입 힌트 — 파라미터 스키마 자동 생성
def get_weather(city: str, unit: str = "celsius") -> dict:
# ③ docstring — 모델에게 전달되는 사용 설명서
"""도시의 현재 날씨를 조회합니다.
사용자가 날씨를 물어볼 때 호출하세요.
city: 도시명 (예: "서울", "Tokyo")
unit: 온도 단위 — "celsius" 또는 "fahrenheit"
반환: {"temp": 22, "condition": "맑음"}
"""
# ④ 반환값 — 모델이 관찰하는 실행 결과
return {"temp": 22, "condition": "맑음"}
- @tool 데코레이터 하나로 Python 함수가 에이전트 도구로 변환
- 타입 힌트 — 파라미터 스키마 자동 생성 (
city: str, 기본값 지원) - docstring — 모델에게 전달되는 사용 설명서, 호출 시점 판단 근거
- 반환값 — 모델이 관찰하는 도구 실행 결과
발표자 노트
아까 잠깐 본 @tool을 이번엔 자세히 뜯어보겠습니다. 타입 힌트에서 파라미터 스키마가 자동 생성되니까, unit처럼 기본값이 있는 선택 파라미터도 그대로 표현됩니다. docstring에는 언제 호출해야 하는지, 파라미터 예시, 반환 형태까지 적어주는 게 좋아요. 모델 입장에서는 이 문서가 도구에 대해 아는 전부거든요. 도구 선택이 자꾸 어긋난다면 코드가 아니라 docstring을 다듬는 게 정답인 경우가 많습니다.
빌트인 도구 카테고리
두 공급원 — SDK 코어 동봉 6종과 strands-agents-tools 카테고리별 확장
핵심 포인트
- 코어 동봉 6종
- bash · file_editor · http_request · notebook · sleep · stop — v1.50 이후 SDK 코어에 동봉, 별도 설치 없이 즉시 사용
- file_read · file_write · editor
- 파일 — 로컬 파일 CRUD, 코드 편집
- http_request · retrieve
- 네트워크 — REST API 호출, RAG 검색
- shell · python_repl
- 시스템 — 명령 실행·코드 평가, 최소 권한으로 등록
- use_aws · calculator · current_time
- AWS·유틸 — AWS CLI 래핑, 계산, 시간
코드
# 임포트 — 필요한 도구만 골라서
from strands import Agent
from strands_tools import file_read, http_request, current_time
# 등록 — 리스트에 담으면 끝
agent = Agent(tools=[file_read, http_request, current_time])
발표자 노트
직접 만들기 전에 여기 있는지 먼저 확인하세요. 빌트인 도구의 공급원은 두 갈래인데, bash나 file_editor, http_request 같은 핵심 6종은 SDK 코어에 동봉되어 있어서 tools 패키지를 설치하지 않아도 바로 쓸 수 있고, 그 밖의 카테고리별 확장 도구는 strands-agents-tools 패키지에서 import 한 줄이면 바로 씁니다. 특히 use_aws는 AWS CLI를 래핑해서 에이전트가 AWS 리소스를 직접 다루게 해주니까 AWS 환경에서는 활용도가 높습니다. 다만 shell이나 python_repl처럼 강력한 도구는 에이전트에게 실행 권한을 주는 셈이라는 점을 기억하세요. 프로덕션에서는 꼭 필요한 도구만 골라 넣는 최소 권한 원칙이 안전합니다.
MCPClient 연결
외부 MCP(Model Context Protocol) 서버의 도구를 동적으로 로드
# 임포트 — Agent와 MCP 클라이언트, stdio 전송
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters
# ① MCP 클라이언트 — transport callable로 stdio 서버 연결
mcp = MCPClient(lambda: stdio_client(StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
)))
# ② 연결 컨텍스트 — 서버 도구를 로드해 에이전트에 등록
with mcp:
agent = Agent(
tools=mcp.list_tools_sync(), # ③ MCP 서버 도구 자동 등록
)
result = agent("/tmp 디렉토리의 파일 목록을 보여줘")
# ④ 선택 — client_name: MCP 초기화 시 서버에 알릴 클라이언트 이름
# MCPClient(transport, client_name="cost-agent")
# ⑤ 보조 — mcpServers JSON 설정(파일/딕셔너리)을 일괄 로드
# agent = Agent(tools=MCPClient.load_servers("mcp.json"))
- MCPClient — transport callable로 stdio MCP 서버 연결
with컨텍스트 — 연결 수명 관리, 블록 안에서만 도구 사용 가능- list_tools_sync() — 서버 도구를 조회해 Agent에 자동 등록
client_name— 서버에 전달되는 클라이언트 식별자 파라미터- load_servers() — 서버당 MCPClient를 만들어 반환하는 보조 API, 주 흐름은 서버별 인스턴스화
발표자 노트
마지막 경로는 외부 연결입니다. MCP는 도구를 표준 프로토콜로 노출하는 방식이라, 이미 공개된 MCP 서버가 있다면 코드 몇 줄로 그 서버의 도구 전체를 가져올 수 있습니다. 예시는 npx로 파일시스템 MCP 서버를 띄워 연결하는 코드인데요, with 블록이 연결 수명을 관리하니까 도구 사용은 반드시 블록 안에서 해야 합니다. list_tools_sync()가 서버의 도구 목록을 받아 Agent에 등록해 주고요. 서버에 우리 클라이언트를 알리고 싶으면 client_name 파라미터를 지정할 수 있고, 연결할 서버가 여럿이면 mcpServers JSON 설정을 통째로 읽어 서버당 MCPClient를 만들어 주는 load_servers()도 보조로 제공됩니다. 사내에 이미 MCP 서버를 운영 중이시라면, Strands 에이전트에 그대로 꽂을 수 있다는 뜻입니다.
모델 프로바이더
공식 15종 프로바이더 · BedrockModel · Structured Output
모델 프로바이더
에이전트의 두뇌를 갈아 끼우는 계층 — Agent 코드는 그대로
모델 프로바이더
Agent 코드는 그대로 두고 모델만 교체합니다 — 프로바이더마다 다른 것은 인증뿐입니다.
- 공식 15종 + 커뮤니티 10종 — Bedrock · Anthropic · OpenAI 등
- 기본: BedrockModel — IAM 인증 · 프로덕션 권장
- 교체 비용: model_id 한 줄
- Structured Output — 프로바이더 공통
개발은 무료 로컬 Ollama, 프로덕션은 Bedrock — 어느 조합이든 tools·system_prompt 코드는 바뀌지 않습니다.
발표자 노트
본격적으로 들어가기 전에 큰 그림을 한 장으로 잡고 시작하겠습니다. 메시지는 하나예요. 모델을 바꿔도 Agent 코드는 그대로라는 겁니다. 공식 15종 프로바이더 중 기본은 BedrockModel이고, 개발 단계에서는 무료인 Ollama로 돌리다가 프로덕션에서 Bedrock으로 넘어가는 조합이 흔합니다. 이제 전체 목록부터 표로 보시죠.
공식 15종 모델 프로바이더
하나의 Agent 코드로 모델만 교체 — 프로바이더별로 다른 것은 인증뿐
| 클래스 | 용도 |
|---|---|
| BedrockModel | AWS 프로덕션 (권장) |
| NovaAPIModel | Amazon Nova — Python 전용 |
| AnthropicModel | Anthropic API 직접 |
| OpenAIModel | GPT 시리즈 |
| OpenAIResponsesModel | Responses API |
| GeminiModel | Google AI |
| OllamaModel | 로컬 추론 (무료) |
| LiteLLMModel | 100+ 모델 통합 프록시 |
| 클래스 | 용도 |
|---|---|
| LlamaCPPModel | 로컬 GGUF |
| LlamaAPIModel | Llama 클라우드 |
| MistralModel | Mistral AI |
| SageMakerModel | 커스텀 엔드포인트 |
| WriterModel | Writer AI |
| VercelModel | Vercel AI SDK — TS 전용 |
| 커뮤니티 10종 | Cohere · vLLM · xAI 등 — 별도 연동 |
발표자 노트
표가 길지만 메시지는 하나입니다. Agent 코드는 그대로 두고 모델만 갈아 끼운다는 거죠. 개발 단계에서는 무료인 Ollama 로컬 모델로 돌리고 프로덕션은 Bedrock으로 가는 조합이 흔한 패턴입니다. 표에서 두 가지만 주의하시면 되는데, 최근 추가된 Amazon Nova는 Python 전용이고, 반대로 Vercel은 TypeScript SDK 전용입니다. 공식 15종 밖에도 Cohere나 vLLM 같은 커뮤니티 연동이 10종 있고, 표에 없는 모델이라도 LiteLLM 프록시를 거치면 100개 이상 연결됩니다.
BedrockModel 설정
기본 프로바이더 — model_id 한 줄로 모델 교체
설정 코드
from strands.models import BedrockModel
# 기본 — model_id만 지정 (us. 접두어 = Cross-Region 추론 프로파일)
model = BedrockModel(model_id="us.anthropic.claude-sonnet-4-6")
# 상세 설정 — 리전·생성 파라미터 제어
model = BedrockModel(
model_id="us.anthropic.claude-sonnet-4-6",
region_name="us-east-1",
temperature=0.1,
max_tokens=4096,
)
# 조립 — 어떤 설정이든 Agent에는 model 하나로 전달
agent = Agent(model=model, tools=[...])
핵심 파라미터
- model_id
- 추론 프로파일 ID — 파운데이션 모델 ARN이 아님
- region_name
- 모델 가용 리전 (us-east-1 · us-west-2 등)
- temperature
- 응답 무작위성 — 낮을수록 결정적
- max_tokens
- 응답 길이 상한
발표자 노트
기본 프로바이더인 BedrockModel 설정을 보겠습니다. 가장 흔한 실수가 model_id인데, 파운데이션 모델 ARN이 아니라 추론 프로파일 ID를 넣어야 합니다. us. 접두어가 붙으면 크로스 리전 추론 프로파일이라 트래픽이 여러 리전으로 분산되고요. temperature는 에이전트 용도라면 낮게 잡아서 도구 선택이 결정적으로 나오게 하는 편이 안정적입니다. 어떤 설정을 하든 Agent에는 model 인자 하나로 들어간다는 구조는 동일합니다.
Structured Output
에이전트의 답을 코드가 받을 때 — 자유 텍스트 대신 스키마로, 프로바이더 공통
사용 코드
from pydantic import BaseModel
from strands import Agent
# 출력 스키마 정의 — Pydantic 모델이 곧 JSON Schema
class WeatherReport(BaseModel):
city: str
temp_celsius: float
condition: str
humidity: int
# 스키마 지정 호출 — structured_output_model로 스키마 전달
agent = Agent(model="us.anthropic.claude-sonnet-4-6")
result = agent("서울 날씨를 알려줘", structured_output_model=WeatherReport)
# 결과 접근 — 파싱 실패 없이 Pydantic 인스턴스로 수신
report = result.structured_output
# report.city == "서울", report.temp_celsius == 22.5
Pydantic 스키마란
파이썬 표준 데이터 검증 라이브러리입니다.
- 클래스 필드 선언이 곧 JSON Schema로 변환되어 모델에 전달
- 응답이 스키마와 다르면 검증 단계에서 걸러짐
- 결과는 타입 있는 Python 객체 — 문자열 파싱 코드가 사라짐
발표자 노트
에이전트의 답을 사람이 아니라 코드가 받아야 할 때가 있죠. 자유 텍스트를 정규식으로 파싱하는 대신, Pydantic 클래스로 스키마를 선언하고 structured_output_model로 넘기면 됩니다. 클래스 필드 선언이 그대로 JSON Schema로 변환되어 모델에 전달되고, 결과는 타입 있는 Python 객체로 돌아옵니다. 에이전트를 파이프라인의 한 단계로 쓰거나 다른 시스템과 연동할 때 사실상 필수 기능이고요. 특정 모델 전용이 아니라 프로바이더 공통으로 동작한다는 점도 기억해 주세요.
프레임워크 포지셔닝
Strands vs LangGraph vs CrewAI vs Bedrock Agents
프레임워크 포지셔닝
설계 철학이 다른 네 가지 선택지 — Strands가 서 있는 자리
네 가지 선택지
어느 하나가 정답이 아니라 설계 철학이 다릅니다 — 첫 분기는 코드 레벨 제어의 필요 여부입니다.
- Strands — 모델 중심 · 프로덕션 직접 빌드
- LangGraph — 그래프 워크플로우
- CrewAI — 역할 기반 프로토타입
- Bedrock Agents — 완전 관리형
Strands의 자리 — 코드 레벨 제어 + 최소 추상화 + AWS 배포 경로 내장, 프로덕션 에이전트를 직접 빌드할 때의 선택지입니다.
발표자 노트
마지막 파트입니다. 네 프레임워크는 우열이 아니라 설계 철학의 차이로 갈립니다. 코드 레벨 제어가 필요 없다면 완전 관리형인 Bedrock Agents, 그래프 설계가 핵심이면 LangGraph, 역할 기반의 빠른 프로토타입이면 CrewAI, 프로덕션 에이전트를 직접 빌드한다면 Strands입니다. 이제 여섯 개 축으로 나란히 놓고 비교해 보겠습니다.
에이전트 프레임워크 비교
설계 철학·추상화 수준·배포 경로가 다른 4가지 선택지
| 축 | Strands | LangGraph | CrewAI | Bedrock Agents |
|---|---|---|---|---|
| 설계 철학 | 모델 중심 · 최소 추상화 | 그래프 워크플로우 | 역할 기반 멀티에이전트 | 완전 관리형 |
| 코드 레벨 | Python/TS 직접 제어 | StateGraph 노드 정의 | YAML + Python | API/콘솔만 |
| 멀티에이전트 | Agents-as-Tools · Swarm · Graph | 노드 간 메시지 | Crew + Task | 내장 협업 |
| 배포 | Lambda · Fargate · AgentCore | 자체 관리 | 자체 관리 | AWS 관리형 |
| 커스터마이징 | 무제한 (코드 레벨) | 그래프 내 제한 | 역할 템플릿 | 제한적 |
| 적합 시나리오 | 프로덕션 에이전트 직접 빌드 | 복잡한 조건부 분기 | 빠른 프로토타입 | 인프라 관리 최소화 |
갈림길은 배포 행 — LangGraph·CrewAI는 자체 관리, Strands는 Lambda · Fargate · AgentCore 경로가 열려 있고, Bedrock Agents는 AWS 관리형입니다.
발표자 노트
어느 하나가 정답이라기보다, 설계 철학이 다른 네 가지 선택지입니다. 표에서 배포 행을 봐주세요. LangGraph와 CrewAI는 배포 인프라를 직접 관리해야 하는 반면, Strands는 Lambda, Fargate, AgentCore까지 AWS 배포 경로가 열려 있습니다. 반대편 극단인 Bedrock Agents는 완전 관리형이라 편하지만 커스터마이징이 제한되고요. 여러분 팀이라면 이 여섯 개 축 중에 어느 것을 가장 중요하게 보시겠어요? 그 답에 따라 선택이 달라집니다.
선택 의사결정 트리
예/아니오로 내려가는 팀 맞춤 프레임워크 선택 가이드
발표자 노트
방금 그 질문을 순서도로 만들면 이렇게 됩니다. 첫 분기가 가장 중요한데, 코드 레벨 제어가 필요 없다면 고민할 것 없이 Bedrock Agents로 운영 부담을 없애는 게 낫습니다. 제어가 필요하다면 그다음은 워크플로우의 성격이에요. 복잡한 조건부 분기가 핵심이면 LangGraph, 역할 기반의 빠른 프로토타입이면 CrewAI, 그리고 프로덕션 에이전트를 직접 빌드하는 일반적인 경우가 Strands입니다. 지금 진행 중인 프로젝트를 이 트리에 한번 대입해 보시죠.
Strands의 차별점
다른 프레임워크와 구분되는 핵심 설계 결정
최소 추상화
마법 없는 코드 — Agent, Tool, Model 3개 클래스만으로 시작
프로바이더 독립
공식 15종(+커뮤니티 10종) 모델 프로바이더 — 코드 변경 없이 model_id만 교체
배포 내장
Lambda · Fargate · AgentCore Runtime — SDK에서 프로덕션까지
세 결정이 가리키는 방향은 하나 — Agent · Tool · Model 세 가지로 시작해 프로덕션까지 가는 SDK입니다.
발표자 노트
마무리하면서 Strands의 설계 결정 세 가지를 정리하겠습니다. 첫째, 추상화를 최소로 유지해서 디버깅할 때 프레임워크 내부를 파고들 일이 적습니다. 둘째, 프로바이더 독립이라 모델 선택이 벤더 계약이 아니라 코드 한 줄의 문제가 됩니다. 셋째, agentcore CLI 두 명령으로 관리형 런타임까지 가는 배포 경로가 SDK에 내장되어 있습니다. 오늘 내용을 한 문장으로 줄이면, Agent, Tool, Model 세 가지로 시작해서 프로덕션까지 가는 SDK라고 기억해 주시면 됩니다.
Agent · Tool · Model — 세 가지로 시작하는 프로덕션 에이전트