ReAct 프롬프팅은 언어 모델이 추론과 행동을 번갈아 하는 패턴입니다. 무엇을 할지에 대한 생각을 쓰고, 검색이나 도구 호출 같은 행동을 하고, 결과를 읽은 다음에야 다음 단계를 정합니다. 이름은 Reason + Act의 줄임말이고, Yao 등의 2022년 논문 "ReAct: Synergizing Reasoning and Acting in Language Models"에서 나왔습니다. 사용자 인터페이스를 만드는 JavaScript 라이브러리 React와는 관련이 없습니다.
웹을 탐색하고, 코드를 실행하고, 파일을 수정하는 대부분의 AI 에이전트는 이 루프의 어떤 버전을 돌립니다. 한 번 손으로 따라가 보면 에이전트의 동작을 예측하고 디버깅하기가 훨씬 쉬워집니다.
Thought, Action, Observation 루프
ReAct 프롬프트는 모델에게 세 종류의 줄을 쓰게 합니다.
- Thought(생각): 현재 상황과 빠진 정보에 대한 추론.
- Action(행동):
search[query],read_file[path],finish[answer]같은 정해진 형식의 도구 호출 하나. - Observation(관찰): 그 행동의 결과. 이 줄은 모델이 쓰지 않습니다. 프로그램(또는 여러분)이 행동을 실행하고 실제 출력을 기록에 더합니다.
그러고 나면 전체 기록이 모델에게 돌아가고, 모델은 다음 Thought를 씁니다. 모델이 finish 행동을 고르면 루프가 끝납니다. 모델은 도구를 직접 실행하지 않습니다. 요청만 할 뿐이고, 그 요청을 수행할지는 모델을 둘러싼 코드가 결정합니다.
추론과 행동을 합치면 각각보다 나은 이유
논문은 ReAct를 추론만 하는 프롬프트(생각의 사슬), 행동만 하는 프롬프트(추론을 쓰지 않는 도구 호출)와 비교했습니다. 위키백과 검색 도구를 쓴 질의응답과 사실 확인에서, 추론만 하는 쪽은 모델이 지어낸 사실 위에 자신만만한 논증을 쌓는 경우가 많았지만 ReAct는 그 사실을 찾아볼 수 있었습니다. 가장 좋은 질의응답 결과는 ReAct와 생각의 사슬을 합치고, 한쪽이 확신 있는 답을 내지 못하면 다른 쪽으로 넘어가는 방식에서 나왔습니다. 행동만 하는 쪽은 목표를 쪼개고 찾은 것을 답으로 엮는 데 어려움을 겪었고, 글로 쓴 생각이 계획을 눈에 보이게 유지해 주었습니다. 텍스트 기반 집안일 게임과 가상 쇼핑 사이트라는 두 가지 상호작용 과제에서, 예시 한두 개로 프롬프트한 ReAct는 저자들이 비교한 학습된 기준 모델보다 좋은 결과를 냈습니다.
일반적인 교훈은 이렇습니다. 생각의 사슬은 모델의 계획을 돕고, 도구는 사실을 주며, 둘 다 필요한 작업에는 어느 한쪽만으로는 부족합니다.
턴별로 보는 ReAct 에이전트
아래 블록은 도구 두 개를 가진 작은 코딩 에이전트입니다. 각 탭은 같은 대화의 한 단계 뒤 모습이고, Observation 줄은 프로그램이 앞의 행동을 실행한 뒤 붙여 넣은 것입니다. 답변을 순서대로 읽어 보세요.
Thought: 어떤 테스트가 어떻게 실패하는지 모릅니다. 코드를 읽기 전에 테스트를 실행하면 정확히 어떤 단언문이 실패하는지 볼 수 있습니다. Action: run_tests[tests/test_cart.py]
두 가지를 눈여겨보세요. 첫 번째 행동은 추측이 아니라 테스트 실행입니다. 모델은 코드를 읽기 전에 증거를 모았습니다. 그리고 각 생각은 가장 최근의 관찰을 참조하는데, 이것이 생각의 사슬이 할 수 없는 부분입니다.
채팅 앱에서 ReAct 해 보기
루프를 보는 데 코드가 필요하지는 않습니다. ChatGPT, Claude, Gemini에서 여러분이 도구가 되면 됩니다. 모델이 행동을 요청하면, 여러분이 수행하고 결과를 붙여 넣습니다.
Thought: 아무 작업을 안 할 때 팬이 시끄럽다면 보통 백그라운드에서 무언가가 CPU를 쓰고 있거나, 팬이 냉각에 애를 먹고 있는 경우(먼지, 막힌 통풍구)입니다. CPU 사용량을 먼저 확인하면 이 두 경우를 가를 수 있습니다.
Action: 작업 관리자(Windows에서 Ctrl+Shift+Esc)나 활성 상태 보기(Mac에서 응용 프로그램 > 유틸리티)를 여세요. 프로세스를 CPU 순으로 정렬하고 30초 기다린 다음, 상위 세 개 프로세스와 CPU 사용률을 알려 주세요.
같은 지시에 각 행동 뒤로 실제 함수를 붙이면 그것이 에이전트입니다.
Python으로 만든 최소한의 ReAct 루프
코드에서는 프로그램이 기록을 보내고, 답변에서 Action 줄을 찾고, 해당 함수를 실행하고, Observation을 덧붙이고, 반복합니다. 이 예시는 OpenAI Python SDK와 위 코딩 에이전트의 시스템 프롬프트를 씁니다.
import re
import subprocess
from pathlib import Path
from openai import OpenAI
client = OpenAI()
MODEL = "your-model-id" # e.g. from your provider's model list
PROJECT = Path.cwd().resolve()
READABLE = {".py", ".md", ".txt", ".toml", ".cfg"}
def inside_project(arg):
# The model chooses arg: resolve it and refuse anything outside the project.
path = (PROJECT / arg).resolve()
if not path.is_relative_to(PROJECT):
raise ValueError(f"{arg} is outside the project")
return path
def run_tests(arg):
path = inside_project(arg)
if not (path.is_file() and path.name.startswith("test_") and path.suffix == ".py"):
raise ValueError(f"{arg} is not a test file")
result = subprocess.run(["pytest", str(path), "-q"], capture_output=True, text=True)
return result.stdout[-2000:]
def read_file(arg):
path = inside_project(arg)
if not path.is_file() or path.suffix not in READABLE:
raise ValueError(f"{arg} is not a file this agent may read")
return path.read_text()[:20000]
TOOLS = {"run_tests": run_tests, "read_file": read_file}
def react(task, system_prompt, max_steps=8):
transcript = f"Task: {task}\n"
for _ in range(max_steps):
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": transcript},
],
temperature=0,
)
text = response.choices[0].message.content
match = re.search(r"Action: (\w+)\[(.*)\]", text)
if not match:
return text
transcript += text[: match.end()] + "\n"
tool, arg = match.groups()
if tool == "finish":
return arg
try:
observation = TOOLS[tool](arg) if tool in TOOLS else f"Unknown tool: {tool}"
except Exception as error: # a missing file becomes an observation, not a crash
observation = f"Error: {error}"
transcript += f"Observation:\n{observation}\n"
return "Stopped after max_steps without an answer."
중요한 세부 사항이 네 가지 있습니다. 기록은 Action 바로 뒤에서 잘라 냅니다. 모델이 가끔 계속 써 내려가면서 자기만의 Observation을 지어내기 때문입니다. 실패한 도구(예를 들어 잘못된 파일 이름)는 루프를 멈추는 대신 모델이 반응할 수 있는 Observation이 됩니다. 단계 제한은 끝없이 도는 모델을 멈춥니다. 그리고 거대한 테스트 로그가 컨텍스트 윈도우를 채우지 않도록 테스트 출력을 잘라 냅니다. 그 윈도우에 무엇을 넣을지 정하는 일이 컨텍스트 엔지니어링입니다.
두 도구는 무언가를 실행하기 전에 인자도 검사합니다. 인자를 고르는 것은 모델이므로 제한은 도구에 두어야 합니다. inside_project는 프로젝트 폴더 밖으로 나가는 경로(../를 쓴 경우 포함)를 거부하고, run_tests는 test_*.py 파일만 받으며, read_file은 몇 가지 코드와 텍스트 파일 형식만 열고 최대 20,000자까지만 돌려줍니다. 그래서 .env 파일이나 SSH 키가 다음 요청에 실리는 일이 없습니다. 그래도 pytest는 프로젝트의 코드를 실행하므로, 이런 에이전트는 컨테이너나 버려도 되는 체크아웃에서 돌리고 비밀 정보가 있는 컴퓨터에서는 돌리지 마세요.
OpenAI, Anthropic, Google의 요즘 API는 네이티브 도구 호출도 제공합니다. 각 도구를 이름과 JSON 스키마로 설명하면, 모델이 Action: 줄 대신 구조화된 도구 호출을 돌려줍니다. 루프는 똑같고, 파싱만 사라집니다.
ReAct 루프의 안전
에이전트는 자기가 쓰지 않은 텍스트, 즉 웹 페이지, 파일, 도구 출력을 읽습니다. 그중 어느 것에든 모델을 겨냥한 지시가 들어 있을 수 있는데, 이것이 프롬프트 인젝션입니다. 각 도구에는 필요한 최소한의 권한만 주고(가능하면 읽기 전용), 삭제하거나, 보내거나, 결제하는 일은 사람이 확인하게 하고, 모델이 요청한 행동은 맹목적으로 실행할 명령이 아니라 확인해야 할 신뢰할 수 없는 입력으로 다루세요.
자주 묻는 질문
ReAct 프롬프팅이란 무엇인가요?
ReAct(Reason + Act의 줄임말)는 언어 모델이 짧은 추론을 쓰고, 검색이나 도구 호출 같은 행동을 하고, 그 결과를 읽은 다음 다시 추론하는 프롬프트 패턴입니다. Yao 등의 2022년 논문 "ReAct: Synergizing Reasoning and Acting in Language Models"에서 나왔습니다. JavaScript 라이브러리 React와는 아무 관련이 없습니다.
ReAct의 Thought, Action, Observation은 무엇인가요?
Thought(생각)는 다음에 무엇을 할지에 대한 모델의 추론입니다. Action(행동)은 search[python 3.13 release notes]처럼 정해진 형식의 도구 호출입니다. Observation(관찰)은 도구의 출력으로, 프로그램이 행동을 실행해 얻은 뒤 프롬프트에 더합니다. 모델이 답과 함께 finish 행동을 할 때까지 루프가 반복됩니다.
ReAct와 생각의 사슬은 어떻게 다른가요?
생각의 사슬은 모델이 이미 아는 것으로만 추론하므로, 사슬 초반의 잘못된 사실은 계속 틀린 채로 남습니다. ReAct는 추론 사이사이에 실제 정보를 가져오는 행동을 끼워 넣으므로, 모델이 계속하기 전에 사실을 확인하거나, 테스트 실패를 보거나, 파일을 읽을 수 있습니다. ReAct 논문은 추론을 검색 결과에 근거하게 하면 생각의 사슬만 쓸 때 생기던 지어낸 사실이 줄어든다는 것을 발견했습니다.
AI 에이전트는 지금도 ReAct를 쓰나요?
루프는 그렇습니다. 오늘날 대부분의 에이전트는 추론하고, 도구를 호출하고, 결과를 읽고, 다시 결정하는데, 이것이 ReAct 순환입니다. 바뀐 것은 형식입니다. 텍스트에서 Action: 줄을 파싱하는 대신, 요즘 API에는 네이티브 도구 호출 기능이 있어서 모델이 구조화된 도구 요청을 돌려주고 코드가 결과를 돌려보냅니다.
코드 없이 ChatGPT에서 ReAct를 쓸 수 있나요?
네, 직접 도구 역할을 하면 됩니다. 모델에게 Thought 하나와 Action 하나로 답한 뒤 멈추라고 하세요. 여러분이 그 행동(검색, 파일 열기, 명령 실행)을 하고, 결과를 Observation으로 붙여 넣고, 반복합니다. 느리지만 에이전트가 다음 할 일을 어떻게 정하는지 정확히 보여 줍니다.