AI 온톨로지 구축 — 그래프와 벡터DB, 두 관점이 각각 무엇을 포기하는가
온톨로지 구축을 “지식을 표현하는 방법”이라고 설명하면 아무것도 설명되지 않는다. 실제로 푸는 문제는 하나다. 무엇과 무엇이 같은 것인지를 누가 정하는가.
정산과 settlement와 SettlementService가 같은 것인가. 주문 취소와 환불은 다른 개념인가. 이 판정을 사람이 스키마에 미리 적어두면 그래프 관점이고, 모델이 임베딩 거리로 그때그때 계산하면 벡터DB 관점이다. 나머지 차이는 전부 여기서 파생된다.
두 관점을 다 굴려봤다. 하나는 ~/wiki 라는 손으로 만든 지식베이스, 다른 하나는 memory-qa 라는 벡터 검색 RAG 서비스다. 둘 다 조용히 실패했고, 실패하는 방식이 정반대였다. 그 대비가 이 글의 뼈대다.
1. 그래프 관점 — 합의를 먼저 적고, 그 합의를 기계가 검사한다
표준 스택이 두 겹인 이유
RDF 위의 온톨로지 언어는 W3C OWL 2다. OWL 2는 클래스·프로퍼티·개체에 형식 의미론을 부여해서, 적혀 있지 않은 사실을 추론(entailment)해낼 수 있게 만든 언어다. 사양은 Direct Semantics(기술논리 SROIQ 기반)와 RDF-Based Semantics 두 가지 의미론을 정의하고, 추론 비용을 감당 가능한 범위로 자르기 위해 EL·QL·RL 프로파일을 따로 둔다.1
그런데 OWL만으로는 “이 데이터가 규격을 지켰는지”를 못 본다. OWL은 열린 세계를 전제로 추론하는 언어이지 검증하는 언어가 아니기 때문이다. 적혀 있지 않은 것은 거짓이 아니라 그냥 모르는 것으로 취급된다. 그래서 W3C는 2017년에 SHACL을 별도 권고로 냈다. SHACL은 RDF 그래프를 조건 집합에 대해 검증하는 언어이고, 조건을 담은 shapes graph 와 검사 대상인 data graph 를 분리한다. Core는 반드시 구현해야 하고 SPARQL 확장은 선택이다.2
이 두 겹 구조가 그래프 관점의 성격을 그대로 보여준다. 어휘를 정의하는 일과, 그 어휘가 지켜지는지 확인하는 일은 별개의 작업이고, 둘 다 사람이 해야 한다.
실제로 그래프 관점을 굴린 결과
~/wiki 는 개인 지식베이스지만 구조는 축소된 온톨로지다. SCHEMA.md 가 7키 프론트매터와 “페이지당 위키링크 최소 2개”를 규격으로 못박고, 문서끼리 위키링크로 엣지를 만든다.
여기서 두 가지가 관측됐다.
첫째, 규격 문서 자체가 조용히 사라졌다. 머지 커밋 a772439 이후 SCHEMA.md 는 717바이트만 남았다. 프론트매터 템플릿도 위키링크 규칙도 함께 증발했다. CI 게이트가 없어서 빌드는 안 깨졌고, 아무도 몰랐다. 위키 자신의 문서가 “중요한 규칙일수록 프롬프트에서 코드로 옮긴다”고 적어놓은 상태에서, 정작 그 규칙이 코드로 강제되지 않아 없어진 것이다.
둘째, 준수율 숫자는 기준을 안 밝히면 부풀려진다. “프론트매터가 있기만 하면 통과” 기준으로 재면 76%, 복원한 7키 규격으로 재면 36%(26/71)다. 층별로 보면 concepts/ 는 13/15(86.7%)로 건강하고, 링크 0개인 42건은 대부분 애초에 규격 대상이 아닌 legacy 디렉터리였다.
셋째가 가장 아프다. 관계 타입이 references 하나뿐이었다 — 엣지 108개 전부. 스키마상으로는 그래프지만, 실질은 링크 목록이다. 엣지에 의미가 없으면 “A가 B를 반증한다”, “A는 B의 특수한 경우다” 같은 질의를 할 수 없다. 그래프의 값어치는 노드가 아니라 엣지 타입의 다양성에서 나오는데, 그걸 채우는 건 순수한 사람 노동이라 제일 먼저 생략된다.
해결은 래칫 게이트였다. scripts/validate-wiki.py 를 2층으로 걸고 — 1층은 SCHEMA.md 자체의 섹션 수·최소 바이트·필수 키를 면제 없이 검사, 2층은 페이지 검사를 .wiki-baseline.json 기준선으로 — 기존 부채 48건은 통과시키고 새 위반과 기존 악화만 차단했다. 48건을 전부 빨갛게 만드는 게이트는 첫날에 꺼지고, 꺼진 게이트가 애초의 원인이었다.
그래프 관점의 비용
- 어휘를 정하는 합의 비용이 선불이다. 그리고 이 비용은 자동화가 안 된다.
- 규칙은 문서에 적힌 순간부터 증발하기 시작한다. 코드로 강제되지 않는 온톨로지는 온톨로지가 아니다.
- 커버리지가 곧 성능이다. 안 적은 관계는 존재하지 않는 관계다.
2. 벡터DB 관점 — 합의를 안 하고, 거리로 대신한다
인덱스 자체가 그래프다
벡터 검색의 사실상 표준 인덱스인 HNSW는 다층 근접 그래프다. 각 층이 서로 다른 길이 스케일의 링크를 담고, 상위 층에서 대충 내려오다가 하위 층에서 정밀 탐색한다. 저자들은 이 구조로 탐색 복잡도가 로그 스케일로 확장된다고 보고했다(Malkov & Yashunin, arXiv 2016 / IEEE TPAMI 2018 게재).3
여기서 첫 번째 아이러니가 나온다. 벡터 관점을 택해도 바닥에는 그래프가 깔린다. 다만 그 그래프의 엣지는 사람이 정한 의미가 아니라 모델이 계산한 거리다. 온톨로지를 안 만든 게 아니라, 온톨로지를 모델에게 외주 준 것에 가깝다.
이건 이미 평범한 RDBMS 기능이기도 하다. pgvector 공식 저장소는 HNSW를 “multilayer graph”로 설명하고, 인덱스 없는 기본 동작은 정확 최근접 탐색(완전 recall), 인덱스를 붙이면 recall 일부를 속도와 맞바꾸는 근사 탐색이 된다고 명시한다. 근사 인덱스를 추가하면 같은 질의라도 결과가 달라진다는 경고까지 README에 있다.4
이 문장이 벡터 관점의 성격을 정확히 요약한다. 정확성이 설정값이다.
실제로 벡터 관점을 굴린 결과
memory-qa 는 K3s 위에 올린 RAG Q&A 서비스다. 질문이 오면 의미 검색으로 top-3를 뽑고, 그 본문을 Gemini에 넣어 답을 만든다. 스키마도 어휘도 없다. 문서를 넣으면 끝이다.
증상은 “200 OK인데 답이 빈다”였다. 원인을 파보니 세 층이었고, 처음 세운 가설은 결과였지 원인이 아니었다.
1순위는 코퍼스 공백이었다. 코드로는 못 고친다. 벡터 34개 중 실문서 18개, 크기 165~989바이트. 16개 중 15개가 특정 날짜 이후 정지 상태였고, 정작 자주 묻는 주제의 문서는 인덱스에 아예 없었다. 검색 엔진 자체 지표는 Zero-Result Rate 33.3% / Rerank Used 0. 랭커는 답이 있을 때는 제대로 골랐다. 엉뚱한 게 나온 건 고를 게 없어서였다.
2순위는 점수 임계값 부재. 히트가 0건일 때만 폴백이었고, 유사도가 아무리 낮아도 top-3를 그대로 프롬프트에 밀어넣었다. 시스템 프롬프트가 “근거 없으면 모른다고 답하라”였으니 → 쓰레기 컨텍스트 → “모릅니다”. 사용자가 본 빈 답변의 정체가 이거였다.
임계값을 잡으려고 라이브 질의 14건을 실측했다. 결과가 이 글에서 제일 중요한 숫자다.
| 구분 | 최고 유사도 점수 범위 |
|---|---|
| 답이 코퍼스에 있는 질문 | 0.5568 ~ 0.6293 |
| 답이 코퍼스에 없는 질문 | 0.4535 ~ 0.5565 |
두 구간의 간격이 0.0003이다. “관련 있음”과 “전혀 없음”을 가르는 경계가 소수점 넷째 자리에 붙어 있었다. MIN_SCORE=0.55 로 자르긴 했지만, 이건 원리에서 유도한 값이 아니라 이 코퍼스에서만 맞는 경험값이다. 문서가 바뀌면 다시 재야 한다.
3순위는 스캐폴딩 잠식. 검색 엔진이 자동 생성한 .overview.md / .abstract.md 같은 목차성 문서가, 답이 있는 질문 8건 중 4건에서 정답 문서를 제치고 top-1을 먹었다. 목차 문서는 온갖 키워드를 조금씩 담고 있어서 어떤 질문과도 적당히 가깝다. TOP_K=3 에서 슬롯 하나를 통째로 날리는 것이다.
여기서 핵심은 이거다. 벡터 공간에는 “이건 목차 문서다”라는 타입 정보가 없다. 그래프 관점이었다면 rdf:type 한 줄이나 SHACL 제약 하나로 끝날 문제를, 벡터 관점에서는 SKIP_SCAFFOLD=1 이라는 파일명 기반 휴리스틱으로 막아야 했다. 타입을 안 적기로 한 대가를, 타입을 흉내내는 코드로 갚은 셈이다.
부수적으로, 캐시는 무죄였다. 캐시 히트 39.6ms vs 미스 4794ms — 약 121배 차이라 캐시가 의심스러워 보였지만, 캐시는 이미 만들어진 나쁜 답을 충실히 돌려주고 있었을 뿐이다.
벡터 관점의 비용
- 경계가 없다. 코퍼스에 답이 없어도 무조건 상위 K개를 돌려준다. “없음”을 표현하려면 임계값을 사람이 손으로 정해야 하고, 그 임계값은 코퍼스마다 다르다.
- 타입이 없다. 문서의 종류·역할·신뢰도가 벡터에 안 들어간다.
- 설명이 없다. 왜 이 문서를 골랐는지가 스칼라 하나뿐이다. 위 표의 0.5565와 0.5568은 사람이 볼 때 아무 의미도 없는 숫자다.
- 대신 선불 비용이 0에 가깝다. 넣으면 바로 검색된다.
~/wiki의 엣지 타입 하나를 손으로 늘리는 시간에 벡터DB는 문서 수천 개를 삼킨다.
3. 두 관점이 실제로 만나는 지점
두 실패를 겹쳐 보면 정확히 상보적이다.
| 그래프 관점 | 벡터DB 관점 | |
|---|---|---|
| 동일성 판정 주체 | 사람 (스키마) | 모델 (거리) |
| 선불 비용 | 크다 (어휘 합의) | 거의 없다 |
| 실패 모드 | 채우다 만다 — 엣지 108개가 전부 references |
없어도 뭔가 준다 — Zero-Result 33.3%인데 답은 매번 나옴 |
| “모른다”의 표현 | 구조적으로 가능 (질의 결과 0건) | 임계값을 손으로 정해야 가능 |
| 왜 골랐는지 | 경로로 설명됨 | 스칼라 하나 |
| 무너지는 계기 | 규격이 코드로 강제되지 않을 때 | 코퍼스에 공백이 있을 때 |
업계가 이 둘을 섞기 시작한 대표 사례가 마이크로소프트 리서치의 GraphRAG다. LLM으로 원문에서 엔티티 지식그래프를 뽑고, 커뮤니티 단위 요약을 미리 만들어 두었다가, 질의 시 map-reduce로 부분 답변을 모아 최종 답을 만든다. 기존 벡터 RAG가 잘 못하는 “이 코퍼스 전체의 주제가 뭐냐”류 전역 질문을 겨냥한 구조다.5
논문이 보고한 수치는 이렇다 — 다만 저자(마이크로소프트 리서치) 자체 측정이고, 판정자가 사람이 아니라 LLM(LLM-as-judge)이며, 중립 제3자 재현 결과가 아니다. 그 전제로만 읽어야 한다.
- 포괄성(comprehensiveness) 승률 72~83%, 다양성(diversity) 승률 62~82% — 벡터 RAG 베이스라인 대비
- 커뮤니티 요약 레벨에 따라 컨텍스트 토큰이 26~33% 적게(하위 레벨 C3), 최상위 요약(C0)에서는 97% 이상 적게 들었다
방향 자체는 위 표와 정확히 일치한다. 벡터가 못 하는 건 “전역 구조”고, 그래프가 못 하는 건 “구축 비용”이다. GraphRAG는 그래프 구축을 사람 대신 LLM에게 시켜서 후자를 깎았다. 이 접근의 진짜 리스크도 거기 있다 — LLM이 뽑은 엔티티와 관계가 아무도 검수하지 않은 온톨로지라는 것. 내 ~/wiki 사례가 보여준 게 정확히 “검수되지 않는 스키마는 조용히 무너진다”였다.
4. 그래서 무엇을 기준으로 고르나
이론으로 고르지 말고, 답해야 하는 질문의 형태로 고르는 게 맞다고 본다.
- “없다”가 정답인 질문이 자주 오는가? → 그래프. 벡터는 없음을 표현하는 데 임계값 튜닝이라는 추가 장치가 필요하고, 위 실측처럼 그 경계가 0.0003일 수도 있다.
- 왜 그 답이 나왔는지 사람에게 설명해야 하는가? → 그래프. 경로는 설명이 되고 코사인 값은 설명이 안 된다.
- 문서가 하루에도 수십 개씩 늘어나는가? → 벡터. 어휘 합의는 그 속도를 못 따라간다.
- 질문이 “이 코퍼스 전체가 무슨 얘기냐” 류인가? → 둘 다 부족하다. GraphRAG 계열의 요약 계층이 이 자리를 노린다.
그리고 어느 쪽을 고르든, 이번에 실측으로 확인한 것 두 개는 공통이다.
- 그래프를 골랐다면 게이트를 같이 만든다. 규격 문서 하나가 머지에서 717바이트로 줄어도 아무도 모른다. 그리고 게이트는 래칫으로 걸어야 한다 — 기존 부채까지 전부 빨갛게 만드는 게이트는 첫날에 꺼진다.
- 벡터를 골랐다면 코퍼스부터 본다. retrieval 코드를 아무리 고쳐도 인덱스에 없는 문서는 안 나온다. Zero-Result Rate와 top-1이 무엇이었는지를 로그로 남기지 않으면, 검증 불가능한 가설을 몇 주씩 붙들게 된다. 실제로
Handler.log_message가pass이고 retrieval 경로에print가 없어서 점수가 원리적으로 파드 로그에 안 남았고, 그게 진단이 늦은 이유였다.
온톨로지 구축을 그래프냐 벡터냐의 선택으로 보면 취향 싸움이 된다. “동일성 판정을 누구에게 맡기고, 그 대가를 어디서 치를 것인가”의 선택으로 보면 두 관점 다 자기 청구서를 정직하게 들고 있다. 그래프는 앞에서 받고, 벡터는 뒤에서 받는다.
References
본문의 ~/wiki 준수율·엣지 타입 통계와 memory-qa 의 유사도 점수 구간·스캐폴딩 잠식 비율·Zero-Result Rate·캐시 지연은 전부 필자 개인 환경에서의 실측값이며 일반화된 벤치마크가 아니다. 관련 선행 글: RAG를 pgvector로 붙이며 만난 조용한 함정 셋, LLM-Wiki 지식베이스를 측정해봤다
-
W3C, OWL 2 Web Ontology Language Document Overview (Second Edition), W3C Recommendation, 2012-12-11. https://www.w3.org/TR/owl2-overview/ ↩
-
W3C, Shapes Constraint Language (SHACL), W3C Recommendation, 2017-07-20. https://www.w3.org/TR/shacl/ ↩
-
Y. A. Malkov, D. A. Yashunin, Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs, arXiv:1603.09320 (2016); IEEE TPAMI (2018), doi:10.1109/TPAMI.2018.2889473. https://arxiv.org/abs/1603.09320 ↩
-
pgvector 공식 저장소 README (인덱싱 / HNSW / IVFFlat 절). https://github.com/pgvector/pgvector ↩
-
D. Edge et al., From Local to Global: A Graph RAG Approach to Query-Focused Summarization, arXiv:2404.16130, Microsoft Research. https://arxiv.org/abs/2404.16130 — 본문에 인용한 승률·토큰 절감 수치는 저자 자체 측정이며 판정에 LLM-as-judge를 사용했다. 중립 제3자의 재현 결과가 아니다. ↩