앞선 두 글에서 RAG 파이프라인과 온톨로지 설계를 각각 다뤘습니다. 이 글은 그 둘을 붙이는 이야기입니다.
먼저 왜 붙여야 하는지부터 봐야 합니다. 온톨로지를 만들었으니 RAG에 쓰자는 순서는 거꾸로입니다. 벡터 RAG로 안 풀리는 질문이 실제로 있고, 그게 구조를 요구하는 질문일 때 온톨로지가 답이 됩니다.
1. 벡터 RAG가 못 푸는 질문들
벡터 검색은 “질문과 의미가 비슷한 청크” 를 찾습니다. 이 전제가 깨지는 질문 유형이 네 가지 있습니다.
다중 홉(multi-hop) 질문
"알고리즘을 들으려면 결국 어떤 과목들을 먼저 들어야 하나요?"
답을 만들려면 이런 연쇄를 따라가야 합니다.
알고리즘 → 자료구조 → 프로그래밍기초 → (없음)
그런데 이 세 관계는 서로 다른 문서에 흩어져 있습니다. 각 과목의 강의계획서에 “선수과목: X” 한 줄씩 적혀 있을 뿐입니다.
벡터 검색은 “알고리즘”과 비슷한 청크를 가져오므로 알고리즘 강의계획서만 찾습니다. 거기엔 “선수과목: 자료구조”까지만 적혀 있습니다. 자료구조의 선수과목을 알려면 한 번 더 검색해야 하는데, 그럴 이유를 시스템이 모릅니다.
전역 요약 질문
"우리 학과 교육과정의 전체적인 특징은 무엇인가요?"
답이 어느 한 청크에도 없습니다. 200개 과목 전체를 봐야 나오는 답인데, 벡터 검색은 상위 5개만 가져옵니다.
집계·비교 질문
"3학점 전공필수 과목이 몇 개인가요?"
"A 트랙과 B 트랙의 졸업 학점 차이는?"
세어야 하는 질문입니다. 텍스트를 아무리 잘 찾아도 LLM이 세는 것은 신뢰할 수 없습니다.
관계 질의
"김OO 교수님이 가르치는 과목 중 인공지능 역량과 연결된 것"
두 조건의 교집합입니다. 벡터 공간에는 교집합 연산이 없습니다.
2. 두 갈래 접근
이 문제를 푸는 방식은 크게 둘이고, 출발점이 다릅니다.
| GraphRAG | 스키마 기반 질의 생성 | |
|---|---|---|
| 출발점 | 비정형 문서만 있음 | 이미 구조화된 DB·온톨로지가 있음 |
| 그래프를 | LLM이 문서에서 추출해 만듦 | 이미 존재함 |
| 질의 방식 | 그래프 탐색 + 요약 | 자연어 → SPARQL/Cypher 변환 |
| 정확도 | 추출 품질에 좌우 | 스키마가 보장 |
| 구축 비용 | 문서 전체 LLM 처리 (비쌈) | 온톨로지 설계 (사람 비용) |
교육 도메인은 대개 후자에 가깝습니다. 학사 정보 시스템에 과목·교원·수강 데이터가 이미 정형으로 있습니다. 문서에서 그래프를 새로 추출할 게 아니라, 있는 구조를 질의할 수 있게 여는 것이 맞습니다.
3. GraphRAG — 문서에서 그래프를 만드는 쪽
색인 단계
문서 → 청킹 → [LLM] 개체·관계 추출 → 그래프 구성
↓
[Leiden 등] 커뮤니티 탐지
↓
[LLM] 커뮤니티별 요약 생성
개체·관계 추출에서 LLM에게 이런 걸 시킵니다.
아래 텍스트에서 개체와 관계를 추출하시오.
개체 유형: 과목, 교원, 역량, 학과
관계 유형: 선수과목, 담당, 달성역량, 개설
출력: (개체1, 관계, 개체2) 형식
추출된 트리플이 모여 그래프가 됩니다. 그다음 커뮤니티 탐지 로 촘촘히 연결된 노드 무리를 찾아 계층으로 묶고, 각 커뮤니티를 LLM이 요약해 둡니다.
질의 단계 — 두 가지 모드
| 모드 | 무엇을 하나 | 어떤 질문에 |
|---|---|---|
| 지역 검색(Local) | 관련 개체를 찾고 그 이웃을 따라 확장해 컨텍스트 구성 | “자료구조 선수과목은?” — 특정 개체 중심 |
| 전역 검색(Global) | 미리 만든 커뮤니티 요약들을 훑어 답을 종합 | “교육과정 전체 특징은?” — 데이터셋 전반 |
전역 검색이 GraphRAG의 결정적 차별점입니다. 벡터 RAG로는 구조적으로 불가능했던 “전체를 아우르는 질문”에 답할 수 있습니다. 커뮤니티 요약을 색인 시점에 미리 만들어 두었기 때문입니다.
비용을 반드시 따져야 합니다
GraphRAG는 비쌉니다. 색인 시점에 전체 문서를 LLM으로 처리하고, 커뮤니티 요약도 LLM으로 만듭니다.
문서 10,000건 × 청크 5개 × (추출 + 요약) LLM 호출
→ 수만 번의 호출
→ 문서가 갱신될 때마다 부분적으로 반복
소규모·정적 코퍼스에는 훌륭하지만, 대규모·자주 바뀌는 문서에는 부담이 큽니다. 도입 전에 색인 비용을 계산해 보는 게 좋습니다.
4. 스키마 기반 질의 생성 — 구조가 이미 있는 쪽
온톨로지나 그래프 DB가 이미 있다면, LLM에게 문서를 읽히는 대신 질의문을 작성하게 합니다.
자연어 질문 → [LLM + 스키마] → SPARQL/Cypher → 실행 → 결과 → [LLM] 답변 생성
스키마를 프롬프트에 넣습니다
당신은 교육 온톨로지 질의 작성자입니다. 아래 스키마만 사용해
SPARQL 질의를 작성하시오. 스키마에 없는 속성은 쓰지 마시오.
[클래스]
ex:과목, ex:교원, ex:역량, ex:학과, ex:수강이력
[속성]
ex:과목명 (과목 → 문자열)
ex:학점 (과목 → 정수)
ex:선수과목 (과목 → 과목, 이행적)
ex:담당교원 (과목 → 교원)
ex:달성역량 (과목 → 역량)
skos:broader (역량 → 역량)
[질문]
알고리즘을 들으려면 어떤 과목을 먼저 들어야 하나요?
생성되는 질의는 이렇습니다.
PREFIX ex: <http://example.org/edu#>
SELECT ?선수 ?선수명 WHERE {
ex:알고리즘 ex:선수과목+ ?선수 .
?선수 ex:과목명 ?선수명 .
}
ex:선수과목+ 한 글자로 다중 홉이 해결됩니다. 벡터 RAG가 구조적으로 못 하던 일입니다.
이 방식의 결정적 장점
답이 데이터베이스에서 나옵니다. LLM은 질의문만 작성하고, 사실 자체는 생성하지 않습니다.
벡터 RAG: LLM이 문서를 읽고 답을 "생성" → 환각 가능
질의 생성: LLM이 질의를 작성, DB가 답을 "반환" → 환각 구조적으로 차단
“3학점 전공필수가 몇 개인가”에 COUNT가 답하면 틀릴 수가 없습니다.
그런데 질의가 틀리면
환각은 사라졌지만 새로운 실패 지점이 생겼습니다. LLM이 질의문을 잘못 씁니다.
| 실패 | 대응 |
|---|---|
없는 속성 사용 (ex:교수님이름) |
생성된 질의를 스키마에 대조 검증. 통과 못 하면 오류를 붙여 재생성 |
| 문법 오류 | 파서로 먼저 검증 후 실행 |
| 결과 0건 | “없다”인지 “질의가 틀렸다”인지 구분 안 됨 → 완화된 질의로 재시도 |
| 의도와 다른 질의 | 생성된 질의를 사용자에게 보여 줍니다. 검증 가능성 확보 |
스키마 검증 루프가 이 방식의 핵심 장치입니다.
질의 생성 → 스키마 대조 → (실패) → 오류 메시지 붙여 재생성 (최대 3회)
↓ (통과)
실행 → 결과
그리고 읽기 전용 권한으로만 실행합니다. LLM이 생성한 질의를 쓰기 권한으로 돌리는 건 위험합니다.
5. 실무에서는 섞습니다
어느 한쪽만 쓰는 경우는 드뭅니다. 질문 유형에 따라 경로를 나누는 것이 실제 구조입니다.
질문
│
┌──────┴──────┐
[질문 유형 분류]
│ │
구조적 질문 서술적 질문
(관계·집계·경로) (설명·정의·절차)
│ │
그래프 질의 벡터 검색
│ │
└──────┬───────────────┘
▼
컨텍스트 조립 → LLM → 답변
벡터로 진입점, 그래프로 확장
가장 실용적인 조합입니다.
1. 질문을 벡터 검색해 관련 개체를 찾는다
"자료구조 어렵나요?" → ex:CS201 (자료구조)
2. 그 개체에서 그래프를 1~2홉 확장한다
ex:CS201 → 선수과목, 담당교원, 달성역량, 후속과목
3. 확장된 개체들의 서술 텍스트를 함께 컨텍스트에 넣는다
벡터 검색이 “어디서 시작할지”를 정하고, 그래프가 “무엇을 더 가져올지”를 정합니다. 각자 잘하는 일을 합니다.
온톨로지로 검색 품질을 올리는 다른 방법들
그래프 질의까지 가지 않아도, 온톨로지는 RAG 파이프라인 여러 지점에서 씁니다.
① 질의 확장 — SKOS의 altLabel 로 동의어를 붙입니다.
사용자: "코딩 역량 관련 과목"
→ ex:프로그래밍역량 의 altLabel: "코딩 역량", "Programming Competency"
→ 검색어를 확장해 BM25 리콜을 올림
② 메타데이터 필터 생성 — 질문에서 구조적 조건을 뽑아 필터로 씁니다.
"2024학년도 컴공과 전공필수"
→ { year: 2024, department: 컴퓨터공학과, type: 전공필수 }
→ 벡터 검색 전에 후보를 좁힘
③ 청크 메타데이터 강화 — 청크에 온톨로지 개체 ID를 붙여 두면, 답변에서 정확한 출처와 연관 개체를 제시할 수 있습니다.
④ 답변 검증 — 생성된 답변에서 사실 주장을 뽑아 그래프에 대조합니다. “자료구조는 4학점”이라고 답했는데 그래프에 3학점이면 잡아냅니다.
6. 에이전트에 붙이기
Agent 패턴에서 본 도구 호출 구조에 그래프를 얹으면 모델이 스스로 어느 경로를 쓸지 고릅니다.
tools = [
{
"name": "search_documents",
"description": (
"학사 문서를 자연어로 검색한다. 설명·절차·정의를 묻는 "
"서술형 질문에 사용한다. 예: '휴학 절차', '복수전공이란'"
),
},
{
"name": "query_knowledge_graph",
"description": (
"교육 지식 그래프에 구조적 질의를 한다. 선수과목 경로, "
"개수 집계, 조건 교집합처럼 관계를 따라가거나 세어야 하는 "
"질문에 사용한다. 예: 'X의 모든 선수과목', '3학점 과목 수'"
),
},
{
"name": "get_entity_detail",
"description": "특정 과목·교원·역량의 상세 정보와 직접 연결된 개체를 반환한다.",
},
]
도구 설명에 “어떤 질문에 쓰는지”를 예시까지 써 두는 것이 중요합니다. 5장에서 본 원칙 그대로인데, 두 검색 도구가 비슷해 보이면 모델이 아무거나 고릅니다.
이렇게 붙이면 멀티스텝 추론이 자연스럽게 나옵니다.
질문: "자료구조 선수과목을 가르치는 교수님이 누구예요?"
에이전트:
1. query_knowledge_graph("자료구조의 선수과목") → 프로그래밍기초
2. get_entity_detail("프로그래밍기초") → 담당: 김OO
3. 답변 생성
한 번의 검색으로 못 푸는 질문을 도구를 연달아 부르며 푸는 것 — 이게 에이전트를 쓰는 이유입니다.
7. 온톨로지가 환각을 줄이는 세 가지 경로
공고의 “지식 정합성” 항목과 직결되는 부분입니다. 온톨로지는 세 지점에서 환각을 막습니다.
① 답을 생성이 아니라 조회로 만듭니다. 앞서 본 대로 집계·관계 질문의 답이 DB에서 나오면 틀릴 수가 없습니다.
② 답변을 검증합니다. 생성된 답변의 사실 주장을 그래프에 대조해 불일치를 잡습니다.
답변: "자료구조는 4학점이며 프로그래밍기초를 선수과목으로 합니다."
↓ 주장 추출
(자료구조, 학점, 4) → 그래프 확인 → 3학점. ✗ 불일치
(자료구조, 선수과목, 프로그래밍기초) → 그래프 확인 → 일치. ✓
③ 애초에 데이터가 깨지지 않게 막습니다. SHACL 검증과 순환 탐지로 모순된 데이터가 저장소에 들어가는 것 자체를 차단합니다. RAG가 아무리 잘 찾아도 원본이 틀렸으면 답이 틀립니다. 가장 근본적인 층위입니다.
8. 단계적 도입 순서
온톨로지부터 만들고 시작하면 대개 실패합니다. 효과가 확인되는 순서로 쌓아야 합니다.
1단계 벡터 RAG 부터 만든다
→ 실패 질문 로그를 모은다
2단계 실패 유형을 분류한다
→ 다중 홉? 집계? 관계? 전역 요약?
→ 구조를 요구하는 질문이 얼마나 되는지 센다
3단계 그 질문이 의미 있는 비율이면, 그때 필요한 부분만 그래프로
→ 전체 도메인을 모델링하지 않는다
→ 실패 질문이 건드리는 개체·관계만
4단계 라우팅을 붙인다 (질문 유형 → 경로)
5단계 에이전트로 올린다 (모델이 스스로 도구 선택)
3단계의 “필요한 부분만”이 핵심입니다. 교육 도메인 전체를 온톨로지로 만드는 건 몇 달짜리 일이고, 그중 대부분은 질문에 쓰이지 않습니다. 실제 질문이 요구하는 관계부터 모델링하면 몇 주 만에 효과를 봅니다.
정리
| 질문 | 답 |
|---|---|
| 왜 온톨로지를 붙이나 | 벡터 RAG가 다중 홉·전역 요약·집계·관계 교집합을 구조적으로 못 풀기 때문 |
| GraphRAG와 질의 생성의 차이 | 출발점. 문서만 있으면 GraphRAG, 구조가 이미 있으면 질의 생성. 교육 도메인은 대개 후자 |
| GraphRAG의 진짜 장점은 | 전역 검색. 커뮤니티 요약을 미리 만들어 “전체 특징” 같은 질문에 답한다 |
| GraphRAG의 비용은 | 색인 시점에 문서 전체를 LLM 처리. 자주 바뀌는 대규모 코퍼스에는 부담 |
| 질의 생성이 왜 환각을 막나 | LLM 이 질의문만 쓰고 사실은 DB 가 반환하기 때문. 대신 질의가 틀릴 수 있어 스키마 검증 루프 필수 |
| 실무 구조는 | 섞는다. 벡터로 진입점, 그래프로 확장이 가장 실용적 |
| 어떻게 시작하나 | 온톨로지부터 만들지 않는다. 벡터 RAG → 실패 질문 분류 → 필요한 관계만 모델링 |
참고 문헌
- Edge et al., From Local to Global: A Graph RAG Approach to Query-Focused Summarization (2024): https://arxiv.org/abs/2404.16130
- Microsoft GraphRAG: https://github.com/microsoft/graphrag
- Peng et al., Graph Retrieval-Augmented Generation: A Survey (2024): https://arxiv.org/abs/2408.08921
- Traag et al., From Louvain to Leiden: guaranteeing well-connected communities (2019): https://arxiv.org/abs/1810.08473