에이아이트릭스
경력근거: 배포를 '설치 완료'가 아닌 안정화·운영까지 소유, 문제 발생 시 원인 파악 및 재발 방지
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 FDE 의 주요업무로 'VitalCare와 Data Platform을 병원 환경에 맞게 구축하고 EMR·의료기기·LIS 등과 연동' 하는 일과 '배포를 '설치 완료'가 아닌 안정화·운영까지 소유, 문제 발생 시 원인 파악 및 재발 방지' 를 함께 적었습니다. VitalCare 는 EMR 의 활력징후와 혈액검사를 받아 병상마다 점수를 내는 제품이라, 연동 한 곳이 늦으면 점수가 멈추거나 묵은 값으로 계산되고 그 병상의 알람이 늦습니다. 설치 뒤 안정화를 맡은 사람이 매일 아침 먼저 알아야 할 것이 어느 병상의 점수가 어느 연동 때문에 밀렸는가이고, 이 CLI 는 그 질문 하나에 답합니다. 우대사항의 '시계열·생체신호 등 대용량 데이터 파이프라인 구축 경험' 과 'Docker, Kubernetes 기반 On-premise / Private Cloud 환경 경험' 은 2·3주차에 한 주 치 기록 처리와 병원 내부 서버 배포로 이어집니다. 이 포트폴리오는 여러 시스템에서 온 시각을 병상 단위로 맞춰 경과를 계산하고 규칙으로 판정하는 기술을 보여줍니다. 임상 판단이나 VitalCare 예측 모델에 대한 전문성은 주장하지 않으며, 이 도구의 판정은 데이터 시각 비교입니다.
주요업무에 '배포를 '설치 완료'가 아닌 안정화·운영까지 소유, 문제 발생 시 원인 파악 및 재발 방지' 가 있습니다. 설치 다음 날부터 점수가 제때 계산되는지 병상마다 보는 일이 그 소유의 첫 단계이고, 판정별 까닭이 원인 파악의 출발점입니다.
'VitalCare와 Data Platform을 병원 환경에 맞게 구축하고 EMR·의료기기·LIS 등과 연동' 과 자격요건의 '데이터 연동·SQL·스크립팅 등 직접 구현' 이 나란히 있습니다. 활력징후와 혈액검사, 입퇴원이 각각 다른 연동으로 들어오므로, 판정 셋이 연동 세 갈래와 짝을 이룹니다.
우대사항에 '시계열·생체신호 등 대용량 데이터 파이프라인 구축 경험' 과 'Docker, Kubernetes 기반 On-premise / Private Cloud 환경 경험' 이 있습니다. 2주차 7일치 처리와 3주차 k3s CronJob 이 이 두 항목을 받습니다.
에이아이트릭스 FDE 엔지니어가 VitalCare(EMR 의 활력징후와 혈액검사를 받아 병상마다 악화 위험 점수를 내는 에이아이트릭스 제품)를 설치한 병원에서 안정화 기간 아침마다 돌리는 CLI 입니다. VitalCare 에서 내려받은 점수 계산 기록, EMR 에서 내려받은 입퇴원 병상 명단, 설치 때 병원과 정한 병동별 데이터 주기표 세 파일을 받아 재원 병상마다 다섯 판정 중 하나를 정합니다. 환자마다 마지막 계산을 골라 점검 시각까지 흐른 분과, 그 계산에 쓰인 활력징후·혈액검사가 몇 분 묵었는지를 날짜를 넘겨 계산하고, 퇴원 시각 뒤에 남은 계산은 계산 번호의 중복을 걷어 셉니다. 보는 것은 시각뿐이고 점수 값이나 환자의 상태는 보지 않습니다.
병원에 VitalCare 를 설치한 뒤 안정화 기간을 맡은 에이아이트릭스 FDE 엔지니어 — 매일 아침 병원에서 내려받은 점수 기록과 입퇴원 병상 명단을 자기 터미널에서 돌려, 어느 병동의 어느 병상에서 점수가 밀렸는지와 그 원인이 측정값 지연인지 입퇴원 반영 누락인지를 좇는다
설치를 마친 병원에서 점수가 늦는 병상이 나와도 FDE 엔지니어가 아침에 바로 짚지 못하는 까닭은 기록이 없어서가 아니라, 점수 계산 기록은 VitalCare 에, 입퇴원은 EMR 에, 병동별 주기는 설치 문서에 따로 있어 한 병상의 세 시각을 한자리에서 견준 적이 없기 때문입니다. 세 파일을 등록번호와 병동으로 맞추고 경과 분을 계산해 두면, 계산이 멈춘 병상과 묵은 측정값으로 계산된 병상, 퇴원이 반영되지 않은 병상이 병동별 목록으로 나옵니다.
FDE 엔지니어가 점수 계산 기록과 병상 명단, 병동 주기표에 점검 시각을 넣어 명령 한 줄을 치면 병상마다 다섯 판정 중 하나와 마지막 계산 시각, 세 가지 경과 분을 병동별 구획으로 찍고 같은 내용을 마크다운 리포트로 남기는 CLI 입니다. 환자별 마지막 계산 고르기, 자정을 넘는 시각 차이 계산, 병동 주기표를 기준으로 한 우선순위 판정, 퇴원 뒤 계산의 중복 걷어 세기가 이 주의 계산입니다. 세 파일은 병원에서 CSV 로 내려받았다고 두고, 시스템에 직접 붙는 수집은 2주차로 미룹니다.
7일치 아침 내보내기를 한꺼번에 돌려 병동별로 판정이 며칠째 되풀이되는지 세고, 손으로 단 판정과 CLI 판정을 견주는 평가 러너를 만듭니다. 직접 만든 HL7 v2 ADT 퇴원 메시지(A03)를 읽어 병상 명단의 퇴원 시각과 메시지 수신 시각을 견주고, 같은 CLI 를 Docker 이미지로 묶어 AWS 에서 합성 데이터로 매일 아침 돌립니다.
로컬 Kubernetes(k3s)에 같은 이미지를 07:00 CronJob 으로 올려 병원 내부 서버 배포를 흉내 내고, 병동별 걸린 병상 수를 모니터링 지표로 내보냅니다. 걸린 병상은 클로드가 병동별로 모아 병원 전산팀에 보낼 확인 요청 초안을 씁니다.
FDE 엔지니어가 `vc-freshness check --runs score_runs_0927.csv --roster bed_roster_0927.csv --policy ward_policy.csv --at '2026-09-27 07:00'` 을 치면 CCU C-03 이 점수가 멈춤 구획에 마지막 계산 04:30 과 150분 경과로, 63병동 6302-3 이 퇴원 뒤에도 점수가 남음 구획에 퇴원 16:30 과 이후 계산 5회로 찍힙니다. 같은 실행이 병동별로 묶어 남긴 마크다운 리포트를 열어, 병원 전산팀에 넘길 병상 목록이 그대로 있는지 보여 줍니다.
합성 병원 하루 치 병상마다 손으로 판정을 달아 CLI 판정과 일치하는 병상 수를 셉니다. 이 수가 알려 주는 것은 경과 분 계산과 우선순위 규칙이 적어 둔 대로 도는지 하나뿐입니다. 세 파일과 정답 판정을 모두 작성자가 지었기 때문입니다.
혈액검사 시각이 빈 입원 직후 환자, 혈액검사 주기가 지나도록 혈액검사 시각이 빈 환자, 기준과 정확히 같은 간격의 병상, 두 번 들어온 계산 번호, 자정을 넘긴 활력징후 시각, 퇴원 전에만 계산이 있는 퇴원 환자, 입원 뒤 계산이 없는 환자, 다른 병상으로 다시 입원한 환자는 병상별 기대 판정과 함께 고정 파일로 두고, 규칙을 고친 커밋마다 이 병상들의 판정이 그대로인지 CLI 로 대조합니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 합성 병원 기록을 작성자가 채점한 결과이고, 안정화 기간에 점수 지연을 실제로 먼저 찾아내는지는 VitalCare 가 설치된 병원의 실제 내보내기와 병원 전산팀의 원인 확인이 있어야 잴 수 있습니다. 이 포트폴리오는 그 첫날 아침에 바로 돌릴 CLI 와 평가 러너를 만듭니다.
일치 병상 수는 제가 지은 병원 기록에 제가 단 판정을 견준 값이라, 이 도구가 실제 병원에서 점수 지연을 먼저 찾아낸다는 근거가 되지 못합니다. 면접에서는 경과 분과 병동 주기표로 판정이 어떻게 정해지는지를 보여 주고, 실제 내보내기를 받으면 시각 표기와 input_vitals_at 의 뜻부터 병원과 맞출 계획을 말합니다.
혈액검사 결과가 아직 없는 입원 직후 환자(SYN-240391)와 주기가 지나도록 없는 환자(SYN-240467), 기준선에 정확히 걸린 병상, 겹친 내보내기로 두 번 들어온 계산 번호, 입원 뒤 계산이 한 줄도 없는 환자(SYN-240488), 퇴원 뒤 다른 병상으로 다시 입원한 환자(SYN-240479)를 샘플에 일부러 넣었습니다. SYN-240448 은 처음 부등호를 '이상' 으로 적으면 멈춤으로 잘못 떨어지는 병상이라, 규칙을 고칠 때마다 이 병상이 제때 갱신됨에 남는지부터 봅니다.
이 도구는 시각만 봅니다. 점수가 제때 계산되고 입력 시각도 새것인데 값이 잘못 들어온 경우(단위가 바뀐 활력징후 등)는 다섯 판정 어디에도 걸리지 않으므로, 제때 갱신됨이 점수 값이 맞다는 뜻은 아닙니다.
input_vitals_at 이 측정 시각인지 EMR 입력 시각인지는 병원마다 다릅니다. 입력 시각이면 간호사가 몰아서 입력한 병동이 오래된 활력징후로 걸리므로, 실제 도입에서는 이 열의 뜻을 병원 전산팀과 먼저 확인합니다.
병동 주기표는 설치 때 정한 연동 설정값이지 임상 기준이 아닙니다. 판정이 걸렸다고 그 환자의 점수가 틀렸다는 뜻도 아니고, 점수를 어떻게 쓸지는 의료진이 정합니다.
실제 도입에서는 점수 계산 기록과 병상 명단에 실제 등록번호가 담깁니다. 병원 밖으로 꺼내지 않고 병원 내부 서버에서만 돌리며, 확인 요청 초안에는 병상 번호와 시각만 넘깁니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
병원에 설치한 VitalCare 에서 FDE 엔지니어가 아침마다 내려받는 점수 계산 기록입니다. 계산 한 번이 한 줄이고, 계산 번호와 합성 등록번호, 병상, 계산 시각, 그 계산에 쓰인 활력징후 측정 시각과 혈액검사 결과 시각을 둡니다. 점수 값과 활력징후·혈액검사 수치는 싣지 않습니다. 이 도구는 시각만 봅니다. 입원 뒤 혈액검사 결과가 아직 없는 환자는 혈액검사 시각이 비어 있고, 내보내기 구간이 겹치면 같은 계산 번호가 두 줄로 들어옵니다.
| run_no | patient_key | bed_id | scored_at | input_vitals_at | input_lab_at |
|---|---|---|---|---|---|
| VC-0927-5215-0540 | SYN-240233 | 5215-1 | 2026-09-27 05:40 | 2026-09-26 22:15 | 2026-09-26 05:50 |
| VC-0927-5215-0640 | SYN-240233 | 5215-1 | 2026-09-27 06:40 | 2026-09-26 22:15 | 2026-09-26 05:50 |
| VC-0927-M04-0655 | SYN-240391 | M-04 | 2026-09-27 06:55 | 2026-09-27 06:30 | — |
| VC-0926-6302-1610 | SYN-240402 | 6302-3 | 2026-09-26 16:10 | 2026-09-26 15:50 | 2026-09-26 06:20 |
| VC-0926-6302-1710 | SYN-240402 | 6302-3 | 2026-09-26 17:10 | 2026-09-26 15:50 | 2026-09-26 06:20 |
아래 JSON Schema 를 그대로 만족하는 점수 계산 기록을 400줄 더 만들어 주세요. 한 병원의 일반병동 여덟 곳과 중환자실 세 곳(MICU·SICU·CCU)에 재원 중인 환자 60명 안팎이 한 시간마다 한 번씩 점수를 받는다고 보고, 2026-09-26 00:00 부터 2026-09-27 07:00 사이의 계산을 시간순으로 씁니다. 등록번호는 모두 'SYN-' 뒤 여섯 자리 합성 값으로 쓰고 실제 환자와 이어지지 않게 합니다. 계산 번호는 'VC-' 뒤에 월일 네 자리, 병상 번호, 시각 네 자리를 하이픈으로 잇습니다. input_vitals_at 은 scored_at 보다 늦지 않게, input_lab_at 은 input_vitals_at 보다 드물게 바뀌도록 씁니다. 쉬운 데이터만 만들지 말고 다음을 섞으세요: 몇 시간 동안 계산이 끊긴 병상 두세 곳, 계산은 이어지는데 활력징후 시각이 여러 시간째 그대로인 병상, 입원 직후라 input_lab_at 이 null 인 환자, 입원한 지 병동 혈액검사 주기가 지났는데도 input_lab_at 이 null 인 환자, 퇴원한 뒤에도 몇 시간 계산이 이어진 환자, 내보내기 구간이 겹쳐 같은 run_no 가 두 줄 들어간 경우, 기준 주기와 정확히 같은 간격으로 계산된 병상. 판정 결과나 정상·지연 같은 표시는 넣지 마세요. 형식 깨뜨리기 시도로 시각을 '2026.09.27 06:40'·'09/27 06:40' 처럼 다른 꼴로 적은 줄 5건을 따로 만들어 파서가 걸러내는지 보게 합니다.
{"type":"object","properties":{"run_no":{"type":"string","description":"VitalCare 가 점수를 한 번 계산할 때 붙이는 계산 번호입니다"},"patient_key":{"type":"string","description":"합성 등록번호(SYN- 로 시작)입니다"},"bed_id":{"type":"string"},"scored_at":{"type":"string","description":"점수를 계산한 시각, YYYY-MM-DD HH:MM"},"input_vitals_at":{"type":"string","description":"그 계산에 쓰인 가장 최근 활력징후의 측정 시각, YYYY-MM-DD HH:MM"},"input_lab_at":{"type":["string","null"],"description":"그 계산에 쓰인 가장 최근 혈액검사 결과 시각, YYYY-MM-DD HH:MM. 입원 뒤 혈액검사 결과가 한 번도 들어오지 않았으면 null"}},"required":["run_no","patient_key","bed_id","scored_at","input_vitals_at","input_lab_at"]}{"type":"object","properties":{"run_no":{"type":"string","description":"VitalCare 가 점수를 한 번 계산할 때 붙이는 계산 번호입니다"},"patient_key":{"type":"string","description":"합성 등록번호(SYN- 로 시작)입니다"},"bed_id":{"type":"string"},"scored_at":{"type":"string","description":"점수를 계산한 시각, YYYY-MM-DD HH:MM"},"input_vitals_at":{"type":"string","description":"그 계산에 쓰인 가장 최근 활력징후의 측정 시각, YYYY-MM-DD HH:MM"},"input_lab_at":{"type":["string","null"],"description":"그 계산에 쓰인 가장 최근 혈액검사 결과 시각, YYYY-MM-DD HH:MM. 입원 뒤 혈액검사 결과가 한 번도 들어오지 않았으면 null"}},"required":["run_no","patient_key","bed_id","scored_at","input_vitals_at","input_lab_at"]}같은 날 아침 병원 EMR 에서 내려받은 입퇴원 병상 명단입니다. 환자 한 명의 입원 한 번이 한 줄이고, 합성 등록번호와 병상, 병동, 입원 시각, 퇴원 시각을 둡니다. 재원 중인 환자는 퇴원 시각이 비어 있습니다. VitalCare 가 받은 입퇴원 소식과 이 명단이 어긋나면 퇴원한 환자에게 점수가 계속 계산됩니다. 같은 환자가 퇴원 뒤 다시 입원하면 같은 등록번호로 줄이 하나 더 생깁니다.
| patient_key | bed_id | ward | admitted_at | discharged_at |
|---|---|---|---|---|
| SYN-240233 | 5215-1 | 52병동 | 2026-09-25 14:05 | — |
| SYN-240391 | M-04 | MICU | 2026-09-26 21:40 | — |
| SYN-240402 | 6302-3 | 63병동 | 2026-09-20 09:00 | 2026-09-26 16:30 |
| SYN-240427 | 6105-1 | 61병동 | 2026-09-22 15:30 | — |
| SYN-240436 | S-02 | SICU | 2026-09-25 18:15 | — |
아래 JSON Schema 를 그대로 만족하는 입퇴원 병상 명단을 60줄 더 만들어 주세요. 점수 계산 기록과 같은 병원·같은 병동 이름을 쓰고, 등록번호는 'SYN-' 뒤 여섯 자리 합성 값으로 점수 계산 기록과 짝이 맞게 둡니다. 병상 번호는 일반병동이면 병동 번호 두 자리로 시작하는 네 자리 뒤에 하이픈과 침상 번호를, 중환자실이면 'M-'·'S-'·'C-' 뒤에 두 자리를 씁니다. 쉬운 데이터만 만들지 말고 다음을 섞으세요: 2026-09-26 오후에 퇴원했는데 점수 계산 기록에는 그 뒤에도 계산이 남은 환자, 퇴원 전에 계산이 멈춰 퇴원 뒤 계산이 없는 환자, 2026-09-26 밤에 입원해 아직 혈액검사 결과가 없는 환자, 병동 혈액검사 주기보다 오래전에 입원했는데 혈액검사 결과가 한 번도 없는 환자, 새벽에 입원했는데 점수 계산 기록에 줄이 하나도 없는 환자, 전날 퇴원했다가 같은 등록번호로 다른 병상에 다시 입원한 환자. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"patient_key":{"type":"string","description":"합성 등록번호(SYN- 로 시작)입니다"},"bed_id":{"type":"string"},"ward":{"type":"string"},"admitted_at":{"type":"string","description":"YYYY-MM-DD HH:MM"},"discharged_at":{"type":["string","null"],"description":"퇴원 시각, YYYY-MM-DD HH:MM. 재원 중이면 null"}},"required":["patient_key","bed_id","ward","admitted_at","discharged_at"]}{"type":"object","properties":{"patient_key":{"type":"string","description":"합성 등록번호(SYN- 로 시작)입니다"},"bed_id":{"type":"string"},"ward":{"type":"string"},"admitted_at":{"type":"string","description":"YYYY-MM-DD HH:MM"},"discharged_at":{"type":["string","null"],"description":"퇴원 시각, YYYY-MM-DD HH:MM. 재원 중이면 null"}},"required":["patient_key","bed_id","ward","admitted_at","discharged_at"]}FDE 팀이 설치 때 병원 전산팀과 정해 둔 병동별 데이터 주기표입니다. 점수 재계산 주기와 활력징후·혈액검사가 VitalCare 에 들어와야 하는 주기를 시간 단위로 둡니다. 임상 권고가 아니라 연동 설정값이며, 이 도구는 이 표를 기준으로 받아 쓰고 새로 만들지 않습니다.
| ward | score_interval_h | vital_interval_h | lab_interval_h |
|---|---|---|---|
| 41병동 | 2 | 12 | 72 |
| 51병동 | 1 | 8 | 48 |
| 52병동 | 1 | 8 | 48 |
| 61병동 | 1 | 8 | 48 |
| 63병동 | 1 | 8 | 48 |
아래 JSON Schema 를 그대로 만족하는 병동별 데이터 주기표를 다른 병원 두 곳 몫으로 20줄 더 만들어 주세요. 일반병동은 점수 재계산 1~2시간, 활력징후 6~12시간, 혈액검사 48~72시간 범위에서, 중환자실은 점수 재계산 1시간, 활력징후 1~2시간, 혈액검사 24시간 안팎에서 고릅니다. 병동 이름은 병원마다 '71병동'·'내과계 중환자실'·'MICU' 처럼 부르는 방식이 다를 수 있으나 한 병원 안에서는 점수 계산 기록·병상 명단과 같은 이름을 씁니다. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"ward":{"type":"string"},"score_interval_h":{"type":"number","minimum":0,"description":"설치 때 병원과 정한 점수 재계산 주기(시간)"},"vital_interval_h":{"type":"number","minimum":0,"description":"설치 때 병원과 정한 활력징후 수신 주기(시간)"},"lab_interval_h":{"type":"number","minimum":0,"description":"설치 때 병원과 정한 혈액검사 결과 수신 주기(시간)"}},"required":["ward","score_interval_h","vital_interval_h","lab_interval_h"]}{"type":"object","properties":{"ward":{"type":"string"},"score_interval_h":{"type":"number","minimum":0,"description":"설치 때 병원과 정한 점수 재계산 주기(시간)"},"vital_interval_h":{"type":"number","minimum":0,"description":"설치 때 병원과 정한 활력징후 수신 주기(시간)"},"lab_interval_h":{"type":"number","minimum":0,"description":"설치 때 병원과 정한 혈액검사 결과 수신 주기(시간)"}},"required":["ward","score_interval_h","vital_interval_h","lab_interval_h"]}| Python | 세 CSV 읽기, 환자별 마지막 계산 고르기, 날짜를 넘는 분 계산, 우선순위 판정, CLI 와 마크다운 리포트를 맡습니다. |
| SQL | 2주차에 세 파일을 PostgreSQL 에 올려 창 함수로 같은 판정을 짜고 Python 판정과 병상마다 대조합니다. |
| EMR 연동 | 입퇴원 병상 명단을 EMR 내보내기 모양으로 받아 등록번호로 점수 계산 기록과 잇습니다. |
| LIS 연동 | 혈액검사 결과 시각이 비었거나 묵은 병상을 따로 판정해 LIS 쪽 수신 문제를 가려냅니다. |
| HL7 | 2주차에 직접 만든 ADT^A03 퇴원 메시지를 파싱해 퇴원 시각과 수신 시각을 꺼냅니다. |
| 시계열 데이터 파이프라인 | 2주차에 7일치 아침 내보내기를 날짜별로 쌓아 병동별 판정이 며칠째 되풀이되는지 셉니다. |
| Docker | CLI 와 기준 주기표를 이미지 하나로 묶어 어느 서버에서나 같은 판정이 나오게 합니다. |
| Kubernetes | 3주차에 k3s 에 07:00 CronJob 으로 올리고 리포트를 볼륨에 남깁니다. |
| MLOps 모니터링 | 3주차에 병동별 걸린 병상 수를 Prometheus 지표로 내보내 모델 입력이 제때 들어오는지를 운영 지표로 봅니다. |
| AWS | 2주차에 같은 이미지를 EC2 한 대에서 합성 데이터로 매일 아침 돌립니다. |
✗ fail → 한 병상에 판정이 둘 붙거나 기준선 병상이 뒤집히면 우선순위와 비교 부등호부터 고치고 다시 돌립니다.
✓ pass → 2주차 7일치 처리와 평가 러너, ADT 메시지 읽기로 넘어갑니다.
✗ fail → 하루에만 몰렸으면 그날의 연동 장애로 두고, 자동 실행보다 판정 규칙의 기준값을 먼저 병원과 다시 맞춥니다.
✓ pass → 3주차 k3s CronJob 과 지표 내보내기, 확인 요청 초안으로 넘어갑니다.
병상별 점수 갱신 판정입니다. 재원 환자는 병상마다 한 줄, 퇴원 환자는 퇴원 뒤 계산이 남은 경우에만 한 줄을 냅니다. 판정과 함께 마지막 계산 시각과 세 가지 경과 분, 퇴원 뒤 계산 횟수를 실어 터미널의 병동별 구획과 마크다운 리포트가 그대로 읽습니다.
{"type":"object","properties":{"check_no":{"type":"string"},"patient_key":{"type":"string"},"bed_id":{"type":"string"},"ward":{"type":"string"},"checked_at":{"type":"string","description":"CLI 의 --at 으로 받은 점검 시각입니다"},"verdict":{"type":"string","enum":["제때 갱신됨","점수가 멈춤","오래된 활력징후로 계산됨","오래된 혈액검사로 계산됨","퇴원 뒤에도 점수가 남음"]},"last_scored_at":{"type":["string","null"],"description":"지금 입원(재원 환자) 또는 그 입원 줄(퇴원 환자)의 admitted_at 뒤에 찍힌 같은 patient_key 의 score_runs.scored_at 가운데 가장 늦은 시각입니다. 재원 환자인데 입원 뒤 계산이 하나도 없으면 null 입니다"},"score_lag_min":{"type":["integer","null"],"minimum":0,"description":"checked_at 에서 last_scored_at 을 뺀 분입니다. 입원 뒤 계산이 하나도 없는 재원 환자는 checked_at 에서 admitted_at 을 뺀 분입니다. 퇴원 환자는 null 입니다"},"vitals_lag_min":{"type":["integer","null"],"minimum":0,"description":"마지막 계산 줄의 scored_at 에서 input_vitals_at 을 뺀 분입니다. 퇴원 환자와 입원 뒤 계산이 없는 환자는 null 입니다"},"lab_lag_min":{"type":["integer","null"],"minimum":0,"description":"마지막 계산 줄의 scored_at 에서 input_lab_at 을 뺀 분입니다. input_lab_at 이 null 이거나, 퇴원 환자거나, 입원 뒤 계산이 없는 환자면 null 입니다"},"post_discharge_runs":{"type":["integer","null"],"minimum":0,"description":"그 퇴원 줄과 patient_key·bed_id 가 같고, scored_at 이 discharged_at 보다 늦으며 같은 patient_key 의 다음 입원 admitted_at 보다 이른 줄의 run_no 를 중복 없이 센 수입니다. 재원 환자는 null 입니다"},"evidence":{"type":"array","items":{"type":"object","properties":{"table":{"type":"string","enum":["score_runs","bed_roster","ward_policy"]},"field":{"type":"string"},"agg":{"type":["string","null"],"enum":["count","sum","avg","min","max",null]},"value":{}},"required":["table","field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["check_no","patient_key","bed_id","ward","checked_at","verdict","evidence","summary"]}[
{
"check_no": "FC-0927-02",
"patient_key": "SYN-240233",
"bed_id": "5215-1",
"ward": "52병동",
"checked_at": "2026-09-27 07:00",
"verdict": "오래된 활력징후로 계산됨",
"last_scored_at": "2026-09-27 06:40",
"score_lag_min": 20,
"vitals_lag_min": 505,
"lab_lag_min": 1490,
"post_discharge_runs": null,
"evidence": [
{
"table": "score_runs",
"field": "scored_at",
"agg": null,
"value": "2026-09-27 06:40"
},
{
"table": "score_runs",
"field": "input_vitals_at",
"agg": null,
"value": "2026-09-26 22:15"
},
{
"table": "ward_policy",
"field": "vital_interval_h",
"agg": null,
"value": 8
}
],
"summary": "06:40 계산은 제때 돌았지만 쓰인 활력징후가 전날 22:15 측정분이라, 두 시각 사이 505분이 52병동 활력징후 주기 8시간(480분)을 넘었습니다.",
"hedge": "새 측정이 없었는지, 측정은 했는데 EMR 에 늦게 입력됐는지는 병동 간호 기록을 봐야 알 수 있습니다."
},
{
"check_no": "FC-0927-03",
"patient_key": "SYN-240391",
"bed_id": "M-04",
"ward": "MICU",
"checked_at": "2026-09-27 07:00",
"verdict": "제때 갱신됨",
"last_scored_at": "2026-09-27 06:55",
"score_lag_min": 5,
"vitals_lag_min": 25,
"lab_lag_min": null,
"post_discharge_runs": null,
"evidence": [
{
"table": "score_runs",
"field": "scored_at",
"agg": null,
"value": "2026-09-27 06:55"
},
{
"table": "score_runs",
"field": "input_lab_at",
"agg": null,
"value": null
},
{
"table": "bed_roster",
"field": "admitted_at",
"agg": null,
"value": "2026-09-26 21:40"
},
{
"table": "ward_policy",
"field": "lab_interval_h",
"agg": null,
"value": 24
}
],
"summary": "전날 21:40 에 입원한 뒤 560분이 지났고 혈액검사 결과 시각은 아직 비어 있지만, MICU 혈액검사 주기 24시간(1440분) 안이라 첫 결과를 기다리는 중으로 보고 제때 갱신됨으로 둡니다. 마지막 계산은 5분 전, 쓰인 활력징후는 25분 전 것입니다.",
"hedge": "첫 채혈 결과가 아직 안 나온 것인지 LIS 연동이 이 병상만 빠진 것인지는 이 기록만으로 가려지지 않으며, 입원 뒤 24시간인 2026-09-27 21:40 이 지나도 비어 있으면 오래된 혈액검사로 계산됨으로 넘어갑니다."
},
{
"check_no": "FC-0927-04",
"patient_key": "SYN-240402",
"bed_id": "6302-3",
"ward": "63병동",
"checked_at": "2026-09-27 07:00",
"verdict": "퇴원 뒤에도 점수가 남음",
"last_scored_at": "2026-09-26 21:10",
"score_lag_min": null,
"vitals_lag_min": null,
"lab_lag_min": null,
"post_discharge_runs": 5,
"evidence": [
{
"table": "bed_roster",
"field": "discharged_at",
"agg": null,
"value": "2026-09-26 16:30"
},
{
"table": "score_runs",
"field": "scored_at",
"agg": "max",
"value": "2026-09-26 21:10"
},
{
"table": "score_runs",
"field": "run_no",
"agg": "count",
"value": 5
},
{
"table": "score_runs",
"field": "run_no",
"agg": null,
"value": "VC-0926-6302-1910"
}
],
"summary": "16:30 에 퇴원한 환자에게 17:10 부터 21:10 까지 점수가 다섯 번 더 계산됐습니다. 19:10 계산은 두 줄로 들어와 한 번으로 셉니다.",
"hedge": "입퇴원 소식이 VitalCare 에 늦게 닿은 것으로 보이며, 정확한 경로는 병원 연동 서버의 퇴원 메시지 수신 기록으로 확인합니다."
},
{
"check_no": "FC-0927-06",
"patient_key": "SYN-240448",
"bed_id": "7203-2",
"ward": "72병동",
"checked_at": "2026-09-27 07:00",
"verdict": "제때 갱신됨",
"last_scored_at": "2026-09-27 05:00",
"score_lag_min": 120,
"vitals_lag_min": 360,
"lab_lag_min": 1370,
"post_discharge_runs": null,
"evidence": [
{
"table": "score_runs",
"field": "scored_at",
"agg": "max",
"value": "2026-09-27 05:00"
},
{
"table": "ward_policy",
"field": "score_interval_h",
"agg": null,
"value": 1
},
{
"table": "score_runs",
"field": "input_vitals_at",
"agg": null,
"value": "2026-09-26 23:00"
},
{
"table": "ward_policy",
"field": "vital_interval_h",
"agg": null,
"value": 6
}
],
"summary": "마지막 계산에서 120분이 지나 재계산 주기의 두 배와 같고, 활력징후도 주기 6시간과 같은 360분 전 것이라 두 기준 모두 넘지 않았습니다.",
"hedge": "두 값이 기준선에 정확히 걸려 있어, 한 시간 뒤 다시 돌리면 점수가 멈춤으로 넘어갈 수 있습니다."
},
{
"check_no": "FC-0927-07",
"patient_key": "SYN-240452",
"bed_id": "C-03",
"ward": "CCU",
"checked_at": "2026-09-27 07:00",
"verdict": "점수가 멈춤",
"last_scored_at": "2026-09-27 04:30",
"score_lag_min": 150,
"vitals_lag_min": 200,
"lab_lag_min": 510,
"post_discharge_runs": null,
"evidence": [
{
"table": "bed_roster",
"field": "discharged_at",
"agg": null,
"value": null
},
{
"table": "score_runs",
"field": "scored_at",
"agg": "max",
"value": "2026-09-27 04:30"
},
{
"table": "ward_policy",
"field": "score_interval_h",
"agg": null,
"value": 1
},
{
"table": "score_runs",
"field": "input_vitals_at",
"agg": null,
"value": "2026-09-27 01:10"
},
{
"table": "ward_policy",
"field": "vital_interval_h",
"agg": null,
"value": 2
}
],
"summary": "04:30 이후 150분 동안 계산이 없어 점수가 멈춤입니다. 마지막 계산에 쓰인 활력징후도 200분 전 것이라 CCU 주기 2시간을 넘었지만, 우선순위에 따라 멈춤 하나로 적습니다.",
"hedge": "활력징후 수신이 먼저 끊기고 계산이 뒤따라 멈춘 모양이라, 의료기기 연동 쪽부터 봅니다."
},
{
"check_no": "FC-0927-08",
"patient_key": "SYN-240436",
"bed_id": "S-02",
"ward": "SICU",
"checked_at": "2026-09-27 07:00",
"verdict": "오래된 혈액검사로 계산됨",
"last_scored_at": "2026-09-27 06:50",
"score_lag_min": 10,
"vitals_lag_min": 30,
"lab_lag_min": 1520,
"post_discharge_runs": null,
"evidence": [
{
"table": "score_runs",
"field": "scored_at",
"agg": null,
"value": "2026-09-27 06:50"
},
{
"table": "score_runs",
"field": "input_lab_at",
"agg": null,
"value": "2026-09-26 05:30"
},
{
"table": "ward_policy",
"field": "lab_interval_h",
"agg": null,
"value": 24
}
],
"summary": "06:50 계산에 쓰인 혈액검사가 전날 05:30 결과라, 1520분이 SICU 혈액검사 주기 24시간(1440분)을 넘었습니다.",
"hedge": "오늘 새벽 채혈 결과가 LIS 에서 아직 넘어오지 않았을 수 있어, 08시 이후 다시 돌려 풀리는지 봅니다."
},
{
"check_no": "FC-0927-09",
"patient_key": "SYN-240488",
"bed_id": "5104-2",
"ward": "51병동",
"checked_at": "2026-09-27 07:00",
"verdict": "점수가 멈춤",
"last_scored_at": null,
"score_lag_min": 200,
"vitals_lag_min": null,
"lab_lag_min": null,
"post_discharge_runs": null,
"evidence": [
{
"table": "bed_roster",
"field": "admitted_at",
"agg": null,
"value": "2026-09-27 03:40"
},
{
"table": "bed_roster",
"field": "discharged_at",
"agg": null,
"value": null
},
{
"table": "ward_policy",
"field": "score_interval_h",
"agg": null,
"value": 1
}
],
"summary": "03:40 에 입원한 뒤 200분 동안 점수가 한 번도 계산되지 않아, 51병동 재계산 주기 1시간의 두 배인 120분을 넘었습니다.",
"hedge": "입원 소식이 VitalCare 에 닿지 않았는지, 닿았는데 계산 대상에 오르지 않았는지는 병원 연동 서버의 입원 메시지 수신 기록으로 가립니다."
},
{
"check_no": "FC-0927-10",
"patient_key": "SYN-240479",
"bed_id": "4108-3",
"ward": "41병동",
"checked_at": "2026-09-27 07:00",
"verdict": "제때 갱신됨",
"last_scored_at": "2026-09-27 05:30",
"score_lag_min": 90,
"vitals_lag_min": 40,
"lab_lag_min": 20,
"post_discharge_runs": null,
"evidence": [
{
"table": "bed_roster",
"field": "admitted_at",
"agg": null,
"value": "2026-09-26 20:30"
},
{
"table": "score_runs",
"field": "scored_at",
"agg": "max",
"value": "2026-09-27 05:30"
},
{
"table": "ward_policy",
"field": "score_interval_h",
"agg": null,
"value": 2
}
],
"summary": "전날 20:30 에 41병동으로 다시 입원한 뒤 05:30 에 계산된 점수가 90분 전 것이라, 41병동 재계산 주기 2시간의 두 배 안에 듭니다.",
"hedge": "같은 등록번호가 전날 52병동에서 퇴원했지만, 그 뒤 계산은 새 입원 병상 몫이라 퇴원 뒤 점수로 세지 않았습니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 제때 갱신됨·점수가 멈춤·오래된 활력징후로 계산됨·오래된 혈액검사로 계산됨·퇴원 뒤에도 점수가 남음 다섯 값 중 하나인지 확인합니다. 2. 필수 필드 check_no·patient_key·bed_id·ward·checked_at·verdict·evidence·summary 가 모두 존재하는지 확인합니다. 3. score_lag_min 이 checked_at 에서 last_scored_at 을 뺀 분과 같은지 다시 계산합니다. 자정을 넘는 두 시각도 날짜를 포함해 뺍니다. SYN-240452 는 07:00 에서 04:30 을 빼 150분입니다. 4. vitals_lag_min 이 마지막 계산 줄의 scored_at 에서 input_vitals_at 을 뺀 분과 같은지 다시 계산합니다. SYN-240233 은 2026-09-27 06:40 에서 2026-09-26 22:15 를 빼 505분입니다. 5. lab_lag_min 이 마지막 계산 줄의 scored_at 에서 input_lab_at 을 뺀 분과 같은지 다시 계산합니다. input_lab_at 이 null 이면 lab_lag_min 도 null 인지 봅니다. 6. post_discharge_runs 가 그 퇴원 줄과 patient_key·bed_id 가 같고 scored_at 이 discharged_at 보다 늦은 줄의 run_no 를 중복 없이 센 수와 같은지 다시 계산합니다. 같은 patient_key 로 다음 입원이 있으면 그 admitted_at 부터의 줄은 다음 입원 몫이라 세지 않습니다. SYN-240402 는 여섯 줄이지만 VC-0926-6302-1910 이 두 번 들어와 5입니다. SYN-240479 는 52병동 5220-3 에서 퇴원한 뒤 41병동 4108-3 으로 다시 입원해 05:30 계산이 있지만, 병상이 다르고 다시 입원한 뒤의 계산이라 0입니다. 7. score_lag_min·vitals_lag_min·lab_lag_min·post_discharge_runs 가 채워졌으면 0 이상인지 범위를 봅니다. 8. 재원 환자의 마지막 계산은 지금 입원의 admitted_at 뒤 줄에서만 고릅니다. 입원 뒤 계산이 하나도 없으면 admitted_at 을 마지막 계산 시각 자리에 두고 score_lag_min 을 셉니다(SYN-240488 은 03:40 입원 뒤 계산이 없어 200분). bed_roster.discharged_at 이 비어 있고 score_lag_min 이 해당 ward 의 ward_policy.score_interval_h 에 120 을 곱한 값보다 크면 verdict 는 점수가 멈춤입니다. SYN-240448 은 120분으로 기준과 같아 이 판정에 걸리지 않습니다. 9. 점수가 멈춤에 걸리지 않은 재원 환자의 vitals_lag_min 이 ward_policy.vital_interval_h 에 60 을 곱한 값보다 크면 verdict 는 오래된 활력징후로 계산됨입니다. SYN-240448 은 360분으로 72병동 기준 360분과 같아 걸리지 않습니다. 10. 앞 두 판정에 걸리지 않고 입원 뒤 계산이 한 줄 이상 있는 재원 환자의 lab_lag_min 이 ward_policy.lab_interval_h 에 60 을 곱한 값보다 크거나 input_lab_at 이 null 이면 verdict 는 오래된 혈액검사로 계산됨입니다. 예외로, 마지막 계산 줄의 input_lab_at 이 null 이고 checked_at 에서 bed_roster.admitted_at 을 뺀 분이 lab_interval_h 에 60 을 곱한 값 이하이면 입원 뒤 첫 혈액검사를 기다리는 중이라 이 판정을 내리지 않고 11번으로 넘어갑니다. SYN-240391 은 MICU 에 전날 21:40 입원해 560분으로 1440분 안이라 제때 갱신됨입니다. SYN-240467 은 41병동에 2026-09-24 06:40 입원해 4340분이 지나 4320분을 넘겼는데도 비어 있어 종전대로 오래된 혈액검사로 계산됨입니다. 11. 재원 환자가 8~10 어디에도 걸리지 않으면 verdict 는 제때 갱신됨입니다. SYN-240479 의 41병동 재입원 줄이 그런 환자입니다. 12. bed_roster.discharged_at 이 채워져 있고 post_discharge_runs 가 1 이상이면 verdict 는 퇴원 뒤에도 점수가 남음입니다. SYN-240479 는 퇴원 시각 11:00 전 계산만 있어 post_discharge_runs 가 0 이라 판정 줄을 만들지 않습니다. 13. evidence 의 table·field 가 score_runs·bed_roster·ward_policy 에 실재하는 컬럼인지 대조합니다. 14. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 줄의 값 원문과 같은지 확인합니다. 15. summary 와 hedge 가 존댓말로 끝나는지 확인합니다. [수동 검증 · 사람이 봅니다] 1. 점수가 멈춘 병상이 한 병동에 몰렸을 때 VitalCare 쪽인지 병원 연동 서버 쪽인지는 FDE 엔지니어가 병원 전산팀과 함께 로그를 열어 정합니다. 2. 활력징후·혈액검사가 늦게 들어온 까닭이 측정 지연인지 입력 지연인지는 병동 간호 기록을 볼 수 있는 병원 담당자가 확인합니다. [평가 러너 · 2주차] 1. 7일치 아침 내보내기에 병상마다 손으로 단 판정과 CLI 판정이 일치하는 병상 수를 셉니다. 2. 세 파일을 PostgreSQL 에 올려 창 함수로 짠 같은 규칙의 SQL 판정과 Python 판정이 병상마다 같은지 대조합니다.