Blog
ai-agentllmlangchainlanggraphai

AI Agent 완전 가이드 - 개념, 아키텍처, 프레임워크, 실전 구축

AI Agent의 핵심 개념, ReAct/Plan-and-Execute 아키텍처, Tool Use, 메모리 관리, 주요 프레임워크 비교, 구현 실습, 안전성 설계, 엔터프라이즈 활용 사례를 체계적으로 정리합니다.

Data Dynamics2026年4月16日38 min read
This post is not yet translated. The original Korean version is shown below.

AI Agent는 LLM이 스스로 판단하고, 도구를 사용하며, 목표를 달성하기 위해 자율적으로 행동하는 시스템입니다. 이 글에서는 AI Agent의 핵심 개념부터 아키텍처, 프레임워크 비교, 구현 실습, 엔터프라이즈 활용까지 체계적으로 다룹니다.


1. AI Agent란 무엇인가

정의와 개념

AI Agent는 주어진 목표를 달성하기 위해 환경을 인식하고, 추론하며, 자율적으로 행동을 수행하는 AI 시스템입니다. 단순히 질문에 답하는 것을 넘어, 복잡한 태스크를 여러 단계로 분해하고, 필요한 도구를 선택적으로 활용하며, 중간 결과를 바탕으로 다음 행동을 결정합니다.

[기존 LLM 챗봇]
사용자 질문 → LLM → 텍스트 응답

[AI Agent]
사용자 목표 → 계획 수립 → 도구 선택 → 행동 수행 → 결과 관찰 → 다음 행동 결정 → ... → 목표 달성

기존 LLM 챗봇과의 차이점

구분LLM 챗봇AI Agent
상호작용1회 질문-응답다단계 자율 수행
도구 사용없음 (텍스트만 생성)검색, API 호출, 코드 실행 등
계획 수립없음목표 분해 → 단계별 계획
상태 관리대화 기록만 유지작업 상태, 중간 결과 추적
자율성수동적 (질문에만 반응)능동적 (스스로 판단하고 행동)
오류 대응없음실패 감지 → 전략 수정 → 재시도
대표 사례ChatGPT 대화Claude Code, Devin, AutoGPT

Agent의 핵심 구성 요소

AI Agent는 네 가지 핵심 요소로 구성됩니다.

┌─────────────────────────────────────────────┐
│                AI Agent                      │
│                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │  인식     │  │  추론     │  │  행동     │  │
│  │ Perceive │→│ Reason   │→│  Act     │  │
│  │          │  │          │  │          │  │
│  │ - 사용자  │  │ - 계획   │  │ - 도구   │  │
│  │   입력    │  │ - 분석   │  │   호출   │  │
│  │ - 환경   │  │ - 판단   │  │ - API    │  │
│  │   관찰    │  │          │  │ - 코드   │  │
│  └──────────┘  └──────────┘  └──────────┘  │
│        ↑                           │         │
│        │      ┌──────────┐         │         │
│        └──────│  기억     │←────────┘         │
│               │ Memory   │                   │
│               │          │                   │
│               │ - 대화   │                   │
│               │ - 결과   │                   │
│               │ - 지식   │                   │
│               └──────────┘                   │
└─────────────────────────────────────────────┘

1. 인식 (Perception): 사용자 입력, 도구 실행 결과, 환경 상태를 이해

2. 추론 (Reasoning): 현재 상황을 분석하고 다음 행동을 계획

3. 행동 (Action): 도구 호출, API 요청, 코드 실행 등 실제 작업 수행

4. 기억 (Memory): 대화 이력, 중간 결과, 학습된 지식 저장 및 활용


2. AI Agent 아키텍처

ReAct (Reasoning + Acting) 패턴

ReAct는 추론(Reasoning)과 행동(Acting)을 번갈아 수행하는 가장 기본적인 Agent 패턴입니다. 2022년 Yao et al.이 제안했습니다.

[ReAct 루프]

질문: "현재 환율로 100 달러는 몇 원인가?"

Thought 1: 현재 USD/KRW 환율을 확인해야 합니다.
Action 1:  exchange_rate_api(from="USD", to="KRW")
Observation 1: 1 USD = 1,350 KRW

Thought 2: 환율 정보를 얻었으니 계산할 수 있습니다.
Action 2:  calculator(100 * 1350)
Observation 2: 135,000

Thought 3: 계산이 완료되었습니다.
Answer: 현재 환율(1 USD = 1,350 KRW)로 100달러는 135,000원입니다.

ReAct의 장점:

  • 추론 과정이 투명하여 디버깅 용이
  • 도구 사용을 자연스럽게 통합
  • 중간 관찰 결과를 바탕으로 유연하게 대응

ReAct의 한계:

  • 매 단계마다 LLM 호출 필요 (비용/지연 증가)
  • 복잡한 태스크에서 루프가 길어질 수 있음
  • 장기 계획 수립에 약함

Plan-and-Execute 패턴

