레드브릭
경력근거: 실무 데이터 소스를 처리하는 파이프라인 구축 및 청킹, 임베딩, 벡터 검색 최적화
레드브릭은 고객사 비즈니스 요구를 맞춤형 AI 애플리케이션으로 구현해 배포하는 자리를 뽑고, 주요 업무에 실무 데이터 소스를 처리하는 파이프라인과 청킹·임베딩·벡터 검색 최적화를 적어 두었습니다. 고객사마다 문서가 다르니 어느 설정이 맞는지는 들어가 봐야 알고, 그래서 설정 판정을 규칙으로 돌릴 수 있게 만들어 두면 다음 고객사에서 질문표만 갈아 끼우면 됩니다. 이 재사용 구조가 공고가 말하는 평가 체계 세팅의 최소 형태이고, 고객사 문서를 통째로 넘겨받아 시작하는 파견 자리에서만 성립합니다. 공고 업무는 인프라 배포부터 비용 최적화까지 걸쳐 있는데 1주 범위에서 고를 수 있는 것은 하나라 회수 판정 축을 골랐습니다. 이 포트폴리오는 판단 기준을 데이터로 분리해 규칙으로 집행하고 근거를 남기는 기술을 보여줍니다. 멀티클라우드 배포나 고객사 업종 지식 자체의 전문성은 주장하지 않습니다. 그 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
실무 데이터 소스를 처리하는 파이프라인을 만들고 청킹과 임베딩, 벡터 검색을 최적화하는 일이 주요 업무에 적혀 있습니다. 그 최적화의 첫 결정이 문서를 어떻게 자를지입니다.
LLMOps 항목에 모델 버전 관리와 프롬프트 관리, 평가 체계 세팅이 들어 있습니다. 설정을 바꿔 가며 회수가 되는지 재는 것이 그 평가 체계의 가장 앞자리입니다.
추상적인 비즈니스 목표를 기술 아키텍처와 인도 계획으로 바꾸는 것이 첫 업무로 적혀 있습니다. 어느 설정으로 시작할지를 근거와 함께 정하는 것이 그 인도 계획의 첫 줄입니다.
고객사에서 받은 문서 묶음과 담당자가 적어 준 확인 질문, 그리고 청킹 설정 후보표를 받아 설정마다 정답 문단이 회수되는지 계산하고 어느 설정으로 시작할지를 사유와 함께 출력하며 고객 보고용 리포트를 남기는 CLI입니다. 회수 판정은 질문에 딸린 정답 인용이 그 설정으로 자른 청크 안에 통째로 들어 있는지를 문자열로 대조해 정합니다.
고객사에 RAG를 붙이러 처음 들어간 파견 엔지니어입니다. 문서를 몇 자로 자를지, 겹침을 얼마나 줄지 감으로 정하고 질문 몇 개를 손으로 던져 보다가, 답이 이상하면 자른 길이 탓인지 검색 탓인지 모른 채 설정을 다시 만집니다.
질문마다 정답이 문서 어디에 있는지 적혀 있으면, 어떤 청킹 설정이 그 정답을 회수하는지는 사람이 답을 읽어 보지 않아도 대조로 갈라집니다.
문서 묶음과 확인 질문표, 설정 후보표를 인자로 받아 설정마다 질문별 회수 판정을 터미널에 출력하고 고객 보고용 리포트를 떨구는 CLI입니다. 벡터 DB 없이 문자열 대조로 회수를 판정합니다.
pgvector에 청크를 넣고 상위 몇 번째에 정답 청크가 오는지까지 재어, 잘림 문제와 순위 문제를 나눠 보여주는 구성입니다.
확인 질문표를 파일로 분리해 고객사마다 다른 질문 묶음을 같은 판정으로 돌리고, 설정 결정 근거를 인도 계획 문서에 붙일 수 있게 내보냅니다.
담당자가 문서 12건과 질문 12건, 설정 후보 4개를 넣어 설정별 회수율과 걸린 질문이 사유별로 나뉜 판정표와 고객 보고용 리포트가 나오는 것을 시연합니다.
잘림·정답 결측·미등록 문서가 섞인 입력 30종을 넣어도 출력 형식이 깨지지 않고, 판정하지 못한 질문은 사유가 붙어 남습니다.
정답 인용이 문서 본문에 없는 질문은 회수 실패로 세지 않고 질문표 결함으로 따로 답니다. 문서 탓이 아닌 것을 설정 탓으로 만들지 않습니다.
판정과 손으로 만든 정답표를 맞춰 사유별로 어긋난 건을 셉니다. 단, 문서와 질문을 모두 작성자가 만든 합성 기준이라 성능 측정이 아니라 데모 조건 동작입니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 더미데이터 자가채점이며, 실제 회수 정확도는 레드브릭 고객사의 실제 문서와 실제 질문이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
쉬운 질문만 넣으면 어떤 설정을 골라도 회수가 됩니다. 정답이 문단 경계에 걸치는 질문과 정답 인용이 아예 없는 질문을 더미에 일부러 심었습니다.
회수가 됐다고 해서 그 설정이 답까지 잘 만든다고 단정할 수 없습니다. 답 품질은 이 도구가 보지 않으므로, 판정은 정답 문단이 들어왔는지까지만 읽어야 합니다.
정답 인용을 담당자가 손으로 적어야 합니다. 그 인용이 부정확하면 판정도 함께 틀리므로, 도입 단계에서는 인용을 문서에서 직접 복사하도록 안내해야 합니다.
실제 도입하면 문서 형식이 훨씬 지저분합니다. 표와 이미지가 섞인 문서에서는 자르기 전에 텍스트 추출 품질이 먼저 문제가 되고, 그 단계는 이 도구의 범위 밖입니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사에서 받은 문서 묶음입니다. 문단이 길어 한 청크에 안 들어가는 문서와 표를 그대로 옮겨 붙인 문서, 질문표가 가리키는데 묶음에 없는 문서를 일부러 섞었습니다.
| doc_id | title | section | body | char_len |
|---|---|---|---|---|
| d_refund_01 | 환불 규정 | 제3장 | 환불은 수령일로부터 7일 안에 신청해야 합니다. 단순 변심은 왕복 배… | 72 |
| d_refund_02 | 환불 규정 부칙 | 부칙 | 부칙 시행일은 2026년 1월 1일입니다. | 24 |
| d_ship_01 | 배송 안내 | 제1장 | 주문은 결제 확인 후 영업일 기준 2일 안에 출고합니다. 도서산간은 … | 50 |
| d_ship_02 | 배송 지연 처리 | 제2장 | 출고가 5일을 넘기면 고객에게 지연 사유를 알리고 취소 여부를 확인합… | 58 |
| d_member_01 | 회원 등급 | 제1장 | 등급은 최근 6개월 구매 금액으로 정합니다. 일반은 30만원 미만, … | 132 |
아래 JSON Schema 에 맞는 고객사 문서 행을 더 만들어 주세요. {"type":"object","properties":{"doc_id":{"type":"string"},"title":{"type":"string"},"section":{"type":"string"},"body":{"type":"string"},"char_len":{"type":"integer"}},"required":["doc_id","title","section","body","char_len"]} doc_id 는 d_ 로 시작하는 식별자이고 char_len 은 body 의 글자 수와 같아야 합니다. body 는 규정 문서 문체로 쓰되 열 건에 한 번쯤 150자를 넘겨 한 청크에 안 들어가게 해 주세요.{"type":"object","properties":{"doc_id":{"type":"string"},"title":{"type":"string"},"section":{"type":"string"},"body":{"type":"string"},"char_len":{"type":"integer"}},"required":["doc_id","title","section","body","char_len"]}시험해 볼 청킹 설정 후보입니다. 겹침이 아직 정해지지 않은 설정을 일부러 섞었습니다.
| config_id | chunk_chars | overlap_chars | split_by | top_k | label |
|---|---|---|---|---|---|
| cfg_01 | 50 | 0 | char | 3 | 짧게 자르기 |
| cfg_02 | 100 | 20 | char | 3 | 중간 길이 |
| cfg_03 | 200 | 40 | char | 3 | 길게 자르기 |
| cfg_04 | 200 | 0 | paragraph | 3 | 문단 단위 |
| cfg_05 | 80 | — | char | 3 | 겹침 미정 |
아래 JSON Schema 에 맞는 청킹 설정 행을 더 만들어 주세요. {"type":"object","properties":{"config_id":{"type":"string"},"label":{"type":"string"},"chunk_chars":{"type":"integer"},"overlap_chars":{"type":["integer","null"]},"split_by":{"type":"string"},"top_k":{"type":"integer"}},"required":["config_id","label","chunk_chars","overlap_chars","split_by","top_k"]} config_id 는 cfg_ 로 시작하고 split_by 는 char·paragraph·sentence 중 하나입니다. overlap_chars 는 chunk_chars 보다 작아야 하고 열 행에 한 번쯤 null 로 비워 주세요. config_id 는 cfg_ 뒤에 두 자리 번호를 붙이고 label 은 그 설정을 부르는 한국어 명사구입니다.{"type":"object","properties":{"config_id":{"type":"string"},"label":{"type":"string"},"chunk_chars":{"type":"integer"},"overlap_chars":{"type":["integer","null"]},"split_by":{"type":"string"},"top_k":{"type":"integer"}},"required":["config_id","label","chunk_chars","overlap_chars","split_by","top_k"]}고객사 담당자가 적어 준 확인 질문입니다. 정답 인용을 문서에서 그대로 복사하지 않아 본문에 없는 질문과, 묶음에 없는 문서를 가리키는 질문을 일부러 섞었습니다.
| q_id | question | gold_doc_id | gold_quote | must_recall |
|---|---|---|---|---|
| q_001 | 환불은 며칠 안에 신청해야 하나요? | d_refund_01 | 환불은 수령일로부터 7일 안에 신청해야 합니다 | true |
| q_002 | 단순 변심이면 배송비는 누가 내나요? | d_refund_01 | 단순 변심은 왕복 배송비를 구매자가 부담하고 | true |
| q_003 | 출고는 며칠 걸리나요? | d_ship_01 | 결제 확인 후 영업일 기준 2일 안에 출고합니다 | true |
| q_004 | VIP 등급 기준은 얼마인가요? | d_member_01 | VIP는 300만원 이상입니다 | true |
| q_005 | 등급 강등은 언제 되나요? | d_member_01 | 강등은 두 달 연속 기준에 못 미칠 때만 적용합니다 | true |
아래 JSON Schema 에 맞는 확인 질문 행을 더 만들어 주세요. {"type":"object","properties":{"q_id":{"type":"string"},"question":{"type":"string"},"gold_doc_id":{"type":"string"},"gold_quote":{"type":"string"},"must_recall":{"type":"boolean"}},"required":["q_id","question","gold_doc_id","gold_quote","must_recall"]} q_id 는 q_ 로 시작하는 일련번호이고 gold_quote 는 그 문서 본문에서 그대로 복사한 구절이어야 합니다. 다만 열 행에 한 번쯤 본문에 없는 구절이나 묶음에 없는 gold_doc_id 를 섞어 주세요.{"type":"object","properties":{"q_id":{"type":"string"},"question":{"type":"string"},"gold_doc_id":{"type":"string"},"gold_quote":{"type":"string"},"must_recall":{"type":"boolean"}},"required":["q_id","question","gold_doc_id","gold_quote","must_recall"]}| Python | 세 입력 파일을 읽어 문서를 자르고 회수 판정을 돌려 리포트를 만드는 본체입니다. |
| LangChain | 청킹 전략을 갈아끼우는 자리입니다. 문자 기준과 문단 기준 분할을 같은 인터페이스로 돌립니다. |
| pgvector | 2주차에 청크를 넣어 상위 몇 번째에 정답 청크가 오는지 재는 자리입니다. |
| Docker | 고객사 노트북에서 같은 결과가 나오도록 실행 환경을 묶습니다. |
| LLM | 회수가 된 질문에 한해 답 초안을 만들어 보여주는 보조입니다. 회수 판정에는 쓰지 않습니다. |
| AWS | 3주차에 고객사 VPC 안에서 같은 판정을 돌릴 때 쓰는 실행 환경입니다. |
✓ pass → 여기서 멈춰도 완결입니다. 더 키우려면 임베딩 검색을 붙여 순위까지 봅니다.
✓ pass → 순위까지가 한 덩어리입니다. 더 키우려면 질문표를 고객사별로 분리합니다.
확인 질문 하나가 어떤 청킹 설정에서 회수되는지의 판정 결과입니다. 회수되지 않으면 무엇 때문인지를 근거와 함께 담습니다.
{"type":"object","properties":{"q_id":{"type":"string"},"config_id":{"type":"string"},"evaluated_doc_id":{"type":["string","null"]},"verdict":{"type":"string","enum":["recalled","split_across_chunks","not_in_topk","gold_quote_missing","doc_unmapped","config_incomplete"]},"gold_chunk_no":{"type":["integer","null"]},"observed_value":{"type":["number","null"]},"required_value":{"type":["number","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":["q_id","config_id","evaluated_doc_id","verdict","gold_chunk_no","observed_value","required_value","evidence","next_action"]}[
{
"q_id": "q_001",
"config_id": "cfg_02",
"evaluated_doc_id": "d_refund_01",
"verdict": "recalled",
"gold_chunk_no": 1,
"observed_value": 100,
"required_value": 72,
"evidence": [
{
"field": "gold_quote",
"value": "환불은 수령일로부터 7일 안에 신청해야 합니다",
"quote": "환불은 수령일로부터 7일 안에 신청해야 합니다"
},
{
"field": "chunk_chars",
"value": 100
},
{
"field": "char_len",
"value": 72
},
{
"field": "doc_id",
"value": "d_refund_01"
}
],
"next_action": "정답 구절이 첫 청크에 통째로 들어갑니다. 이 설정을 후보로 남깁니다."
},
{
"q_id": "q_012",
"config_id": "cfg_04",
"evaluated_doc_id": "d_cs_01",
"verdict": "recalled",
"gold_chunk_no": 1,
"observed_value": 200,
"required_value": 62,
"evidence": [
{
"field": "gold_quote",
"value": "평일 오전 9시부터 오후 6시까지 받습니다",
"quote": "평일 오전 9시부터 오후 6시까지 받습니다"
},
{
"field": "split_by",
"value": "paragraph"
},
{
"field": "char_len",
"value": 62
},
{
"field": "doc_id",
"value": "d_cs_01"
}
],
"next_action": "문단 단위로 잘라도 한 청크에 들어갑니다. 짧은 문서에는 문단 분할이 안전합니다."
},
{
"q_id": "q_002",
"config_id": "cfg_01",
"evaluated_doc_id": "d_refund_01",
"verdict": "split_across_chunks",
"gold_chunk_no": null,
"observed_value": 50,
"required_value": 72,
"evidence": [
{
"field": "gold_quote",
"value": "단순 변심은 왕복 배송비를 구매자가 부담하고"
},
{
"field": "chunk_chars",
"value": 50
},
{
"field": "char_len",
"value": 72
},
{
"field": "doc_id",
"value": "d_refund_01"
}
],
"next_action": "정답 구절이 50자 경계에 걸려 두 청크로 갈렸습니다. 겹침을 두거나 청크를 키웁니다."
},
{
"q_id": "q_009",
"config_id": "cfg_07",
"evaluated_doc_id": "d_long_01",
"verdict": "not_in_topk",
"gold_chunk_no": 6,
"observed_value": 5,
"required_value": 5,
"evidence": [
{
"field": "top_k",
"value": 5
},
{
"field": "chunk_chars",
"value": 30
},
{
"field": "char_len",
"value": 168
},
{
"field": "doc_id",
"value": "d_long_01"
}
],
"next_action": "정답 청크가 상위 다섯 안에 들지 못했습니다. 청크를 키우거나 top_k 를 올립니다."
},
{
"q_id": "q_010",
"config_id": "cfg_02",
"evaluated_doc_id": "d_refund_01",
"verdict": "gold_quote_missing",
"gold_chunk_no": null,
"observed_value": null,
"required_value": null,
"evidence": [
{
"field": "gold_quote",
"value": "반품 수수료는 5천원입니다"
},
{
"field": "doc_id",
"value": "d_refund_01"
}
],
"next_action": "정답 구절이 그 문서 본문에 없습니다. 설정 탓이 아니라 질문표 결함이라 담당자에게 인용을 다시 받습니다."
},
{
"q_id": "q_011",
"config_id": "cfg_02",
"evaluated_doc_id": null,
"verdict": "doc_unmapped",
"gold_chunk_no": null,
"observed_value": null,
"required_value": null,
"evidence": [
{
"field": "gold_doc_id",
"value": "d_dormant_01"
},
{
"field": "must_recall",
"value": false
}
],
"next_action": "질문이 가리키는 문서가 묶음에 없습니다. 문서를 받기 전에는 판정에서 뺍니다."
},
{
"q_id": "q_003",
"config_id": "cfg_05",
"evaluated_doc_id": "d_ship_01",
"verdict": "config_incomplete",
"gold_chunk_no": null,
"observed_value": null,
"required_value": null,
"evidence": [
{
"field": "overlap_chars",
"value": null
},
{
"field": "config_id",
"value": "cfg_05"
}
],
"next_action": "이 설정의 겹침이 정해져 있지 않아 자르지 않았습니다. 값을 채운 뒤 다시 돌립니다."
},
{
"q_id": "q_008",
"config_id": "cfg_03",
"evaluated_doc_id": "d_table_01",
"verdict": "recalled",
"gold_chunk_no": 1,
"observed_value": 200,
"required_value": 66,
"evidence": [
{
"field": "gold_quote",
"value": "카드 | 2.5% | 3.5%",
"quote": "카드 | 2.5% | 3.5%"
},
{
"field": "chunk_chars",
"value": 200
},
{
"field": "char_len",
"value": 66
},
{
"field": "doc_id",
"value": "d_table_01"
}
],
"next_action": "표를 옮겨 붙인 문서라 크게 잘라야 행이 안 끊깁니다. 표 문서는 큰 설정으로 따로 돌립니다."
}
][자동 검증 · 1주차] 1. verdict 가 enum 여섯 값 안에 있는지 확인합니다. 2. q_id·config_id·evaluated_doc_id·verdict·gold_chunk_no·observed_value·required_value·evidence·next_action 이 모두 있는지 확인합니다. 3. char_len 이 그 문서 body 의 글자 수와 같은지 확인합니다. 4. overlap_chars 가 chunk_chars 보다 작은지 확인하고, null 이면 verdict 가 config_incomplete 인지 확인합니다. 5. evaluated_doc_id 가 client_documents 에 실재하는 doc_id 인지 확인합니다. 6. evidence 의 field 가 세 입력 표의 컬럼에 실재하는지 확인합니다. 7. evidence 의 quote 가 그 문서 body 의 일부인지 확인합니다. 8. next_action 이 존댓말로 끝나는지 확인합니다. [판정 규칙] 1. 질문의 gold_doc_id 가 client_documents 에 없으면 doc_unmapped 로 남기고 그 질문은 나머지 판정에서 뺍니다. 2. gold_quote 가 그 문서 body 에 문자열로 없으면 gold_quote_missing 입니다. 설정 탓으로 세지 않습니다. 3. 설정의 overlap_chars 가 비어 있으면 config_incomplete 로 남기고 자르지 않습니다. 4. 문서를 설정값대로 자른 뒤 gold_quote 가 어느 한 청크 안에 통째로 들어가면 그 청크 번호를 적고, 어느 청크에도 통째로 들어가지 않으면 split_across_chunks 입니다. 5. 통째로 들어간 청크가 있어도 그 번호가 top_k 보다 크면 not_in_topk 입니다. 그 밖은 recalled 입니다. 6. 사유는 하나만 붙입니다. 우선순위는 doc_unmapped → gold_quote_missing → config_incomplete → split_across_chunks → not_in_topk → recalled 입니다. [수동 검증] 1. 회수된 청크가 사람이 보기에도 그 질문의 답을 담고 있는지 고객사 담당자가 확인합니다. 2. 표를 옮겨 붙인 문서에서 행이 끊기지 않았는지 눈으로 확인합니다. [평가 러너 · 2주차] 1. 설정별 회수율을 세어 어느 설정이 가장 많은 질문을 회수하는지 나눕니다. 2. 문서와 질문을 작성자가 만든 합성 기준이라 이 수치는 성능 측정이 아니라 데모 조건 동작입니다.