AI Agent 완전 가이드 - 개념, 아키텍처, 프레임워크, 실전 구축
AI Agent의 핵심 개념, ReAct/Plan-and-Execute 아키텍처, Tool Use, 메모리 관리, 주요 프레임워크 비교, 구현 실습, 안전성 설계, 엔터프라이즈 활용 사례를 체계적으로 정리합니다.
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 비교:
| 구분 | ReAct | Plan-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="이번 달 신규 가입자 수를 조회하고 추이를 분석해줘"
)프레임워크 비교 요약
| 프레임워크 | 개발사 | 패턴 | 강점 | 적합 사례 |
|---|---|---|---|---|
| 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. 안전성과 제어
가드레일 (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분/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% 감소 |
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 엔지니어링 팀