먼저 전체 계획을 수립한 뒤, 각 단계를 순차적으로 실행하는 패턴입니다. 복잡한 태스크에서 ReAct보다 효율적입니다.

[Plan-and-Execute]

목표: "Q3 매출 보고서를 작성하세요"

=== Planning Phase ===
Plan:
1. 데이터베이스에서 Q3 매출 데이터 조회
2. Q2 데이터와 비교 분석
3. 주요 제품별 매출 추이 분석
4. 차트 및 그래프 생성
5. 보고서 초안 작성

=== Execution Phase ===
Step 1: sql_query("SELECT ... FROM sales WHERE quarter = 'Q3'")
  → 결과: Q3 매출 데이터 (1,000 행)

Step 2: sql_query("SELECT ... FROM sales WHERE quarter = 'Q2'")
  → 결과: Q2 매출 데이터 → 비교 분석 수행

Step 3: analyze_trends(q2_data, q3_data, group_by="product")
  → 결과: 제품별 매출 추이

Step 4: create_chart(trend_data, chart_type="bar")
  → 결과: 차트 이미지 생성

Step 5: generate_report(all_results)
  → 결과: 보고서 초안 완성

=== Replan (필요 시) ===
"차트에 전년 동기 비교도 추가해야 함" → 계획 수정 → 추가 실행

ReAct vs Plan-and-Execute 비교:

구분ReActPlan-and-Execute
계획없음 (매 단계 즉흥)사전 계획 수립
유연성매우 높음중간 (재계획 가능)
효율성낮음 (많은 LLM 호출)높음 (계획 1회 + 실행)
적합 태스크단순, 탐색적복잡, 다단계
오류 복구매 단계 수정 가능재계획 필요

멀티 에이전트 아키텍처

여러 전문화된 Agent가 협업하여 복잡한 태스크를 수행하는 아키텍처입니다.

[멀티 에이전트 패턴]

                    ┌──────────────┐
                    │  Supervisor  │  ← 작업 분배 및 조율
                    │  (관리자)     │
                    └──────┬───────┘
                           │
              ┌────────────┼────────────┐
              ↓            ↓            ↓
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │ Research │ │  Coder   │ │ Reviewer │
        │  Agent   │ │  Agent   │ │  Agent   │
        │ (조사)    │ │ (코딩)    │ │ (검토)    │
        └──────────┘ └──────────┘ └──────────┘
              │            │            │
              ↓            ↓            ↓
          웹 검색      코드 실행      코드 분석
          문서 조회    파일 수정      테스트 실행

주요 멀티 에이전트 패턴:

패턴설명적합 사례
Supervisor관리자가 작업을 분배하고 결과를 취합복잡한 프로젝트 관리
Hierarchical계층 구조로 하위 에이전트 관리대규모 조직 시뮬레이션
Peer-to-Peer에이전트 간 직접 메시지 교환토론, 코드 리뷰
Pipeline순차적으로 결과를 전달데이터 처리 파이프라인
Debate에이전트 간 토론으로 품질 향상의사결정, 검증

Agent Loop 구조

모든 Agent 아키텍처의 기반이 되는 핵심 루프 구조입니다.

# Agent Loop 의사 코드
def agent_loop(goal: str, tools: list, max_steps: int = 10):
    messages = [{"role": "user", "content": goal}]
    
    for step in range(max_steps):
        # 1. LLM에게 다음 행동 결정 요청
        response = llm.generate(messages, tools=tools)
        
        # 2. 응답이 최종 답변이면 종료
        if response.is_final_answer:
            return response.content
        
        # 3. 도구 호출이면 실행
        if response.tool_calls:
            for tool_call in response.tool_calls:
                result = execute_tool(tool_call)
                messages.append({
                    "role": "tool",
                    "content": result,
                    "tool_call_id": tool_call.id
                })
        
        # 4. 결과를 메시지에 추가하고 다음 반복
        messages.append(response)
    
    return "최대 단계 수에 도달했습니다."

3. 도구 사용 (Tool Use / Function Calling)

Tool Use의 개념과 원리

Tool Use(또는 Function Calling)는 LLM이 외부 도구를 호출하여 자신이 직접 수행할 수 없는 작업을 처리하는 메커니즘입니다.

[Tool Use 동작 흐름]

사용자: "서울의 현재 기온은?"

1. LLM이 도구 호출 필요성 판단
2. LLM이 도구 호출 요청 생성:
   → get_weather(city="서울")

3. 시스템이 실제 API 호출 실행
   → Weather API → {"temp": 18, "condition": "맑음"}

4. LLM이 결과를 자연어로 변환
   → "서울의 현재 기온은 18°C이며, 날씨는 맑습니다."

참고: LLM이 직접 도구를 실행하는 것이 아닙니다. LLM은 "어떤 도구를 어떤 인자로 호출해야 하는지"를 결정하고, 실제 실행은 호스트 시스템이 담당합니다.

도구 정의와 스키마 설계

도구는 이름, 설명, 파라미터 스키마로 정의됩니다.

