업스테이지
경력근거: 문서 내 의미 있는 정보를 자동으로 추출하는 Key-Value 추출 기술
※ 실제 산출물이 아닌 AI 예측 시안입니다.
업스테이지는 OCR과 Key-Value 추출을 제품으로 파는 회사이고, 이 포지션은 고객사 PoC와 벤치마크 테스트를 이끄는 자리입니다. 공공기관은 이미 대장에 손으로 적어 온 값이 있어서 그 값이 곧 정답표가 되는데, 항목 이름을 기관마다 다르게 적어 두기 때문에 이름을 맞춘 뒤라야 대조가 성립합니다. 이 순서는 기관마다 다른 서식을 상대하는 회사에서만 문제가 됩니다. 공고 업무는 기술 지원부터 설치, 파인튜닝까지 걸쳐 있는데 1주 범위에서 고를 수 있는 것은 하나라 대조 판정 축을 골랐습니다. 이 포트폴리오는 항목 이름을 표준으로 맞추고 값을 규칙으로 대조해 근거를 남기는 기술을 보여줍니다. OCR 모델 자체의 성능 개선이나 파인튜닝 전문성은 주장하지 않습니다. 그 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
주요 업무에 고객사 대상 PoC와 벤치마크 테스트 리딩이 적혀 있습니다. 추출 결과를 고객사 정답과 대조해 항목별로 가르는 일이 그 테스트의 판정부입니다.
고객사 서버에 클러스터와 AI 솔루션을 설치하는 일이 업무에 있습니다. 공공기관 환경이라 화면을 띄우기보다 명령 한 번으로 돌려 리포트를 남기는 형태가 맞습니다.
OCR과 Key-Value 추출, Document Parsing이 회사 제품의 핵심으로 적혀 있습니다. 그 추출 결과를 무엇과 견줘 맞다고 말할지가 도입 현장의 첫 질문입니다.
문서에서 뽑은 항목값과 고객사가 쓰던 정답값을 대조해, 항목별로 맞았는지 어긋났는지를 나눠 출력하고 고객 보고용 리포트를 남기는 CLI입니다. 판정은 값을 정규화한 뒤 문자열로 견주는 대조로 정합니다.
공공기관 PoC를 맡은 담당자입니다. 모델이 뽑은 결과를 엑셀에 붙이고 기관이 쓰던 대장값을 옆 열에 붙여 눈으로 맞춰 보는데, 같은 항목을 기관마다 다른 이름으로 적어 두어 어느 열끼리 견줘야 하는지부터 맞추느라 하루가 갑니다.
항목 이름이 표준으로 맞춰져 있으면, 추출이 맞았는지는 사람이 눈으로 비교하지 않아도 값 대조로 갈라집니다.
추출 결과 파일과 정답 파일을 인자로 받아 항목별 판정을 터미널에 출력하고 고객 보고용 리포트를 떨구는 CLI입니다. 모델을 직접 돌리지 않고 이미 뽑아 둔 결과 파일만 읽습니다.
항목별 일치 건수와 사유별 분포를 리포트에 표로 넣어, 어느 항목이 도입에 걸림돌인지 한 장으로 보여주는 구성입니다.
항목 이름 사전을 파일로 분리해 기관마다 다른 서식을 같은 판정으로 돌리고, 컨테이너 이미지로 묶어 고객사 서버에서 그대로 실행합니다.
담당자가 추출 결과 50행과 정답 파일을 넣어 항목별 판정표와 고객 보고용 리포트가 나오는 것을 시연합니다.
결측·중복·미등록 항목이 섞인 입력 30종을 넣어도 출력 형식이 깨지지 않고, 판정하지 못한 항목은 사유가 붙어 남습니다.
정답이 비어 있는 항목은 맞은 것으로 세지 않고 정답 없음으로 따로 답니다. 채점하지 못한 것을 정답으로 만들지 않습니다.
판정과 손으로 만든 정답표를 맞춰 사유별로 어긋난 건을 셉니다. 단, 추출값과 정답을 모두 작성자가 만든 합성 기준이라 성능 측정이 아니라 데모 조건 동작입니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 더미데이터 자가채점이며, 실제 추출 정확도는 업스테이지 모델의 실제 출력과 기관의 실제 대장이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
쉬운 건만 넣으면 무엇을 맞혀도 증거가 되지 않습니다. 항목 이름의 띄어쓰기가 흔들린 건, 모델이 값을 못 뽑은 건, 대장에 정답이 아직 없는 건을 섞어 둔 이유입니다.
값이 다르다고 해서 모델이 틀렸다고 단정할 수 없습니다. 기관 대장 쪽이 옛 값일 수 있으므로, 판정은 두 값이 다르다는 사실까지만 읽어야 합니다.
실제 도입하면 기관마다 항목 이름과 값 표기 규칙이 다릅니다. 표준 사전을 기관과 합의해 채우는 일이 도입 단계의 실제 과제입니다.
OCR 모델의 성능을 올리는 일은 이 도구의 범위 밖입니다. 뽑아 온 결과를 견주기만 하므로, 왜 틀렸는지의 원인 분석은 모델링팀이 맡습니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
문서에서 뽑은 항목값입니다. 항목 이름이 서식마다 띄어쓰기와 대소문자로 흔들리고, 사전에 없는 신규 항목과 같은 추출 번호가 두 번 들어온 건, 모델이 값을 못 뽑은 행을 일부러 섞었습니다.
| extraction_no | doc_no | field_key_raw | extracted_value | confidence | page_no |
|---|---|---|---|---|---|
| EX-001 | DOC-001 | 사업자등록번호 | 123-45-67890 | 0.98 | 1 |
| EX-002 | DOC-001 | 계약금액 | 45,000,000 | 0.94 | 1 |
| EX-003 | DOC-001 | 계약일자 | 2026-03-14 | 0.91 | 1 |
| EX-004 | DOC-002 | 사업자 등록번호 | 222-33-44444 | 0.87 | 1 |
| EX-005 | DOC-002 | 계약금액 | 12,000,000 | 0.55 | 2 |
아래 JSON Schema에 맞는 문서 추출 결과 행을 더 만들어 주세요. {"type":"object","properties":{"extraction_no":{"type":"string"},"doc_no":{"type":"string"},"field_key_raw":{"type":"string"},"extracted_value":{"type":["string","null"]},"confidence":{"type":"number"},"page_no":{"type":"integer"}},"required":["extraction_no","doc_no","field_key_raw","extracted_value","confidence","page_no"]} 한 doc_no에 항목이 두 건 이상 붙도록 만들어 주세요. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 미매핑으로 field_key_raw가 사전에 없어 field_key_id가 null인 행, 둘째 표기흔들림으로 같은 항목을 사업자등록번호와 사업자 등록번호처럼 띄어쓰기만 다르게 적은 행, 셋째 중복으로 같은 extraction_no가 두 행에 있는 재적재 건, 넷째 결측으로 extracted_value가 null이고 confidence가 0.0인 미추출 행, 다섯째 경계극단으로 confidence가 1.0인 행. 또한 형식 깨뜨리기 시도 3건을 별도 파일로 만들어 confidence에 문자열을 넣거나 doc_no를 빼서 스키마 검증이 걸러내는지 확인하게 해 주세요.{"type":"object","properties":{"extraction_no":{"type":"string"},"doc_no":{"type":"string"},"field_key_raw":{"type":"string"},"extracted_value":{"type":["string","null"]},"confidence":{"type":"number"},"page_no":{"type":"integer"}},"required":["extraction_no","doc_no","field_key_raw","extracted_value","confidence","page_no"]}기관이 대장에 손으로 적어 온 정답값입니다. 아직 채워지지 않은 항목을 일부러 섞었습니다.
| doc_no | field_key_id | field_label | expected_value | source_system |
|---|---|---|---|---|
| DOC-001 | biz_reg_no | 사업자등록번호 | 123-45-67890 | 계약대장 |
| DOC-001 | contract_amount | 계약금액 | 45000000 | 계약대장 |
| DOC-001 | contract_date | 계약일자 | 2026-03-14 | 계약대장 |
| DOC-002 | biz_reg_no | 사업자등록번호 | 222-33-44444 | 계약대장 |
| DOC-002 | contract_amount | 계약금액 | 12500000 | 계약대장 |
아래 JSON Schema에 맞는 기관 대장 정답 행을 더 만들어 주세요. {"type":"object","properties":{"doc_no":{"type":"string"},"field_key_id":{"type":"string"},"field_label":{"type":"string"},"expected_value":{"type":["string","null"]},"source_system":{"type":"string"}},"required":["doc_no","field_key_id","field_label","expected_value","source_system"]} field_label은 한글로 적어 주세요. 금액은 쉼표 없이 숫자만 적어 추출값과 표기가 다르게 해 주세요. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 결측으로 expected_value가 null이라 채점할 수 없는 행, 둘째 추출 결과에는 있는데 대장에는 아예 없는 doc_no와 field_key_id 조합. 또한 형식 깨뜨리기 시도 3건을 별도 파일로 만들어 field_key_id를 빼거나 expected_value에 숫자 타입을 넣어 스키마 검증이 걸러내는지 확인하게 해 주세요.{"type":"object","properties":{"doc_no":{"type":"string"},"field_key_id":{"type":"string"},"field_label":{"type":"string"},"expected_value":{"type":["string","null"]},"source_system":{"type":"string"}},"required":["doc_no","field_key_id","field_label","expected_value","source_system"]}| Python | 추출 결과와 정답을 읽어 대조 판정을 돌리고 리포트를 만드는 본체입니다. |
| AI/ML | 모델이 뽑은 항목값과 확신도를 입력으로 받아 판정에 씁니다. 모델 학습은 이 도구의 범위 밖입니다. |
| Ubuntu | 고객사 서버 기본 환경으로 보고 경로와 줄바꿈 처리를 여기에 맞춥니다. |
| Windows | 기관 담당자 PC에서도 같은 명령이 돌도록 경로 구분자와 인코딩을 함께 다룹니다. |
| Java | 기관 기존 시스템이 내려주는 결과 파일을 읽을 때 쓰는 보조 변환기입니다. |
| 컨테이너 | 판정기를 이미지로 묶어 폐쇄망 고객사 서버에서도 같은 방식으로 돌립니다. |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 항목별 일치 건수를 표로 정리합니다.
✓ pass → 리포트까지가 한 덩어리입니다. 더 키우려면 항목 사전을 기관별로 분리하고 컨테이너로 묶습니다.
추출 항목 한 건의 판정 결과입니다. 정답과 맞았는지, 어긋났다면 왜인지를 근거와 함께 담습니다.
{"type":"object","properties":{"extraction_no":{"type":"string"},"verdict":{"type":"string","enum":["match","value_mismatch","extraction_missing","truth_missing","field_unmapped","duplicate_extraction"]},"normalized_extracted":{"type":["string","null"]},"normalized_expected":{"type":["string","null"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{},"quote":{"type":["string","null"]}},"required":["field","value"]}},"next_action":{"type":"string"}},"required":["extraction_no","verdict","normalized_extracted","normalized_expected","evidence","next_action"]}[
{
"extraction_no": "EX-001",
"verdict": "match",
"normalized_extracted": "1234567890",
"normalized_expected": "1234567890",
"evidence": [
{
"field": "extracted_value",
"value": "123-45-67890"
},
{
"field": "expected_value",
"value": "123-45-67890"
},
{
"field": "confidence",
"value": 0.98
}
],
"next_action": "값이 맞습니다. 기준선 항목으로 남깁니다."
},
{
"extraction_no": "EX-002",
"verdict": "match",
"normalized_extracted": "45000000",
"normalized_expected": "45000000",
"evidence": [
{
"field": "extracted_value",
"value": "45,000,000"
},
{
"field": "expected_value",
"value": "45000000"
},
{
"field": "confidence",
"value": 0.94
}
],
"next_action": "쉼표만 다르고 값이 같습니다. 표기 정규화가 먹었으므로 그대로 둡니다."
},
{
"extraction_no": "EX-005",
"verdict": "value_mismatch",
"normalized_extracted": "12000000",
"normalized_expected": "12500000",
"evidence": [
{
"field": "extracted_value",
"value": "12,000,000"
},
{
"field": "expected_value",
"value": "12500000"
},
{
"field": "confidence",
"value": 0.55
}
],
"next_action": "금액이 어긋났고 확신도도 낮습니다. 원본 문서의 해당 면을 사람이 다시 봅니다."
},
{
"extraction_no": "EX-006",
"verdict": "extraction_missing",
"normalized_extracted": null,
"normalized_expected": "2026-03-20",
"evidence": [
{
"field": "extracted_value",
"value": null
},
{
"field": "expected_value",
"value": "2026-03-20"
},
{
"field": "page_no",
"value": 2
}
],
"next_action": "모델이 값을 뽑지 못했습니다. 맞은 것으로 세지 않고 해당 면의 레이아웃을 확인합니다."
},
{
"extraction_no": "EX-009",
"verdict": "truth_missing",
"normalized_extracted": "8900000",
"normalized_expected": null,
"evidence": [
{
"field": "extracted_value",
"value": "8,900,000"
},
{
"field": "expected_value",
"value": null
},
{
"field": "source_system",
"value": "계약대장"
}
],
"next_action": "대장에 정답이 비어 있어 채점하지 못합니다. 기관에 값을 요청합니다."
},
{
"extraction_no": "EX-008",
"verdict": "field_unmapped",
"normalized_extracted": "긴급복구",
"normalized_expected": null,
"evidence": [
{
"field": "field_key_raw",
"value": "수의계약 사유",
"quote": "수의계약"
}
],
"next_action": "사전에 없는 항목입니다. 사전에 추가하기 전에는 항목별 집계에서 빼 둡니다."
},
{
"extraction_no": "EX-005",
"verdict": "duplicate_extraction",
"normalized_extracted": "12000000",
"normalized_expected": "12500000",
"evidence": [
{
"field": "extraction_no",
"agg": "count",
"value": 2
}
],
"next_action": "같은 추출 번호가 두 번 적재됐습니다. 앞 건으로만 판정하고 뒤 건을 뺍니다."
}
][자동 검증 · 1주차] 1. verdict가 enum 여섯 값 안에 있는지 확인합니다. 2. extraction_no·verdict·normalized_extracted·normalized_expected·evidence·next_action이 모두 있는지 확인합니다. 3. confidence가 0.0 이상 1.0 이하인지 확인합니다. 4. extracted_value가 null이면 verdict가 extraction_missing인지 확인합니다. 5. evidence의 field가 두 입력 표의 컬럼에 실재하는지 확인합니다. 6. evidence의 quote가 그 행 field_key_raw의 일부인지 확인합니다. 7. next_action이 존댓말로 끝나는지 확인합니다. [판정 규칙] 1. 항목 이름은 대소문자와 띄어쓰기, 구분자를 없애 소문자로 모은 뒤 agency_ground_truth의 field_label와 맞춥니다. 맞는 것이 없으면 미매핑으로 남기고 그 행은 나머지 판정에서 뺍니다. 2. 같은 행 번호가 두 번 들어오면 앞 건만 남기고 뒤 건은 적재 단계에서 사유와 함께 뺍니다. [수동 검증] 1. 값이 어긋난 항목이 모델 오류인지 대장 쪽 옛 값인지 담당자가 원본 문서를 열어 확인합니다. 2. EX-004처럼 항목 이름의 띄어쓰기가 흔들렸지만 사전으로 맞춰진 행은 미매핑 사유를 달지 않는 것이 맞는지 확인합니다. [평가 러너 · 2주차] 1. 항목별로 맞은 건수와 사유별 분포를 세어 어느 항목이 도입에 걸림돌인지 나눕니다. 2. 추출값과 정답을 작성자가 만든 합성 기준이라 이 수치는 성능 측정이 아니라 데모 조건 동작입니다.