안녕하세요, 저는 HolySheep AI의 기술 작가입니다. 이번 가이드에서는 LangGraph의 ReAct 패턴을 처음부터 완전히 구현하는 방법을 설명드리겠습니다. ReAct는 "Reasoning + Acting"의 약자로, AI가 생각하고 행동하는 과정을 결합한 강력한 에이전트 설계 방식입니다.

ReAct 패턴이란 무엇인가?

ReAct 패턴은 AI 모델이 주어진 문제를 풀 때 단순히 답변을 생성하는 것이 아니라, 생각(Reasoning)하고 행동(Acting)을 반복하며 단계적으로 해결책에 도달하는 방식입니다. 예를 들어 수학 문제를 풀 때 "이 문제를 이해했다", "공식을 적용해야겠다", "계산해보자"라는 과정을 거치는 것과 같습니다.

📸 [텍스트 설명: ReAct 에이전트의 워크플로우 다이어그램 - Thought → Action → Observation → Thought 반복]

1. 개발 환경 준비

가장 먼저 필요한 도구를 설치하겠습니다. 터미널에서 다음 명령어를 실행하세요:

# pip로 필요한 패키지 설치
pip install langchain-core langchain-openai langgraph python-dotenv

프로젝트 폴더 생성 및 이동

mkdir my-react-agent cd my-react-agent

환경변수 파일 생성

touch .env

📸 [터미널 스크린샷 힌트: pip install 완료 후 Successfully installed 메시지 확인]

2. HolySheep AI API 설정

이제 HolySheep AI에서 API 키를 발급받겠습니다. HolySheep AI는 지금 가입하면 무료 크레딧을 제공하며, 해외 신용카드 없이 로컬 결제가 가능합니다. 가입 후 대시보드에서 API 키를 복사하세요.

# .env 파일에 API 키 저장
echo "HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY" > .env

3. 기본 ReAct 에이전트 구현

이제 LangGraph를 사용하여 ReAct 에이전트를 만들어보겠습니다. 먼저 전체 코드를 보여드린 후, 각 부분을 자세히 설명드리겠습니다.

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator

HolySheep AI API 키 로드

load_dotenv()

HolySheep AI API 설정

llm = ChatOpenAI( model="gpt-4.1", base_url="https://api.holysheep.ai/v1", api_key=os.getenv("HOLYSHEEP_API_KEY"), temperature=0.7 )

상태 정의

class AgentState(TypedDict): messages: list reasoning: str action: str observation: str

도구 정의 (간단한 계산기 도구)

def calculator(expression: str) -> str: """수학 계산기 도구""" try: result = eval(expression) return f"결과: {result}" except Exception as e: return f"계산 오류: {str(e)}"

도구 목록

tools = { "calculator": calculator }

ReAct 프롬프트 템플릿

REACT_PROMPT = """당신은 단계적으로 사고하는 ReAct 에이전트입니다. 사용자 질문: {input} 다음 형식으로 답변하세요: Thought: 이 문제를 어떻게 풀어야 할지 생각하세요 Action: 사용할 도구 이름 (예: calculator) Action Input: 도구에 전달할 입력값 Observation: 도구 실행 결과를 관찰하세요 ... (필요한 만큼 반복) Final Answer: 최종 답변 도구 목록: {tools} 이전 대화: {history} 결론에 도달하면 Final Answer로 답변하세요.""" def run_agent(state: AgentState) -> AgentState: """ReAct 에이전트 실행 함수""" messages = state["messages"] history = "\n".join([f"{m['role']}: {m['content']}" for m in messages[:-1]]) # LLM 호출 prompt = REACT_PROMPT.format( input=messages[-1]["content"], tools=list(tools.keys()), history=history or "없음" ) response = llm.invoke(prompt) response_text = response.content # 파싱: Thought, Action, Action Input 추출 lines = response_text.split("\n") reasoning = "" action = "" action_input = "" for line in lines: if line.startswith("Thought:"): reasoning = line.replace("Thought:", "").strip() elif line.startswith("Action:"): action = line.replace("Action:", "").strip() elif line.startswith("Action Input:"): action_input = line.replace("Action Input:", "").strip() # 도구 실행 observation = "" if action and action_input: if action in tools: observation = tools[action](action_input) return { "messages": messages + [{"role": "assistant", "content": response_text}], "reasoning": reasoning, "action": action, "observation": observation } def should_continue(state: AgentState) -> str: """계속할지 종료할지 결정""" response = state["messages"][-1]["content"] if "Final Answer:" in response: return END return "continue"

그래프 생성

graph = StateGraph(AgentState) graph.add_node("agent", run_agent) graph.set_entry_point("agent") graph.add_conditional_edges("agent", should_continue, {"continue": "agent", END: END})

컴파일

react_agent = graph.compile()

실행 예제

if __name__ == "__main__": initial_state = { "messages": [{"role": "user", "content": "25 * 4 + 10은 무엇입니까?"}], "reasoning": "", "action": "", "observation": "" } result = react_agent.invoke(initial_state) print("최종 결과:", result["messages"][-1]["content"])