# Anthropic Claude Tool Use 예시
import anthropic
 
client = anthropic.Anthropic()
 
tools = [
    {
        "name": "execute_sql",
        "description": "데이터베이스에 SQL 쿼리를 실행하고 결과를 반환합니다. SELECT 쿼리만 허용됩니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "실행할 SQL SELECT 쿼리"
                },
                "database": {
                    "type": "string",
                    "enum": ["analytics", "production", "staging"],
                    "description": "쿼리를 실행할 데이터베이스"
                }
            },
            "required": ["query", "database"]
        }
    },
    {
        "name": "send_slack_message",
        "description": "Slack 채널에 메시지를 전송합니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "channel": {
                    "type": "string",
                    "description": "Slack 채널명 (예: #engineering)"
                },
                "message": {
                    "type": "string",
                    "description": "전송할 메시지 내용"
                }
            },
            "required": ["channel", "message"]
        }
    }
]
 
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{
        "role": "user",
        "content": "analytics DB에서 이번 달 매출 합계를 조회하고, 결과를 #sales 채널에 공유해줘."
    }]
)

도구 설계 모범 사례:

원칙설명예시
명확한 이름도구의 기능을 정확히 반영execute_sql (O), do_stuff (X)
상세한 설명LLM이 언제 사용할지 판단할 수 있도록"SELECT 쿼리만 허용" 명시
타입 명시파라미터 타입, enum 등 제약 조건 포함"enum": ["analytics", "production"]
최소 권한필요한 권한만 부여읽기 전용 도구와 쓰기 도구 분리
에러 반환실패 시 명확한 에러 메시지{"error": "쿼리 구문 오류"}

주요 도구 유형

도구 유형설명예시
검색 (Retrieval)외부 데이터 소스에서 정보 검색벡터 DB 검색, 웹 검색, 사내 위키 검색
API 호출외부 서비스 API 호출날씨, 환율, Slack, Jira, GitHub API
코드 실행프로그래밍 코드 실행Python 코드 실행, SQL 쿼리, Bash 명령
파일 조작파일 읽기/쓰기/수정문서 생성, CSV 처리, 로그 분석
계산수학적 계산 수행통계, 환율 변환, 데이터 분석
브라우저웹 브라우저 제어웹 페이지 탐색, 폼 작성, 스크린샷

4. 메모리와 상태 관리

단기 기억 (대화 컨텍스트)

현재 대화 세션의 메시지 이력을 유지하는 기본적인 메모리입니다.

# 단기 기억: 대화 메시지 리스트
messages = [
    {"role": "system", "content": "당신은 데이터 엔지니어링 어시스턴트입니다."},
    {"role": "user", "content": "Spark 클러스터 상태를 확인해줘"},
    {"role": "assistant", "content": "...", "tool_calls": [...]},
    {"role": "tool", "content": "클러스터 상태: 정상, 노드 5개 활성"},
    {"role": "assistant", "content": "Spark 클러스터는 정상입니다. 5개 노드가 활성 상태입니다."},
    {"role": "user", "content": "어제 실행한 배치 잡 상태도 확인해줘"},
    # ... 대화가 계속됨
]

단기 기억의 과제:

  • 컨텍스트 윈도우 제한: 대화가 길어지면 초기 메시지가 잘릴 수 있음
  • 관리 전략: 요약(Summarize), 슬라이딩 윈도우, 중요 메시지 유지
# 컨텍스트 윈도우 관리: 요약 방식
def manage_context(messages, max_tokens=4096):
    if count_tokens(messages) > max_tokens:
        # 오래된 메시지를 요약으로 압축
        old_messages = messages[1:-5]  # 시스템 프롬프트, 최근 5개 제외
        summary = llm.summarize(old_messages)
        
        return [
            messages[0],  # 시스템 프롬프트 유지
            {"role": "system", "content": f"이전 대화 요약: {summary}"},
            *messages[-5:]  # 최근 5개 메시지 유지
        ]
    return messages

장기 기억 (벡터 DB, 외부 저장소)

세션을 넘어 지속되는 지식과 경험을 저장하는 메모리입니다.

# 장기 기억: 벡터 DB에 경험 저장 및 검색
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
 
# 장기 기억 저장소
long_term_memory = Chroma(
    collection_name="agent_memory",
    embedding_function=OpenAIEmbeddings(),
    persist_directory="./memory_db"
)
 
# 경험 저장
def save_experience(task: str, result: str, success: bool):
    long_term_memory.add_texts(
        texts=[f"태스크: {task}\n결과: {result}\n성공: {success}"],
        metadatas=[{
            "task_type": classify_task(task),
            "success": success,
            "timestamp": datetime.now().isoformat()
        }]
    )
 
# 과거 경험 검색 (유사한 태스크 수행 시 참고)
def recall_experience(current_task: str, k: int = 3):
    results = long_term_memory.similarity_search(current_task, k=k)
    return results

장기 기억의 활용:

