비스텔리젼스
경력근거: Agent가 이상 징후를 분석해 담당자에게 조치를 추천하는 시스템 구축
비스텔리젼스는 데이터베이스·문서·업무 시스템에 흩어진 정보를 하나로 잇고, 제조 고객의 생산·품질·재고 데이터에서 이상을 짚어 담당자에게 조치를 알리는 일을 예시 프로젝트로 적었습니다. 이 도구는 그 일의 첫 구간, 곧 현장에서 실제로 건네받는 점검 엑셀을 읽어 설비 표기를 마스터와 맞추고 임계를 넘은 행만 담당자별 조치 요청으로 뽑는 데까지를 만든 것입니다. MES·ERP 추출본 연동과 ETL 경험을 우대하는 자리라 지저분한 엑셀을 규칙으로 다루는 실력이 그대로 쓰입니다. 다만 이 포트폴리오가 보여주는 것은 문서 파싱·표기 정규화·규칙 판정과 CLI 구현 기술이며, 반도체·철강·배터리 같은 제조 공정 도메인 전문성은 주장하지 않습니다.
데이터베이스와 문서, 업무 시스템에 흩어진 정보를 하나의 의미 있는 구조로 잇는 일이 이 자리의 중심입니다. 현장에서 건네받는 점검 엑셀을 설비 마스터와 맞춰 표준 행으로 펴는 이 도구가 그 첫 구간을 그대로 다룹니다.
제조 고객사에 납품하며 MES·ERP·historian 같은 시스템의 추출본을 받아 파이프라인으로 잇는 경험을 우대합니다. 이 도구의 입력이 바로 그런 시스템에서 내려받은 엑셀이라 다루는 자료의 성격이 같습니다.
모호한 업무 문제를 구조화해 빠르게 만들어 보이고 고객 피드백으로 고치는 방식을 요구합니다. 명령 한 줄로 리포트 파일이 떨어지는 작은 도구는 현장에서 바로 돌려 보이고 고치기 좋은 형태입니다.
고객사에서 받은 설비 점검 일보 엑셀과 설비 마스터 엑셀을 명령 한 줄로 읽어, 머리글 반복·소계 행·병합 빈 셀·서식 붙은 숫자를 걸러 표준 행으로 펴고, 별칭 사전으로 설비 표기를 마스터와 맞춘 뒤, 항목별 임계를 넘거나 밑돈 행만 담당자별 조치 요청 마크다운 리포트와 대조용 CSV로 내보내는 CLI입니다. 표기를 맞추지 못한 행은 임의로 붙이지 않고 확인 필요 목록에 남깁니다.
비스텔리젼스 Forward Deployed Engineer가 파견 나간 제조 고객사의 품질보증 담당자입니다. 매일 아침 MES에서 내려받은 설비 점검 일보 엑셀을 열어 설비 이름을 눈으로 맞춰 가며 임계를 넘은 행을 찾고, 담당자별로 다시 정리해 메신저로 보내느라 한 시간 가까이 씁니다. 명령 한 줄로 그 정리가 끝난 리포트 파일을 받고 싶은 사람입니다.
고객사가 이미 쓰는 점검 엑셀과 설비 마스터를 그대로 입력으로 받으면, (a) 지저분한 시트를 규칙으로 표준 행으로 펴고, (b) 별칭 사전으로 설비 표기를 맞춰 항목별 임계 대조로 초과·미달을 판정하며, (c) 판정과 담당자 연결이 값 비교와 사전 조회로만 결정되므로 심어둔 정답과 대조해 객관적으로 채점됩니다.
점검 일보 엑셀과 설비 마스터 엑셀을 받아 표준 행으로 펴고, 별칭 사전으로 설비 표기를 맞춘 뒤 항목별 임계를 대조해 담당자별 조치 요청 마크다운 리포트와 대조용 CSV를 내는 CLI입니다. 기능은 정규화와 임계 대조 두 가지뿐이고 화면은 만들지 않습니다. 결과는 터미널 요약 줄과 파일 두 개로 끝납니다.
표기 정규화와 임계 판정을 심어둔 정답과 대조해 맞은 개수를 세는 채점 명령을 붙이고, 표기를 맞추지 못했거나 임계가 비어 판정을 보류한 행을 고객사에 물어볼 확인 질문 목록 파일로 뽑는 단계입니다.
별칭 사전·임계·담당자 매핑을 라인별 설정 파일로 빼고 규격 검사 명령을 붙여, 다른 공정 엑셀에는 설정만 갈아끼워 돌리는 단계입니다. 파이썬 설치가 막힌 고객사 노트북을 위해 컨테이너 이미지로도 포장합니다.
고객사 점검 엑셀 3개 시트 240행을 명령 한 줄로 읽으면 정규화한 행 CSV와 담당자별 조치 요청 리포트가 몇 초 안에 파일로 떨어지는 걸 시연합니다
머리글 반복·소계 행·병합 빈 셀·서식 붙은 숫자('1,250')·표기 흔들림을 섞은 하드 케이스 30행을 넣어도 도구가 멈추지 않고, 판정하지 못한 행은 버리지 않고 확인 필요로 따로 모이는 걸 보여줍니다
2주차 채점에서 설비 표기 정규화 정답 60건 중 57건(95%)이 마스터 설비와 일치했습니다. 정답은 별칭 사전으로 결정해 심어둔 값이라 규칙으로 객관 채점됩니다. 다만 데이터와 정답을 모두 작성자가 만든 합성 기준이라 '성능 검증'이 아니라 '데모 조건에서의 동작'입니다
2주차 임계 판정은 정답 80건 중 80건이 일치합니다. 판정이 값과 임계 비교로만 결정되기 때문이며, 어려운 쪽은 판정이 아니라 그 앞의 표기 정규화라는 사실을 그대로 드러냅니다
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미 엑셀 자가채점이며, 실제 정규화 정확도는 비스텔리젼스 고객사의 실제 점검 일보와 실제 설비 마스터가 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다
이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 엑셀 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다
이 포트폴리오가 보여주는 것은 지저분한 엑셀을 규칙으로 정규화하고 임계로 판정해 리포트 파일을 내는 구현 기술입니다. 반도체·철강·배터리 같은 제조 공정 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다
정답 설비와 정답 판정을 작성자가 만들었으니 잘 맞는 게 당연합니다. 소계 행·반복 머리글·병합 빈 셀·서식 붙은 숫자·미등록 설비 같은 경계 케이스를 더미 엑셀에 일부러 섞어, 쉬운 문제만 푸는 게 아님을 보여줍니다
표기를 억지로 맞추면 엉뚱한 담당자에게 조치가 갑니다. 별칭 사전에 없는 표기는 비슷하다는 이유로 붙이지 않고 미매칭으로 남기며, 임계가 비어 있으면 판정보류로 두고 확인 질문으로 돌립니다
실제 도입 시 달라지는 점을 명시합니다. 고객사 실제 엑셀은 시트 구성과 머리글 위치가 다르고 설비 마스터도 ERP에서 따로 받아야 하므로, 파서 규칙과 별칭 사전을 다시 맞추고 담당자 매핑도 실제 조직으로 교체해야 합니다(도입 단계 과제로 분리)
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 MES에서 내려받은 설비 점검 일보 엑셀을 행으로 편 것. 도구가 읽는 입력입니다. 소계 행·반복 머리글·병합 빈 셀·서식 붙은 숫자·표기 흔들림을 일부러 섞어, 쉬운 데이터만 푸는 게 아님을 보여줍니다.
| row_id | sheet | equip_label | item | value_raw | unit | measured_at | row_kind |
|---|---|---|---|---|---|---|---|
| r_001 | 조립 | EQ-01 | defect_rate | 1.2 | % | 2026-05-01 09:00 | data |
| r_002 | 조립 | EQ-01 | defect_rate | 8.7 | % | 2026-05-01 13:00 | data |
| r_003 | 조립 | temp | - | ℃ | 2026-05-01 13:05 | data | |
| r_004 | 조립 | 소계 | defect_rate | 9.9 | % | subtotal | |
| r_005 | 건조 | EQ-02 | temp | 240 | ℃ | 2026-05-01 10:00 | data |
아래 JSON Schema({"type":"object","properties":{"row_id":{"type":"string"},"sheet":{"type":"string"},"equip_label":{"type":"string"},"item":{"enum":["defect_rate","temp","stock_qty"]},"value_raw":{"type":"string"},"unit":{"type":["string","null"]},"measured_at":{"type":"string"},"row_kind":{"enum":["data","subtotal","header_repeat"]}},"required":["row_id","sheet","equip_label","item","value_raw","unit","measured_at","row_kind"]})에 맞는 제조 설비 점검 일보 더미 60행을 JSON 배열로 생성합니다. 시트는 조립·건조·포장 3종, 설비는 EQ-01~EQ-04, 항목은 defect_rate/temp/stock_qty. 모두 가상 더미.
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(쉬운 문제만 푸는 함정 회피): (1) 소계 행(row_kind=subtotal, equip_label='소계') 3건, (2) 반복 머리글(row_kind=header_repeat, value_raw가 숫자가 아님) 2건, (3) 병합 셀 때문에 비어 버린 설비 표기(equip_label='') 3건, (4) 서식 붙은 숫자('1,250')와 결측 표시('-') 각 3건, (5) 표기 흔들림('EQ-01'/'eq 01'/'EQ01'/'EQ-03 ') 5건, (6) 마스터에 없는 설비(EQ-09 등) 2건, (7) 단위 누락(unit=null) 2건. JSON 배열만.{"type":"object","properties":{"row_id":{"type":"string"},"sheet":{"type":"string"},"equip_label":{"type":"string"},"item":{"enum":["defect_rate","temp","stock_qty"]},"value_raw":{"type":"string"},"unit":{"type":["string","null"]},"measured_at":{"type":"string"},"row_kind":{"enum":["data","subtotal","header_repeat"]}},"required":["row_id","sheet","equip_label","item","value_raw","unit","measured_at","row_kind"]}고객사에서 함께 받은 설비 마스터 엑셀. 별칭 사전과 항목별 임계·방향·담당자·조치 문구 틀을 담아, 정규화와 판정이 대조하는 기준입니다. 별칭 누락·담당자 미지정·임계 미정을 일부러 섞어, 쉬운 데이터만 푸는 게 아님을 보여줍니다.
| row_id | equip_id | aliases | process | owner | item | threshold | direction | action_template |
|---|---|---|---|---|---|---|---|---|
| m_001 | eq_01 | EQ-01|eq 01|EQ01 | 조립 | 김반장 | defect_rate | 5 | max | {equip} 불량률이 {value}%로 임계 {threshold}%… |
| m_002 | eq_01 | EQ-01|eq 01|EQ01 | 조립 | 김반장 | stock_qty | 10 | min | {equip} 부품 재고가 {value}EA로 기준 {threshol… |
| m_003 | eq_01 | EQ-01|eq 01|EQ01 | 조립 | 김반장 | temp | — | max | — |
| m_004 | eq_02 | EQ-02|eq 02 | 건조 | 이기사 | temp | 200 | max | {equip} 건조로 온도가 {value}℃로 임계 {threshol… |
| m_005 | eq_02 | EQ-02|eq 02 | 건조 | 이기사 | defect_rate | 5 | max | {equip} 불량률이 {value}%로 임계 {threshold}%… |
아래 JSON Schema({"type":"object","properties":{"row_id":{"type":"string"},"equip_id":{"type":"string"},"aliases":{"type":"string"},"process":{"type":"string"},"owner":{"type":["string","null"]},"item":{"enum":["defect_rate","temp","stock_qty"]},"threshold":{"type":["number","null"]},"direction":{"enum":["max","min"]},"action_template":{"type":["string","null"]}},"required":["row_id","equip_id","aliases","process","owner","item","threshold","direction","action_template"]})에 맞는 설비 마스터 더미 30행을 JSON 배열로 생성합니다. 설비 eq_01~eq_06, 공정은 조립·건조·포장·도장, 항목은 defect_rate/temp/stock_qty이며 stock_qty는 direction=min, 나머지는 max로 둡니다. aliases는 '|'로 구분한 실제 현장 표기 변형입니다.
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: (1) 별칭이 비어 있어 원본 표기와 정확히 같아야만 맞는 행(aliases='') 3건, (2) 담당자 미지정(owner=null, action_template=null) 2건, (3) 임계 미정(threshold=null, action_template=null) 2건, (4) 나머지는 임계·방향·담당자·조치 문구 틀이 서로 어긋나지 않는 정상 행. action_template에는 {equip}·{value}·{threshold}·{owner} 자리표시자를 그대로 남겨라. JSON 배열만.{"type":"object","properties":{"row_id":{"type":"string"},"equip_id":{"type":"string"},"aliases":{"type":"string"},"process":{"type":"string"},"owner":{"type":["string","null"]},"item":{"enum":["defect_rate","temp","stock_qty"]},"threshold":{"type":["number","null"]},"direction":{"enum":["max","min"]},"action_template":{"type":["string","null"]}},"required":["row_id","equip_id","aliases","process","owner","item","threshold","direction","action_template"]}| Python | 엑셀 점검 일보를 표준 행으로 읽고 임계를 대조해 리포트 파일을 쓰는 CLI의 구현 언어입니다 |
| SQL | 정규화한 행과 설비 마스터를 로컬 SQLite에 넣고 조회 질의로 임계 이탈 행과 담당자를 뽑습니다 |
| ETL | 엑셀 추출본을 표준 행으로 바꿔 적재하는 단계가 이 도구의 앞단 그대로입니다 |
| Docker | 파이썬 설치가 막힌 고객사 노트북에서도 같은 결과가 나오도록 도구를 컨테이너 이미지로 포장합니다(3주차) |
| TypeScript | 라인별 설정 파일의 규격을 타입으로 정의하고 값이 규격에 맞는지 검사하는 3주차 설정 점검 명령의 구현 언어입니다 |
| LLM | 2주차에 판정을 보류한 행을 고객사에 물어볼 확인 질문 문장으로 다듬는 보조 역할만 맡습니다 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차(정답 대조 채점 + 확인 질문 목록 파일)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(라인별 설정 파일 분리 + 컨테이너 포장)로 진행합니다.
점검 행 1건의 판정 결과. 원본 표기와 맞춘 설비, 값·임계 대조 결과, 담당자와 조치 문구, 확인 필요 여부를 한 스키마로 담아 담당자별 조치 요청 리포트와 대조용 CSV를 만듭니다.
{"type":"object","properties":{"row_id":{"type":"string"},"equip_label_raw":{"type":"string"},"equip_id":{"type":["string","null"]},"match_kind":{"enum":["exact","alias","normalized","unmatched","skipped_row"]},"item":{"type":"string"},"value":{"type":["number","null"]},"unit":{"type":["string","null"]},"threshold":{"type":["number","null"]},"verdict":{"enum":["over","under","ok","hold"]},"owner":{"type":["string","null"]},"action_line":{"type":["string","null"]},"needs_confirm":{"type":"boolean"},"reason":{"type":"string"}},"required":["row_id","equip_label_raw","equip_id","match_kind","item","value","unit","threshold","verdict","owner","action_line","needs_confirm","reason"]}[
{
"input_ref": "r_002 (조립 시트 EQ-01 불량률 8.7%)",
"output": {
"row_id": "r_002",
"equip_label_raw": "EQ-01",
"equip_id": "eq_01",
"match_kind": "exact",
"item": "defect_rate",
"value": 8.7,
"unit": "%",
"threshold": 5,
"verdict": "over",
"owner": "김반장",
"action_line": "EQ-01 불량률이 8.7%로 임계 5.0%를 넘었습니다. 김반장이 조립 라인을 세우고 원인을 점검합니다",
"needs_confirm": false,
"reason": "값 8.7 > 임계 5.0 (direction=max)"
}
},
{
"input_ref": "r_006 (건조 시트 'eq 02' 불량률 2.0%)",
"output": {
"row_id": "r_006",
"equip_label_raw": "eq 02",
"equip_id": "eq_02",
"match_kind": "alias",
"item": "defect_rate",
"value": 2,
"unit": "%",
"threshold": 5,
"verdict": "ok",
"owner": "이기사",
"action_line": null,
"needs_confirm": false,
"reason": "별칭 사전에서 'eq 02'를 eq_02로 맞췄고 값 2.0 ≤ 임계 5.0이라 조치 대상이 아닙니다"
}
},
{
"input_ref": "r_009 (포장 시트 EQ-09 재고 0 EA)",
"output": {
"row_id": "r_009",
"equip_label_raw": "EQ-09",
"equip_id": null,
"match_kind": "unmatched",
"item": "stock_qty",
"value": 0,
"unit": "EA",
"threshold": null,
"verdict": "hold",
"owner": null,
"action_line": null,
"needs_confirm": true,
"reason": "'EQ-09'가 설비 마스터와 별칭 사전 어디에도 없어 담당자를 추측하지 않고 확인 필요로 남깁니다"
}
},
{
"input_ref": "r_004 (조립 시트 '소계' 행)",
"output": {
"row_id": "r_004",
"equip_label_raw": "소계",
"equip_id": null,
"match_kind": "skipped_row",
"item": "defect_rate",
"value": null,
"unit": "%",
"threshold": null,
"verdict": "hold",
"owner": null,
"action_line": null,
"needs_confirm": false,
"reason": "소계 행이라 판정 대상에서 제외하고 합계가 개별 설비 값으로 섞이지 않게 막습니다"
}
}
][자동 검증] 코드가 매 행마다 자동으로 거릅니다. 1) JSON Schema 형식 통과: 출력이 정한 필드·타입을 지키는지 검사하고, 어기면 거부합니다. 2) verdict가 값과 임계, direction으로 계산한 결과와 같은지 코드가 다시 계산해 대조합니다. 규칙으로 결정되는 판정을 임의로 바꾸지 못하게 막는 장치입니다. 3) equip_id가 별칭 사전 조회 결과와 같은지 대조하고, match_kind가 unmatched면 owner와 action_line이 반드시 null이어야 합니다. 담당자를 지어내지 못하게 막습니다. 4) action_line이 마스터의 action_template에 값·임계·담당자를 채운 문자열과 정확히 일치하는지 대조합니다. 5) threshold가 null이거나 값이 숫자로 안 읽히면 verdict=hold와 needs_confirm=true를 코드가 강제합니다. 6) row_kind가 subtotal이나 header_repeat인 행은 match_kind=skipped_row로 고정하고 조치 요청 리포트에 넣지 않습니다. [수동 검증] 사람이 일부만 확인합니다. 1) 전체 행의 5%는 사람이 원본 엑셀 셀과 직접 대조합니다(spot check). 2) 확인 필요 목록은 매 실행마다 전부 눈으로 읽고, 별칭 사전에 넣을지 사람이 정합니다. [전제] 정답 설비는 별칭 사전으로, 정답 판정은 값과 임계 비교로 결정해 심어두므로 정규화·판정 정확도를 규칙으로 객관 채점할 수 있습니다. 작은 모델은 확인 질문 문장을 다듬는 데만 쓰고 판정에는 관여하지 않습니다.