ReAct 패턴 — 생각(추론)과 행동(툴)을 한 루프에 묶기
RoundPrep 제7화. “툴만 있으면 된다”던 첫 프로토타입이 EMR을 이상한 순서로 두드렸다. Thought–Action 루프로 다음 행동을 말로 고정해야 했다.
들어가며
툴만 붙인 에이전트는 잘못된 순서로 DB를 두드리거나, 관찰 없이 추측으로 답을 마감하기 쉽다. ReAct는 그 사이에 짧은 추론 문장을 끼워 넣어, 다음 행동을 스스로 설명하게 만든다.
이 글은 논문의 수학적 세부까지가 아니라, 의사 독자가 루프를 읽고 구현·검수할 때 무엇을 보면 되는지에 맞췄다.
다루지 않는 것: 특정 SDK 코드. 다루는 것: 패턴 구조, 효과 메커니즘, 한계.
한줄 요약
ReAct는 AI가 행동(툴 호출) 전에 짧은 추론(생각) 을 텍스트로 남기고, 그다음 관찰(툴 결과) 으로 루프를 돌리는 패턴이다.
테제
생각(추론)과 행동(툴)을 한 루프에 묶지 않으면, 툴은 엉뚱한 순서로 호출된다.
스테이크
메트포르민 예시처럼 한 번의 잘못된 툴 순서가 임상에서 무엇을 망가뜨리는지 팀은 이미 본다.
전환점 — RoundPrep 메모
ReAct를 켰더니 루프가 돌고 비용만 샜다. 최대 스텝 없이 운영에 넣지 않기로 한다.
본문
근거 한 줄
Shunyu Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models (arXiv:2210.03629, ICLR 2023). 저자들은 추론만 하거나 행동만 하는 기준선과 비교해, 같은 LLM에서도 ReAct가 ALFWorld·WebShop·HotpotQA 등 상호작용·추론이 섞인 과제에서 성공률을 크게 끌어올렸다고 보고한다. 표에 나온 비율은 과제·모델·프롬프트가 고정된 실험실 조건이므로, “툴 호출이 36%에서 71%로”처럼 한 줄 숫자로 일반화해 쓰면 오해가 생긴다. 임상 시나리오에 그대로 대입하지 말 것.
왜 숫자 대신 패턴을 외워야 하나
같은 LLM·같은 툴 묶음이라도 과제 정의가 바뀌면 성능 곡선은 통째로 바뀐다. 중요한 건 행동 전에 이유를 생성해 다음 토큰 분포를 정렬한다는 구조다. 그 효과가 임상 문서 작업에도 비슷하게 나올지는 별도 검증이 필요하다.
이해를 돕기 위해 아래에서는 생각 없이 행동하는 실패와 ReAct 루프를 대비한다.
생각 없이 행동하는 AI의 실패
응급실에서 AI에게 질문했다.
“이 환자에게 메트포르민 처방 가능한가?”
생각 없이 행동하는 AI는 이렇게 한다:
- 약물 정보 DB에서 메트포르민 검색
- 결과: “메트포르민은 2형 당뇨 치료에 사용됨”
- 답변: “사용 가능합니다”
이 환자의 신기능이 심각하게 저하되어 있었다. eGFR 25였다. 메트포르민은 eGFR 30 미만에서 금기다. AI는 약물의 일반적 용도만 검색했고, 이 환자에게 안전한지를 판단하지 않았다.
ReAct가 같은 질문에 접근하는 방식
질문: "이 환자에게 메트포르민 처방 가능한가?"
[생각] 메트포르민 처방 전 신기능을 먼저 확인해야 한다.
신부전 환자에서 젖산증 위험이 있기 때문이다.
[행동] 환자 최신 Creatinine 수치 조회
[관찰] Creatinine = 2.3 mg/dL
[생각] Creatinine 2.3은 높다. 나이와 성별을 고려한 eGFR을 계산해야 한다.
[행동] eGFR 계산 (Creatinine 2.3, 65세, 남성)
[관찰] eGFR = 28 mL/min/1.73m²
[생각] eGFR 28은 Stage 4 CKD다. 메트포르민 금기 기준을 확인한다.
[행동] 임상 가이드라인 검색 — 메트포르민 금기 기준
[관찰] eGFR < 30에서 메트포르민 사용 금기
[생각] 이 환자는 금기 기준에 해당한다. 대안 약물을 제시해야 한다.
[행동] 가이드라인 검색 — CKD Stage 4 당뇨 치료 대안
[관찰] GLP-1 RA는 신기능 무관하게 사용 가능. SGLT2i는 eGFR ≥ 20에서 가능.
[최종 답변] 이 환자에게 메트포르민은 eGFR 28로 금기입니다.
대안으로 GLP-1 RA(세마글루타이드 등) 또는
eGFR 재확인 후 SGLT2i를 고려할 수 있습니다.
각 행동 전에 왜 이 행동이 필요한지 먼저 말한다. 이 “생각” 단계가 AI를 올바른 방향으로 안내한다.
왜 이게 작동하는가
LLM은 생성 중인 텍스트에 영향을 받는다. “생각: 신기능을 먼저 확인해야 한다”를 텍스트로 생성하고 나면, 그 다음에 나올 행동이 신기능 확인으로 자연스럽게 이어진다.
생각을 먼저 쓰게 만드는 것이 일종의 자기 안내(Self-prompting) 역할을 한다.
의사가 처방 전에 “이 환자는 신기능이 저하되어 있으므로…” 라고 소리 내서 생각하는 것과 같다. 생각을 말로 표현하는 것 자체가 실수를 줄인다.
ReAct의 구조
패턴은 단순하다. 루프를 돌면서 세 단계를 반복한다:
[생각(Thought)] → [행동(Action)] → [관찰(Observation)]
↓
(다시 생각으로)
종료 조건이 충족되면 루프에서 빠져나와 최종 답변을 낸다.
중요한 것은 관찰 단계다. 실제 툴 호출 결과를 받아서, 그걸 바탕으로 다음 생각이 업데이트된다. AI가 혼자 추측하는 것이 아니라 실제 세계의 데이터로 계속 보정한다.
ReAct가 해결하지 못하는 문제
루프 탈출 실패: 생각-행동-관찰을 반복하다가 종료 조건을 만족하지 못해서 계속 도는 경우가 있다. 최대 반복 횟수를 설정해야 한다.
초기 생각이 틀린 경우: “신기능을 확인해야 한다” 대신 “알레르기를 먼저 확인하자”고 생각하면, 이후 모든 행동이 엉뚱한 방향으로 간다. 생각의 전제가 잘못되면 ReAct도 틀린다.
비용: 각 생각-행동-관찰이 LLM 호출이다. 5번 반복하면 5번 비용이 든다.
언제 ReAct를 쓰고 언제 안 쓰나
ReAct가 필요한 경우:
- 어떤 정보가 필요한지 처음에 알 수 없는 경우
- 단계적으로 정보를 모아야 하는 경우
- 중간 결과에 따라 다음 행동이 달라지는 경우
ReAct가 과한 경우:
- 어떤 툴을 호출할지 처음부터 정해진 경우 (단순히 DB 조회 하나면 충분)
- 비용과 속도가 중요하고 단순 답변으로 충분한 경우
반례 — ReAct만 믿으면 안 되는 경우
첫 “생각”이 틀린 전제를 깔면 이후 행동·관찰이 전부 엉뚱한 방향으로 쌓인다. 또 종료 조건·최대 스텝 없이 두면 루프만 비용을 태운다. 응급·고위험 의사결정에서는 ReAct를 감사 가능한 고정 플로우·HITL과 같이 쓰고, 루프 출력 전부를 로그로 남길지 먼저 정한다.
실무 체크리스트
- Thought/Action/Observation 스키마를 프롬프트나 파서로 고정했는가?
- 최대 반복 횟수와 타임아웃을 설정했는가?
- 툴 실패 시 관찰 메시지에 무엇을 넣을지(에러 코드 vs 요약) 정했는가?
- 임상·개인정보 툴은 호출 전·후 마스킹 규칙이 있는가?
- “생각” 텍스트를 사용자에게 노출할지·숨길지 정책이 있는가?
ReAct 루프가 포함된 에이전트를 LangGraph로 구현하면 → LangGraph 입문
여러 에이전트가 각자 ReAct로 생각하고 협력하는 구조 → 오케스트레이터-서브에이전트 패턴 (워크플로 패턴 4번)
다음 회의 한 줄
서진: “생각 텍스트는 로그에 남길지 정책부터 정합시다. 교수님 설명용이에요, 감사용이에요.”
다음 화
루프가 여러 개면 누가 지휘하는지가 필요하다 — 슈퍼바이저 패턴
편집 초안(cu2604021138). 논문 수치·모델 권장은 발행일 이후 바뀔 수 있다. 임상 적용은 규제·지침·면허 의료인 판단을 따른다.
더 알고 싶다면
🔗 관련 글
- 태스크 분해 — 루프를 돌기 전에 태스크를 나누는 방법
- LangGraph 입문 — ReAct 루프를 그래프로 구현하는 방법
- 오케스트레이션 디버깅 — ReAct 루프가 꼬였을 때 원인 찾는 법
- 에이전틱 RAG — ReAct와 반복 검색의 조합