ReAct + 도구(Tools): LLM이 실제로 계산기와 검색 엔진을 사용하는 방법
요약
LLM이 ReAct 루프를 통해 실제 계산기나 검색 엔진과 같은 외부 도구를 사용하는 원리를 설명합니다. 모델이 텍스트를 생성하면 코드가 이를 파싱하여 실제 함수를 실행하고 결과를 다시 모델에 주입하는 에이전트 구조를 다룹니다.
핵심 포인트
- ReAct 패턴은 Thought, Action, Observation의 반복 루프로 구성됩니다.
- LLM은 직접 함수를 실행하는 것이 아니라 특정 형식의 텍스트를 생성합니다.
- 개발자는 모델의 출력을 파싱하여 실제 도구와 연결하는 루프를 구현해야 합니다.
- 도구 사용을 통해 LLM의 수학적 계산 오류와 정보 최신성 문제를 해결할 수 있습니다.
당신의 LLM이 방금 계산기와 검색 엔진을 집어 들었습니다. 그 방법을 알려드리겠습니다.
PromptFromZero의 1일 차에서는 ReAct 루프인 Thought(생각) → Action(행동) → Observation(관찰) 과정을 보여주었습니다. 완료될 때까지 이를 반복하는 방식이죠. 보기에는 깔끔했습니다. 하지만 그것은 약간의 거짓말이기도 했습니다. 해당 데모의 "도구 호출 (tool calls)"은 하드코딩된 가짜였기 때문입니다. 모델이 실제로 무언가를 호출한 것은 아니었습니다.
7일 차에서는 그 문제를 해결합니다. 우리는 실제 도구들을 루프에 연결하며, 갑자기 모델은 환각 (hallucination) 없이 수학 계산을 하고, 실시간 정보를 찾아보고, 이메일을 보내고, 파일을 읽는 등 당신이 건네주는 어떤 함수든 수행할 수 있게 됩니다.
이것이 바로 Perplexity, Cursor의 에이전트 모드, 그리고 지난 2년 동안 당신이 본 모든 "에이전트형 (agentic)" 제품 뒤에 숨겨진 패턴입니다. 그 구조를 이해하고 나면 놀라울 정도로 단순합니다.
아무런 도구가 없는 LLM의 문제점
언어 모델에게 23 × 47을 곱하라고 하면 아마 맞출 것입니다. 하지만 7,391 × 8,847을 곱하라고 하면 이야기를 지어내기 시작합니다. 프랑스의 인구가 "지금 당장" 얼마인지 물어보면, 이미 몇 달 또는 몇 년 전의 데이터인 학습 데이터 속의 숫자를 알려줄 것입니다.
이것은 버그가 아니라 아키텍처 (architecture)의 문제입니다. LLM은 고정된 텍텍스트 예측기입니다. 실행 중인 프로세스도, 시계도, 인터넷도 없습니다. 오직 학습 중에 암기한 패턴을 기반으로 다음 토큰 (token)을 예측할 수 있을 뿐입니다.
ReAct + 도구 (Tools)는 이를 보완하는 패치입니다.
작동을 가능하게 하는 형식
당신은 모델에게 다음 세 가지를 설정하는 시스템 프롬프트 (system prompt)를 제공합니다:
- Thought 레이블 — 모델은 행동하기 전에 항상 자신의 추론 과정을 작성해야 합니다.
- Action 형식 — 당신의 코드가 파싱 (parse)할 수 있는 특정 문자열 패턴입니다.
- 도구 목록 (tool list) — 어떤 도구가 존재하는지, 무엇을 하는지, 어떻게 호출하는지 정의합니다.
다음은 최소한의 시스템 프롬프트 예시입니다:
당신은 ReAct 에이전트입니다.
사용 가능한 도구:
...
그게 전부입니다. 이제 모델은 소리 내어 추론하고, 도구를 선택하며, 당신의 코드가 가로챌 수 있는 방식으로 호출 형식을 맞추어야 한다는 것을 알게 됩니다.
실제 실행 추적: "23 × 47 + 프랑스 인구는 얼마인가?"
프롬프트가 잘 작성된 모델이 단계별로 생성하는 결과는 다음과 같습니다:
Thought: 두 가지가 필요합니다 — 23 × 47의 결과(수학)와 프랑스의 인구(찾아봐야 할 사실). 계산부터 시작하겠습니다.
...
무슨 일이 일어났는지 주목하세요: 모델은 하위 문제의 성격에 따라 어떤 도구(tool)를 사용할지 _선택_했습니다. 수학 → 계산기. 사실 → 검색. 모델은 답변할 준비가 되기 전까지 세 번의 도구 호출(tool calls)을 체이닝(chaining)하기까지 했습니다. 루프(loop)가 계속 실행된 이유는 당신의 코드가 관찰(Observations)을 계속 주입했기 때문입니다.
코드에서의 루프
다음은 뼈대(skeleton) 코드입니다. 메시지 배열(messages array)을 받는 모든 LLM에서 작동합니다:
function parseAction(text) {
const m = text.match(/Action:\s*(\w+)\[(.+?)\]/);
if (m) return { tool: m[1], input: m[2] };
...
주목해야 할 세 가지 사항:
- 모델은 당신의 함수를 호출하지 않습니다. 모델은 텍스트를 작성합니다. 당신이 그 텍스트를 파싱(parse)합니다. 당신이 함수를 실행합니다. 당신이 결과를 주입합니다. 모델은 그저 말할 뿐이며, 당신의 코드가 행동합니다.
- 단계 제한(step cap)은 타협할 수 없는 필수 사항입니다.
step < 8과 같은 제한이 없다면, 혼란에 빠진 모델은 무한 루프를 돌며 당신의 API 예산을 소진할 것입니다. - 도구를 추가하는 것은 매우 간단합니다. 시스템 프롬프트(system prompt)에 한 줄을 추가하고
runTool에 분기(branch)를 하나 추가하면 됩니다. 루프 자체는 전혀 변하지 않습니다.
모든 행동(Action) 전에 사고(Thought) 과정이 중요한 이유
토큰을 아끼기 위해 사고(Thought) 요구 사항을 생략하고 싶은 유혹이 들 수도 있습니다. 그러지 마세요.
사고(Thought) 과정은 모델이 도구를 선택하기 _전_에 계획을 확정하도록 강제합니다. 이 과정이 없으면 모델은 반사적으로 도구를 호출한 뒤 나중에 결과를 합리화하는 경향이 있습니다. 사고 과정이 있으면 모델은 무엇이 필요한지 결정한 _후_에 그것을 요청합니다. 정확도가 눈에 띄게 향상되며, 추적(traces)을 디버깅하기가 훨씬 쉬워집니다.
또한, 에이전트(agent)의 추론 과정을 사람이 읽을 수 있는 로그(log) 형태로 무료로 얻을 수 있다는 의미이기도 하며, 이는 운영 환경(production)에서 문제가 발생했을 때 매우 중요합니다.
이 패턴을 언제 사용해야 할까요?
다음과 같은 경우에 사용하세요:
- 질문이 실시간 데이터 (가격, 날씨, 최신 뉴스)를 필요로 하는 경우
- 질문이 큰 숫자에 대한 신뢰할 수 있는 산술 (arithmetic) 연산을 필요로 하는 경우
- 답변을 위해 각각 다른 능력이 필요한 **여러 하위 단계 (sub-steps)**가 필요한 경우
- 사용자에게 답변이 생성된 과정에 대한 **검증 가능한 추적 (verifiable trace)**을 제공하고 싶은 경우
다음과 같은 경우에는 건너뛰세요 (단순한 프롬프팅만 사용하세요):
- 질문이 단순한 Q&A이며 모델이 이미 학습을 통해 답을 알고 있는 경우
- 지연 시간 (Latency)이 매우 중요한 경우 — 모든 도구 호출 (tool call)은 왕복 시간 (round trip)을 추가합니다
- 결정론적 (deterministic) 출력이 필요한 경우 — 에이전트 (agents)는 비결정론적 (non-determinism) 요소를 도입합니다
대화형 데모 (The interactive demo)
이 시리즈는 모든 기술을 실시간 대화형 페이지로 제공합니다. 7일 차 데모에서는 세 가지 질문 중 하나(
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기