
LLM 에이전트의 도구 호출을 모델을 호출하지 않고 결정론적으로 테스트하기
요약
LLM 에이전트 개발 시 모델 호출 없이 에이전트의 루프와 오케스트레이션 로직을 결정론적으로 테스트하는 방법을 제안합니다. LLM 클라이언트를 추상화하여 테스트 시에는 미리 정의된 스크립트로 교체함으로써 비용을 절감하고 CI 환경에서 안정적인 회귀 테스트를 수행할 수 있습니다.
핵심 포인트
- 에이전트 버그의 대부분은 모델 성능이 아닌 루프와 배선(orchestration) 문제임
- LLM 호출을 추상화하여 테스트 시에는 스크립트 기반의 Fake LLM으로 교체
- 모델 호출 없이도 인자 전달, 예외 처리, 무한 루프 방지 등을 검증 가능
- 비용 $0, 네트워크 호출 0회로 빠르고 결정론적인 CI 테스트 환경 구축
HuggingFace가 「Is it agentic enough? Benchmarking open models on your own tooling」이라는 기사를 발표했다 (2026/6/18). 이것이 의미하는 바는, 모델이 자신의 도구 환경에서 얼마나 잘 작동하는지를 스스로 측정할 수 있다는 것이다.
하지만 내가 현장에서 빠졌던 함정은 그것이 아니었다. 벤치마크는 모델의 똑똑함을 측정한다. 하지만 에이전트가 실무에서 발생하는 버그의 대부분은 모델이 아니라 자신의 루프(loop) 측에 있다. 인수를 잘못 전달하거나, 도구의 에러를 모델에게 다시 전달하는 것을 잊거나, 멈추지 않는 등의 문제다. 이는 벤치마크를 아무리 돌려도 나타나지 않는다.
매번 모델을 호출하기 때문이다. 느리고, 비용이 발생하며, 동일한 입력이라도 출력이 흔들린다. 비결정론적인(non-deterministic) 것을 CI에 올리면, 가끔 실패하는 테스트가 되어 아무도 믿지 않게 된다.
해결책은 간단하다. LLM 클라이언트를 얇은 추상화(abstraction)로 만들고, 테스트할 때만 대본(script)으로 교체하는 것이다.
에이전트 본체가 의존하는 것은 「messages를 전달하면 다음 수(next step)가 반환된다」라는 단 하나의 메서드뿐으로 만든다.
# agent.py
from dataclasses import dataclass
from typing import Callable, Protocol
...
실무에서는 respond 안에서 실제 프로바이더(provider)를 호출하고, 반환된 tool_call을 Step에 담아내는 어댑터(adapter)를 작성한다. 테스트에서는 이 부분을 통째로 교체한다.
정해진 Step을 순서대로 반환하기만 하면 된다. 전달받은 messages도 기록해 둔다.
# fake_llm.py
class ScriptedLLM:
def __init__(self, script):
...
dict(m)으로 복사하는 것이 은근히 중요하다. messages는 루프 도중에 수정되는 가변(mutable) 리스트이므로, 참조(reference) 상태로 가지고 있으면 모든 턴이 최종 상태로 변해버려 검사가 성립하지 않는다. 처음에 이것 때문에 30분을 허비했다.
# test_agent.py (발췌)
def make_tools(log):
return {
...
내 손으로 실행한 결과는 다음과 같다.
$ python -m unittest test_agent -v
test_calls_tool_then_answers ... ok
test_infinite_loop_is_bounded ... ok
...
5개 테스트에 0.2ms, 네트워크 호출 0회, 비용 $0.00. 표준 라이브러리인 unittest만으로 동작하므로 추가 설치도 필요 없다.
여기서 잡아내는 버그들을 나열해 보면 성격이 명확해진다. 인수를 잘못 전달하고 있지는 않은가. 도구 결과가 다음 턴의 messages로 돌아가고 있는가. 도구가 예외(exception)를 던져도 모델에 전달되어 회복할 수 있는가. 알 수 없는 도구 이름으로 죽는가. 멈추지 않는 모델이라도 max_steps로 차단할 수 있는가.
이 모든 것은 모델의 똑똑함과는 무관하다. 배선(orchestration)의 올바름을 묻고 있는 것이다. 그리고 배선의 버그는 자신의 코드에만 존재한다. 벤치마크는 상대방의 모델을 평가하는 도구이며, 자신의 루프에서 발생하는 이러한 종류의 실수는 절대 비춰주지 않는다. 역할이 다르다.
실운용에서는 이 대본 테스트를 토대로 회귀(regression)용 시나리오를 추가해 나가면 된다. 「이 도구가 빈 배열을 반환했을 때」, 「두 번째 호출에서 다른 인수가 되었을 때」를 Step의 열로 작성하기만 하면 재현 테스트가 된다. 버그 보고가 들어오면 그 대화 열을 대본으로 옮겨 빨간색 테스트를 하나 만들고, 이를 초록색으로 만든다. 모델을 단 한 번도 호출하지 않고 말이다.
벤치마크로 똑똑함을 측정하는 것도 중요하지만, 출하(shipping)를 지키는 것은 결정론적 테스트라고 생각한다. 똑똑한 모델로 교체해도, 반환 누락이 발생하는 루프는 고쳐지지 않는다. 처음에 만들어야 할 것은 평가 스크립트보다 이 대본 한 장이었다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기