36차시. 평가 실습
4. 실습 ② — 답변 평가
출처: 원본 PDF 36차시_평가실습.pdf 8쪽 · 쪽 그림 8쪽
LLM 심판을 씁니다. 26차시 Verifier와 비슷하지만 평가 전용 프롬프트를 만듭니다.
📝 eval/prompts.py
from langchain_core.prompts import ChatPromptTemplate
# ── Faithfulness: 근거 충실성 ──
FAITH_PROMPT = ChatPromptTemplate.from_template(
"당신은 평가자입니다. 답변의 각 주장이 근거 자료에 "
"있는지 확인하고 JSON만 출력하십시오.\n\n"
"[근거]\n{context}\n\n"
"[답변]\n{answer}\n\n"
"평가 방법\n"
"1. 답변을 사실 주장 단위로 나눕니다\n"
"2. 각 주장이 근거에 있으면 지지, 없으면 미지지로 셉니다\n"
"3. 문장을 잇는 표현이나 일반 안내는 세지 않습니다\n\n"
'{{"supported": 3, "unsupported": 1, "note": "수수료 언급이 근거에 없음"}}'
)
# ── Correctness: 정답 일치 ──
CORRECT_PROMPT = ChatPromptTemplate.from_template(
"당신은 채점자입니다. 시스템 답변이 모범 답안과 "
"같은 내용인지 판정하고 JSON만 출력하십시오.\n\n"
"[질문] {question}\n"
"[모범 답안] {gold}\n"
"[시스템 답변] {answer}\n\n"
"채점 기준\n"
"- 표현이 달라도 핵심 사실이 같으면 correct: true\n"
"- 숫자·기간·조건이 다르면 false\n"
"- 모범 답안이 '확인할 수 없습니다'인데 시스템이 "
"구체적으로 답했다면 false (환각)\n"
"- 길이나 문체는 평가하지 않습니다\n\n"
'{{"correct": true, "reason": "7일 이내로 일치"}}'
)📝 eval/generation.py
import json, re
import config
from eval.prompts import FAITH_PROMPT, CORRECT_PROMPT
from nodes.generator import build_context
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# ★ 생성과 다른 모델로 자기 편향을 줄인다
_judge_llm = ChatOpenAI(model=config.EVAL_MODEL, temperature=0)
_faith = FAITH_PROMPT | _judge_llm | StrOutputParser()
_correct = CORRECT_PROMPT | _judge_llm | StrOutputParser()
def _parse(raw, default):
m = re.search(r"\{.*\}", raw, re.S)
if not m:
return default
try:
return json.loads(m.group())
except json.JSONDecodeError:
return default
def eval_faithfulness(answer, docs):
if not docs:
return None # 근거가 없으면 평가 불가
v = _parse(_faith.invoke({"context": build_context(docs),
"answer": answer}),
{"supported": 0, "unsupported": 1})
s, u = v.get("supported", 0), v.get("unsupported", 0)
return s / (s + u) if (s + u) else 1.0
def eval_correctness(question, gold, answer):
v = _parse(_correct.invoke({"question": question, "gold": gold,
"answer": answer}),
{"correct": False, "reason": "판정 실패"})
return bool(v.get("correct")), v.get("reason", "")