온톨로지 RAG (Ontology-guided RAG, 온톨로지 기반 RAG)
2026.10.03 · 21분
1. 온톨로지 RAG란?
온톨로지 RAG는 온톨로지가 정한 스키마에 맞춰 지식 그래프를 만들고, 그 스키마를 이용해 질의하고, 온톨로지 규칙으로 답을 보강·검증하는 RAG입니다. Graph RAG의 한 갈래이며, 그래프의 모양을 LLM 대신 사람이 설계한 온톨로지가 정합니다.
Graph RAG (자유 추출) 온톨로지 RAG (스키마 강제)
───────────────────── ─────────────────────────────
LLM: "이 문장에서 뭐든 뽑아" LLM: "Company, Person, Drug 만,
ACQUIRED, WORKS_AT 관계만 뽑아"
결과: 결과:
(네이버) -인수함-> (왓패드) (company:naver) -ACQUIRED-> (company:wattpad)
(NAVER) -acquired-> (Wattpad Inc) ※ 별칭 정규화로 한 노드
(네이버) -사들였다-> (왓패드) ※ 관계 이름 하나
→ 노드 6개, 관계 이름 3종 → 노드 2개, 관계 1개업계에서 통일된 용어는 아닙니다. "Ontology-grounded RAG", "Schema-guided GraphRAG", "KG-RAG" 등으로도 부르며, 이 노트에서는 온톨로지(스키마)가 구축·질의·검증 전 과정을 이끄는 RAG를 온톨로지 RAG라고 부릅니다.
2. Graph RAG와 무엇이 다른가?
| 구분 | Graph RAG (예: MS GraphRAG) | 온톨로지 RAG |
|---|---|---|
| 그래프 스키마 | LLM이 자유롭게 정함 (유형 정도만 지정) | 온톨로지가 클래스·관계·제약을 정함 |
| 엔티티 정규화 | 이름 문자열 기준 병합 | 식별자·별칭 사전으로 해소 |
| 질의 방식 | 임베딩 + 그래프 확장, 커뮤니티 요약 | Text2Cypher / Text2SPARQL로 정확한 질의 |
| 잘하는 질문 | "전체 주제는?", 탐색적 질문 | "A사의 최종 모회사는?", "이 약과 금기인 약은?" |
| 답의 성격 | 요약·서술 | 사실·목록·수치 |
| 검증 | 어렵다 | 스키마 검증, 추론 규칙, 쿼리 재실행 |
| 초기 비용 | 낮다 (설계 없이 시작) | 높다 (온톨로지 설계 필요) |
| 적합 데이터 | 다양한 문서, 탐색 단계 | 개념이 정해진 전문 도메인 |
3. 전체 파이프라인
┌───────────────────────┐
│ 온톨로지 │
│ 클래스·관계·제약·별칭 │
└───┬──────────┬────────┘
스키마 주입 │ │ 스키마 주입 / 추론 규칙
▼ ▼
[구축] [질의]
문서 ──> 청크 ──> 스키마 제약 추출 ──┐ 질문
▼ │
검증 (SHACL 등) ▼
│ ┌──────────────────┐
▼ │ Text2Cypher / │
엔티티 해소 │ Text2SPARQL │
│ │ (스키마를 프롬프트에)│
▼ └────────┬─────────┘
┌────────────────────┐ ▼
│ 지식 그래프 (KG) │<── 쿼리 실행
│ + 추론으로 확장된 사실│ │
└────────────────────┘ ▼
결과 행 + 경로
│
▼
(필요하면 원문 청크도)
│
▼
LLM이 답 작성
+ 근거(쿼리·경로) 표시4. 온톨로지 기반 KG 구축
4-1. 스키마 제약 추출
LLM에게 허용된 클래스와 관계만 주고 그 안에서 고르게 합니다.
ONTOLOGY = {
"classes": ["Company", "Person", "Product"],
"relations": {
"ACQUIRED": ("Company", "Company"),
"WORKS_AT": ("Person", "Company"),
"CEO_OF": ("Person", "Company"),
"MAKES": ("Company", "Product"),
},
}
PROMPT = """너는 정보 추출기다. 아래 온톨로지에 있는 클래스와 관계만 사용하라.
온톨로지에 없는 관계는 절대 만들지 말고, 해당 사항이 없으면 빈 배열을 반환하라.
클래스: {classes}
관계(주어 클래스 → 목적어 클래스): {relations}
출력 JSON: {{"entities": [{{"name":..., "class":...}}],
"triples": [{{"s":..., "p":..., "o":..., "evidence": "원문 구절"}}]}}
텍스트: {text}"""evidence(원문 구절)를 함께 받으면 나중에 답의 근거를 원문까지 추적할 수 있습니다.
4-2. 추출 결과 검증
LLM은 지시를 어길 수 있으므로 코드로 다시 확인합니다. 온톨로지의 domain/range가 그대로 검증 규칙이 됩니다.
def validate(out: dict, onto=ONTOLOGY) -> list[dict]:
cls = {e["name"]: e["class"] for e in out["entities"] if e["class"] in onto["classes"]}
ok = []
for t in out["triples"]:
rule = onto["relations"].get(t["p"])
if rule is None:
continue # 온톨로지에 없는 관계 → 버림
if (cls.get(t["s"]), cls.get(t["o"])) != rule:
continue # domain/range 위반 → 버림
ok.append(t)
return ok
if __name__ == "__main__":
out = {"entities": [{"name": "네이버", "class": "Company"},
{"name": "왓패드", "class": "Company"},
{"name": "김철수", "class": "Person"}],
"triples": [{"s": "네이버", "p": "ACQUIRED", "o": "왓패드"},
{"s": "김철수", "p": "ACQUIRED", "o": "왓패드"}, # Person은 인수 불가
{"s": "네이버", "p": "LIKES", "o": "왓패드"}]} # 없는 관계
assert [t["s"] for t in validate(out)] == ["네이버"]RDF 스택이라면 이 단계를 SHACL 검증으로 대신할 수 있습니다.
4-3. 엔티티 정규화와 해소 (Entity Resolution)
같은 대상이 여러 이름으로 나오는 문제를 해결합니다. 온톨로지 RAG의 품질은 여기서 크게 갈립니다.
추출된 이름 정규화 해소 결과 (정식 ID)
─────────────── ───────── ──────────────────
"NAVER" naver ─┐
"네이버(주)" 네이버 ─┼─> company:naver
"네이버 주식회사" 네이버 ─┘
"Samsung Electronics" samsung electronics ─┐
"삼성전자" 삼성전자 ─┴─> company:samsung_elec
"삼성" 삼성 ─> ? (삼성물산? 삼성전자?) → 보류·검수5. 질의: 스키마를 프롬프트에 넣어 쿼리를 만든다
5-1. Text2Cypher / Text2SPARQL
벡터 검색은 "비슷한 문장"을 찾고, 온톨로지 RAG는 정확한 쿼리를 만듭니다. LLM이 자연어 질문을 Cypher나 SPARQL로 번역하고, DB가 실행합니다.
질문: "네이버가 인수한 회사 중 제품을 만드는 회사와 그 제품은?"
│
▼ (스키마 + 질문 → LLM)
MATCH (:Company {id: "company:naver"})-[:ACQUIRED]->(c:Company)-[:MAKES]->(p:Product)
RETURN c.name, p.name
│
▼ (DB 실행)
| c.name | p.name |
| 왓패드 | 왓패드 앱 |
│
▼ (결과 + 질문 → LLM)
"네이버가 인수한 왓패드는 '왓패드 앱'을 만듭니다. (근거: 위 쿼리 결과)"5-2. 스키마를 프롬프트에 넣기
LLM이 존재하지 않는 레이블이나 관계를 지어내지 않게 하려면 스키마를 그대로 보여 주는 것이 가장 효과적입니다.
SCHEMA = """노드:
(:Company {id, name, founded})
(:Person {id, name})
(:Product {id, name, category})
관계:
(:Company)-[:ACQUIRED {year}]->(:Company)
(:Person)-[:WORKS_AT {since}]->(:Company)
(:Person)-[:CEO_OF]->(:Company)
(:Company)-[:MAKES]->(:Product)
규칙:
- 위에 없는 레이블·관계·속성은 사용하지 마라.
- 회사 이름은 id로 매칭하라. 별칭 → id 표: {aliases}
- 읽기 전용 쿼리만 작성하라 (CREATE, MERGE, DELETE, SET 금지)."""
FEW_SHOT = """질문: 김철수가 일하는 회사는?
Cypher: MATCH (:Person {name: "김철수"})-[:WORKS_AT]->(c:Company) RETURN c.name"""
def text2cypher(question: str, llm) -> str:
prompt = f"{SCHEMA}\n\n예시:\n{FEW_SHOT}\n\n질문: {question}\nCypher:"
return llm.text(prompt).strip()Neo4j GraphRAG 패키지의 Text2CypherRetriever도 같은 원리로, 그래프 스키마와 예시를 LLM에 넘겨 Cypher를 만들고 실행합니다.
RDF 스택이라면 같은 방식으로 온톨로지(클래스, 속성, rdfs:label, rdfs:comment)를 요약해 넣고 SPARQL을 생성합니다.
PREFIX ex: <http://example.org/>
SELECT ?companyName ?productName WHERE {
ex:naver ex:acquired ?c .
?c ex:makes ?p ; ex:name ?companyName .
?p ex:name ?productName .
}5-3. 생성된 쿼리를 믿지 말고 확인하기
| 위험 | 대응 |
|---|
6. 추론으로 답 보강
온톨로지 규칙을 적용하면 문서에 직접 쓰여 있지 않은 사실도 답할 수 있습니다.
문서에서 추출한 사실 온톨로지 규칙
────────────────── ──────────────────────────────
알파테크 SUBSIDIARY_OF 베타랩스 SUBSIDIARY_OF 은 전이적
베타랩스 SUBSIDIARY_OF 감마홀딩스 ACQUIRED 의 역은 ACQUIRED_BY
감마홀딩스 ACQUIRED 베타랩스 CEO_OF ⊂ WORKS_AT (하위 속성)
박지훈 CEO_OF 알파테크
질문: "감마홀딩스 그룹 소속 회사에서 일하는 사람은?"
추론 후 사용 가능한 사실
알파테크 SUBSIDIARY_OF 감마홀딩스 (전이성)
박지훈 WORKS_AT 알파테크 (CEO_OF ⊂ WORKS_AT)
→ 답: 박지훈 (알파테크 CEO)방법은 두 가지입니다.
| 방법 | 설명 | 장단점 |
|---|---|---|
| 사전 구체화 (materialization) | 적재 시 추론해 결과 트리플을 저장 | 질의가 빠름 / 저장량 증가, 원본 변경 시 재계산 |
| 질의 시 추론 | 쿼리에서 규칙을 펼침 (경로 패턴, 추론 엔진) | 항상 최신 / 질의가 무거움 |
프로퍼티 그래프에서는 전이성을 경로 패턴으로 흉내 냅니다.
// SUBSIDIARY_OF 전이성: 1단계 이상 따라감
MATCH (c:Company)-[:SUBSIDIARY_OF*1..]->(:Company {id: "company:gamma"})
MATCH (p:Person)-[:WORKS_AT|CEO_OF]->(c)
RETURN p.name, c.name;7. 검증 가능성과 설명 가능성
온톨로지 RAG의 가장 큰 장점은 "왜 이 답인가"를 보여 줄 수 있다는 것입니다.
답: 박지훈은 감마홀딩스 그룹 소속입니다.
근거:
1. 실행 쿼리: MATCH (c)-[:SUBSIDIARY_OF*1..]->(:Company {id:"company:gamma"}) ...
2. 경로: 박지훈 -CEO_OF-> 알파테크 -SUBSIDIARY_OF-> 베타랩스 -SUBSIDIARY_OF-> 감마홀딩스
3. 추론 규칙: SUBSIDIARY_OF 전이성, CEO_OF ⊂ WORKS_AT
4. 원문: "알파테크는 베타랩스의 100% 자회사로..." (보고서 p.12, chunk #381)| 구분 | 벡터 RAG | Graph RAG | 온톨로지 RAG |
|---|---|---|---|
| 근거 형태 | 청크 목록 | 엔티티·관계·커뮤니티 요약 | 쿼리 + 경로 + 규칙 + 원문 |
| 재현성 | 임베딩·Top-k에 따라 변동 | 요약이 LLM 생성물 | 같은 쿼리면 같은 결과 |
| 감사(audit) | 어렵다 | 부분적 | 쿼리 로그로 가능 |
| 오답 원인 분석 | 검색? 생성? 모호 | 추출? 요약? 모호 | 쿼리 오류 / 데이터 누락 / 규칙 오류로 분리 가능 |
이 차이는 규제 산업에서 "AI가 왜 이렇게 답했는지"를 설명해야 할 때 특히 중요합니다.
8. 적합한 도메인
| 도메인 | 왜 맞는가 | 활용 온톨로지 예 | 대표 질문 |
|---|---|---|---|
| 금융 | 법인 관계, 지배구조, 규제 보고에 정확성 필수 | FIBO, LEI | "이 거래상대방의 최종 모회사와 총 익스포저는?" |
| 의료 | 용어 표준이 이미 있고 오답 비용이 큼 | SNOMED CT, ICD, RxNorm | "이 환자 약물 중 상호작용 경고가 있는 조합은?" |
| 제조 | 부품-설비-공정-불량의 연결 추적 | 사내 제품/설비 온톨로지 | "이 불량 부품이 들어간 완제품 로트는?" |
| 법률 | 법령-조항-판례-개념의 참조 구조 | 법령 온톨로지, ELI | "이 조항을 인용한 판례 중 개정 이후 것은?" |
공통점은 다음과 같습니다.
- 개념이 이미 정해져 있다 (표준 용어, 규제 정의)
- 틀리면 비용이 크다 (돈, 생명, 법적 책임)
- 관계 질문이 많다 (누가 누구의, 어디에 포함, 무엇과 충돌)
- (감사, 규제)
9. 비용: 공짜가 아니다
| 비용 항목 | 내용 |
|---|---|
| 온톨로지 설계 | 도메인 전문가와 역량 질문 정의, 클래스·관계 합의. 가장 큰 비용 |
| 매핑 | 기존 DB·문서를 온톨로지에 맞추는 규칙 작성 |
| 엔티티 해소 | 별칭 사전, 외부 식별자 연결, 검수 인력 |
| 유지보수 | 새 상품·규제가 생기면 온톨로지 버전업, 기존 데이터 마이그레이션 |
| 질의 품질 | Text2Cypher 예시 관리, 실패 쿼리 분석 |
| 전문 인력 | 온톨로지 엔지니어, 그래프 DB 운영 |
비용을 줄이는 방법은 다음과 같습니다.
- 표준 온톨로지 재사용: FIBO, SNOMED CT, schema.org 일부를 가져다 씁니다.
- 작게 시작: 역량 질문 5~10개에 필요한 클래스·관계만 정의하고 늘려 갑니다.
- LLM으로 초안: 문서 샘플에서 LLM이 클래스·관계 후보를 제안하게 하고, 전문가가 고릅니다.
- 혼합 전략: 핵심 엔티티(회사, 제품)만 온톨로지로 엄격히, 나머지는 자유 추출이나 벡터 검색으로.
10. 언제 쓰지 않는가
- 질문이 대부분 서술·요약형: "이 보고서의 시사점은?" 같은 질문은 쿼리로 풀리지 않습니다. 벡터 RAG나 Graph RAG(global)가 맞습니다.
- 도메인 개념이 아직 불분명: 무엇을 모델링할지 모르면 온톨로지가 계속 바뀌어 비용만 듭니다.
- 도메인 전문가 참여가 불가능: 개발자가 추측으로 만든 온톨로지는 오히려 오답을 체계화합니다.
- 틀려도 괜찮은 내부 도구: 사내 위키 검색 정도라면 과합니다.
11. 장점과 단점
| 장점 | 설명 |
|---|---|
| 정확한 구조 질의 | 집계·목록·다중 홉을 쿼리로 정확히 계산 |
| 일관된 그래프 | 스키마 강제와 엔티티 해소로 중복·난립 방지 |
| 추론 | 문서에 직접 없는 사실도 규칙으로 도출 |
| 설명 가능성 | 쿼리·경로·규칙·원문을 근거로 제시 |
| 재사용 | 같은 KG를 RAG 외에 분석·BI·규제 보고에도 사용 |
| 단점 | 설명 |
|---|---|
| 초기 설계 비용 | 온톨로지 설계와 전문가 합의 |
| 경직성 | 스키마 밖의 정보는 버려진다 (추출 누락) |
| 유지보수 | 온톨로지 변경 시 데이터·프롬프트·예시를 함께 수정 |
| Text2Cypher 오류 | 복잡한 질문에서 잘못된 쿼리를 그럴듯하게 생성 |
| 서술형 질문에 약함 | 원문 검색과의 하이브리드가 필요 |
12. 참고 자료
- From Local to Global: A Graph RAG Approach to Query-Focused Summarization (Microsoft GraphRAG 논문) - https://arxiv.org/abs/2404.16130
- Neo4j GraphRAG Python Package - https://neo4j.com/docs/neo4j-graphrag-python/current/
- Unleashing the power of schema: What's new in the Neo4j GraphRAG package for Python (Neo4j 블로그) - https://neo4j.com/blog/developer/unleashing-the-power-of-schema/
- Neo4j GraphAcademy: Text to Cypher retriever - https://graphacademy.neo4j.com/courses/genai-graphrag-python/3-retrieval/2-text-to-cypher-retriever/
- From human experts to machines: An LLM supported approach to ontology and knowledge graph construction (2024) - https://arxiv.org/abs/2403.08345
- Ontology Learning and Knowledge Graph Construction: A Comparison of Approaches and Their Impact on RAG Performance (2025) - https://arxiv.org/abs/2511.05991
- W3C SHACL (Shapes Constraint Language) - https://www.w3.org/TR/shacl/
- W3C OWL 2 Web Ontology Language Overview - https://www.w3.org/TR/owl2-overview/
기준일: 2026-10-03
13. 핵심 정리
온톨로지 RAG는 온톨로지가 정한 스키마로 지식 그래프를 만들고, 그 스키마로 질의하며, 온톨로지 규칙으로 답을 보강·검증하는 RAG다. Graph RAG가 LLM의 자유 추출로 그래프 모양을 정한다면, 온톨로지 RAG는 허용된 클래스·관계만 추출하고 domain/range로 검증하며 엔티티 해소로 같은 대상을 한 노드로 합친다. 질의는 스키마를 프롬프트에 넣은 Text2Cypher/Text2SPARQL로 정확한 쿼리를 만들고, 읽기 전용 권한과 스키마 대조로 생성된 쿼리를 검사한다. 전이성·역관계·하위 속성 같은 규칙으로 문서에 없는 사실을 도출하고, 쿼리·경로·규칙·원문을 근거로 보여 줄 수 있어 금융·의료·제조·법률처럼 정확성과 설명이 중요한 도메인에 맞는다. 대가는 온톨로지 설계와 유지보수 비용이며, 서술형 질문에는 벡터 검색과의 하이브리드가 필요하다.