건설파트너
경력근거: 실제 건설 실무자 분들이 도면 기반으로 공사비 견적을 어떻게 내는지
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 도면 기반으로 공사비 견적을 어떻게 내는지 파고들어 암묵지를 찾아내고, 가설을 세워 실험한 뒤 에이전트를 만들어 개선하라고 주요업무에 적습니다. 이 포트폴리오는 그 순서를 그대로 따릅니다. 자재명 매칭이라는 암묵지를 규칙으로 옮기고, 못 붙는 항목을 사유별로 모아 다음 규칙을 어디에 더할지 정합니다. 다만 이 포트폴리오는 표기 정규화와 금액 계산 구현을 보여줍니다. 건설 적산 실무나 표준품셈 해석에 대한 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
공고는 실무자가 도면으로 공사비 견적을 어떻게 내는지 집요하게 파고들어 암묵지를 찾아내라고 첫 줄에 적습니다. 정해진 명세를 받아 구현하는 자리가 아니라 업무 절차를 뜯어보고 규칙으로 옮기는 자리입니다.
도면을 이해하고 견적을 산출하는 방법을 세분화해 가설을 세우고 실험한 뒤 에이전트를 만들고 개선한다고 적혀 있습니다. 한 번에 완성하는 설계가 아니라 작게 만들어 돌려 보고 고치는 순서를 요구합니다.
매주 고객을 만나 만든 것을 보여주고 피드백 루프를 돌린다고 업무에 명시합니다. 일주일 안에 손에 잡히는 산출물이 나와야 하는 리듬이라 완결된 작은 결과물을 만드는 능력이 그대로 업무 능력입니다.
견적 담당자가 도면에서 뽑아 둔 물량 산출표와 사내 단가표를 넘기면, 자재명을 정규화해 단가를 붙이고 항목별 금액을 계산한 견적서 초안을 파일로 냅니다. 단가를 못 붙인 항목과 수량이 빈 항목은 금액을 비운 채 확인 목록으로 따로 모읍니다. 붙일지 말지는 매칭 규칙이 정하고, 모델은 그 항목이 왜 단가를 못 받았는지 한 줄로 요약합니다.
저는 건설사 견적팀에서 입찰 견적을 냅니다. 도면 검토가 끝나면 물량 산출표가 엑셀로 넘어오는데, 자재명이 도면 표기 그대로라 사내 단가표의 품명과 조금씩 다릅니다. 붙는 것부터 채우고 나머지는 하나씩 찾아 넣는데, 입찰 마감 전날에는 어디까지 확인했는지도 헷갈립니다.
견적 초안이 오래 걸리는 이유는 금액 계산이 어려워서가 아니라 자재명 표기가 흔들려 붙는 항목과 못 붙는 항목이 섞여 있기 때문입니다. 표기를 정규화해 붙이고 못 붙인 항목만 사유별로 모아 주면, 담당자가 확인할 자리가 줄어들고 나머지는 그대로 견적서가 됩니다.
명령 한 줄로 도는 CLI 하나가 완결됩니다. 자재명을 정규화해 단가를 붙이고 항목별 금액을 계산한 견적서 초안을 마크다운 파일로 내며, 못 붙인 항목은 사유와 같이 확인 목록에 남깁니다. 도면 파일 자체를 읽는 일은 물량 산출표가 이미 나왔다고 가정하고 3주차로 미룹니다.
담당자가 손으로 붙인 정답 매칭 80건을 넣어 규칙이 같은 단가를 골랐는지 자동으로 맞춰 보고, 어긋난 유형을 표로 냅니다. 자주 틀리는 표기 묶음이 보이면 정규화 규칙에 더합니다.
같은 자재의 과거 견적 단가와 이번 단가를 비교해 벌어진 항목을 견적서 초안 아래에 따로 적습니다. 담당자가 마감 전에 볼 자리가 한 곳으로 모입니다.
견적 담당자가 물량표와 단가표를 넘기면 표기가 다른 자재명도 붙고, 항목별 금액을 채운 견적서 초안이 파일로 나옵니다.
수량 칸이 비거나 단가표에 없는 자재명이 섞이거나 규격 표기가 흔들린 입력 30종을 넘겨도 실행이 멈추지 않고, 못 붙인 항목이 사유와 같이 확인 목록에 남습니다.
정답 매칭 80건으로 규칙이 고른 단가의 일치율을 냅니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 더미데이터 자가채점이며 실제 매칭 적중률은 건설파트너의 실 물량 산출표와 사내 단가표가 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 수량 결측·규격 표기 흔들림·단가표에 없는 자재명을 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
단가가 붙었다는 사실이 그 단가가 맞다는 뜻은 아닙니다. 같은 자재라도 현장 조건에 따라 적용 단가가 달라지므로 판정을 결론으로 읽지 않게 근거 값을 항상 같이 냅니다.
실제 도입에서는 현장마다 물량 산출표의 열 구성과 자재명 관례가 다릅니다. 표기 사전이 늘어나면 규칙을 코드에서 빼 설정으로 옮겨야 하며, 그것은 도입 단계 과제로 분리합니다.
도면 파일에서 물량을 뽑는 일은 이 포트폴리오의 범위 밖입니다. 여기서는 물량 산출표가 이미 나온 뒤부터 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
도면 검토가 끝나고 넘어온 물량 산출표입니다. 매칭의 한쪽 축이며 수량 미기재와 도면 표기 그대로의 자재명을 의도적으로 포함합니다. 자재 코드는 담기지 않아 이름과 규격을 정규화해 단가표와 맞춰야 합니다.
| item_id | drawing_no | material_name_raw | spec_raw | unit | quantity | floor | memo |
|---|---|---|---|---|---|---|---|
| tk_1001 | A-201 | 레미콘 25-24-15 | 25-24-15 | m3 | 184.5 | 지하1층 | — |
| tk_1002 | A-201 | 이형철근 SD400 D13 | SD400 D13 | ton | 12.4 | 지하1층 | — |
| tk_1003 | A-202 | 이형철근 sd400 d13 | sd400 d13 | ton | 3.8 | 1층 | 도면 표기 그대로, 띄어쓰기 흔들림 |
| tk_1004 | A-203 | 석고보드 9.5T | 9.5T | m2 | — | 2층 | 수량 미기재 |
| tk_1005 | A-203 | 단열재 압출법 50T | 50T | m2 | 620 | 2층 | — |
아래 JSON Schema 에 맞는 물량 산출 행을 더 만들어 주세요. {"type":"object","properties":{"item_id":{"type":"string"},"drawing_no":{"type":"string"},"material_name_raw":{"type":"string"},"spec_raw":{"type":["string","null"]},"unit":{"type":"string","enum":["m2","m3","m","ea","ton"]},"quantity":{"type":["number","null"]},"floor":{"type":"string"},"memo":{"type":["string","null"]}},"required":["item_id","drawing_no","material_name_raw","unit","floor"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) quantity 가 null 인 수량 미기재 행, (2) quantity 가 0 인 설계 변경 소멸 행과 수십 톤대 최대 물량 행, (3) material_name_raw 의 띄어쓰기와 대소문자가 흔들린 행, (4) 단가표에 없는 신규 자재명. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"item_id":{"type":"string"},"drawing_no":{"type":"string"},"material_name_raw":{"type":"string"},"spec_raw":{"type":["string","null"]},"unit":{"type":"string","enum":["m2","m3","m","ea","ton"]},"quantity":{"type":["number","null"]},"floor":{"type":"string"},"memo":{"type":["string","null"]}},"required":["item_id","drawing_no","material_name_raw","unit","floor"]}사내 자재 단가표입니다. 매칭의 다른 쪽 축이며 같은 자재의 규격 표기 변형과 단가 미확정 항목을 포함합니다.
| price_id | material_code | material_name | spec | unit | unit_price_krw | quoted_on | vendor |
|---|---|---|---|---|---|---|---|
| up_5001 | CON-2524 | 레미콘 | 25-24-15 | m3 | 92,000 | 2026-07-20 | 한성레미콘 |
| up_5002 | RBR-D13 | 이형철근 | SD400 D13 | ton | 1,080,000 | 2026-07-20 | 동국제강 |
| up_5003 | GYP-095 | 석고보드 | 9.5T | m2 | 4,200 | 2026-07-18 | KCC |
| up_5004 | INS-XPS50 | 압출법 단열재 | 50T | m2 | 11,800 | 2026-07-18 | 벽산 |
| up_5005 | TIL-300 | 자기질 타일 | 300x300 | m2 | 18,500 | 2026-07-15 | 대림바스 |
아래 JSON Schema 에 맞는 단가표 행을 더 만들어 주세요. {"type":"object","properties":{"price_id":{"type":"string"},"material_code":{"type":"string"},"material_name":{"type":"string"},"spec":{"type":["string","null"]},"unit":{"type":"string","enum":["m2","m3","m","ea","ton"]},"unit_price_krw":{"type":["integer","null"]},"quoted_on":{"type":"string"},"vendor":{"type":"string"}},"required":["price_id","material_code","material_name","unit","quoted_on","vendor"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) unit_price_krw 가 null 인 단가 미확정 행, (2) 규격만 다르고 자재명이 같은 행 묶음, (3) 물량 산출표에는 없는 자재 코드, (4) 같은 자재의 견적일이 다른 행. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"price_id":{"type":"string"},"material_code":{"type":"string"},"material_name":{"type":"string"},"spec":{"type":["string","null"]},"unit":{"type":"string","enum":["m2","m3","m","ea","ton"]},"unit_price_krw":{"type":["integer","null"]},"quoted_on":{"type":"string"},"vendor":{"type":"string"}},"required":["price_id","material_code","material_name","unit","quoted_on","vendor"]}| Python | CLI 본체의 언어입니다. 물량표를 읽어 자재명을 정규화하고 단가를 붙여 금액을 계산하는 규칙이 여기 있습니다. |
| JavaScript | 3주차에 견적서 초안을 브라우저에서 열어 보는 정적 미리보기 페이지를 만듭니다. |
| Java | 사내 적산 시스템이 자바 기반일 때를 대비해 견적서 초안을 그 시스템이 읽는 표 형식으로도 내보냅니다. |
| LLM | 확인 목록에 오른 항목이 왜 단가를 못 받았는지 담당자가 읽을 한 줄로 요약합니다. 붙일지 말지는 매칭 규칙이 정하고 모델은 여기에 개입하지 않습니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 규칙이 고른 단가가 담당자 정답과 같은지 맞춰 보는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 과거 견적 단가와 비교해 튄 항목을 짚는 단계로 확장합니다.
견적서 초안의 한 줄입니다. 붙인 단가와 계산한 금액, 그리고 못 붙였을 때의 사유를 함께 남깁니다.
{"type":"object","properties":{"line_id":{"type":"string"},"item_id":{"type":"string"},"matched_price_id":{"type":["string","null"]},"amount_krw":{"type":["integer","null"]},"status":{"type":"string","enum":["priced","unit_price_missing","quantity_missing","name_unmapped","spec_mismatch"]},"evidence":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["takeoff","price_master"]},"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{"type":["string","number","null"]}},"required":["source","field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["line_id","item_id","status","evidence","summary"]}[
{
"line_id": "ln_0001",
"item_id": "tk_1001",
"matched_price_id": "up_5001",
"amount_krw": 16974000,
"status": "priced",
"evidence": [
{
"source": "takeoff",
"field": "quantity",
"agg": null,
"value": 184.5
},
{
"source": "price_master",
"field": "unit_price_krw",
"agg": null,
"value": 92000
}
],
"summary": "레미콘 184.5m3 에 단가 92,000원을 붙여 16,974,000원으로 계산했습니다.",
"hedge": null
},
{
"line_id": "ln_0002",
"item_id": "tk_1002",
"matched_price_id": "up_5002",
"amount_krw": 13392000,
"status": "priced",
"evidence": [
{
"source": "takeoff",
"field": "quantity",
"agg": null,
"value": 12.4
},
{
"source": "price_master",
"field": "unit_price_krw",
"agg": null,
"value": 1080000
}
],
"summary": "이형철근 12.4톤에 단가 1,080,000원을 붙여 13,392,000원으로 계산했습니다.",
"hedge": null
},
{
"line_id": "ln_0003",
"item_id": "tk_1003",
"matched_price_id": "up_5002",
"amount_krw": 4104000,
"status": "priced",
"evidence": [
{
"source": "takeoff",
"field": "material_name_raw",
"agg": null,
"value": "이형철근 sd400 d13"
},
{
"source": "price_master",
"field": "material_name",
"agg": null,
"value": "이형철근"
}
],
"summary": "띄어쓰기와 대소문자를 정규화한 이름과 규격으로 이형철근 SD400 D13 단가에 붙였습니다.",
"hedge": "합성 기준 판정이라 실제 현장에서는 같은 표기가 다른 규격을 가리킬 수 있습니다."
},
{
"line_id": "ln_0004",
"item_id": "tk_1004",
"matched_price_id": "up_5003",
"amount_krw": null,
"status": "quantity_missing",
"evidence": [
{
"source": "takeoff",
"field": "quantity",
"agg": null,
"value": null
},
{
"source": "price_master",
"field": "unit_price_krw",
"agg": null,
"value": 4200
}
],
"summary": "단가는 붙었지만 수량이 비어 있어 금액을 비웠습니다.",
"hedge": null
},
{
"line_id": "ln_0005",
"item_id": "tk_1006",
"matched_price_id": null,
"amount_krw": null,
"status": "name_unmapped",
"evidence": [
{
"source": "takeoff",
"field": "material_name_raw",
"agg": null,
"value": "친환경 저탄소 레미콘 25-24-15"
},
{
"source": "price_master",
"field": "material_name",
"agg": "count",
"value": 0
}
],
"summary": "정규화한 이름으로 단가표를 찾았지만 걸리는 행이 없어 붙이지 못했습니다.",
"hedge": null
},
{
"line_id": "ln_0006",
"item_id": "tk_1012",
"matched_price_id": "up_5008",
"amount_krw": null,
"status": "unit_price_missing",
"evidence": [
{
"source": "takeoff",
"field": "quantity",
"agg": null,
"value": 74
},
{
"source": "price_master",
"field": "unit_price_krw",
"agg": null,
"value": null
}
],
"summary": "단가표의 창호 단가가 아직 비어 있어 금액을 내지 못했습니다.",
"hedge": null
},
{
"line_id": "ln_0007",
"item_id": "tk_1007",
"matched_price_id": "up_5005",
"amount_krw": 0,
"status": "priced",
"evidence": [
{
"source": "takeoff",
"field": "quantity",
"agg": null,
"value": 0
},
{
"source": "price_master",
"field": "unit_price_krw",
"agg": null,
"value": 18500
}
],
"summary": "설계 변경으로 물량이 0 이라 금액도 0 원으로 남깁니다.",
"hedge": null
},
{
"line_id": "ln_0008",
"item_id": "tk_1009",
"matched_price_id": "up_5007",
"amount_krw": 2976000,
"status": "spec_mismatch",
"evidence": [
{
"source": "takeoff",
"field": "spec_raw",
"agg": null,
"value": null
},
{
"source": "price_master",
"field": "spec",
"agg": null,
"value": "2.0T"
}
],
"summary": "물량표에 규격이 없어 자재 코드만으로 붙였고, 규격 확인이 필요한 줄로 표시했습니다.",
"hedge": "합성 기준 판정이라 실제로는 다른 두께의 시트가 쓰였을 수 있습니다."
}
][자동 검증 · 코드가 판정합니다] 1. status 가 enum 다섯 값 중 하나인지 확인합니다. 2. line_id·item_id·status·evidence·summary 가 모두 채워졌는지 확인합니다. 3. amount_krw 가 evidence 의 quantity 와 unit_price_krw 를 곱한 값과 같은지 다시 계산합니다. 둘 중 하나가 null 이면 amount_krw 도 null 인지 확인합니다. 4. evidence 의 field 이름이 물량표와 단가표에 실재하는 컬럼인지 대조합니다. 물량표에는 자재 코드가 없어 이름과 규격으로만 붙습니다. 5. evidence 의 value 가 수록된 행의 값과 같은지 확인합니다. 6. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 담당자가 손으로 붙인 정답 매칭 80건과 규칙이 고른 matched_price_id 를 비교해 일치율과 어긋난 유형을 냅니다. [수동 검증 · 사람이 봅니다] 1. 규격이 비어 있어 코드만으로 붙인 줄을 표본 20건 눈으로 확인합니다. 2. 물량이 0 인 줄을 견적서에 남길지 뺄지는 현장 관례를 따라 담당자가 정합니다.
근거: 실제 건설 실무자 분들이 도면 기반으로 공사비 견적을 어떻게 내는지