모델 응답 평가

모델 응답 평가 (Evaluation Testing)

AI 애플리케이션을 테스트할 때는 생성된 콘텐츠를 평가해서 AI 모델이 환각(hallucination) 응답을 만들어내지 않았는지 확인해야 해요. LLM은 비결정적이라 같은 질문에도 매번 다른 답을 낼 수 있으니, 응답 품질을 측정하는 기준이 꼭 필요하죠.

한 가지 방법은 응답 평가에 AI 모델 자체를 사용하는 거예요. 이때는 평가용으로 가장 좋은 모델을 고르면 되는데, 반드시 응답을 생성한 모델과 같을 필요는 없어요. 즉 생성은 모델 A가, 평가는 모델 B가 맡는 식으로 분리할 수 있어요.

Spring AI가 응답 평가를 위해 제공하는 인터페이스는 Evaluator인데요, 이렇게 정의돼 있어요:

@FunctionalInterface
public interface Evaluator {
    EvaluationResponse evaluate(EvaluationRequest evaluationRequest);
}

평가의 입력은 EvaluationRequest로, 이렇게 생겼어요:

public class EvaluationRequest {

	private final String userText;

	private final List<Content> dataList;

	private final String responseContent;

	public EvaluationRequest(String userText, List<Content> dataList, String responseContent) {
		this.userText = userText;
		this.dataList = dataList;
		this.responseContent = responseContent;
	}

  ...
}
  • userText: 사용자가 입력한 원본 텍스트 String
  • dataList: Retrieval Augmented Generation 같은 곳에서 가져온, 원본 입력에 덧붙여진 문맥 데이터
  • responseContent: AI 모델이 만들어낸 응답 콘텐츠 String

출처: 공식문서

RelevancyEvaluator (관련성 평가)

RelevancyEvaluatorEvaluator 인터페이스의 구현체로, AI가 생성한 응답이 주어진 문맥에 얼마나 관련 있는지(relevance) 판정해 줘요. 이 평가기는 RAG 흐름의 품질을 가늠하는 데 유용한데, 검색된 문맥을 기준으로 모델의 응답이 사용자 입력과 관련 있는지를 따져 보거든요.

평가는 사용자 입력, AI 모델의 응답, 문맥 정보를 바탕으로 진행돼요. 프롬프트 템플릿으로 AI 모델에게 "응답이 사용자 입력과 문맥에 관련 있는가"를 묻는 구조예요.

RelevancyEvaluator가 기본으로 쓰는 프롬프트 템플릿은 이래요:

Your task is to evaluate if the response for the query
is in line with the context information provided.

You have two options to answer. Either YES or NO.

Answer YES, if the response for the query
is in line with context information otherwise NO.

Query:
{query}

Response:
{response}

Context:
{context}

Answer:

참고: .promptTemplate() 빌더 메서드로 직접 만든 PromptTemplate 객체를 넘겨주면 프롬프트 템플릿을 커스터마이즈할 수 있어요. 자세한 내용은 아래 "커스텀 템플릿"을 참고하세요.

통합 테스트에서 사용하기

RetrievalAugmentationAdvisor를 이용한 RAG 흐름 결과를 검증하는 통합 테스트에서 RelevancyEvaluator를 쓰는 예시예요:

@Test
void evaluateRelevancy() {
    String question = "Where does the adventure of Anacletus and Birba take place?";

    RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
            .vectorStore(pgVectorStore)
            .build())
        .build();

    ChatResponse chatResponse = ChatClient.builder(chatModel).build()
        .prompt(question)
        .advisors(ragAdvisor)
        .call()
        .chatResponse();

    EvaluationRequest evaluationRequest = new EvaluationRequest(
        // The original user question
        question,
        // The retrieved context from the RAG flow
        chatResponse.getMetadata().get(RetrievalAugmentationAdvisor.DOCUMENT_CONTEXT),
        // The AI model's response
        chatResponse.getResult().getOutput().getText()
    );

    RelevancyEvaluator evaluator = new RelevancyEvaluator(ChatClient.builder(chatModel));

    EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);

    assertThat(evaluationResponse.isPass()).isTrue();
}