유형저장 내용활용 방법
사용자 선호도선호하는 응답 형식, 도메인 지식 수준응답 스타일 조정
과거 태스크이전에 수행한 작업과 결과유사 태스크 시 참고
학습된 규칙시행착오에서 얻은 교훈같은 실수 반복 방지
도메인 지식사내 기술 스택, 아키텍처 정보맥락에 맞는 응답

작업 기억 (Scratchpad, 중간 결과)

현재 태스크를 수행하는 동안 중간 결과와 상태를 추적하는 메모리입니다.

# 작업 기억: Scratchpad
class AgentScratchpad:
    def __init__(self):
        self.plan = []           # 현재 계획
        self.completed = []      # 완료된 단계
        self.intermediate = {}   # 중간 결과물
        self.observations = []   # 관찰 기록
    
    def update_plan(self, plan: list):
        self.plan = plan
    
    def mark_complete(self, step: int, result: str):
        self.completed.append(step)
        self.intermediate[f"step_{step}"] = result
    
    def get_context(self) -> str:
        """현재 작업 상태를 텍스트로 변환"""
        return f"""
현재 계획: {self.plan}
완료 단계: {self.completed}
중간 결과: {self.intermediate}
남은 단계: {[s for s in self.plan if s not in self.completed]}
"""

5. Agent 프레임워크 비교

LangChain / LangGraph

가장 널리 사용되는 LLM 애플리케이션 프레임워크입니다.

LangChain: 체인(Chain) 기반의 선형적 워크플로 구성

LangGraph: 그래프(Graph) 기반의 복잡한 Agent 워크플로 구성

# LangGraph를 이용한 ReAct Agent
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
 
@tool
def search_database(query: str) -> str:
    """사내 데이터베이스에서 정보를 검색합니다."""
    # 실제 DB 검색 로직
    return f"검색 결과: {query}에 대한 데이터..."
 
@tool
def run_sql(sql: str) -> str:
    """SQL 쿼리를 실행하고 결과를 반환합니다."""
    # 실제 SQL 실행 로직
    return f"쿼리 결과: ..."
 
llm = ChatOpenAI(model="gpt-4o")
tools = [search_database, run_sql]
 
# ReAct Agent 생성
agent = create_react_agent(llm, tools)
 
# 실행
result = agent.invoke({
    "messages": [{"role": "user", "content": "이번 분기 매출을 조회해줘"}]
})
# LangGraph를 이용한 커스텀 Agent 워크플로
from langgraph.graph import StateGraph, MessagesState, START, END
 
def should_continue(state: MessagesState):
    """도구 호출이 있으면 계속, 없으면 종료"""
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        return "tools"
    return END
 
def call_model(state: MessagesState):
    """LLM 호출"""
    response = llm.invoke(state["messages"])
    return {"messages": [response]}
 
def call_tools(state: MessagesState):
    """도구 실행"""
    # 도구 실행 로직
    ...
 
# 그래프 구성
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", call_tools)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent")
 
app = workflow.compile()

CrewAI

역할(Role) 기반 멀티 에이전트 프레임워크입니다. 각 Agent에 역할, 목표, 배경을 부여하고 협업하게 합니다.

from crewai import Agent, Task, Crew
 
# 에이전트 정의
researcher = Agent(
    role="데이터 리서처",
    goal="정확한 시장 데이터와 트렌드를 조사합니다",
    backstory="10년 경력의 시장 분석 전문가입니다.",
    tools=[search_tool, web_scraper],
    llm="gpt-4o"
)
 
writer = Agent(
    role="보고서 작성자",
    goal="조사 결과를 명확한 보고서로 작성합니다",
    backstory="기술 문서 작성 경험이 풍부한 테크니컬 라이터입니다.",
    llm="gpt-4o"
)
 
reviewer = Agent(
    role="품질 검토자",
    goal="보고서의 정확성과 완성도를 검토합니다",
    backstory="데이터 검증과 QA 전문가입니다.",
    llm="gpt-4o"
)
 
# 태스크 정의
research_task = Task(
    description="2025년 AI 시장 동향을 조사하세요.",
    agent=researcher,
    expected_output="시장 규모, 성장률, 주요 트렌드 요약"
)
 
write_task = Task(
    description="조사 결과를 기반으로 보고서를 작성하세요.",
    agent=writer,
    expected_output="구조화된 시장 동향 보고서"
)
 
review_task = Task(
    description="보고서의 데이터 정확성과 논리를 검토하세요.",
    agent=reviewer,
    expected_output="검토 의견 및 수정 사항"
)
 
# Crew 구성 및 실행
crew = Crew(
    agents=[researcher, writer, reviewer],
    tasks=[research_task, write_task, review_task],
    verbose=True
)
 
result = crew.kickoff()

AutoGen (Microsoft)

Microsoft의 대화 기반 멀티 에이전트 프레임워크입니다. Agent 간의 대화를 통해 작업을 수행합니다.

from autogen import AssistantAgent, UserProxyAgent
 
