9장. 에이전트 루프 직접 구현
왜 에이전트 루프가 필요한가 — 결과를 봐야 다음 할 일이 정해진다
한 줄 요약
고객 문의에 답하려면 도구를 여러 번 호출해야 할 때가 있습니다. 먼저 조회한 결과에 따라 다음에 확인할 정보가 달라지기 때문입니다. 이처럼 모델이 도구를 호출하고, 결과를 확인한 뒤, 다음 행동을 결정하는 과정을 반복하는 구조를 에이전트 루프(agent loop) 라고 합니다. 이번 장에서는 이 구조를 직접 구현합니다.
1. 한 번의 조회로 풀리지 않는 문의
고객(김도윤, C003)이 이렇게 묻습니다.
"지금 배송중인 제 주문들 전부, 상품명이랑 택배사·송장번호를 정리해서 알려주세요."
8장에서 만든 도구로 이 문의를 푼다고 하고, 사람이 먼저 나눠 봅니다.
| 순서 | 할 일 | 쓰는 도구 |
|---|---|---|
| 1 | 내 주문이 무엇무엇인지 본다 | get_my_orders |
| 2 | 그중 상태가 "배송중"인 것을 고른다 | (판단) |
| 3 | 고른 주문마다 택배사와 송장번호를 본다 | get_order_status × 주문 수 |
| 4 | 모아서 정리해 답한다 | (답변) |
1번만으로 끝나지 않는 이유는 8장의 설계에 있습니다. get_my_orders는 요약만 돌려줍니다. 택배사와 송장번호는 get_order_status로 한 건씩 봐야 나옵니다.
그리고 3번에서 어떤 주문번호를 조회할지는 1번의 결과를 보기 전에는 알 수 없습니다. 배송중인 주문이 몇 건인지도 모릅니다. 한 건일 수도, 세 건일 수도, 없을 수도 있습니다.
순서를 미리 코드로 적어 둘 수 없는 일입니다. 결과를 보고 다음을 정해야 합니다.
2. 사실 이미 써 온 것
여기서 한 가지 짚습니다. 7장의 "에어프라이어 재고 있어요?"에서 모델은 search_products를 부른 뒤 그 결과에서 상품ID를 읽어 check_stock을 불렀습니다. 결과를 보고 다음을 정한 것입니다.
그 일을 해 준 것이 6장부터 써 온 자동 함수 호출입니다.
automatic_function_calling=types.AutomaticFunctionCallingConfig(maximum_remote_calls=4)
SDK가 안에서 "모델을 부르고, 도구 요청이 있으면 실행하고, 결과를 넣어 다시 부르는" 일을 되풀이했습니다. 우리는 그 반복을 상자 안에 둔 채 써 왔습니다.
이번 장에서는 그 상자를 엽니다. 5장에서 왕복을 손으로 돌려 본 다음에 6장의 자동 호출을 썼던 것과 같은 순서입니다.
3. 이걸 모르면 무엇을 판단하지 못하는가
- 에이전트가 왜 그 도구를 그 순서로 불렀는지 설명하지 못합니다. 상자 안에서 일어난 일이기 때문입니다.
- 답이 나오기까지 모델을 몇 번 불렀는지, 토큰이 어디서 늘었는지 알지 못합니다.
- 루프가 끝나지 않을 때 어디에 멈춤 장치를 달아야 하는지 모릅니다.
- 뒤에서 프레임워크(11장)를 쓸 때 그것이 대신해 주는 일이 무엇인지 가려내지 못합니다.
루프를 직접 만들어 보면 "여기에 토큰을 세는 코드를 넣을 수 있겠다", "여기서 멈추게 할 수 있겠다"가 보입니다. 그 자리가 10장에서 다룰 가드레일이 들어갈 자리입니다.
4. 이번 장에서 만드는 것
| 지금까지 | 이번 장 |
|---|---|
| SDK가 반복을 대신했다 | 반복문을 우리가 쓴다 |
| 몇 번 돌았는지는 이력에서 나중에 확인 | 돌 때마다 [step 1], [step 2]로 출력 |
| 도구 실행도 SDK가 했다 | 도구 실행도 우리 코드가 한다 |
새로 만드는 도구는 없습니다. 8장의 haru_tools.py를 그대로 불러 씁니다. 이번 장에서 쓰는 코드는 반복문 하나, 30줄 남짓입니다.
산출물은 lesson09_react_loop.py의 react_loop() 함수입니다.
핵심 정리
- 결과를 봐야 다음 조회를 정할 수 있는 문의는 한 번의 도구 호출로 풀리지 않습니다.
- 그런 일을 끝날 때까지 되풀이하는 구조가 에이전트 루프입니다.
- 6장부터 쓴 자동 함수 호출의 안쪽이 바로 이 루프입니다.
- 이번 장에서는 그 반복을 직접 써서 어디에 무엇을 끼울 수 있는지 봅니다.
- 도구는 새로 만들지 않고
haru_tools.py를 불러 씁니다.