반응형

 

AI Agent 시대다.

AI Agent를 쉽게 만들 수 있는 Framework 가 생겨났다.

AI Agent 엔지니어라는 직업이 나올 수도 있다.

많이 사용하는 AI Agent Framework 대표적인 것 4개를 선정하고, QuickStart 로 AI Agent 를 만들어본다.

그리고 지극히 주관적인 나의 느낌을 담아 장/단점을 정리하겠다.

후보1) Langchain - 너무 유명해서

후보2) AWS Strands SDK - 내가 AWS 클라우드 환경에 익숙해서

 

후보3) CrewAI - 비기너에게 적합하다고 해서  

후보4) Autogen - 멀티에이전트 구현에 집중됨.

후보5) LangGraph - 그래프 기반으로 stateful, multi-agent 시스템에 접근하는 확장성있는 Framework

 

 

Framework 를 고르는데 중요한 요소 (Key Factor)

  • 복잡한 워크플로우 까지 구현이 가능한가. - 복잡한 워크플로우도 구현 가능해?
  • 데이터 통합 - 다양한 데이터 소스로 부터 데이터를 연동하고 반환이 가능해?
  • 멀티에이전트 콜라보 - 여러 에이전트와의 협업 성능이 좋은가?
  • 노 코드 VS 코드 친화적이냐 - 노코드로 에이전트를 만들수 있는가? 반면에 개발자들에게 조금 더 친숙한 개발 환경을 제공하는가

 

LangChain - 생태계 광범위, 큰 커뮤니티
- 다양한 통합 도구 지원
- 성숙하고 안정적인 프레임워크
- LangSmith 제공
- Chain/Agent/RAG 등 유연한 패턴
- 과도한 추상화로 학습 곡선 높음
- 다중 레이어로 성능 저하
- 메모리 모듈 불안정
- 잦은 API 변경으로 호환성 문제
- 모듈식 아키텍처
- RAG/문서 처리 강점
- 엔터프라이즈급 기능 지원
- 복잡한 RAG 시스템
- 외부 서비스 통합 필요
- 엔터프라이즈 환경
AWS Strands SDK - 모델 중심 접근
- 간단한 코드로 에이전트 구축
- AWS 네이티브 통합
- 프로덕션 검증 (Amazon Q 등)
- A2A 멀티 에이전트 지원
- MCP 네이티브 지원
- 2024년 출시, 커뮤니티 작음
- AWS 환경 의존
- 문서 및 자료 부족
- Model-driven 아키텍처
- 세션 관리/상태 지속성
- 비동기 및 스트리밍 지원
- 에이전트 간 도구 공유
- AWS 클라우드 개발
- 빠른 프로토타이핑/배포
- 최신 LLM 활용
- 엔터프라이즈급 확장
CrewAI - 쉬운 학습 곡선
- 직관적 역할 기반 팀워크
- 빠른 멀티에이전트 개발
- 우수한 문서와 예제
- 자연스러운 협업
- 높은 추상화로 세부 제어 어려움
- 로깅/디버깅 제한적
- 맞춤화 한계
- LangChain 의존성
- 스트리밍 미지원
- Agent, Crew, Task 구조
- 순차/병렬 처리 지원
- 역할 기반 협업
- 메모리 관리 내장
- 입문자용 멀티에이전트
- 빠른 프로토타입
- 단순한 팀 기반 워크플로우
- 내부 자동화
Autogen - 자연스러운 대화 중심
- 유연한 에이전트 토폴로지
- Docker 내 안전한 코드 실행
- AutoGen Studio 제공
- Microsoft 지원, 확장성 높음
- 수동 설정 복잡
- 대화 오케스트레이션 학습 필요
- 문서 불일치
- 고성능 LLM 필요
- 디버깅 난이도 높음
- 비동기 이벤트 루프 구조
- 다양한 에이전트 역할
- 대화 기반 작업 조정
- RPC 기반 분산 처리
- 동적 에이전트 대화
- 개발/코딩 어시스턴트
- R&D 환경
- 복잡한 멀티에이전트 실험
LangGraph - 그래프 기반 정밀 제어
- 뛰어난 상태 지속성 및 복구
- DAG 기반 시각화
- LangChain 통합
- 조건부 분기/오류 처리 강력
- 그래프/상태 관리 학습 곡선 높음
- 초기 구조 정의 필요
- 단순 작업에 과도
- LangChain 이슈 상속
- 문서 업데이트 지연
- DAG 아키텍처
- 조건부 엣지/분기 로직
- 상태 기반 워크플로우
- LangSmith 모니터링
- 복잡한 다단계 워크플로우
- 상태 관리 필수 시스템
- 결정적 프로세스
- 금융/헬스케어 등 규제 환경

 

 