# 어시스턴트 에이전트
assistant = AssistantAgent(
    name="data_engineer",
    system_message="당신은 데이터 엔지니어입니다. Python 코드로 데이터 분석 작업을 수행합니다.",
    llm_config={"model": "gpt-4o"}
)
 
# 사용자 프록시 (코드 실행 담당)
user_proxy = UserProxyAgent(
    name="executor",
    human_input_mode="NEVER",  # 자동 실행
    code_execution_config={"work_dir": "workspace"}
)
 
# 대화 시작
user_proxy.initiate_chat(
    assistant,
    message="sales.csv 파일을 읽어서 월별 매출 추이를 분석하고 차트를 그려주세요."
)

Claude Agent SDK (Anthropic)

Anthropic의 에이전트 구축 SDK로, Claude 모델을 기반으로 안전하고 제어 가능한 Agent를 구축합니다.

import anthropic
from anthropic.types import ToolUseBlock
 
client = anthropic.Anthropic()
 
tools = [
    {
        "name": "read_file",
        "description": "파일의 내용을 읽어 반환합니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "파일 경로"}
            },
            "required": ["path"]
        }
    },
    {
        "name": "write_file",
        "description": "파일에 내용을 작성합니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "파일 경로"},
                "content": {"type": "string", "description": "작성할 내용"}
            },
            "required": ["path", "content"]
        }
    }
]
 
def agent_loop(goal: str):
    messages = [{"role": "user", "content": goal}]
    
    while True:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=4096,
            tools=tools,
            messages=messages
        )
        
        # 도구 호출이 없으면 최종 응답
        if response.stop_reason == "end_turn":
            return extract_text(response)
        
        # 도구 호출 처리
        messages.append({"role": "assistant", "content": response.content})
        
        tool_results = []
        for block in response.content:
            if isinstance(block, ToolUseBlock):
                result = execute_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result
                })
        
        messages.append({"role": "user", "content": tool_results})
 
result = agent_loop("config.yaml 파일을 읽어서 포트 설정을 8080으로 변경해줘")

OpenAI Agents SDK

OpenAI의 Agent 구축 프레임워크로, Responses API를 기반으로 합니다.

from openai import OpenAI
 
client = OpenAI()
 
# Agent를 Responses API로 구현
response = client.responses.create(
    model="gpt-4o",
    tools=[
        {"type": "web_search"},
        {"type": "code_interpreter"},
        {
            "type": "function",
            "function": {
                "name": "query_database",
                "description": "사내 데이터베이스를 조회합니다.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "sql": {"type": "string"}
                    }
                }
            }
        }
    ],
    input="이번 달 신규 가입자 수를 조회하고 추이를 분석해줘"
)

프레임워크 비교 요약

프레임워크개발사패턴강점적합 사례
LangGraphLangChain그래프 기반 워크플로유연한 상태 관리, 커스텀 워크플로복잡한 커스텀 Agent
CrewAICrewAI역할 기반 멀티 에이전트직관적 역할 설계팀 시뮬레이션, 다단계 작업
AutoGenMicrosoft대화 기반 멀티 에이전트코드 실행, 연구용코드 생성, 데이터 분석
Claude Agent SDKAnthropicTool Use + Agent Loop안전성, 긴 컨텍스트엔터프라이즈 Agent
OpenAI Agents SDKOpenAIResponses API 기반통합 도구 (검색, 코드)범용 Agent

6. Agent 구현 실습

단일 Agent 구현 (Tool Use + ReAct)

데이터베이스 조회와 분석을 수행하는 Agent를 구현합니다.

import anthropic
import json
 
client = anthropic.Anthropic()
 
# 도구 정의
tools = [
    {
        "name": "query_sales_db",
        "description": "매출 데이터베이스에서 데이터를 조회합니다. SQL 쿼리를 실행합니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "sql": {"type": "string", "description": "실행할 SQL 쿼리"}
            },
            "required": ["sql"]
        }
    },
    {
        "name": "calculate",
        "description": "수학 계산을 수행합니다. Python 수식을 평가합니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {"type": "string", "description": "Python 수식"}
            },
            "required": ["expression"]
        }
    },
    {
        "name": "create_report",
        "description": "분석 결과를 보고서로 작성합니다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "content": {"type": "string"},
                "format": {"type": "string", "enum": ["markdown", "html", "text"]}
            },
            "required": ["title", "content"]
        }
    }
]
 
# 도구 실행 함수
def execute_tool(name: str, input_data: dict) -> str:
    if name == "query_sales_db":
        # 실제로는 DB 연결하여 실행
        return json.dumps({"rows": [
            {"month": "2025-01", "revenue": 1200000},
            {"month": "2025-02", "revenue": 1350000},
            {"month": "2025-03", "revenue": 1180000}
        ]})
    elif name == "calculate":
        result = eval(input_data["expression"])
        return str(result)
    elif name == "create_report":
        return f"보고서 '{input_data['title']}' 생성 완료"
    return "알 수 없는 도구"
 
