JavaPark
01 / 29

온톨로지로 똑똑한 검색 만들기

벡터 검색의 한계에서 시작해
GraphRAG까지 가는 7단계

02 / 29

오늘의 핵심 질문

RAG 검색을 만들어봤는데
왜 정확한 답이 안 나올까요?

  • "Claude Code 시리즈 첫 글"을 물어도 엉뚱한 글이 섞입니다.
  • 비슷한 단어가 많은 글이 가까운 글이 아닙니다.
  • 답에 근거가 없으니 환각인지 사실인지 알기 어렵습니다.
03 / 29

결론 한 줄

벡터 검색은 의미가 비슷한 것은 잘 찾지만
관계가 맞는 것은 못 찾습니다.

→ 그 약점을 온톨로지·지식 그래프가 메웁니다.
→ 합치면 GraphRAG.

04 / 29

벡터 검색의 한계 1

벡터는 단어가 비슷한 문서를 잘 찾습니다.
하지만 관계를 표현하지 못합니다.

질문 벡터가 찾는 것 사용자가 원한 것
"시리즈 1편" "시리즈"·"1편"이 본문에 등장 메타데이터상 1번 글
"4월에 쓴 GraphRAG" "4월"·"GraphRAG" 등장 본문 작성일·태그 일치 글
"이 글이 인용한 자료" 본문이 비슷한 다른 글 cites 관계 글
05 / 29

벡터 검색의 한계 2

검색된 문서에 사실 관계가 명시되지 않음.
LLM이 "아마 이런 뜻일 거야" 추론 → 환각.

반대로 A authored-by B 같은
**명시적 사실(triple)**이 검색 결과에 들어오면
LLM은 그걸 그대로 인용합니다.

06 / 29

온톨로지란?

도메인의 개념·속성·관계를 형식적으로 명세한 것.

클래스(Class)   : Post, Author, Tag, Series, Category
속성(Property) : title, publishedAt, slug
관계(Relation) : writtenBy, hasTag, cites

일상에서 우리가 매일 쓰는 분류 체계를
명시적으로 적어둔 것뿐입니다.

07 / 29

온톨로지 vs 지식 그래프

구분 온톨로지 지식 그래프
비유 매뉴얼·설계도 실제로 채워진 데이터베이스
위치 스키마(schema) 인스턴스(instance)

온톨로지는 스키마, 지식 그래프는 그 위에 데이터를 채워 넣은 그래프.

08 / 29

왜 지금 다시 뜨는가?

오래된 기술인데 2024년부터 다시 주목.

  • Microsoft GraphRAG (2024) — 벡터 + 그래프 결합
  • Neo4j + LangChain/LlamaIndex 통합 본격화
  • LLM이 Cypher/SPARQL을 대신 써줌

→ 그래프 질의어 진입 장벽이 사라짐.
→ 남는 건 온톨로지를 잘 설계하느냐의 문제.

09 / 29

설계의 첫 단추

도메인 분석부터? 아니요.

답할 질의 리스트부터 적습니다.

어떤 질문에 답할 시스템인지 모르고 시작하면
만들어 놓고 보니 핵심 질의를 못 푸는 그래프가 됩니다.

10 / 29

블로그 검색용 질의 7개

# 질의 필요한 정보
Q1 "시리즈의 1번째 글" 시리즈 + 순번
Q2 "4월에 쓴 GraphRAG 글" 날짜 + 태그
Q3 "AI 카테고리 최신 5개" 카테고리 + 정렬
Q4 "이 글이 인용한 외부 자료" 인용 관계
Q5 "이 글과 비슷한 글" 의미 매칭 + 관계
Q6 "Claude Code·GraphRAG 둘 다인 시리즈" 태그 교차 + 시리즈
Q7 "먼저 읽어야 할 글" 선수 관계

이 7개가 온톨로지 테스트 케이스.

11 / 29

설계 4단계

  1. 답할 질의를 적는다
  2. 질의에서 명사 → 클래스 후보 뽑기
  3. 각 클래스에 속성·관계 도출
  4. 7개 질의를 다 표현할 수 있는지 검증

설계는 늘 가장 어렵고,
잘못 잡으면 구현이 통째로 뒤집힙니다.

12 / 29

설계에서 흔히 빠지는 함정

  1. 분류 vs 관계 — Category를 속성으로 둘까 클래스로 둘까?
  2. is-a 남발 — subClassOf로 트리만 키움
  3. 양방향 관계를 두 개 만들기
  4. 너무 잘게 쪼개기

과소 설계가 아니라 과대 설계가 더 비쌉니다.

13 / 29

구축: 스택 선정

후보 모델 질의어 LLM 통합
Neo4j LPG Cypher 1급 시민
GraphDB RDF SPARQL 일부 어댑터
Memgraph LPG Cypher 통합 진행 중
Apache Fuseki RDF SPARQL 거의 없음

GraphRAG가 목적이라면 Neo4j Community.

14 / 29

도커로 5분 만에 띄우기

services:
  neo4j:
    image: neo4j:5-community
    ports: ["7474:7474", "7687:7687"]
    environment:
      - NEO4J_AUTH=neo4j/please-change-me
      - NEO4J_PLUGINS=["apoc"]
    volumes:
      - ./neo4j/data:/data

docker compose up -d → Browser(:7474)

15 / 29

ERD → 온톨로지 매핑

이미 RDB에 ERD가 있다면 그게 1차 초안.