Spring AI 프로젝트에는 RelevancyEvaluatorQuestionAnswerAdvisor(tests)와 RetrievalAugmentationAdvisor(tests)를 검증하는 여러 통합 테스트가 있어요.

커스텀 템플릿 (Custom Template)

RelevancyEvaluator는 평가 프롬프트에 기본 템플릿을 쓰는데, .promptTemplate() 빌더 메서드로 직접 만든 PromptTemplate을 넘기면 이 동작을 바꿀 수 있어요.

커스텀 PromptTemplate은 어떤 TemplateRenderer 구현이든 쓸 수 있어요(기본은 StringTemplate 엔진 기반의 StPromptTemplate). 중요한 조건은 템플릿이 다음 자리 표시자를 반드시 포함해야 한다는 거예요:

  • query 자리 표시자 — 사용자 질문을 받아요.
  • response 자리 표시자 — AI 모델의 응답을 받아요.
  • context 자리 표시자 — 문맥 정보를 받아요.

FactCheckingEvaluator (사실 확인 평가)

FactCheckingEvaluatorEvaluator 인터페이스의 또 다른 구현체로, AI가 생성한 응답이 주어진 문맥에 대해 사실적으로 정확한지(factual accuracy) 판정해 줘요. 주장(claim)이 문서(document)에 논리적으로 뒷받침되는지 확인해서 AI 출력의 환각을 탐지·줄이는 데 쓸 수 있어요.

'claim'과 'document'를 AI 모델에 제시해서 평가를 요청해요. 이 목적에 특화된 더 작고 효율적인 모델이 있는데, 예를 들어 Bespoke의 Minicheck가 있어요. GPT-4 같은 플래그십 모델에 비해 이런 확인 비용을 크게 줄여주죠. Minicheck는 Ollama를 통해서도 쓸 수 있어요.

사용법

FactCheckingEvaluator 생성자는 ChatClient.Builder를 파라미터로 받아요:

public FactCheckingEvaluator(ChatClient.Builder chatClientBuilder) {
  this.chatClientBuilder = chatClientBuilder;
}

이 평가기가 사실 확인에 쓰는 프롬프트 템플릿은 이래요:

Document: {document}
Claim: {claim}

{document}는 문맥 정보, {claim}은 평가 대상인 AI 모델의 응답이에요.

예시

Ollama 기반 ChatModel, 특히 Bespoke-Minicheck 모델로 FactCheckingEvaluator를 쓰는 예시예요:

@Test
void testFactChecking() {
  // Set up the Ollama API
  OllamaApi ollamaApi = new OllamaApi("http://localhost:11434");

  ChatModel chatModel = new OllamaChatModel(ollamaApi,
			OllamaChatOptions.builder().model(BESPOKE_MINICHECK).numPredict(2).temperature(0.0d).build())


  // Create the FactCheckingEvaluator
  var factCheckingEvaluator = new FactCheckingEvaluator(ChatClient.builder(chatModel));

  // Example context and claim
  String context = "The Earth is the third planet from the Sun and the only astronomical object known to harbor life.";
  String claim = "The Earth is the fourth planet from the Sun.";

  // Create an EvaluationRequest
  EvaluationRequest evaluationRequest = new EvaluationRequest(context, Collections.emptyList(), claim);

  // Perform the evaluation
  EvaluationResponse evaluationResponse = factCheckingEvaluator.evaluate(evaluationRequest);

  assertFalse(evaluationResponse.isPass(), "The claim should not be supported by the context");

}

위 예시에서 context는 "지구는 태양에서 세 번째 행성"이라고 하고, claim은 "지구는 태양에서 네 번째 행성"이라고 주장해요. 평가 결과 isPass()false가 되면서 "claim이 context에 의해 뒷받침되지 않아야 한다"는 검증이 통과하죠.

더 알아보기