# Agent 루프
def run_agent(goal: str):
    messages = [{"role": "user", "content": goal}]
    
    print(f"목표: {goal}\n{'='*50}")
    
    for step in range(10):
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=4096,
            system="당신은 데이터 분석 Agent입니다. 도구를 활용하여 사용자의 요청을 수행하세요.",
            tools=tools,
            messages=messages
        )
        
        # 텍스트 응답 출력
        for block in response.content:
            if hasattr(block, "text"):
                print(f"\n[Agent 응답] {block.text}")
        
        # 최종 응답이면 종료
        if response.stop_reason == "end_turn":
            break
        
        # 도구 호출 처리
        messages.append({"role": "assistant", "content": response.content})
        tool_results = []
        
        for block in response.content:
            if block.type == "tool_use":
                print(f"\n[도구 호출] {block.name}({json.dumps(block.input, ensure_ascii=False)})")
                result = execute_tool(block.name, block.input)
                print(f"[도구 결과] {result[:200]}")
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result
                })
        
        messages.append({"role": "user", "content": tool_results})
    
    print(f"\n{'='*50}\nAgent 실행 완료 (단계: {step + 1})")
 
# 실행
run_agent("Q1 매출 데이터를 조회하고, 월별 증감률을 계산한 뒤 보고서를 작성해주세요.")

멀티 Agent 워크플로 구현

LangGraph를 이용하여 조사-작성-검토 워크플로를 구현합니다.

from langgraph.graph import StateGraph, MessagesState, START, END
from langchain_openai import ChatOpenAI
from typing import Literal
 
llm = ChatOpenAI(model="gpt-4o")
 
# 각 Agent 노드 정의
def researcher(state: MessagesState):
    """조사 Agent: 데이터 수집 및 분석"""
    system = "당신은 데이터 리서처입니다. 주어진 주제에 대해 핵심 데이터를 조사하세요."
    messages = [{"role": "system", "content": system}] + state["messages"]
    response = llm.invoke(messages)
    return {"messages": [response]}
 
def writer(state: MessagesState):
    """작성 Agent: 보고서 작성"""
    system = "당신은 테크니컬 라이터입니다. 조사 결과를 체계적인 보고서로 작성하세요."
    messages = [{"role": "system", "content": system}] + state["messages"]
    response = llm.invoke(messages)
    return {"messages": [response]}
 
def reviewer(state: MessagesState):
    """검토 Agent: 품질 검토"""
    system = """당신은 품질 검토자입니다. 보고서의 정확성과 완성도를 검토하세요.
    검토 결과를 'APPROVED' 또는 'REVISION_NEEDED: [수정사항]'으로 반환하세요."""
    messages = [{"role": "system", "content": system}] + state["messages"]
    response = llm.invoke(messages)
    return {"messages": [response]}
 
def should_revise(state: MessagesState) -> Literal["writer", END]:
    """검토 결과에 따라 수정 또는 완료 결정"""
    last_message = state["messages"][-1].content
    if "REVISION_NEEDED" in last_message:
        return "writer"
    return END
 
# 워크플로 그래프 구성
workflow = StateGraph(MessagesState)
workflow.add_node("researcher", researcher)
workflow.add_node("writer", writer)
workflow.add_node("reviewer", reviewer)
 
workflow.add_edge(START, "researcher")
workflow.add_edge("researcher", "writer")
workflow.add_edge("writer", "reviewer")
workflow.add_conditional_edges("reviewer", should_revise)
 
app = workflow.compile()
 
# 실행
result = app.invoke({
    "messages": [{"role": "user", "content": "2025년 생성형 AI 시장 동향 보고서를 작성해주세요."}]
})

실전 예제: RAG + Agent 결합

RAG 검색을 도구로 활용하는 Agent를 구현합니다.

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
 
# 벡터 DB (사전 구축된 사내 문서)
vectorstore = Chroma(
    persist_directory="./company_docs_db",
    embedding_function=OpenAIEmbeddings()
)
 
@tool
def search_internal_docs(query: str) -> str:
    """사내 기술 문서, 정책, 가이드를 검색합니다.
    Spark, Kafka, NiFi, Kudu 등의 기술 문서와 운영 가이드를 찾을 때 사용합니다."""
    results = vectorstore.similarity_search(query, k=3)
    return "\n\n---\n\n".join([
        f"[출처: {r.metadata.get('source', 'unknown')}]\n{r.page_content}"
        for r in results
    ])
 
@tool
def run_spark_query(sql: str) -> str:
    """Spark SQL 쿼리를 실행하고 결과를 반환합니다.
    데이터 분석, 집계, 조인 등의 쿼리를 수행할 때 사용합니다."""
    # 실제 Spark 연동 로직
    return f"쿼리 실행 결과: ..."
 
@tool
def create_jira_ticket(title: str, description: str, priority: str) -> str:
    """Jira 티켓을 생성합니다.
    버그 리포트, 작업 요청, 개선 사항 등록 시 사용합니다."""
    # 실제 Jira API 호출
    return f"Jira 티켓 생성 완료: PROJ-1234 '{title}'"
 