Framework 별 간단한 Agent 구현

1) Langchain

"""
LangChain 커스텀 툴 - 에러 수정 버전

설치:
pip install langchain langchain-ollama
"""

from langchain.tools import tool
from langchain_ollama import OllamaLLM  # 새로운 import
from langchain.agents import AgentExecutor, create_react_agent
from langchain.prompts import PromptTemplate
from langchain import hub


# 간단한 덧셈 툴
@tool
def add_numbers(a: int, b: int) -> int:
    """두 숫자를 더합니다. a와 b는 정수여야 합니다."""
    result = a + b
    print(f"[툴 실행] {a} + {b} = {result}")
    return result


@tool
def multiply_numbers(a: int, b: int) -> int:
    """두 숫자를 곱합니다. a와 b는 정수여야 합니다."""
    result = a * b
    print(f"[툴 실행] {a} × {b} = {result}")
    return result


def main():
    print("=" * 70)
    print("LangChain 툴 연동 예제 (수정 버전)")
    print("=" * 70)
    
    # 툴 목록
    my_tools = [add_numbers, multiply_numbers]
    
    print("\n등록된 툴:")
    for tool in my_tools:
        print(f"  • {tool.name}: {tool.description}")
    
    # 1. 새로운 Ollama 클래스 사용
    llm = OllamaLLM(
        model="llama3.1:8b",
        temperature=0
    )
    
    # 2. PromptTemplate을 올바르게 생성
    # 방법 A: 직접 PromptTemplate 생성
    prompt = PromptTemplate.from_template("""
Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action in JSON format, e.g. {{"arg1": value1, "arg2": value2}}
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

IMPORTANT: Action Input must be valid JSON format with double quotes.
Example: {{"a": 5, "b": 3}}

Begin!

Question: {input}
Thought: {agent_scratchpad}
""")
    
    # 방법 B: LangChain Hub에서 기본 ReAct 프롬프트 가져오기 (권장)
    # prompt = hub.pull("hwchase17/react")
    
    # 3. Agent 생성
    agent = create_react_agent(
        llm=llm,
        tools=my_tools,
        prompt=prompt  # PromptTemplate 객체를 전달
    )
    
    # 4. AgentExecutor 생성
    agent_executor = AgentExecutor(
        agent=agent,
        tools=my_tools,
        verbose=True,
        handle_parsing_errors=True,
        max_iterations=5
    )
    
    # 5. 테스트 질문들
    questions = [
        "5와 3을 더하면?",
        "10과 7을 곱하면?",
        "12 더하기 8은 얼마인가요?",
    ]
    
    print("\n" + "=" * 70)
    print("Agent 실행 테스트")
    print("=" * 70)
    
    for i, question in enumerate(questions, 1):
        print(f"\n[질문 {i}] {question}")
        print("-" * 70)
        try:
            result = agent_executor.invoke({"input": question})
            print(f"\n✅ 최종 답변: {result['output']}")
        except Exception as e:
            print(f"❌ 오류: {e}")
        print("=" * 70)


# 간단한 툴 직접 테스트 (Agent 없이)
def simple_test():
    print("\n" + "=" * 70)
    print("툴 직접 테스트 (Agent 없이)")
    print("=" * 70)
    
    # 직접 호출
    result1 = add_numbers.invoke({"a": 10, "b": 20})
    print(f"\n결과 1: {result1}")
    
    result2 = multiply_numbers.invoke({"a": 5, "b": 6})
    print(f"결과 2: {result2}")
    
    print("\n" + "=" * 70)


if __name__ == "__main__":
    # 1. 먼저 툴만 간단히 테스트
    simple_test()
    
    print("\n\n")
    
    # 2. Agent와 함께 사용
    try:
        main()
    except Exception as e:
        print(f"\n❌ Agent 실행 오류: {e}")
        print("\n💡 Ollama가 실행 중인지 확인하세요: ollama serve")
        print("💡 모델이 설치되어 있는지 확인하세요: ollama pull llama3.1:8b")

