Skip to content

feat: add rag retrieval evaluation dataset - #110

Merged
Yu-JeSeung merged 2 commits into
mainfrom
feat/chatbot_assessment_automation
May 11, 2026
Merged

feat: add rag retrieval evaluation dataset#110
Yu-JeSeung merged 2 commits into
mainfrom
feat/chatbot_assessment_automation

Conversation

@Yu-JeSeung

@Yu-JeSeung Yu-JeSeung commented May 8, 2026

Copy link
Copy Markdown
Contributor

관련 이슈

Open #109

🎯 배경

  • 챗봇 RAG 성능 개선을 위해 검색 품질을 정량적으로 확인할 수 있는 평가 기준이 필요했습니다.
  • 기존에는 개별 회귀 테스트 중심이라 Top-1/Top-3 검색 정확도, 출처 URL 일치 여부, 날짜 포함 여부, 응답 시간 등을 한 번에 보기 어려웠습니다.
  • 평가 과정에서 복학, 현장실습, 졸업보류, 졸업유예, 전문학사 졸업학점처럼 검색 후보는 잡히지만 최종 답변 출처가 어긋나는 케이스가 확인되어, 검색 결과 재정렬 규칙도 함께 보정했습니다.

🔍 주요 내용

  • RAG 평가 자동화 스크립트 추가

    • tests/regression/evaluate_rag_retrieval.py
    • Top-1 URL 정확도, Top-3 URL 정확도, 제목 일치, 답변 키워드 포함, 출처 URL 일치, 날짜 포함, hallucination proxy, 응답 시간 측정
  • RAG 평가셋 30개 추가

    • tests/regression/rag_eval_cases.json
    • 연락처, 정책, 졸업, 학과 소개, 기숙사, 개인정보처리방침, 학사일정 케이스 포함
  • 검색/답변 재정렬 규칙 개선

    • 졸업보류, 졸업유예, 졸업연기는 학점 질문이 아니면 졸업학점 추출 로직을 타지 않도록 분리
    • title, leaf_title, breadcrumb의 핵심 토큰 매칭 점수를 최종 답변 후보 정렬에 반영
    • 현장실습 안내처럼 부서/센터가 아닌 학사안내 페이지가 우선되어야 하는 정책성 질문 보정
    • 전문학사 졸업학점 질의에서 전문학사 페이지가 우선되도록 졸업학점 검색 재정렬 보정
  • 검증 결과

    • 신규 RAG 평가셋 30개 통과
    • 기존 query index 호환성/메타데이터 품질 회귀 테스트 통과

변경 요약

RAG 검색 성능을 평가하기 위한 자동화된 평가 스크립트와 30개의 테스트 케이스를 추가했습니다. 동시에 졸업학점, 정책 검색 등에 대한 답변 재순위 로직을 개선하여 더 정확한 검색 결과를 제공합니다.

주요 변경점

  • 📊 평가 스크립트 추가 (evaluate_rag_retrieval.py): Top-1/Top-3 URL 정확도, 제목 매칭, 할루시네이션 감지 등 8가지 메트릭으로 RAG 성능 측정
  • 📋 평가 데이터셋 (rag_eval_cases.json): 연락처, 정책, 졸업, 학과, 기숙사, 개인정보, 학사일정 등 7개 카테고리의 30개 테스트 케이스
  • 🔍 졸업 관련 쿼리 처리 개선: "졸업보류", "졸업유예", "졸업연기" 같은 특정 쿼리를 따로 처리하여 불필요한 학점 추출 로직 방지
  • 답변 재순위 로직 확장: 제목, 하위 제목, 브레드크럼 정보를 활용한 추가 매칭 스코어로 답변 순위 개선
  • 📚 학과 정보 우선순위 조정: 정책 관련 질문에 학술 지도 페이지를 학과/센터 페이지보다 우선 배치
  • 🎓 전문학사 졸업학점 최적화: "전문학사 졸업학점" 쿼리에 특화된 페이지를 선호하도록 부스트