# RAG + Agent
llm = ChatOpenAI(model="gpt-4o")
tools = [search_internal_docs, run_spark_query, create_jira_ticket]
 
agent = create_react_agent(
    llm, tools,
    prompt="당신은 Data Dynamics의 시니어 데이터 엔지니어입니다. "
           "사내 문서를 검색하고, 데이터를 분석하며, 필요 시 Jira 티켓을 생성합니다."
)
 
# 실행
result = agent.invoke({
    "messages": [{
        "role": "user",
        "content": "Kudu 테이블의 파티셔닝 전략을 사내 가이드에서 찾아보고, "
                   "현재 orders 테이블에 적용할 수 있는 최적화 방안을 분석해주세요. "
                   "개선 사항이 있으면 Jira 티켓도 만들어줘."
    }]
})

7. 안전성과 제어

가드레일 (Guardrails) 설계

Agent가 의도치 않은 행동을 하지 않도록 제약을 설정합니다.

# 가드레일 구현 예시
class AgentGuardrails:
    def __init__(self):
        self.allowed_tools = {"search_docs", "run_sql", "calculate"}
        self.blocked_patterns = [
            r"DROP\s+TABLE",
            r"DELETE\s+FROM",
            r"UPDATE\s+.*SET",
            r"INSERT\s+INTO",
            r"rm\s+-rf",
        ]
        self.max_steps = 15
        self.max_cost_usd = 1.0
    
    def validate_tool_call(self, tool_name: str, tool_input: dict) -> tuple:
        """도구 호출 전 검증"""
        # 1. 허용된 도구인지 확인
        if tool_name not in self.allowed_tools:
            return False, f"허용되지 않은 도구: {tool_name}"
        
        # 2. 위험한 패턴 검사
        input_str = json.dumps(tool_input)
        for pattern in self.blocked_patterns:
            if re.search(pattern, input_str, re.IGNORECASE):
                return False, f"위험한 패턴 감지: {pattern}"
        
        return True, "OK"
    
    def check_budget(self, current_cost: float) -> bool:
        """비용 한도 확인"""
        return current_cost < self.max_cost_usd
 
guardrails = AgentGuardrails()
 
# Agent 루프에서 가드레일 적용
for block in response.content:
    if block.type == "tool_use":
        is_valid, reason = guardrails.validate_tool_call(block.name, block.input)
        if not is_valid:
            # 도구 실행 차단, 에이전트에 피드백
            tool_results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": f"차단됨: {reason}",
                "is_error": True
            })

Human-in-the-Loop

중요한 결정이나 위험한 작업 전에 사람의 승인을 받는 메커니즘입니다.

def human_approval_required(tool_name: str, tool_input: dict) -> bool:
    """사람의 승인이 필요한 작업인지 판단"""
    high_risk_tools = {"send_email", "create_jira_ticket", "deploy", "delete_file"}
    return tool_name in high_risk_tools
 
def request_human_approval(tool_name: str, tool_input: dict) -> bool:
    """사람에게 승인 요청"""
    print(f"\n{'='*50}")
    print(f"[승인 요청] Agent가 다음 작업을 수행하려 합니다:")
    print(f"  도구: {tool_name}")
    print(f"  입력: {json.dumps(tool_input, indent=2, ensure_ascii=False)}")
    print(f"{'='*50}")
    
    approval = input("승인하시겠습니까? (y/n): ")
    return approval.lower() == "y"
 
# Agent 루프에서 적용
for block in response.content:
    if block.type == "tool_use":
        if human_approval_required(block.name, block.input):
            if not request_human_approval(block.name, block.input):
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": "사용자가 작업을 거부했습니다.",
                    "is_error": True
                })
                continue
        
        result = execute_tool(block.name, block.input)

권한 관리와 샌드박싱

제어 수준설명구현 방법
도구 수준사용 가능한 도구 제한화이트리스트 기반 도구 목록
입력 검증도구 입력값 검증정규식, 스키마 검증
실행 격리코드 실행을 샌드박스 환경에서 수행Docker 컨테이너, VM
네트워크 제한접근 가능한 네트워크 범위 제한방화벽, 프록시
시간 제한단일 작업의 최대 실행 시간타임아웃 설정
비용 제한LLM API 호출 비용 상한토큰 카운트, 예산 관리

에러 처리와 폴백 전략

class AgentErrorHandler:
    def __init__(self, max_retries: int = 3):
        self.max_retries = max_retries
        self.error_counts = {}
    
    def handle_tool_error(self, tool_name: str, error: Exception) -> str:
        """도구 실행 에러 처리"""
        self.error_counts[tool_name] = self.error_counts.get(tool_name, 0) + 1
        
        if self.error_counts[tool_name] >= self.max_retries:
            return f"도구 '{tool_name}'이 {self.max_retries}회 연속 실패했습니다. 다른 방법을 시도하세요."
        
        return f"에러 발생: {str(error)}. 재시도 가능합니다. ({self.error_counts[tool_name]}/{self.max_retries})"
    
    def handle_llm_error(self, error: Exception) -> str:
        """LLM 호출 에러 처리"""
        if "rate_limit" in str(error).lower():
            time.sleep(5)  # Rate limit 시 대기
            return "RETRY"
        elif "context_length" in str(error).lower():
            return "TRUNCATE"  # 컨텍스트 축소
        return "ABORT"

