
생성 수정
IBM * 레드햇 AX 과정 · 23강
뜻을 읽는 기계 — 시맨틱, 그리고 기억을 고칠 것인가 찾아올 것인가
2026년 7월 13일(월) ~ 7월 17일(금)
이 장을 다 읽으면
- 시맨틱(Semantic) 이 웹의 태그와 AI의 의도 분류라는 서로 다른 두 자리에서 같은 뜻으로 쓰이는 이유를 설명할 수 있다.
<div>로 짠 화면과 시맨틱 태그로 짠 화면이 눈에는 똑같아 보이는데 무엇이 다른지 세 가지 목적(SEO·접근성·유지보수)으로 말할 수 있다.- 사람이 새 지식을 얻는 두 가지 방법(오픈북 · 요점정리)이 AI의 RAG와 PEFT/QLoRA에 어떻게 정확히 대응되는지 설명할 수 있다.
- RAG를 Retrieve → Augment → Generate 세 단계로 쪼개고, 이 방식이 환각(Hallucination) 을 왜 줄이는지 말할 수 있다.
- 가중치(Weights) 를 한 문장으로 정의하고, Full Fine-Tuning · PEFT(LoRA) · QLoRA · RAG 네 기술이 가중치를 각각 어떻게 취급하는지 표로 재현할 수 있다.
- 어떤 과제를 받았을 때 RAG로 갈지 파인튜닝으로 갈지 판단하는 기준을 대고, 그 순서가 왜 "프롬프트 → RAG → 파인튜닝"인지 설명할 수 있다.
- 하네스 가드레일(Harness Guardrail) 의 두 기둥(표준화된 어댑터 레이어 · TDD 기반 평가 루프)을 구별하고, 앞에서 배운 포트&어댑터와 어떻게 이어지는지 말할 수 있다.
- 데이터가 set(Disk) → frame(Memory) → base(Runtime) 3단계로 흐른다는 것을 알고, 3주차의 디스크↔메모리 두 칸이 왜 여기서 세 칸이 되는지 설명할 수 있다.
- 런타임 객체의 변수를 value(불변) 와 price(가변) 로 가르고, price의 총합이 왜 환경(Environment) 이 되는지 피드백 루프로 설명할 수 있다.
- 크롤링과 스크래핑을 구별하고, 수집한 데이터를 어디에(Redis) 무엇으로(jsonl) 남기는지 절차대로 실행할 수 있다.
0. 이 장의 판별 축
앞에서 배운 대로, 새 용어를 만나면 비유를 찾기 전에 아래 축 중 어디에 꽂히는지부터 정하세요. 이번 주는 특히 위험합니다. 백과사전과 포스트잇, 오픈북 시험, 수도꼭지 밸브 — 예쁜 비유가 유난히 많이 나옵니다. 비유부터 외우면 "그럼 RAG도 결국 학습 아닌가요?" 에서 무너집니다.
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 |
|---|---|---|---|---|
| 1 | 형태인가 의미인가 | 형태 (어떻게 보이는가) | 의미 (무엇인가) | <div>·<span> ↔ <header>·<article> / 키워드 일치 ↔ 시맨틱 인텐트 |
| 2 | 밖에서 찾는가 안을 고치는가 | 밖에서 찾아온다 | 안을 고친다 | RAG ↔ 파인튜닝 / 사람의 검색 ↔ 사람의 암기 |
| 3 | 전부인가 일부인가 | 전부 조정 | 일부만 조정 | Full Fine-Tuning ↔ PEFT(LoRA·QLoRA) |
| 4 | 불변인가 가변인가 | value (Immutable) | price (Mutable) | 일련번호·규격 ↔ 감가상각·잔여량 |
| 5 | 어디에 있는가 (3칸) | set (Disk) — frame (Memory) — base (Runtime) | 정적 데이터셋 ↔ DataFrame ↔ 실행 중인 기준 객체 |
축 1과 축 2가 이 장의 중심입니다. 축 1은 이 장의 제목이자 이번 주 내내 반복되는 단어(시맨틱)의 정체이고, 축 2는 이번 주의 가장 큰 결정입니다. AI에게 새 지식을 주는 방법은 결국 두 가지뿐이고, 어느 쪽을 고르느냐가 비용·속도·위험을 전부 바꿉니다. 축 3은 축 2의 오른쪽(안을 고친다)을 한 번 더 쪼갠 것입니다.
앞 장에서 이어집니다
- 축 1은 10주차 축 1(구문론 ↔ 의미론)의 아들입니다. 10주차에서는 "형식이 맞는가, 뜻이 맞는가"라는 철학적 축이었고, 이번 주에는 그 축이 HTML 태그 한 줄과 게이트웨이 분기 코드 한 줄로 내려옵니다.
- 축 5는 3주차 축 1(디스크 ↔ 메모리)의 확장입니다. 3주차에서는 두 칸이었습니다. 이번 주에 런타임이라는 세 번째 칸이 정식으로 추가됩니다. 메모리에 올라와 있는 것(frame)과 실제로 실행되며 상태가 변하는 것(base)이 다르기 때문입니다. 3주차의
프로그램(메모리에 로드됨) ↔ 프로세스(CPU를 쓰며 실행 중)구분이 데이터 쪽에서 다시 나타난 것입니다.- 축 4는 5주차의 정적(STATIC) ↔ 동적(DYNAMIC) 축과 같은 축입니다. 그때는
Attribute ↔ Property,클래스 변수 ↔ 인스턴스 변수,상수 ↔ 변수세 쌍으로 나왔습니다. 이번 주에는 시뮬레이션 객체의 변수 설계로 내려와value ↔ price라는 이름을 얻습니다.- 하네스(Harness) 는 이 교재에서 계속 나온 단어입니다. 이번 주에 가드레일(Guardrail) 이라는 짝을 만나 완성됩니다.
1. 시맨틱 — 형태가 아니라 뜻 ⭐
1.1 한 단어가 두 곳에서 나온다
이번 주에 시맨틱(Semantic) 이라는 단어가 완전히 다른 두 자리에서 나옵니다. 프론트엔드 이야기를 하다가 "시맨틱 태그를 쓰세요, <div> 남발하지 마세요"라고 하고, AI 게이트웨이 이야기를 하다가 "이 라우터는 시맨틱 인텐트 분류기입니다"라고 합니다. HTML 태그와 챗봇 라우팅이 무슨 관계일까요. 같은 단어이고 같은 뜻입니다.
⭐ 시맨틱(Semantic) — 소프트웨어에서 시맨틱은 "눈에 보이는 형태를 넘어, 그것이 담고 있는 본질적인 의미를 컴퓨터가 이해할 수 있게 만드는 것"입니다.
두 자리에서 대상만 다릅니다.
| 대상 | 형태(0) | 의미(1) | |
|---|---|---|---|
| 시맨틱 태그 | HTML 문서 | 이 상자는 회색이고 위쪽에 있다 | 이 상자는 머리말(header)이다 |
| 시맨틱 인텐트 | 사용자 문장 | 이 문장에 "비"라는 글자가 있다 | 이 문장은 날씨를 묻고 있다 |
둘 다 "겉모습만 보고 처리하지 말고, 그게 무엇인지를 보고 처리하라"는 같은 요구입니다.
1.2 정의 — 유(類)와 종차(種差)
- 시맨틱 태그(Semantic Tag) — [HTML 태그]인데, [태그 이름 자체가 그 영역의 역할을 선언한다]는 점에서
<div>·<span>같은 의미 없는 상자와 구별됩니다. - 시맨틱 인텐트(Semantic Intent) — [사용자 입력 해석 방식]인데, [단어의 물리적 일치가 아니라 문장 맥락 속의 진짜 목적을 판단한다]는 점에서 키워드 매칭과 구별됩니다.
- 시맨틱 웹(Semantic Web) — [웹의 설계 사상]인데, [문서를 사람만이 아니라 기계도 뜻으로 읽을 수 있게 구조화한다]는 점에서 단순 하이퍼텍스트 웹과 구별됩니다.
번역어 주의 —
Semantic을 "의미론적"이라고만 옮기면 철학 용어처럼 들려 실감이 사라집니다. 현장에서 이 단어의 실제 뜻은 "이름표를 제대로 붙여라" 에 가깝습니다. 상자에 "상자"라고 쓰지 말고 "머리말", "본문", "바닥글"이라고 쓰라는 것입니다.
1.3 시맨틱 태그 — 같은 화면, 다른 문서
10주차 9.2절에서 <div style="font-size:32px"> 과 <h1> 이 같은 화면, 다른 뜻이라는 것을 배웠습니다. 이번 주에 그 이야기가 문서 전체의 뼈대로 확장됩니다. 아래 두 코드는 브라우저에서 완전히 똑같이 보입니다.
<!-- ❌ 의미 없는 상자만 쌓은 문서 -->
<div class="top">
<div class="logo">랙테일러</div>
<div class="menu">
<div><a href="/">홈</a></div>
<div><a href="/blog">블로그</a></div>
</div>
</div>
<div class="body">
<div class="post">
<div class="title">RAG란 무엇인가</div>
<div class="text">외부 문서를 찾아와 답을 만드는 방식입니다.</div>
</div>
</div>
<div class="bottom">© 2026</div>
<!-- ⭕ 시맨틱 태그로 뼈대를 선언한 문서 -->
<header>
<h1>랙테일러</h1>
<nav>
<ul>
<li><a href="/">홈</a></li>
<li><a href="/blog">블로그</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h2>RAG란 무엇인가</h2>
<p>외부 문서를 찾아와 답을 만드는 방식입니다.</p>
</article>
</main>
<footer>© 2026</footer>
두 번째 문서는 CSS를 전부 걷어내도 무엇이 머리말이고 무엇이 본문인지 알 수 있습니다. 첫 번째 문서는 CSS를 걷어내는 순간 그냥 글자 더미가 됩니다. 이 차이가 결정적인 이유는 검색엔진 로봇과 스크린 리더는 CSS를 보지 않기 때문입니다.
1.4 주요 시맨틱 태그
| 태그 | 선언하는 의미 | <div> 로 짰다면 |
|---|---|---|
<header> | 이 영역은 머리말이다 (로고·타이틀·상단 내비) | <div class="top"> |
<nav> | 이 영역은 길잡이 링크 모음이다 | <div class="menu"> |
<main> | 이 문서의 본문 중심 영역이다 (문서당 하나) | <div class="body"> |
<article> | 그 자체로 독립해 배포 가능한 한 덩어리 (글 하나, 상품 하나) | <div class="post"> |
<section> | 주제로 묶인 한 구획 (보통 제목을 동반) | <div class="block"> |
<aside> | 본문과 직접 관련이 적은 곁가지 (사이드바·광고) | <div class="side"> |
<footer> | 이 영역은 바닥글이다 (저작권·연락처) | <div class="bottom"> |
<figure> / <figcaption> | 그림과 그 설명의 짝 | <div class="img"> |
<time> | 이 글자는 시각이다 (datetime 속성으로 기계용 값 제공) | <span>2026-07-13</span> |
💡
<article>과<section>이 가장 자주 헷갈립니다. 판별 질문 하나: "이 덩어리만 잘라서 RSS로 내보내도 말이 되는가?" 말이 되면<article>, 문서 안에서만 말이 되면<section>입니다.
1.5 시맨틱 태그를 쓰는 세 가지 목적
목적을 외우지 말고 누가 그것을 읽는가로 기억하세요. 읽는 주체가 셋이라서 목적도 셋입니다.
① SEO — 읽는 주체는 검색엔진 로봇. 검색엔진 로봇이 페이지의 뼈대와 핵심 정보를 오차 없이 이해해서 검색 결과 상위에 노출하도록 기여합니다. 로봇 입장에서 <div class="title"> 의 title 은 개발자가 지은 이름일 뿐입니다. 옆 회사는 <div class="hd"> 라고 썼을 수도 있어 신뢰할 수 없습니다. 반면 <h1> 과 <article> 은 HTML 규격이 정한 이름이라 전 세계가 같은 뜻으로 씁니다. 10주차의 구문론 ↔ 의미론 축이 그대로 작동합니다 — class="title" 은 구문(형태)이고 <h1> 은 의미입니다.
② 웹 접근성(Accessibility) — 읽는 주체는 스크린 리더. 시각장애인용 화면 낭독기가 각 영역의 역할을 정확히 파악해 사용자에게 전달합니다. 스크린 리더 사용자는 페이지를 위에서 아래로 다 듣지 않고 "내비게이션으로 건너뛰기", "본문으로 건너뛰기" 같은 랜드마크 이동을 씁니다. 이 랜드마크가 바로 <nav>, <main>, <header>, <footer> 입니다. <div> 로만 짠 페이지에는 건너뛸 지점 자체가 존재하지 않습니다.
③ 코드 가독성·유지보수 — 읽는 주체는 동료 개발자(그리고 6개월 뒤의 나). 협업 시 동료가 복잡한 레이아웃 구조를 한눈에 직관적으로 해석할 수 있게 만듭니다. </div> 가 40개 연달아 닫히는 파일에서는 어느 것이 무엇을 닫는지 세는 데 시간이 갑니다. </main>, </article>, </footer> 는 자기가 무엇을 닫는지 스스로 말합니다.
⚠️ 흔한 오해: "시맨틱 태그를 쓰면 화면이 예뻐지거나 빨라진다" ❌ 렌더링 결과와 속도는 사실상 같습니다. 바뀌는 것은 기계가 읽어낼 수 있는 정보의 양입니다. 사람 눈에는 0의 변화, 기계 입장에서는 1의 변화입니다.
1.6 반례 — 이건 시맨틱이 아닙니다
| 이건 시맨틱이 아닙니다 | 왜 | 무엇인가 |
|---|---|---|
<div class="header"> | 태그는 여전히 의미 없는 상자입니다. class 는 내가 지은 별명일 뿐 | 그냥 <div> |
<b> 로 제목 만들기 | <b> 는 "굵게 보여라"는 형태 지시 | 표현용 태그 |
<h1> 을 한 페이지에 열 개 쓰기 | 위계가 무너지면 뼈대가 아니라 소음이 됩니다 | 잘못 쓴 시맨틱 태그 |
<section> 을 여백 주려고 쓰기 | 레이아웃 목적으로 쓰면 의미가 거짓말이 됩니다 | 오용 |
| CSS 클래스 이름을 의미 있게 짓기 | 좋은 습관이지만 기계는 여전히 읽지 못합니다 | 코드 컨벤션 |
⭐ 핵심 경계선: 시맨틱이냐 아니냐는 내가 붙인 이름이냐, 규격이 정한 이름이냐로 갈립니다. 내가 지은 이름은 나와 내 팀만 압니다. 규격이 정한 이름은 전 세계의 기계가 압니다.
1.7 시맨틱 인텐트 — 문장에서 목적을 꺼내기
시맨틱 인텐트(Semantic Intent, 의미론적 의도) — 사용자가 입력한 구체적인 단어(Keyword) 자체의 물리적 일치 여부를 따지는 대신, 문장 맥락 속에 담긴 진짜 목적이나 의도(Intent) 를 시스템이 의미적으로 이해하고 처리하는 개념입니다.
사용자 A: "내일 비 올까?"
사용자 B: "내일 외출할 때 우산 필요해?"
두 문장에는 공통 단어가 '내일' 하나뿐이고 B에는 '비'라는 글자가 아예 없습니다. 키워드 매칭으로는 B를 날씨 질문으로 잡아낼 방법이 없습니다. 그런데 시맨틱 인텐트 분석은 두 문장을 똑같이 '날씨 정보 조회'라는 단 하나의 동작(Function)에 매핑하여, 사용자가 어떻게 묻든 일관된 정보를 제공합니다.
"내일 비 올까?" ─┐
"내일 외출할 때 우산 필요해?" ├─▶ [의도 분류기] ─▶ get_weather(date="tomorrow")
"낼 날씨 어때" ─┘
주요 쓰임새는 세 곳입니다. ① 검색엔진의 시맨틱 검색 — "취소하고 싶어요"로 "환불 규정" 문서를 찾아냅니다(10주차 pgvector 절과 같은 이야기입니다). ② 자연어 처리(NLP) 기반 챗봇 설계 — 자유로운 문장을 정해진 기능에 연결합니다. ③ LLM 에이전트 구축 — 이번 주 8장에서 만들 게이트웨이가 정확히 이 일을 합니다.
⭐ 시맨틱 인텐트는 10주차에서 배운 임베딩(뜻을 좌표로 바꾸기) 위에서 돌아갑니다. "비 올까"와 "우산 필요해"가 벡터 공간에서 가까운 좌표에 찍히기 때문에 같은 의도로 묶을 수 있습니다. 임베딩이 재료이고, 시맨틱 인텐트는 그 재료로 만든 판정입니다.
1.8 두 얼굴을 한 표로
| 시맨틱 태그 & 웹 | 시맨틱 인텐트 | |
|---|---|---|
| 대상 | HTML 문서 구조 | 사용자 자연어 문장 |
| 형태(0)에 해당하는 것 | <div>, <span>, class 이름 | 단어의 물리적 일치(키워드 매칭) |
| 의미(1)에 해당하는 것 | <header>, <nav>, <article> | 문장 맥락 속의 목적(Intent) |
| 읽는 주체 | 검색엔진 로봇, 스크린 리더, 동료 | 라우터 모델, 에이전트 |
| 정하는 사람 | HTML 표준(W3C·WHATWG) | 내가 설계한 인텐트 목록 |
| 쓰이는 곳 | 프론트엔드 마크업, SEO | 챗봇 라우팅, 에이전트 게이트웨이 |
| 공통점 | 겉모습이 아니라 정체로 처리한다 | ← 같음 |
앞으로 다시 만납니다 — 시맨틱 인텐트는 8장에서 게이트웨이의 3분기 라우터로 코드가 됩니다. 그리고 그 라우터를 만드는 방법이 4장의 QLoRA입니다.
2. 인간과 AI의 학습 구조 대응 ⭐
2.1 방법은 두 가지뿐입니다
이번 주에서 가장 오래 남을 통찰입니다. 사람이 모르는 것을 알아내는 방법은 결국 두 가지밖에 없습니다. 밖에서 찾아오거나(검색창을 열어두고 찾아 응용 — 머릿속은 안 바뀝니다), 안을 고치거나(공부해서 넣어둠 — 다음부터는 안 찾아도 됩니다). AI도 똑같고, 이름만 다릅니다.
[ 지식 활용 구조의 직관적 대비 ]
■ 외부 자료 참고 (오픈북 시험)
- 인간 (Human) : Search
인터넷 검색 창을 모니터 한쪽에 열어두고 필요한 사실을 찾아내어 응용
- 인공지능 (AI) : RAG
자체 신경망을 변경하지 않고, 외부 벡터 DB에서 필요한 문서를 검색해
프롬프트에 동적으로 반영
■ 머릿속 기억 수정 (공부 및 내재화)
- 인간 (Human) : 핵심 요점 정리 공부
기존 기본 상식 위에 얇은 요점 정리 노트를 얹어 빠르게 마스터
- 인공지능 (AI) : PEFT / QLoRA
원본 가중치는 얼려둔 채, 아주 미세한 영역의 보조 가중치망만 학습
2.2 대응표
| 외부 자료 참고 (오픈북) | 머릿속 기억 수정 (내재화) | |
|---|---|---|
| 인간 | Search — 검색해서 찾아 씀 | 요점정리 공부 — 기본 상식 위에 얇은 노트를 얹음 |
| AI | RAG | PEFT / QLoRA |
| 뇌(가중치)가 바뀌는가 | ❌ 안 바뀜 | ⭕ 바뀜 (단, 아주 조금만) |
| 속도 | 즉시 (찾자마자 씀) | 느림 (공부하는 시간 필요) |
| 최신 정보 | ⭕ 검색하면 오늘 것도 나옴 | ❌ 공부한 시점까지만 |
| 매번 드는 비용 | 매 질문마다 검색 비용 | 한 번 공부하면 이후 공짜 |
| 잘 맞는 일 | 사실 조회, 사내 문서 Q&A, 규정 확인 | 말투·형식·전문 도메인 감각 익히기 |
| 못 하는 일 | 말투나 사고방식 자체를 바꾸기 | 오늘 아침 뉴스 반영하기 |
2.3 이 비유가 정확한 이유
비유는 보통 어딘가에서 깨지는데, 이 비유는 꽤 깊은 곳까지 정확합니다.
① 오픈북 시험에서도 기본기가 없으면 못 풉니다. 자료를 펼쳐놔도 읽고 조합할 능력이 없으면 소용없습니다. RAG도 아무리 좋은 문서를 넣어줘도 모델이 그 문서를 이해하고 종합할 능력이 없으면 답이 안 나옵니다. 그래서 RAG는 "약한 모델을 강하게 만드는 기술"이 아니라 "모르는 사실을 알려주는 기술" 입니다.
② 요점정리는 기본 상식 위에 얹는 것입니다. 백지 상태에서 요점정리만 보고 공부하는 사람은 없습니다. PEFT도 원본 모델(기본 상식)은 그대로 두고 그 위에 얇은 보조망만 얹기 때문에 좋은 베이스 모델이 전제입니다.
③ 둘은 배타적이지 않습니다. 공부도 하고 시험장에 자료도 들고 갑니다. 실무의 정답도 대부분 RAG + 파인튜닝 병행입니다(6.3절).
2.4 이 비유가 깨지는 지점 ⚠️
| 깨지는 지점 | 사람 | AI |
|---|---|---|
| 찾은 것을 기억하는가 | 검색하다 보면 자연스럽게 외워짐 | RAG는 절대 기억되지 않음. 대화가 끝나면 흔적 없이 사라짐 |
| 공부한 것을 잊는가 | 시간이 지나면 잊음 | 학습된 가중치는 파일로 남아 안 잊음. 대신 덮어쓰면 이전 능력이 손상될 수 있음(파국적 망각) |
⭐ RAG는 기억이 아닙니다. 컨텍스트입니다. 이 문장 하나만 붙잡고 있으면 앞으로 헷갈릴 일이 거의 없습니다. 4주차에서 배운 무상태(Stateless) 를 떠올리세요. RAG로 넣어준 문서는 그 요청 한 번짜리입니다.
2.5 판별 훈련 — 다음은 어느 쪽인가
| 상황 | RAG인가 파인튜닝인가 | 왜 |
|---|---|---|
| 어제 개정된 사내 휴가 규정을 반영하고 싶다 | RAG | 최신성이 핵심. 규정 바뀔 때마다 재학습은 불가능 |
| 모든 답변을 "~습니다"체 존댓말 세 문장으로 고정하고 싶다 | 프롬프트 → 안 되면 파인튜닝 | 말하는 방식의 문제이지 아는 내용의 문제가 아님 |
| 판결문 특유의 문체와 논증 구조를 익히게 하고 싶다 | 파인튜닝 | 도메인 감각은 문서를 넣어준다고 생기지 않음 |
| 특정 판례의 사건번호와 판시사항을 정확히 인용하게 하고 싶다 | RAG | 사실 조회. 파인튜닝으로 사실을 외우게 하면 환각이 늘어남 |
| 사용자 질문을 3가지 의도 중 하나로 분류하게 하고 싶다 | 파인튜닝(QLoRA) | 매번 똑같은 판단을 빠르게 반복해야 함. 8장의 게이트웨이 |
⚠️ 넷째 줄이 특히 중요합니다. "사실을 외우게 하려고 파인튜닝하지 마세요." 파인튜닝은 사실을 정확하게 외우는 데 서툴러 비슷하게 그럴듯한 것을 만들어냅니다. 정확한 사실이 필요하면 반드시 RAG입니다.
3. RAG — 밖에서 찾아오기
3.1 정의와 세 단계
RAG (Retrieval-Augmented Generation, 검색 증강 생성) — [LLM에게 지식을 공급하는 방법]인데, [모델 가중치를 전혀 바꾸지 않고 외부 저장소에서 찾아온 문서를 프롬프트에 붙여 넣는다]는 점에서 파인튜닝과 구별됩니다. 한국어 이름 "검색 증강 생성"은 드물게 원어를 잘 옮긴 번역어입니다. 세 단어가 그대로 세 단계입니다.
[사용자 질문]
│
▼
① Retrieve (검색) ─▶ 외부 데이터베이스(Knowledge Base)에서
│ 신뢰도 높은 최신 자료를 찾아낸다
▼
② Augment (증강) ─▶ 찾아낸 자료를 사용자의 원래 질문과 엮어
│ 프롬프트에 추가한다
▼
③ Generate (생성) ─▶ 그 프롬프트를 받은 모델이 답변을 만든다
│
▼
[근거가 붙은 답변]
| 단계 | 영문 | 무엇을 하는가 | 무엇으로 하는가 |
|---|---|---|---|
| ① | Retrieve | 질문과 뜻이 가까운 문서 조각을 찾아온다 | 벡터 DB (pgvector·Qdrant), 그래프 DB (Neo4j) |
| ② | Augment | 찾아온 조각을 질문 옆에 붙여 프롬프트를 만든다 | 프롬프트 템플릿 |
| ③ | Generate | 그 프롬프트만 보고 답을 만든다 | LLM (EXAONE·Qwen·Gemini 등) |
⭐ 가장 자주 하는 오해: "RAG는 AI가 인터넷을 검색하는 기능이다" ❌ 내가 미리 넣어둔 저장소를 뒤지는 것입니다. 그 저장소에 없는 것은 영원히 못 찾습니다. 그래서 RAG 프로젝트의 진짜 일은 모델 고르기가 아니라 저장소에 무엇을 어떻게 넣을 것인가입니다.
3.2 핵심 장점 세 가지
① 재학습이 필요 없어 비용과 오버헤드가 극도로 낮습니다. GPU도, 학습 데이터셋도, 며칠의 학습 시간도 필요 없습니다. 문서를 하나 더 넣고 싶으면 그 문서만 임베딩해서 DB에 추가하면 끝입니다.
② 최신 정보와 기밀 문서를 반영하기에 가장 적합합니다. 실시간으로 변하는 정보(오늘의 환율, 어제 개정된 규정)를 즉시 반영합니다. 기업 내부 보안 문서를 모델에 학습시키지 않고 쓸 수 있다는 점도 중요합니다. 파인튜닝으로 기밀 문서를 학습시키면 그 정보가 가중치에 녹아들어 다시 못 뺍니다. RAG는 DB에서 그 문서를 지우면 그 순간 접근이 끊깁니다 — 삭제가 가능한 지식입니다. 10주차의 소버린 AI("데이터가 밖으로 안 나간다")의 실제 구현이 로컬 모델 + 로컬 벡터 DB의 RAG입니다.
③ 환각(Hallucination)을 눈에 띄게 줄입니다. 모델이 그럴듯한 거짓말을 지어내는 현상을 환각이라고 합니다. LLM은 "모른다"고 답하도록 학습된 것이 아니라 그럴듯한 다음 단어를 만들도록 학습되었기 때문에, 모르는 것을 물으면 침묵 대신 가장 그럴듯한 문장을 만듭니다. RAG는 이 구조를 바꿉니다.
❌ RAG 없음: "네가 아는 걸로 답해" → 모르면 지어냄
⭕ RAG 있음: "이 문서만 근거로 답해. → 근거가 없으면 없다고 말할 수 있음
근거에 없으면 모른다고 해"
환각을 없앨 수는 없습니다. 줄이는 것입니다. 그래서 답변을 검증 가능한 형태(출처 · 수치 · 링크)로 받아야 합니다. 답변에 청크 ID를 함께 내보내면 사람이 근거를 눈으로 확인할 수 있습니다.
3.3 RAG 파이프라인 — 실패는 대부분 검색에서 납니다
[준비] 문서 → 청킹(Chunking) → 임베딩 → 벡터DB 저장
[질의] 질문 → 임베딩 → 유사 청크 top-k 검색 → 프롬프트 주입 → 생성
망가지는 지점을 자주 망가지는 순서대로 적습니다.
| 순위 | 지점 | 증상 | 처방 |
|---|---|---|---|
| 1 | 청킹 | 답이 문장 중간에서 끊긴 근거를 씀 / 엉뚱한 내용이 섞임 | 너무 잘게 = 문맥 잘림, 너무 크게 = 노이즈. 문단·헤딩 경계 우선, 겹침(overlap) 10~20% |
| 2 | top-k | 근거가 부족하거나, 반대로 컨텍스트가 낭비됨 | 적으면 근거 부족, 많으면 정확도 하락. 보통 3~5부터 시작 |
| 3 | 재순위(rerank) | 근거는 들어왔는데 정작 중요한 게 뒤에 밀림 | 벡터 검색 결과를 다시 정렬. 체감 품질이 크게 오름 |
| 4 | 출처 표기 | 틀렸는지 맞았는지 확인할 방법이 없음 | 답변에 청크 ID를 같이 내보내 환각 검증을 가능하게 |
💡 디버깅 요령 (외워둘 가치가 있습니다) — 답이 이상하면 모델을 바꾸기 전에 검색된 청크를 눈으로 봅니다. 근거가 틀렸으면 검색 문제(청킹·임베딩·top-k), 근거는 맞는데 답이 틀렸으면 프롬프트·모델 문제입니다. 이 두 갈래를 구분하지 않고 모델부터 바꾸면 며칠을 날립니다.
3.4 RAG로 풀 수 없는 것
| RAG가 못 하는 일 | 왜 |
|---|---|
| 모델의 말투·문체를 바꾸기 | 문서를 넣어준다고 말버릇이 바뀌지 않습니다 |
| 모델의 추론 능력 자체를 올리기 | 자료를 줘도 못 읽는 사람은 못 읽습니다 |
| 도메인 특유의 사고방식 익히게 하기 | 판사처럼 생각하는 법은 판례를 붙여준다고 생기지 않습니다 |
| 매 요청의 지연·비용을 0으로 만들기 | 검색 + 긴 프롬프트만큼 매번 비용이 붙습니다 |
이 네 줄이 다음 장(PEFT)이 존재하는 이유입니다.
4. PEFT와 QLoRA — 안을 고치되, 아주 조금만
4.1 먼저 Full Fine-Tuning부터
파인튜닝(Fine-Tuning, 미세 조정) — 사전 학습된 범용 모델에 정제된 소량의 특화 데이터를 추가 학습시켜 전문 영역 전용 모델로 만드는 것입니다.
범용 AI 모델 → 정제된 소량의 특화 데이터 → 전문 전용 챗봇 모델
(Domain-Specific Chatbot)
Full Fine-Tuning(전체 파인튜닝) 은 이 과정에서 모델 내부의 모든 파라미터를 전부 조정합니다. 70억 개짜리 모델이면 70억 개를 전부 건드립니다. 문제는 자원입니다. 학습하려면 가중치만이 아니라 그래디언트와 옵티마이저 상태까지 메모리에 올려야 해서 대략 모델 크기의 몇 배에 해당하는 VRAM이 필요합니다. 7B 모델 하나를 Full Fine-Tuning 하려면 개인 GPU로는 손도 못 댑니다.
⚠️ 자원 말고 다른 문제도 있습니다. 전부 다 고치면 원래 잘하던 것을 잊어버릴 수 있습니다. 이것을 파국적 망각(Catastrophic Forgetting) 이라고 합니다. 법률 문서만 잔뜩 학습시켰더니 일반 대화 능력이 무너지는 식입니다.
4.2 PEFT — 1~2%만 학습한다
PEFT (Parameter-Efficient Fine-Tuning, 효율적 파인튜닝) — [파인튜닝의 한 방식]인데, [원본 가중치는 얼려두고(Freeze) 극소수의 보조 신경망만 추가해 그것만 학습한다]는 점에서 Full Fine-Tuning과 구별됩니다. 핵심 숫자는 전체의 1~2% 내외입니다.
┌───────────────────────────────────────┐
│ 원본 모델 가중치 (동결 · Freeze) 🔒 │ 98~99% ← 절대 안 건드림
└───────────────────────────────────────┘ +
┌───────────────────────────────────────┐
│ 보조 신경망 (학습 대상) ✏️ │ 1~2% ← 여기만 학습
└───────────────────────────────────────┘
- 대표 기법: LoRA (Low-Rank Adaptation)
- 핵심 장점: 학습에 필요한 연산 자원과 메모리를 드라마틱하게 절약합니다. 그리고 이것이 sLLM(소형 언어 모델)을 활용한 전문 영역 튜닝의 기본 전제가 됩니다.
⭐ 왜 이게 되는가 — LoRA의 통찰은 "모델을 특정 과제에 적응시킬 때 실제로 필요한 변화량은 생각보다 매우 단순한 형태를 가진다"는 것입니다. 거대한 변화표를 통째로 저장하는 대신 훨씬 작은 두 장의 표의 곱으로 표현할 수 있어 저장할 것이 급격히 줄어듭니다. 비전공자용 한 문장으로는 "고칠 곳 전체를 다시 쓰지 말고, 고친 부분만 적은 얇은 정오표를 옆에 붙인다."
4.3 QLoRA — 압축한 뒤에 포스트잇을 붙인다
QLoRA (Quantized Low-Rank Adaptation, 초경량 파인튜닝) — [PEFT의 한 기법]인데, [원본 가중치를 4비트로 압축한 상태에서 LoRA를 얹는다]는 점에서 일반 LoRA와 구별됩니다.
Q + LoRA
│ └── 보조 신경망만 학습 (PEFT)
└── Quantization: 원본 가중치 정밀도를 4비트(NF4)로 대폭 낮춰 압축
양자화(Quantization) 는 숫자의 소수점 자릿수를 깎는 것입니다. 가중치 하나가 16비트나 32비트로 저장되어 있던 것을 4비트로 줄입니다. 정밀도가 떨어지는 대신 메모리가 4분의 1, 8분의 1로 줄어듭니다. NF4 는 이 4비트 압축에 쓰는 형식의 이름입니다.
직관적 비유 — 수십 권짜리 무거운 백과사전(원본 모델)을 한 권짜리 거친 요약본(4비트 모델) 으로 가볍게 바꾼 뒤, 그 위에 포스트잇(LoRA) 을 붙여가며 빠르게 맞춤형 필기 공부를 하는 것과 같습니다.
이 비유에서 놓치면 안 되는 부분은 요약본은 손대지 않는다는 것입니다. 포스트잇만 붙였다 뗐다 합니다. 그래서 포스트잇 묶음(=어댑터)만 따로 보관하고 교체할 수 있습니다.
핵심 장점은 일반 개발 환경이나 소규모 스타트업 수준에서 보유한 단 한 대의 소비자용 그래픽카드(GPU)만으로도 수십억~수백억 매개변수의 고성능 sLLM을 직접 파인튜닝해 나만의 커스텀 AI 모델을 배포할 수 있게 만든 혁신 기술이라는 점입니다.
⭐ 이 문장을 가볍게 읽지 마세요. QLoRA 이전에는 파인튜닝이 대기업과 연구소의 일이었고, 이후에 그것이 책상 위 컴퓨터 한 대의 일이 되었습니다.
4.4 세 기술의 관계
셋은 대등한 형제가 아니라 포함 관계입니다. 1주차 축 1(포함 관계)이 여기서 다시 나옵니다.
파인튜닝 (Fine-Tuning)
├── Full Fine-Tuning — 전부 조정
└── PEFT — 일부만 조정
├── LoRA — 보조 신경망 추가
├── QLoRA — 4비트 압축 + LoRA
└── (그 외 기법들)
| Full Fine-Tuning | PEFT (LoRA) | QLoRA | |
|---|---|---|---|
| 원본 가중치 | 전부 수정 | 동결(Freeze) | 4비트로 압축 후 동결 |
| 학습 대상 | 전체 파라미터 | 보조 신경망 (1~2%) | 보조 신경망 (1~2%) |
| 필요 VRAM | 매우 큼 | 중간 | 작음 |
| 결과물 | 새 모델 파일 전체 | 어댑터 파일 (작음) | 어댑터 파일 (작음) |
| 개인 GPU 한 대로 | ❌ | 모델이 작으면 가능 | ⭕ 가능 |
4.5 어댑터 패턴이라는 것 — 베이스 모델은 갈아끼울 수 있다
QLoRA의 결과물은 새 모델이 아니라 어댑터 파일입니다.
⭐ 어댑터 패턴이므로, 나중에 베이스 모델 이름만 바꿔 끼우면 됩니다.
GPU가 작아서 Qwen2.5-1.5B-Instruct 로 학습을 시작했다면, 나중에 장비가 좋아졌을 때 베이스 모델 이름만 3B로 교체하고 같은 데이터로 다시 어댑터를 학습시키면 됩니다. 파이프라인, 데이터셋, 코드 구조는 그대로입니다. 3주차와 6주차에서 배운 어댑터(Adapter) 패턴 — "호환되지 않는 것 사이에 끼워 연결하는 것" — 이 AI 모델 수준으로 올라온 것이고, 같은 구조가 크기를 바꿔 반복되는 프랙탈입니다.
💡 그래서 작게 시작하는 것이 손해가 아닙니다. 나중에 갈아끼울 수 있기 때문에 작게 시작하는 것이 옳습니다.
4.6 장비에 맞는 모델 고르기 — VRAM이 한계를 정한다
10주차 1.4절의 "VRAM이 한계를 정한다"가 QLoRA 실습에 그대로 적용됩니다. RTX 3050(VRAM 6GB) 환경에서의 원픽은 Qwen2.5-1.5B-Instruct 입니다. 이유가 셋입니다.
| # | 이유 |
|---|---|
| 1 | VRAM을 1.8GB만 사용합니다. QLoRA 학습이 빠르고 OOM(Out Of Memory) 확률이 거의 0 입니다 |
| 2 | 시맨틱 인텐트 분류(라우팅)에서는 3B와 정확도 차이가 거의 없습니다. 분류는 생성보다 쉬운 과제입니다 |
| 3 | 어댑터 패턴이므로 나중에 베이스 모델 이름만 3B로 교체하면 됩니다 |
Qwen2.5-3B 는 VRAM 3GB 이상을 요구해 6GB 환경에서는 간당간당합니다. "돌아가긴 하는데 배치 크기를 조금만 키우면 터진다"는 상태가 가장 나쁩니다. 학습 도중에 터지기 때문입니다.
| 모델 | 대략 VRAM 사용 | 6GB 환경 판정 | 용도 |
|---|---|---|---|
Qwen2.5-1.5B-Instruct | 약 1.8GB | ⭕ 안정 | 라우터 + 응답 겸용 (PoC 원픽) |
Qwen2.5-3B-Instruct | 3GB 이상 | ⚠️ 간당간당 | 여유 있으면 승격 |
EXAONE-3.5-7.8B-Instruct-AWQ | 훨씬 큼 | ❌ 학습 불가 | 추론 전용(허브)으로만 고려 |
⭐ PoC 단계의 결정: EXAONE 7B를 지우고
Qwen2.5-1.5B-Instruct하나로 라우터와 응답을 겸용합니다. "일단 돌아가는 것을 만든다"가 PoC(Proof of Concept, 개념 증명)의 목적이기 때문입니다. 구조가 검증된 다음에 모델을 키우는 것이 순서입니다. 반대로 하면 무엇이 문제인지 영영 모릅니다.
4.7 QLoRA 전 확인사항
| # | 확인할 것 | 왜 |
|---|---|---|
| 1 | GPU가 실제로 잡히는가 (nvidia-smi, torch.cuda.is_available()) | CPU로 돌면 며칠이 걸립니다 |
| 2 | VRAM 여유가 있는가 (다른 프로세스가 쓰고 있지 않은가) | 브라우저·다른 컨테이너가 VRAM을 먹고 있는 경우가 흔합니다 |
| 3 | 베이스 모델이 로컬에 내려받아져 있는가 | 학습 시작 후 다운로드가 걸리면 시간 낭비입니다 |
| 4 | 데이터 형식이 모델의 대화 템플릿과 맞는가 | 형식이 틀리면 학습은 되는데 결과가 이상해집니다 |
| 5 | 데이터가 적고 깨끗한가 | 적고 깨끗한 수백 건 > 지저분한 수만 건. 데이터 품질이 전부입니다 |
| 6 | 학습 중간 저장(checkpoint) 경로를 잡았는가 | 중간에 끊기면 처음부터 다시 해야 합니다 |
| 7 | 모델 파일이 .gitignore 에 들어있는가 | 수 GB짜리 파일이 깃허브로 올라가면 저장소가 망가집니다 |
4.8 학습 데이터의 두 종류
| 종류 | 무엇을 가르치는가 | 데이터 형태 | 예 |
|---|---|---|---|
| 도메인 적응 학습 (Domain Adaptation) | 그 분야의 말과 사고방식 | 원문 텍스트 덩어리 | 법령 조문, 판결문 원문 |
| 인스트럭션 튜닝 (Instruction Tuning) | 지시를 받고 답하는 형식 | 질문–답변 쌍 | "이 사안의 쟁점은?" → "쟁점은 …입니다" |
⭐ 둘 다 필요합니다. 도메인 적응만 하면 법률 문체로 중얼거리기만 하고, 인스트럭션 튜닝만 하면 대답은 잘하는데 내용이 얕습니다. 보통 도메인 적응 → 인스트럭션 튜닝 순서로 얹습니다. "기본 상식 위에 요점정리 노트"라는 2장의 비유가 여기서도 성립합니다.
5. 가중치 — AI의 기억은 어디에 있는가 ⭐
5.1 정의와 작동 메커니즘
⭐ 가중치(Weights) — 인공 신경망 구조에서 각 뉴런(인공 뇌세포) 사이를 유기적으로 연결해 주는 '시냅스의 결합 강도(밀도)' 이자, AI가 수많은 학습 데이터 속에서 수없이 부딪히며 찾아낸 '수도꼭지 조절 밸브 값(기억의 실체)' 입니다.
두 비유가 각각 다른 것을 설명합니다. 시냅스의 결합 강도는 가중치가 무엇인지(뉴런 사이 연결선의 굵기), 수도꼭지 조절 밸브는 가중치가 무엇을 하는지(신호를 얼마나 통과시킬지)를 설명합니다.
입력 신호 ──[밸브: 가중치 0.9]──▶ 강하게 통과 (정답과 밀접한 핵심 정보)
입력 신호 ──[밸브: 가중치 0.02]─▶ 거의 차단 (정답과 무관한 노이즈)
- 결과(정답)를 도출하는 데 가장 밀접한 영향을 주는 핵심 정보에는 높은 가중치를 주어 신호를 강화합니다.
- 정답과 아무 관련이 없는 노이즈 정보의 가중치는 0에 가깝게 수렴시켜 차단합니다.
학습이란 이 밸브들을 조금씩 돌려가며 정답에 가까워지는 조합을 찾는 과정이고, 학습이 끝난 뒤 남는 밸브 값들의 집합이 곧 그 모델의 전부입니다.
⭐ "모델 파일"이란 결국 이 밸브 값들을 적어둔 숫자 목록입니다. 7B 모델이란 밸브가 70억 개라는 뜻입니다. 그래서 3주차에서 배운 직렬화(Serialization) 로 파일에 저장하고 다시 불러올 수 있는 것입니다.
5.2 네 기술의 가중치 취급 비교표 ⭐
이 표가 이 장에서 가장 중요한 표입니다. 지금까지 배운 네 기술이 결국 가중치를 어떻게 대하느냐로 전부 갈립니다.
| 기술 분류 | 가중치(Weight)를 다루는 방식 |
|---|---|
| 전체 파인튜닝 (Full Fine-Tuning) | 모델 내부의 수백억 개 모든 조절 밸브(가중치) 를 데이터에 맞춰 미세 조정함 (극도의 GPU 자원 소모) |
| PEFT (LoRA) | 기존 가중치 조절 밸브는 완전히 잠가서 동결(Freeze)하고, 그 옆에 가볍게 새로 연결한 일부 보조 밸브만 정밀 조절함 |
| QLoRA | 기존 가중치의 소수점 자릿수 정밀도를 4비트 수준으로 투박하게 깎아내어 가중치 자체를 거대 압축(양자화) 한 후, 보조 밸브를 조절함 |
| RAG | 모델 내부의 기존 가중치 밸브는 단 0.1%도 건드리지 않고, 외부에 있는 최신 백과사전을 가져다가 펼쳐서 참고용으로 활용함 |
5.3 이 표를 축으로 다시 읽기
밖에서 찾기 (축 2 = 0) 안을 고치기 (축 2 = 1)
│ │
RAG ┌─────────┴─────────┐
(가중치 0% 변경) 전부 (축 3 = 0) 일부 (축 3 = 1)
│ │
Full Fine-Tuning PEFT (LoRA) → 압축하면 QLoRA
축 두 개로 네 기술이 정확히 갈립니다. 비유를 하나도 안 써도 갈립니다. 이것이 이 교재가 계속 강조하는 "비유보다 축이 먼저" 의 실제 효용입니다.
5.4 이 단어들이 놓인 자리 — 선언에서 파인튜닝까지
이번 주에 나온 단어들은 갑자기 등장한 것이 아니라 앞에서 배운 개발 단계의 마지막 칸에 들어갑니다.
| 단계 | 용어 | 무엇인가 | 예 |
|---|---|---|---|
| 1. 구조 설계 및 선언 | 디클러레이션 (Declaration, 선언) | 식별자의 존재를 알리고 가상 메모리 공간을 확보 | let password; — "예약됨!" |
| 데피니션 (Definition, 정의) | 데이터 형식·로직·스키마 구조를 확정 | CREATE TABLE User ( ... ); | |
| 2. 데이터 수집·전처리 | 리파인 (Refine, 정제) | 노이즈·결측치·중복 제거, 정형 데이터로 변환 | 크롤링 결과 정리 (11장) |
| 3. 모델 최적화·고도화 | 파인튜닝 (Fine-Tuning) | 사전 학습 모델에 특정 도메인 데이터를 추가 학습, 가중치 최적화 | QLoRA (4장) |
프론트엔드/DB 설계 → Raw Data ➡ Refined Data → 백엔드 AI 학습 → 지능형 서비스
(Declaration & Definition) (Refine) (Fine-Tuning) 고도화
⭐ 이 흐름이 중요한 이유는 파인튜닝이 개발의 시작이 아니라 마지막 단계임을 보여주기 때문입니다. 데이터가 정제되어 있지 않으면 파인튜닝은 무의미합니다.
6. 언제 무엇을 쓸 것인가
6.1 실무 순서: 프롬프트 → RAG → 파인튜닝
⭐ 대부분의 과제는 2단계(RAG)에서 끝납니다.
| 단계 | 방법 | 바꾸는 것 | 비용 | 최신성 | 적합한 과제 |
|---|---|---|---|---|---|
| 1 | 프롬프트 | 지시 방식 | 거의 0 | — | 형식·톤 지시 |
| 2 | RAG | 아는 내용 | 중간 (인프라) | 즉시 반영 | 사내 문서 Q&A, 매뉴얼, 규정 |
| 3 | 파인튜닝 | 말하는 방식·형식 | 높음 (데이터 + GPU) | 재학습 필요 | 일관된 출력 포맷, 도메인 말투 |
두 개의 대표 문장으로 기억하세요. "우리 회사 규정을 알게 하고 싶다" → RAG. "무조건 이 JSON 형식으로만 답하게" → 프롬프트(+구조화 출력), 그래도 안 되면 파인튜닝.
6.2 결정 표 — 다섯 개 질문
| # | 질문 | 예(1)이면 | 아니오(0)이면 |
|---|---|---|---|
| 1 | 반영해야 할 내용이 자주 바뀌는가 | RAG | 다음 질문으로 |
| 2 | 근거의 출처를 사용자에게 보여줘야 하는가 | RAG | 다음 질문으로 |
| 3 | 나중에 그 지식을 삭제해야 할 수 있는가 (개인정보 등) | RAG | 다음 질문으로 |
| 4 | 바꾸고 싶은 것이 아는 내용이 아니라 말하는 방식인가 | 파인튜닝 | 프롬프트로 충분 |
| 5 | 같은 판단을 아주 빠르고 싸게 수없이 반복해야 하는가 | 파인튜닝(sLLM) | 프롬프트/RAG |
5번이 8장의 게이트웨이 라우터가 파인튜닝인 이유입니다. 사용자 질문 한 건마다 분류가 돌기 때문에 빠르고 싸야 합니다. 거대 모델을 API로 부르면 분류 한 번에 몇백 밀리초와 비용이 붙습니다.
6.3 하이브리드가 기본값입니다
[사용자 질문]
│
▼
[QLoRA로 튜닝한 작은 라우터] ← 파인튜닝: 의도를 빠르게 분류
│
├─ RAG 필요 → [벡터 DB 검색] → [문서 붙여서 생성] ← RAG: 사실을 정확하게
├─ CRUD 필요 → [DB 조회/수정]
└─ 그 외 → [외부 LLM API 호출]
파인튜닝은 "판단"에, RAG는 "사실"에 쓰는 것이 좋은 분업입니다. 이것이 8장에서 실제로 구현할 구조입니다.
7. 하네스 가드레일 ⭐
7.1 카파시의 하네스 원칙
⭐ "LLM을 절대 날것(Raw)으로 세상과 직접 부딪히게 두지 말고, 외부에서 통제하는 규칙과 환경틀을 짜라."
이 원칙의 맥락은 이렇습니다. LLM은 확률적으로 다음 단어를 만드는 기계라서 같은 질문에도 매번 조금씩 다른 답을 냅니다. 그런데 우리가 만드는 서비스는 결정론적으로 동작해야 합니다. 이 간극을 그냥 두면 LLM이 어느 날 JSON 대신 설명문을 뱉는 순간 서비스가 멈추고, 외부 API 명세가 바뀌면 코드가 죽습니다. LLM을 통제하는 것이 아니라, LLM 주변에 틀을 짜는 것이 해법입니다.
7.2 정의
하네스 가드레일(Harness Guardrail) — [엔지니어링 아키텍처]인데, [예측 불가능한 외부 환경과 내부 핵심 비즈니스 로직 사이에 안전한 완충 지대를 제공한다]는 점에서 단순한 입력/출력 검증기와 구별됩니다.
여기서 "예측 불가능한 외부 환경"이란 생성형 AI 모델의 출력 변동성, 외부 API의 장애 및 정책 변경, 인프라 변동(네트워크·컨테이너 재시작)을 말합니다. 하네스 가드레일은 엔터프라이즈 시스템이 요구하는 높은 수준의 신뢰성과 제어력을 확보하며, 시스템의 파멸적인 오작동을 차단(Failsafe)하는 방어벽 역할을 수행합니다.
번역어 주의 —
Harness를 "마구(馬具)"로 옮기면 뜻이 안 옵니다. 원래 뜻은 말이 제멋대로 못 가게 씌우는 통제 장구이고, 공학에서는 전선 다발을 규격에 맞게 묶은 배선 뭉치(Wire Harness) 를 뜻합니다. 둘 다 자유롭게 날뛸 수 있는 것을 정해진 통로로만 흐르게 만든다는 공통점이 있습니다.Guardrail은 도로 가장자리의 가드레일 — 넘어가면 안 되는 선입니다.
7.3 구조도
┌─────────────────────────┐
│ 외부 환경 / AI 모델 │ (예측 불가능한 출력, 외부 API, 인프라 변동)
└────────────┬────────────┘
│ (Raw Data / Response)
▼
┌────────────────────────────────────────────────────────┐
│ 하네스 가드레일 (Harness Guardrail) │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────┐ │
│ │ ① 표준화된 어댑터 레이어 │ │ ② TDD 평가 루프 │ │
│ │ (정책 변동/규격 변환) │ │ (RAG 환각 검증) │ │
│ └─────────────────────────┘ └─────────────────────┘ │
└────────────┬───────────────────────────────────────────┘
│ (정제 및 검증이 완료된 신뢰할 수 있는 데이터)
▼
┌─────────────────────────┐
│ 내부 시스템 / 비즈니스 │ (안정적인 서비스 운영 환경 확보)
└─────────────────────────┘
⭐ 이 그림에서 가장 중요한 것은 위와 아래가 절대 직접 만나지 않는다는 것입니다. 하네스 가드레일은 단순한 검증 함수가 아니라 아키텍처 수준의 격리 구조입니다.
7.4 ① 표준화된 어댑터 레이어 (Standardized Adapter Layer)
- 결합도 완화(Decoupling) — 외부 데이터 소스 및 모델 인터페이스와 내부 핵심 로직 사이에 표준화된 프로토콜 중계 레이어를 둡니다.
- 유연한 대응성 — 외부 API 명세나 서비스 정책이 변경되더라도 메인 비즈니스 로직을 전혀 수정하지 않고 어댑터 레이어 단에서의 즉각적인 조치만으로 전체 연동의 영속성을 유지할 수 있습니다.
이것은 새 개념이 아니라 3주차·6주차의 포트&어댑터(헥사고날)가 AI 시대의 이름으로 다시 나온 것입니다.
| 헥사고날 아키텍처 | 하네스 가드레일 |
|---|---|
| 포트(Port) — 도메인이 정의한 "무엇을 할지"의 명세 | 내부 로직이 기대하는 표준 응답 형식 |
| 어댑터(Adapter) — 특정 기술로 구현한 것 | Gemini용 어댑터, EXAONE용 어댑터, Qwen용 어댑터 |
| 기술이 바뀌면 어댑터만 교체 | 모델이 바뀌면 어댑터만 교체 |
# 하네스 어댑터의 뼈대 — 내부 로직은 이 형식만 알면 됩니다
class LLMPort:
def ask(self, prompt: str) -> str:
raise NotImplementedError
class GeminiAdapter(LLMPort):
def ask(self, prompt: str) -> str:
raw = gemini_client.generate(prompt) # 외부 규격
return normalize(raw) # 우리 규격으로 변환
class QwenLocalAdapter(LLMPort):
def ask(self, prompt: str) -> str:
raw = local_pipeline(prompt) # 다른 외부 규격
return normalize(raw) # 같은 우리 규격으로 변환
⭐ Gemini가 응답 형식을 바꾸면
GeminiAdapter안의normalize만 고칩니다. 인터렉터, 유스케이스, 라우터는 한 줄도 안 고칩니다. 이것이 "메인 로직 무수정"의 실제 모습입니다.
7.5 ② TDD 기반 독립적 평가 루프 (Evaluation Loop)
- 실시간 검증 — 검증 환경(Sandbox) 내에 테스트 주도 개발(TDD) 사상을 도입해 인출 단계에서 일어나는 위험 요소를 사전 검사합니다.
- 할루시네이션 제어 — RAG 시스템에서 발생하는 환각(Hallucination), 편향(Bias), 탈옥(Prompt Injection) 시도 등을 실시간 모니터링하여 가드레일 통과 여부를 엄격하게 판정합니다.
일반 소프트웨어의 TDD는 "이 입력이면 이 출력"을 먼저 쓰고 코드를 짭니다. 그런데 LLM은 같은 입력에도 매번 다른 문장을 뱉어서 글자 단위 비교 테스트는 성립하지 않습니다. 그래서 LLM의 평가 루프는 기준을 바꿉니다.
| 무엇을 검사하는가 | 예 |
|---|---|
| 형식 | JSON으로 파싱되는가, 필수 키가 있는가 |
| 근거 일치 | 답변의 주장이 검색된 청크 안에 실제로 있는가 |
| 금지 사항 | 개인정보·욕설·경쟁사 언급이 없는가 |
| 인젝션 방어 | "이전 지시를 무시하라" 류의 문장이 통과되지 않는가 |
| 범위 이탈 | 우리 서비스가 다루기로 한 주제 밖으로 나가지 않았는가 |
⭐ "독립적"이라는 말이 핵심입니다. 평가 루프는 LLM에게 "잘했어?"라고 묻지 않고 LLM과 별개의 규칙·별개의 코드로 판정합니다. 채점자가 응시자와 같은 사람이면 채점이 아니기 때문입니다.
7.6 기대 효과 세 가지
| 효과 | 내용 |
|---|---|
| 비즈니스 연속성 (Business Continuity) | 외부 연동 시스템의 갑작스러운 중단·오작동 시에도 로컬 대체 비즈니스 논리를 가동해 서비스 무중단을 구현 |
| 독립적 품질 평가 | LLM 성능 의존성을 낮추고, 가드레일 자체의 엄밀성 지표를 기반으로 전체 시스템의 서비스 수준 협약(SLA)을 유지·관리 |
| 안전장치 (Failsafe) | 예상치 못한 치명적 예외가 감지되면 에러 전파를 격리 차단하고 안전한 기본값(Fallback)을 사용자에게 제공 |
Failsafe와 Fallback을 구별하세요. Failsafe는 고장이 나도 안전한 방향으로 고장 나게 설계하는 것(인증 실패 시 무조건 거부), Fallback은 주 경로가 막히면 대체 경로로 우회하는 것(로컬 모델이 죽으면 외부 API로)입니다.
7.7 실제 설계 — 모델이 RAG를 건너뛰고 환각했을 때
증상: 로컬 모델(EXAONE)에게 "DB에 있는 축구 데이터로만 답해"라고 시켰는데, RAG를 하지 않고 자기가 아는 것처럼 그럴듯하게 답을 지어냈습니다.
나쁜 대응: 프롬프트에 "제발 DB를 찾아봐"를 더 강하게 씁니다. → 확률적으로만 개선됩니다. 언젠가 또 뚫립니다.
하네스 대응: 모델의 답을 믿지 않고 검사하는 층을 바깥에 둡니다.
[질문] → [모델 응답]
│
▼
┌──────────────────────────────┐
│ 하네스: 이 답변에 근거가 있는가? │
│ - 검색이 실제로 호출되었는가? │
│ - 답변 속 고유명사·숫자가 │
│ 검색된 청크 안에 존재하는가? │
└───────────┬──────────────────┘
│
예 ─────┴───── 아니오
│ │
▼ ▼
[사용자에게] [강제로 DB 검색 → 근거를 붙여 재생성]
│
그래도 근거가 없으면
▼
[Fallback: "해당 정보를 찾지 못했습니다"]
⭐ 핵심 발상의 전환: "모델이 RAG를 하게 만들자"가 아니라 "모델이 RAG를 안 했다는 것을 시스템이 판정하고 강제로 시키자" 입니다. 전자는 부탁이고 후자는 구조입니다. 부탁은 확률이고, 구조는 보장입니다.
7.8 하네스는 문서로도 씁니다 — docker-rules.md
하네스는 코드만이 아닙니다. AI 코딩 도구에게 주는 규칙 문서도 하네스입니다. 이번 주에 같은 DB·백엔드를 만들라는 요청이 반복되면서 도커 컨테이너가 계속 늘어나는 문제가 있었습니다. 중복 컨테이너를 정리한 뒤, "동일한 DB·백엔드 생성 요청이 와도 기존 것이 있으면 체크해 승인받는다" 는 룰을 fastapi/_docs/docker-rules.md 에 하네스로 추가했습니다.
# docker-rules.md (하네스)
## 컨테이너 생성 전 필수 절차
1. `docker ps -a` 로 동일 목적의 컨테이너가 이미 있는지 확인한다.
2. 있으면 **새로 만들지 않는다.** 재사용하거나, 사용자에게 승인을 요청한다.
3. 포트 충돌 여부를 확인한다.
4. 컨테이너 이름은 `<프로젝트>-<역할>` 규칙을 따른다.
## 금지
- 승인 없이 기존 컨테이너를 삭제하지 않는다.
- 승인 없이 동일 이미지의 컨테이너를 중복 생성하지 않는다.
⭐ 9주차의 "규칙을 사람이 아니라 도구가 강제하게" 축(린터·pre-commit)이 여기서 AI 에이전트에게로 확장된 것입니다. 사람에게 강제하는 도구가 린터라면, AI에게 강제하는 도구가 하네스 문서입니다.
7.9 이번 주에 정리된 하네스의 자리
| 무엇을 통제하는가 | 하네스의 형태 | 배운 곳 |
|---|---|---|
| 외부 API·모델의 규격 변동 | 어댑터 레이어 | 3주차, 6주차, 이번 주 7.4 |
| LLM 출력의 품질·안전성 | TDD 평가 루프 | 이번 주 7.5 |
| 사람이 쓰는 코드의 스타일 | 린터·포매터·pre-commit | 9주차 |
| AI 코딩 도구의 행동 | CLAUDE.md, docker-rules.md | 3주차, 9주차, 이번 주 7.8 |
| 비밀 키에 대한 접근 | 키메이커(싱글톤 시크릿 매니저) | 3주차, 이번 주 8.4 |
전부 같은 구조입니다. 예측 불가능한 쪽과 지켜야 할 쪽 사이에 정해진 통로 하나만 남기는 것입니다.
8. 시맨틱 인텐트 게이트웨이 — 세 갈래로 나누는 문 ⭐
8.1 게이트웨이가 놓이는 자리
1장의 시맨틱 인텐트, 4장의 QLoRA, 7장의 하네스가 여기서 하나로 합쳐집니다. 게이트웨이(Gateway) 는 사용자의 질문이 가장 먼저 도착하는 문이고, 하는 일은 답을 만드는 것이 아니라 "이 질문은 어디로 보내야 하는가"를 정하는 것입니다. 9주차에서 배운 스타 토폴로지의 허브가 라우팅을 담당한다고 했는데, 게이트웨이의 인텐트 분류기가 그 허브의 판단 기관입니다.
[프론트 채팅창]
│
▼
┌──────────────────────────────────────┐
│ Gateway — 시맨틱 인텐트 분류기 │ ← Qwen2.5-1.5B-Instruct (QLoRA 튜닝)
│ "이 문장의 진짜 목적은 무엇인가?" │
└───────┬──────────┬───────────┬───────┘
│ │ │
① RAG ② CRUD ③ Gemini API
│ │ │
▼ ▼ ▼
벡터DB 검색 DB 조회/수정 외부 LLM 호출
→ 근거 생성 → 결과 반환 → 범용 답변
8.2 세 갈래 분기
| # | 분기 | 언제 이쪽으로 가는가 | 예시 질문 |
|---|---|---|---|
| ① | RAG | 우리 DB에 있는 지식·문서를 근거로 답해야 할 때 | "지난 시즌 이 선수 기록 알려줘" |
| ② | CRUD | 데이터를 조회·생성·수정·삭제해야 할 때 | "내 즐겨찾기에 이 팀 추가해줘" |
| ③ | Gemini API | 우리 도메인 밖의 범용 지식·창작이 필요할 때 | "축구 규칙을 초등학생에게 설명해줘" |
세 번째 분기를 위해 app/use_cases/terran_vessel_gemini_interactor.py 에 GEMINI_API_KEY 로 외부 LLM을 연결합니다.
⭐ 왜 세 개인가가 중요합니다. 인텐트는 많을수록 좋은 것이 아닙니다. 20개로 쪼개면 분류 정확도가 떨어지고 각 인텐트마다 학습 데이터가 필요해집니다. 먼저 "처리 방식이 근본적으로 다른가"로 쪼갭니다. RAG·CRUD·외부호출은 처리 경로가 완전히 다릅니다. 세부 구분("어느 팀 기록인가")은 분기 안에서 하면 됩니다.
8.3 왜 이 라우터를 파인튜닝으로 만드는가
6.2절 결정표의 5번 질문 — "같은 판단을 아주 빠르고 싸게 수없이 반복해야 하는가" — 에 예이기 때문입니다.
| 큰 모델을 API로 호출해 분류 | QLoRA로 튜닝한 1.5B 로컬 라우터 | |
|---|---|---|
| 지연 | 수백 ms ~ 수 초 | 수십 ms |
| 비용 | 질문 한 건마다 과금 | 0 |
| 데이터 | 질문이 외부로 나감 | 밖으로 안 나감 (소버린) |
| 안정성 | 외부 장애에 직접 노출 | 로컬에서 동작 |
그리고 4.6절에서 확인했듯 분류(라우팅) 과제에서는 1.5B와 3B의 정확도 차이가 거의 없습니다. "작은 모델로 충분한 일"과 "큰 모델이 필요한 일"을 나누는 것이 이 구조의 핵심입니다.
8.4 키메이커 — 인터렉터는 .env 를 모릅니다
인터렉터가 .env 키에 직접 접근하지 않습니다. fastapi/core/matrix/vault_keymaker_secret_manager.py(키메이커)가 키를 관리해 제공합니다.
# ❌ 인터렉터가 직접 환경변수를 읽는 방식
import os
class GeminiInteractor:
def run(self, prompt):
key = os.getenv("GEMINI_API_KEY") # 여기저기서 이 줄이 반복됨
# ⭕ 키메이커를 통해서만 받는 방식
from core.matrix.vault_keymaker_secret_manager import KeyMaker
class GeminiInteractor:
def run(self, prompt):
key = KeyMaker.get("GEMINI_API_KEY") # 접근 경로가 한 곳
이렇게 하면 ① 접근 경로가 하나여서 키를 어디서 읽는지 파일 하나만 보면 되고, ② 검증과 로깅을 한 곳에서 하므로 키가 없을 때의 에러 메시지·마스킹 규칙을 한 번만 쓰면 되고, ③ 키 소스를 바꾸기 쉬워 나중에 .env 에서 AWS Secrets Manager로 옮겨도 키메이커만 고치면 됩니다.
⭐ 3주차에서 배운 싱글톤 키메이커가 이번 주에
core/matrix아래 정식 자리를 얻은 것이고, 7.9절 표의 마지막 줄 — 비밀 키 접근을 통제하는 하네스 — 입니다.
8.5 모델(의사 분류기)을 apps 에 둘까 core 에 둘까
답을 외우지 말고 판단 기준을 가져가세요.
core/ | apps/ | |
|---|---|---|
| 성격 | 순수 인프라 — 도메인 지식이 없음 | 도메인 로직 — 우리 서비스의 업무 |
| 예 | DB 엔진, 외부 클라이언트, 키메이커 | 회원, 주문, 축구 콘텐츠, 어드민 |
| 판별 질문 | "이걸 다른 회사 프로젝트에 그대로 복사해도 말이 되는가?" | "우리 서비스가 아니면 의미가 없는가?" |
이 기준으로 보면, 모델을 호출하는 클라이언트(로컬 LLM 어댑터) 는 core 입니다(어느 프로젝트에 갖다 놔도 똑같이 동작합니다). 우리 서비스의 세 인텐트로 분류하는 라우터 는 apps 입니다(RAG/CRUD/Gemini라는 분기 자체가 우리 도메인의 결정입니다).
⭐ 9주차의 원칙 — "
core/에는 도메인 지식을 넣지 않습니다" — 가 그대로 적용됩니다. 이 원칙을 어기면core가 우리 서비스에 묶여버려 재사용이 불가능해집니다.
8.6 EXAONE 추론 통합 위치 — silicon_valley 확장
로컬 LLM 추론 기능은 silicon_valley 확장이 권장입니다. 이미 'AI 에이전트' 역할이 지정된 앱이라 새 앱을 만들면 역할이 중복되고, Gemini 클라이언트(core/matrix) 옆에 로컬 LLM 어댑터를 추가하는 형태가 되어 7.4절의 어댑터 레이어와 정확히 맞기 때문입니다.
그리고 허브는 fastapi/apps/star_craft 에 그대로 둡니다. 새 기능을 붙인다고 허브를 옮기면 스타 토폴로지가 훼손됩니다. 9주차에서 정한 "의존성은 spoke → hub 만 허용" 규칙을 지키려면 허브의 위치가 고정되어 있어야 합니다.
8.7 질문이 지나는 경로를 매번 로그로 남깁니다
⭐ 질문이 지나는 경로를 인식하도록 매번 로그를 작성합니다.
[REQ-8f21] 질문 수신: "지난 시즌 이 선수 기록 알려줘"
[REQ-8f21] 인텐트 분류 → RAG (confidence 0.94)
[REQ-8f21] 벡터 검색 top_k=5 → 청크 [c_112, c_318, c_047, c_205, c_090]
[REQ-8f21] 프롬프트 주입 완료 (토큰 1,842)
[REQ-8f21] 생성 완료 (2.1s) / 근거 청크 인용: c_112, c_318
이 로그가 없으면 3.3절의 디버깅 요령("검색 문제인가 프롬프트 문제인가")을 적용할 방법이 없습니다. 각 줄에 요청 ID(REQ-8f21) 를 붙이는 것도 중요합니다. 동시에 여러 요청이 처리되면 로그가 뒤섞이기 때문입니다.
LangTailor