엑셈
경력근거: 외부 API가 불가한 환경에서의 오픈웨이트 모델 선정·벤치마킹, 양자화 트레이드오프 판단
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고가 적은 '검색(Retrieval), 임베딩, 평가, 추론 프로세스를 포함한 시스템을 고객 데이터 및 도메인 특성에 맞춰 설계·구현' 과 '외부 API가 불가한 환경에서의 오픈웨이트 모델 선정·벤치마킹, 양자화 트레이드오프 판단' 이 이 도구의 정의 그대로입니다. 데이터베이스와 시스템 성능 관리 제품을 고객사 안에 설치해 운영해 온 엑셈에서는 모델 선택이 사내 실험실 비교가 아니라 고객사 반입 심사와 장비 자원 한계 안에서 결정되고, 그 결정을 고객 이해관계자에게 문서로 소명해야 합니다. 다만 이 포트폴리오가 보여주는 것은 검색·평가 파이프라인 구현과 모델 선정 근거를 만드는 기술이며, 금융·공공 규제 심사 실무나 대규모 GPU 서빙 운영 경험은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
공고가 검색과 임베딩, 평가, 추론 프로세스를 고객 데이터와 도메인 특성에 맞춰 설계하고 구현하는 일을 Forward Deployed Engineer 의 업무로 적었습니다. 이 도구가 그 네 단계를 조합 축으로 펼쳐 한 번에 돌리는 실물입니다.
공고가 외부 API 를 쓸 수 없는 환경에서 오픈웨이트 모델을 고르고 벤치마킹하며 양자화 트레이드오프를 판단하는 능력을 요구합니다. 조합별 근거 적중과 응답 지연, 메모리 사용을 나란히 놓은 비교표가 그 판단을 문서로 남깁니다.
공고가 현장에서 기술 검증을 직접 기획하고 수행한 경험을 우대하면서, 현장에서 나온 문제를 일회성 패치가 아니라 재사용 자산과 제품 요구사항으로 정리하라고 적었습니다. 고객사만 바꿔 다시 돌리는 실행기와 실행 기록이 그 형태입니다.
고객사가 건넨 운영 문서 묶음과 검증 질문 목록을 입력으로 받아, 반입 후보 로컬 오픈웨이트 모델과 양자화 수준, 검색 설정을 조합해 순서대로 자동 실행하는 CLI입니다. 질문마다 어느 문서를 근거로 인용했는지 정답 문단과 대조해 규칙으로 채점하고, 조합별 근거 적중과 응답 지연, 메모리 사용을 나란히 놓은 비교표와 선정 리포트를 마크다운 파일로 내놓습니다. 외부 API 호출은 전 구간에서 없습니다.
고객사 회의실에서 '그래서 어떤 모델을 넣을 거냐'는 질문을 받았을 때 인상 대신 그 회사 문서로 돌린 표를 펴 보이고 싶은 현장 엔지니어입니다. 노트북에서 몇 번 물어본 느낌만으로 모델을 고르고 납품 뒤에 답변 품질 항의를 받는 상황을 다시 겪고 싶지 않은 사람입니다.
모델과 양자화 수준, 검색 설정을 조합 축으로 놓고 고객사 질문 세트로 한 번에 돌리면, 현장 모델 선정이 개인 감각이 아니라 다시 재현되는 실행 기록으로 남습니다.
GPU 장비 없이 CPU 만으로 도는 소형 오픈웨이트 모델 2종을 llama.cpp 로 부르는 데서 시작합니다(GPU 서빙은 3주차로 미룹니다). 고객사 운영 문서 30건과 검증 질문 30건 더미를 넣으면 모델 2종과 양자화 2단계, 검색 설정 2종을 곱한 8조합을 순서대로 돌려 근거 적중과 응답 지연, 메모리 사용을 표로 내고 선정 리포트 마크다운 파일을 만드는 완결품입니다.
1주차 실행 결과에서 정답 문단이 지정되지 않은 질문, 존재하지 않는 문서를 가리키는 질문, 부서 표기가 흔들리는 질문을 채점 제외로 분리해 세고, 고객 담당자에게 확인할 질문 목록을 별도 파일로 냅니다. 조합마다 실행 파라미터와 결과를 기록으로 남겨 같은 표를 다시 만듭니다.
2주차까지의 조합 실행 기록을 그대로 입력으로 써서 vLLM 서빙 축을 추가해 CPU 대비 응답 지연과 처리량을 비교하고, 라이선스와 파일 용량, 장비 자원 한계로 조합을 미리 걸러내는 반입 심사 필터와 고객사 내부팀 운영 가이드가 붙은 상태입니다.
고객사 문서 묶음과 검증 질문 30건을 넣으면 모델 2종과 양자화 2단계, 검색 설정 2종을 곱한 8조합이 순서대로 돌고 조합별 비교표와 선정 리포트 파일이 나오는 것을 명령 한 줄로 시연합니다.
질문마다 모델이 인용한 문서 id 를 미리 지정한 정답 문단과 대조해 적중 여부를 규칙으로 판정하고, 판정 근거를 질문 단위로 전부 표시합니다(자동 판정, 채점 대상 질문 전건).
정답 문단이 지정되지 않은 질문, 존재하지 않는 문서를 가리키는 질문, 부서 표기가 흔들리는 질문을 섞은 30건을 넣어도 실행이 끊기지 않고 채점 불가 사유가 리포트에 사유별로 남습니다.
위 적중 수치는 문서와 정답 문단을 작성자가 만든 자가채점이라 성능 증명이 아니라 데모 조건 동작입니다. 실제 조합 선택이 맞았는지는 엑셈 고객사의 실제 문서와 현업 검수자가 있어야 측정되고, 이 포트폴리오는 그 검증을 현장에서 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 문서와 작성자가 지정한 정답 문단으로 잰 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 내가 만들었으니 잘 맞는 것이 당연하다는 함정을 피하려고, 정답 미지정·문서 없음·신구 상충·표기 흔들림 질문을 더미에 일부러 섞어 채점기가 그것들을 구분해 내는지 확인합니다.
근거 문단 적중은 인용한 문서가 맞았는지만 봅니다. 인용은 맞는데 요약이 어긋난 경우는 규칙으로 잡히지 않아, 조합 상위 후보에 한해 사람이 직접 읽는 확인 표본을 따로 둡니다.
CPU 소형 모델 기준의 응답 지연과 메모리 수치는 고객사 실장비와 다릅니다. GPU 서빙 최적화와 실제 반입 심사 통과는 실장비와 고객사 절차가 있어야 하는 도입 단계 과제로 분리합니다.
이 포트폴리오는 검색·평가 파이프라인 구현과 모델 선정 근거 작성 기술을 보여줄 뿐, 금융·공공 규제 산업의 심사 실무 경험은 주장하지 않습니다. 모르는 영역은 면접에서 모른다고 말합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 담당자가 엑셀로 건넨 검증 질문 목록입니다. 질문마다 근거가 되어야 할 운영 문서와 정답 문단이 지정되어 있고, 이 지정이 조합 채점의 기준이 됩니다. 지정이 비어 있거나 없는 문서를 가리키는 행이 섞여 있어 채점 불가 분리의 입력이기도 합니다.
| question_id | question | category | expected_doc_id | expected_span | source_team_raw | source_team_id | must_refuse |
|---|---|---|---|---|---|---|---|
| Q-001 | 운영 데이터베이스 전체 백업은 언제 수행하나요? | 운영절차 | OPS-01 | 운영 DB 는 매일 새벽 2시에 전체 백업을 수행한다 | DB운영팀 | T-DB | false |
| Q-002 | 데이터베이스 성능 점검은 얼마나 자주 하나요? | 성능지표 | OPS-05 | DB 성능 점검은 월 1회 정기 수행하며 | DB운영팀 | T-DB | false |
| Q-003 | 야간 배치가 두 번 실패하면 누구에게 연락이 가나요? | 장애대응 | OPS-02 | 2회 실패 시 당직 엔지니어에게 알림이 발송된다 | 인프라운영팀 | T-INFRA | false |
| Q-004 | 미사용 계정은 며칠 뒤에 잠기나요? | 권한관리 | — | — | 보안팀 | T-SEC | false |
| Q-005 | 접속 로그는 얼마나 보관하나요? | 보안규정 | OPS-99 | 접속 로그는 1년 보관한다 | 보안팀 | T-SEC | false |
다음 JSON Schema 를 그대로 만족하는 고객사 검증 질문 행을 30건 더 만들어 주세요: {"type":"object","properties":{"question_id":{"type":"string"},"question":{"type":"string"},"category":{"type":"string","enum":["운영절차","성능지표","보안규정","장애대응","권한관리"]},"expected_doc_id":{"type":["string","null"]},"expected_span":{"type":["string","null"]},"source_team_raw":{"type":["string","null"]},"source_team_id":{"type":["string","null"]},"must_refuse":{"type":"boolean"}},"required":["question_id","question","category","must_refuse"]} 질문은 데이터베이스·시스템 운영 문서에서 답이 나오는 형태로 쓰고, expected_doc_id 는 'OPS-##' 형식을 씁니다. 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: (1) expected_doc_id 와 expected_span 이 둘 다 null 인 미지정 질문 4건(채점 제외로 분리돼야 함), (2) 문서 세트에 없는 OPS 번호를 가리키는 미매핑 질문 3건, (3) must_refuse 가 true 라 문서에 답이 없다고 답해야 하는 질문 3건, (4) 같은 부서를 'DB운영팀'·'디비운영팀'·'DB 운영팀' 처럼 다르게 적고 source_team_id 가 null 인 표기 흔들림 4건, (5) 열 글자 이하의 아주 짧은 질문과 두 문서 기준이 상충하는 것을 묻는 아주 긴 질문 각 2건. 형식 자체를 깨뜨리는 시도(필수 필드 누락·타입 깨짐)도 3건 만들어 도구가 읽기 단계에서 걸러 내는지 확인합니다.{"type":"object","properties":{"question_id":{"type":"string"},"question":{"type":"string"},"category":{"type":"string","enum":["운영절차","성능지표","보안규정","장애대응","권한관리"]},"expected_doc_id":{"type":["string","null"]},"expected_span":{"type":["string","null"]},"source_team_raw":{"type":["string","null"]},"source_team_id":{"type":["string","null"]},"must_refuse":{"type":"boolean"}},"required":["question_id","question","category","must_refuse"]}고객사 폐쇄망에 반입할 후보 로컬 오픈웨이트 모델 목록입니다. 모델 계열과 양자화 수준, 파일 용량, 라이선스 표기, 필요 메모리가 들어 있어 조합 축을 펼치는 기준이자 반입 심사 필터의 입력이 됩니다.
| model_id | family | params_b | quantization | file_size_mb | license_raw | min_memory_gb | import_approved |
|---|---|---|---|---|---|---|---|
| M-001 | Qwen2.5-7B-Instruct | 7.6 | Q4_K_M | 4,680 | Apache-2.0 | 8 | true |
| M-002 | Qwen2.5-7B-Instruct | 7.6 | Q8_0 | 8,100 | Apache-2.0 | 12 | true |
| M-003 | Llama-3.1-8B-Instruct | 8 | Q4_K_M | 4,920 | Llama 3.1 Community License | 8 | — |
| M-004 | Llama-3.1-8B-Instruct | 8 | Q8_0 | 8,540 | 라마 3.1 커뮤니티 라이선스 | 12 | — |
| M-005 | EEVE-Korean-10.8B | 10.8 | Q4_K_M | 6,520 | Apache-2.0 | 10 | true |
다음 JSON Schema 를 그대로 만족하는 반입 후보 모델 행을 20건 더 만들어 주세요: {"type":"object","properties":{"model_id":{"type":"string"},"family":{"type":"string"},"params_b":{"type":["number","null"]},"quantization":{"type":"string","enum":["Q4_K_M","Q8_0","FP16","AWQ-4bit"]},"file_size_mb":{"type":["number","null"]},"license_raw":{"type":["string","null"]},"min_memory_gb":{"type":["number","null"]},"import_approved":{"type":["boolean","null"]}},"required":["model_id","family","quantization"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: (1) license_raw 가 null 이거나 '내부 배포용' 처럼 심사가 필요한 표기 4건, (2) min_memory_gb 나 file_size_mb 가 null 인 미측정 항목 3건, (3) 0.5B 이하 초소형과 30B 이상 대형처럼 자원 한계 양 끝의 경계값 4건, (4) 같은 모델 계열을 'Llama 3.1 Community License' 와 '라마 3.1 커뮤니티 라이선스' 처럼 다르게 적은 표기 흔들림 3건, (5) import_approved 가 null 이라 아직 판정되지 않은 항목 5건. 형식 자체를 깨뜨리는 시도(quantization 에 없는 값 넣기)도 2건 만들어 도구가 조합 축을 만들기 전에 걸러 내는지 확인합니다.{"type":"object","properties":{"model_id":{"type":"string"},"family":{"type":"string"},"params_b":{"type":["number","null"]},"quantization":{"type":"string","enum":["Q4_K_M","Q8_0","FP16","AWQ-4bit"]},"file_size_mb":{"type":["number","null"]},"license_raw":{"type":["string","null"]},"min_memory_gb":{"type":["number","null"]},"import_approved":{"type":["boolean","null"]}},"required":["model_id","family","quantization"]}| Python | 조합 실행기와 규칙 채점기, 리포트 생성기를 담은 CLI의 구현 언어입니다 |
| llama.cpp | CPU 만으로 후보 오픈웨이트 모델을 로컬 구동해 양자화 수준별 응답을 받아오는 실행기입니다 |
| vLLM | 3주차에 GPU 장비가 있는 환경을 가정해 같은 조합을 다시 재고, CPU 결과와 응답 지연·처리량을 비교합니다 |
| Milvus | 문서 청크 임베딩을 저장하고 벡터 검색 축을 담당합니다 |
| Elasticsearch | 키워드 검색 축을 담당하며, 벡터와 섞는 비율이 검색 설정 조합 변수가 됩니다 |
| PostgreSQL | 조합 실행 기록과 질문 단위 판정 결과를 저장합니다 |
| Redis | 같은 문서 묶음으로 조합을 다시 돌릴 때 임베딩 재계산을 건너뛰는 캐시입니다 |
| FastAPI | 2주차에 붙이는 결과 조회 API 로, 팀원이 조합별 판정 결과를 브라우저에서 확인합니다 |
| Docker | 도구와 모델 실행기, 검색 엔진을 이미지로 묶어 고객사 반입 단위로 만듭니다 |
| MLflow | 조합마다 파라미터와 결과를 실행 기록으로 남겨 같은 비교표를 다시 만들게 합니다 |
| AutoRAG | 검색 설정 탐색 방식을 참고해 조합 축(청크 크기·상위 k·검색 모드)을 정의합니다 |
| Linux 서버·GPU 환경 | 메모리와 VRAM 한계를 조합 탈락 기준으로 계산에 넣습니다 |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 채점 불가 질문 분리와 고객 확인 목록 생성으로 넘어갑니다.
✓ pass → GPU 서빙 축 추가와 반입 심사 필터 작성으로 확장합니다.
조합 하나의 실행 결과를 (1) 어떤 모델·양자화·검색 설정이었는지, (2) 채점 대상과 제외 건수, (3) 근거 문단 적중과 응답 지연·메모리, (4) 질문 단위 판정 근거, (5) 추천 여부와 그 한 줄 사유로 담습니다. 선정 리포트 마크다운은 이 객체들을 표로 옮긴 결과입니다.
{"type":"object","properties":{"combo_id":{"type":"string"},"model_id":{"type":"string"},"quantization":{"type":"string"},"retrieval":{"type":"object","properties":{"chunk_size":{"type":"integer"},"top_k":{"type":"integer"},"mode":{"type":"string","enum":["vector","keyword","hybrid"]}},"required":["chunk_size","top_k","mode"]},"scored_count":{"type":"integer"},"excluded_count":{"type":"integer"},"citation_hit_rate":{"type":["number","null"]},"refusal_correct_rate":{"type":["number","null"]},"p95_latency_ms":{"type":["number","null"]},"peak_memory_mb":{"type":["number","null"]},"evidence":{"type":"array","items":{"type":"object","properties":{"question_id":{"type":"string"},"expected_doc_id":{"type":["string","null"]},"cited_doc_id":{"type":["string","null"]},"verdict":{"type":"string","enum":["적중","오인용","정답거절","거절실패","채점불가"]},"exclude_reason":{"type":["string","null"]}},"required":["question_id","verdict"]}},"recommendation":{"type":"string","enum":["추천","조건부","제외"]},"recommendation_reason":{"type":"string"},"hedge":{"type":"string"}},"required":["combo_id","model_id","quantization","retrieval","scored_count","excluded_count","evidence","recommendation","recommendation_reason","hedge"]}[
{
"combo_id": "C-03",
"model_id": "M-001",
"quantization": "Q4_K_M",
"retrieval": {
"chunk_size": 400,
"top_k": 5,
"mode": "hybrid"
},
"scored_count": 25,
"excluded_count": 5,
"citation_hit_rate": 0.88,
"refusal_correct_rate": 0.67,
"p95_latency_ms": 3120,
"peak_memory_mb": 6240,
"evidence": [
{
"question_id": "Q-001",
"expected_doc_id": "OPS-01",
"cited_doc_id": "OPS-01",
"verdict": "적중",
"exclude_reason": null
},
{
"question_id": "Q-008",
"expected_doc_id": "OPS-01",
"cited_doc_id": "OPS-09",
"verdict": "오인용",
"exclude_reason": null
},
{
"question_id": "Q-006",
"expected_doc_id": null,
"cited_doc_id": null,
"verdict": "정답거절",
"exclude_reason": null
},
{
"question_id": "Q-005",
"expected_doc_id": "OPS-99",
"cited_doc_id": "OPS-04",
"verdict": "채점불가",
"exclude_reason": "지정된 정답 문서 OPS-99 가 문서 세트에 없습니다"
}
],
"recommendation": "추천",
"recommendation_reason": "혼합 검색에서 근거 문단 적중이 가장 높고, 메모리 사용이 반입 심사 기준 8GB 안에 들어옵니다.",
"hedge": "적중률은 작성자가 지정한 정답 문단 기준의 합성 채점이라 고객사 실제 문서에서의 정확도를 뜻하지 않습니다."
},
{
"combo_id": "C-06",
"model_id": "M-002",
"quantization": "Q8_0",
"retrieval": {
"chunk_size": 400,
"top_k": 5,
"mode": "hybrid"
},
"scored_count": 25,
"excluded_count": 5,
"citation_hit_rate": 0.9,
"refusal_correct_rate": 0.67,
"p95_latency_ms": 7480,
"peak_memory_mb": 11800,
"evidence": [
{
"question_id": "Q-002",
"expected_doc_id": "OPS-05",
"cited_doc_id": "OPS-05",
"verdict": "적중",
"exclude_reason": null
},
{
"question_id": "Q-007",
"expected_doc_id": "OPS-01",
"cited_doc_id": "OPS-01",
"verdict": "적중",
"exclude_reason": null
},
{
"question_id": "Q-004",
"expected_doc_id": null,
"cited_doc_id": "OPS-03",
"verdict": "채점불가",
"exclude_reason": "고객이 정답 문단을 지정하지 않았습니다"
}
],
"recommendation": "조건부",
"recommendation_reason": "적중은 2%p 높지만 응답 지연이 두 배가 넘고 메모리가 반입 기준을 넘어, 장비가 확보된 고객사에서만 선택합니다.",
"hedge": "지연과 메모리는 작성자 노트북 CPU 기준 측정이라 고객사 서버 성능을 대신하지 않습니다."
},
{
"combo_id": "C-08",
"model_id": "M-007",
"quantization": "Q4_K_M",
"retrieval": {
"chunk_size": 800,
"top_k": 3,
"mode": "vector"
},
"scored_count": 25,
"excluded_count": 5,
"citation_hit_rate": 0.52,
"refusal_correct_rate": 0,
"p95_latency_ms": 640,
"peak_memory_mb": 980,
"evidence": [
{
"question_id": "Q-011",
"expected_doc_id": "OPS-06",
"cited_doc_id": "OPS-04",
"verdict": "오인용",
"exclude_reason": null
},
{
"question_id": "Q-006",
"expected_doc_id": null,
"cited_doc_id": "OPS-10",
"verdict": "거절실패",
"exclude_reason": null
},
{
"question_id": "Q-009",
"expected_doc_id": "OPS-08",
"cited_doc_id": "OPS-08",
"verdict": "적중",
"exclude_reason": null
}
],
"recommendation": "제외",
"recommendation_reason": "가장 빠르고 가볍지만 근거가 없는 질문에도 답을 만들어 내서, 규정 답변 용도로 쓰기 어렵습니다.",
"hedge": "거절 판정 표본이 3건뿐이라 이 수치는 경향만 보여 주는 합성 기준 추정입니다."
}
][자동 검증 · 사람 손 없이 매 실행마다 돌아갑니다] 1. 필수 필드와 enum 값이 스키마를 지키는지 확인합니다. quantization 이나 verdict 에 정의되지 않은 값이 들어오면 그 조합 결과를 리포트에 싣지 않고 실패로 표시합니다. 2. verdict 는 지정된 정답 문서 id 와 모델이 인용한 문서 id 의 일치로만 정해집니다. 모델이 쓴 문장은 판정에 쓰지 않습니다. 3. scored_count 와 excluded_count 의 합이 입력 질문 수와 같은지 대조합니다. 어긋나면 질문이 조용히 빠진 것이라 실행을 중단합니다. 4. verdict 가 채점불가이면 exclude_reason 이 반드시 채워졌는지 확인합니다. 5. citation_hit_rate 는 evidence 배열에서 다시 계산해 저장된 값과 맞는지 대조합니다. [수동 검증 · 조합 상위 후보에 한해 사람이 직접 봅니다] 1. 인용 문서는 맞는데 요약이 어긋난 답변이 있는지 20건 표본을 읽습니다. 이것은 규칙으로 잡히지 않습니다. 2. 신구 문서가 상충하는 질문에서 어느 쪽을 근거로 골랐는지가 납득되는지 확인합니다. 3. 추천 사유 문장이 표의 수치와 어긋나지 않는지 확인합니다.