AI에게 "이 주제 3줄로 정리해줘"라고 시켜본 적 있을 것이다. 그럴듯한 요약이 나온다. 그런데 읽다 보면 꼭 뭔가 빠져 있다 — 잘 자는 법을 물었더니 카페인 얘긴 있는데 침실 온도는 없고, 운동 얘긴 통째로 사라졌다.

"아, 그것도 좀 찾아줘." 다시 시키자니 귀찮다. 그래서 반쪽짜리 요약을 안은 채 그냥 창을 닫는다. 그 익숙한 피로가 오늘 이야기의 출발점이다.

한 번 물어본 걸 다시 물어보게 만드는 건 늘 내 몫이었다. 그런데 만약, AI가 스스로 "어, 이거 아직 부족한데?" 하고 한 바퀴 더 검색하러 갔다가, 이만하면 됐다 싶을 때 알아서 멈춰 답을 준다면? 그 "한 바퀴 더 돌까?"라는 판단 딱 한 줄을, 오늘 당신이 직접 채운다.

🌱 이 글은 ★★☆ 손에 익히기본문 9분 + 직접 만들기 2분(★☆☆ 경로는 30초). 막히면 '정답 보기'로 30초 완주가 보장됩니다.

구경만 하다가, 드디어 내 손으로

지난 두 편을 기억하는 독자라면 흐름이 보일 것이다. 1편에서 우리는 목표 한 줄을 던지고 에이전트가 혼자 여러 바퀴 도는 걸 구경했다. 2편에서는 그 루프의 뚜껑을 열어, 에이전트가 매 바퀴 생각하고(think) → 도구를 쓰고(act) → 결과를 관찰하고(observe) → 다시 생각하는 반복을 X-레이로 해부했다.

이번엔 다르다. 구경도 해부도 지나, 처음으로 직접 조립한다. 그리고 그 추상적이던 "생각→행동→관찰"이 오늘은 아주 구체적인 세 부품으로 손에 잡힌다.

  • 검색 — 도구를 써서 새 정보를 물어온다 (act)
  • 요약 — 물어온 걸 읽고 아는 것에 더한다 (observe)
  • 판단 — "이만하면 됐나, 한 바퀴 더 돌까?"를 정한다 (think → 다시 or 멈춤)

앞의 두 부품(검색·요약)은 이미 굴러가게 만들어 두었다. 당신이 채울 건 세 번째, 판단 하나뿐이다.

💡 더 깊이 — 이게 Vol.1과 뭐가 다른가 개념

Vol.1의 chat-with-your-notes는 "한 번 검색하고, 한 번 답하고" 끝났다. agent-that-acts는 도구를 딱 한 번 불러 썼다. 오늘 만드는 건 그다음 진화다 — 검색을 여러 바퀴 반복하고, 스스로 멈출 때를 판단하는 루프. 전문 용어로는 도구 호출(tool calling)을 종료 조건과 엮은 리서치 에이전트의 씨앗이다. 실무의 리서치 에이전트도 뼈대는 이것과 똑같다: 검색 도구를 반복 호출하다가, 충분하면 멈춘다.

정직하게 — 이 '검색'은 진짜 구글이 아니다

여기서 한 가지 솔직하게 짚고 가자. 오늘 만들 에이전트의 "검색"은 진짜로 웹을 뒤지지 않는다.

브라우저 안에서 진짜 웹검색을 돌리려면 API 키와 CORS라는 장벽을 넘어야 한다. 그 순간 "누구나 클릭 한 번으로"라는 약속이 깨진다. 그래서 우리는 진짜 웹 대신, 이 페이지에 미리 담아둔 작은 문서 더미에서 한 건씩 꺼내 온다. 요약도 거대 언어모델이 아니라, 겹치는 정보를 걸러내는 규칙 몇 줄로 돌아간다.

왜 이렇게 축소했을까? 오늘의 진짜 주인공은 검색 엔진도, 요약 AI도 아니기 때문이다. 주인공은 스스로 반복하고, 스스로 멈추는 판단 하나다. 그 판단을 설치·가입·키 없이 마찰 0으로 체감하도록, 나머지는 일부러 축소 모형으로 만들었다. 이 모형을 진짜 웹검색과 진짜 LLM으로 키우는 법은 5편 goal-to-agent에서 이어진다.

축소 모형이라고 시시한 건 아니다. 반전이 하나 숨어 있다 — 이 문서 더미에는 서로 겹치는 정보가 섞여 있다. 그래서 에이전트가 계속 검색하다 보면, 어느 순간부터 "이미 아는 얘기"만 돌아온다. 정보가 마르는 순간이 실제로 온다는 뜻이다. 바로 이 지점이 오늘의 핵심을 만든다.

루프의 심장은 '멈출 줄 아는 판단'이다

우리는 보통 지능을 "얼마나 많이, 얼마나 잘 하느냐"로 생각한다. 그런데 에이전트에서는 정반대 능력이 똑같이 중요하다 — 멈출 때를 아는 것.

멈출 줄 모르는 에이전트를 상상해 보자. 이미 다 아는 걸 계속 검색하고, 같은 요약을 반복하고, 시간과 돈(진짜 서비스라면 토큰 비용)을 태운다. 반대로 너무 일찍 멈추면 반쪽짜리 답만 내놓는다. 그래서 "한 바퀴 더 돌까, 여기서 멈출까"를 정하는 판단은 장식이 아니라 에이전트의 심장이다. 오늘 당신이 채울 게 바로 그 심장이다.

그 심장의 이름은 shouldContinue(state)다. 말 그대로 "계속해야 하나?"를 묻는 함수다. state(상태)에는 지금까지의 진행 상황이 다 들어 있다:

  • state.facts.length — 지금까지 모은 정보 조각 수
  • state.targetFacts — 목표 (이만큼 모으면 충분하다고 정한 값)
  • state.newFactsThisRound — 이번 검색이 새로 준 정보 수 (0이면 정보가 마른 것)
  • state.rounds — 지금까지 돈 바퀴 수

당신이 할 일은 이 값들을 보고 true(한 바퀴 더!) 또는 false(멈추고 답하자)를 돌려주는 조건 한 줄을 넣는 것이다. 딱 한 칸. 검색도 요약도 이미 돌아가니, 이 한 줄이 마지막 부품이다.

겁먹을 필요 없다. 막히면 힌트 버튼이 있고, 정답 보기 버튼도 (쉬운 것·똑똑한 것 두 종류) 있다. 그리고 무엇을 넣어도 — 심지어 아무것도 안 넣거나, return true처럼 엉뚱하게 넣어도 — 브라우저가 멈추거나 무한루프에 빠지는 일은 없다. 안전장치는 뒤에서 다시 설명하겠다. 일단 겁내지 말고 채워 보자.

🔧 지금 해보기 ACT-3-repeat-loop ★★☆ · 막히면 정답 보기로 30초

옆(또는 아래)의 데모에서, 조사할 질문 하나를 고르고 shouldContinue(state)의 빈칸 한 줄을 채운 뒤 ▶ 에이전트 돌리기를 눌러보자. 카운터가 1→2→3으로 오르며 에이전트가 검색→요약→"충분한가?" 판단을 스스로 반복하고, 정보가 충분해지거나 마르면 알아서 멈춰 요약을 내놓는다. 막막하면 "정답 보기 ★☆☆"를 누르고 ▶만 눌러도 30초 만에 첫 성공이다.

샌드박스 · 코드 조립 · 외부 의존 0 · 빈칸 한 줄 채우고 ▶ 실행. 무엇을 넣어도(빈칸·오답·문법오류) 브라우저는 안전합니다 — 최대 8바퀴 안전상한(HARD_CAP) 내장.ACT-3-repeat-loop

목표를 8로 올려보라 — 진짜 배움은 여기서 온다

첫 성공을 맛봤다면, 이제 이 데모의 진짜 묘미로 들어가자. 여기서 "멈출 줄 아는 판단"이 왜 심장인지가 손끝으로 온다.

먼저 가장 쉬운 조건을 넣어 돌려보자.

return state.facts.length < state.targetFacts;

"아직 목표만큼 못 모았으면 계속." 목표 5로 돌리면 에이전트는 얌전히 5바퀴를 돌고 5개를 모아 멈춘다. 깔끔하다. 여기까지는 아무 문제 없어 보인다.

이제 목표 슬라이더를 8로 올리고 같은 조건으로 다시 돌려보자. 무슨 일이 벌어질까?

에이전트는 8개를 채우려고 계속 검색하는데, 문서 더미가 먼저 바닥난다. 새 정보는 안 나오는데 조건은 "8개 못 채웠으니 계속"이라고 우기니, 에이전트가 헛돈다. 결국 안전 상한(8바퀴)에 부딪혀 강제로 멈춘다. 화면의 마지막 바퀴가 경고색으로 물드는 게 보일 것이다.

문제가 뭐였을까? 이 조건은 "목표만큼 모았나"만 볼 뿐, "새 정보가 아직 나오긴 하나"를 안 물어봤다. 그래서 우물이 마른 줄도 모르고 두레박을 계속 내린다.