📸 [코드 실행 결과 힌트: Thought → Action: calculator → Action Input: 25 * 4 + 10 → Observation: 결과: 110 → Final Answer: 110]

4. 디버깅과 상태 추적

ReAct 에이전트의 장점은 각 단계를 추적할 수 있다는 점입니다. 실행 과정에서 무슨 일이 일어나는지 확인하려면 상태를 출력해보세요:

def debug_agent(state: AgentState, step: int):
    """각 단계별 디버깅 정보 출력"""
    print(f"\n{'='*50}")
    print(f"🔍 스텝 {step}")
    print(f"{'='*50}")
    print(f"📝 현재 질문: {state['messages'][-1]['content']}")
    
    if state["reasoning"]:
        print(f"💭 추론: {state['reasoning']}")
    if state["action"]:
        print(f"🎯 실행: {state['action']}({state.get('action_input', 'N/A')})")
    if state["observation"]:
        print(f"👁️ 관찰: {state['observation']}")
    
    return state

수정된 에이전트 실행 함수

def run_agent_with_debug(state: AgentState) -> AgentState: """디버깅 포함 ReAct 에이전트""" result = run_agent(state) # 마지막 응답에서 파싱 response = result["messages"][-1]["content"] lines = response.split("\n") for line in lines: if line.startswith("Action Input:"): result["action_input"] = line.replace("Action Input:", "").strip() debug_agent(result, len(result["messages"])) return result

5. HolySheep AI 모델 비교와 비용 최적화

HolySheep AI를 사용하면 여러 모델을 단일 API 키로 테스트하고 비용을 비교할 수 있습니다. 저는 실무에서 모델 선택 기준을 정리해봤습니다:

ReAct 패턴에서는 반복적인 추론 단계가 필요하므로, 저는 Gemini 2.5 Flash로 초기 개발 및 테스트를 진행하고, 프로덕션에서 최종 응답 생성만 GPT-4.1로 전환하는 전략을 사용합니다. 이 방식으로 약 60%의 비용 절감이 가능했습니다.

# HolySheep AI에서 모델 교체 예시

개발 환경: 빠른 반복을 위해 저가 모델 사용