주의/리스크

  • 🔧 재순위 로직이 복잡해지면서 예상치 못한 쿼리에서 순위 변동 가능성 — 모니터링 필요
  • 💾 30개 테스트 케이스는 기본값이므로 실제 사용자 쿼리로 추가 검증 권장

다음 액션

  • ✅ 프로덕션 배포 후 평가 스크립트를 CI/CD 파이프라인에 통합하여 지속적 모니터링
  • 📊 실제 사용자 쿼리로 더 많은 케이스를 수집하여 테스트 커버리지 확대

@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@Yu-JeSeung has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 24 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 984ebda7-6f8a-44c9-8cb4-bdae61f6d4e8

📥 Commits

Reviewing files that changed from the base of the PR and between d8eff06 and 93a1d7c.

📒 Files selected for processing (2)
  • .github/workflows/rag-light-check.yml
  • scripts/check_rag_eval.sh
📝 Walkthrough

워크스루

이 PR은 졸업학점 관련 질의 감지를 위한 키워드/정규표현식을 확대하고, 답변 생성 시 재순위 신호를 다중화한 후, RAG 검색 및 답변 동작을 검증하는 회귀 테스트 인프라를 신규 도입합니다.


변경사항

질의 분류 및 재순위 강화

계층 / 파일 요약
졸업/학점 키워드 확대
LLM/sub_model/query_index.py
졸업학점 관련 검색 키워드 집합을 확대하고, 졸업학점 요건 및 비학점 정책 문구 감지를 위한 컴파일된 정규표현식 추가.
질의 분류 로직 개선
LLM/sub_model/query_index.py
_looks_like_grad_query 함수를 재작성: 비학점 정책 패턴 차단 로직, _compact() 정규화, 확대된 키워드 집합 활용.
질의 용어 추출 및 점수 계산
LLM/sub_model/query_index.py
_query_match_terms 함수로 한글 용어 토큰화 및 불용어 필터링 후 추출, _text_match_score 함수로 용어 겹침 개수 계산.
졸업학점 답변 재순위
LLM/sub_model/query_index.py
리프 제목 및 경로 필드 안전 추출, 제목/리프 제목/경로 간 용어 일치 점수, 전문학사/학사 기반 전문성 부스트, _rr 재순위 공식 확장.
일반 검색 답변 재순위
LLM/sub_model/query_index.py
제목 부분 문자열 일치, 제목/리프/경로 간 용어 점수 특징, 의도 기반 조건부 학사 정책 매칭 부스트 추가.

RAG 회귀 테스트 인프라

계층 / 파일 요약
테스트 스크립트 설정
tests/regression/evaluate_rag_retrieval.py
스크립트 진입점, 저장소 루트를 sys.path에 추가하여 RAG 모듈 임포트 가능하게 구성.
정규표현식 및 보조 함수
tests/regression/evaluate_rag_retrieval.py
날짜/URL 감지 정규표현식, JSON 케이스 로드, 부분 문자열 포함 여부 검사, 검색 결과 URL 추출/검사 헬퍼 함수.
URL 및 제목 일치 검증
tests/regression/evaluate_rag_retrieval.py
검색 결과 내 예상 URL 조각의 순위 계산, 상위 결과 제목과 예상 제목 조각 일치 여부 확인.
답변 환각 감지
tests/regression/evaluate_rag_retrieval.py
생성 답변에서 URL 추출, 검색/예상 URL 제외 후 미식별 URL에 unknown_url:<url> 플래그 부여.
검색 케이스 평가
tests/regression/evaluate_rag_retrieval.py
evaluate_search_case(): 검색·답변 생성, URL 순위/제목/키워드/소스 URL/날짜 검증, 환각 감지, 통과 여부 판정.
일정 케이스 평가
tests/regression/evaluate_rag_retrieval.py
evaluate_schedule_case(): schedule_search 호출, 답변/날짜 제약 계산, 검색 필드 기본값 설정, 통과 판정.
보고서 생성 및 CLI
tests/regression/evaluate_rag_retrieval.py
보호된 백분율 계산, 전체/카테고리별 통과율·정확도·지연시간 통계 계산, 인자 파싱/케이스 평가/JSON 저장/요약 출력/종료 코드 처리.
회귀 테스트 케이스
tests/regression/rag_eval_cases.json
contact, policy, grad, department, dorm, privacy, schedule 카테고리별 테스트 케이스: 예상 URL/제목, 필수 답변 키워드, 소스 URL/날짜 검증 플래그.

