5.11.2 단계별 중간 출력물 검증을 기반으로 한 디버깅 오라클 구축

5.11.2 단계별 중간 출력물 검증을 기반으로 한 디버깅 오라클 구축

LangSmith와 같은 시각적 트레이싱 도구가 디버깅을 편하게 만들어 주는 것은 사실이나, “엔지니어가 대시보드를 눈으로 살펴봐야 한다“는 것은 여전히 수동(Manual) 테스트의 영역에 머물러 있다는 뜻이다. 진정한 화이트박스 단위 테스트는 이 중간 트레이스(Span) 데이터를 파이썬 코드 레벨로 다시 끌고 들어와, 각 파이프라인 단계마다 자동화된 결정론적 단언문(Assert)을 꽂아 넣는 것이다.

1. 다단계 파이프라인의 연쇄적 붕괴 방지

에이전트(Agent) 모델의 가장 흔한 실패 패턴은 **‘오류의 눈덩이 효과(Snowball Effect)’**다. 첫 번째 단계(예: 의도 파악)에서 발생한 사소한 포맷 환각이 두 번째 단계(API 호출)에 파라미터로 주입되고, 세 번째 단계에서 치명적 오류로 폭발하는 현상이다.

만약 최종 출력물만 검증하는 블랙박스 테스트를 돌린다면 에러 로그는 “3단계 시스템 크래시“로만 기록된다. 오라클은 파이프라인의 끝에서 기다릴 것이 아니라, 각 스팬(Span)이 마무리되는 매 순간 개입하여 조건부 검문소(Checkpoint) 역할을 수행해야 한다.

# 복잡한 다단계 에이전트의 중간 상태를 검증하는 단위 테스트 패턴
def test_multi_stage_agent_pipeline():
    # 1. 테스트 실행 및 트레이스 트리 전체를 반환받음
    agent_trace = run_agent_with_trace(user_input="내일 서울 날씨 어때?")
    
    # 트레이스 트리에서 각 '단계별 중간 출력물(Span Output)'을 추출
    intent_span = agent_trace.get_span_by_name("IntentClassification")
    tool_span = agent_trace.get_span_by_name("WeatherAPICall")
    final_span = agent_trace.get_span_by_name("FinalResponse")
    
    # 2. 중간 검증 오라클 (Intermediate Verification)
    
    # 검문소 1: 의도 파악기(Intent Classifier)가 정확한 카테고리를 뱉었는가?
    assert intent_span.output["intent"] == "WEATHER_INQUIRY", \
        f"[1단계 붕괴] 의도 파악 실패: {intent_span.output}"
        
    # 검문소 2: 도구 호출기(Tool Caller)가 올바른 파라미터로 백엔드 API를 찔렀는가?
    # (결정론적 구조 검증)
    assert tool_span.input_arguments["location"] == "Seoul", \
        f"[2단계 붕괴] 번역/지역 식별 실패: {tool_span.input_arguments}"
        
    # 검문소 3: 최종 출력물(Final Response)
    assert "비" or "맑음" in final_span.output_text

2. 중간 출력과 JSON Schema (다음 장을 위한 포석)

이러한 단계별 중간 검증 오라클이 작동하기 위한 대전제가 있다. 바로 “각 스팬(Span)의 출력물이 프로그래밍 언어로 단언(Assert)할 수 있게 **구조화(Structured)**되어 있어야 한다“는 점이다.

만약 intent_span의 출력이 {"intent": "WEATHER_INQUIRY"}라는 깔끔한 JSON이 아니라, “사용자는 지금 날씨를 묻고 있는 것 같습니다“라는 자연어라면, 우리는 이 오라클을 구축할 수 없다. 트레이싱을 통한 완벽한 화이트박스 테스트는 LLM의 입출력을 강력한 제약으로 묶어버리는 JSON Schema 없이는 완성될 수 없다.

3. Chapter 5. 결론: 결정론적 통제망의 완성

우리는 5장에 걸쳐 ’유닛 테스트’라는 익숙한 소프트웨어 공학의 도구를 거대 언어 모델(LLM)에 맞게 재정의했다.

비결정적인 확률 덩어리인 LLM을 길들이기 위해 우리는 VCR/Mocking으로 외부 환경을 통제했고, 프라퍼티(Property) 기반 테스트로 무한한 엣지 케이스를 폭격했으며, Promptfoo와 DeepEval 같은 현대적 벤치마킹 시스템 위에 이를 얹었다. 마지막으로 옵저버빌리티(Observability)를 통해 파이프라인의 모든 구간에 결정론적 단언문(Assert)을 꽂아 넣음으로써, LLM을 철저한 통제망(Control Web) 안에 가두는 데 성공했다.

코드 레벨에서의 방어선(Testing) 구축이 완료되었다면, 이제 시스템 전체의 데이터 입출력 규격을 강제하는 본질적인 구조적 제약을 설계할 차례다. 이어지는 Chapter 6에서는 이러한 오라클의 가장 강력한 무기이자 실질적인 뼈대인 **‘JSON Schema와 강제 구조화 출력’**의 심연으로 파고들어 볼 것이다.