dev_llm = ChatOpenAI( model="deepseek-v3.2", # $0.42/MTok base_url="https://api.holysheep.ai/v1", api_key=os.getenv("HOLYSHEEP_API_KEY") )

프로덕션 환경: 고품질 응답

prod_llm = ChatOpenAI( model="gpt-4.1", # $8/MTok base_url="https://api.holysheep.ai/v1", api_key=os.getenv("HOLYSHEEP_API_KEY") )

6. 실제 활용 예제: 날씨 조회 에이전트

ReAct 패턴의 진정한 힘은 여러 도구를 조합할 때 발휘됩니다. 날씨 조회와 일정을 결합한 예제를 만들어보겠습니다:

# 도구 확장 예시
def get_weather(location: str) -> str:
    """날씨 정보 조회 (시뮬레이션)"""
    weather_db = {
        "서울": "맑음, 22°C",
        "부산": "흐림, 19°C",
        "뉴욕": "비, 15°C"
    }
    return weather_db.get(location, f"{location}의 날씨 정보를 찾을 수 없습니다")

def get_calendar(date: str) -> str:
    """일정 조회 (시뮬레이션)"""
    calendar_db = {
        "오늘": "팀 미팅 14:00",
        "내일": "프로젝트 마감",
        "모레": "휴가"
    }
    return calendar_db.get(date, f"{date}에 일정이 없습니다")

tools_v2 = {
    "calculator": calculator,
    "get_weather": get_weather,
    "get_calendar": get_calendar
}

향상된 ReAct 프롬프트

ENHANCED_REACT_PROMPT = """당신은 도구를 활용하는 ReAct 에이전트입니다. 사용자 질문: {input} 사용 가능한 도구: {tools} 다음 형식으로 단계별로 진행하세요: Thought: 무엇을 해야 하는지 생각 Action: 도구 이름 또는 "final" Action Input: 도구에 전달할 값 또는 최종 답변 Observation: 도구 결과 (final 선택 시 생략) 질문이 명확해지면 Final Answer로 답변하세요.""" def run_enhanced_agent(state: AgentState) -> AgentState: """향상된 ReAct 에이전트""" messages = state["messages"] prompt = ENHANCED_REACT_PROMPT.format( input=messages[-1]["content"], tools="\n".join([f"- {name}: {func.__doc__}" for name, func in tools_v2.items()]) ) response = llm.invoke(prompt) response_text = response.content # 도구 실행 로직 action = "" action_input = "" observation = "" for line in response_text.split("\n"): if line.startswith("Action:"): action = line.replace("Action:", "").strip() elif line.startswith("Action Input:"): action_input = line.replace("Action Input:", "").strip() if action in tools_v2 and action != "final": observation = tools_v2[action](action_input) return { "messages": messages + [{"role": "assistant", "content": response_text}], "reasoning": "", "action": action, "observation": observation }

테스트 실행

if __name__ == "__main__": test_state = { "messages": [{"role": "user", "content": "서울 날씨와 오늘 일정을 알려주세요"}], "reasoning": "", "action": "", "observation": "" } result = run_enhanced_agent(test_state) print("\n응답:", result["messages"][-1]["content"])

📸 [실행 결과 예시: 서울의 날씨를 조회하고 오늘 일정을 조회한 후 종합 답변 생성]

자주 발생하는 오류와 해결책

오류 1: API 키 인증 실패

# ❌ 잘못된 예시 - 실제 도메인 사용
llm = ChatOpenAI(
    base_url="https://api.openai.com/v1",  # 직접 호출은 HolySheep에서 불필요
    api_key="sk-xxxx"
)

✅ 올바른 예시 - HolySheep AI 게이트웨이 사용

llm = ChatOpenAI( base_url="https://api.holysheep.ai/v1", # HolySheep 통과 api_key=os.getenv("HOLYSHEEP_API_KEY") )

원인: API 키가 HolySheep AI 게이트웨이에서 인식되지 않음
해결: base_url을 https://api.holysheep.ai/v1으로 설정하고 HolySheep 키 사용

오류 2: 무한 루프 (Maximum iterations exceeded)

# ❌ 문제: 종료 조건 미설정
graph = StateGraph(AgentState)
graph.add_node("agent", run_agent)
graph.set_entry_point("agent")
react_agent = graph.compile()  # 무한 반복 위험!

✅ 해결: 최대 반복 횟수 제한

from langgraph.checkpoint.memory import MemorySaver MAX_ITERATIONS = 10 def should_continue(state: AgentState) -> str: iteration_count = len([m for m in state["messages"] if m["role"] == "assistant"]) if iteration_count >= MAX_ITERATIONS: return END if "Final Answer:" in state["messages"][-1]["content"]: return END return "continue" checkpointer = MemorySaver() react_agent = graph.compile(checkpointer=checkpointer)

원인: 에이전트가 Final Answer 없이 무한히 도구를 호출
해결: 최대 반복 횟수 설정 및 checkpointer로 상태 관리

오류 3: 도구 응답 파싱 오류

# ❌ 문제: LLM 응답 형식 불일치

LLM이 "The answer is 42" 형식으로 응답하면 파싱 실패

✅ 해결: 정규화 함수 사용

import re def parse_llm_response(response_text: str) -> dict: """LLM 응답을 구조화된 형식으로 파싱""" result = { "thought": "", "action": "", "action_input": "", "final_answer": "" } # 각 라인 파싱 patterns = { "thought": r"Thought:\s*(.+?)(?=\n|$)", "action": r"Action:\s*(\w+)", "action_input": r"Action Input:\s*(.+?)(?=\n|$)", "final_answer": r"Final Answer:\s*(.+?)(?=\n|$)" } for key, pattern in patterns.items(): match = re.search(pattern, response_text, re.DOTALL) if match: result[key] = match.group(1).strip() return result

사용 예시

response_text = "Thought: 계산이 필요합니다\nAction: calculator\nAction Input: 2+2\nFinal Answer: 4" parsed = parse_llm_response(response_text) print(parsed) # {'thought': '계산이 필요합니다', 'action': 'calculator', ...}

원인: LLM이 프롬프트 형식을 지키지 않고 응답
해결: 정규식으로 유연하게 파싱하거나, 파싱 실패 시 재시도 로직 추가

오류 4: 토큰 초과 (Token limit exceeded)

# ✅ 해결: 대화 기록 관리 및 토큰 절약
def trim_messages(messages: list, max_messages: int = 10) -> list:
    """과거 메시지를 정리하여 토큰 사용량 절약"""
    if len(messages) <= max_messages:
        return messages
    
    # 최근 메시지만 유지
    return messages[-max_messages:]

def run_agent_with_trim(state: AgentState) -> AgentState:
    """토큰 관리를 포함한 에이전트"""
    # 메시지 정리
    trimmed_messages = trim_messages(state["messages"])
    state["messages"] = trimmed_messages
    
    # 에이전트 실행...
    result = run_agent(state)
    
    # 관찰 결과 추가
    if result["observation"]:
        result["messages"].append({
            "role": "system",
            "content": f"Observation: {result['observation']}"
        })
    
    return result

원인: 긴 대화 기록이 컨텍스트 창을 초과
해결: 슬라이딩 윈도우 방식으로 과거 메시지 정리

결론

이번 가이드에서는 LangGraph의 ReAct 패턴을 HolySheep AI와 함께 구현하는 방법을 다루었습니다. 핵심 포인트는 다음과 같습니다:

저는 실무에서 이 패턴을 활용하여 고객 지원 챗봇, 데이터 분석 에이전트, 자동화 워크플로우 등을 구현했습니다. HolySheep AI의 글로벌 연결 안정성과 로컬 결제 편의성 덕분에 개발 속도가 크게 향상되었습니다.

지금 바로 시작해보세요!

👉 HolySheep AI 가입하고 무료 크레딧 받기