2) Strands

from strands import Agent
from strands.models.ollama import OllamaModel
from strands import tool


@tool
def add_numbers(a:int, b:int) -> int:
    """
    add two numbers together
    Args:
        a: first number
        b: second number

    Returns:
        sum of a and b
    """
    return a+b

model = OllamaModel(
    model_id="llama3.1:8b",
    host="http://localhost:11434",
    temperature=0.0)

agent = Agent(model=model, tools=[add_numbers])

response = agent("1+2")
print(response)

 

3) crawAI

"""
CrewAI + Ollama를 사용한 커스텀 툴 예제

설치:
pip install crewai crewai-tools langchain-ollama
"""

from crewai import Agent, Task, Crew, Process
from crewai.tools import BaseTool  
from crewai import LLM
from typing import Type
from pydantic import BaseModel, Field


# 1. 덧셈 툴 생성
class AddNumbersInput(BaseModel):
    """덧셈 툴의 입력 스키마"""
    a: int = Field(..., description="첫 번째 숫자")
    b: int = Field(..., description="두 번째 숫자")


class AddNumbersTool(BaseTool):
    name: str = "add_numbers"
    description: str = "두 개의 숫자를 더합니다. 덧셈 계산이 필요할 때 사용하세요."
    args_schema: Type[BaseModel] = AddNumbersInput
    
    def _run(self, a: int, b: int) -> str:
        """덧셈 실행"""
        result = a + b
        return f"{a} + {b} = {result}"


def main():
    print("=" * 70)
    print("CrewAI + Ollama 3.1:8b + 커스텀 툴 예제")
    print("=" * 70)
    
    # Ollama 3.1:8b 모델 설정
    llm = LLM(
        model="ollama/llama3.1:8b",  # ⚠️ "ollama/" prefix 필수!
        base_url="http://localhost:11434"
    )
    # 툴 인스턴스 생성
    add_tool = AddNumbersTool()
    
    print("\n✅ 등록된 툴:")
    print(f"  • {add_tool.name}: {add_tool.description}")
    
    # Agent 생성 - 수학 전문가
    math_expert = Agent(
        role="수학 계산 전문가",
        goal="사용자의 계산 요청을 정확하게 처리합니다",
        backstory="""당신은 숫자 계산에 능숙한 수학 전문가입니다. 
        주어진 툴을 사용하여 정확한 계산 결과를 제공합니다.""",
        tools=[add_tool],
        llm=llm,
        verbose=True,
        allow_delegation=False
    )
    
    # Task 1: 덧셈 작업
    task1 = Task(
        description="15와 27을 더한 결과를 계산하세요.",
        expected_output="두 숫자의 덧셈 결과를 명확하게 제시",
        agent=math_expert
    )

    
    # Crew 생성 및 실행
        agents=[math_expert],
        tasks=[task1],
        process=Process.sequential,  # 순차적 실행
        verbose=True
    )
    
    print("\n" + "=" * 70)
    print("🚀 Crew 실행 시작")
    print("=" * 70)
    
    try:
        result = crew.kickoff()
        
        print("\n" + "=" * 70)
        print("✅ 최종 결과")
        print("=" * 70)
        print(result)
        
    except Exception as e:
        print(f"\n❌ 오류 발생: {e}")
        import traceback
        traceback.print_exc()


# 간단한 툴 테스트
def simple_tool_test():
    print("\n" + "=" * 70)
    print("툴 직접 테스트")
    print("=" * 70)
    
    add_tool = AddNumbersTool()
    
    # 덧셈 테스트
    result1 = add_tool._run(a=10, b=20)
    print(f"\n덧셈 결과: {result1}")

 

 

