본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2026. 4. 9. · 0 Views
에이전트 클래식 패러다임 구현 완벽 가이드
ReAct, Plan-and-Solve, Reflection 등 AI 에이전트의 핵심 클래식 패러다임을 원리부터 실전 구현까지 체계적으로 학습합니다. 초보 개발자도 이해할 수 있도록 비유와 스토리로 풀어냅니다.
목차
- ReAct_패러다임의_원리와_구현
- Thought_Action_Observation_사이클_이해
- Plan_and_Solve_패러다임_구현
- Reflection_패턴과_자기_평가
- 패러다임_비교와_선택_가이드
- 실전_예제_각_패러다임_적용해보기
1. ReAct 패러다임의 원리와 구현
앞서 "AI 에이전트 완전 정복 - Hello Agents" 코스에서 LLM의 기초를 학습했습니다. 이제 그 LLM을 엔진으로 삼아 에이전트가 실제로 '생각하고 행동하는' 방식을 배울 차례입니다.
김개발 씨는 팀 미팅에서 "에이전트가 어떻게 스스로 문제를 해결하나요?"라는 질문을 던졌고, 박시니어 씨는 천천히 화이트보드 앞에 섰습니다.
ReAct(Reasoning + Acting) 패러다임은 에이전트가 추론과 행동을 번갈아 수행하며 문제를 해결하는 방식입니다. 마치 경험 많은 선생님이 문제를 풀 때 속으로 생각하고, 손으로 풀고, 결과를 확인하는 과정과 같습니다.
이 패턴은 에이전트가 단순한 질의응답을 넘어 복잡한 다단계 작업을 수행할 수 있게 해줍니다.
다음 코드를 살펴봅시다.
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, Tool
# 도구 정의: 에이전트가 사용할 수 있는 기능
tools = [
Tool(name="Search", func=lambda q: f"검색 결과: {q}",
description="질문에 대해 검색합니다"),
Tool(name="Calculator", func=lambda e: str(eval(e)),
description="수학 계산을 수행합니다"),
]
# ReAct 에이전트 생성
llm = ChatOpenAI(model="gpt-4", temperature=0)
agent = create_react_agent(llm, tools,
prompt="당신은 ReAct 패러다임을 따르는 에이전트입니다."
" Thought, Action, Observation 순서로 생각하세요.")
result = agent.invoke({"input": "서울의 인구와 부산의 인구를 합산해주세요"})
print(result)
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 이전 카드뉴스에서 LLM의 기초를 배우긴 했지만, 막상 "에이전트가 어떻게 스스로 문제를 푸는 거지?"라는 근본적인 의문이 풀리지 않았습니다.
미팅에서 용기를 내어 질문하자, 박시니어 씨가 화이트보드에 "ReAct"라고 크게 적었습니다. "에이전트가 똑똑하게 행동하는 비결이 바로 여기 있어요." ReAct는 **Reasoning(추론)**과 **Acting(행동)**을 합친 말입니다.
쉽게 비유하자면, 요리사가 레시피를 보며 요리하는 과정과 같습니다. 먼저 "다음에 무엇을 해야 하지?"라고 생각하고(추론), 실제로 재료를 자르거나 불을 켭니다(행동).
그리고 결과를 확인한 뒤 다시 생각합니다. 이 사이클이 문제가 해결될 때까지 반복되는 것이죠.
이 패턴이 없던 시절의 에이전트는 어떻게 동작했을까요? 단순히 사용자의 질문에 대해 한 번 답변하고 끝나는 방식이었습니다.
"서울 인구와 부산 인구를 더해줘"라는 요청에 대해, 이전 방식으로는 한 번에 답하려다 틀리기 쉬웠습니다. 인구 수를 모르면 추측해서 대답할 수밖에 없었죠.
ReAct는 이 문제를 elegantly 해결합니다. 에이전트가 스스로 "서울 인구를 먼저 검색해야겠다"라고 생각하고 검색 도구를 호출합니다.
그 결과를 확인한 뒤 "이번엔 부산 인구를 검색하자"라고 다시 생각합니다. 마지막으로 두 값을 더하는 계산 도구를 사용합니다.
코드를 살펴보겠습니다. 먼저 Tool 객체로 에이전트가 사용할 수 있는 도구들을 정의합니다.
검색 기능과 계산 기능이 여기에 해당합니다. 그다음 create_react_agent 함수로 LLM과 도구들을 결합하여 ReAct 에이전트를 생성합니다.
이 에이전트는 사용자의 입력을 받으면 Thought-Action-Observation 사이클을 자동으로 실행합니다. 실무에서는 어떻게 활용될까요?
고객센터 챗봇을 예로 들어보겠습니다. 고객이 "배송 상태를 확인하고, 반품이 가능한지 알려줘"라고 물으면, ReAct 에이전트는 자연스럽게 두 단계로 나누어 처리합니다.
먼저 배송 조회 API를 호출하고, 그 결과를 바탕으로 반품 정책을 확인한 뒤 종합적인 답변을 제공합니다. 하지만 주의할 점도 있습니다.
도구를 너무 많이 등록하면 에이전트가 오히려 혼란스러워집니다. "어떤 도구를 써야 할지 모르겠어!"라는 상황이 벌어질 수 있죠.
따라서 필요한 도구만 최소한으로 등록하는 것이 좋습니다. 또한 각 도구의 description을 명확하게 작성해야 에이전트가 적절한 도구를 선택할 수 있습니다.
김개발 씨는 화이트보드의 다이어그램을 보며 고개를 끄덕였습니다. "그러니까 에이전트가 혼자서 생각하고, 행동하고, 확인하는 거군요!" 박시니어 씨가 미소를 지었습니다.
"정확해요. 이게 ReAct의 핵심이에요."
실전 팁
💡 - ReAct 에이전트를 구현할 때는 도구의 description을 명확하게 작성하세요. 에이전트가 올바른 도구를 선택하는 데 직접적인 영향을 미칩니다.
- 복잡한 작업일수록 에이전트가 Thought 단계에서 충분히 생각할 수 있도록 temperature를 낮게(0~0.3) 설정하세요.
2. Thought Action Observation 사이클 이해
ReAct 패러다임의 핵심 메커니즘인 Thought-Action-Observation 사이클을 깊이 파헤쳐봅시다. 김개발 씨는 박시니어 씨의 설명을 듣고, "그 사이클이 정확히 어떻게 돌아가는 건지 코드로 보고 싶어요"라고 부탁했습니다.
박시니어 씨는 노트북을 열고 직접 사이클을 구현해 보이기 시작했습니다.
Thought-Action-Observation 사이클은 ReAct 에이전트가 한 번의 질문에 대해 생각, 행동, 관찰의 세 단계를 반복하며 정답에 도달하는 루프입니다. 마치 탐정이 사건을 해결할 때 단서를 수집하고, 가설을 세우고, 증거를 확인하는 과정과 같습니다.
이 사이클을 이해하면 에이전트의 내부 동작 원리를 완벽하게 파악할 수 있습니다.
다음 코드를 살펴봅시다.
import re
def react_loop(question, max_iterations=5):
"""ReAct 사이클의 핵심 루프 구현"""
context = f"질문: {question}\n"
for i in range(max_iterations):
# Thought: 에이전트가 현재 상황을 분석
thought = llm.invoke(f"{context}\n생각(Thought):")
# Action: 필요한 도구를 선택하여 실행
action_match = re.search(r'Action:\s*(\w+)', thought)
if not action_match:
return thought # 최종 답변 도달
tool_name = action_match.group(1)
observation = execute_tool(tool_name, thought)
# Observation을 컨텍스트에 추가하고 다시 사이클
context += f"Thought: {thought}\nAction: {tool_name}\n"
context += f"Observation: {observation}\n"
return context
김개발 씨는 ReAct의 개념은 이해했지만, 정확히 코드 안에서 이 사이클이 어떻게 돌아가는지 궁금했습니다. 박시니어 씨는 "직접 구현해보면 이해가 빨라요"라며 노트북 화면에 코드를 하나씩 적어나갔습니다.
"먼저 사이클의 세 가지 단계를 정확히 이해해볼까요?" Thought(생각) 단계에서 에이전트는 현재 상황을 분석합니다. "사용자가 무엇을 원하지?", "지금까지 알아낸 정보는 무엇이지?", "다음에 무엇을 해야 할까?"와 같은 질문을 스스로에게 던집니다.
마치 체스 선수가 "다음 수를 뭘 둘까?"라고 생각하는 것과 비슷합니다. Action(행동) 단계에서는 Thought의 결론에 따라 실제로 도구를 호출합니다.
검색 API를 요청하거나, 계산기를 실행하거나, 데이터베이스를 조회하는 등 구체적인 동작이 일어납니다. 이 단계가 없으면 에이전트는 생각만 하고 아무것도 하지 못하는 몽상가가 됩니다.
Observation(관찰) 단계에서는 Action의 결과를 받아 처리합니다. 검색 결과가 돌아왔을 때, 그 정보를 해석하고 다음 Thought에 활용할 수 있도록 정리합니다.
마치 실험 결과를 노트에 기록하는 과정과 같습니다. 코드를 한 줄씩 살펴보겠습니다.
react_loop 함수는 최대 5번까지 사이클을 반복합니다. 무한 루프에 빠지는 것을 방지하기 위한 안전장치죠.
각 반복에서 먼저 LLM에게 현재 컨텍스트를 주어 Thought를 생성합니다. 그다음 정규표현식으로 Action을 추출하고, 해당 도구를 실행하여 Observation을 얻습니다.
이 Observation을 다시 컨텍스트에 추가하여 다음 사이클로 넘어갑니다. 이 사이클이 없던 시절에는 어떻게 했을까요?
개발자가 에이전트의 모든 가능한 경로를 미리 하드코딩해야 했습니다. "사용자가 A를 물어보면 B API를 호출하고, C를 물어보면 D API를 호출해"라는 식으로요.
새로운 질문 유형이 생길 때마다 코드를 수정해야 했죠. 지금 생각하면 끔찍합니다.
ReAct 사이클을 사용하면 에이전트가 스스로 적절한 단계를 계획하고 실행합니다. 개발자는 도구만 제공하면 됩니다.
"이 도구들이 있어, 알아서 잘 써봐"라고 말하는 것만으로도 에이전트는 상황에 맞는 행동을 선택합니다. 실무에서 이 사이클이 특히 빛을 발하는 곳은 데이터 분석 파이프라인입니다.
"이번 달 매출 데이터를 분석하고, 전년 동기 대비 성장률을 계산한 뒤, 인사이트를 정리해줘"와 같은 복잡한 요청도 사이클을 통해 자연스럽게 처리됩니다. 하지만 주의할 점이 있습니다.
사이클이 너무 많이 반복되면 비용과 시간이 기하급수적으로 증가합니다. 따라서 max_iterations 같은 제한을 반드시 설정해야 합니다.
또한 Observation의 결과가 너무 길면 컨텍스트가 넘쳐날 수 있으니, 결과를 적절히 요약하는 것도 중요합니다. 김개발 씨는 코드를 따라가며 눈을 반짝였습니다.
"아, while 루프 안에서 생각하고 행동하고 확인하는 거군요!" 박시니어 씨가 고개를 끄덕였습니다. "맞아요.
이 루프가 바로 에이전트의 두뇌예요."
실전 팁
💡 - max_iterations는 보통 3~5회로 설정하세요. 너무 많으면 비용이 크게 증가하고, 너무 적으면 복잡한 문제를 풀지 못합니다.
- Observation 결과는 필요한 정보만 추출하여 컨텍스트에 추가하세요. 전체 결과를 그대로 넣으면 토큰 소모가 커집니다.
3. Plan and Solve 패러다임 구현
ReAct가 "한 단계씩 생각하며 행동"하는 방식이라면, Plan-and-Solve는 "먼저 전체 계획을 세우고 순서대로 실행"하는 방식입니다. 김개발 씨는 프로젝트 일정을 짜다가 문득 생각했습니다.
"에이전트도 사람처럼 계획을 세울 수 있을까?" 박시니어 씨가 대답했습니다. "당연하죠.
그게 Plan-and-Solve예요."
Plan-and-Solve 패러다임은 에이전트가 문제를 해결하기 전에 전체 계획을 먼저 수립하고, 그 계획을 순차적으로 실행하는 방식입니다. 마치 건축가가 건물을 짓기 전에 설계도면을 그리고, 그에 따라 공사를 진행하는 것과 같습니다.
ReAct보다 복잡한 다단계 작업에서 더 안정적인 성능을 보여줍니다.
다음 코드를 살펴봅시다.
from langchain_openai import ChatOpenAI
def plan_and_solve(problem):
"""Plan-and-Solve 패러다임 구현"""
llm = ChatOpenAI(model="gpt-4", temperature=0)
# 1단계: 전체 계획 수립
plan_prompt = (
f"문제: {problem}\n"
"이 문제를 해결하기 위한 단계별 계획을 세워주세요.\n"
"각 단계는 명확하고 구체적이어야 합니다."
)
plan = llm.invoke(plan_prompt).content
# 2단계: 계획을 파싱하여 순차 실행
steps = [s.strip() for s in plan.split('\n') if s.strip()]
results = []
for i, step in enumerate(steps):
# 각 단계를 순차적으로 실행
step_result = llm.invoke(
f"계획의 {i+1}단계: {step}\n"
f"이전 결과: {results[-1] if results else '없음'}\n"
"이 단계를 실행한 결과를 알려주세요."
).content
results.append(step_result)
return results
김개발 씨는 ReAct 사이클을 이해한 뒤, 새로운 의문이 생겼습니다. "사이클을 반복하다 보면 엉뚱한 방향으로 갈 수도 있지 않나요?" 박시니어 씨가 눈을 빛내며 대답했습니다.
"정확한 지적이에요. 그 문제를 해결하는 게 바로 Plan-and-Solve 패러다임입니다." Plan-and-Solve를 쉽게 비유하자면 여행 계획을 세우는 것과 같습니다.
ReAct는 여행지에 도착해서 "음, 뭘 먹을까? 어디를 갈까?"라고 그때그때 결정하는 여행자입니다.
반면 Plan-and-Solve는 출발 전에 "1일 차: 경복궁 -> 북촌 한옥마을 -> 인사동"이라며 전체 일정을 미리 짜는 여행자입니다. 어떤 방식이 더 나을까요?
답은 상황에 따라 다릅니다. 단순한 질문에는 ReAct가 빠르고 효율적입니다.
하지만 "시장 조사 보고서를 작성해줘"처럼 여러 단계가 필요한 복잡한 작업에는 Plan-and-Solve가 훨씬 안정적입니다. Plan-and-Solve가 등장한 배경을 살펴볼까요?
ReAct 방식으로 복잡한 작업을 수행하면, 에이전트가 중간에 방향을 잃거나 불필요한 단계를 반복하는 경우가 종종 발생했습니다. "계산을 하려고 했는데 갑자기 검색을 하고, 다시 계산으로 돌아오고..."하는 식이죠.
이런 비효율을 줄이기 위해 계획 기반 접근법이 제안되었습니다. 코드를 분석해보겠습니다.
첫 번째 단계에서 LLM에게 문제를 주고 전체 계획을 수립하도록 합니다. "1.
데이터 수집, 2. 전처리, 3.
분석, 4. 시각화, 5.
보고서 작성"처럼 구체적인 단계들이 출력됩니다. 두 번째 단계에서는 이 계획을 파싱하여 각 단계를 순차적으로 실행합니다.
이때 이전 단계의 결과를 다음 단계에 전달하여 일관성을 유지합니다. 이 방식의 큰 장점은 추적 가능성입니다.
계획이 명시적으로 세워지기 때문에, 에이전트가 어디까지 진행했고 어디서 문제가 발생했는지 쉽게 파악할 수 있습니다. 디버깅이 훨씬 수월해지죠.
실무에서는 연구 논문 요약, 비즈니스 분석 보고서 생성, 다단계 데이터 파이프라인 구축 같은 작업에 활용됩니다. 예를 들어 "경쟁사 분석 보고서를 작성해줘"라는 요청을 받으면, Plan-and-Solve 에이전트는 "1.
경쟁사 목록 수집 -> 2. 각사 재무 데이터 조회 -> 3.
SWOT 분석 -> 4. 종합 의견 도출"의 계획을 세우고 차근차근 실행합니다.
하지만 단점도 있습니다. 계획 단계에서 한 번 세운 계획이 틀리면, 중간에 수정하기 어렵습니다.
또한 계획 수립 자체가 한 번의 LLM 호출이므로, 복잡한 문제에서는 완벽한 계획을 세우지 못할 수도 있습니다. 김개발 씨는 두 패러다임을 비교하며 노트에 정리했습니다.
"ReAct는 유연하지만 방향을 잃을 수 있고, Plan-and-Solve는 안정적이지만 융통성이 부족하군요." 박시니어 씨가 칭찬했습니다. "훌륭한 정리예요.
두 패러다임의 장단점을 이해하는 것이 가장 중요합니다."
실전 팁
💡 - Plan-and-Solve에서는 각 단계의 결과를 다음 단계에 컨텍스트로 전달하세요. 이전 결과를 참고해야 일관된 최종 출력을 얻을 수 있습니다.
- 계획이 너무 세분화되면 오히려 비효율적입니다. 3~7개 정도의 핵심 단계로 구성하는 것이 적절합니다.
4. Reflection 패턴과 자기 평가
지금까지 배운 패러다임들은 에이전트가 '앞으로 나아가는' 방식이었습니다. 하지만 진정으로 똑똑한 에이전트라면 자신의 결과를 돌아보고 개선할 수 있어야 합니다.
김개발 씨가 "에이전트도 자기 반성을 하나요?"라고 묻자, 박시니어 씨는 "물론이죠. 그게 Reflection 패턴이에요"라고 대답했습니다.
Reflection(반성) 패턴은 에이전트가 자신의 출력 결과를 스스로 평가하고, 부족한 점을 찾아 개선하는 과정입니다. 마치 작가가 초안을 쓰고 스스로 퇴고하는 과정과 같습니다.
이 패턴을 적용하면 에이전트의 응답 품질이 크게 향상되며, 특히 창의적 작업과 분석 작업에서 뛰어난 효과를 보여줍니다.
다음 코드를 살펴봅시다.
def reflection_loop(task, max_reflections=3):
"""Reflection 패턴 구현"""
llm = ChatOpenAI(model="gpt-4", temperature=0.7)
# 1단계: 초기 답변 생성
response = llm.invoke(f"다음 작업을 수행하세요: {task}").content
for i in range(max_reflections):
# 2단계: 자기 평가 (비판자 역할)
critique = llm.invoke(
f"원본 작업: {task}\n"
f"현재 답변: {response}\n"
"이 답변의 문제점과 개선점을 분석하세요."
).content
# 3단계: 평가를 바탕으로 개선
improved = llm.invoke(
f"원본 작업: {task}\n"
f"이전 답변: {response}\n"
f"비판: {critique}\n"
"비판을 반영하여 답변을 개선하세요."
).content
# 개선이 충분하면 종료
if is_satisfactory(improved, critique):
return improved
response = improved
return response
김개발 씨는 프로젝트에서 코드 리뷰를 받은 경험이 떠올랐습니다. 선배가 코드를 보며 "이 부분은 이렇게 고치면 더 좋겠어요"라고 피드백을 주었고, 그 피드백 덕분에 코드가 훨씬 깔끔해졌습니다.
"에이전트도 이런 피드백 루프를 가질 수 있다면 좋겠는데요?" 박시니어 씨가 미소를 지었습니다. "이미 있어요.
Reflection이라고 부르죠." Reflection 패턴을 비유하자면, 요리사가 완성된 요리를 맛보고 "소금이 좀 부족하네"라고 판단한 뒤 다시 조리하는 과정입니다. 첫 번째 시도에서 완벽한 결과를 내는 것은 불가능합니다.
하지만 자신의 결과를 객관적으로 평가하고 수정하는 능력이 있다면, 점점 더 나은 결과를 만들어낼 수 있습니다. 이 패턴이 필요한 이유를 생각해볼까요?
LLM은 한 번의 답변으로 완벽한 결과를 내지 못하는 경우가 많습니다. 특히 복잡한 분석, 창의적 글쓰기, 코드 생성 같은 작업에서는 첫 시도가 부족한 경우가 흔합니다.
Reflection은 이런 한계를 극복하는 강력한 방법입니다. 코드의 구조를 살펴보겠습니다.
첫 번째로 LLM이 초기 답변을 생성합니다. 그다음 반복문 안에서 두 가지 역할을 번갈아 수행합니다.
하나는 **비판자(Critic)**로서 현재 답변의 문제점을 찾아내는 역할이고, 다른 하나는 **개선자(Revisor)**로서 비판을 반영하여 답변을 수정하는 역할입니다. 이 두 역할을 번갈아 수행하며 점진적으로 답변의 품질을 높입니다.
흥미로운 점은 비판자와 개선자가 모두 동일한 LLM이라는 것입니다. 모델이 스스로 자신의 출력을 평가하고 개선합니다.
마치 한 사람이 작가와 편집자 역할을 동시에 수행하는 것과 같죠. 놀랍게도 이 방식이 효과적입니다.
LLM은 충분한 지시만 주어지면 자신의 오류를 발견하는 능력이 있습니다. 최근 연구에서는 Reflection의 효과가 입증되었습니다.
코드 생성 작업에서 Reflection을 적용한 그룹이 그렇지 않은 그룹보다 정확도가 약 20% 높았습니다. 특히 복잡한 알고리즘 문제에서 그 효과가 두드러졌습니다.
실무에서는 블로그 글 작성, 코드 생성, 데이터 분석 리포트 작성 같은 작업에 Reflection을 적용합니다. 예를 들어 "Python으로 웹 스크래퍼를 작성해줘"라는 요청에 대해, 첫 번째 답변에서 예외 처리가 빠졌다면 Reflection 단계에서 "예외 처리가 누락되었습니다"라는 비판이 나오고, 개선된 코드에 예외 처리가 추가됩니다.
하지만 Reflection도 만능은 아닙니다. 반복 횟수가 많아질수록 비용이 증가하고, 오히려 과도한 수정으로 원래 의도를 잃어버릴 수도 있습니다.
또한 단순한 사실 확인 같은 작업에는 Reflection이 불필요합니다. "한국의 수도는 어디인가요?" 같은 질문에 반성을 추가할 필요는 없죠.
김개발 씨는 감탄했습니다. "에이전트가 스스로 피드백을 주고받으면서 발전한다니, 사람과 정말 비슷하네요." 박시니어 씨가 고개를 끄덕였습니다.
"그래서 Reflection을 '에이전트의 성장 엔진'이라고도 부르죠."
실전 팁
💡 - Reflection은 max_reflections를 2~3회로 제한하세요. 너무 많은 반복은 오히려 품질을 저하시킬 수 있습니다.
- 단순한 정보 조회에는 Reflection을 적용하지 마세요. 복잡한 생성, 분석, 코딩 작업에만 사용하는 것이 효율적입니다.
5. 패러다임 비교와 선택 가이드
ReAct, Plan-and-Solve, Reflection까지 세 가지 패러다임을 배웠습니다. 김개발 씨의 머릿속에는 자연스러운 의문이 떠올랐습니다.
"이 중에서 어떤 걸 써야 할까요?" 박시니어 씨가 화이트보드에 표를 그리기 시작했습니다. "선택은 상황에 따라 다릅니다.
각 패러다임의 특징을 비교해볼게요."
각 패러다임은 고유한 강점과 약점을 가지고 있으며, 해결하려는 문제의 유형과 복잡도에 따라 적절한 패러다임을 선택해야 합니다. 마치 공사 현장에서 작업에 따라 삽, 망치, 드릴을 선택하는 것과 같습니다.
올바른 선택이 에이전트의 성능을 결정짓는 핵심 요소입니다.
다음 코드를 살펴봅시다.
# 패러다임 선택 가이드 매핑
PARADIGM_GUIDE = {
"ReAct": {
"best_for": ["단순 질의응답", "도구 호출이 1~3회인 작업",
"실시간 대화형 챗봇"],
"strengths": ["빠른 응답 속도", "유연한 대처",
"구현이 간단함"],
"weaknesses": ["복잡한 작업에서 방향 상실 가능",
"비효율적인 단계 반복"],
},
"Plan-and-Solve": {
"best_for": ["다단계 보고서 작성", "연구 논문 분석",
"순차적 데이터 파이프라인"],
"strengths": ["안정적인 실행", "추적 가능성",
"일관된 결과"],
"weaknesses": ["계획 수정이 어려움",
"초기 계획 오류 시 복구 어려움"],
},
"Reflection": {
"best_for": ["창의적 글쓰기", "코드 생성 및 개선",
"복잡한 분석 작업"],
"strengths": ["높은 품질 출력", "자기 개선 능력",
"오류 자동 수정"],
"weaknesses": ["추가 비용 발생", "응답 시간 증가",
"단순 작업에는 불필요함"],
},
}
def select_paradigm(task_type, complexity):
"""작업 유형과 복잡도에 따른 패러다임 추천"""
if complexity <= 3 and "도구" not in task_type:
return "ReAct"
elif complexity >= 7 or "보고서" in task_type:
return "Plan-and-Solve"
else:
return "Reflection" if "생성" in task_type else "ReAct"
김개발 씨는 세 가지 패러다임을 모두 배웠지만, 막상 실무에 적용하려니 어디에 무엇을 써야 할지 막막했습니다. 박시니어 씨는 이런 상황을 잘 알고 있다는 듯 화이트보드에 깔끔한 비교표를 그렸습니다.
"먼저 각 패러다임의 핵심 철학을 정리해볼까요?" ReAct의 핵심 철학은 **'행동 중심'**입니다. 생각하고 행동하되, 너무 깊이 생각하지 않고 빠르게 실행합니다.
마치 경험 많은 요리사가 레시피를 보지 않고도 즉흥적으로 요리하는 것과 같습니다. 이 방식은 속도가 빠르고 유연하지만, 복잡한 요리에는 부적합할 수 있습니다.
Plan-and-Solve의 핵심 철학은 **'계획 중심'**입니다. 실행하기 전에 충분히 생각하고, 계획을 세운 뒤 순서대로 진행합니다.
마치 건축가가 설계도를 완벽하게 그린 뒤 공사를 시작하는 것과 같습니다. 안정적이고 추적 가능하지만, 계획이 틀리면 수정하기 어렵습니다.
Reflection의 핵심 철학은 **'품질 중심'**입니다. 결과물의 품질을 최우선으로 생각하며, 자기 평가를 통해 점진적으로 개선합니다.
마치 소설가가 원고를 여러 번 퇴고하는 것과 같습니다. 품질이 높지만 시간과 비용이 더 듭니다.
코드에서 PARADIGM_GUIDE 딕셔너리는 각 패러다임의 특징을 정리한 것입니다. 이 가이드를 참고하면 상황에 맞는 패러다임을 빠르게 선택할 수 있습니다.
select_paradigm 함수는 작업의 복잡도와 유형을 기반으로 자동으로 패러다임을 추천합니다. 실무에서의 선택 기준을 구체적으로 알아볼까요?
고객센터 챗봇 같은 실시간 응답이 필요한 서비스에는 ReAct가 적합합니다. 빠른 응답 속도가 최우선이기 때문입니다.
반면 분기별 사업 보고서 자동 생성 같은 작업에는 Plan-and-Solve가 좋습니다. 일관된 구조와 누락 없는 내용이 중요하니까요.
그리고 코드 생성이나 창의적 글쓰기에는 Reflection이 빛을 발합니다. 품질이 가장 중요한 작업이니까요.
가장 중요한 점은 이 패러다임들을 조합해서 사용할 수도 있다는 것입니다. 예를 들어 Plan-and-Solve로 전체 계획을 세우고, 각 단계 내에서 ReAct로 세부 작업을 수행한 뒤, 최종 결과에 Reflection을 적용하는 식입니다.
이런 조합을 하이브리드 패러다임이라고 부릅니다. 초보 개발자들이 흔히 하는 실수 중 하나는 "가장 좋아 보이는 패러다임 하나만 고집하는 것"입니다.
"Reflection이 품질이 좋으니까 무조건 Reflection을 쓰면 되지 않나요?"라는 생각은 위험합니다. 단순한 날씨 확인에 Reflection을 적용하면 불필요한 비용과 시간만 낭비됩니다.
김개발 씨는 비교표를 사진으로 찍어 노트에 정리했습니다. "상황에 맞게 선택하는 게 핵심이군요!" 박시니어 씨가 마지막 조언을 덧붙였습니다.
"그리고 가장 좋은 건 여러 패러다임을 조합하는 거예요. 현업에서는 하이브리드 방식을 가장 많이 씁니다."
실전 팁
💡 - 실시간 서비스에는 ReAct, 배치 작업에는 Plan-and-Solve, 품질 집중 작업에는 Reflection을 우선 고려하세요.
- 패러다임을 선택할 때는 응답 속도, 비용, 품질 세 가지 요소를 균형 있게 고려하세요.
6. 실전 예제 각 패러다임 적용해보기
이론은 충분히 배웠습니다. 이제 실제로 코드를 작성하며 세 가지 패러다임을 직접 비교해볼까요?
김개발 씨는 손을 씻고 키보드 앞에 앉았습니다. 박시니어 씨가 옆에 서서 하나의 동일한 문제를 세 가지 방식으로 해결하는 코드를 함께 작성하기 시작했습니다.
동일한 문제를 세 가지 패러다임으로 각각 해결해보면, 각 패러다임의 동작 방식과 장단점을 직관적으로 이해할 수 있습니다. 마치 같은 목적지에 지도, 나침반, GPS 세 가지 방법으로 도달하는 것과 같습니다.
도착지는 같지만 과정이 다르고, 각 방법이 더 잘 맞는 상황이 다릅니다.
다음 코드를 살펴봅시다.
# 동일한 문제: "Python 웹 스크래퍼 작성"을 세 패러다임으로
# 1. ReAct 방식: 즉시 행동하며 해결
def react_scraper(target_url):
"""ReAct: 생각 -> 행동 -> 관찰 반복"""
thought = "웹 페이지 구조를 먼저 파악해야겠다"
html = fetch_page(target_url) # Action: 페이지 가져오기
thought = "원하는 데이터 위치를 찾자"
data = parse_html(html) # Action: 데이터 추출
thought = "데이터를 정리하자"
return format_data(data) # Action: 데이터 정리
# 2. Plan-and-Solve 방식: 계획 후 순차 실행
def plan_solve_scraper(target_url):
"""Plan-and-Solve: 전체 계획 수립 후 단계별 실행"""
plan = [
"1. 타겟 URL 접근 가능 여부 확인",
"2. 페이지 HTML 구조 분석",
"3. CSS 선택자로 데이터 위치 식별",
"4. 데이터 추출 및 정제",
"5. 결과를 JSON 형태로 저장",
]
results = []
for step in plan:
result = execute_step(step, target_url, results)
results.append(result)
return results
# 3. Reflection 방식: 생성 후 자기 평가
def reflection_scraper(target_url):
"""Reflection: 초안 작성 -> 평가 -> 개선 반복"""
draft = generate_scraper_code(target_url) # 초안 생성
for _ in range(2):
critique = evaluate_code(draft) # 코드 평가
if "개선점 없음" in critique:
break
draft = improve_code(draft, critique) # 코드 개선
return draft
김개발 씨와 박시니어 씨는 간단한 문제를 정했습니다. "Python으로 웹 스크래퍼를 작성해보자"는 주제로, 세 가지 패러다임 각각의 방식으로 코드를 구현해보기로 했습니다.
박시니어 씨가 말했습니다. "같은 문제를 다르게 접근하는 걸 보면, 각 패러다임의 특징이 확 와닿을 거예요." 먼저 ReAct 방식으로 구현한 코드를 살펴봅시다.
react_scraper 함수는 명시적인 계획 없이, 각 단계에서 생각(Thought)하고 바로 행동(Action)합니다. "페이지를 가져와야겠다" -> 가져온다 -> "데이터를 추출하자" -> 추출한다.
마치 경험이 풍부한 개발자가 순발력으로 코딩하는 것과 같습니다. 코드가 직관적이고 짧지만, 중간에 예외가 발생하면 대처가 어렵습니다.
두 번째로 Plan-and-Solve 방식입니다. plan_solve_scraper 함수는 실행 전에 5단계의 계획을 명시적으로 세웁니다.
각 단계가 순차적으로 실행되며, 이전 단계의 결과가 다음 단계에 전달됩니다. 이 방식은 전체 흐름이 한눈에 보이고, 어디서 문제가 발생했는지 추적하기 쉽습니다.
하지만 계획에 없는 예외 상황에는 유연하게 대처하기 어렵습니다. 세 번째로 Reflection 방식입니다.
reflection_scraper 함수는 먼저 전체 코드 초안을 생성한 뒤, 스스로 평가하고 개선하는 과정을 반복합니다. "예외 처리가 누락되었습니다", "CSS 선택자가 너무 구체적입니다" 같은 비판이 나오면, 이를 반영하여 코드를 수정합니다.
최종 결과물의 품질이 가장 높지만, 시간과 비용이 가장 많이 듭니다. 세 가지 코드를 나란히 비교해보면 재미있는 패턴이 보입니다.
ReAct 코드는 순차적 사고의 흔적을 보여줍니다. Plan-and-Solve 코드는 구조적 계획의 흔적을 보여줍니다.
Reflection 코드는 발전적 개선의 흔적을 보여줍니다. 실무에서는 어떤 방식을 선택해야 할까요?
만약 긴급한 버그 수정이 필요하다면 ReAct가 적합합니다. 빠르게 원인을 파악하고 수정해야 하니까요.
새로운 기능을 처음부터 구현한다면 Plan-and-Solve가 좋습니다. 체계적인 설계와 구현이 필요하니까요.
그리고 프로덕션 코드의 품질을 높여야 한다면 Reflection이 유용합니다. 코드 리뷰와 개선이 필수적이니까요.
더 나아가, 실무에서는 이 세 가지를 하이브리드로 결합하는 경우가 많습니다. Plan-and-Solve로 전체 구조를 설계하고, 각 단계에서 ReAct로 세부 구현을 수행한 뒤, Reflection으로 전체 코드를 품질 검증하는 식입니다.
이 조합이 가장 강력한 에이전트를 만들어냅니다. 김개발 씨는 세 가지 코드를 비교하며 깊은 이해를 얻었습니다.
"같은 문제인데 접근 방식이 완전히 다르네요. 이제 상황에 맞게 선택할 자신이 생겼어요!" 박시니어 씨가 만족스러운 표정을 지었습니다.
"오늘 배운 내용을 바탕으로 다음에는 저코드 플랫폼에서 에이전트를 직접 만들어볼 거예요. 코딩 없이도 이 패러다임들을 활용할 수 있거든요."
실전 팁
💡 - 세 가지 패러다임을 하이브리드로 결합하면 각 패러다임의 장점만 취할 수 있습니다. Plan-and-Solve로 전체 구조를 잡고, ReAct로 세부 실행을 하며, Reflection으로 품질을 높이세요.
- 실무 프로젝트에서는 먼저 ReAct로 프로토타입을 빠르게 만들고, 점차 Plan-and-Solve와 Reflection을 추가하여 개선하는 점진적 접근이 효과적입니다.
- 다음 카드뉴스에서는 "저코드 플랫폼으로 에이전트 구축"을 다룹니다. 코딩 없이도 배운 패러다임들을 실제 에이전트에 적용하는 방법을 배울 예정입니다.
- 이 카드뉴스는 "AI 에이전트 완전 정복 - Hello Agents" 코스의 4/16편입니다.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
에이전트 발전사 기호주의에서 LLM까지
AI 에이전트의 기원부터 LLM 기반 에이전트까지 발전 과정을 이북 스타일로 살펴봅니다. 초보 개발자도 쉽게 이해할 수 있도록 비유와 스토리텔링으로 풀어냅니다.
Value Embeddings 완벽 분석 ResFormer 아키텍처
AutoResearch 프로젝트의 train.py에 구현된 Value Embeddings(ResFormer) 아키텍처를 심도 있게 분석합니다.
Flash Attention 3과 Rotary Embeddings 완벽 분석
AutoResearch 프로젝트의 train.py에 구현된 Flash Attention 3 커널 선택 로직, Rotary Position Embeddings(RoPE)의 수학적 원리와 구현, 그리고 Sliding Window Attention 패턴을 심도 있게 분석합니다.
GPT 모델 아키텍처 완벽 분석 - CausalSelfAttention부터 GPT까지
AutoResearch의 train.py에 구현된 GPT 모델 아키텍처를 상세 분석합니다. GPTConfig 데이터클래스부터 CausalSelfAttention, MLP, Block, GPT 클래스까지 전체 구조와 가중치 초기화 전략을 다룹니다.
AutoResearch 개요 AI 자율 연구 에이전트 완벽 가이드
Andrej Karpathy의 AutoResearch 프로젝트를 완벽히 분석합니다. AI 에이전트가 자율적으로 LLM 학습 실험을 수행하는 시스템의 전체 아키텍처와 설계 철학을 다룹니다.