매번 같은 답을 내지 않는 AI 워크플로우는 어떻게 테스트해야 할까요?
당신은 AI 워크플로우에 대한 테스트를 작성했습니다. 화요일에는 통과했고, 수요일에는 실패했습니다. 입력도 같고, 테스트 케이스도 같습니다. 프롬프트도 같습니다. 출력만 다릅니다.
그래서 다시 실행해 보았습니다. 통과했습니다.
비결정론적(non-deterministic) 시스템을 테스트하는 것에 오신 것을 환영합니다. 개발자들이 가장 잘 아는 테스트 전략들은 결정론적(deterministic) 코드를 위해 만들어졌습니다: 같은 입력이 들어가면, 같은 출력이 나오고, 녹색 체크 표시가 뜨는 방식입니다. AI 워크플로우는 그렇게 작동하지 않습니다. 모델은 조회 테이블(lookup table)에서 가져오는 것이 아니라 확률 분포(probability distribution)에서 샘플링하기 때문에 매번 다른 응답을 생성합니다. Temperature 설정, 컨텍스트 윈도우 압력, 그리고 모델 버전 업데이트 모두 입력이 동일하더라도 출력을 변화시킵니다.
만약 당신의 테스트 전략이
- 출력에 필요한 핵심 정보가 포함되어 있나요?
- 출력이 예상된 형식(JSON, 목록, 단락)인가요?
- 출력이 예상되는 길이 범위 내에 있나요?
- 출력이 특정 내용을 피하나요 (환각된 이름, 잘못된 숫자, 금지된 용어)?
이것들은 정확히 일치하는 테스트가 아니라 속성 기반 테스트(property-based tests)입니다. 출력값이 특정 문자열과 같은지 여부를 확인하는 것이 아니라 제약 조건을 만족하는지를 확인합니다.
내용이 아닌 구조를 테스트하세요
API 엔드포인트를 테스트할 때, 전체 JSON 응답이 고정 값(fixture) 바이트 단위로 일치하는지 단언하지 않습니다. 대신 응답에 예상된 형태가 있는지—올바른 필드, 올바른 타입, 올바른 상태 코드—를 확인합니다.
AI 워크플로우에도 동일한 논리를 적용하세요. 만약 워크플로우가 category, confidence, reasoning 필드를 가진 JSON 객체를 반환해야 한다면, 다음을 테스트하는 것입니다:
def test_classification_output_structure():
result = run_workflow(sample_input)
...
이 테스트는 카테고리가
이것은 특정 유형의 버그를 잡아냅니다. 즉, 대부분의 시간 동안은 작동하지만 간헐적으로 실패하는 워크플로우입니다. 이러한 버그는 요청 시 재현되지 않기 때문에 운영 환경(production)에서 진단하기 가장 어려운 버그들입니다. 신뢰성 테스트(reliability test)는 배포하기 전에 이러한 문제들을 표면으로 드러냅니다.
깨끗한 테스트 데이터가 아닌 실제 입력값으로 테스트하기
테스트 데이터는 보통 깨끗합니다. 하지만 실제 데이터는 그렇지 않습니다. 깨끗한 테스트 입력과 지저분한 운영 입력 사이의 간극이야말로 대부분의 AI 워크플로우 실패가 존재하는 곳입니다.
{"company": "Acme Corp", "domain": "acme.com"}으로 테스트된 리드 강화(lead enrichment) 워크플로우는 매번 성공할 것입니다. 하지만 동일한 워크플로우가 {"company": "Acme Corp.", "domain": ""}을 받으면 조용히 쓰레기를 생성할 수 있습니다. 이는 오류가 아닙니다. 유효한 응답처럼 보이는 쓰레기입니다.
운영 데이터에서 실제 입력값 코퍼스(corpus)를 구축하세요. 필요한 경우 개인 식별 정보(PII)는 제거하되, 지저분함은 유지해야 합니다. 즉, 빈 필드, 예상치 못한 문자 인코딩, 비정상적으로 긴 문자열, 특수 문자가 포함된 필드, 잘못된 형식의 데이터를 포함하는 필드 등을 말합니다. 이 코퍼스를 대상으로 워크플로우를 실행하고 결과를 확인하세요.
아직 운영 데이터가 없다면, 엣지 케이스(edge case)를 수동으로 만드세요:
- 모든 필드가 비어 있는 경우
- 모든 필드가 최대 길이 문자열로 채워진 경우
- 공백만 포함하는 필드
- 특수 문자가 포함된 필드 (
, ; " ' { } [ ] < >)
- 비영어 문자가 포함된 필드
- 숫자여야 할 곳에 문자열과 같은 잘못된 유형의 데이터를 포함하는 필드
이것들은 가설적인 우려 사항이 아닙니다. 제가 검토했던 CRM 리드 할당 워크플로우에서 최악의 실패 유발 요인은 쉼표가 포함된 회사 이름이었습니다. 강화 API는 쉼표를 기준으로 분리하여
AI 워크플로우의 경우, 모든 단계에서 문제가 발생할 수 있습니다. 모델 호출이 시간 초과(timeout)될 수 있고, 모델이 빈 응답을 반환할 수도 있으며, 예상 형식으로 파싱되지 않는 응답을 반환할 수도 있습니다. 도구 호출에서 오류가 발생하거나, 데이터 소스가 이용 불가능할 수도 있습니다. 컨텍스트 윈도우(context window)가 가득 차서 모델이 정보를 누락하기 시작할 수도 있습니다.
워크플로우의 각 단계에 대해 해당 단계를 실패하게 만드는 테스트를 작성하고 무슨 일이 일어나는지 확인하세요:
def test_model_timeout_handling():
with patch("workflow.model_call", side_effect=TimeoutError):
result = run_workflow(sample_input)
...
워크플로우에 시간 초과된 모델 호출을 위한 폴백(fallback)이 없다면, 이 테스트는 실패할 것입니다. 좋습니다. 2시의 사고에서 발견하는 것보다 테스트에서 발견하는 것이 훨씬 낫습니다.
환각 패턴 확인하기 (Check for Hallucination Patterns)
AI 워크플로우는 구조적으로 잘 갖춰져 있고 자신감 있게 진술하지만, 완전히 틀린 출력을 생성할 수 있습니다. 구조만을 확인하는 테스트로는 이를 잡아낼 수 없습니다. 특정 환각(hallucination) 패턴을 확인하는 테스트가 필요합니다.
일반적인 패턴:
- 모델이 입력에 나타나지 않은 이름, 날짜 또는 통계를 지어내는 경우
- 모델이 존재하지 않는 출처를 인용하는 경우
- 모델이 누락된 필드를 그럴듯해 보이지만 조작된 데이터로 채우는 경우
- 모델이 입력과 모순되는 출력을 생성하는 경우
워크플로우에 알려진 공백을 가진 입력을 공급하고, 출력이 이 공백을 조작된 정보로 채우는지 확인하는 테스트를 작성하세요:
def test_no_hallucinated_contact_info():
# 입력에 연락처 정보가 없음
input_data = {"company": "Acme Corp", "domain": "acme.com"}
...
이 테스트는 특정 실패, 즉 모델이 공백을 지어낸 데이터로
모델 제공업체들은 모델을 업데이트합니다. 때로는 이를 알려주고, 때로는 알려주지 않습니다. 이러한 업데이트는 사용자의 코드 변경 없이도 워크플로우의 동작 방식을 깨뜨릴 수 있습니다.
모든 모델 업데이트 전에 테스트 스위트를 재실행하세요. 이전에 통과했던 테스트가 이제 실패한다면, 모델 업데이트가 무언가를 변경했다는 의미입니다. 이는 사용자 코드의 버그가 아니라, 사용자가 처리해야 할 문제입니다.
테스트가 마지막으로 성공한 모델 버전을 기록해 두세요. 테스트가 실패했을 때 가장 먼저 던져야 할 질문은 "모델 버전이 바뀌었나?"여야 합니다. 만약 그렇다면, 새로운 동작에 맞추기 위해 프롬프트, 파싱 로직 또는 테스트 기대치를 업데이트해야 합니다.
만약 모델 버전을 추적하지 않는다면, 실제 변화가 제공업체 측에서 일어났는데도 "코드 변경"을 디버깅하는 데 몇 시간을 낭비하게 될 것입니다. 저는 팀들이 모델 업데이트로 인해 기본 JSON 출력 형식이 단일 라인에서 예쁘게 들여쓰기된(pretty-printed) 형식으로 변경되면서 깨진 워크플로우를 디버깅하느라 3일을 보낸 것을 본 적이 있습니다. 코드는 잘못되지 않았습니다. 모델이 바뀐 것입니다.
회귀 테스트 스위트 구축하기
프로덕션 환경에서 버그를 발견할 때마다, 그에 대한 테스트를 작성하세요. 이것은 전통적인 소프트웨어의 표준 조언입니다. AI 워크플로우에서는 동일한 버그가 모델이 변경될 때 재발할 수 있기 때문에 더욱 중요합니다.
AI 워크플로우에 대한 회귀 테스트는 다음과 같이 생겼습니다:
- 버그를 유발한 입력을 저장합니다.
- 올바른 출력이 무엇이어야 했는지 정의합니다.
- 해당 입력에 대해 워크플로우가 올바른 출력을 생성하는지 확인하는 속성 기반 테스트(property-based test)를 작성합니다.
- 이를 스위트에 추가하고 모든 변경 사항에 대해 실행합니다.
시간이 지남에 따라, 이 스위트는 사용자가 마주친 모든 실패 모드의 기록이 됩니다. 프롬프트를 업데이트하거나, 모델을 변경하거나, 워크플로우 로직을 수정할 때 발생하는 회귀를 포착합니다. 또한 워크플로우가 처리하는 것으로 알려진 엣지 케이스(edge case)들을 문서화합니다.
테스트만으로는 충분하지 않을 때
테스트는 대부분의 문제를 프로덕션 환경에 도달하기 전에 잡아냅니다. 하지만 테스트에는 한계가 있습니다. 즉, 생각한 입력만을 테스트할 수 있다는 것입니다. 실제 프로덕션 트래픽에는 아무도 예상치 못한 입력들이 포함될 것입니다.
워크플로우가 실제로 작동하며 실제 데이터를 처리할 때는 다른 종류의 안전장치가 필요합니다. 바로 워크플로우 전체를 살펴보고 아직 테스트하지 않은 실패 경계(failure boundaries)를 식별하는 진단 검토입니다. 일반적인 체크리스트로는 안 됩니다. 귀하의 특정 워크플로우, 그 특유의 실패 모드(failure modes), 그리고 구체적인 복구 계획에 대한 개별적인 평가가 필요합니다.
AI 워크플로우를 배포할 예정이고 출시 전 진단이 필요하다면, TryPromptFlow에서 컨설턴트 스타일의 검토를 진행하여 실패 경계를 매핑하고 교정 계획을 제공받을 수 있습니다. 신용카드 정보가 필요 없는 무료 진단을 이용해 보세요.