AI Agent 완전 가이드 - 개념, 아키텍처, 프레임워크, 실전 구축
AI Agent의 핵심 개념, ReAct/Plan-and-Execute 아키텍처, Tool Use, 메모리 관리, 주요 프레임워크 비교, 구현 실습, 안전성 설계, 엔터프라이즈 활용 사례를 체계적으로 정리합니다.
"검색해줘" 라고 하면 검색하고, "계산해줘" 라고 하면 계산하고, "이 결과를 Slack 에 보내줘" 라고 하면 보내는 시스템 — 이것이 AI Agent 입니다. 단순히 텍스트를 생성하는 것을 넘어, 목표를 향해 스스로 계획을 세우고, 도구를 선택하고, 행동하며, 결과를 확인합니다.
이 글에서 배우는 것
- AI Agent 가 LLM 챗봇과 어떻게 다른지 (단발 응답 vs 다단계 자율 수행)
- ReAct, Plan-and-Execute, 멀티 에이전트 아키텍처의 특징과 차이
- Tool Use(Function Calling)의 원리와 도구 설계 모범 사례
- 단기·장기·작업 기억의 관리 방법
- LangGraph, CrewAI, AutoGen, Claude Agent SDK 프레임워크 비교
이 글에서는 AI Agent의 핵심 개념부터 아키텍처, 프레임워크 비교, 구현 실습, 엔터프라이즈 활용까지 체계적으로 다룹니다.
1. AI Agent란 무엇인가
정의와 개념
AI Agent는 주어진 목표를 달성하기 위해 환경을 인식하고, 추론하며, 자율적으로 행동을 수행하는 AI 시스템입니다. 단순히 질문에 답하는 것을 넘어, 복잡한 태스크를 여러 단계로 분해하고, 필요한 도구를 선택적으로 활용하며, 중간 결과를 바탕으로 다음 행동을 결정합니다.
한 문장으로: AI Agent = LLM + 도구 + 반복 루프 — 목표를 이룰 때까지 스스로 생각하고 행동합니다.
기존 LLM 챗봇과의 차이점
| 구분 | LLM 챗봇 | AI Agent |
|---|---|---|
| 상호작용 | 1회 질문-응답 | 다단계 자율 수행 |
| 도구 사용 | 없음 (텍스트만 생성) | 검색, API 호출, 코드 실행 등 |
| 계획 수립 | 없음 | 목표 분해 → 단계별 계획 |
| 상태 관리 | 대화 기록만 유지 | 작업 상태, 중간 결과 추적 |
| 자율성 | 수동적 (질문에만 반응) | 능동적 (스스로 판단하고 행동) |
| 오류 대응 | 없음 | 실패 감지 → 전략 수정 → 재시도 |
| 대표 사례 | ChatGPT 대화 | Claude Code, Devin, AutoGPT |
Agent의 핵심 구성 요소
사람이 일을 처리하는 방식을 생각해 보세요 — 상황을 파악하고(인식), 어떻게 할지 생각하고(추론), 실제로 행동하고(행동), 기억해 두는(기억) 네 단계입니다. AI Agent 도 이 네 가지 요소로 구성됩니다.
1. 인식 (Perception): 사용자 입력, 도구 실행 결과, 환경 상태를 이해
2. 추론 (Reasoning): 현재 상황을 분석하고 다음 행동을 계획
3. 행동 (Action): 도구 호출, API 요청, 코드 실행 등 실제 작업 수행
4. 기억 (Memory): 대화 이력, 중간 결과, 학습된 지식 저장 및 활용
2. AI Agent 아키텍처
ReAct (Reasoning + Acting) 패턴
"생각하고 → 행동하고 → 결과를 보고 → 다시 생각하고" 를 반복하는 패턴입니다. 탐정이 단서를 보며 추리하고, 확인하고, 다시 추리하는 방식과 비슷합니다. 2022년 Yao et al. 이 제안한 가장 기본적인 Agent 패턴입니다.
[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 패턴
건축 공사처럼 먼저 설계도(계획)를 그리고, 순서대로 시공(실행)하는 패턴입니다. 복잡한 태스크에서 매 단계마다 LLM 을 호출하는 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 비교:
| 구분 | ReAct | Plan-and-Execute |
|---|---|---|
| 계획 | 없음 (매 단계 즉흥) | 사전 계획 수립 |
| 유연성 | 매우 높음 | 중간 (재계획 가능) |
| 효율성 | 낮음 (많은 LLM 호출) | 높음 (계획 1회 + 실행) |
| 적합 태스크 | 단순, 탐색적 | 복잡, 다단계 |
| 오류 복구 | 매 단계 수정 가능 | 재계획 필요 |
멀티 에이전트 아키텍처
혼자 모든 걸 잘하는 사람보다 전문가 팀이 협업하는 쪽이 더 좋은 결과를 내는 경우가 있습니다. 여러 전문화된 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의 개념과 원리
LLM 은 혼자 있으면 텍스트만 생성할 수 있습니다. Tool Use 는 LLM 에게 "전화기", "계산기", "데이터베이스" 같은 도구를 쥐어주는 메커니즘입니다. LLM 이 직접 실행하는 것이 아니라 "어떤 도구를 어떻게 써야 하는지" 를 결정하고, 실제 실행은 호스트 시스템이 담당합니다.
[Tool Use 동작 흐름]
사용자: "서울의 현재 기온은?"
1. LLM이 도구 호출 필요성 판단
2. LLM이 도구 호출 요청 생성:
→ get_weather(city="서울")
3. 시스템이 실제 API 호출 실행
→ Weather API → {"temp": 18, "condition": "맑음"}
4. LLM이 결과를 자연어로 변환
→ "서울의 현재 기온은 18°C이며, 날씨는 맑습니다."
참고: LLM이 직접 도구를 실행하는 것이 아닙니다. LLM은 "어떤 도구를 어떤 인자로 호출해야 하는지"를 결정하고, 실제 실행은 호스트 시스템이 담당합니다.
도구 정의와 스키마 설계
도구를 잘 설계하는 것이 Agent 품질의 절반입니다. 이름과 설명이 명확해야 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. 메모리와 상태 관리
사람도 단기 기억(지금 대화 중인 내용), 장기 기억(예전에 배운 지식), 작업 메모(현재 하고 있는 일의 중간 결과) 를 사용합니다. AI Agent 도 마찬가지입니다.
단기 기억 (대화 컨텍스트)
현재 대화 세션의 메시지 이력을 유지하는 가장 기본적인 메모리입니다. 컨텍스트 윈도우가 가득 차면 앞쪽 대화가 잘릴 수 있어 관리 전략이 필요합니다.
# 단기 기억: 대화 메시지 리스트
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 에 경험을 저장하고, 유사한 상황에서 꺼내 참고합니다.
# 장기 기억: 벡터 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 프레임워크 비교
Agent 를 처음부터 만드는 것은 복잡합니다. 프레임워크를 활용하면 검증된 패턴을 재사용해 빠르게 구축할 수 있습니다.
LangChain / LangGraph
가장 널리 사용되는 LLM 애플리케이션 프레임워크입니다. LangChain 은 선형적인 체인 구성에, LangGraph 는 복잡한 그래프 기반 워크플로에 강점이 있습니다.
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="이번 달 신규 가입자 수를 조회하고 추이를 분석해줘"
)프레임워크 비교 요약
| 프레임워크 | 개발사 | 패턴 | 강점 | 적합 사례 |
|---|---|---|---|---|
| LangGraph | LangChain | 그래프 기반 워크플로 | 유연한 상태 관리, 커스텀 워크플로 | 복잡한 커스텀 Agent |
| CrewAI | CrewAI | 역할 기반 멀티 에이전트 | 직관적 역할 설계 | 팀 시뮬레이션, 다단계 작업 |
| AutoGen | Microsoft | 대화 기반 멀티 에이전트 | 코드 실행, 연구용 | 코드 생성, 데이터 분석 |
| Claude Agent SDK | Anthropic | Tool Use + Agent Loop | 안전성, 긴 컨텍스트 | 엔터프라이즈 Agent |
| OpenAI Agents SDK | OpenAI | Responses 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. 안전성과 제어
자율적으로 행동하는 Agent 는 강력하지만, 그만큼 잘못될 가능성도 있습니다. 운영 환경에서는 반드시 안전 장치를 갖춰야 합니다.
가드레일 (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
Agent 가 아무리 똑똑해도 중요한 결정은 사람이 승인해야 합니다. 이메일 발송, Jira 티켓 생성, 배포 같은 돌이키기 어려운 작업은 반드시 사람의 확인을 거치도록 설계하세요.
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 활용 사례
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분/PR | 10분/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% 감소 |
마치며 — 핵심 요약
- AI Agent = LLM + 도구 + 반복 루프입니다. 목표가 달성될 때까지 스스로 생각하고, 행동하고, 관찰합니다.
- ReAct 는 탐색적 태스크에, Plan-and-Execute 는 복잡한 다단계 태스크에 적합합니다.
- Tool Use 는 Agent 의 팔다리입니다. 도구 이름과 설명을 명확하게 작성해야 LLM 이 올바르게 선택합니다.
- 안전성 설계는 선택이 아닙니다. 가드레일, Human-in-the-Loop, 최소 권한 원칙을 항상 적용하세요.
- 멀티 에이전트는 강력하지만 복잡도도 함께 올라갑니다. 먼저 단일 Agent 로 시작하고 필요할 때 확장하세요.
- AIOps, 코드 에이전트, 고객 서비스 자동화 등 엔터프라이즈 현장에서 이미 실질적인 성과를 내고 있습니다.
지금 바로 시작하려면 Anthropic Claude Tool Use 또는 LangGraph 의 간단한 예제로 첫 Agent 를 만들어 보세요. 직접 만들어봐야 개념이 손에 잡힙니다.
References
- Yao, S. et al. (2023). "ReAct: Synergizing Reasoning and Acting in Language Models." ICLR
- Wang, L. et al. (2024). "A Survey on Large Language Model based Autonomous Agents." arXiv
- Xi, Z. et al. (2023). "The Rise and Potential of Large Language Model Based Agents: A Survey." arXiv
- Shinn, N. et al. (2023). "Reflexion: Language Agents with Verbal Reinforcement Learning." NeurIPS
- Anthropic. "Tool Use (Function Calling)" — https://docs.anthropic.com/en/docs/build-with-claude/tool-use
- LangGraph Documentation — https://langchain-ai.github.io/langgraph/
- CrewAI Documentation — https://docs.crewai.com/
- AutoGen Documentation — https://microsoft.github.io/autogen/
— Data Dynamics 엔지니어링 팀