300===Dev Framework/Langgraph

LangGraph로 AI 답변 품질 제대로 평가하기 🔍

블로글러 2025. 3. 9. 20:25

안녕하세요! 오늘은 LangGraph를 사용하여 AI 답변의 퀄리티와 정확도를 평가하는 다양한 방법에 대해 알아보겠습니다. AI 시스템이 제대로 작동하는지 확인하는 건 정말 중요하죠? 함께 살펴볼게요!

LangGraph에서 평가가 왜 중요한가요? 🤔

레스토랑 셰프가 요리를 만들고 손님에게 내기 전에 맛을 보는 것처럼, AI 시스템도 사용자에게 전달하기 전에 품질을 확인해야 합니다. LangGraph에서는 이런 평가를 워크플로우 내에 자연스럽게 통합할 수 있어요!

기본 평가 노드 구현하기 🔨

평가 노드란?

LangGraph에서 평가 노드는 AI가 생성한 답변을 판단하는 특별한 단계예요. 마치 학교 선생님이 학생의 시험지를 채점하는 것과 비슷하죠!

def evaluate_response(state):
    response = state["response"]
    query = state["query"]

    # LLM을 사용한 평가
    evaluation_prompt = f"""
    평가해야 할 질문과 답변이 있습니다:

    질문: {query}
    답변: {response}

    다음 기준으로 1-10점 평가해주세요:
    1. 정확성: 사실에 기반하나요?
    2. 관련성: 질문과 관련 있나요?
    3. 완전성: 모든 측면을 다루었나요?
    4. 명확성: 이해하기 쉬운가요?
    """

    evaluator = ChatOpenAI(temperature=0)
    evaluation = evaluator.invoke(evaluation_prompt)

    return {
        "evaluation": evaluation,
        "next": "feedback_decision"
    }

# 그래프에 평가 노드 추가하기
workflow.add_node("evaluate", evaluate_response)
workflow.add_edge("generate_response", "evaluate")

평가 결과에 따른 분기 처리

def feedback_router(state):
    # 간단한 점수 추출 (실제로는 더 정교한 파싱 필요)
    if "7점 이상" in state["evaluation"]:
        return "final_response"
    else:
        return "improve_response"

workflow.add_conditional_edges("evaluate", feedback_router)
workflow.add_edge("improve_response", "generate_response")  # 다시 생성

이렇게 하면 품질이 낮은 응답은 개선 루프를 통해 다시 생성되고, 좋은 응답만 사용자에게 전달됩니다!

LLM 심사관: AI가 AI를 평가해요 👨‍⚖️

마치 요리 경연대회의 심사위원처럼, 다른 LLM을 '심사관'으로 활용할 수 있어요.

def judge_responses(state):
    response_1 = state["response_model_a"]
    response_2 = state["response_model_b"]
    query = state["query"]

    judge_prompt = f"""
    다음 질문에 대한 두 응답을 비교해주세요:

    질문: {query}

    응답 A: {response_1}
    응답 B: {response_2}

    더 나은 응답은 어떤 것인지, 이유와 함께 선택해주세요.
    """

    judge = ChatOpenAI(model="gpt-4")
    judgment = judge.invoke(judge_prompt)

    # 판정 결과에 따라 처리
    if "응답 A가 더 낫습니다" in judgment:
        better_response = response_1
    else:
        better_response = response_2

    return {
        "final_response": better_response,
        "judgment": judgment
    }

이 방법은 여러 모델이나 프롬프트 전략을 비교할 때 특히 유용해요!

인간 피드백 통합하기 👥

때로는 AI보다 사람의 판단이 더 중요합니다. 사용자 피드백을 워크플로우에 통합해 보세요:

def collect_human_feedback(state):
    response = state["response"]

    # 실제로는 UI를 통해 수집
    print(f"응답: {response}")
    score = input("품질 점수(1-5): ")
    comments = input("개선 제안: ")

    return {
        "human_score": int(score),
        "human_comments": comments,
        "next": "process_feedback"
    }

def improve_based_on_feedback(state):
    if state["human_score"] <= 3:  # 점수가 낮은 경우
        # 피드백을 반영한 개선
        improvement_prompt = f"""
        다음 응답을 개선해주세요:

        원본: {state["response"]}
        사용자 피드백: {state["human_comments"]}
        """

        llm = ChatOpenAI()
        improved = llm.invoke(improvement_prompt)
        return {"improved_response": improved}
    else:
        return {"final_response": state["response"]}

메트릭 기반 자동 평가 📊

숫자로 측정할 수 있는 객관적인 지표를 활용하면 더 일관된 평가가 가능합니다:

