learn

Agent Loop란 무엇인가

Agent Loop란 무엇인가

AI agent가 작업을 완료할 때까지 반복 실행하는 제어 흐름이다. LLM이 스스로 판단해서 도구를 쓰고, 결과를 보고, 다음 행동을 결정하는 루프다.

핵심 구조

1. messages에 user 입력 추가
       ↓
2. LLM 호출
       ↓
3. 응답 분석
  ├─ tool_call 있음 → tool 실행 → 결과를 messages에 추가 → 2로
  └─ tool_call 없음 → 최종 응답 반환 (루프 종료)

LLM이 도구를 사용하는 한 루프는 계속 돈다. 도구가 필요 없는 최종 답변을 생성할 때 비로소 종료된다.

왜 loop인가

LLM 한 번 호출로 작업이 끝나지 않기 때문이다. "이번 주 과제 알려줘"라는 요청을 처리하는 과정을 보면:

  1. LLM → fetch_lms() tool 호출 결정
  2. 실제 LMS 긁어옴 → 결과 반환
  3. LLM → 결과 보고 자연어로 정리 → 최종 답변

이 과정이 2~3 turn이 되고, 복잡한 작업이면 10 turn도 넘는다. loop가 없으면 중간에 멈춘다.

Rust 구현 예시

async fn run(&mut self, user_input: String) -> String {
    self.messages.push(Message::user(user_input));

    loop {
        let response = self.llm.complete(&self.messages).await;
        
        match response.tool_calls {
            Some(calls) => {
                for call in calls {
                    let result = self.execute_tool(&call).await;
                    self.messages.push(Message::tool_result(result));
                }
                continue; // 결과 들고 다시 LLM에게
            }
            None => {
                return response.content; // 최종 답변, 루프 종료
            }
        }
    }
}

messages가 누적되는 이유

LLM은 stateless다 — 매 호출마다 이전 맥락을 기억하지 못한다. 그래서 tool 결과를 포함한 전체 대화 히스토리를 매번 messages에 담아서 넘겨야 한다. 루프를 돌수록 messages가 길어진다.

SearchAuto에서의 위치

searchauto-core crate 안의 Agent struct가 이 loop를 담는다. LlmProvider traitTool trait을 의존하며, 실제 구현체(Qwen3.5, LMS scraper 등)는 외부에서 주입받는다.

pub struct Agent {
    llm: Box<dyn LlmProvider>,
    tools: Vec<Box<dyn Tool>>,
    messages: Vec<Message>,
}

loop의 반복 횟수를 제한하는 max_turns 같은 안전장치도 필요하다 — 무한루프 방지용.