결론

  • 학습 난이도는 strands 가 가장 쉬웠고, 이해해야 할 요소가 적어서 빠르게 테스트 코드를 만들 수 있었지만 레퍼런스가 부족하고, 공식 도큐먼트의 양과 질이 아쉽다.
  • LangCahin 이 테스트 코드를 실행하는데 가장 많은 시간이 소요됐고, Agent 의 프롬프트를 하나 실행하는데도 더 많은 객체가 필요했다. (prompt 를 만들때도 PromptTemplate 객체를 만들어야 하는 점.)
  • crewAI 도 비교적 학습이 간단했지만, Task들을 객체로 만들어 요소에 넣어야 하는 특징이 있었다. Tasks 를 모아서 순차적으로 실행할 수 있고, 병렬로 실행할 수도 있으며, Crew 라는 객체 안에서 Multi Agent 의 다양한 패턴을 정의할 수 있는것이 특징이다.
  • 생산성 면에서는 Strands 가 가장 좋을 것 같다는 결론이다.
반응형
반응형

 

 

오늘은 Strands SDK 로 개발하는 Agent 의 도구에 MCP tool 을 붙여보겠다.

 

일단 MCP 도 Agent 의 하나의 tool 에 불과하다는 개념을 다시 기억하자.

 

MCP 구성은 Server 와 Client 두 가지 구현이 필요하다.

 

MCP Server 는 필요에 따라 직접 구현보다는 IDE 에서 시켜서 구현할 수 있고 ( 필자의 경우 Cursor 를 이용해서 쉽게 mcp 서버를 구현할 수 있었다.) 외부에 공개된 MCP 서버를 직접 사용할 수 있다. 다만 remote 서버의 경우 datasource 에 대한 정보들이 네트워크 트래픽을 타고 외부에 넘어갈 수 있으니 보안에 유의하자.

 

일단 아래 aws 에서도 aws 서비스에 access 할 수 있는 도구들을 mcp 서버로 제공하고 있다. 

- https://awslabs.github.io/mcp/

 

Welcome to AWS MCP Servers | AWS MCP Servers

Get started with AWS MCP Servers and learn core features.

awslabs.github.io

 

그리고 내가 일반적으로 Smithery 에서도 설치가 가능하지만, 설치가 잘 안될 때는 아래 cursor 의 mcp 페이지에서도 설치해보면 된다.

https://docs.cursor.com/ko/tools/mcp

 

Cursor: The best way to code with AI

Built to make you extraordinarily productive, Cursor is the best way to code with AI.

cursor.com

 

Strands SDK 에서의 MCP Client 요약

  • Strands SDK 에서 쉽게 MCP Client 를 구현해줌
  • MCP 클라이언트를 생성할 때 어떤 방식으로 연결할 것인지 선택해야 함
    • Standard I/O (stdio) : 로컬 프로세스를 구현한다면 적합
    • Streamable HTTP : HTTP 이벤트를 호출해서 스트림 방식으로 구현할 때
    • Server-Sent Events (SSE) : HTTP 기반의 서버를 직접 구축했을 때
    • Custom Transport with MCP Client - 그외 직접 프로토콜을 구현할 때

 

많이 활용하는 stdio , streamableHTTP 방식의 예제 코드를 아래에 첨부한다.

 

예제 코드 내용

  • Agent 모델을 만듦.
  • sequential-thingking  mcp 도구 등록.
  • strands tools 에서 기본으로 제공하는 http_request 를 이용해 openweather api 의 도큐먼트 페이지를 분석시킴.

 

from mcp import stdio_client, StdioServerParameters
from mcp.client.streamable_http import streamablehttp_client
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from strands_tools import http_request, retrieve

base_model = BedrockModel(
    model_id="apac.anthropic.claude-3-7-sonnet-20250219-v1:0",
    region_name="ap-northeast-2",
    temperature=0.3,
)

stdio_mcp_client = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(
            command="uvx",
            args=["awslabs.aws-documentation-mcp-server@latest"]
        )
    )
)

# HTTPStreamable 방식으로 붙일 경우 
# sequential_thinking = MCPClient(
#     lambda: streamablehttp_client("https://smithery.ai/server/@smithery-ai/server-sequential-thinking")
# )


# StdIO 방식으로 붙일 경우
sequential_thinking = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(
            command="npx",
            args=[
                "-y",
                "@smithery/cli@latest",
                "run",
                "@smithery-ai/server-sequential-thinking",
                "--key",
                "생성된 사용자 키",
                "--profile",
                "생성된 사용자 프로필"
            ]
        )
    )
)
# 예제 테스트 코드 
with sequential_thinking:
    tools = sequential_thinking.list_tools_sync()
    tools += [http_request, retrieve]

    agent = Agent(tools=tools, model=base_model)
    result = agent("https://openweathermap.org/forecast5 에서 어떤 날씨 데이터 테이블을 수집할 수 있는지 분석해줘")
    print(result)