def calculate_metrics(state):
    generated = state["response"]
    reference = state["reference_answer"]  # 정답

    # 다양한 메트릭 계산
    metrics = {}

    # 1. ROUGE 점수 (텍스트 유사성)
    from rouge import Rouge
    rouge = Rouge()
    metrics["rouge"] = rouge.get_scores(generated, reference)[0]

    # 2. 의미적 유사도 (코사인 유사도)
    from sentence_transformers import SentenceTransformer
    model = SentenceTransformer('all-MiniLM-L6-v2')
    gen_emb = model.encode([generated])
    ref_emb = model.encode([reference])
    from sklearn.metrics.pairwise import cosine_similarity
    metrics["semantic_similarity"] = float(cosine_similarity(gen_emb, ref_emb)[0][0])

    # 3. 종합 점수 계산
    weighted_score = metrics["rouge"]["rouge-l"]["f"] * 0.5 + metrics["semantic_similarity"] * 0.5
    metrics["overall_score"] = weighted_score

    return {
        "metrics": metrics,
        "passed": weighted_score > 0.7  # 70% 이상이면 통과
    }

테스트 케이스로 객관적 평가하기 ✅

마치 소프트웨어 테스트처럼, 미리 준비된 테스트 케이스로 AI 시스템을 평가할 수 있어요:

def run_test_cases(state):
    # 테스트 케이스 정의
    test_cases = [
        {
            "query": "파이썬 리스트 정렬 방법은?",
            "keywords": ["sort()", "sorted()", "key 파라미터", "reverse"]
        },
        {
            "query": "대한민국의 수도는?",
            "exact_answer": "서울"
        }
    ]

    llm = ChatOpenAI()
    test_results = []

    for test in test_cases:
        response = llm.invoke(test["query"])

        if "keywords" in test:
            # 키워드 포함 여부 검사
            keywords_found = sum(1 for kw in test["keywords"] if kw.lower() in response.lower())
            coverage = keywords_found / len(test["keywords"])
            passed = coverage > 0.7  # 70% 이상 키워드 포함
        elif "exact_answer" in test:
            # 정확한 답변 검사
            passed = test["exact_answer"].lower() in response.lower()

        test_results.append({
            "query": test["query"],
            "response": response,
            "passed": passed
        })

    # 전체 성공률 계산
    pass_rate = sum(1 for r in test_results if r["passed"]) / len(test_results)

    return {
        "test_results": test_results,
        "pass_rate": pass_rate
    }

RAG 시스템 특화 평가 📚

검색 기반 시스템(RAG)은 검색과 생성 모두 평가해야 합니다:

def evaluate_rag(state):
    query = state["query"]
    docs = state["retrieved_documents"]
    answer = state["response"]

    # 1. 검색 품질 평가
    retrieval_prompt = f"""
    다음 질문과 검색된 문서의 관련성을 평가해주세요:

    질문: {query}
    문서: {docs}

    1-10점으로 평가하고, 이유를 설명해주세요.
    """

    # 2. 검색 결과 활용도 평가
    utilization_prompt = f"""
    검색된 문서의 정보가 답변에 얼마나 잘 활용되었는지 평가해주세요:

    질문: {query}
    문서: {docs}
    답변: {answer}

    1-10점으로 평가하고, 이유를 설명해주세요.
    """

    evaluator = ChatOpenAI()
    retrieval_score = evaluator.invoke(retrieval_prompt)
    utilization_score = evaluator.invoke(utilization_prompt)

    return {
        "retrieval_score": retrieval_score,
        "utilization_score": utilization_score
    }

반사실적 평가: 강건성 테스트 🛡️

원래 질문을 의도적으로 변형해서 AI 시스템의 일관성을 테스트해 보세요:

def counterfactual_test(state):
    original_query = state["query"]
    original_response = state["response"]

    # 원본 질문의 변형 버전 생성
    variation_prompt = f"""
    다음 질문의 변형 3가지를 만들어주세요:
    {original_query}

    1. 단어만 바꾸기
    2. 질문 구조 변경하기
    3. 추가 조건 넣기
    """

    llm = ChatOpenAI()
    variations = llm.invoke(variation_prompt)

    # 변형 질문에 대한 응답 생성 및 비교
    # ... (코드 생략)

    return {
        "consistency_score": consistency_score,
        "variations": variations_with_responses
    }

종합적인 평가 전략 수립하기 🏆

각 평가 방법은 장단점이 있으므로, 여러 방법을 조합하는 것이 가장 효과적입니다:

# 종합 평가 워크플로우 예시
evaluation_graph = StateGraph("comprehensive_evaluation")