추정 코드 검토 노력

🎯 3 (보통) | ⏱️ ~25분


관련 PR

  • dongsooop/AI#100: 졸업학점 감지, 의도 기반 재순위 신호, 신규 메타데이터/텍스트 폴백 열 사용에 대한 중복 변경사항으로 인해 관련됨.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변화인 RAG 검색 평가 자동화 및 평가셋 추가를 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 리포지토리 템플릿을 충실히 따르며, 관련 이슈, 배경, 주요 내용을 모두 포함하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/chatbot_assessment_automation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
LLM/sub_model/query_index.py (1)

200-210: 💤 Low value

ql 변수가 한국어 키워드에 대해 사실상 무의미한 중복 비교를 수행합니다

GRAD_KWS의 키워드는 모두 한국어 문자로 구성되어 있어 .lower() 적용 결과가 원문과 동일합니다. 따라서 ql = text.lower()any(k in text or k in ql ...) 내부의 or k in ql 조건은 항상 k in text와 동일한 결과를 반환합니다.

♻️ 정리 제안
 def _looks_like_grad_query(q: str) -> bool:
     text = q or ""
     if GRAD_NON_CREDIT_POLICY_RE.search(text) and not re.search(r"(학점|이수)", text):
         return False
-    ql = text.lower()
     compact = _compact(text)
     return (
         bool(GRAD_CREDIT_QUERY_RE.search(text))
         or ("졸업" in compact and "학점" in compact)
-        or any(k in text or k in ql for k in GRAD_KWS)
+        or any(k in text for k in GRAD_KWS)
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@LLM/sub_model/query_index.py` around lines 200 - 210, In
_looks_like_grad_query the lowercase variable ql and the "or k in ql" check are
redundant because GRAD_KWS contains only Korean tokens; remove ql = text.lower()
and simplify the final any(...) clause to any(k in text for k in GRAD_KWS),
leaving the rest of the logic unchanged (keep GRAD_NON_CREDIT_POLICY_RE,
GRAD_CREDIT_QUERY_RE and compact checks intact) to eliminate the unnecessary
duplicate membership test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/regression/evaluate_rag_retrieval.py`:
- Line 228: The default output path is hardcoded to "/tmp/rag_eval_report.json"
which breaks portability and can expose files; change the
ap.add_argument("--out", ...) default to a cross-platform temp location by
importing tempfile and pathlib and using something like
Path(tempfile.gettempdir()) / "rag_eval_report.json" (or accept None and create
a secure temporary file with tempfile.NamedTemporaryFile when needed). Update
the ap.add_argument call (the "--out" argument) to use that computed path
instead of the literal "/tmp/..." and ensure imports for tempfile and
pathlib.Path are added.

In `@tests/regression/rag_eval_cases.json`:
- Around line 203-220: Two test entries have inconsistent URL IDs: for
"department_business_intro" expected_url_contains lists "4670/subview.do" but
answer_must_contain_any contains "4658/subview.do", and for
"department_visual_design_intro" expected_url_contains lists "4647/subview.do"
but answer_must_contain_any contains "4649/subview.do"; fix by making the URL
IDs consistent — either add the alternate IDs (4658 and/or 4649) into the
respective expected_url_contains arrays if they are valid alternative source
URLs, or remove the 4658/4649 entries from answer_must_contain_any if they were
erroneous, ensuring the JSON keys expected_url_contains and
answer_must_contain_any for the two IDs ("department_business_intro" and
"department_visual_design_intro") match.

---

Nitpick comments:
In `@LLM/sub_model/query_index.py`:
- Around line 200-210: In _looks_like_grad_query the lowercase variable ql and
the "or k in ql" check are redundant because GRAD_KWS contains only Korean
tokens; remove ql = text.lower() and simplify the final any(...) clause to any(k
in text for k in GRAD_KWS), leaving the rest of the logic unchanged (keep
GRAD_NON_CREDIT_POLICY_RE, GRAD_CREDIT_QUERY_RE and compact checks intact) to
eliminate the unnecessary duplicate membership test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: efd3c49a-163c-49f6-b404-9afa5a5b67cc

📥 Commits

Reviewing files that changed from the base of the PR and between 8e9aeb3 and d8eff06.

📒 Files selected for processing (3)
  • LLM/sub_model/query_index.py
  • tests/regression/evaluate_rag_retrieval.py
  • tests/regression/rag_eval_cases.json

ap.add_argument("--top-k", type=int, default=5, help="hybrid_search top_k for retrieval metrics")
ap.add_argument("--answer-top-k", type=int, default=6, help="build_answer top_k for answer metrics")
ap.add_argument("--schedule-top-k", type=int, default=5, help="schedule_search top_k for schedule cases")
ap.add_argument("--out", default="/tmp/rag_eval_report.json", help="output report path")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

/tmp 하드코딩 경로는 플랫폼 호환성 및 보안 우려가 있습니다

Ruff S108이 지적하는 것처럼, /tmp는 Windows 환경에서는 기본적으로 존재하지 않아 portability 문제가 발생합니다. 또한 공유 CI 환경에서 /tmp/rag_eval_report.json 파일은 다른 사용자에게 노출될 수 있습니다.

🛡️ 수정 제안
+import tempfile
 ...
 ap.add_argument(
     "--out",
-    default="/tmp/rag_eval_report.json",
+    default=str(Path(tempfile.gettempdir()) / "rag_eval_report.json"),
     help="output report path",
 )
🧰 Tools
🪛 Ruff (0.15.12)

[error] 228-228: Probable insecure usage of temporary file or directory: "/tmp/rag_eval_report.json"

(S108)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/regression/evaluate_rag_retrieval.py` at line 228, The default output
path is hardcoded to "/tmp/rag_eval_report.json" which breaks portability and
can expose files; change the ap.add_argument("--out", ...) default to a
cross-platform temp location by importing tempfile and pathlib and using
something like Path(tempfile.gettempdir()) / "rag_eval_report.json" (or accept
None and create a secure temporary file with tempfile.NamedTemporaryFile when
needed). Update the ap.add_argument call (the "--out" argument) to use that
computed path instead of the literal "/tmp/..." and ensure imports for tempfile
and pathlib.Path are added.

Comment on lines +203 to +220
"id": "department_business_intro",
"category": "department",
"kind": "search",
"query": "경영학과 소개",
"expected_url_contains": ["4670/subview.do"],
"expected_title_contains": ["경영학과"],
"answer_must_contain_any": ["경영학과", "4658/subview.do"],
"requires_source_url": true
},
{
"id": "department_visual_design_intro",
"category": "department",
"kind": "search",
"query": "시각디자인과 소개",
"expected_url_contains": ["4647/subview.do"],
"expected_title_contains": ["시각디자인과"],
"answer_must_contain_any": ["시각디자인과", "4649/subview.do"],
"requires_source_url": true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's check the test file structure and see the actual content around lines 203-220
cat -n tests/regression/rag_eval_cases.json | sed -n '200,225p'

Repository: dongsooop/AI

Length of output: 979


🏁 Script executed:

# Also, let's look at the full context of all department test cases
grep -n "department_" tests/regression/rag_eval_cases.json | head -20

Repository: dongsooop/AI

Length of output: 295


🏁 Script executed:

# Now let's find and examine the evaluation logic that handles these fields
find . -type f -name "*.py" -o -name "*.js" -o -name "*.ts" | head -20

Repository: dongsooop/AI

Length of output: 645


🏁 Script executed:

# Search for the evaluate_search_case function mentioned in the review
rg "evaluate_search_case|source_url_match|answer_keyword_match" --type py -A 5 -B 2

Repository: dongsooop/AI

Length of output: 7025


🏁 Script executed:

# Get more context around the evaluate_search_case function
sed -n '1,100p' tests/regression/evaluate_rag_retrieval.py

Repository: dongsooop/AI

Length of output: 3489


🏁 Script executed:

# Look at the full evaluate_search_case function
sed -n '20,80p' tests/regression/evaluate_rag_retrieval.py

Repository: dongsooop/AI

Length of output: 2307


🏁 Script executed:

# Get line count to see the file size
wc -l tests/regression/evaluate_rag_retrieval.py

Repository: dongsooop/AI

Length of output: 103


🏁 Script executed:

# Get the rest of the evaluate_search_case function
sed -n '60,120p' tests/regression/evaluate_rag_retrieval.py

Repository: dongsooop/AI

Length of output: 2751


🏁 Script executed:

# Let's also check the other department test cases to understand the pattern
sed -n '183,230p' tests/regression/rag_eval_cases.json

Repository: dongsooop/AI

Length of output: 1674


🏁 Script executed:

# Verify the logic one more time by checking the full evaluate_search_case function
sed -n '85,105p' tests/regression/evaluate_rag_retrieval.py

Repository: dongsooop/AI

Length of output: 1179


expected_url_containsanswer_must_contain_any 간 URL ID 불일치 – 데이터 정합성 문제

두 케이스에서 URL ID가 불일치합니다:

  • department_business_intro: expected_url_contains 4670/subview.do vs answer_must_contain_any 4658/subview.do
  • department_visual_design_intro: expected_url_contains 4647/subview.do vs answer_must_contain_any 4649/subview.do

평가 로직에서 source_url_matchexpected_url_contains를 기준으로 검증되지만, answer_must_contain_any의 URL(4658, 4649)은 실제로 검증되지 않습니다. contains_any()의 OR 로직상 한글 키워드("경영학과", "시각디자인과")가 먼저 일치하기 때문입니다. 이는 해당 URL들이 검증 대상인 것처럼 보이나, 실제로는 검증되지 않는 "죽은" 데이터입니다.

일관성을 위해 다음 중 하나를 고려하세요:

  1. 4658/4649가 유효한 대체 URL이면 expected_url_contains에 추가
  2. 단순 오류면 answer_must_contain_any에서 제거

다른 department 케이스들(department_computer_engineering_intro, department_software_intro)은 URL이 일관되게 설정되어 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/regression/rag_eval_cases.json` around lines 203 - 220, Two test
entries have inconsistent URL IDs: for "department_business_intro"
expected_url_contains lists "4670/subview.do" but answer_must_contain_any
contains "4658/subview.do", and for "department_visual_design_intro"
expected_url_contains lists "4647/subview.do" but answer_must_contain_any
contains "4649/subview.do"; fix by making the URL IDs consistent — either add
the alternate IDs (4658 and/or 4649) into the respective expected_url_contains
arrays if they are valid alternative source URLs, or remove the 4658/4649
entries from answer_must_contain_any if they were erroneous, ensuring the JSON
keys expected_url_contains and answer_must_contain_any for the two IDs
("department_business_intro" and "department_visual_design_intro") match.

@Yu-JeSeung Yu-JeSeung added the run-rag-check check rag eval label May 8, 2026
@Yu-JeSeung
Yu-JeSeung merged commit cd17dee into main May 11, 2026
3 checks passed
@Yu-JeSeung
Yu-JeSeung deleted the feat/chatbot_assessment_automation branch May 11, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant