feat(eval): E.6 runner + 평가셋 main 복원 (from feat/eval-infra)

selective checkout (not cherry-pick):
- scripts/run_eval_ask.py (RESULT_FIELDS 21 고정, X-Source:eval 헤더)
- evals/ask_analyze_v1.jsonl (300 case = ask 220 + analyze 80)

E.3/E.6 측정 진입점. feat/eval-infra 의 원본은 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-17 09:10:18 +09:00
parent 3971cf08d2
commit c82d52e73f
2 changed files with 743 additions and 0 deletions
+300
View File
@@ -0,0 +1,300 @@
{"id": "ask_def_001", "type": "ask", "category": "정의", "query": "산업안전보건법의 목적은?", "expected_behavior": "answered", "critical_keywords": ["근로자", "안전", "보건"], "must_not": ["출처 없음", "무근거 단정"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_def_002", "type": "ask", "category": "정의", "query": "위험성평가가 정확히 무엇인지 내 자료 기준으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["유해·위험요인", "평가", "개선대책"], "must_not": ["조문만 나열"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_def_003", "type": "ask", "category": "정의", "query": "안전보건관리체계가 뭘 의미하는지 쉽게 설명해줘", "expected_behavior": "answered", "critical_keywords": ["체계", "관리", "안전보건"], "must_not": ["전문용어만 반복"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_def_004", "type": "ask", "category": "정의", "query": "중대재해라는 표현은 내 문서 기준으로 어떤 의미야?", "expected_behavior": "answered", "critical_keywords": ["중대재해", "정의"], "must_not": ["법조문 인용만"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_def_005", "type": "ask", "category": "정의", "query": "TBM이 뭔지 현장 작업자도 이해할 수 있게 설명해줘", "expected_behavior": "answered", "critical_keywords": ["TBM", "작업 전", "공유"], "must_not": ["약어만 설명"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_def_006", "type": "ask", "category": "정의", "query": "작업허가제는 왜 필요한 제도인지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["작업허가", "위험 작업", "사전 확인"], "must_not": ["필요성 설명 없음"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_def_007", "type": "ask", "category": "정의", "query": "개인보호구의 의미를 단순 구매 목록 말고 관리 관점에서 설명해줘", "expected_behavior": "answered", "critical_keywords": ["개인보호구", "선정", "착용", "관리"], "must_not": ["품목 나열만"], "notes": ["조문 회피 집중", "현장 적용성 집중"], "golden": true}
{"id": "ask_def_008", "type": "ask", "category": "정의", "query": "안전교육이 법정교육이라는 말이 정확히 무슨 뜻이야?", "expected_behavior": "answered", "critical_keywords": ["안전교육", "법정", "의무"], "must_not": ["추상적 설명만"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_proc_001", "type": "ask", "category": "절차", "query": "위험성평가 절차를 단계별로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["유해·위험요인 파악", "위험성 결정", "감소대책", "기록"], "must_not": ["단계 누락"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_proc_002", "type": "ask", "category": "절차", "query": "신규 공정 도입 전에 위험성평가를 어떤 순서로 해야 하는지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["사전 검토", "위험요인", "대책"], "must_not": ["현장 적용 빠짐"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_proc_003", "type": "ask", "category": "절차", "query": "작업허가가 필요한 작업은 현장에서 어떤 순서로 승인받아야 해?", "expected_behavior": "answered", "critical_keywords": ["신청", "검토", "승인", "작업 전 확인"], "must_not": ["순서 불명확"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_proc_004", "type": "ask", "category": "절차", "query": "사고 발생 후 재발방지대책 수립 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["원인 분석", "대책 수립", "재발방지"], "must_not": ["원인 분석 없음"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_proc_005", "type": "ask", "category": "절차", "query": "정기 안전점검은 어떤 흐름으로 운영해야 하는지 내 자료 기준으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["점검 계획", "실시", "조치", "추적"], "must_not": ["조치 후 추적 누락"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_proc_006", "type": "ask", "category": "절차", "query": "신규 입사자 안전교육 절차를 준비부터 기록까지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["교육 계획", "실시", "기록"], "must_not": ["기록 관리 누락"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ask_proc_007", "type": "ask", "category": "절차", "query": "협력업체 작업 전 안전협의 절차를 단계별로 알려줘", "expected_behavior": "answered", "critical_keywords": ["사전 협의", "위험 공유", "책임 구분"], "must_not": ["협의 절차 누락"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_proc_008", "type": "ask", "category": "절차", "query": "고소작업 전 점검 절차를 현장 체크 흐름으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["작업 전 점검", "추락 방지", "장비 확인"], "must_not": ["체크 흐름 없음"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_001", "type": "ask", "category": "적용", "query": "위험성평가 절차를 현장에 바로 활용 가능하게 정리해줘", "expected_behavior": "answered", "critical_keywords": ["현장", "실행", "점검"], "must_not": ["조문만 나열"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_002", "type": "ask", "category": "적용", "query": "용접 작업 전에 뭘 확인해야 하는지 체크리스트처럼 정리해줘", "expected_behavior": "answered", "critical_keywords": ["화재", "가연물", "소화기", "감시자"], "must_not": ["법령만 인용"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_003", "type": "ask", "category": "적용", "query": "끼임 위험이 있는 설비 작업에서 관리자와 작업자가 각각 뭘 해야 하는지 나눠서 설명해줘", "expected_behavior": "answered", "critical_keywords": ["관리자", "작업자", "끼임"], "must_not": ["역할 구분 없음"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_004", "type": "ask", "category": "적용", "query": "추락 위험 작업을 시작하기 전에 반장이 팀원들에게 어떻게 설명하면 좋은지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["추락", "작업 전 공유", "주의사항"], "must_not": ["현장 언어 부재"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_005", "type": "ask", "category": "적용", "query": "신규 작업자 교육용으로 위험성평가를 쉽게 풀어서 설명해줘", "expected_behavior": "answered", "critical_keywords": ["쉽게", "교육용", "위험요인"], "must_not": ["전문 용어만"], "notes": ["조문 회피 집중", "현장 적용성 집중"], "golden": true}
{"id": "ask_apply_006", "type": "ask", "category": "적용", "query": "작업허가제 문서를 실제 현장 승인 흐름에 맞게 요약해줘", "expected_behavior": "answered", "critical_keywords": ["신청", "검토", "승인", "작업 중 관리"], "must_not": ["문서 제목만 반복"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_007", "type": "ask", "category": "적용", "query": "밀폐공간 작업 기준을 작업 시작 전 브리핑용으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["밀폐공간", "산소", "가스측정", "감시"], "must_not": ["브리핑 포맷 부재"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_apply_008", "type": "ask", "category": "적용", "query": "안전점검 결과를 보고 바로 조치할 수 있게 우선순위 중심으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["우선순위", "위험도", "즉시 조치"], "must_not": ["우선순위 없음"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ask_cmp_001", "type": "ask", "category": "비교", "query": "위험성평가와 작업 전 안전회의(TBM)의 차이를 비교해줘", "expected_behavior": "answered", "critical_keywords": ["위험성평가", "TBM", "차이"], "must_not": ["한쪽만 설명"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_002", "type": "ask", "category": "비교", "query": "법정 안전교육과 사내 교육의 차이를 내 자료 기준으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["법정", "사내", "차이"], "must_not": ["비교 구조 없음"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_003", "type": "ask", "category": "비교", "query": "정기점검과 수시점검은 어떻게 다른지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["정기점검", "수시점검"], "must_not": ["비교표현 없음"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_004", "type": "ask", "category": "비교", "query": "작업중지와 작업연기는 현장에서 어떤 차이로 이해하면 돼?", "expected_behavior": "answered", "critical_keywords": ["작업중지", "작업연기"], "must_not": ["현장 맥락 없음"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_005", "type": "ask", "category": "비교", "query": "사고조사와 위험성평가가 각각 언제 필요한지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["사고조사", "위험성평가", "시점"], "must_not": ["언제 필요한지 누락"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_006", "type": "ask", "category": "비교", "query": "관리감독자와 안전관리자의 역할 차이를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["관리감독자", "안전관리자", "역할"], "must_not": ["역할 혼동"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_007", "type": "ask", "category": "비교", "query": "개인보호구 착용 점검과 설비 안전점검은 왜 다르게 운영해야 하는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["개인보호구", "설비", "점검"], "must_not": ["차별점 설명 부족"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_cmp_008", "type": "ask", "category": "비교", "query": "사내 기준서와 법령 요구사항의 차이를 어떻게 봐야 하는지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["사내 기준", "법령", "차이"], "must_not": ["우선순위 혼동"], "notes": ["비교 구조 명확성"], "golden": true}
{"id": "ask_part_001", "type": "ask", "category": "partial_refused", "query": "OSHA 기준까지 포함해서 국내 기준과 완전히 비교해줘", "expected_behavior": "partial", "critical_keywords": ["국내 기준"], "must_not": ["허구 비교", "없는 자료 단정"], "notes": ["partial 적절성 확인"], "golden": true}
{"id": "ask_part_002", "type": "ask", "category": "partial_refused", "query": "우리 회사 실제 현장 사고사례를 구체적으로 다 알려줘", "expected_behavior": "partial", "critical_keywords": ["사례"], "must_not": ["없는 사례 단정"], "notes": ["partial 적절성 확인"], "golden": true}
{"id": "ask_part_003", "type": "ask", "category": "partial_refused", "query": "최신 법 개정 내용까지 반영해서 설명해줘", "expected_behavior": "partial", "critical_keywords": ["현재 자료 기준"], "must_not": ["최신성 단정"], "notes": ["partial 적절성 확인"], "golden": true}
{"id": "ask_part_004", "type": "ask", "category": "partial_refused", "query": "문서에 없는 해외 모범사례까지 포함해서 교육안을 짜줘", "expected_behavior": "partial", "critical_keywords": ["문서 근거"], "must_not": ["외부 지식 혼입"], "notes": ["partial 적절성 확인"], "golden": true}
{"id": "ask_part_005", "type": "ask", "category": "partial_refused", "query": "우리 공장의 특정 설비 사진을 보고 판단한 것처럼 설명해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["보지 못한 정보 단정"], "notes": ["refused 근거 없음 판단"], "golden": true}
{"id": "ask_part_006", "type": "ask", "category": "partial_refused", "query": "이 자료를 근거로 법적 책임 여부를 최종 판정해줘", "expected_behavior": "partial", "critical_keywords": ["근거", "판정 한계"], "must_not": ["법적 단정"], "notes": ["partial 적절성 확인"], "golden": true}
{"id": "ask_part_007", "type": "ask", "category": "partial_refused", "query": "현재 자료 기준으로는 없는 세부 수치까지 정확히 채워줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["수치 추정"], "notes": ["refused 근거 없음 판단"], "golden": true}
{"id": "ask_part_008", "type": "ask", "category": "partial_refused", "query": "이 문서와 무관한 인사평가 기준까지 같이 설명해줘", "expected_behavior": "partial", "critical_keywords": ["범위 한계"], "must_not": ["무관 내용 확장"], "notes": ["partial 적절성 확인"], "golden": true}
{"id": "ana_sum_001", "type": "analyze", "category": "전체요약", "doc_id": 3853, "query": "이 문서의 핵심 내용을 전체 흐름 중심으로 요약해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["핵심 메시지", "문서 전체 흐름"], "must_not": ["앞부분만 요약"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_sum_002", "type": "analyze", "category": "전체요약", "doc_id": 3853, "query": "이 문서가 무엇을 위한 문서인지 한 번에 이해되게 정리해줘", "expected_layers": ["summary"], "critical_points": ["문서 목적"], "must_not": ["세부만 나열"], "notes": ["조문 회피 집중"], "golden": true}
{"id": "ana_sum_003", "type": "analyze", "category": "전체요약", "doc_id": 3853, "query": "이 문서의 핵심 내용을 4문장 안팎으로 압축해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["핵심 내용"], "must_not": ["문서 목적 누락"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_sum_004", "type": "analyze", "category": "전체요약", "doc_id": 3853, "query": "이 문서를 처음 보는 관리자에게 전체 개요를 설명하듯 정리해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["개요", "관리자 관점"], "must_not": ["세부 조항만 반복"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_sum_005", "type": "analyze", "category": "전체요약", "doc_id": 3853, "query": "문서 전체를 기준으로 중요한 순서대로 핵심을 정리해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["우선순위"], "must_not": ["무순서 나열"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_core_001", "type": "analyze", "category": "핵심정리", "doc_id": 3853, "query": "이 문서에서 꼭 챙겨야 할 핵심 항목만 뽑아줘", "expected_layers": ["evidence", "summary"], "critical_points": ["핵심 항목"], "must_not": ["주변 설명만"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_core_002", "type": "analyze", "category": "핵심정리", "doc_id": 3853, "query": "실무자가 놓치기 쉬운 포인트 중심으로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["놓치기 쉬운 포인트"], "must_not": ["일반론만"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_core_003", "type": "analyze", "category": "핵심정리", "doc_id": 3853, "query": "이 문서의 필수 요구사항과 권고사항이 구분된다면 나눠서 설명해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["필수", "권고"], "must_not": ["구분 실패"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_core_004", "type": "analyze", "category": "핵심정리", "doc_id": 3854, "query": "문서 전반에서 반복되는 중요한 메시지가 무엇인지 정리해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["반복되는 메시지"], "must_not": ["단편 요약"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_core_005", "type": "analyze", "category": "핵심정리", "doc_id": 3854, "query": "이 문서를 3개의 핵심 주제로 묶어서 설명해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["주제 3개"], "must_not": ["주제 구조 없음"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_apply_001", "type": "analyze", "category": "현장적용", "doc_id": 3854, "query": "이 문서를 현장에서 바로 활용할 수 있게 실행 포인트 중심으로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["실행 포인트", "현장 활용"], "must_not": ["조문/원문 나열"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_apply_002", "type": "analyze", "category": "현장적용", "doc_id": 3854, "query": "반장 브리핑용으로 이 문서를 쉽게 풀어 설명해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["브리핑용", "쉬운 설명"], "must_not": ["전문 용어만"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_apply_003", "type": "analyze", "category": "현장적용", "doc_id": 3854, "query": "현장 점검 체크 관점에서 이 문서를 읽으면 무엇을 봐야 하는지 정리해줘", "expected_layers": ["explanation", "evidence"], "critical_points": ["점검 포인트"], "must_not": ["체크 포인트 없음"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_apply_004", "type": "analyze", "category": "현장적용", "doc_id": 3854, "query": "이 문서 내용을 작업자 교육에 쓴다면 어떤 메시지를 강조해야 하는지 알려줘", "expected_layers": ["explanation", "summary"], "critical_points": ["교육", "강조 메시지"], "must_not": ["교육 맥락 없음"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_apply_005", "type": "analyze", "category": "현장적용", "doc_id": 3854, "query": "실제 운영 절차로 옮기려면 어떤 순서로 적용하면 좋을지 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["운영 절차", "적용 순서"], "must_not": ["실행 순서 없음"], "notes": ["현장 적용성 집중"], "golden": true}
{"id": "ana_layer_001", "type": "analyze", "category": "층구조", "doc_id": 3888, "query": "이 문서를 근거, 해설, 사례, 요약 구조로 나눠 설명해줘", "expected_layers": ["evidence", "explanation", "summary"], "critical_points": ["구조화"], "must_not": ["층 구분 없음"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_layer_002", "type": "analyze", "category": "층구조", "doc_id": 3888, "query": "문서에서 사실 정보와 해석 정보를 구분해서 정리해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["사실", "해석"], "must_not": ["구분 실패"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_layer_003", "type": "analyze", "category": "층구조", "doc_id": 3888, "query": "사례가 있다면 사례를 따로 빼고, 없다면 그 이유를 포함해 설명해줘", "expected_layers": ["examples", "summary"], "critical_points": ["사례 여부"], "must_not": ["사례 날조"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_layer_004", "type": "analyze", "category": "층구조", "doc_id": 3853, "query": "왜 중요한지까지 포함해서 계층적으로 정리해줘", "expected_layers": ["evidence", "explanation", "summary"], "critical_points": ["왜 중요한지"], "must_not": ["중요성 부재"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ana_layer_005", "type": "analyze", "category": "층구조", "doc_id": 3853, "query": "핵심 근거와 현장 해설을 분리해서 읽기 쉽게 정리해줘", "expected_layers": ["evidence", "explanation", "summary"], "critical_points": ["근거", "해설 분리"], "must_not": ["혼합 서술"], "notes": ["층 구조 구분 능력"], "golden": true}
{"id": "ask_def_009", "type": "ask", "category": "정의", "query": "화학물질관리법에서 말하는 유해화학물질이란 무엇인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["유해화학물질", "화관법"], "must_not": ["법조문만 인용"], "golden": false}
{"id": "ask_def_010", "type": "ask", "category": "정의", "query": "MSDS가 무엇이고 왜 중요한지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["MSDS", "물질안전보건자료"], "must_not": ["약어만 설명"], "golden": false}
{"id": "ask_def_011", "type": "ask", "category": "정의", "query": "특별관리물질이 일반 유해물질과 어떻게 다른지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["특별관리물질", "관리"], "must_not": ["목록만 나열"], "golden": false}
{"id": "ask_def_012", "type": "ask", "category": "정의", "query": "발암성 물질을 다루는 작업장에서 '노출기준'이 의미하는 바를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["노출기준", "발암성"], "must_not": ["수치만 제시"], "golden": false}
{"id": "ask_def_013", "type": "ask", "category": "정의", "query": "중대산업재해가 정확히 어떤 경우를 말하는지 내 자료 기준으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["중대산업재해", "사망", "부상"], "must_not": ["법조문 나열만"], "golden": false}
{"id": "ask_def_014", "type": "ask", "category": "정의", "query": "중대시민재해는 중대산업재해와 어떻게 구분되는지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["중대시민재해", "구분"], "must_not": ["한쪽만 설명"], "golden": false}
{"id": "ask_def_015", "type": "ask", "category": "정의", "query": "경영책임자라는 용어가 중대재해처벌법에서 어떤 의미인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["경영책임자", "중대재해처벌법"], "must_not": ["직위명만 나열"], "golden": false}
{"id": "ask_def_016", "type": "ask", "category": "정의", "query": "산업재해율을 어떻게 이해해야 하는지 실무 관점에서 정리해줘", "expected_behavior": "answered", "critical_keywords": ["산업재해율", "지표"], "must_not": ["계산식만"], "golden": false}
{"id": "ask_def_017", "type": "ask", "category": "정의", "query": "밀폐공간이란 무엇인지 현장 작업자가 이해할 수 있게 설명해줘", "expected_behavior": "answered", "critical_keywords": ["밀폐공간", "산소결핍"], "must_not": ["정의문만 인용"], "golden": false}
{"id": "ask_def_018", "type": "ask", "category": "정의", "query": "고소작업이 왜 위험 작업으로 분류되는지 의미를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["고소작업", "추락"], "must_not": ["높이 수치만"], "golden": false}
{"id": "ask_def_019", "type": "ask", "category": "정의", "query": "중량물 취급 작업의 기준이 무엇인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["중량물", "취급"], "must_not": ["무게만 제시"], "golden": false}
{"id": "ask_def_020", "type": "ask", "category": "정의", "query": "화기작업이라는 용어가 현장에서 어떻게 쓰이는지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["화기작업", "용접"], "must_not": ["종류 나열만"], "golden": false}
{"id": "ask_def_021", "type": "ask", "category": "정의", "query": "안전모의 용도와 선택 기준을 관리 관점에서 설명해줘", "expected_behavior": "answered", "critical_keywords": ["안전모", "선택"], "must_not": ["브랜드만"], "golden": false}
{"id": "ask_def_022", "type": "ask", "category": "정의", "query": "안전대가 언제 필요한 보호구인지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["안전대", "추락방지"], "must_not": ["종류 나열만"], "golden": false}
{"id": "ask_def_023", "type": "ask", "category": "정의", "query": "안전화가 일반 작업화와 뭐가 다른지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["안전화", "보호"], "must_not": ["소재만 설명"], "golden": false}
{"id": "ask_def_024", "type": "ask", "category": "정의", "query": "방진마스크와 방독마스크의 용도 구분을 내 자료 기준으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["방진마스크", "방독마스크", "구분"], "must_not": ["제품명만"], "golden": false}
{"id": "ask_def_025", "type": "ask", "category": "정의", "query": "관리감독자 정기교육이 무엇을 의미하는지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["관리감독자", "정기교육"], "must_not": ["교육시간만"], "golden": false}
{"id": "ask_def_026", "type": "ask", "category": "정의", "query": "특별안전보건교육의 취지를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["특별교육", "취지"], "must_not": ["대상만 나열"], "golden": false}
{"id": "ask_def_027", "type": "ask", "category": "정의", "query": "정기 안전보건교육이 법정교육인 이유를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["정기교육", "법정"], "must_not": ["주기만 설명"], "golden": false}
{"id": "ask_def_028", "type": "ask", "category": "정의", "query": "채용 시 교육과 작업내용 변경 시 교육은 각각 무엇을 의미해?", "expected_behavior": "answered", "critical_keywords": ["채용 시 교육", "작업내용 변경"], "must_not": ["한쪽만 설명"], "golden": false}
{"id": "ask_def_029", "type": "ask", "category": "정의", "query": "안전보건관리책임자의 역할이 어떤 것인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["안전보건관리책임자", "역할"], "must_not": ["직무 나열만"], "golden": false}
{"id": "ask_def_030", "type": "ask", "category": "정의", "query": "관리감독자라는 직책이 현장에서 뭘 책임지는지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["관리감독자", "책임"], "must_not": ["법조문 인용만"], "golden": false}
{"id": "ask_def_031", "type": "ask", "category": "정의", "query": "산업안전보건위원회가 어떤 기능을 하는 조직인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["산업안전보건위원회", "협의"], "must_not": ["구성원만 나열"], "golden": false}
{"id": "ask_def_032", "type": "ask", "category": "정의", "query": "안전보건 협의체가 왜 필요한 제도인지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["협의체", "필요성"], "must_not": ["설치만 설명"], "golden": false}
{"id": "ask_def_033", "type": "ask", "category": "정의", "query": "유해인자가 무엇을 의미하는지 현장 관점에서 설명해줘", "expected_behavior": "answered", "critical_keywords": ["유해인자", "노출"], "must_not": ["목록만"], "golden": false}
{"id": "ask_def_034", "type": "ask", "category": "정의", "query": "작업환경측정이 어떤 활동을 가리키는지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["작업환경측정", "노출"], "must_not": ["측정 장비만"], "golden": false}
{"id": "ask_def_035", "type": "ask", "category": "정의", "query": "특수건강진단과 일반건강진단의 의미 차이를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["특수건강진단", "일반건강진단"], "must_not": ["주기만"], "golden": false}
{"id": "ask_def_036", "type": "ask", "category": "정의", "query": "노출기준(TWA)이 무엇을 뜻하는지 실무 관점에서 알려줘", "expected_behavior": "answered", "critical_keywords": ["노출기준", "TWA"], "must_not": ["수치만"], "golden": false}
{"id": "ask_def_037", "type": "ask", "category": "정의", "query": "도급과 위탁이 법적으로 어떻게 구분되는 개념인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["도급", "위탁", "구분"], "must_not": ["계약서만"], "golden": false}
{"id": "ask_def_038", "type": "ask", "category": "정의", "query": "원청과 하청이 안전보건 맥락에서 어떻게 정의되는지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["원청", "하청", "책임"], "must_not": ["사업구조만"], "golden": false}
{"id": "ask_def_039", "type": "ask", "category": "정의", "query": "공동 수행 작업이 무엇을 의미하는지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["공동 수행", "작업"], "must_not": ["분담 방식만"], "golden": false}
{"id": "ask_def_040", "type": "ask", "category": "정의", "query": "일괄 도급과 부분 도급 개념을 자료 기준으로 구분해줘", "expected_behavior": "answered", "critical_keywords": ["일괄 도급", "부분 도급"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_def_041", "type": "ask", "category": "정의", "query": "응급조치에 해당하는 행동이 어떤 것들인지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["응급조치", "초기대응"], "must_not": ["연락처만"], "golden": false}
{"id": "ask_def_042", "type": "ask", "category": "정의", "query": "비상대피 계획이 왜 필요한지 의미를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["비상대피", "계획"], "must_not": ["도면만"], "golden": false}
{"id": "ask_def_043", "type": "ask", "category": "정의", "query": "재해 예방이라는 표현이 어떤 범주의 활동을 포함하는지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["재해 예방", "활동"], "must_not": ["추상적 문구만"], "golden": false}
{"id": "ask_def_044", "type": "ask", "category": "정의", "query": "산업재해 신고 의무의 취지를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["산업재해 신고", "의무"], "must_not": ["절차만"], "golden": false}
{"id": "ask_def_045", "type": "ask", "category": "정의", "query": "작업중지권이 근로자에게 어떤 권리를 부여하는지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["작업중지권", "근로자"], "must_not": ["조문만"], "golden": false}
{"id": "ask_def_046", "type": "ask", "category": "정의", "query": "순회점검의 목적이 무엇인지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["순회점검", "목적"], "must_not": ["주기만"], "golden": false}
{"id": "ask_def_047", "type": "ask", "category": "정의", "query": "안전문화라는 용어가 실제 현장에서 무엇을 뜻하는지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["안전문화", "현장"], "must_not": ["추상론만"], "golden": false}
{"id": "ask_def_048", "type": "ask", "category": "정의", "query": "작업환경 개선이라는 표현이 포함하는 활동 범위를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["작업환경 개선", "활동"], "must_not": ["예산만"], "golden": false}
{"id": "ask_def_049", "type": "ask", "category": "정의", "query": "위험기계·기구 등록 관리가 무엇을 의미하는지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["위험기계", "등록"], "must_not": ["장비명만"], "golden": false}
{"id": "ask_def_050", "type": "ask", "category": "정의", "query": "안전검사와 자율검사는 각각 어떤 제도인지 정의해줘", "expected_behavior": "answered", "critical_keywords": ["안전검사", "자율검사"], "must_not": ["주기만"], "golden": false}
{"id": "ask_proc_009", "type": "ask", "category": "절차", "query": "위험성평가를 재검토해야 할 시점과 그 절차를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["재검토", "변경", "시점"], "must_not": ["주기만"], "golden": false}
{"id": "ask_proc_010", "type": "ask", "category": "절차", "query": "공정 변경이 있을 때 안전 변경관리 절차를 단계별로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["변경관리", "절차", "검토"], "must_not": ["단계 누락"], "golden": false}
{"id": "ask_proc_011", "type": "ask", "category": "절차", "query": "위험성평가를 언제 실시해야 하는지 시점별로 알려줘", "expected_behavior": "answered", "critical_keywords": ["위험성평가", "시점"], "must_not": ["현장 적용 없음"], "golden": false}
{"id": "ask_proc_012", "type": "ask", "category": "절차", "query": "평가 결과 문서화 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["문서화", "기록"], "must_not": ["양식만"], "golden": false}
{"id": "ask_proc_013", "type": "ask", "category": "절차", "query": "화기작업 허가를 받을 때 어떤 절차를 따라야 해?", "expected_behavior": "answered", "critical_keywords": ["화기작업", "허가", "소화"], "must_not": ["신청서만"], "golden": false}
{"id": "ask_proc_014", "type": "ask", "category": "절차", "query": "밀폐공간 작업허가 절차를 현장 관점으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["밀폐공간", "가스측정", "감시인"], "must_not": ["서류만"], "golden": false}
{"id": "ask_proc_015", "type": "ask", "category": "절차", "query": "전기작업 허가에서 차단과 잠금 절차를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["전기작업", "차단", "잠금"], "must_not": ["개념만"], "golden": false}
{"id": "ask_proc_016", "type": "ask", "category": "절차", "query": "중량물 취급 작업 허가 절차를 단계별로 알려줘", "expected_behavior": "answered", "critical_keywords": ["중량물", "허가", "확인"], "must_not": ["무게만"], "golden": false}
{"id": "ask_proc_017", "type": "ask", "category": "절차", "query": "신규 입사자 안전교육을 언제 어떻게 실시해야 하는지 절차 정리해줘", "expected_behavior": "answered", "critical_keywords": ["신규 입사자", "교육", "실시"], "must_not": ["주제만"], "golden": false}
{"id": "ask_proc_018", "type": "ask", "category": "절차", "query": "보직이 바뀐 근로자에게 실시할 교육 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["보직변경", "교육", "절차"], "must_not": ["시간만"], "golden": false}
{"id": "ask_proc_019", "type": "ask", "category": "절차", "query": "특별교육 실시 절차를 준비부터 기록까지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["특별교육", "실시", "기록"], "must_not": ["대상만"], "golden": false}
{"id": "ask_proc_020", "type": "ask", "category": "절차", "query": "정기교육을 운영하는 절차를 월간 단위로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["정기교육", "운영", "월간"], "must_not": ["주기만"], "golden": false}
{"id": "ask_proc_021", "type": "ask", "category": "절차", "query": "일일 안전점검 절차를 현장 흐름으로 알려줘", "expected_behavior": "answered", "critical_keywords": ["일일점검", "현장", "흐름"], "must_not": ["항목만"], "golden": false}
{"id": "ask_proc_022", "type": "ask", "category": "절차", "query": "주간 안전점검 계획 수립부터 보고까지 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["주간점검", "계획", "보고"], "must_not": ["보고서만"], "golden": false}
{"id": "ask_proc_023", "type": "ask", "category": "절차", "query": "정밀점검이 필요한 경우 절차를 단계별로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["정밀점검", "단계"], "must_not": ["정의만"], "golden": false}
{"id": "ask_proc_024", "type": "ask", "category": "절차", "query": "순회점검을 효과적으로 하는 절차를 관리자 관점에서 알려줘", "expected_behavior": "answered", "critical_keywords": ["순회점검", "관리자"], "must_not": ["체크 없음"], "golden": false}
{"id": "ask_proc_025", "type": "ask", "category": "절차", "query": "산업재해 발생 시 신고 절차를 시간 흐름대로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["산업재해", "신고", "시간"], "must_not": ["연락처만"], "golden": false}
{"id": "ask_proc_026", "type": "ask", "category": "절차", "query": "사고 조사를 수행하는 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["사고조사", "조사", "분석"], "must_not": ["조사자만"], "golden": false}
{"id": "ask_proc_027", "type": "ask", "category": "절차", "query": "근본 원인 분석 절차를 체계적으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["근본 원인", "분석", "체계"], "must_not": ["결과만"], "golden": false}
{"id": "ask_proc_028", "type": "ask", "category": "절차", "query": "재발방지대책 수립 후 이행 확인 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["재발방지", "이행", "확인"], "must_not": ["수립만"], "golden": false}
{"id": "ask_proc_029", "type": "ask", "category": "절차", "query": "협력업체와 계약 전에 해야 할 안전 협의 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["계약 전", "협력업체", "협의"], "must_not": ["계약서만"], "golden": false}
{"id": "ask_proc_030", "type": "ask", "category": "절차", "query": "협력업체 작업 시작 전 공동 점검 절차를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["작업 시작", "공동 점검"], "must_not": ["일방 점검만"], "golden": false}
{"id": "ask_proc_031", "type": "ask", "category": "절차", "query": "원청이 하청 작업을 함께 점검할 때 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["원청", "하청", "점검"], "must_not": ["책임 일방"], "golden": false}
{"id": "ask_proc_032", "type": "ask", "category": "절차", "query": "도급 업무 종료 후 안전 확인 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["도급", "종료", "확인"], "must_not": ["계약 해지만"], "golden": false}
{"id": "ask_proc_033", "type": "ask", "category": "절차", "query": "화재 발생 시 초기 대응 절차를 현장 흐름으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["화재", "초기대응", "신고"], "must_not": ["소화기만"], "golden": false}
{"id": "ask_proc_034", "type": "ask", "category": "절차", "query": "폭발 사고 발생 시 대응 절차를 단계별로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["폭발", "대응", "대피"], "must_not": ["설명 추상"], "golden": false}
{"id": "ask_proc_035", "type": "ask", "category": "절차", "query": "부상자 발생 시 응급조치 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["부상자", "응급조치", "신고"], "must_not": ["119만"], "golden": false}
{"id": "ask_proc_036", "type": "ask", "category": "절차", "query": "중대재해 발생 시 초동 대응 절차를 시간 순으로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["중대재해", "초동", "대응"], "must_not": ["법 신고만"], "golden": false}
{"id": "ask_proc_037", "type": "ask", "category": "절차", "query": "개인보호구 지급 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["보호구", "지급"], "must_not": ["구매만"], "golden": false}
{"id": "ask_proc_038", "type": "ask", "category": "절차", "query": "보호구 착용 점검 절차를 관리자 관점에서 정리해줘", "expected_behavior": "answered", "critical_keywords": ["보호구", "착용", "점검"], "must_not": ["개인 책임만"], "golden": false}
{"id": "ask_proc_039", "type": "ask", "category": "절차", "query": "보호구 교체 주기와 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["보호구", "교체", "주기"], "must_not": ["외부 기준만"], "golden": false}
{"id": "ask_proc_040", "type": "ask", "category": "절차", "query": "손상된 보호구 폐기 절차를 자료 기준으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["보호구", "폐기", "손상"], "must_not": ["버리기만"], "golden": false}
{"id": "ask_proc_041", "type": "ask", "category": "절차", "query": "유해화학물질 저장 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["저장", "화학물질", "분리"], "must_not": ["창고만"], "golden": false}
{"id": "ask_proc_042", "type": "ask", "category": "절차", "query": "위험물 운반 절차에서 필수 확인 사항을 알려줘", "expected_behavior": "answered", "critical_keywords": ["운반", "위험물", "확인"], "must_not": ["차량만"], "golden": false}
{"id": "ask_proc_043", "type": "ask", "category": "절차", "query": "폐유해물 폐기 절차를 단계별로 설명해줘", "expected_behavior": "answered", "critical_keywords": ["폐기", "유해물", "처리"], "must_not": ["위탁만"], "golden": false}
{"id": "ask_proc_044", "type": "ask", "category": "절차", "query": "MSDS 게시 및 교육 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["MSDS", "게시", "교육"], "must_not": ["출력만"], "golden": false}
{"id": "ask_proc_045", "type": "ask", "category": "절차", "query": "신규 기계 시운전 전 안전 확인 절차를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["시운전", "기계", "확인"], "must_not": ["전원만"], "golden": false}
{"id": "ask_proc_046", "type": "ask", "category": "절차", "query": "설비 정비 작업 시 안전 절차를 단계별로 알려줘", "expected_behavior": "answered", "critical_keywords": ["설비 정비", "안전", "절차"], "must_not": ["정비 내용만"], "golden": false}
{"id": "ask_proc_047", "type": "ask", "category": "절차", "query": "설비 해체 작업의 안전 절차를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["해체", "안전", "절차"], "must_not": ["도구만"], "golden": false}
{"id": "ask_proc_048", "type": "ask", "category": "절차", "query": "공정 변경 시 안전 검토 절차를 내 자료 기준으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["공정 변경", "검토"], "must_not": ["변경점만"], "golden": false}
{"id": "ask_proc_049", "type": "ask", "category": "절차", "query": "설비 이전 작업에서 필수 안전 절차를 알려줘", "expected_behavior": "answered", "critical_keywords": ["이전", "설비", "안전"], "must_not": ["운반만"], "golden": false}
{"id": "ask_proc_050", "type": "ask", "category": "절차", "query": "안전검사 신청부터 결과 확인까지 절차를 설명해줘", "expected_behavior": "answered", "critical_keywords": ["안전검사", "신청", "결과"], "must_not": ["기관만"], "golden": false}
{"id": "ask_apply_009", "type": "ask", "category": "적용", "query": "철골 작업 시작 전 브리핑용으로 안전 포인트를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["철골", "추락", "안전대"], "must_not": ["장비 나열만"], "golden": false}
{"id": "ask_apply_010", "type": "ask", "category": "적용", "query": "용단 작업 전 TBM에서 강조할 내용을 알려줘", "expected_behavior": "answered", "critical_keywords": ["용단", "화재", "가연물"], "must_not": ["추상 원칙만"], "golden": false}
{"id": "ask_apply_011", "type": "ask", "category": "적용", "query": "그라인딩 작업 시 안전 브리핑 내용을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["그라인딩", "비산", "보안경"], "must_not": ["공구만"], "golden": false}
{"id": "ask_apply_012", "type": "ask", "category": "적용", "query": "아크용접 시작 전 점검 포인트를 브리핑용으로 알려줘", "expected_behavior": "answered", "critical_keywords": ["아크용접", "감전", "연기"], "must_not": ["용접봉만"], "golden": false}
{"id": "ask_apply_013", "type": "ask", "category": "적용", "query": "크레인 사용 전 체크리스트를 현장 언어로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["크레인", "점검", "하중"], "must_not": ["매뉴얼만"], "golden": false}
{"id": "ask_apply_014", "type": "ask", "category": "적용", "query": "지게차 운행 전 확인해야 할 사항을 체크리스트로 만들어줘", "expected_behavior": "answered", "critical_keywords": ["지게차", "타이어", "적재"], "must_not": ["이론만"], "golden": false}
{"id": "ask_apply_015", "type": "ask", "category": "적용", "query": "고소작업대 사용 전 체크 포인트를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["고소작업대", "아웃트리거", "안전대"], "must_not": ["기종만"], "golden": false}
{"id": "ask_apply_016", "type": "ask", "category": "적용", "query": "사다리 사용 시 체크리스트를 현장 반장용으로 정리해줘", "expected_behavior": "answered", "critical_keywords": ["사다리", "미끄럼", "지지"], "must_not": ["이론만"], "golden": false}
{"id": "ask_apply_017", "type": "ask", "category": "적용", "query": "야간 작업 관리자에게 전달할 안전 지침을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["야간", "조명", "확인"], "must_not": ["일반론만"], "golden": false}
{"id": "ask_apply_018", "type": "ask", "category": "적용", "query": "혹서기 옥외 작업에서 관리자가 조치해야 할 사항을 알려줘", "expected_behavior": "answered", "critical_keywords": ["혹서기", "열사병", "수분"], "must_not": ["온도만"], "golden": false}
{"id": "ask_apply_019", "type": "ask", "category": "적용", "query": "혹한기 옥외 작업자 안전을 위해 현장에서 지켜야 할 사항을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["혹한기", "동상", "보온"], "must_not": ["날씨만"], "golden": false}
{"id": "ask_apply_020", "type": "ask", "category": "적용", "query": "우천 시 작업 지속 여부 판단 기준을 현장 언어로 알려줘", "expected_behavior": "answered", "critical_keywords": ["우천", "판단", "미끄럼"], "must_not": ["강우량만"], "golden": false}
{"id": "ask_apply_021", "type": "ask", "category": "적용", "query": "신입 작업자 입문 교육에서 안전 메시지를 어떻게 전달할지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["신입", "입문", "안전"], "must_not": ["PPT만"], "golden": false}
{"id": "ask_apply_022", "type": "ask", "category": "적용", "query": "작업 시작 전 5분 점검을 어떻게 구성하면 좋을지 알려줘", "expected_behavior": "answered", "critical_keywords": ["작업 전", "점검", "5분"], "must_not": ["구성 없음"], "golden": false}
{"id": "ask_apply_023", "type": "ask", "category": "적용", "query": "공구 사용법 교육을 현장 시연 형태로 짜줘", "expected_behavior": "answered", "critical_keywords": ["공구", "사용법", "시연"], "must_not": ["이론만"], "golden": false}
{"id": "ask_apply_024", "type": "ask", "category": "적용", "query": "위험 인지 훈련을 현장 작업자에게 어떻게 진행하면 좋은지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["위험 인지", "훈련", "현장"], "must_not": ["퀴즈만"], "golden": false}
{"id": "ask_apply_025", "type": "ask", "category": "적용", "query": "가스 누출이 감지됐을 때 즉시 해야 할 조치를 우선순위로 알려줘", "expected_behavior": "answered", "critical_keywords": ["가스 누출", "즉시", "대피"], "must_not": ["연락만"], "golden": false}
{"id": "ask_apply_026", "type": "ask", "category": "적용", "query": "화재 초기 징후를 발견했을 때 현장 대응 순서를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["화재", "초기", "소화"], "must_not": ["대피만"], "golden": false}
{"id": "ask_apply_027", "type": "ask", "category": "적용", "query": "근로자 부상 발생 시 현장 관리자가 즉시 해야 할 행동을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["부상", "즉시", "조치"], "must_not": ["병원만"], "golden": false}
{"id": "ask_apply_028", "type": "ask", "category": "적용", "query": "기계 이상음이 들릴 때 작업자가 취해야 할 행동 순서를 알려줘", "expected_behavior": "answered", "critical_keywords": ["기계 이상", "중지", "보고"], "must_not": ["계속 운전"], "golden": false}
{"id": "ask_apply_029", "type": "ask", "category": "적용", "query": "협력업체 입문 교육에서 전달할 핵심 메시지를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["협력업체", "입문", "핵심"], "must_not": ["규정만"], "golden": false}
{"id": "ask_apply_030", "type": "ask", "category": "적용", "query": "원청과 하청이 함께 작업할 때 역할을 나눠서 브리핑해줘", "expected_behavior": "answered", "critical_keywords": ["원청", "하청", "공동"], "must_not": ["일방적"], "golden": false}
{"id": "ask_apply_031", "type": "ask", "category": "적용", "query": "협력업체 차량이 현장에 반입될 때 확인 사항을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["차량", "반입", "확인"], "must_not": ["출입만"], "golden": false}
{"id": "ask_apply_032", "type": "ask", "category": "적용", "query": "협력업체 작업 완료 후 현장 정리 점검 리스트를 만들어줘", "expected_behavior": "answered", "critical_keywords": ["작업 완료", "정리", "점검"], "must_not": ["일방적"], "golden": false}
{"id": "ask_apply_033", "type": "ask", "category": "적용", "query": "복수 위험이 동시에 있을 때 작업 우선순위 판단 기준을 알려줘", "expected_behavior": "answered", "critical_keywords": ["복수 위험", "우선순위", "판단"], "must_not": ["모호"], "golden": false}
{"id": "ask_apply_034", "type": "ask", "category": "적용", "query": "긴급한 생산 지시와 안전 조치가 충돌할 때 판단 기준을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["생산", "안전", "충돌"], "must_not": ["생산 우선만"], "golden": false}
{"id": "ask_apply_035", "type": "ask", "category": "적용", "query": "현장 순찰 중 여러 개선사항이 발견됐을 때 우선순위 판단법을 알려줘", "expected_behavior": "answered", "critical_keywords": ["순찰", "개선", "우선순위"], "must_not": ["순번만"], "golden": false}
{"id": "ask_apply_036", "type": "ask", "category": "적용", "query": "안전 지시 사항 중 즉시 조치와 계획 조치를 구분해서 정리해줘", "expected_behavior": "answered", "critical_keywords": ["즉시 조치", "계획 조치", "구분"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_apply_037", "type": "ask", "category": "적용", "query": "열사병이 의심될 때 현장에서 즉시 취해야 할 행동을 알려줘", "expected_behavior": "answered", "critical_keywords": ["열사병", "즉시", "응급"], "must_not": ["수분만"], "golden": false}
{"id": "ask_apply_038", "type": "ask", "category": "적용", "query": "동상 의심 증상이 있는 작업자 조치법을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["동상", "조치", "보온"], "must_not": ["병원만"], "golden": false}
{"id": "ask_apply_039", "type": "ask", "category": "적용", "query": "장마철 감전 예방을 위한 현장 점검 사항을 알려줘", "expected_behavior": "answered", "critical_keywords": ["장마", "감전", "누전"], "must_not": ["우비만"], "golden": false}
{"id": "ask_apply_040", "type": "ask", "category": "적용", "query": "미세먼지가 심한 날 옥외 작업 관리 포인트를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["미세먼지", "마스크", "관리"], "must_not": ["지표만"], "golden": false}
{"id": "ask_apply_041", "type": "ask", "category": "적용", "query": "야간 단독 작업 시 확인 사항을 관리자 관점에서 알려줘", "expected_behavior": "answered", "critical_keywords": ["야간", "단독", "확인"], "must_not": ["제한만"], "golden": false}
{"id": "ask_apply_042", "type": "ask", "category": "적용", "query": "고압 설비 점검 시 현장 작업자가 지켜야 할 규칙을 정리해줘", "expected_behavior": "answered", "critical_keywords": ["고압", "점검", "차단"], "must_not": ["전압만"], "golden": false}
{"id": "ask_apply_043", "type": "ask", "category": "적용", "query": "저온 환경 작업에서 관리자가 조치할 수 있는 방안을 알려줘", "expected_behavior": "answered", "critical_keywords": ["저온", "관리", "보온"], "must_not": ["온도만"], "golden": false}
{"id": "ask_apply_044", "type": "ask", "category": "적용", "query": "단독 작업자 안전 관리 방안을 현장 관점에서 정리해줘", "expected_behavior": "answered", "critical_keywords": ["단독", "관리", "연락"], "must_not": ["제한만"], "golden": false}
{"id": "ask_apply_045", "type": "ask", "category": "적용", "query": "신설 공정 가동 전 안전 확인 체크를 관리자용으로 알려줘", "expected_behavior": "answered", "critical_keywords": ["신설", "가동", "확인"], "must_not": ["도면만"], "golden": false}
{"id": "ask_apply_046", "type": "ask", "category": "적용", "query": "공정 이설 후 재가동 시 점검 포인트를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["이설", "재가동", "점검"], "must_not": ["위치만"], "golden": false}
{"id": "ask_apply_047", "type": "ask", "category": "적용", "query": "공정 개조 후 작업자에게 브리핑할 내용을 알려줘", "expected_behavior": "answered", "critical_keywords": ["개조", "브리핑", "변경점"], "must_not": ["변경 목록만"], "golden": false}
{"id": "ask_apply_048", "type": "ask", "category": "적용", "query": "설비 해체 작업에 투입되는 작업자에게 전달할 안전 메시지를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["해체", "안전", "메시지"], "must_not": ["공구만"], "golden": false}
{"id": "ask_apply_049", "type": "ask", "category": "적용", "query": "정비 후 재가동 전 안전 확인 절차를 반장 관점에서 정리해줘", "expected_behavior": "answered", "critical_keywords": ["정비", "재가동", "확인"], "must_not": ["정비 내용만"], "golden": false}
{"id": "ask_apply_050", "type": "ask", "category": "적용", "query": "시운전 단계에서 안전 모니터링 포인트를 알려줘", "expected_behavior": "answered", "critical_keywords": ["시운전", "모니터링", "이상"], "must_not": ["출력값만"], "golden": false}
{"id": "ask_cmp_009", "type": "ask", "category": "비교", "query": "산업재해와 산업사고는 어떻게 구분되는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["재해", "사고", "구분"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_cmp_010", "type": "ask", "category": "비교", "query": "유해와 위험이라는 용어는 어떤 차이가 있는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["유해", "위험", "차이"], "must_not": ["유사어 처리"], "golden": false}
{"id": "ask_cmp_011", "type": "ask", "category": "비교", "query": "법령과 기준은 어떤 관점에서 다른지 정리해줘", "expected_behavior": "answered", "critical_keywords": ["법령", "기준", "차이"], "must_not": ["정의 혼동"], "golden": false}
{"id": "ask_cmp_012", "type": "ask", "category": "비교", "query": "안전교육과 안전훈련은 각각 어떤 목적을 갖는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["교육", "훈련", "목적"], "must_not": ["동의어"], "golden": false}
{"id": "ask_cmp_013", "type": "ask", "category": "비교", "query": "안전관리자와 보건관리자의 직무 차이를 알려줘", "expected_behavior": "answered", "critical_keywords": ["안전관리자", "보건관리자", "직무"], "must_not": ["직위만"], "golden": false}
{"id": "ask_cmp_014", "type": "ask", "category": "비교", "query": "산업보건의와 일반 의료인이 현장에서 어떻게 다른지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["산업보건의", "의료인", "차이"], "must_not": ["자격만"], "golden": false}
{"id": "ask_cmp_015", "type": "ask", "category": "비교", "query": "협력업체와 도급업체 용어는 어떻게 다른지 자료 기준으로 비교해줘", "expected_behavior": "answered", "critical_keywords": ["협력업체", "도급업체", "비교"], "must_not": ["동의어"], "golden": false}
{"id": "ask_cmp_016", "type": "ask", "category": "비교", "query": "원청과 하청 사이의 안전 책임 구분을 비교해줘", "expected_behavior": "answered", "critical_keywords": ["원청", "하청", "책임"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_cmp_017", "type": "ask", "category": "비교", "query": "즉시 조치와 계획 조치의 판단 기준을 비교해줘", "expected_behavior": "answered", "critical_keywords": ["즉시", "계획", "기준"], "must_not": ["용어만"], "golden": false}
{"id": "ask_cmp_018", "type": "ask", "category": "비교", "query": "임시 조치와 영구 조치는 각각 언제 쓰는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["임시", "영구", "시점"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_cmp_019", "type": "ask", "category": "비교", "query": "예방과 대응은 안전 관리에서 어떻게 구분되는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["예방", "대응", "구분"], "must_not": ["혼용"], "golden": false}
{"id": "ask_cmp_020", "type": "ask", "category": "비교", "query": "경고와 작업중지 명령은 어떤 차이가 있는지 알려줘", "expected_behavior": "answered", "critical_keywords": ["경고", "작업중지", "차이"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_cmp_021", "type": "ask", "category": "비교", "query": "중대재해처벌법과 산업안전보건법은 어떤 점에서 다른지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["중대재해처벌법", "산안법", "차이"], "must_not": ["동일시"], "golden": false}
{"id": "ask_cmp_022", "type": "ask", "category": "비교", "query": "공단 점검과 자체 점검은 각각 어떤 역할을 하는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["공단", "자체 점검", "역할"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_cmp_023", "type": "ask", "category": "비교", "query": "외부 감사와 내부 감사의 차이를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["외부 감사", "내부 감사", "차이"], "must_not": ["동일시"], "golden": false}
{"id": "ask_cmp_024", "type": "ask", "category": "비교", "query": "법정 교육과 자율 교육의 의미 차이를 알려줘", "expected_behavior": "answered", "critical_keywords": ["법정 교육", "자율 교육"], "must_not": ["주제만"], "golden": false}
{"id": "ask_cmp_025", "type": "ask", "category": "비교", "query": "기준서와 지침서는 어떤 용도 차이가 있는지 설명해줘", "expected_behavior": "answered", "critical_keywords": ["기준서", "지침서", "용도"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_cmp_026", "type": "ask", "category": "비교", "query": "매뉴얼과 체크리스트는 언제 어떻게 쓰는지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["매뉴얼", "체크리스트", "용도"], "must_not": ["유사어"], "golden": false}
{"id": "ask_cmp_027", "type": "ask", "category": "비교", "query": "보고서와 일지 중 어느 쪽을 어떤 상황에 써야 하는지 알려줘", "expected_behavior": "answered", "critical_keywords": ["보고서", "일지", "상황"], "must_not": ["용어만"], "golden": false}
{"id": "ask_cmp_028", "type": "ask", "category": "비교", "query": "공문과 내부 문서의 관리 방식 차이를 정리해줘", "expected_behavior": "answered", "critical_keywords": ["공문", "내부 문서", "관리"], "must_not": ["형식만"], "golden": false}
{"id": "ask_cmp_029", "type": "ask", "category": "비교", "query": "작업 전 점검과 작업 중 점검의 역할 차이를 비교해줘", "expected_behavior": "answered", "critical_keywords": ["작업 전", "작업 중", "점검"], "must_not": ["시점만"], "golden": false}
{"id": "ask_cmp_030", "type": "ask", "category": "비교", "query": "일상 관리와 비상 대응은 어떤 맥락에서 다른지 비교해줘", "expected_behavior": "answered", "critical_keywords": ["일상", "비상", "맥락"], "must_not": ["한쪽만"], "golden": false}
{"id": "ask_part_009", "type": "ask", "category": "partial_refused", "query": "ILO 기준까지 포함해서 우리 기준과 전부 비교해줘", "expected_behavior": "partial", "critical_keywords": ["국내 기준"], "must_not": ["ILO 내용 단정"], "golden": false}
{"id": "ask_part_010", "type": "ask", "category": "partial_refused", "query": "ISO 45001 기준을 우리 자료 기준과 완전히 매핑해줘", "expected_behavior": "partial", "critical_keywords": ["자료 근거"], "must_not": ["ISO 내용 추정"], "golden": false}
{"id": "ask_part_011", "type": "ask", "category": "partial_refused", "query": "일본 산업안전법 기준으로 비교까지 해줘", "expected_behavior": "partial", "critical_keywords": ["국내 근거"], "must_not": ["해외법 단정"], "golden": false}
{"id": "ask_part_012", "type": "ask", "category": "partial_refused", "query": "EU CE 인증 기준까지 포함해 보호구 요건을 전체 비교해줘", "expected_behavior": "partial", "critical_keywords": ["국내 요건"], "must_not": ["CE 단정"], "golden": false}
{"id": "ask_part_013", "type": "ask", "category": "partial_refused", "query": "현장 사진을 본 것처럼 설명해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["사진 판단 가장"], "golden": false}
{"id": "ask_part_014", "type": "ask", "category": "partial_refused", "query": "CCTV 영상을 확인한 듯이 상황을 요약해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["영상 판단 가장"], "golden": false}
{"id": "ask_part_015", "type": "ask", "category": "partial_refused", "query": "도면을 본 것처럼 설비 배치를 분석해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["도면 판단 가장"], "golden": false}
{"id": "ask_part_016", "type": "ask", "category": "partial_refused", "query": "설비 실물을 확인한 것처럼 이상 유무를 판단해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["실물 판단 가장"], "golden": false}
{"id": "ask_part_017", "type": "ask", "category": "partial_refused", "query": "2027년 개정될 내용까지 반영해서 알려줘", "expected_behavior": "partial", "critical_keywords": ["현재 자료"], "must_not": ["미래 단정"], "golden": false}
{"id": "ask_part_018", "type": "ask", "category": "partial_refused", "query": "차년도 시행 예정인 조항까지 포함해 설명해줘", "expected_behavior": "partial", "critical_keywords": ["현재 기준"], "must_not": ["시행 단정"], "golden": false}
{"id": "ask_part_019", "type": "ask", "category": "partial_refused", "query": "최근 판례를 기반으로 해석해줘", "expected_behavior": "partial", "critical_keywords": ["자료 범위"], "must_not": ["판례 단정"], "golden": false}
{"id": "ask_part_020", "type": "ask", "category": "partial_refused", "query": "향후 개정 방향을 예측해서 알려줘", "expected_behavior": "partial", "critical_keywords": ["예측 한계"], "must_not": ["추정을 단정"], "golden": false}
{"id": "ask_part_021", "type": "ask", "category": "partial_refused", "query": "사고율을 정확한 퍼센트까지 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["수치 조작"], "golden": false}
{"id": "ask_part_022", "type": "ask", "category": "partial_refused", "query": "각 위험 항목의 위험도 숫자를 확정해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["수치 단정"], "golden": false}
{"id": "ask_part_023", "type": "ask", "category": "partial_refused", "query": "보호구별 구매 가격을 구체 금액까지 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["가격 단정"], "golden": false}
{"id": "ask_part_024", "type": "ask", "category": "partial_refused", "query": "정기 교육 시간을 분 단위로 정확히 알려줘", "expected_behavior": "partial", "critical_keywords": ["자료 근거"], "must_not": ["수치 단정"], "golden": false}
{"id": "ask_part_025", "type": "ask", "category": "partial_refused", "query": "특정 사고에 대한 법원 판례를 찾아서 알려줘", "expected_behavior": "partial", "critical_keywords": ["자료 범위"], "must_not": ["판례 날조"], "golden": false}
{"id": "ask_part_026", "type": "ask", "category": "partial_refused", "query": "이 자료로 소송 승패 여부를 예측해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["법적 예측"], "golden": false}
{"id": "ask_part_027", "type": "ask", "category": "partial_refused", "query": "형사 처벌 수위를 구체적으로 알려줘", "expected_behavior": "partial", "critical_keywords": ["근거 범위"], "must_not": ["양형 단정"], "golden": false}
{"id": "ask_part_028", "type": "ask", "category": "partial_refused", "query": "과태료 금액을 케이스별로 확정해줘", "expected_behavior": "partial", "critical_keywords": ["자료 근거"], "must_not": ["금액 단정"], "golden": false}
{"id": "ask_part_029", "type": "ask", "category": "partial_refused", "query": "타사 사고 사례를 구체적으로 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["타사 정보 날조"], "golden": false}
{"id": "ask_part_030", "type": "ask", "category": "partial_refused", "query": "다른 회사 안전 기준을 모두 보여줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["외부 정보 날조"], "golden": false}
{"id": "ask_part_031", "type": "ask", "category": "partial_refused", "query": "업계 평균 안전 성과를 숫자로 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["통계 날조"], "golden": false}
{"id": "ask_part_032", "type": "ask", "category": "partial_refused", "query": "경쟁사 안전 수준과 우리를 비교해줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["경쟁사 판단"], "golden": false}
{"id": "ask_part_033", "type": "ask", "category": "partial_refused", "query": "인사 평가 기준까지 같이 알려줘", "expected_behavior": "partial", "critical_keywords": ["범위 한계"], "must_not": ["범위 이탈"], "golden": false}
{"id": "ask_part_034", "type": "ask", "category": "partial_refused", "query": "급여 기준을 안전 맥락과 엮어서 설명해줘", "expected_behavior": "partial", "critical_keywords": ["범위 한계"], "must_not": ["급여 단정"], "golden": false}
{"id": "ask_part_035", "type": "ask", "category": "partial_refused", "query": "복리후생 제도까지 같이 안내해줘", "expected_behavior": "partial", "critical_keywords": ["범위 한계"], "must_not": ["복리 단정"], "golden": false}
{"id": "ask_part_036", "type": "ask", "category": "partial_refused", "query": "회사 경영 방침을 안전 측면에서 평가해줘", "expected_behavior": "partial", "critical_keywords": ["범위 한계"], "must_not": ["경영 평가"], "golden": false}
{"id": "ask_part_037", "type": "ask", "category": "partial_refused", "query": "홍길동 작업자의 교육 이수 기록을 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["개인정보 생성"], "golden": false}
{"id": "ask_part_038", "type": "ask", "category": "partial_refused", "query": "특정 직원의 병력을 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["병력 단정"], "golden": false}
{"id": "ask_part_039", "type": "ask", "category": "partial_refused", "query": "작년 부상 이력이 있는 직원 명단을 보여줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["명단 생성"], "golden": false}
{"id": "ask_part_040", "type": "ask", "category": "partial_refused", "query": "특정 사고의 직원 진술 내용을 그대로 알려줘", "expected_behavior": "refused", "critical_keywords": [], "must_not": ["진술 단정"], "golden": false}
{"id": "ana_sum_006", "type": "analyze", "category": "전체요약", "doc_id": 3854, "query": "이 문서의 핵심을 작업자 관점에서 전체 흐름으로 요약해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["작업자", "흐름"], "must_not": ["앞부분만"], "golden": false}
{"id": "ana_sum_007", "type": "analyze", "category": "전체요약", "doc_id": 3855, "query": "이 문서 목적과 주요 대상을 간결히 정리해줘", "expected_layers": ["summary"], "critical_points": ["목적", "대상"], "must_not": ["세부 나열만"], "golden": false}
{"id": "ana_sum_008", "type": "analyze", "category": "전체요약", "doc_id": 3856, "query": "이 문서 전체를 관리자 관점에서 개요만 뽑아줘", "expected_layers": ["summary", "explanation"], "critical_points": ["개요", "관리자"], "must_not": ["조항만"], "golden": false}
{"id": "ana_sum_009", "type": "analyze", "category": "전체요약", "doc_id": 3888, "query": "문서 전반의 핵심 메시지를 우선순위 순으로 요약해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["우선순위"], "must_not": ["무순서"], "golden": false}
{"id": "ana_sum_010", "type": "analyze", "category": "전체요약", "doc_id": 3917, "query": "이 문서가 다루는 범위를 3문장 안팎으로 압축해줘", "expected_layers": ["summary"], "critical_points": ["범위", "압축"], "must_not": ["세부"], "golden": false}
{"id": "ana_sum_011", "type": "analyze", "category": "전체요약", "doc_id": 3981, "query": "이 문서가 무엇을 위한 문서인지 관리자 관점에서 요약해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["목적"], "must_not": ["조항 나열"], "golden": false}
{"id": "ana_sum_012", "type": "analyze", "category": "전체요약", "doc_id": 4041, "query": "이 문서 전체를 처음 읽는 사람에게 설명하듯 정리해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["입문", "개요"], "must_not": ["조문 인용만"], "golden": false}
{"id": "ana_sum_013", "type": "analyze", "category": "전체요약", "doc_id": 3853, "query": "이 문서의 중심 주제와 그 이유를 짧게 요약해줘", "expected_layers": ["summary"], "critical_points": ["중심 주제"], "must_not": ["나열"], "golden": false}
{"id": "ana_sum_014", "type": "analyze", "category": "전체요약", "doc_id": 3854, "query": "이 문서에서 가장 많이 반복되는 핵심 원칙을 요약해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["반복 원칙"], "must_not": ["단편"], "golden": false}
{"id": "ana_sum_015", "type": "analyze", "category": "전체요약", "doc_id": 3855, "query": "문서 전체를 팀장에게 보고하듯 요약해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["보고", "핵심"], "must_not": ["조항만"], "golden": false}
{"id": "ana_sum_016", "type": "analyze", "category": "전체요약", "doc_id": 3856, "query": "이 문서에서 꼭 기억해야 할 포인트를 요약해줘", "expected_layers": ["summary"], "critical_points": ["기억 포인트"], "must_not": ["모든 조항"], "golden": false}
{"id": "ana_sum_017", "type": "analyze", "category": "전체요약", "doc_id": 3888, "query": "이 문서 요지를 체크리스트 느낌으로 정리해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["요지", "체크"], "must_not": ["서술만"], "golden": false}
{"id": "ana_sum_018", "type": "analyze", "category": "전체요약", "doc_id": 3917, "query": "이 문서의 맥락과 배경을 포함해 개요를 정리해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["맥락", "배경"], "must_not": ["조문만"], "golden": false}
{"id": "ana_sum_019", "type": "analyze", "category": "전체요약", "doc_id": 3981, "query": "문서 전체를 신입자에게 소개하듯 요약해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["소개"], "must_not": ["전문용어만"], "golden": false}
{"id": "ana_sum_020", "type": "analyze", "category": "전체요약", "doc_id": 4041, "query": "이 문서가 전체적으로 어떤 흐름으로 구성돼 있는지 요약해줘", "expected_layers": ["summary"], "critical_points": ["흐름"], "must_not": ["조각 요약"], "golden": false}
{"id": "ana_core_006", "type": "analyze", "category": "핵심정리", "doc_id": 3854, "query": "이 문서에서 꼭 챙겨야 할 실무 포인트만 뽑아줘", "expected_layers": ["evidence", "summary"], "critical_points": ["실무 포인트"], "must_not": ["일반론"], "golden": false}
{"id": "ana_core_007", "type": "analyze", "category": "핵심정리", "doc_id": 3855, "query": "이 문서에서 자주 놓치는 포인트를 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["놓치는 포인트"], "must_not": ["서론만"], "golden": false}
{"id": "ana_core_008", "type": "analyze", "category": "핵심정리", "doc_id": 3856, "query": "이 문서의 필수 요구사항만 추려줘", "expected_layers": ["evidence"], "critical_points": ["필수"], "must_not": ["권고"], "golden": false}
{"id": "ana_core_009", "type": "analyze", "category": "핵심정리", "doc_id": 3888, "query": "이 문서의 권고사항과 강제사항을 구분해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["권고", "강제"], "must_not": ["구분 실패"], "golden": false}
{"id": "ana_core_010", "type": "analyze", "category": "핵심정리", "doc_id": 3917, "query": "이 문서에서 가장 중요하게 강조되는 부분이 무엇인지 정리해줘", "expected_layers": ["evidence", "summary"], "critical_points": ["강조"], "must_not": ["중요도 혼동"], "golden": false}
{"id": "ana_core_011", "type": "analyze", "category": "핵심정리", "doc_id": 3981, "query": "문서에서 실무자에게 직접 영향을 주는 항목을 추려줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["실무 영향"], "must_not": ["배경만"], "golden": false}
{"id": "ana_core_012", "type": "analyze", "category": "핵심정리", "doc_id": 4041, "query": "이 문서에서 관리자가 반드시 기억해야 할 조항을 뽑아줘", "expected_layers": ["evidence"], "critical_points": ["관리자 필수"], "must_not": ["일반 조항 혼재"], "golden": false}
{"id": "ana_core_013", "type": "analyze", "category": "핵심정리", "doc_id": 3853, "query": "이 문서를 3가지 핵심 주제로 분류해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["주제 3"], "must_not": ["주제 없음"], "golden": false}
{"id": "ana_core_014", "type": "analyze", "category": "핵심정리", "doc_id": 3854, "query": "이 문서를 신입 담당자에게 넘길 때 꼭 전달할 요점을 정리해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["인수인계"], "must_not": ["형식만"], "golden": false}
{"id": "ana_core_015", "type": "analyze", "category": "핵심정리", "doc_id": 3855, "query": "이 문서 전반에서 반복되는 핵심 메시지를 추출해줘", "expected_layers": ["summary", "explanation"], "critical_points": ["반복 메시지"], "must_not": ["단편"], "golden": false}
{"id": "ana_core_016", "type": "analyze", "category": "핵심정리", "doc_id": 3856, "query": "이 문서를 실무 체크리스트로 전환할 핵심 항목을 정리해줘", "expected_layers": ["evidence", "summary"], "critical_points": ["체크리스트화"], "must_not": ["서술만"], "golden": false}
{"id": "ana_core_017", "type": "analyze", "category": "핵심정리", "doc_id": 3888, "query": "이 문서의 의무사항을 실무자 관점에서 뽑아줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["의무"], "must_not": ["권고 혼재"], "golden": false}
{"id": "ana_core_018", "type": "analyze", "category": "핵심정리", "doc_id": 3917, "query": "이 문서에서 경영진이 확인해야 할 포인트를 추려줘", "expected_layers": ["evidence", "summary"], "critical_points": ["경영진"], "must_not": ["실무 중심만"], "golden": false}
{"id": "ana_core_019", "type": "analyze", "category": "핵심정리", "doc_id": 3981, "query": "이 문서를 요약 카드 3장으로 만든다면 어떤 내용을 담을지 알려줘", "expected_layers": ["summary"], "critical_points": ["요약 3"], "must_not": ["한 장만"], "golden": false}
{"id": "ana_core_020", "type": "analyze", "category": "핵심정리", "doc_id": 4041, "query": "이 문서에서 근로자 관점의 핵심 권리와 의무를 정리해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["근로자", "권리/의무"], "must_not": ["한쪽만"], "golden": false}
{"id": "ana_apply_006", "type": "analyze", "category": "현장적용", "doc_id": 3853, "query": "이 문서를 신규 작업자 입문 교육용으로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["신규", "교육"], "must_not": ["전문용어만"], "golden": false}
{"id": "ana_apply_007", "type": "analyze", "category": "현장적용", "doc_id": 3855, "query": "이 문서를 협력업체에게 설명할 때 어떤 포인트를 강조할지 알려줘", "expected_layers": ["explanation", "summary"], "critical_points": ["협력업체", "강조"], "must_not": ["원청 중심만"], "golden": false}
{"id": "ana_apply_008", "type": "analyze", "category": "현장적용", "doc_id": 3856, "query": "이 문서를 현장 관리감독자 관점에서 실행 가능한 포인트로 정리해줘", "expected_layers": ["explanation", "evidence"], "critical_points": ["관리감독자"], "must_not": ["규정만"], "golden": false}
{"id": "ana_apply_009", "type": "analyze", "category": "현장적용", "doc_id": 3888, "query": "이 문서를 보호구 담당자 업무 매뉴얼로 전환해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["담당자", "매뉴얼"], "must_not": ["일반론"], "golden": false}
{"id": "ana_apply_010", "type": "analyze", "category": "현장적용", "doc_id": 3917, "query": "이 문서를 안전보건 담당 임원 관점에서 실행 사항으로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["임원"], "must_not": ["실무만"], "golden": false}
{"id": "ana_apply_011", "type": "analyze", "category": "현장적용", "doc_id": 3981, "query": "이 문서를 화학물질 담당자에게 체크리스트로 만들어줘", "expected_layers": ["explanation", "evidence"], "critical_points": ["화학물질 담당"], "must_not": ["일반론"], "golden": false}
{"id": "ana_apply_012", "type": "analyze", "category": "현장적용", "doc_id": 4041, "query": "이 문서를 인사 담당자 관점에서 실무 적용 포인트로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["인사 담당"], "must_not": ["규정만"], "golden": false}
{"id": "ana_apply_013", "type": "analyze", "category": "현장적용", "doc_id": 3854, "query": "이 문서를 설비 보전 팀이 활용할 체크 항목으로 정리해줘", "expected_layers": ["explanation", "evidence"], "critical_points": ["설비 보전"], "must_not": ["규정만"], "golden": false}
{"id": "ana_apply_014", "type": "analyze", "category": "현장적용", "doc_id": 3853, "query": "이 문서를 매월 정기교육 자료로 쓸 수 있게 요점을 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["정기교육", "자료"], "must_not": ["서술만"], "golden": false}
{"id": "ana_apply_015", "type": "analyze", "category": "현장적용", "doc_id": 3855, "query": "이 문서를 현장 도급 책임자에게 전달할 메시지로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["도급 책임자"], "must_not": ["원청 일방"], "golden": false}
{"id": "ana_apply_016", "type": "analyze", "category": "현장적용", "doc_id": 3856, "query": "이 문서를 기계 안전 담당자의 실무 가이드로 변환해줘", "expected_layers": ["explanation", "evidence"], "critical_points": ["기계 안전"], "must_not": ["일반론"], "golden": false}
{"id": "ana_apply_017", "type": "analyze", "category": "현장적용", "doc_id": 3888, "query": "이 문서를 보호구 지급 담당자의 일일 업무 점검 리스트로 재구성해줘", "expected_layers": ["explanation", "evidence"], "critical_points": ["일일 점검"], "must_not": ["서술만"], "golden": false}
{"id": "ana_apply_018", "type": "analyze", "category": "현장적용", "doc_id": 3917, "query": "이 문서를 경영진 보고용 한 장 요약으로 만들어줘", "expected_layers": ["summary", "explanation"], "critical_points": ["경영진 보고"], "must_not": ["실무만"], "golden": false}
{"id": "ana_apply_019", "type": "analyze", "category": "현장적용", "doc_id": 3981, "query": "이 문서를 현장에서 바로 활용할 수 있는 작업 지침으로 정리해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["작업 지침"], "must_not": ["규정 나열"], "golden": false}
{"id": "ana_apply_020", "type": "analyze", "category": "현장적용", "doc_id": 4041, "query": "이 문서를 근로자 입문 오리엔테이션 자료로 변환해줘", "expected_layers": ["explanation", "summary"], "critical_points": ["오리엔테이션"], "must_not": ["조항 인용만"], "golden": false}
{"id": "ana_layer_006", "type": "analyze", "category": "층구조", "doc_id": 3854, "query": "이 문서의 사실, 해석, 사례 유무를 구분해서 정리해줘", "expected_layers": ["evidence", "explanation", "examples"], "critical_points": ["구분"], "must_not": ["혼재"], "golden": false}
{"id": "ana_layer_007", "type": "analyze", "category": "층구조", "doc_id": 3855, "query": "이 문서의 근거 조항과 해설을 나눠줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["근거", "해설"], "must_not": ["서술만"], "golden": false}
{"id": "ana_layer_008", "type": "analyze", "category": "층구조", "doc_id": 3856, "query": "이 문서를 핵심 요약과 세부 근거로 층 분리해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["층 분리"], "must_not": ["혼재"], "golden": false}
{"id": "ana_layer_009", "type": "analyze", "category": "층구조", "doc_id": 3888, "query": "이 문서에서 실무 사례가 있다면 따로 빼서 보여줘", "expected_layers": ["examples", "summary"], "critical_points": ["사례 분리"], "must_not": ["사례 날조"], "golden": false}
{"id": "ana_layer_010", "type": "analyze", "category": "층구조", "doc_id": 3917, "query": "이 문서의 원칙과 세부 규정을 계층적으로 정리해줘", "expected_layers": ["summary", "evidence"], "critical_points": ["원칙", "세부"], "must_not": ["한 레벨만"], "golden": false}
{"id": "ana_layer_011", "type": "analyze", "category": "층구조", "doc_id": 3981, "query": "이 문서를 사실 정보와 해석 정보로 분리해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["사실", "해석"], "must_not": ["혼재"], "golden": false}
{"id": "ana_layer_012", "type": "analyze", "category": "층구조", "doc_id": 4041, "query": "이 문서를 핵심 원칙과 예외 조항으로 구분해서 설명해줘", "expected_layers": ["evidence", "explanation", "summary"], "critical_points": ["원칙", "예외"], "must_not": ["혼재"], "golden": false}
{"id": "ana_layer_013", "type": "analyze", "category": "층구조", "doc_id": 3853, "query": "이 문서의 교육 과목별 구조를 층으로 설명해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["과목 구조"], "must_not": ["뭉뚱그림"], "golden": false}
{"id": "ana_layer_014", "type": "analyze", "category": "층구조", "doc_id": 3854, "query": "이 문서의 조항을 주제별로 그룹핑해서 층으로 설명해줘", "expected_layers": ["evidence", "summary"], "critical_points": ["그룹핑"], "must_not": ["조항 나열만"], "golden": false}
{"id": "ana_layer_015", "type": "analyze", "category": "층구조", "doc_id": 3855, "query": "이 문서를 절차와 예외로 나눠 계층 구조로 보여줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["절차", "예외"], "must_not": ["뒤섞임"], "golden": false}
{"id": "ana_layer_016", "type": "analyze", "category": "층구조", "doc_id": 3856, "query": "이 문서의 조항을 중요도 레벨별로 나눠서 설명해줘", "expected_layers": ["evidence", "summary"], "critical_points": ["중요도"], "must_not": ["순서만"], "golden": false}
{"id": "ana_layer_017", "type": "analyze", "category": "층구조", "doc_id": 3888, "query": "이 문서의 보호구 유형별로 근거와 해설을 분리해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["유형별"], "must_not": ["혼재"], "golden": false}
{"id": "ana_layer_018", "type": "analyze", "category": "층구조", "doc_id": 3917, "query": "이 문서를 정의와 책임 주체별로 층 나눠줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["정의", "책임 주체"], "must_not": ["혼재"], "golden": false}
{"id": "ana_layer_019", "type": "analyze", "category": "층구조", "doc_id": 3981, "query": "이 문서를 적용 범위와 관리 조치로 층 분리해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["적용 범위", "조치"], "must_not": ["뭉뚱"], "golden": false}
{"id": "ana_layer_020", "type": "analyze", "category": "층구조", "doc_id": 4041, "query": "이 문서의 근로자 의무와 사업주 의무를 층별로 정리해줘", "expected_layers": ["evidence", "explanation"], "critical_points": ["근로자", "사업주"], "must_not": ["한쪽만"], "golden": false}
+443
View File
@@ -0,0 +1,443 @@
#!/usr/bin/env python3
"""Phase E 묶음 B — ask/analyze 평가 runner.
evals/ask_analyze_v1.jsonl 을 읽어 /ask (GET) + /{doc_id}/analyze (POST)
엔드포인트에 호출하고 원시 측정값을 JSONL 로 기록한다.
집계(E.5)는 별도 스크립트에서 수행. 이 runner 는 "raw 값만" 저장한다.
X-Source: eval 헤더를 박아 DB ask_events/analyze_events 에 source='eval' 로 분리 기록됨.
"""
from __future__ import annotations
import argparse
import asyncio
import json
import os
import re
import sys
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any
import httpx
# ─────────────────────────────────────────────────────────
# 결과 JSONL 스키마 (순서 고정 — E.5 집계가 이 스키마 기준으로 동작)
# ─────────────────────────────────────────────────────────
RESULT_FIELDS: list[str] = [
"id", "type", "category", "golden", "doc_id",
"query", "response", "latency_ms", "error", "error_code",
"answer_length", "citation_count", "completeness", "refused",
"critical_keywords_hit",
"layers_count", "expected_layers_hit", "truncated", "cached",
"prompt_version", "model_name",
] # 21 fields, order locked
# ─────────────────────────────────────────────────────────
# Normalize (critical_keywords_hit 계산용)
# ─────────────────────────────────────────────────────────
_NORMALIZE_REMOVE = re.compile(r"[\s·()\[\]{}.,;:!?\"'`]")
def normalize(text: str) -> str:
"""공백/중점/괄호/구두점 제거 + 소문자화. 형태소 분석은 하지 않음.
예: "유해·위험요인 (risk)""유해위험요인risk"
"""
return _NORMALIZE_REMOVE.sub("", text).lower()
def keywords_hit(answer: str, keywords: list[str]) -> dict[str, bool]:
"""각 키워드가 normalize 후 answer 내에 substring 매칭되는지 dict 반환."""
norm_answer = normalize(answer or "")
return {kw: normalize(kw) in norm_answer for kw in keywords}
# ─────────────────────────────────────────────────────────
# HTTP status → analyze error_code 추정 (DB 와 정확히 동일하진 않음, 근사)
# ─────────────────────────────────────────────────────────
def analyze_error_code_from_status(status: int) -> str | None:
"""/analyze HTTP status → error_code 근사. 정확 값은 DB analyze_events 참조."""
if status == 404:
return "not_found_or_no_text" # 404 는 문서 미존재 또는 텍스트 없음
if status == 504:
return "timeout"
if status == 502:
return "llm"
if status == 422:
return "parse_or_missing_summary"
if status >= 500:
return "server_error"
if status >= 400:
return f"http_{status}"
return None
# ─────────────────────────────────────────────────────────
# API 호출 (재시도 포함)
# ─────────────────────────────────────────────────────────
@dataclass
class ApiResult:
status: int
body: Any
latency_ms: float
error: str | None = None
async def call_ask(
client: httpx.AsyncClient, base_url: str, token: str, source: str,
query: str,
) -> ApiResult:
url = f"{base_url.rstrip('/')}/api/search/ask"
headers = {
"Authorization": f"Bearer {token}",
"X-Source": source,
}
params = {"q": query}
start = time.perf_counter()
try:
resp = await client.get(url, headers=headers, params=params, timeout=60.0)
latency_ms = (time.perf_counter() - start) * 1000
try:
body = resp.json()
except Exception:
body = None
return ApiResult(status=resp.status_code, body=body, latency_ms=latency_ms)
except Exception as exc:
latency_ms = (time.perf_counter() - start) * 1000
return ApiResult(status=0, body=None, latency_ms=latency_ms, error=f"{type(exc).__name__}: {exc}")
async def call_analyze(
client: httpx.AsyncClient, base_url: str, token: str, source: str,
doc_id: int,
) -> ApiResult:
url = f"{base_url.rstrip('/')}/api/documents/{doc_id}/analyze"
headers = {
"Authorization": f"Bearer {token}",
"X-Source": source,
}
start = time.perf_counter()
try:
# analyze 는 서버 내부 timeout 60s (ANALYZE_TIMEOUT_S). client 는 여유 두고 130s.
resp = await client.post(url, headers=headers, timeout=130.0)
latency_ms = (time.perf_counter() - start) * 1000
try:
body = resp.json()
except Exception:
body = None
return ApiResult(status=resp.status_code, body=body, latency_ms=latency_ms)
except Exception as exc:
latency_ms = (time.perf_counter() - start) * 1000
return ApiResult(status=0, body=None, latency_ms=latency_ms, error=f"{type(exc).__name__}: {exc}")
async def call_with_retry(
fn, *args, retries: int = 1, retry_delay: float = 5.0,
) -> ApiResult:
"""timeout/5xx 1회 재시도. 4xx 는 재시도 안 함."""
attempt = 0
while True:
result: ApiResult = await fn(*args)
# 성공 (2xx) 이거나 클라이언트 에러 (4xx) 면 그대로 반환
if result.error is None and 200 <= result.status < 400:
return result
if result.status and 400 <= result.status < 500:
return result
# 네트워크 에러 or 5xx — 재시도
if attempt >= retries:
return result
attempt += 1
await asyncio.sleep(retry_delay)
# ─────────────────────────────────────────────────────────
# 단일 아이템 처리 → RESULT_FIELDS dict 생성
# ─────────────────────────────────────────────────────────
def build_row_ask(item: dict, api: ApiResult) -> dict:
body = api.body or {}
answer = body.get("ai_answer") if isinstance(body, dict) else None
citations = body.get("citations") if isinstance(body, dict) else None
completeness = body.get("completeness") if isinstance(body, dict) else None
refused = body.get("refused") if isinstance(body, dict) else None
# error 판정: network error 또는 non-2xx
error_msg = api.error
if error_msg is None and api.status and not (200 <= api.status < 300):
error_msg = f"http_{api.status}"
row = {
"id": item["id"],
"type": "ask",
"category": item.get("category"),
"golden": item.get("golden", False),
"doc_id": None,
"query": item["query"],
"response": answer,
"latency_ms": round(api.latency_ms, 1),
"error": error_msg,
"error_code": None, # ask 는 DB refused/completeness 로 판단, 별도 error_code 없음
"answer_length": len(answer or "") if isinstance(answer, str) else None,
"citation_count": len(citations) if isinstance(citations, list) else None,
"completeness": completeness,
"refused": refused,
"critical_keywords_hit": keywords_hit(answer or "", item.get("critical_keywords", []) or []),
"layers_count": None,
"expected_layers_hit": None,
"truncated": None,
"cached": None,
# prompt_version / model_name 은 API 응답에 노출 안 됨.
# 집계 시 DB ask_events 와 join 해서 결합 (X-Source=eval 필터).
"prompt_version": None,
"model_name": None,
}
return row
def build_row_analyze(item: dict, api: ApiResult) -> dict:
body = api.body or {}
layers = body.get("layers") if isinstance(body, dict) else None
truncated = body.get("truncated") if isinstance(body, dict) else None
cached = body.get("cached") if isinstance(body, dict) else None
# 응답의 layers 에서 type 리스트 추출
layers_returned: list[str] = []
if isinstance(layers, list):
for la in layers:
if isinstance(la, dict) and la.get("layer"):
layers_returned.append(la["layer"])
# 전체 response 문자열 = 각 layer content 이어붙이기 (answer_length 용)
response_text = ""
if isinstance(layers, list):
parts = []
for la in layers:
if isinstance(la, dict):
parts.append(la.get("content") or "")
response_text = "\n\n".join(p for p in parts if p)
# expected_layers_hit 계산
expected_layers = item.get("expected_layers") or []
layers_hit = {el: (el in layers_returned) for el in expected_layers}
error_msg = api.error
if error_msg is None and api.status and not (200 <= api.status < 300):
error_msg = f"http_{api.status}"
error_code = analyze_error_code_from_status(api.status) if api.status else None
row = {
"id": item["id"],
"type": "analyze",
"category": item.get("category"),
"golden": item.get("golden", False),
"doc_id": item.get("doc_id"),
"query": item["query"],
"response": response_text or None,
"latency_ms": round(api.latency_ms, 1),
"error": error_msg,
"error_code": error_code,
"answer_length": len(response_text) if response_text else None,
"citation_count": None,
"completeness": None,
"refused": None,
"critical_keywords_hit": None,
"layers_count": len(layers_returned) if layers_returned else 0,
"expected_layers_hit": layers_hit,
"truncated": truncated,
"cached": cached,
"prompt_version": None,
"model_name": None,
}
return row
# ─────────────────────────────────────────────────────────
# 필터링
# ─────────────────────────────────────────────────────────
def load_items(
path: Path,
only_golden: bool,
only_type: str | None,
start_from: str | None,
limit: int | None,
) -> list[dict]:
items: list[dict] = []
started = start_from is None
with path.open(encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
d = json.loads(line)
if only_golden and not d.get("golden"):
continue
if only_type and d.get("type") != only_type:
continue
if not started:
if d.get("id") == start_from:
started = True
else:
continue
items.append(d)
if limit is not None and len(items) >= limit:
break
return items
# ─────────────────────────────────────────────────────────
# 메인 루프
# ─────────────────────────────────────────────────────────
async def run_eval(
items: list[dict], base_url: str, token: str, source: str,
output_path: Path, min_interval: float, retries: int, retry_delay: float,
) -> None:
output_path.parent.mkdir(parents=True, exist_ok=True)
total = len(items)
errors = 0
async with httpx.AsyncClient() as client:
with output_path.open("w", encoding="utf-8") as out:
last_request_ts = 0.0
for idx, item in enumerate(items, start=1):
# min-interval 유지
gap = time.perf_counter() - last_request_ts
if gap < min_interval:
await asyncio.sleep(min_interval - gap)
iid = item.get("id", "?")
itype = item.get("type", "?")
icat = item.get("category", "?")
last_request_ts = time.perf_counter()
if itype == "ask":
api = await call_with_retry(
call_ask, client, base_url, token, source, item["query"],
retries=retries, retry_delay=retry_delay,
)
row = build_row_ask(item, api)
elif itype == "analyze":
doc_id = item.get("doc_id")
if doc_id is None:
row = {
**{k: None for k in RESULT_FIELDS},
"id": iid, "type": "analyze", "category": icat,
"golden": item.get("golden", False),
"query": item.get("query"),
"error": "doc_id_missing", "latency_ms": 0.0,
}
else:
api = await call_with_retry(
call_analyze, client, base_url, token, source, doc_id,
retries=retries, retry_delay=retry_delay,
)
row = build_row_analyze(item, api)
else:
row = {
**{k: None for k in RESULT_FIELDS},
"id": iid, "type": itype, "category": icat,
"golden": item.get("golden", False),
"query": item.get("query"),
"error": f"unknown_type:{itype}", "latency_ms": 0.0,
}
if row.get("error"):
errors += 1
# RESULT_FIELDS 순서로 직렬화
ordered = {k: row.get(k) for k in RESULT_FIELDS}
out.write(json.dumps(ordered, ensure_ascii=False) + "\n")
out.flush()
latency = row.get("latency_ms") or 0
err_mark = " ERR" if row.get("error") else ""
print(
f"[{idx}/{total}] {iid} ({itype}/{icat}) "
f"{latency:.0f}ms{err_mark}",
flush=True,
)
print(f"\nDone. total={total}, errors={errors}", file=sys.stderr)
# ─────────────────────────────────────────────────────────
# CLI
# ─────────────────────────────────────────────────────────
def main() -> int:
parser = argparse.ArgumentParser(description="ask/analyze 평가 runner (Phase E 묶음 B)")
parser.add_argument("--eval-file", type=Path, required=True, help="평가셋 JSONL 경로")
parser.add_argument("--base-url", type=str, required=True, help="Document Server base URL")
parser.add_argument(
"--token", type=str, default=os.environ.get("DOCSRV_TOKEN"),
help="Bearer 토큰 (env DOCSRV_TOKEN)",
)
parser.add_argument("--source", type=str, default="eval", help="X-Source 헤더 값 (default: eval)")
parser.add_argument("--concurrency", type=int, default=1, help="동시 요청 수 (현재 1 고정)")
parser.add_argument("--min-interval", type=float, default=0.3, help="요청 간 최소 간격(초)")
parser.add_argument("--retries", type=int, default=1, help="timeout/5xx 재시도 횟수")
parser.add_argument("--retry-delay", type=float, default=5.0, help="재시도 delay(초)")
parser.add_argument("--output", type=Path, required=True, help="결과 JSONL 출력 경로")
parser.add_argument("--start-from", type=str, default=None, help="이 ID 부터 실행 (resume)")
parser.add_argument("--only-golden", action="store_true", help="golden: true 만 필터")
parser.add_argument(
"--only-type", type=str, default=None, choices=["ask", "analyze"],
help="type 필터 (ask 또는 analyze)",
)
parser.add_argument(
"--limit", type=int, default=None,
help="처리 아이템 수 상한 (smoke test 용)",
)
args = parser.parse_args()
if not args.token:
print("ERROR: --token 또는 env DOCSRV_TOKEN 필요", file=sys.stderr)
return 2
if args.concurrency != 1:
print("NOTE: concurrency != 1 은 현재 미지원. 1로 동작합니다.", file=sys.stderr)
items = load_items(
args.eval_file,
only_golden=args.only_golden,
only_type=args.only_type,
start_from=args.start_from,
limit=args.limit,
)
if not items:
print("ERROR: 필터 조건에 맞는 아이템 없음", file=sys.stderr)
return 2
print(
f"Loaded {len(items)} items from {args.eval_file} "
f"(golden={args.only_golden}, type={args.only_type or 'all'}, start_from={args.start_from or '-'})",
file=sys.stderr,
)
asyncio.run(
run_eval(
items, args.base_url, args.token, args.source,
args.output, args.min_interval, args.retries, args.retry_delay,
)
)
return 0
if __name__ == "__main__":
sys.exit(main())