# 다양한 평가 노드 추가
evaluation_graph.add_node("llm_judge", evaluate_with_llm)
evaluation_graph.add_node("metrics", calculate_metrics)
evaluation_graph.add_node("human_feedback", collect_human_feedback)
evaluation_graph.add_node("test_cases", run_test_cases)

# 평가 결과 집계 노드
def aggregate_results(state):
    # 다양한 평가 결과 종합
    llm_score = state.get("llm_score", 0)
    metrics_score = state.get("metrics", {}).get("overall_score", 0)
    human_score = state.get("human_score", 0)
    test_pass_rate = state.get("pass_rate", 0)

    # 가중치를 적용한 최종 점수 계산
    final_score = (
        llm_score * 0.3 + 
        metrics_score * 0.3 + 
        human_score/5 * 0.3 + 
        test_pass_rate * 0.1
    )

    return {
        "final_score": final_score,
        "verdict": "통과" if final_score > 0.7 else "개선 필요"
    }

evaluation_graph.add_node("aggregate", aggregate_results)

실제 활용 예: 질문-답변 시스템 평가 💬

실제 질문-답변 시스템을 위한 평가 그래프를 만들어 보겠습니다:

# 기본 Q&A 시스템 구조
qa_workflow = StateGraph("qa_system")

# 기본 노드 추가
qa_workflow.add_node("process_query", process_user_query)
qa_workflow.add_node("retrieve_info", retrieve_information)
qa_workflow.add_node("generate_answer", generate_answer)

# 평가 노드 추가
qa_workflow.add_node("evaluate", evaluate_response)

# 개선 노드 추가
qa_workflow.add_node("improve", improve_response)

# 노드 연결
qa_workflow.add_edge("process_query", "retrieve_info")
qa_workflow.add_edge("retrieve_info", "generate_answer")
qa_workflow.add_edge("generate_answer", "evaluate")

# 평가 결과에 따른 분기
def quality_router(state):
    score = parse_score(state["evaluation"])
    if score < 7:
        return "improve"
    else:
        return "final"

qa_workflow.add_conditional_edges("evaluate", quality_router)
qa_workflow.add_edge("improve", "generate_answer")  # 개선 후 다시 응답 생성
qa_workflow.add_edge("final", END)

주의할 점 ⚠️

  1. 평가 기준의 명확성

    • 평가 기준이 모호하면 결과도 일관성이 없어요
    • 구체적이고 측정 가능한 기준을 설정하세요
    • 예: "좋은 답변"보다는 "사실적 정확성, 관련성, 완전성, 명확성"처럼 세부 기준 사용
  2. 평가자 LLM의 한계

    • LLM을 평가자로 사용할 때 평가자 자체의 편향이나 오류 가능성 주의
    • 가능하면 더 강력한 모델(예: GPT-4)을 평가자로 사용하세요
    • 평가 프롬프트를 신중하게 설계하고 테스트하세요
  3. 계산 비용 고려

    • 모든 응답을 평가하면 API 호출 비용이 크게 증가할 수 있어요
    • 샘플링 전략이나 중요한 단계에만 평가 적용 고려
    • 개발 단계에서는 철저히 평가하고, 프로덕션에서는 샘플링 방식 고려
  4. 인간 피드백의 중요성

    • 자동화된 평가만으로는 사용자 만족도를 완전히 측정할 수 없어요
    • 정기적인 인간 평가자의 피드백을 수집하고 반영하세요
    • A/B 테스트를 통해 실제 사용자 반응 측정하기
  5. 평가 결과의 해석

    • 점수만 보지 말고 실패 패턴과 개선 가능성 분석하세요
    • 특정 유형의 질문에서 성능이 낮은지 파악하세요
    • 평가 결과를 바탕으로 시스템을 지속적으로 개선하세요
  6. 평가 체계의 진화

    • 고정된 평가 체계보다 지속적으로 발전하는 평가 체계가 중요해요
    • 새로운 평가 방법과 메트릭을 실험하고 통합하세요
    • 사용자 피드백을 바탕으로 평가 기준도 함께 발전시키세요

마치며 🎁

LangGraph에서 답변 퀄리티와 정확도를 평가하는 것은 단순한 점검이 아니라, 시스템 개선을 위한 핵심 과정입니다. 다양한 평가 방법을 조합하여 여러분의 AI 시스템이 지속적으로 발전할 수 있는 토대를 마련하세요!

레스토랑 셰프가 끊임없이 맛을 개선하듯, AI 시스템도 지속적인 평가와 개선을 통해 더 나은 서비스를 제공할 수 있답니다. 평가는 끝이 아니라 더 나은 시작을 위한 과정이에요!


궁금한 점이 있으시면 댓글로 남겨주세요! 😊


참고 자료

728x90