# with stdio_mcp_client:
#     tools = stdio_mcp_client.list_tools_sync()
#     agent = Agent(tools = tools, model = base_model)
#     result = agent("What is AWS Lambda?")
반응형
반응형

 

 


AI Agent 가 최근 개발 업계에서 화두인것 같다.
Agent 를 쉽게 개발하기 위한 도구들이 많이 생겨 나오고 있고, 우연히 웨비나를 통해 알게 된 Strands 에 대해서 사용법을 포함해 간단히 소개하고자 한다.

Strands 는 AWS 에서 개발한 AI Agent 를 개발하기 위한 오픈 SDK 다.



strand 는 가닥이라는 의미인데, Agent 와 tool 두 가닥으로 쉽게 Agent 를 구현할 수 있다는 의미.. 일 거다.  

아무튼 예제가 너무 쉽게 눈에 들어와서, 최근 AI Agent 과제를 진행하는데 이 SDK 를 선택하기로 했다. 잘 알려진  Langchain 은 너무 거대해서 공부할게 많아 보인다.

준비는 python 개발 환경과 LLM 을 사용할 수 있는 준비가 되겠다.


Strands package 설치는 아래 내용을 진행한다. (가상환경 셋팅 포함)

 

- 준비사항
- python 3.10 이상 설치
- 가상 환경 설치
- strands-agents SDK 패키지 설치

 

$ sudo yum install python3.11 -y
$ python3.11 -m venv .venv

$ pip install strands-agents strands-agents-tools

 

 


LLM 을 사용할 수 있는 준비가 되어야 하는데, 2가지를 소개하겠다.


1) 오픈 소스 Ollama 활용
- Ollama 설치 와 LLM 모델 다운로드 및 실행 (https://ollama.com/)

 

2) AWS 관리형 서비스 활용
- AWS Bedrock 의 LLM 모델활성화

  • AWS Bedrock 토큰 설정
    • 프로그램을 실행할 때 API 를 사용할 수 있는 토큰이 코드 또는 환경변수에  등록되어있어야 한다
    • Discover -> API Keys 진입 > API 키 생성
  • 트러블 슈팅
    • 모델 아이디를 조회하는 방법은 Discover -> Model catalog -> LLM 모델 선택
    • 모델 아이디앞에 지역 코드를 추가 [1]

예제 코드

from strands import Agent, tool
from strands.models import BedrockModel
from strands_tools import calculator
from strands.models.ollama import OllamaModel

  
  

base_model = BedrockModel(
model_id="apac.anthropic.claude-3-7-sonnet-20250219-v1:0",
region_name="ap-northeast-2",
)

base_model = OllamaModel(
host="http://localhost:11434",
model_id = "gpt-oss:20b",,
temperature=0.7
)

@tool
def weather():
""" Get weather """

return "흐림"

  
  
  

agent = Agent(model=base_model,
tools = [weather, calculator],
system_prompt="너는 간단한 수학계산과 날씨를 알려줄수 있는 어이스턴트야.")

response = agent("오늘 날씨는 어때?")

print(response)

 

위 코드 몇줄로 AI Agent 를 만들었다.


Tool 은 AI Agent 가 외부 세계와 상호작용하고 작업을 수행할 수 있게 해주는 기능. 또는 인터페이스를 의미한다.

기본적으로 strands 에서 제공하는 tool 들이 있고 직접 구현할 수 도 있다.
예를 들어 위 예제 코드에서 나는 weather() 라는 툴에 "흐림"을 하드코딩해서 return 했다. AI Agent 는 날씨를 매번 흐림으로 답변 할 것이다.

요즘 많이들 구현하는 MCP 도 Tool의 종류다.


오늘은 여기까지.

 

 

참고

[1] https://repost.aws/questions/QUEU82wbYVQk2oU4eNwyiong/bedrock-api-invocation-error-on-demand-throughput-isn-s-supported
[2] https://github.com/strands-agents/sdk-python

반응형

+ Recent posts