이제 조건을 한 단어 더 붙여 똑똑하게 만들어 보자.

return state.facts.length < state.targetFacts && state.newFactsThisRound > 0;

"목표에 못 미쳤고 그리고 이번 검색이 새 정보를 줬을 때만 계속." 목표를 8로 둔 채 이 조건으로 돌리면, 에이전트는 정보가 마르는 순간(newFactsThisRound가 0이 되는 순간) 스스로 "더 파봐야 소용없네" 하고 멈춘다 — 목표를 다 못 채웠는데도. 7바퀴쯤에서 6개를 안고 자기 발로 멈추는 걸 볼 것이다.

두 조건을 나란히 돌려보면 차이가 눈에 박힌다. 하나는 우물이 마른 줄 모르고 상한까지 헛돌고, 하나는 마르는 걸 알아채고 스스로 멈춘다. "충분함"을 어떻게 정의하느냐 — 그 한 줄이 에이전트의 지능을 가른다. 이것이 오늘 손으로 확인하는 한 문장이다: 멈출 줄 아는 판단이 곧 설계다.

💡 더 깊이 — "충분함"을 내 방식대로 정의하기 (★★★) 심화

정답은 하나가 아니다. 종료 규칙은 당신이 설계하는 것이다. 예를 들어 —

  • return state.rounds < 3; → 무조건 딱 3바퀴만 돌고 멈춘다(속도 우선).
  • 핵심 각도가 다 모였는지 검사: const core = ["카페인","빛","온도"]; return !core.every(a => state.aspects.includes(a)); → 이 세 주제를 다 다루면 멈춘다(품질 우선).

"몇 개를 모았나"가 아니라 "무엇을 다뤘나", "새 정보가 몇 바퀴째 안 나오나"로도 충분함을 정의할 수 있다. 실무의 리서치 에이전트도 결국 이 "언제 멈출까"의 설계 싸움이다.

틀려도 안전하다 — 그리고 다음 편으로

앞에서 "무엇을 넣어도 브라우저가 안 멈춘다"고 했다. 그 안전장치를 이제 밝힌다.

이 데모의 엔진 안에는 HARD_CAP = 8이라는 상한이 박혀 있다. 당신이 판단 조건을 아무리 엉뚱하게 채워도 — return true로 "무조건 계속"을 넣어도 — 에이전트는 최대 8바퀴에서 무조건 멈춘다. 빈칸으로 그냥 돌리면? 1바퀴 만에 얌전히 멈춘다(빈 판단은 "멈춤"으로 처리되니까). 문법을 틀리면? 실행 전에 빨간 안내가 뜨고 브라우저는 멀쩡하다. 그러니 마음 놓고 이것저것 넣어 보라. 실패가 안전한 놀이터다.

그런데 이 안전벨트가 오늘은 조용히 뒤를 지켜줬지만, 실전에서는 이 벨트를 당신이 직접 채워야 한다. 상한을 얼마로 걸지, 예산을 얼마나 태울지, 위험한 도구는 어디서 막을지 — 그게 없으면 진짜 에이전트는 정말로 폭주한다. 다음 4편 stop-the-runaway에서는 일부러 폭주하게 만든 루프를 돌려 토큰 미터가 치솟는 걸 목격하고, 그 안전벨트를 하나씩 손으로 걸어 볼 것이다. 오늘 심장을 만들었다면, 다음 편에서는 브레이크를 단다.

마치며 — 에이전트 = 루프 + 도구 + 멈출 줄 아는 판단

방금 당신은 한 줄을 채웠을 뿐이다. 그런데 그 한 줄이 검색이라는 도구반복이라는 루프를 맞물려 돌게 만들었고, 정보가 마르는 순간 스스로 멈추게 했다. 반쪽짜리 요약을 안고 창을 닫던 그 피로를, 오늘은 에이전트가 대신 견뎌 준 셈이다.

"에이전트가 뭐냐"는 질문에, 이제 당신은 화면을 가리키며 답할 수 있다 — 에이전트는 루프 + 도구 + 멈출 줄 아는 판단이다. 정의를 외운 게 아니라, 그 세 부품 중 심장을 직접 끼워 넣어 봤으니까.

당신의 에이전트는 몇 바퀴 만에 답을 냈는가? 잠·집중·번아웃 중 다른 질문으로도 돌려보고, #내에이전트 태그로 "N바퀴 만에 답 냈다"를 자랑해 보자. 누군가는 그 자랑을 보고 "나는 몇 바퀴일까?" 하며 키보드 위로 손을 올릴 것이다 — 바로 지금 당신이 그랬던 것처럼.