“VIP 기준이 뭐죠?”

유통사 BI Agent 프로젝트에서 있었던 일입니다.

현업 담당자가 테스트 중에 Agent에게 물었습니다. “지난달 VIP 고객 매출 알려줘.” 시스템이 숫자를 뱉어냈는데, 담당자 표정이 좋지 않았습니다. “이거 뭔가 이상한데요. VIP 기준이 우리 팀이랑 다른 것 같아요.”

마케팅의 VIP와 CRM의 VIP가 달랐습니다. 매출도 마찬가지입니다. 순매출이냐 총매출이냐에 따라 수억 단위로 차이가 납니다.

처음 겪는 문제가 아니었습니다. DW를 클라우드로 옮기는 프로젝트에서도 봤고, 차세대 정보계를 여러 벤더와 1년 넘게 만들 때도 똑같았습니다. 벤더마다 “매출”, “원가"의 기준이 달라서 데이터 정합성 잡느라 몇 주씩 지연됐습니다. 용어 하나 안 맞으면 전체 일정이 밀립니다. DW/BI 프로젝트를 하면서 이 문제가 안 나온 적이 없습니다.

엔터프라이즈 DW에는 테이블과 컬럼이 있습니다. 없는 건 맥락입니다. “이 컬럼이 비즈니스에서 뭘 의미하는지"가 기계가 읽을 수 있는 형태로 어디에도 정의되어 있지 않습니다.

그래서 직접 만들기로 했습니다.

NL2SQL은 만능이 아니다

요즘 NL2SQL 도구가 많습니다. 자연어를 SQL로 바꿔주는 것 자체는 이미 됩니다.

실환경에 붙여보면 얘기가 달라집니다. 벤치마크 점수가 높은 모델을 실제 DW에 연결하니까 체감 정확도가 확 떨어졌습니다. 카드사·통신사·공공데이터까지 내외부 데이터를 통합해 놓은 환경이었습니다. LLM이 이 복잡도(테이블 구조와 질문의 난이도)를 감당하지 못했습니다.

DW의 DDL을 열어보면 원인이 보입니다. T_CUST_MST.CUST_GRD_CD, T_ORD_DTL.SALE_AMT 같은 약어 테이블이 수천 개입니다. 벤치마크 DB의 customer_name, order_date와는 차원이 다릅니다. 같은 회사인데도 사업부마다 테이블 네이밍 규칙이 다르고, “매출"이라는 단어 하나가 사업부마다 다른 테이블을 가리킵니다.

파생 지표는 더 골치입니다. “순매출"은 단일 컬럼이 아닙니다. SUM(SALE_AMT) - SUM(RTN_AMT) - SUM(DC_AMT) 같은 계산식인데, 이 공식은 어떤 DDL에도 적혀 있지 않습니다. 현업 머릿속에 있거나, 잘해야 누군가의 Excel 정의서 어딘가에 묻혀 있습니다.

NL2SQL의 병목은 SQL 생성 능력이 아닙니다. 맥락이 없습니다.

온톨로지라는 선택

대화형 BI에서 프롬프트 엔지니어링을 아무리 최적화해도 DDL만으로는 한계가 뚜렷했습니다. 멀티에이전트 아키텍처도 설계해 봤는데, 근본 문제는 같았습니다. LLM에게 줄 맥락 자체가 없습니다.

온톨로지를 붙이기로 했습니다.

거창한 얘기가 아닙니다. 실용적으로 보면 이런 것입니다:

# 순매출 용어 정의
- term: 순매출
  definition: 총매출에서 반품과 에누리를 차감한 금액
  formula: SUM(SALE_AMT) - SUM(RTN_AMT) - SUM(DC_AMT)
  synonyms: [Net Sales, 순매출액, 넷세일즈]
  related_tables: [T_SALE_DTL, T_RTN_DTL]
  owner: 재무팀

이런 용어 정의를 메타데이터 카탈로그에 등록하고, NL2SQL 엔진의 RAG Store에 자동 동기화합니다. LLM이 “순매출"이라는 단어를 봤을 때 어떤 테이블의 어떤 컬럼을 어떤 계산식으로 조합해야 하는지 알게 되는 구조입니다.

처리 흐름은 이렇습니다:

DataNexus 질의 처리 흐름

온톨로지 적용 전후 차이 -내부 목표치가 EX(Execution Accuracy) 기준 +15~20%p 향상입니다. MVP에서 EX 80% 이상, 안정화 단계에서 90% 이상. 현실적인 수치인지는 만들어 보면서 검증합니다.

DDL 붙여넣기로는 안 되는 이유

엔터프라이즈 DW의 DDL을 다 붙여넣으면 수만수십만 토큰입니다. 테이블이 수백수천 개인 환경에서는 컨텍스트 윈도우에 다 못 넣습니다. 넣더라도 LLM이 그 안에서 정확한 테이블을 골라내는 건 Needle-in-a-Haystack 문제입니다.