8. 엔터프라이즈 AI Agent 활용 사례

코드 생성 에이전트

대표 사례: Claude Code, GitHub Copilot, Cursor

코드 생성 에이전트는 코드를 읽고, 수정하고, 테스트를 실행하며, 버그를 수정하는 종합적인 개발 도우미입니다.

[코드 에이전트 워크플로]

사용자: "로그인 API에 rate limiting을 추가해줘"

Agent 행동:
1. [파일 검색] 로그인 관련 코드 파일 탐색
   → auth/login.py, middleware/rate_limit.py 발견

2. [파일 읽기] 기존 코드 구조 분석
   → Express 미들웨어 패턴 확인

3. [코드 작성] Rate limiting 미들웨어 구현
   → redis 기반 sliding window 알고리즘 적용

4. [테스트 작성] 단위 테스트 추가
   → 정상 케이스, 제한 초과 케이스 커버

5. [테스트 실행] 테스트 실행 및 결과 확인
   → 모든 테스트 통과 확인

6. [결과 보고] 변경 사항 요약 제공

활용 효과:

지표도입 전도입 후변화
코드 작성 속도기준2~3배 향상반복 작업 자동화
코드 리뷰 시간30분/PR10분/PR자동 리뷰 초안 제공
버그 발견률수동 테스트+40% 향상자동 테스트 생성

데이터 분석 에이전트

자연어로 데이터 분석을 요청하면 SQL 작성, 실행, 시각화까지 자동으로 수행합니다.

[데이터 분석 에이전트]

사용자: "지난 분기 대비 이탈률이 높은 고객 세그먼트를 분석해줘"

Agent 행동:
1. [SQL 생성] 고객 이탈 데이터 조회 쿼리 작성
2. [SQL 실행] 쿼리 실행 및 결과 수집
3. [분석] 세그먼트별 이탈률 계산 및 비교
4. [시각화] 차트 생성 (세그먼트별 이탈률 비교)
5. [인사이트] 이탈률 증가 원인 추론
6. [보고서] 분석 결과 보고서 작성

고객 서비스 자동화

고객 문의를 분류하고, 내부 문서를 검색하며, 답변을 생성하고, 필요 시 사람에게 에스컬레이션합니다.

[고객 서비스 Agent 워크플로]

고객: "주문한 상품이 아직 배송되지 않았어요. 주문번호 ORD-12345"

Agent 행동:
1. [의도 분류] 배송 상태 문의로 분류
2. [주문 조회] order_api.get("ORD-12345")
   → 상태: 배송 중, 예상 도착: 내일
3. [물류 조회] logistics_api.track("TRK-67890")
   → 현재 위치: 서울 허브, 배송기사 배정됨
4. [응답 생성] 배송 상태 안내 메시지 작성
5. [만족도 확인] 추가 도움 필요 여부 확인

→ 에스컬레이션 조건: 배송 지연 3일 이상, 파손, 환불 요청

IT 운영 자동화 (AIOps)

시스템 모니터링, 장애 감지, 원인 분석, 자동 복구를 수행하는 Agent입니다.

[AIOps Agent]

알림: "서버 CPU 사용률 95% 초과 (server-prod-03)"

Agent 행동:
1. [모니터링 조회] Prometheus에서 서버 메트릭 수집
   → CPU 95%, Memory 78%, Disk I/O 높음

2. [로그 분석] 최근 로그에서 이상 패턴 검색
   → "OutOfMemoryError" 다수 발견, 특정 프로세스 메모리 누수 의심

3. [원인 분석] 프로세스별 리소스 사용량 확인
   → java_app 프로세스가 메모리 12GB 사용 (정상: 4GB)

4. [조치 결정] 자동 복구 가능 여부 판단
   → 프로세스 재시작으로 해결 가능 (사전 승인된 작업)

5. [자동 복구] 프로세스 재시작 실행
   → systemctl restart java_app

6. [검증] 메트릭 정상화 확인
   → CPU 35%, Memory 45% — 정상 복귀

7. [보고] Slack #ops 채널에 장애 보고서 전송
   → 발생 시각, 원인, 조치 내용, 현재 상태

AIOps Agent 효과:

지표수동 운영AIOps Agent개선
장애 감지 시간 (MTTD)~15분~1분93% 단축
장애 복구 시간 (MTTR)~45분~5분89% 단축
야간 당직 호출월 20회월 3회85% 감소
반복 장애 발생빈번패턴 학습으로 예방60% 감소

References


— Data Dynamics 엔지니어링 팀