Strands Agents란?
AWS가 공개한 오픈소스 에이전트 SDK. python과 typescript를 지원합니다
Langgraph vs Strands Agents
비교에 들어가기 전에 짚을 게 있습니다. 한쪽만 되고 다른 쪽은 안 되는 식의 기능 비교를 하려 하면, 대부분 성립하지 않습니다.
지금은 두 프레임워크 모두 model-driven loop, graph, supervisor/swarm 같은 multi-agent, human-in-the-loop와 같은 것들을 다 지원합니다. LangGraph도 prebuilt agent(create_agent)로 model-driven loop를 그대로 주고, Strands에도 실행 순서를 구조로 정의하는 GraphBuilder가 있습니다
그러니 진짜 비교 축은 "무엇을 할 수 있느냐"가 아니라 "무엇을 지향하느냐"입니다. 두 프로젝트는 GitHub 소개 문구에서 어떤 것을 지향하는 지 엿볼수있습니다.


- Strands: "model-driven", 그리고 "Build AI agents in just a few lines of code."
- LangGraph: "Low-level orchestration framework for building stateful agents."
Strands가 지향하는 것: 모델이 주도한다 (model-driven)
과거에는 LLM이 자연어를 이해하기 위해서만 학습되어 "agent"로서 기능하기 어려웠습니다. 최근에는 점점 에이전트와 같은 형태를 띄게 되었습니다.
예시로, OpenAI는 tools(functions), structured output, Responses API등을 출시하면서 점점 에이전트스럽게 발전해갔습니다.
최근 모델은 어떤 tool을 쓸지 판단하고, parameter를 만들고, 결과를 보고 다음 행동을 정하고, 필요하면 tool을 더 부르고, 최종 답을 냅니다. Strands는 이러한 변화에 맞춘 SDK입니다.
여기서 model-driven은 "모델을 호출한다"는 뜻은 아니고 agent의 다음 행동을 모델이 정한다는 뜻입니다. 개발자는 Model, Tools, System Prompt를 주고, 모든 흐름은 모델이 agent loop 안에서 정합니다. 오케스트레이션이 명시적 graph가 아니라 모델 중심 loop 안에 들어갑니다.
LangGraph가 지향하는 것: 코드가 주도한다 (low-level orchestration)
LangGraph는 정반대 지점을 기본값으로 둡니다. 이름 그대로 low-level orchestration framework입니다. State를 정의하고, node를 만들고, edge를 연결하고, router node가 다음 node를 고릅니다. 오케스트레이션을 모델의 판단이 아니라 개발자가 명시합니다.
그 대신 통제하기가 쉽습니다. 라우팅 기준이 코드에 드러나고, 각 단계의 책임이 분리되고, 상태가 명시적이라 승인, 재시도, 감사 로그를 끼워 넣기 쉽습니다.
Strands Agents Basic
설치
pip install strands-agents strands-agents-tools
에이전트 호출, 기본 모델 provider는 Amazon Bedrock이고 아무설정없이 쓰려면 aws 자격증명이 필요합니다
from strands import Agent
agent = Agent(system_prompt="너는 친절한 한국어 도우미야. 답변은 항상 한국어로 해.")
response = agent("주말에 뭐 하면 좋을지 추천해줘")
print(response)
tool 사용
from strands import Agent, tool
@tool
def count_letter(word: str, letter: str) -> int:
"""단어에 특정 글자가 몇 번 나오는지 센다.
이 docstring과 타입 힌트가 모델에게 도구 설명으로 전달된다."""
return word.lower().count(letter.lower())
agent = Agent(tools=[count_letter])
response = agent("strawberry에 r이 몇 개 있어?")
print(response)
Agents as Tools : 서브 에이전트를 상위에이전트의 도구로 등록해서 사우이에이전트의 모델이 판단하게 하는 패턴입니다.
from strands import Agent
research_agent = Agent(system_prompt="너는 리서치 전문가야. 사실 위주로 간결하게 정리해.")
writing_agent = Agent(system_prompt="너는 카피라이터야. 주어진 내용을 매력적인 한 문단으로 다듬어.")
orchestrator = Agent(
system_prompt="요청에 맞는 전문가를 호출해라.",
tools=[research_agent, writing_agent],
)
예시
은행 챗봇을 단순화해서 세 업무만 처리한다고 해봅시다
1. 계좌/예금
2. 대출
3. 전자금융
Strands의 기본 경로는 sub_agent를 tool처럼 등록하고, 상위 agent(모델)가 알아서 고르게 하는 것입니다.
from strands import Agent, tool
... # tool 정의
deposit_agent = Agent(
system_prompt="너는 계좌·예금·적금 상담원이다. 필요하면 도구를 사용해 답해라.",
tools=[get_account_status, search_savings_products],
)
loan_agent = Agent(
system_prompt="너는 대출 상담원이다. 필요하면 도구를 사용해 답해라.",
tools=[check_loan_policy],
)
ebanking_agent = Agent(
system_prompt="너는 전자금융 상담원이다. 필요하면 도구를 사용해 답해라.",
tools=[search_ebanking_guide],
)
banking_agent = Agent(
system_prompt="사용자 질문을 보고 deposit, loan, ebanking 중 가장 적절한 전문가를 고른다.",
tools=[deposit_agent, loan_agent, ebanking_agent],
)
result = banking_agent("정기예금 금리 알려줘")
라우팅도, 각 sub_agent가 어떤 tool을 쓸지도 모델이 정합니다. 코드가 짧고 PoC가 빠릅니다.
LangGraph의 기본 경로는 router와 conditional edge로 흐름을 명시하는 것입니다.
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langchain_core.tools import tool
... # tool 정의
deposit_agent = create_agent(model=model, system_prompt="너는 계좌·예금·적금 상담원이다. 필요하면 도구를 사용해 답해라.",
tools=[get_account_status, search_savings_products])
loan_agent = create_agent(model=model, system_prompt="너는 대출 상담원이다. 필요하면 도구를 사용해 답해라.",
tools=[check_loan_policy])
ebanking_agent = create_agent(model=model, system_prompt="너는 전자금융 상담원이다. 필요하면 도구를 사용해 답해라.",
tools=[search_ebanking_guide])
# 상태
class BankingState(TypedDict):
user_message: str
route: str
answer: str
# 라우터: 모델로 분류해 라벨만 반환 (라우팅은 그래프가 담당)
Route = Literal["deposit", "loan", "ebanking"]
def classify_intent(message: str) -> Route:
label = model.invoke(
f"사용자 질문을 보고 deposit, loan, ebanking 중 가장 적절한 전문가를 고른다.\ndeposit, loan, ebanking 중 하나로만 답해라.\n메시지: {message}"
).content.strip()
return label if label in ("deposit", "loan", "ebanking") else "deposit"
def router_node(state: BankingState):
return {"route": classify_intent(state["user_message"])}
# 각 노드는 해당 전문가 agent를 실행
def run(agent, message: str) -> str:
result = agent.invoke({"messages": [{"role": "user", "content": message}]})
return result["messages"][-1].content
def deposit_node(state: BankingState):
return {"answer": run(deposit_agent, state["user_message"])}
def loan_node(state: BankingState):
return {"answer": run(loan_agent, state["user_message"])}
def ebanking_node(state: BankingState):
return {"answer": run(ebanking_agent, state["user_message"])}
builder = StateGraph(BankingState)
builder.add_node("router", router_node)
builder.add_node("deposit", deposit_node)
builder.add_node("loan", loan_node)
builder.add_node("ebanking", ebanking_node)
builder.add_edge(START, "router")
builder.add_conditional_edges(
"router",
lambda s: s["route"],
{"deposit": "deposit", "loan": "loan", "ebanking": "ebanking"},
)
for node in ("deposit", "loan", "ebanking"):
builder.add_edge(node, END)
graph = builder.compile()
result = graph.invoke({"user_message": "정기예금 금리 알려줘"})
print(result["answer"])
여기선 라우팅 기준이 코드에 명시되어있고 상태 또한 명시적입니다. 코드가 더 길지만, 어디서 무엇이 일어나는지가 눈에 보입니다.
다시 강조하면, 둘 다 상대의 스타일도 표현할 수 있습니다. LangGraph도 create_agent로 위 Strands만큼 짧게 model-driven 버전을 만들 수 있고, Strands도 GraphBuilder로 위 LangGraph처럼 명시적으로 짤 수 있습니다. 위 두 코드는 능력의 한계가 아니라 각자 추구하는 기본 경로일 뿐입니다.
언제 무엇을, 지향에 맞춰 고르기
선택 기준은 "되냐 안 되냐"가 아니라 "내 문제에 어느 지향이 맞느냐"입니다.
Strands의 지향이 맞는 경우:
- 빠른 agent PoC (몇 줄로 시작 가능)
- AWS 환경 (Bedrock, AgentCore, MCP 사용이 매끄러움)
LangGraph의 지향이 맞는 경우:
- 업무 절차가 명확할 때
- 상태 관리가 중요할 때
- 실패 처리, 재시도가 중요할 때
- 금융처럼 auditability가 중요할 때
https://aws.amazon.com/ko/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/
'개발업무 > 개발' 카테고리의 다른 글
| 카카오톡 챗봇 ChatGPT 연동하기 (0) | 2026.05.19 |
|---|---|
| OpenAI LLM API: Responses, Chat Completions, Batch (0) | 2026.04.25 |
| OAuth란 (0) | 2026.03.21 |
| Spring Boot: WebClient vs RestTemplate (0) | 2025.09.30 |
| LoadRunner로 WebSocket(STOMP) 부하 테스트하기 (1) | 2025.08.29 |