온톨로지와 RAG·Agent 연계 설계

앞선 두 글에서 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 → 실패 질문 분류 → 필요한 관계만 모델링

참고 문헌