컨포트랩
경력근거: 대부분의 제조 현장은 여전히 작업자의 경험, 수기 기록, 엑셀 취합, 사후 보고에 의존합니다.
컨포트랩의 FDE 는 고객 공장에 들어가 시공을 감리하고 PLC·센서의 데이터 연동을 확인한 뒤 시운영까지 책임지는 자리라서, 연동이 진짜로 맞는지 판정하는 일이 프로젝트의 마지막 관문입니다. 회사가 문제로 지목한 것이 바로 수기 기록과 엑셀 취합이라, 그 수기 일지를 버릴 자료가 아니라 자동 수집값을 검증할 대조 기준으로 쓰는 각도는 PORTA 를 중소 제조 현장에 올리는 맥락이 있어야 나옵니다. 설비 이름이 사람마다 다르게 적히고 값 칸이 비어 있고 단위가 섞여 있는 엑셀이 입력이라는 전제도 그 현장에서 나옵니다. 이 포트폴리오가 보여주는 것은 이종 기록 대조 판정 규칙과 근거 인용 검증과 문서 자동 생성 기술이며 제조 공정과 설비에 대한 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
현장이 수기 기록과 엑셀 취합에 기대고 있다는 문제 인식이 회사 소개의 첫 문단이고 FDE 업무도 어떤 일이 수기·엑셀에 의존하는지 분석하는 데서 시작하므로, 그 수기 기록 자체를 입력으로 받아 자동 수집값과 대조하는 것을 제품의 골조로 둡니다.
현장 시공을 감리하고 PLC·센서의 데이터 연동을 확인해 시운영까지 끌고 가는 것이 업무 목록에 그대로 적혀 있어서, 연동이 제대로 됐는지 판정하는 규칙과 재시공 요청 근거를 만드는 쪽으로 산출물을 잡았습니다.
우대 경험이 PLC 와 Modbus·OPC-UA 같은 산업용 통신과 센서 연동에 몰려 있어서, 태그 아이디와 품질 코드와 수집 시각을 다루는 데이터 모양을 1주차부터 그대로 쓰고 3주차에 OPC-UA 로 직접 읽는 어댑터까지 붙입니다.
PORTA 를 올린 고객 공장의 시운영 단계에서, 작업자가 엑셀로 적어 온 수기 점검일지와 시공 뒤 PLC·센서에서 자동 수집된 태그 로그와 시공 파트너가 넘긴 태그 매핑표 세 파일을 읽어, 같은 설비의 같은 항목을 같은 시각 창 안에서 대조하는 CLI입니다. 터미널에서 verify 명령에 점검 일자를 주면 값이 허용오차를 넘은 항목과 수집이 비어 있는 항목과 매핑표에 없는 항목이 판정 근거를 달고 정리된 시운영 계측 검증 보고서 Markdown 파일이 생기고, 표준출력에는 설비별 요약 표와 미해결 건수가 찍힙니다. 판정마다 일지의 몇 번째 행과 어느 태그 로그를 근거로 삼았는지 함께 적어, 시공 파트너에게 재시공을 요청할 때 담당자가 바로 근거를 내밀게 합니다.
시공이 끝난 고객 공장에 나가 데이터 연동이 제대로 됐는지 확인해야 하는데, 작업자가 적어 온 점검일지 엑셀과 수집 서버에서 내려받은 태그 로그를 화면 양쪽에 띄워 놓고 설비 이름부터 눈으로 맞춰 보다가, 배율이 잘못 잡힌 센서 하나를 시운영이 한참 지나 발견했던 프로젝트 담당자가, 터미널에서 명령 한 번을 치고 어긋난 항목과 근거가 정리된 검증 보고서를 받아 그대로 시공 파트너에게 보내고 싶어 합니다.
수기 점검일지와 태그 로그는 결국 같은 설비의 같은 항목을 다른 경로로 적은 숫자라서, 태그 매핑표의 단위와 배율과 허용오차와 시각 창을 기준으로 두 값을 계산으로 맞춰 보면 연동 결함의 대부분인 매핑 누락과 수집 공백과 배율 오설정이 사람 눈 대신 규칙으로 잡힌다는 가정입니다.
수기 점검일지와 태그 로그와 태그 매핑표 더미 세 파일을 놓고 터미널에서 verify 명령에 점검 일자를 주면, 단위와 배율을 환산해 허용오차를 넘은 항목과 시각 창 안에 수집이 없는 항목과 매핑표에 없는 항목을 판정해 설비별로 정리한 시운영 계측 검증 보고서 Markdown 파일을 쓰고 표준출력에 요약 표를 찍는 CLI가 도는 완결품입니다. 만드는 덩어리는 대조 판정 규칙과 보고서 생성 둘뿐이고 서버와 화면이 없어 평일 닷새에 혼자 끝나는 범위이며, 데이터는 실제 고객 자료 대신 작성자가 만든 더미를 씁니다.
1주차 보고서를 입력으로, explain 하위 명령이 점검일지 비고란의 자유서술에서 승온 중이었다거나 라인을 세우고 잰 값이라는 예외 사유 후보와 그 원문 인용을 뽑아 보고서의 확인 필요 절에 붙이고, 뽑힌 인용이 원문 비고에 실재하는 문자열인지 기계로 대조해 실재하지 않는 항목을 버리는 추출 계층입니다. 예외 사유가 붙어도 판정 자체는 바뀌지 않고 사람이 확인할 표시만 달립니다.
시운영 회차별 판정 결과를 SQLite 에 쌓고 diff 하위 명령이 지난 회차 대비 해소된 항목과 그대로 남은 항목과 새로 생긴 항목을 계산해 재시공 요청 목록으로 뽑는 확장입니다. 여기에 파일 입력 대신 OPC-UA 시뮬레이터에 붙어 태그 값을 직접 읽어 오는 어댑터를 더해, 수집 경로가 파일이든 통신이든 같은 판정 규칙을 태우는 구조를 보입니다.
점검일지 13행과 태그 로그 12건과 매핑표 10행을 놓고 터미널에서 verify 명령에 점검 일자를 주면, 값이 어긋난 항목과 수집이 빈 항목과 매핑이 없는 항목이 일지 행 번호와 태그 값 근거를 달고 실린 검증 보고서 Markdown 파일이 생기고 표준출력에 설비별 요약 표가 찍히는 흐름을 시연합니다.
값 칸이 비었거나 숫자 앞에 약 자가 붙었거나 소수점이 콤마로 적힌 행과 설비명 표기가 흔들리는 행과 매핑표에 없는 설비·항목과 품질 코드가 불량인 수집값을 섞은 입력 30종을 넣어도 도구가 죽지 않고 확인 필요 절로 분리하는 것을 보입니다. 다만 데이터와 정답을 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
점검 일자를 고정하면 같은 입력에서 언제나 같은 보고서 파일이 나오는 결정론을 테스트로 보이고, 미해결 항목이 하나라도 남으면 종료 코드로 알려 다른 스크립트가 이어받게 합니다.
2주차에는 규칙 판정과 작성자가 다시 확인한 라벨의 일치율을 재고, 추출된 예외 사유 전건을 원문 문자열 대조로 확인해 실재하지 않는 인용 0건을 통과 기준으로 삼습니다. 이 수치 역시 원문과 라벨을 작성자가 만든 합성 조건에서의 값입니다.
위 수치는 더미데이터 자가채점이며, 실제 검증 정확도는 컨포트랩이 시공한 공장의 실제 점검일지와 수집 서버 로그와 현장 담당자의 확인이 있어야 측정 가능합니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모가 도는 상태와 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 값이 비거나 텍스트가 섞인 칸과 설비명 표기 흔들림과 매핑표에 없는 항목과 천 배로 어긋난 값을 더미에 일부러 섞어 두었습니다.
값이 어긋났다고 해서 원인이 센서 고장인지 배율 설정인지 작업자의 측정 실수인지는 이 도구가 단정하지 않습니다. 어긋난 사실과 근거만 적고 원인 판단은 현장 확인 항목으로 넘깁니다. 십진 배수 비율로 배율 오설정을 의심하는 규칙도 우연히 열 배가 난 실제 이상을 배율 문제로 잘못 몰 수 있어 확인 필요 표시를 함께 답니다.
이 포트폴리오가 보여주는 것은 이종 기록 대조 판정과 근거 인용 검증과 문서 자동 생성 기술이며, 공정 조건이나 설비 진단 같은 제조 도메인 깊이는 주장하지 않습니다. 어느 정도 차이가 정상 변동인지는 현장 엔지니어가 정해 주어야 하고, 모르는 부분은 면접에서 모른다고 말합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객 공장 작업자가 엑셀로 적어 온 수기 점검일지 더미입니다. CLI가 이 행을 기준으로 같은 설비의 같은 항목 수집값을 찾아 대조합니다. 값 칸이 문자열인 것은 엑셀에서 사람이 적은 그대로를 받기 때문입니다.
| log_id | sheet_row | logged_at | equip_raw | item_raw | value_raw | unit_raw | inspector | note |
|---|---|---|---|---|---|---|---|---|
| L-001 | 7 | 2026-07-27T09:05:00 | 1호 컴프레서 | 토출압력 | 7.2 | bar | 김점검 | — |
| L-002 | 8 | 2026-07-27T09:10:00 | 1호 컴프레서 | 토출온도 | 82.0 | ℃ | 김점검 | — |
| L-003 | 9 | 2026-07-27T09:20:00 | 2호 건조로 | 내부온도 | 148 | ℃ | 김점검 | 승온 중이라 아직 오르는 값입니다 |
| L-004 | 10 | 2026-07-27T09:30:00 | 냉각수 펌프A | 유량 | 12,5 | ㎥/h | 박현장 | — |
| L-005 | 11 | 2026-07-27T09:35:00 | 냉각수펌프 A | 흡입압력 | 1.8 | bar | 박현장 | — |
아래 JSON Schema에 맞는 수기 점검일지 행을 25건 더 만들어 주세요. {"type":"object","properties":{"log_id":{"type":"string"},"sheet_row":{"type":"integer"},"logged_at":{"type":["string","null"]},"equip_raw":{"type":"string"},"item_raw":{"type":"string"},"value_raw":{"type":"string"},"unit_raw":{"type":["string","null"]},"inspector":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["log_id","sheet_row","equip_raw","item_raw","value_raw"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 value_raw 가 빈 문자열인 미기입 행, 둘째 value_raw 에 약 자나 물결표나 단위가 함께 적혀 숫자만 뽑아야 하는 행, 셋째 소수점이 콤마로 적힌 행, 넷째 같은 설비를 공백과 괄호와 옛 이름으로 다르게 적은 표기 흔들림 행, 다섯째 매핑표에 없는 점검 항목이나 없는 설비를 적은 행, 여섯째 logged_at 이 null 이라 시각을 알 수 없는 행, 일곱째 note 에 승온 중이라거나 라인을 세우고 쟀다는 사정이 적힌 행. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"log_id":{"type":"string"},"sheet_row":{"type":"integer"},"logged_at":{"type":["string","null"]},"equip_raw":{"type":"string"},"item_raw":{"type":"string"},"value_raw":{"type":"string"},"unit_raw":{"type":["string","null"]},"inspector":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["log_id","sheet_row","equip_raw","item_raw","value_raw"]}시공 뒤 PLC·센서에서 자동 수집된 태그 로그 더미입니다. CLI가 점검일지 행의 시각 창 안에서 같은 태그의 값을 골라 대조 상대로 씁니다.
| reading_id | tag_id | ts | value | quality |
|---|---|---|---|---|
| R-001 | CMP1.PRESS_OUT | 2026-07-27T09:06:00 | 7.25 | good |
| R-002 | CMP1.TEMP_OUT | 2026-07-27T09:11:00 | 81.4 | good |
| R-003 | DRY2.TEMP_IN | 2026-07-27T09:22:00 | 165.2 | good |
| R-004 | PMP_A.FLOW | 2026-07-27T09:31:00 | 12.4 | good |
| R-005 | PMP_A.PRESS_IN | 2026-07-27T09:36:00 | 1,810 | good |
아래 JSON Schema에 맞는 태그 수집 행을 30건 더 만들어 주세요. {"type":"object","properties":{"reading_id":{"type":"string"},"tag_id":{"type":"string"},"ts":{"type":"string"},"value":{"type":["number","null"]},"quality":{"type":"string","enum":["good","uncertain","bad"]}},"required":["reading_id","tag_id","ts","quality"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 매핑표에 있는 태그인데 그 시각 창 안에 수집이 하나도 없는 구간, 둘째 quality 가 bad 나 uncertain 인 값, 셋째 value 가 null 인 행, 넷째 수기 값과 정확히 천 배나 백 배 어긋나 배율 오설정으로 보이는 행, 다섯째 시각 창 경계에 걸쳐 포함 여부가 갈리는 행, 여섯째 같은 태그가 몇 초 간격으로 여러 건 올라와 어느 값을 고를지 정해야 하는 구간. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"reading_id":{"type":"string"},"tag_id":{"type":"string"},"ts":{"type":"string"},"value":{"type":["number","null"]},"quality":{"type":"string","enum":["good","uncertain","bad"]}},"required":["reading_id","tag_id","ts","quality"]}시공 파트너가 넘긴 태그 매핑표 더미입니다. 어느 설비의 어느 점검 항목이 어느 태그이고 단위와 배율과 허용오차와 시각 창이 얼마인지 담아, 대조 판정의 기준이자 검증 대상이 됩니다.
| map_id | equip_label | item | tag_id | unit | scale | tolerance_abs | window_min |
|---|---|---|---|---|---|---|---|
| MP-01 | 1호 컴프레서 | 토출압력 | CMP1.PRESS_OUT | bar | 1 | 0.3 | 10 |
| MP-02 | 1호 컴프레서 | 토출온도 | CMP1.TEMP_OUT | ℃ | 1 | 1.5 | 10 |
| MP-03 | 2호 건조로 | 내부온도 | DRY2.TEMP_IN | ℃ | 1 | 3 | 15 |
| MP-04 | 냉각수 펌프A | 유량 | PMP_A.FLOW | ㎥/h | 1 | 1 | 10 |
| MP-05 | 냉각수 펌프A | 흡입압력 | PMP_A.PRESS_IN | bar | 0.001 | 0.2 | 10 |
아래 JSON Schema에 맞는 태그 매핑 행을 20건 더 만들어 주세요. {"type":"object","properties":{"map_id":{"type":"string"},"equip_label":{"type":"string"},"item":{"type":"string"},"tag_id":{"type":"string"},"unit":{"type":"string"},"scale":{"type":"number"},"tolerance_abs":{"type":["number","null"]},"window_min":{"type":"integer"}},"required":["map_id","equip_label","item","tag_id","unit","scale","window_min"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 tolerance_abs 가 null 이라 허용오차 기준이 정해지지 않은 행, 둘째 scale 이 0.001 이나 1000 처럼 단위 환산이 필요한 행, 셋째 같은 설비의 같은 항목이 태그만 다르게 두 번 적힌 중복 행, 넷째 equip_label 이 점검일지 표기와 공백이나 괄호만 다른 행, 다섯째 window_min 이 지나치게 짧아 대조할 수집값이 잘 잡히지 않는 행, 여섯째 태그 로그에는 한 번도 나타나지 않는 태그를 가리키는 행. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"map_id":{"type":"string"},"equip_label":{"type":"string"},"item":{"type":"string"},"tag_id":{"type":"string"},"unit":{"type":"string"},"scale":{"type":"number"},"tolerance_abs":{"type":["number","null"]},"window_min":{"type":"integer"}},"required":["map_id","equip_label","item","tag_id","unit","scale","window_min"]}| PLC · 센서 연동 | 태그 로그 입력을 PLC 수집기가 실제로 내려주는 모양인 태그 아이디와 수집 시각과 값과 품질 코드로 맞춰, 현장 수집 서버에서 받은 파일을 그대로 물릴 수 있게 합니다. 품질 코드가 불량인 값은 대조에서 빼고 별도 항목으로 보고합니다. |
| 산업용 통신(Modbus · OPC-UA) | 1주차는 수집 결과 파일을 읽는 것으로 끝내고, 3주차에 OPC-UA 시뮬레이터에 붙어 태그를 직접 읽어 오는 어댑터를 붙여 파일 경로와 통신 경로가 같은 판정 규칙을 타게 합니다. 실장비 없이 시뮬레이터로 확인하는 범위임을 보고서에 적습니다. |
| 산업 IoT · 데이터 수집 | 수기 시각과 수집 시각이 정확히 같을 수 없다는 전제로 항목별 시각 창을 두고 그 안의 값을 골라 대조하며, 값이 비어 있거나 품질이 나쁜 경우를 판정 결과로 남깁니다. |
| SI 프로젝트 · 시공 감리 | 태그 매핑표를 제가 만드는 것이 아니라 시공 파트너의 산출물로 받아 검증 대상으로 두고, 매핑이 빠졌거나 배율이 어긋난 항목을 재시공 요청 목록으로 뽑아 감리 산출물이 되게 합니다. |
| 노코드 활용 | 허용오차와 시각 창과 배율 같은 판정 기준을 코드에서 빼내 매핑표 열로 두어, 현장 담당자가 표 한 장만 고쳐도 판정이 바뀌게 합니다. PORTA 노코드 환경에 규칙을 옮기기 전 단계의 모양입니다. |
| 구현 언어(Python) | 공고가 특정 기술 스택보다 이해력과 문제 해결을 본다고 밝혀 스택 지정이 없으므로, 엑셀과 CSV 를 그대로 읽고 표 계산이 편한 Python 으로 만들고 판정 규칙에는 테스트를 붙입니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차에서 비고란의 예외 사유를 인용해 붙입니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 3주차에서 회차를 쌓아 해소·잔존을 계산하고 OPC-UA 어댑터를 붙입니다.
시운영 계측 검증 보고서를 이루는 판정 한 건입니다. 어느 규칙으로 판정했고 일지의 몇 번째 행과 어느 수집값을 근거로 삼았는지 담아, 시공 파트너에게 재시공을 요청할 때 담당자가 바로 근거를 내밀게 합니다.
{"type":"object","properties":{"equip_label":{"type":"string"},"item":{"type":"string"},"log_id":{"type":"string"},"tag_id":{"type":["string","null"]},"verdict":{"type":"string","enum":["match","value_gap","scale_suspect","no_reading","bad_quality","unmapped","unreadable_entry"]},"basis":{"type":"string","enum":["rule_tolerance","rule_ratio","rule_window","rule_quality","rule_mapping","rule_parse","llm_note"]},"manual_value":{"type":["number","null"]},"tag_value_scaled":{"type":["number","null"]},"gap":{"type":["number","null"]},"tolerance_abs":{"type":["number","null"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"quote":{"type":"string"}},"required":["field","quote"]}},"needs_human_review":{"type":"boolean"},"hedge":{"type":["string","null"]}},"required":["equip_label","item","log_id","verdict","basis","evidence","needs_human_review"]}[
{
"equip_label": "2호 건조로",
"item": "내부온도",
"log_id": "L-003",
"tag_id": "DRY2.TEMP_IN",
"verdict": "value_gap",
"basis": "rule_tolerance",
"manual_value": 148,
"tag_value_scaled": 165.2,
"gap": 17.2,
"tolerance_abs": 3,
"evidence": [
{
"field": "log.sheet_row",
"quote": "9"
},
{
"field": "reading.value",
"quote": "165.2"
}
],
"needs_human_review": false,
"hedge": null
},
{
"equip_label": "냉각수 펌프A",
"item": "유량",
"log_id": "L-013",
"tag_id": "PMP_A.FLOW",
"verdict": "scale_suspect",
"basis": "rule_ratio",
"manual_value": 12.6,
"tag_value_scaled": 12600,
"gap": 12587.4,
"tolerance_abs": 1,
"evidence": [
{
"field": "reading.value",
"quote": "12600"
},
{
"field": "mapping.scale",
"quote": "1"
}
],
"needs_human_review": true,
"hedge": "수집값이 수기값의 천 배라 배율 설정이 리터 단위로 잡혔을 가능성이 큽니다. 실제 원인은 현장에서 확인해야 합니다."
},
{
"equip_label": "공기압축기 2호",
"item": "토출압력",
"log_id": "L-008",
"tag_id": "CMP2.PRESS_OUT",
"verdict": "no_reading",
"basis": "rule_window",
"manual_value": 6.9,
"tag_value_scaled": null,
"gap": null,
"tolerance_abs": 0.3,
"evidence": [
{
"field": "log.logged_at",
"quote": "2026-07-27T10:05:00"
},
{
"field": "mapping.window_min",
"quote": "10"
}
],
"needs_human_review": false,
"hedge": null
},
{
"equip_label": "1호 컴프레서",
"item": "소음",
"log_id": "L-011",
"tag_id": null,
"verdict": "unmapped",
"basis": "rule_mapping",
"manual_value": 72,
"tag_value_scaled": null,
"gap": null,
"tolerance_abs": null,
"evidence": [
{
"field": "log.item_raw",
"quote": "소음"
}
],
"needs_human_review": true,
"hedge": "매핑표에 없는 점검 항목이라 수집 대상에서 빠진 것인지 항목 이름이 다른 것인지 시공 파트너에게 확인해야 합니다."
},
{
"equip_label": "포장라인 컨베이어",
"item": "속도",
"log_id": "L-006",
"tag_id": "PKG.CONV_SPD",
"verdict": "unreadable_entry",
"basis": "rule_parse",
"manual_value": null,
"tag_value_scaled": 0,
"gap": null,
"tolerance_abs": 0.5,
"evidence": [
{
"field": "log.value_raw",
"quote": ""
},
{
"field": "log.note",
"quote": "라인 정지 중이라 못 쟀습니다"
}
],
"needs_human_review": true,
"hedge": "수기 값이 비어 있어 대조하지 않았습니다. 비고에 적힌 사정은 2주차 추출 계층이 인용으로 붙인 것이며 판정을 바꾸지 않습니다."
}
][자동 검증] 1. 판정 JSON 이 위 스키마를 통과하는지 확인합니다. verdict 나 basis 가 정해진 값 밖이면 그 항목을 버립니다. 2. basis 가 rule_tolerance 나 rule_ratio 인 항목은 manual_value 와 tag_value_scaled 로 gap 을 다시 계산해 기록된 값과 일치하는지 대조합니다. 계산 결과와 근거가 어긋나면 재시공 요청 전체를 신뢰할 수 없기 때문입니다. 3. evidence 의 quote 가 입력 파일의 실제 필드 값과 문자 그대로 일치하는지 확인합니다. 지어낸 근거가 붙은 항목은 버립니다. 4. tag_value_scaled 가 태그 원값에 매핑표 scale 을 곱한 값인지 재계산해 단위 환산이 규칙대로 됐는지 확인합니다. 5. verdict 가 no_reading 인 항목은 그 태그의 수집이 정말로 시각 창 안에 없는지 원본 로그를 훑어 다시 확인합니다. 6. basis 가 llm_note 인 항목은 quote 가 비고란 원문에 실재하는 문자열인지 대조하고, needs_human_review 가 true 인지 확인합니다. 사람 말에서 뽑은 사정은 확인 표시 없이 보고서에 실리지 않습니다. 7. 같은 입력과 같은 점검 일자로 두 번 실행한 보고서 파일이 문자 그대로 같은지 비교합니다. 결정론이 깨지면 규칙에 시각 의존 버그가 들어간 것입니다. [수동 검증] 1. 생성된 보고서 한 부를 시운영 담당자 관점에서 읽고, 시공 파트너에게 그대로 보내도 되는 문장인지 봅니다. 2. scale_suspect 로 잡힌 항목이 정말 배율 문제인지, 아니면 설비가 실제로 그만큼 어긋난 것인지 원본 값과 함께 봅니다. 3. 표기 흔들림 때문에 다른 설비로 잘못 이어진 행이 없는지 매핑 결과를 훑습니다. 잘못 이어졌다면 조회 규칙의 과매칭 사례로 기록하고 다듬습니다.