ReAct Reason(추론) + Act(행동)의 합성어로, LLM이 단계적으로 생각하면서 필요할 때 도구를 사용하도록 만드는 패턴이다. 앞 편의 LangGraph 기초 위에, 이번엔 "생각하고 → 도구를 쓰고 → 결과를 관찰하고 → 다시 생각하는" 루프를 가진 에이전트를 만든다.

 

1. ReAct의 출력 형식

ReAct 에이전트는 시스템 프롬프트로 "단계적으로 생각하고 필요하면 도구를 쓰라"고 지시받고, 정해진 구조로 출력한다.

  • Thought(생각) — 다음에 무엇을 할지 추론한다.
  • Action(행동) — 사용할 도구의 이름.
  • Action Input(행동 입력) — 그 도구에 넣을 입력.
  • Observation(관찰) — 도구가 돌려준 결과.
  • Final Answer(최종 답변) — 관찰들을 바탕으로 한 최종 응답.

핵심은 Observation(도구 결과)이 다시 추론으로 피드백된다는 점이다. LLM은 도구 결과를 보고 다음 생각을 이어가며, 더 이상 도구 호출이 필요 없을 때 Final Answer를 낸다.

그림 1. ReAct 루프. LLM이 Action으로 도구를 호출하고, Observation을 받아 다시 추론한다.

 

2. 동작 예시 — "도쿄 날씨 어때? 뭘 입을까?"

두 개의 도구가 있다고 하자. 날씨를 알려주는 검색 도구와, 날씨에 맞춰 옷을 추천하는 옷 추천 도구. 질문 하나에 대해 ReAct 에이전트는 다음처럼 추론한다.

 

도구 결과가 매번 대화 이력에 더해져 다시 LLM으로 들어간다. 더 호출할 도구가 없어지는 순간 최종 답변이 나오고 과정이 끝난다.

 

3. LangGraph로 구현하기

이제 이 흐름을 LangGraph로 만든다. 상태는 대화 메시지 리스트 하나면 충분하다. add_messages는 노드가 실행될 때마다 새 메시지를 리스트에 추가하도록 해 주고, Annotated는 messages 필드에 이 동작을 붙이기 위해 필요하다.

도구와 모델(에이전트) 준비

검색 도구는 TavilySearchResults를, 옷 추천 도구는 @tool 데코레이터로 만든다. 옷 추천 도구는 "rain", "wet" 같은 키워드를 보고 적절한 옷을 제안한다(도구 설명은 docstring에 적는다). 그다음 프롬프트에 모델을 연결하고 도구를 바인딩하면 에이전트가 된다.

노드와 조건부 엣지

노드는 둘이다. agent 노드는 모델을 호출해 다음 생각(또는 도구 호출)을 만들고, tools 노드는 마지막 메시지의 도구 호출을 실제로 실행한다. should_continue는 마지막 AI 메시지에 도구 호출이 남아 있는지 보고 경로를 정한다. 있으면 tools로, 없으면 END로 간다. 그리고 tools → agent 엣지로 결과를 다시 추론에 넣는다.

그림 2. ReAct의 LangGraph 그래프. agent가 도구 호출을 내면 tools로, 없으면 END로. tools는 결과를 안고 다시 agent로 돌아간다.

 

4. 실행과 흐름

실행 과정을 따라가면 이렇다. 사용자 메시지가 agent로 들어가 LLM이 응답하고, 그 AI 메시지가 상태의 messages에 추가된다. 이어 should_continue가 마지막 메시지를 꺼내 도구 호출이 있는지 본다. 있으면 tools 노드로 가서 도구 이름과 인자를 뽑아 실행하고, 결과를 ToolMessage로 감싸 다시 모델로 넘긴다. 이 과정이 도구 호출이 더 없을 때까지 반복되고, 그 시점에 최종 답변이 생성되며 그래프는 END에 도달한다.

 

다음 글에서는 에이전트가 자기 출력을 스스로 비평하고 고치는 방법을 다룬다. 답을 내고 끝내는 대신, 되돌아보며 품질을 끌어올리는 Reflection Reflexion 패턴이다.

 

+ Recent posts