Files

772 lines
26 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
$ uv run tools/flow.py test cases/lab02.json --webhook base-faq-import -v
✔ before: POST http://localhost:8800/faq-reset · HTTP 200 · 0.0s · {"cleared": 0}
→ request POST http://localhost:8800/faq-reset
{}
← response HTTP 200
{
"cleared": 0
}
✔ before: POST http://localhost:8800/quarantine-clear · HTTP 200 · 0.0s · {"cleared": 0}
→ request POST http://localhost:8800/quarantine-clear
{}
← response HTTP 200
{
"cleared": 0
}
✔ before: POST http://localhost:8800/quality-log-clear · HTTP 200 · 0.0s · {"cleared": 0}
→ request POST http://localhost:8800/quality-log-clear
{}
← response HTTP 200
{
"cleared": 0
}
✔ before: POST http://localhost:8800/alerts-clear · HTTP 200 · 0.0s · {"cleared": 0}
→ request POST http://localhost:8800/alerts-clear
{}
← response HTTP 200
{
"cleared": 0
}
POST http://localhost:5678/webhook/base-faq-import · 6 case(s) × 1
✘ 나쁜 배치는 통째로 거절 (422) · 0.1s · status 200 (expected 422); invalid_ratio = null (expected 0.3..1)
→ request POST /webhook/base-faq-import
{
"batch": "B-1",
"items": [
{
"id": "FAQ-101",
"title": "사내 와이파이 연결",
"keywords": [
"와이파이",
"wifi",
"무선"
],
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 잡히지 않으면 공유기 번호를 IT 포털에 신고합니다."
},
{
"id": "FAQ-102",
"title": "회의실 예약",
"keywords": [
"회의실",
"예약"
],
"answer": "사내 포털의 '회의실 예약' 메뉴에서 날짜 · 시간 · 인원을 적어 예약합니다."
},
{
"id": "FAQ-103",
"title": "",
"keywords": [
"출입증"
],
"answer": "출입증을 잃어버렸으면 총무팀에 재발급을 신청합니다."
},
{
"id": "FAQ-10",
"title": "노트북 반납",
"keywords": [
"노트북",
"반납"
],
"answer": "퇴사 5영업일 전까지 IT팀에 노트북을 반납합니다."
},
{
"id": "FAQ-105",
"title": "모니터 추가 신청",
"keywords": [
"모니터"
],
"answer": ""
},
{
"id": "FAQ-106",
"title": "회의실 예약 규칙",
"keywords": [
"회의실 규칙"
],
"answer": "회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. "
},
{
"id": "FAQ-102",
"title": "회의실 예약 (중복)",
"keywords": [
"회의실"
],
"answer": "사내 포털에서 회의실을 예약합니다. 취소도 같은 메뉴에서 합니다."
},
{
"id": "FAQ-108",
"title": "화상회의 장비",
"keywords": [],
"answer": "화상회의 장비는 각 층 비품실에서 빌릴 수 있습니다."
},
{
"id": "FAQ-109",
"title": "보안 사고 신고",
"keywords": [
"보안",
"신고"
],
"answer": "보안 사고는 IT팀 김민지(010-1234-5678)에게 바로 전화해 주세요."
},
{
"id": "FAQ-110",
"title": "사내 메신저 설치",
"keywords": [
"메신저"
],
"answer": "사내 메신저는 http://bit.ly/corp-msg 에서 받아 설치합니다."
},
{
"id": "FAQ-003",
"title": "VPN 접속 오류 (새 판)",
"keywords": [
"재택근무"
],
"answer": "VPN 클라이언트를 최신으로 업데이트하고 인증서를 확인합니다."
},
{
"id": "FAQ-112",
"title": "VPN 접속 오류",
"keywords": [
"vpn",
"재택근무"
],
"answer": "재택근무 중 VPN이 끊기면 클라이언트를 다시 시작하고 인증서 만료일을 확인합니다."
},
{
"id": "FAQ-113",
"title": "프린터 토너 교체",
"keywords": [
"토너"
],
"answer": "메일함 용량이 꽉 차면 오래된 메일을 보관함으로 옮겨 용량을 확보합니다."
}
]
}
← response HTTP 200
{
"batch": "B-1",
"total": 13,
"imported": 12,
"quarantined": 0,
"rejected": false,
"score": 0.9231,
"imported_ids": [
"FAQ-101",
"FAQ-102",
"FAQ-103",
"FAQ-10",
"FAQ-106",
"FAQ-102",
"FAQ-108",
"FAQ-109",
"FAQ-110",
"FAQ-003",
"FAQ-112",
"FAQ-113"
],
"quarantined_ids": []
}
✘ 배치 2 · 통과한 줄만 넣고 나쁜 줄은 격리 · 1.1s · quarantined_ids has none of ['FAQ-209']
→ request POST /webhook/base-faq-import
{
"batch": "B-2",
"items": [
{
"id": "FAQ-201",
"title": "회의실 예약",
"keywords": [
"회의실",
"예약"
],
"answer": "사내 포털의 '회의실 예약' 메뉴에서 날짜 · 시간 · 인원을 적어 예약합니다."
},
{
"id": "FAQ-202",
"title": "출입증 재발급",
"keywords": [
"출입증",
"재발급"
],
"answer": "출입증을 잃어버렸으면 총무팀에 신분증을 가지고 가서 재발급을 신청합니다."
},
{
"id": "FAQ-203",
"title": "노트북 반납",
"keywords": [
"노트북",
"반납"
],
"answer": "퇴사 5영업일 전까지 IT팀에 노트북과 충전기를 반납합니다."
},
{
"id": "FAQ-204",
"title": "모니터 추가 신청",
"keywords": [
"모니터",
"추가"
],
"answer": "IT 포털의 '장비 신청'에서 모니터 추가를 신청하면 재고 확인 뒤 지급합니다."
},
{
"id": "FAQ-205",
"title": "윈도우 업데이트",
"keywords": [
"업데이트",
"윈도우"
],
"answer": "윈도우 업데이트는 매달 둘째 주 수요일 밤에 자동으로 설치됩니다. 끝나면 다시 시작합니다."
},
{
"id": "FAQ-206",
"title": "화상회의 장비 대여",
"keywords": [
"화상회의",
"카메라"
],
"answer": "화상회의 카메라와 마이크는 각 층 비품실에서 빌리고 같은 날 돌려줍니다."
},
{
"id": "FAQ-207",
"title": "사내 메신저 설치",
"keywords": [
"메신저"
],
"answer": "사내 메신저는 IT 포털(https://intra.example.com/msg)에서 받아 설치합니다."
},
{
"id": "FAQ-208",
"title": "공유 드라이브 권한",
"keywords": [
"공유 드라이브",
"권한"
],
"answer": "공유 드라이브 권한은 팀장이 IT 포털에서 신청하면 하루 안에 열립니다."
},
{
"id": "FAQ-209",
"title": "보안 사고 신고",
"keywords": [
"보안",
"신고"
],
"answer": "보안 사고는 security.desk@example.com 으로 바로 메일을 보내 주세요."
},
{
"id": "FAQ-210",
"title": "모니터 밝기 조절",
"keywords": [
"밝기"
],
"answer": "연차 휴가는 입사 1년 뒤부터 15일이며 인사 포털에서 신청합니다."
}
]
}
← response HTTP 200
{
"batch": "B-2",
"total": 10,
"imported": 10,
"quarantined": 0,
"rejected": false,
"score": 1,
"imported_ids": [
"FAQ-201",
"FAQ-202",
"FAQ-203",
"FAQ-204",
"FAQ-205",
"FAQ-206",
"FAQ-207",
"FAQ-208",
"FAQ-209",
"FAQ-210"
],
"quarantined_ids": []
}
✘ FAQ에 들어갔고 메일 주소는 안 들어감 · 0.0s · faqs must not have ['security.desk']
→ request POST http://localhost:8800/faq/list
{}
← response HTTP 200
{
"count": 27,
"faqs": [
{
"id": "FAQ-001",
"title": "비밀번호 재설정",
"keywords": [
"비밀번호",
"패스워드",
"password",
"재설정",
"초기화"
],
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정합니다."
},
{
"id": "FAQ-002",
"title": "계정 잠김 해제",
"keywords": [
"계정",
"잠김",
"잠금",
"로그인",
"실패"
],
"answer": "로그인 5회 실패 시 30분 동안 잠깁니다. 30분 후 다시 시도하거나 헬프데스크에 잠금 해제를 요청합니다."
},
{
"id": "FAQ-003",
"title": "VPN 접속 오류",
"keywords": [
"vpn",
"원격",
"재택",
"접속",
"연결"
],
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다."
},
{
"id": "FAQ-004",
"title": "프린터 출력 안 됨",
"keywords": [
"프린터",
"출력",
"인쇄",
"용지"
],
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력합니다."
},
{
"id": "FAQ-005",
"title": "메일함 용량 초과",
"keywords": [
"메일",
"용량",
"메일함",
"첨부"
],
"answer": "오래된 메일과 큰 첨부 파일을 보관함으로 옮기거나 삭제해 용량을 확보합니다."
},
{
"id": "FAQ-006",
"title": "소프트웨어 설치 요청",
"keywords": [
"설치",
"소프트웨어",
"프로그램",
"라이선스"
],
"answer": "IT 포털의 '소프트웨어 신청' 메뉴에서 승인 요청을 올리면 승인 후 원격으로 설치됩니다."
},
{
"id": "FAQ-103",
"title": "",
"keywords": [
"출입증"
],
"answer": "출입증을 잃어버렸으면 총무팀에 재발급을 신청합니다."
},
{
"id": "FAQ-109",
"title": "보안 사고 신고",
"keywords": [
"보안",
"신고"
],
"answer": "보안 사고는 IT팀 김민지(010-1234-5678)에게 바로 전화해 주세요."
},
{
"id": "FAQ-110",
"title": "사내 메신저 설치",
"keywords": [
"메신저"
],
"answer": "사내 메신저는 http://bit.ly/corp-msg 에서 받아 설치합니다."
},
{
"id": "FAQ-106",
"title": "회의실 예약 규칙",
"keywords": [
"회의실 규칙"
],
"answer": "회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. 회의실은 사내 포털의 '회의실 예약' 메뉴에서 날짜와 시간을 고른 뒤 인원과 목적을 적어 예약합니다. "
},
{
"id": "FAQ-003",
"title": "VPN 접속 오류 (새 판)",
"keywords": [
"재택근무"
],
"answer": "VPN 클라이언트를 최신으로 업데이트하고 인증서를 확인합니다."
},
{
"id": "FAQ-112",
"title": "VPN 접속 오류",
"keywords": [
"vpn",
"재택근무"
],
"answer": "재택근무 중 VPN이 끊기면 클라이언트를 다시 시작하고 인증서 만료일을 확인합니다."
},
{
"id": "FAQ-10",
"title": "노트북 반납",
"keywords": [
"노트북",
"반납"
],
"answer": "퇴사 5영업일 전까지 IT팀에 노트북을 반납합니다."
},
{
"id": "FAQ-108",
"title": "화상회의 장비",
"keywords": [],
"answer": "화상회의 장비는 각 층 비품실에서 빌릴 수 있습니다."
},
{
"id": "FAQ-102",
"title": "회의실 예약 (중복)",
"keywords": [
"회의실"
],
"answer": "사내 포털에서 회의실을 예약합니다. 취소도 같은 메뉴에서 합니다."
},
{
"id": "FAQ-113",
"title": "프린터 토너 교체",
"keywords": [
"토너"
],
"answer": "메일함 용량이 꽉 차면 오래된 메일을 보관함으로 옮겨 용량을 확보합니다."
},
{
"id": "FAQ-101",
"title": "사내 와이파이 연결",
"keywords": [
"와이파이",
"wifi",
"무선"
],
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 잡히지 않으면 공유기 번호를 IT 포털에 신고합니다."
},
{
"id": "FAQ-201",
"title": "회의실 예약",
"keywords": [
"회의실",
"예약"
],
"answer": "사내 포털의 '회의실 예약' 메뉴에서 날짜 · 시간 · 인원을 적어 예약합니다."
},
{
"id": "FAQ-203",
"title": "노트북 반납",
"keywords": [
"노트북",
"반납"
],
"answer": "퇴사 5영업일 전까지 IT팀에 노트북과 충전기를 반납합니다."
},
{
"id": "FAQ-204",
"title": "모니터 추가 신청",
"keywords": [
"모니터",
"추가"
],
"answer": "IT 포털의 '장비 신청'에서 모니터 추가를 신청하면 재고 확인 뒤 지급합니다."
},
{
"id": "FAQ-205",
"title": "윈도우 업데이트",
"keywords": [
"업데이트",
"윈도우"
],
"answer": "윈도우 업데이트는 매달 둘째 주 수요일 밤에 자동으로 설치됩니다. 끝나면 다시 시작합니다."
},
{
"id": "FAQ-206",
"title": "화상회의 장비 대여",
"keywords": [
"화상회의",
"카메라"
],
"answer": "화상회의 카메라와 마이크는 각 층 비품실에서 빌리고 같은 날 돌려줍니다."
},
{
"id": "FAQ-207",
"title": "사내 메신저 설치",
"keywords": [
"메신저"
],
"answer": "사내 메신저는 IT 포털(https://intra.example.com/msg)에서 받아 설치합니다."
},
{
"id": "FAQ-202",
"title": "출입증 재발급",
"keywords": [
"출입증",
"재발급"
],
"answer": "출입증을 잃어버렸으면 총무팀에 신분증을 가지고 가서 재발급을 신청합니다."
},
{
"id": "FAQ-210",
"title": "모니터 밝기 조절",
"keywords": [
"밝기"
],
"answer": "연차 휴가는 입사 1년 뒤부터 15일이며 인사 포털에서 신청합니다."
},
{
"id": "FAQ-209",
"title": "보안 사고 신고",
"keywords": [
"보안",
"신고"
],
"answer": "보안 사고는 security.desk@example.com 으로 바로 메일을 보내 주세요."
},
{
"id": "FAQ-208",
"title": "공유 드라이브 권한",
"keywords": [
"공유 드라이브",
"권한"
],
"answer": "공유 드라이브 권한은 팀장이 IT 포털에서 신청하면 하루 안에 열립니다."
}
]
}
✘ 격리함에 이유와 함께 · 0.0s · items has none of ['"pii"']
→ request POST http://localhost:8800/quarantine/list
{}
← response HTTP 200
{
"size": 0,
"items": []
}
✔ before: POST http://localhost:8800/faq-reset · HTTP 200 · 0.0s · {"cleared": 21}
→ request POST http://localhost:8800/faq-reset
{}
← response HTTP 200
{
"cleared": 21
}
✔ before: POST http://localhost:8800/quarantine-clear · HTTP 200 · 0.0s · {"cleared": 0}
→ request POST http://localhost:8800/quarantine-clear
{}
← response HTTP 200
{
"cleared": 0
}
✘ LLM · 제목과 답이 다른 줄 (FAQ-210) · 1.1s · imported = 10 (expected 8); quarantined = 0 (expected 2); codes.llm_mismatch = null (expected 1); quarantined_ids has none of ['FAQ-210']
→ request POST /webhook/base-faq-import
{
"batch": "B-2",
"items": [
{
"id": "FAQ-201",
"title": "회의실 예약",
"keywords": [
"회의실",
"예약"
],
"answer": "사내 포털의 '회의실 예약' 메뉴에서 날짜 · 시간 · 인원을 적어 예약합니다."
},
{
"id": "FAQ-202",
"title": "출입증 재발급",
"keywords": [
"출입증",
"재발급"
],
"answer": "출입증을 잃어버렸으면 총무팀에 신분증을 가지고 가서 재발급을 신청합니다."
},
{
"id": "FAQ-203",
"title": "노트북 반납",
"keywords": [
"노트북",
"반납"
],
"answer": "퇴사 5영업일 전까지 IT팀에 노트북과 충전기를 반납합니다."
},
{
"id": "FAQ-204",
"title": "모니터 추가 신청",
"keywords": [
"모니터",
"추가"
],
"answer": "IT 포털의 '장비 신청'에서 모니터 추가를 신청하면 재고 확인 뒤 지급합니다."
},
{
"id": "FAQ-205",
"title": "윈도우 업데이트",
"keywords": [
"업데이트",
"윈도우"
],
"answer": "윈도우 업데이트는 매달 둘째 주 수요일 밤에 자동으로 설치됩니다. 끝나면 다시 시작합니다."
},
{
"id": "FAQ-206",
"title": "화상회의 장비 대여",
"keywords": [
"화상회의",
"카메라"
],
"answer": "화상회의 카메라와 마이크는 각 층 비품실에서 빌리고 같은 날 돌려줍니다."
},
{
"id": "FAQ-207",
"title": "사내 메신저 설치",
"keywords": [
"메신저"
],
"answer": "사내 메신저는 IT 포털(https://intra.example.com/msg)에서 받아 설치합니다."
},
{
"id": "FAQ-208",
"title": "공유 드라이브 권한",
"keywords": [
"공유 드라이브",
"권한"
],
"answer": "공유 드라이브 권한은 팀장이 IT 포털에서 신청하면 하루 안에 열립니다."
},
{
"id": "FAQ-209",
"title": "보안 사고 신고",
"keywords": [
"보안",
"신고"
],
"answer": "보안 사고는 security.desk@example.com 으로 바로 메일을 보내 주세요."
},
{
"id": "FAQ-210",
"title": "모니터 밝기 조절",
"keywords": [
"밝기"
],
"answer": "연차 휴가는 입사 1년 뒤부터 15일이며 인사 포털에서 신청합니다."
}
]
}
← response HTTP 200
{
"batch": "B-2",
"total": 10,
"imported": 10,
"quarantined": 0,
"rejected": false,
"score": 1,
"imported_ids": [
"FAQ-201",
"FAQ-202",
"FAQ-203",
"FAQ-204",
"FAQ-205",
"FAQ-206",
"FAQ-207",
"FAQ-208",
"FAQ-209",
"FAQ-210"
],
"quarantined_ids": []
}
✘ LLM · 규칙에 걸린 줄은 그대로 (FAQ-305) · 0.1s · imported = 6 (expected 5); quarantined_ids has none of ['FAQ-305']
→ request POST /webhook/base-faq-import
{
"batch": "B-3",
"items": [
{
"id": "FAQ-301",
"title": "택배 수령",
"keywords": [
"택배"
],
"answer": "회사로 온 택배는 1층 안내 데스크에서 사원증을 보여 주고 받습니다."
},
{
"id": "FAQ-302",
"title": "주차 등록",
"keywords": [
"주차"
],
"answer": "주차 등록은 총무팀 포털에서 차량 번호를 적어 신청합니다."
},
{
"id": "FAQ-303",
"title": "명함 신청",
"keywords": [
"명함"
],
"answer": "명함은 인사 포털에서 신청하면 일주일 안에 자리로 배달됩니다."
},
{
"id": "FAQ-304",
"title": "사내 식당 메뉴",
"keywords": [
"식당",
"메뉴"
],
"answer": "사내 식당 메뉴는 매주 월요일 아침 사내 게시판에 올라옵니다."
},
{
"id": "FAQ-305",
"title": "교육 신청",
"keywords": [
"교육"
],
"answer": "짧"
},
{
"id": "FAQ-306",
"title": "건강검진",
"keywords": [
"건강검진"
],
"answer": "건강검진은 매년 9월에 인사팀이 안내하며 지정 병원에서 받습니다."
}
]
}
← response HTTP 200
{
"batch": "B-3",
"total": 6,
"imported": 6,
"quarantined": 0,
"rejected": false,
"score": 1,
"imported_ids": [
"FAQ-301",
"FAQ-302",
"FAQ-303",
"FAQ-304",
"FAQ-305",
"FAQ-306"
],
"quarantined_ids": []
}
Passed 0/6
report: runs/lab02-20261101-180552.json
✔ after: POST http://localhost:8800/faq-reset · HTTP 200 · 0.0s · {"cleared": 16}
→ request POST http://localhost:8800/faq-reset
{}
← response HTTP 200
{
"cleared": 16
}
$ uv run tools/flow.py quality
FAQ: 6 (nothing added)
quarantine: 0
quality log: 0
alerts: 0