오믈렛
경력근거: 고객사에 직접 임베딩되어 비즈니스 문제 정의부터 구현, 프로덕션 배포, 운영, 제품 환류까지 전 과정을 책임지는
오믈렛은 '고객사에 직접 임베딩되어' 문제 정의부터 '프로덕션 배포, 운영, 제품 환류까지 전 과정을 책임지는' FDE 를 찾고, 우대사항에 '물류, 제조, 방산, 의료 등 오믈렛이 다루는 산업 도메인에 대한 이해' 를 적었습니다. OaaSIS TMS 가 내놓는 배차 계획은 고객사마다 다른 적재 한도와 근무 규칙, 납품 시간창 앞에서 되돌아오는데, 그 제약은 계약한 현장에 가야 손에 들어옵니다. '모호한 요구사항을 기술적으로 해결 가능한 정량적 과제 및 사양으로 재정의' 하라는 두 번째 업무가 이 도구의 입력표 그 자체입니다. 이 포트폴리오는 계획과 제약을 경로 단위로 맞춰 어긴 곳을 사유로 나누는 기술을 보여줍니다. 차량 경로 문제를 푸는 최적화 알고리즘이나 화물 운송 규제 해석 전문성은 주장하지 않습니다. 면접에서 그 알고리즘 쪽으로 질문이 들어오면 풀어 본 적 없다고 먼저 답하겠습니다. 제가 내놓을 수 있는 것은 현장이 말로 하던 제약을 적재 한도와 근무 분, 시간창이라는 표로 받아 낸 과정입니다.
공고가 '고객사에 직접 임베딩되어' 문제 정의부터 '프로덕션 배포, 운영, 제품 환류까지 전 과정을 책임지는' 엔지니어를 찾는다고 적었습니다. 최적화가 낸 계획을 현장이 그대로 쓰게 만드는 마지막 한 칸이 이 도구가 서는 자리입니다.
'고객사 현업 담당자와 밀착 소통하여' 얻은 말을 '모호한 요구사항을 기술적으로 해결 가능한 정량적 과제 및 사양으로 재정의' 하라고 공고가 적었습니다. '이 계획은 우리 현장에서 못 돈다' 는 말을 적재 한도와 근무 분, 납품 시간창이라는 숫자로 바꾸는 일이 그 재정의입니다.
'고객 맞춤형 솔루션을 재사용 가능한 모듈 및 플랫폼 표준 기능으로 추상화' 가 업무에 들어 있고, 우대사항은 '물류, 제조, 방산, 의료 등 오믈렛이 다루는 산업 도메인에 대한 이해' 를 듭니다. 고객사마다 다른 제약을 한 표로 받아 규칙으로 돌리면 그 규칙이 OaaSIS 쪽 표준 기능으로 넘어갑니다.
라우팅 엔진이 뽑아 준 하루치 배차 계획을 정차 단위로 읽어 경로마다 하나씩 판정합니다. 그 경로 정차들의 적재 중 가장 무거운 값을 차량 적재 한도와 견주고, 첫 정차 출발부터 마지막 정차 도착까지를 분으로 재어 기사 근무 한도와 견주며, 정차 도착 시각이 그 차량에 걸린 납품 시간창 안에 드는지 봅니다. 시간창은 동네가 아니라 차량마다 정해져 있어, 한 경로가 여러 동네를 물어도 그 차량의 창 하나로 견줍니다. 새벽 배송처럼 시간창이 자정을 넘는 경우도 같은 규칙으로 셉니다. 판정에 쓴 적재와 분, 한도 값을 결과에 함께 실어 배차 담당이 어느 정차를 손볼지 바로 봅니다.
고객사 물류 현장에 들어가 최적화를 붙이는 오믈렛 FDE 엔지니어
최적화가 낸 계획이 현장에서 되돌아오는 이유는 해가 나빠서가 아니라 그 현장의 적재 한도와 근무 규칙, 납품 시간창이 엔진에 들어가 있지 않기 때문입니다. 계획과 제약표를 경로마다 맞춰 보면 내보내기 전에 손볼 경로가 사유별로 나옵니다.
배차 계획 파일과 제약표를 넣으면 경로마다 제약 안·적재를 넘김·근무시간을 넘김·약속 시간창 밖·제약이 없는 차량 중 하나를 판정하고, 적재와 운행 분, 한도 값을 붙여 고객사별로 묶은 수정 목록을 터미널과 Markdown 파일로 내는 CLI 가 도는 완결품입니다. 제약표는 현장 담당이 채운 표를 받아 넣는 것으로 두고, 사내 시스템 연동은 3주차로 미룹니다.
여러 날치 판정을 합쳐 사유별 경로 수를 세고, 같은 차량이나 같은 차종에서 되풀이되는 사유를 엔진 입력값 손질 후보로 냅니다.
어긴 경로를 차량별로 나눠 고객사 배차 담당에게 보낼 수정 요청 문서를 조립하고, 제약표에 없던 차량은 받아야 할 제약값 목록으로 함께 냅니다.
FDE 엔지니어가 계획 날짜와 고객사 이름으로 명령을 걸면 경로 84개가 사유별로 줄지어 나오고 경로마다 적재와 운행 분이 한도와 나란히 서는 장면을 시연합니다.
손으로 정답을 단 경로 60건에 판정을 돌려 사유별 일치율을 냅니다. 계획도 제약표도 작성자가 지어낸 합성 기준이므로 이 값은 성능 검증이 아니라 데모 조건에서의 동작 확인입니다.
기사 근무 한도가 비어 있는 차량, 마지막 정차의 출발 시각이 빠진 경로, 자정을 넘는 시간창, 제약표에 없는 차량 번호를 섞은 입력 30종을 먼저 통과시킵니다. 출력 형식이 깨지지 않고 사유별 경로 수가 그대로 나와야 다음 단계로 갑니다.
적재 한도를 kg 에서 팔레트 수로 세는 현장으로 갈아끼우면 어느 경로의 사유가 뒤집히는지 회귀로 확인합니다. 규칙을 바꿔도 판정기가 그대로 서는지 보는 자리입니다.
위 네 줄은 작성자가 지어낸 계획과 제약표를 상대로 잰 것이라 성능이 아닙니다. 새벽에 기사들이 계획을 다시 짜는 일이 줄었는지는 OaaSIS TMS 를 붙인 고객사의 실 배차 기록을 같은 규칙에 통과시켜야 답이 나옵니다. 이 포트폴리오가 맡는 구간은 그 실행을 바로 걸 수 있도록 규칙과 평가 러너를 세워 두는 데까지입니다.
배차 계획도 제약표도 제 손으로 만든 더미입니다. 그 위에서 잰 사유별 일치율은 답을 미리 아는 사람이 매긴 점수라 성능이 아니라 동작 확인입니다. 고객사 실 배차 기록이 붙어야 성능 이야기가 시작됩니다.
더미가 깨끗하면 규칙이 늘 맞으므로, 기사 근무 한도가 비어 있는 차량과 출발 시각이 빠진 정차, 자정을 넘는 시간창, 제약표에 없는 차량을 일부러 넣어 두었습니다.
이 포트폴리오는 계획과 제약을 경로 단위로 맞춰 어긴 곳을 사유로 나누는 기술을 보여줍니다. 차량 경로 문제를 푸는 최적화 알고리즘이나 화물 운송 규제 해석 전문성은 주장하지 않습니다. 판정은 적재 비교와 분 계산이 정하고 클로드는 수정 요청 문구만 씁니다.
제약을 어겼다는 판정이 그 계획을 버리라는 뜻은 아닙니다. 현장이 합의한 예외로 도는 경로도 있어, 고치는 쪽이 계획인지 제약표인지는 배차 담당이 정합니다.
배차 계획과 기사 근무 기록은 기사 개인을 특정하는 값이라 고객사 밖으로 나가지 않는 것이 보통입니다. 그래서 이 도구를 현장에 올릴 때 가장 먼저 정할 것은 기능이 아니라 값이 어디까지 나가는지입니다. 판정은 현장 망 안에서 끝내고, 문구 작성에 넘기는 것은 경로 번호와 적재·분 수치까지로 한정합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
라우팅 엔진이 내놓은 하루치 배차 계획입니다. 정차 하나가 한 행이고 계획 번호와 날짜, 고객사, 경로 번호, 차량, 기사, 정차 순번과 이름, 도착·출발 시각, 그 정차에서 차에 실려 있는 중량을 담습니다. 새벽 배송 경로는 날짜가 넘어간 시각으로 들어오고, 마지막 정차의 출발 시각은 복귀 기록이 안 올라와 비어 있기도 합니다.
| plan_no | plan_date | client_name | route_id | vehicle_id | driver_id | stop_seq | stop_name | arrive_at | depart_at | load_kg |
|---|---|---|---|---|---|---|---|---|---|---|
| SP-2041-01 | 2026-09-22 | 남부물류 | R-2041 | 80나1122 | DR-014 | 1 | 가락 물류창고 | 2026-09-22T09:20 | 2026-09-22T09:45 | 3,420 |
| SP-2041-02 | 2026-09-22 | 남부물류 | R-2041 | 80나1122 | DR-014 | 2 | 송파 대리점 | 2026-09-22T10:30 | 2026-09-22T10:55 | 2,180 |
| SP-2041-03 | 2026-09-22 | 남부물류 | R-2041 | 80나1122 | DR-014 | 3 | 강동 편의점 물류 | 2026-09-22T11:40 | 2026-09-22T12:05 | 900 |
| SP-2077-01 | 2026-09-22 | 남부물류 | R-2077 | 81가4410 | DR-022 | 1 | 안성 허브 | 2026-09-22T06:10 | 2026-09-22T06:30 | 4,120 |
| SP-2077-02 | 2026-09-22 | 남부물류 | R-2077 | 81가4410 | DR-022 | 2 | 평택 공장 자재동 | 2026-09-22T17:50 | 2026-09-22T18:15 | 1,500 |
아래 JSON Schema 를 그대로 만족하는 배차 정차 행을 400개 더 만들어 주세요. 경로를 78개 두고 경로마다 정차를 2~8개 답니다. 위 샘플 여섯 경로와 합쳐 경로가 84개가 되게 합니다. 한 경로 안에서 stop_seq 는 1부터 이어지고 도착 시각은 순번대로 늦어지며, 적재는 뒤 정차로 갈수록 줄어듭니다. 새벽 경로는 날짜가 하루 넘어간 시각으로 쓰고, 스무 행에 한 번꼴로 마지막 정차의 depart_at 을 비웁니다. 제약표에 없는 차량 번호도 몇 경로 넣으세요. 형식을 일부러 깨뜨린 파일도 5건 만들되 도착 시각에 09:20 처럼 시각만 적거나 적재를 '3,420kg' 문자열로 적는 식으로 만드세요. 판정 결과나 사유는 넣지 마세요.
{"type": "object", "properties": {"plan_no": {"type": "string"}, "plan_date": {"type": "string"}, "client_name": {"type": "string"}, "route_id": {"type": "string"}, "vehicle_id": {"type": "string"}, "driver_id": {"type": "string"}, "stop_seq": {"type": "integer"}, "stop_name": {"type": "string"}, "arrive_at": {"type": ["string", "null"]}, "depart_at": {"type": ["string", "null"]}, "load_kg": {"type": ["integer", "null"]}}, "required": ["plan_no", "plan_date", "client_name", "route_id", "vehicle_id", "driver_id", "stop_seq", "stop_name", "arrive_at", "depart_at", "load_kg"]}{"type": "object", "properties": {"plan_no": {"type": "string"}, "plan_date": {"type": "string"}, "client_name": {"type": "string"}, "route_id": {"type": "string"}, "vehicle_id": {"type": "string"}, "driver_id": {"type": "string"}, "stop_seq": {"type": "integer"}, "stop_name": {"type": "string"}, "arrive_at": {"type": ["string", "null"]}, "depart_at": {"type": ["string", "null"]}, "load_kg": {"type": ["integer", "null"]}}, "required": ["plan_no", "plan_date", "client_name", "route_id", "vehicle_id", "driver_id", "stop_seq", "stop_name", "arrive_at", "depart_at", "load_kg"]}고객사 배차 담당이 채워 준 차량별 운영 제약표입니다. 제약 번호와 고객사, 차량과 차종, 적재 한도, 기사 근무 한도 분, 그 차량에 걸린 납품 시간창 시작·끝을 담습니다. 시간창은 동네 단위가 아니라 차량 행에 붙어 있습니다. 기사 근무 한도는 현장이 아직 정하지 않아 비어 있는 차량이 있고, 새벽 배송 차량은 시작이 끝보다 늦은 시간창으로 들어옵니다. 계획에 나오는 차량 중 이 표에 아예 없는 것도 두어 사유가 갈리게 둡니다.
| constraint_no | client_name | vehicle_id | vehicle_type | max_load_kg | driver_max_minutes | window_start | window_end |
|---|---|---|---|---|---|---|---|
| OC-0001 | 남부물류 | 80나1122 | 3.5톤 윙바디 | 3,000 | 540 | 09:00 | 18:00 |
| OC-0002 | 남부물류 | 81가4410 | 5톤 카고 | 4,500 | 540 | 06:00 | 15:00 |
| OC-0003 | 남부물류 | 82서9087 | 2.5톤 냉장 | 2,500 | 480 | 09:00 | 12:00 |
| OC-0004 | 남부물류 | 80나3391 | 3.5톤 윙바디 | 3,200 | 540 | 08:00 | 18:00 |
| OC-0005 | 남부물류 | 83우2217 | 2.5톤 냉장 | 2,800 | — | 22:00 | 06:00 |
아래 JSON Schema 를 그대로 만족하는 차량 제약 행을 120개 더 만들어 주세요. 차량 번호는 한국 화물차 표기로 쓰고 차종은 1톤 탑차·2.5톤 냉장·3.5톤 윙바디·5톤 카고·11톤 윙바디 중에서 고릅니다. 적재 한도는 차종에 맞춰 1,200에서 11,000 사이로 두고, 근무 한도는 480·540·600 중에서 두되 열 행에 한 번꼴로 비웁니다. 시간창은 대부분 주간이되 몇 행은 22:00~06:00 처럼 자정을 넘게 씁니다. 배차 계획에 나오는 차량 중 일부는 일부러 넣지 마세요. 판정 결과는 넣지 마세요.
{"type": "object", "properties": {"constraint_no": {"type": "string"}, "client_name": {"type": "string"}, "vehicle_id": {"type": "string"}, "vehicle_type": {"type": "string"}, "max_load_kg": {"type": "integer"}, "driver_max_minutes": {"type": ["integer", "null"]}, "window_start": {"type": "string"}, "window_end": {"type": "string"}}, "required": ["constraint_no", "client_name", "vehicle_id", "vehicle_type", "max_load_kg", "driver_max_minutes", "window_start", "window_end"]}{"type": "object", "properties": {"constraint_no": {"type": "string"}, "client_name": {"type": "string"}, "vehicle_id": {"type": "string"}, "vehicle_type": {"type": "string"}, "max_load_kg": {"type": "integer"}, "driver_max_minutes": {"type": ["integer", "null"]}, "window_start": {"type": "string"}, "window_end": {"type": "string"}}, "required": ["constraint_no", "client_name", "vehicle_id", "vehicle_type", "max_load_kg", "driver_max_minutes", "window_start", "window_end"]}| Python | 적재 집계와 분 단위 시간 계산, 다섯 사유 판정 규칙과 CLI 를 맡습니다. |
| TypeScript | 고객사 담당이 제약표를 채워 넣는 입력 화면을 3주차에 붙일 때 씁니다. |
| SQL | 여러 날치 판정을 쌓아 사유별 경로 수와 되풀이되는 차량을 2주차에 집계합니다. |
| ETL | 고객사가 내려준 계획 파일과 제약표를 판정이 읽는 두 표로 맞춰 올리는 적재 단계를 맡습니다. |
| LLM API | 어긴 경로 하나를 배차 담당이 그대로 읽을 수정 요청 문구로 바꿉니다. |
| Evals | 손으로 정답을 단 경로 60건에 판정을 돌려 사유별 일치율을 재는 평가 러너를 2주차에 둡니다. |
| Docker | 판정 실행기를 이미지 한 벌로 묶어 고객사 환경에서 같은 결과가 나오게 합니다. |
| Kubernetes | 하루치 판정을 정해진 시각에 도는 작업으로 올려 3주차 운영에 얹습니다. |
| AWS | 2주차부터 판정 배치와 결과 파일 보관을 AWS 로 배포해 운영합니다. |
| GCP | 고객사 환경이 GCP 인 현장을 위해 같은 컨테이너 이미지를 두 클라우드에 올려 둡니다. |
✗ fail → 제약표에 없는 차량이 절반을 넘으면 판정을 더 돌리지 않습니다. 용차까지 포함한 차량 목록을 배차 담당에게 받아 제약표를 다시 세우는 것이 먼저입니다.
✓ pass → 2주차 사유별 집계로 넘어갑니다.
✗ fail → 날마다 다른 경로만 걸리면 손질 후보를 내지 않고 경로별로 둡니다.
✓ pass → 3주차 수정 요청 문서로 넘어갑니다.
배차 경로 하나가 고객사 운영 제약을 지키는지 정합니다. 다섯 사유 중 하나를 싣고 그 경로의 가장 큰 적재와 적재 한도, 운행 분과 근무 한도, 그 차량에 걸린 시간창을 벗어난 분을 함께 담아 수정 목록과 요청 문서가 그대로 읽습니다.
{"type": "object", "properties": {"verdict_no": {"type": "string"}, "plan_date": {"type": "string"}, "client_name": {"type": "string"}, "route_id": {"type": "string"}, "vehicle_id": {"type": "string"}, "driver_id": {"type": "string"}, "constraint_no": {"type": ["string", "null"]}, "verdict": {"type": "string", "enum": ["제약 안", "적재를 넘김", "근무시간을 넘김", "약속 시간창 밖", "제약이 없는 차량"]}, "peak_load_kg": {"type": ["integer", "null"], "description": "그 route_id 정차들의 load_kg 중 가장 큰 값입니다. load_kg 가 전부 비어 있으면 null 입니다"}, "load_limit_kg": {"type": ["integer", "null"], "description": "그 vehicle_id 의 ops_constraints.max_load_kg 입니다. 제약표에 차량이 없으면 null 입니다"}, "route_minutes": {"type": ["integer", "null"], "description": "그 route_id 의 stop_seq 가장 앞 행 depart_at 에서 가장 뒤 행 arrive_at 까지의 분입니다. 날짜가 넘어가는 새벽 운행도 이어 셉니다. 둘 중 하나가 비어 있으면 null 입니다"}, "driver_limit_minutes": {"type": ["integer", "null"], "description": "그 vehicle_id 의 ops_constraints.driver_max_minutes 입니다. 제약표에 차량이 없거나 값이 비어 있으면 null 입니다"}, "window_over_minutes": {"type": ["integer", "null"], "description": "약속 시간창을 가장 많이 벗어난 정차의 초과 분입니다. 시간창은 ops_constraints 의 그 vehicle_id 행에 달린 값이라 한 경로의 정차 전부를 같은 창으로 봅니다. window_start 가 window_end 보다 늦으면 자정을 넘는 시간창으로 읽습니다. 벗어난 정차가 없거나 제약표에 차량이 없으면 null 입니다"}, "worst_stop_seq": {"type": ["integer", "null"], "description": "판정 근거가 된 정차의 stop_seq 입니다. 차량 단위 사유이면 null 입니다"}, "evidence": {"type": "array", "items": {"type": "object", "properties": {"table": {"type": "string", "enum": ["dispatch_plan", "ops_constraints"]}, "field": {"type": "string"}, "agg": {"type": ["string", "null"]}, "value": {}}, "required": ["table", "field", "value"]}}, "summary": {"type": "string"}, "hedge": {"type": ["string", "null"]}}, "required": ["verdict_no", "plan_date", "route_id", "vehicle_id", "driver_id", "verdict", "evidence", "summary"]}[
{
"verdict_no": "DV-001",
"plan_date": "2026-09-22",
"client_name": "남부물류",
"route_id": "R-2041",
"vehicle_id": "80나1122",
"driver_id": "DR-014",
"constraint_no": "OC-0001",
"verdict": "적재를 넘김",
"peak_load_kg": 3420,
"load_limit_kg": 3000,
"route_minutes": 115,
"driver_limit_minutes": 540,
"window_over_minutes": null,
"worst_stop_seq": 1,
"evidence": [
{
"table": "dispatch_plan",
"field": "load_kg",
"agg": "max",
"value": 3420
},
{
"table": "ops_constraints",
"field": "max_load_kg",
"agg": null,
"value": 3000
},
{
"table": "dispatch_plan",
"field": "route_id",
"agg": null,
"value": "R-2041"
}
],
"summary": "첫 정차에서 3,420kg 을 실은 계획인데 그 차량의 적재 한도가 3,000kg 이라 420kg 이 넘습니다.",
"hedge": null
},
{
"verdict_no": "DV-002",
"plan_date": "2026-09-22",
"client_name": "남부물류",
"route_id": "R-2077",
"vehicle_id": "81가4410",
"driver_id": "DR-022",
"constraint_no": "OC-0002",
"verdict": "근무시간을 넘김",
"peak_load_kg": 4120,
"load_limit_kg": 4500,
"route_minutes": 680,
"driver_limit_minutes": 540,
"window_over_minutes": 170,
"worst_stop_seq": null,
"evidence": [
{
"table": "dispatch_plan",
"field": "depart_at",
"agg": null,
"value": "2026-09-22T06:30"
},
{
"table": "dispatch_plan",
"field": "arrive_at",
"agg": null,
"value": "2026-09-22T17:50"
},
{
"table": "ops_constraints",
"field": "driver_max_minutes",
"agg": null,
"value": 540
}
],
"summary": "안성 허브를 06시 30분에 떠나 평택에 17시 50분에 닿는 계획이라 기사 운행이 680분이 되고 한도 540분을 140분 넘깁니다.",
"hedge": "이 경로는 약속 시간창도 170분 벗어나지만, 운행 길이를 줄이면 도착 시각이 함께 당겨지므로 근무시간 사유로 먼저 냅니다."
},
{
"verdict_no": "DV-003",
"plan_date": "2026-09-22",
"client_name": "남부물류",
"route_id": "R-2103",
"vehicle_id": "82서9087",
"driver_id": "DR-031",
"constraint_no": "OC-0003",
"verdict": "약속 시간창 밖",
"peak_load_kg": 2280,
"load_limit_kg": 2500,
"route_minutes": 290,
"driver_limit_minutes": 480,
"window_over_minutes": 160,
"worst_stop_seq": 2,
"evidence": [
{
"table": "dispatch_plan",
"field": "arrive_at",
"agg": null,
"value": "2026-09-22T14:40"
},
{
"table": "ops_constraints",
"field": "window_end",
"agg": null,
"value": "12:00"
},
{
"table": "ops_constraints",
"field": "window_start",
"agg": null,
"value": "09:00"
}
],
"summary": "왕십리 매장 도착이 14시 40분인데 그 차량에 걸린 납품 시간창이 09시부터 12시까지라 160분 늦습니다.",
"hedge": null
},
{
"verdict_no": "DV-004",
"plan_date": "2026-09-22",
"client_name": "남부물류",
"route_id": "R-2150",
"vehicle_id": "84바5512",
"driver_id": "DR-040",
"constraint_no": null,
"verdict": "제약이 없는 차량",
"peak_load_kg": 1960,
"load_limit_kg": null,
"route_minutes": 65,
"driver_limit_minutes": null,
"window_over_minutes": null,
"worst_stop_seq": null,
"evidence": [
{
"table": "dispatch_plan",
"field": "vehicle_id",
"agg": null,
"value": "84바5512"
},
{
"table": "ops_constraints",
"field": "vehicle_id",
"agg": "count",
"value": 11
}
],
"summary": "84바5512 로 짠 경로인데 제약표 11대 어디에도 그 차량이 서 있지 않아 적재도 운행 길이도 견줄 기준이 없습니다.",
"hedge": "용차로 붙인 차량이면 제약표에 아직 안 올라온 것일 수 있으므로, 반려가 아니라 받아야 할 제약값으로 넘깁니다."
},
{
"verdict_no": "DV-005",
"plan_date": "2026-09-22",
"client_name": "남부물류",
"route_id": "R-2188",
"vehicle_id": "80나3391",
"driver_id": "DR-008",
"constraint_no": "OC-0004",
"verdict": "제약 안",
"peak_load_kg": 2780,
"load_limit_kg": 3200,
"route_minutes": 425,
"driver_limit_minutes": 540,
"window_over_minutes": null,
"worst_stop_seq": null,
"evidence": [
{
"table": "dispatch_plan",
"field": "load_kg",
"agg": "max",
"value": 2780
},
{
"table": "ops_constraints",
"field": "max_load_kg",
"agg": null,
"value": 3200
},
{
"table": "dispatch_plan",
"field": "depart_at",
"agg": null,
"value": null
}
],
"summary": "적재 2,780kg 에 운행 425분으로 세 가지 한도를 모두 지킵니다.",
"hedge": "마지막 정차의 출발 시각이 비어 있지만 운행 길이는 첫 출발과 마지막 도착으로 재므로 판정은 그대로 섭니다."
},
{
"verdict_no": "DV-006",
"plan_date": "2026-09-22",
"client_name": "남부물류",
"route_id": "R-2205",
"vehicle_id": "83우2217",
"driver_id": "DR-011",
"constraint_no": "OC-0005",
"verdict": "제약 안",
"peak_load_kg": 2410,
"load_limit_kg": 2800,
"route_minutes": 75,
"driver_limit_minutes": null,
"window_over_minutes": null,
"worst_stop_seq": null,
"evidence": [
{
"table": "ops_constraints",
"field": "driver_max_minutes",
"agg": null,
"value": null
},
{
"table": "dispatch_plan",
"field": "arrive_at",
"agg": null,
"value": "2026-09-23T01:20"
},
{
"table": "ops_constraints",
"field": "window_start",
"agg": null,
"value": "22:00"
}
],
"summary": "22시부터 06시까지인 새벽 시간창 안에 두 정차가 모두 들어오고 적재도 2,410kg 으로 한도 안입니다.",
"hedge": "이 차량은 기사 근무 한도가 비어 있어 운행 길이 검사를 걸지 않았습니다. 그 값을 받아 넣으면 판정이 바뀔 수 있습니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 다섯 값 중 하나인지 확인합니다. 2. verdict_no·plan_date·route_id·vehicle_id·driver_id·verdict·evidence·summary 가 모두 채워졌는지 확인합니다. 3. peak_load_kg 가 그 route_id 정차들의 load_kg 중 가장 큰 값과 같은지 다시 계산합니다. 4. route_minutes 가 그 route_id 의 stop_seq 가장 앞 행 depart_at 에서 가장 뒤 행 arrive_at 까지의 분과 같은지 다시 계산합니다. 날짜가 넘어가는 새벽 운행도 이어 세고, 둘 중 하나가 비어 있으면 null 인지 봅니다. 5. window_over_minutes 가 그 경로에서 시간창을 가장 많이 벗어난 정차의 초과 분과 같은지 다시 계산합니다. 시간창은 그 차량의 ops_constraints 행에서 가져오고, window_start 가 window_end 보다 늦으면 자정을 넘는 창으로 읽습니다. 6. peak_load_kg 가 0 이상 30000 이하, route_minutes 가 0 이상 1440 이하인지 범위를 봅니다. 7. 그 vehicle_id 의 ops_constraints 행이 없으면 verdict 가 제약이 없는 차량이고 load_limit_kg·driver_limit_minutes·window_over_minutes 가 모두 null 인지 확인합니다. 8. 제약표에 차량이 있는 경로에서 peak_load_kg 가 load_limit_kg 보다 크면 verdict 가 적재를 넘김인지 확인합니다. 9. 7·8번에 걸리지 않은 경로에서만 봅니다 — route_minutes 가 driver_limit_minutes 보다 크면 verdict 가 근무시간을 넘김입니다. driver_limit_minutes 가 비어 있으면 이 검사를 돌리지 않고 넘어갑니다. 10. 7·8·9번에 걸리지 않은 경로에서만 봅니다 — window_over_minutes 가 0 보다 크면 verdict 가 약속 시간창 밖입니다. R-2077 처럼 근무시간에 이미 걸린 경로는 시간창을 벗어나도 9번 사유로 남고, 벗어난 분은 window_over_minutes 와 hedge 에만 적습니다. 11. verdict 가 제약 안이면 7~10번 어느 검사에도 걸리지 않는지 다시 봅니다. 12. 7~10번이 곧 판정 차례입니다 — 제약이 없는 차량, 적재를 넘김, 근무시간을 넘김, 약속 시간창 밖 순으로 먼저 걸리는 갈래 하나만 씁니다. 뒤 차례 검사는 앞에서 안 걸린 경로에만 돌리므로, 한 경로가 두 제약에 동시에 걸려도 사유는 하나입니다. 다만 driver_max_minutes 가 비어 있는 차량은 운행 길이 검사를 돌리지 않고 넘어갑니다. R-2205 가 그런 경로입니다. 13. constraint_no 가 있으면 ops_constraints 에 실재하는 행 번호이고 그 행의 vehicle_id 가 경로의 차량과 같은지 대조합니다. 14. evidence 의 field 이름이 두 데이터셋에 실재하는 컬럼인지 대조합니다. 15. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 행 값과 같은지 확인합니다. 16. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 1. 손으로 정답을 단 경로 60건에 판정을 돌려 사유별 일치율을 냅니다. 2. 차량마다 걸린 횟수를 세어 두 날 이상 되풀이되는 차량을 추립니다. [수동 검증 · 사람이 봅니다] 1. 현장이 합의한 예외로 도는 경로인지 배차 담당이 정합니다. 2. 제약표에 없던 차량의 적재 한도와 근무 한도를 고객사에서 받아 넣습니다.