ERD Neo4j
테이블 Label
PK Unique constraint
FK Relationship type
일반 컬럼 Property
조인 테이블(N:N) 관계로 직접 변환

그래프엔 검색·관계에 쓰는 컬럼만 옮긴다.

16 / 29

LLM으로 본문 자동 추출

frontmatter로 못 잡는 인용·관련·선수 관계는
Claude Tool Use로 구조화 JSON 추출.

tools: [extractionTool],
tool_choice: { type: "tool", name: "record_post_extractions" },
system: [
  { type: "text", text: SYSTEM_PROMPT,
    cache_control: { type: "ephemeral" } }
]

핵심: 출력 스키마 강제 + 프롬프트 캐싱.

17 / 29

비용 — 실측

블로그 글 100개 기준 월 비용:

항목 월 비용
Neo4j (로컬 도커) $0
LLM 추출 ~$0.18
임베딩 ~$0.02
질의 LLM(1000회) ~$5
약 $5.2

입력이 큰 작업에선 프롬프트 캐싱이 사실상 필수.

18 / 29

검수 3단계

LLM 추출 결과를 그래프에 적재하기 전:

  1. URL 실재 확인 — HEAD 요청 200이 아니면 드롭
  2. slug 존재 확인 — 알려진 slug 집합과 매칭
  3. evidence 본문 부분문자열 매칭 — 인용이 진짜인지

이 3단계만으로 사람 검수 부담이 1/10로.

19 / 29

GraphRAG 파이프라인

자연어 질의
   ↓ 라우팅
graph / vector / hybrid
   ↓ 검색
Cypher 실행 / 벡터 인덱스 조회
   ↓ 합성
LLM이 결과를 자연어 답변으로
   ↓
[refs: ...] 근거 첨부

100줄도 안 되는 코드로 완성.

20 / 29

자연어 → Cypher

LLM에 스키마를 시스템 프롬프트로 박아두는 게 핵심.

const SCHEMA = `
Labels: Post, Tag, Series, Category, Author, ExternalResource
Relationships:
- (Post)-[:HAS_TAG]->(Tag)
- (Post)-[:BELONGS_TO_SERIES]->(Series)
- (Post)-[:CITES]->(ExternalResource)
...
`

안 그러면 존재하지 않는 라벨을 지어냅니다.

21 / 29

안전 장치

LLM이 만든 Cypher를 그대로 실행하지 말 것.

function isReadOnly(cypher) {
  return !/\b(CREATE|MERGE|DELETE|SET|REMOVE|DROP)\b/.test(cypher.toUpperCase())
}
  • 쓰기 명령 차단
  • LIMIT 강제
  • 실패 시 fallback 1회까지
22 / 29

라우터: 채널 분담

질의 성격 라우팅
엔티티·관계 graph "시리즈 1편"
의미 매칭 vector "RAG 정확도 올리는 방법"
둘 다 hybrid "신뢰할 만한 외부 자료"

모든 질의를 hybrid로 보내면 비용이 조용히 부풉니다.

23 / 29

4-tier 데이터 아키텍처

이미 RDB·DW·ES가 있는 조직이라면.

잘하는 일 못하는 일
OLTP (PG/MySQL) 트랜잭션 관계 탐색
DW (Snowflake) 대용량 집계 실시간
검색엔진 (ES) 자유 텍스트 관계
그래프 (Neo4j) 관계·의미 집계·트랜잭션

그래프는 대체가 아니라 보강.

24 / 29

안티패턴 5가지

  1. 그래프를 새 OLTP로 쓰기
  2. 모든 RDB 컬럼을 그래프에 미러링
  3. 그래프를 DW 대체로 사용
  4. CDC 없이 매번 풀 재적재
  5. 검색엔진을 그래프로 대체 시도

잘하는 일은 잘하는 도구에.

25 / 29

어떤 도메인에 추천하나

도메인 적합도
블로그·문서 검색 좋음
e-commerce 매우 좋음
학술 인용망 매우 좋음
법령·규정 좋음
대화 로그 보통
시계열 센서 비추천
단순 FAQ 비추천

분기점: 관계가 본질적으로 풍부한 도메인인가?

26 / 29

다시 한다면

  1. SCHEMA를 단일 소스 파일
  2. 글이 적으면 벡터는 나중에
  3. 사람 검수 UI를 일찍 만들기
  4. 회귀 테스트셋 일찍 만들기
  5. 안 풀리는 질의 로그를 따로 모으기

운영 두 달 차부터 가장 큰 자산이 되는 것들.

27 / 29

결론

관계 기반 정확도는 확실히 올라갑니다.
답변에 **근거(refs)**가 따라붙어 신뢰가 생깁니다.

다만 만능은 아닙니다.
관계가 풍부한 도메인이고
답의 근거가 중요한 서비스라면 GraphRAG가 답.

28 / 29

토론 질문

여러분 도메인에서 답하고 싶은
질의 5개는 무엇인가요?

그게 곧 여러분의
온톨로지 설계 출발점입니다.

29 / 29

더 읽어볼 글

전체 시리즈 (blog.javapark.kr/ai/):

  • ontology-search-01-why — 벡터 검색 한계
  • ontology-search-02-modeling — 설계
  • ontology-search-03-build — Neo4j 적재
  • ontology-search-04-llm-extraction — LLM 추출
  • ontology-search-05-graphrag — 검색·합성
  • ontology-search-06-lessons — 회고
  • ontology-search-07-structured-and-bigdata — 정형 DB·빅데이터 (확장편)