보안도 걸립니다. 기업 내부 스키마를 외부 API에 통째로 보낼 수 없습니다. 같은 “매출"이라도 A그룹사 사용자와 B그룹사 사용자가 봐야 하는 범위가 다릅니다. Row-level Security가 필요한 환경입니다.

가장 큰 문제는 지속성입니다. DDL은 바뀝니다. 비즈니스 용어 정의도 바뀝니다. 차세대 정보계를 오픈하고 나서도 단계별 릴리즈가 이어지면 매번 메타데이터가 바뀝니다. 일회성 프롬프트가 아니라, 변경을 감지해서 자동으로 RAG Store를 갱신하는 파이프라인이 필요합니다.

따로 플랫폼을 만들어야겠다고 생각했습니다.

DataNexus가 하려는 것

네 가지 컴포넌트로 구성됩니다.

DataNexus 전체 구성도

  1. 메타데이터 카탈로그 -비즈니스 용어 정의, 테이블 메타, 데이터 계보를 한 곳에서 관리합니다. 온톨로지의 원천(Source of Truth).
  2. NL2SQL 엔진 -자연어를 SQL로 변환하되, 온톨로지에서 가져온 맥락을 프롬프트에 주입합니다. DDL만 던져주는 방식과 정확도 차이가 확 납니다.
  3. 문서 지식엔진 -사업보고서, 정책문서 같은 비정형 데이터를 GraphRAG + 벡터 하이브리드로 검색합니다.
  4. 그래프 DB -온톨로지를 지식 그래프로 저장합니다. 그룹사별 Multi-DB 격리까지.

카탈로그에서 정의한 온톨로지가 NL2SQL과 문서검색에 자동 동기화되어, 사용자 질문에 맥락이 붙는 구조입니다. 각 컴포넌트에 사용한 오픈소스와 선정 이유는 다음 글에서 다룹니다.

왜 지금인가

범용 모델은 빠르게 좋아지고 있습니다. 단순 기획이나 문서 생성은 곧 commodity가 됩니다. 차이를 만들려면 기업 데이터의 맥락을 구조화해서 모델에 주입하는 시스템이 있어야 합니다.

우리 회사의 “순매출” 정의가 뭔지, LLM이 아무리 똑똑해져도 모릅니다. 기업 내부에만 있는 지식이기 때문입니다.

LLM 연구에서는 이걸 “Non-verifiable Domain” 이라고 부릅니다. 수학이나 코딩은 정답을 자동 검증할 수 있는데, 기업 내부의 암묵적 지식은 외부에서 판별할 방법이 없습니다. 이런 데이터 위에 쌓이는 경쟁 우위가 “Data Moat” 입니다.

이 우위가 영원할 거라고 보진 않습니다. 범용 모델의 일반화 속도를 DataNexus의 데이터 축적 속도가 앞서야 합니다.

Data Moat를 쌓는 방법은 이렇습니다:

  • 온톨로지 기반 맥락 -도메인 전문가가 용어를 정제할수록 두꺼워지는 메타데이터 카탈로그
  • 역할별 해석 -같은 질문이라도 재무팀과 마케팅팀에 다른 응답을 주는 페르소나 최적화. 사용 패턴이 쌓일수록 개인화됩니다.
  • 시간축 지식 그래프 -Temporal Knowledge Graph로 “작년 4분기 기준 VIP 정의"와 “올해 기준 VIP 정의"를 구분
  • 비공개 데이터 자산 -그룹사별 그래프 DB 격리 + Row-level Security. 각 그룹사의 데이터가 독립 자산이 됩니다.

올해 상반기까지 MVP를 내고 데이터 축적 루프를 돌리는 게 목표입니다.

이 블로그의 목적

DataNexus를 만들면서 부딪히는 의사결정, 삽질, 해결 과정을 기록합니다.

다룰 것들:

  • 기술 스택을 선정한 과정 (후보군 탈락 사유 포함)
  • 메타데이터 카탈로그의 Business Glossary를 온톨로지로 쓸 때의 한계와 우회
  • SKOS 호환 레이어를 넣은 이유
  • NL2SQL 엔진의 User-Aware 설계와 Row-level Security
  • CQ(Competency Questions)로 온톨로지를 사전 검증하는 법
  • Query Router에서 결정론적 vs 확률론적 라우팅을 나누는 기준
  • 에이전트 태스크를 쪼개는 79% Rule

이론보다는 실제로 부딪힌 문제와 그걸 어떻게 풀었는지 (또는 아직 못 풀었는지)를 쓸 예정입니다.

다음 글

DataNexus의 기술 스택 -4개의 오픈소스를 이 조합으로 결정하기까지의 과정. 후보군에서 탈락한 것들과 그 이유를 정리합니다.