Agent Loop란 무엇인가
AI agent가 작업을 완료할 때까지 반복 실행하는 제어 흐름이다. LLM이 스스로 판단해서 도구를 쓰고, 결과를 보고, 다음 행동을 결정하는 루프다.
핵심 구조
1. messages에 user 입력 추가
↓
2. LLM 호출
↓
3. 응답 분석
├─ tool_call 있음 → tool 실행 → 결과를 messages에 추가 → 2로
└─ tool_call 없음 → 최종 응답 반환 (루프 종료)
LLM이 도구를 사용하는 한 루프는 계속 돈다. 도구가 필요 없는 최종 답변을 생성할 때 비로소 종료된다.
왜 loop인가
LLM 한 번 호출로 작업이 끝나지 않기 때문이다. "이번 주 과제 알려줘"라는 요청을 처리하는 과정을 보면:
- LLM →
fetch_lms()tool 호출 결정 - 실제 LMS 긁어옴 → 결과 반환
- 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 trait과 Tool trait을 의존하며, 실제 구현체(Qwen3.5, LMS scraper 등)는 외부에서 주입받는다.
pub struct Agent {
llm: Box<dyn LlmProvider>,
tools: Vec<Box<dyn Tool>>,
messages: Vec<Message>,
}
loop의 반복 횟수를 제한하는 max_turns 같은 안전장치도 필요하다 — 무한루프 방지용.