실무 Multi-Agent 오케스트레이션 9장 · 에이전트 루프 직접 구현 2 / 7 ← 이전목차다음 → TechLead Cro

9장. 에이전트 루프 직접 구현

ReAct — 생각, 행동, 관찰을 되풀이한다

한 줄 요약

에이전트 루프의 한 바퀴는 생각(Thought) → 행동(Action) → 관찰(Observation) 세 단계로 나눌 수 있습니다. 이 방식을 ReAct라고 부릅니다. 생각은 모델이 하고, 행동은 우리 코드가 실행하고, 관찰은 우리 코드가 모델에게 돌려줍니다.


1. 사람이 일하는 방식

주문 내역을 대신 찾아 주는 직원을 떠올립니다.

  1. "주문 목록부터 봐야겠다." — 생각
  2. 주문 목록 화면을 연다. — 행동
  3. "배송중이 세 건 있네." — 관찰
  4. "세 건의 송장을 하나씩 봐야겠다." — 다음 생각
  5. …

한 번 보고 끝나지 않습니다. 본 것을 근거로 다음에 할 일을 정하고, 더 볼 것이 없을 때 답합니다.


2. ReAct라는 이름

이 되풀이에 붙은 이름이 ReAct입니다. Reasoning(추론)과 Acting(행동)을 합친 말로, 2022년에 발표된 논문에서 제안되었습니다. 이름을 알아 두면 자료를 찾을 때 검색어로 쓸 수 있습니다.

단계 뜻 누가 하나 코드에서
Thought (생각) 지금 무엇을 해야 하는지 정한다 모델 generate_content 호출 한 번
Action (행동) 정한 대로 도구를 실행한다 우리 코드 fn(**fc.args)
Observation (관찰) 실행 결과를 모델이 볼 수 있게 한다 우리 코드 결과를 contents에 추가

5장에서 배운 결정과 실행의 분리가 그대로입니다. 모델은 "이 도구를 이 인자로 불러 달라"고 요청할 뿐이고, 실행은 우리 코드가 합니다. ReAct는 그 왕복을 여러 번 잇는 것입니다.


3. 생각은 어디에 보이는가

"생각" 단계에서 모델이 속으로 한 추론을 우리가 글로 받는 것은 아닙니다. 우리에게 보이는 것은 생각의 결과입니다.

모델의 응답 뜻
function_call이 들어 있다 "아직 더 알아봐야 한다. 이 도구를 불러 달라"
function_call이 없고 글만 있다 "알아볼 것이 끝났다. 이것이 답이다"

그래서 루프의 갈림길은 한 줄입니다.

if not response.function_calls:
    return response.text          # 도구 요청이 없다 → 최종 답변

response.function_calls는 응답에 들어 있는 도구 요청의 목록입니다. 없으면 None입니다.


4. 한 번의 생각에 행동이 여러 개일 수 있다

모델은 한 응답에 도구 요청을 여러 개 담을 수 있습니다. "이 세 주문을 각각 조회해 달라"를 한 번에 말하는 것입니다. 이것을 병렬 함수 호출(parallel function calling) 이라고 합니다.

그래서 행동 단계는 요청 하나가 아니라 목록 전체를 돌며 실행합니다.

observation_parts = []
for fc in response.function_calls:           # 요청이 여러 개일 수 있다
    fn = tools.get(fc.name)                  # 이름으로 도구 함수를 찾는다
    result = fn(**fc.args) if fn else {"error": f"없는 도구: {fc.name}"}
    observation_parts.append(
        types.Part.from_function_response(name=fc.name, response={"result": result}))
코드 뜻
fc.name 모델이 요청한 도구 이름
fc.args 모델이 채운 인자 (딕셔너리)
tools.get(fc.name) 8장의 도구 딕셔너리에서 그 이름의 함수를 꺼낸다
fn(**fc.args) 인자 딕셔너리를 풀어 넣어 실행한다

**fc.args는 딕셔너리를 키=값 인자로 풀어 넣는 파이썬 문법입니다. fc.args가 {"order_id": "HR20260725002"}이면 fn(order_id="HR20260725002")와 같습니다.

8장에서 make_tools가 도구를 이름이 붙은 딕셔너리로 돌려준 이유가 여기 있습니다. 모델이 말한 이름으로 함수를 바로 찾을 수 있습니다.


5. 몇 바퀴를 돌지는 누가 정하는가

우리 코드에는 "먼저 목록을 조회하고, 다음에 상세를 조회하라"는 순서가 없습니다.

정하는 것 누가
어떤 도구를 부를지 모델
한 번에 몇 개를 부를지 모델
몇 바퀴 만에 답할지 모델
도구를 실제로 실행하는 것 우리 코드
최대 몇 바퀴까지 허락할지 우리 코드
어떤 도구를 쓸 수 있게 할지 우리 코드

모델에게 맡긴 것이 많습니다. 그래서 마지막 두 줄, 우리가 쥐고 있는 한도가 중요합니다. 이 한도를 다듬는 일이 10장으로 이어집니다.


핵심 정리

  • 루프의 한 바퀴는 생각 → 행동 → 관찰입니다. 이 방식의 이름이 ReAct입니다.
  • 생각은 모델, 행동(실행)과 관찰(결과 전달)은 우리 코드가 합니다.
  • 응답에 function_call이 있으면 계속, 없으면 최종 답변입니다.
  • 모델은 한 응답에 도구 요청을 여러 개 담을 수 있습니다. 행동 단계는 목록 전체를 실행합니다.
  • 도구의 순서와 바퀴 수는 모델이 정하고, 한도와 쓸 수 있는 도구는 우리가 정합니다.
← 이전 절왜 에이전트 루프가 필요한가 — 결과를 봐야 다음 할 일이 정해진다다음 절 →contents — 루프의 기억은 리스트 하나다
오명운 · macro@prag-ai.com