본문으로 건너뛰기

데이터 품질은 스스로 자란다: AI 루프 시스템이 바꾼 온톨로지 자동화

온톨로지AI 에이전트지식그래프데이터 품질
Data & AI이민식2026년 7월 6일

안녕하세요, UMOS Data/AI 팀에서 지식그래프를 개발하고 있는 데이터 분석가 이민식입니다.

저희 팀은 창고(WMS), 배차(TMS), 차량(FMS), 호출(TAP)처럼 서로 다른 시스템에 흩어져 있는 운영 데이터를 하나의 지식그래프로 연결하고, 그 위에서 자연어 질문을 SPARQL(지식그래프에 데이터를 물어보는 질문 언어)로 바꿔 답하는 온톨로지 기반 챗봇을 만들고 있습니다. 여기서 자연스럽게 드는 질문이 있습니다. 왜 하필 온톨로지일까요?

왜 온톨로지인가

각 시스템 안에서는 "이번 달 정비 예정 차량"이나 "오늘 배차 지연 건수" 같은 조회가 이미 가능합니다. 하지만 "정비 이력이 배차 지연에 얼마나 영향을 줬는가"처럼 시스템 경계를 넘나드는 질문에는 답할 수 없었습니다. 데이터가 없어서가 아니라 서로 다른 이름과 구조로 흩어져 있었기 때문입니다. 온톨로지는 이 데이터를 하나의 의미 구조로 묶어, 도메인을 넘나드는 질문에도 답할 수 있게 만드는 방법입니다. 그리고 이 연결은 한 번 만들고 끝나는 게 아니라, 창고·차량의 실제 상태 변화를 놓치지 않고 계속 반영해야 유지됩니다 — 이렇게 디지털 트윈을 촘촘하게 유지하는 일도 온톨로지 작업의 일부입니다.

그런데 이렇게 만든 챗봇이 실제로 믿을 만한 답을 내놓는지는 별개의 문제입니다. 이 글은 그 챗봇을 검증하는 일, 그중에서도 판정을 사람이 아니라 AI에게 맡기면서 데이터 품질 자체가 스스로 채워지는 구조로 바뀐 과정을 다룹니다.

원래는 분석가가 붙어야 하는 일이었습니다

이 챗봇을 검증하려면 클러스터별로 나눈 테스트 케이스(TC) 수십~수백 개를 하나씩 확인해야 했습니다. 응답이 맞았는지, SPARQL이 의도대로 짜였는지, 틀렸다면 어디가 문제인지를 사람이 하나씩 확인하던 시절에는 검증 속도가 분석가 한 명의 판단 속도에 그대로 묶여 있었습니다. 지금은 그 판정 대부분을 LLM 체인이 대신하고, 사람은 판정 기준을 지키는 역할만 남았습니다.

예를 들어 "이번 달 정비 예정인 트럭이 몇 대야?"라는 질문 하나를 검증한다고 해보겠습니다. 이 글에서는 같은 질문을 처음부터 끝까지 따라가며 각 단계를 보여드리겠습니다. 사람이 직접 하면 다음 네 가지를 전부 거쳐야 합니다.

  • 챗봇 답변에 "정비 예정 대수, 차량 목록"이라는 채점 기준(rubric)이 요구하는 정보가 담겼는지 읽고 판단
  • 답변을 만들 때 생성된 SPARQL이 실제로 "이번 달"·"정비 예정" 조건을 걸었는지 까서 확인
  • 틀렸다면 원인이 API 오류인지, SPARQL 생성 자체가 안 된 건지, 정비 일정 데이터가 아직 없어서인지 구분
  • 이런 실패가 반복되면 "정비 일정 테이블을 더 적재해야 하는지" 리포트로 정리

TC가 몇 개일 때는 사람이 감당할 수 있습니다. 하지만 도메인이 늘고 클러스터가 늘어날수록, 이 네 가지 작업 전부가 분석가 한 명의 판단 속도에 묶였습니다. 온톨로지를 넓히는 속도가 아니라 사람이 판정하는 속도가 병목이 된 겁니다.

판정을 사람 대신 AI에게 맡기다

그래서 이 판정 작업 자체를 사람이 아니라 AI에게 맡겼습니다. 여기서 클래스는 엑셀로 치면 "어떤 시트를 봤는지"(정비 일정, 차량 같은 것), 프로퍼티는 "그 시트에서 어떤 열(항목)을 가져왔는지"(예정일, 차량 번호 같은 것)에 가깝습니다. 이 질문에 제대로 답하려면 어떤 시트와 열을 봐야 하는지 미리 정답을 적어두는데, 이걸 expected_classes, expected_properties라고 부릅니다.

같은 질문 — "이번 달 정비 예정인 트럭이 몇 대야?" — 이 다음 세 단계를 어떻게 통과하는지 보면 구조가 보입니다.

  1. 평가: 질문을 실제 API에 던져 응답을 받고, Rubric Judge(LLM)가 "정비 예정 대수와 차량 목록이 담겼는지" PASS/FAIL을 채점합니다.
  2. 진단: 실패했다면 SPARQL Judge(LLM)가 생성된 SPARQL에서 "이번 달"·"정비 예정" 조건에 해당하는 클래스·프로퍼티를 실제로 썼는지 추출해, 기대한 조회 경로를 얼마나 충족했는지(match_score) 계산하고 원인을 분류합니다.
  3. 개선안 도출: 원인이 "정비 일정 데이터가 없어서"(NO_DATA)로 분류되면, 온톨로지-DB 스키마 매핑을 역으로 뒤집어 어떤 테이블·컬럼을 적재해야 하는지 자동으로 집계합니다.

같은 질문은 다음 여섯 가지 결과 중 하나로 분류됩니다.

Cause이 질문이 이렇게 실패했을 때
API_ERROR질문을 보냈는데 API 응답 자체가 오지 않음
NO_SPARQL답변은 왔지만 "이번 달 정비 예정"을 조회할 SPARQL이 만들어지지 않음
NO_DATASPARQL은 "이번 달 정비 예정" 조건을 정확히 걸었지만, 정비 일정 데이터가 아직 적재되지 않아 결과가 0건
WRONG_SPARQLSPARQL이 "정비 완료" 조건으로 잘못 짜였고, 답변도 없음
HALLUCINATIONSPARQL은 "정비 완료" 조건으로 잘못 짜였는데, 그럴듯한 숫자를 답변으로 만들어냄
PASS"이번 달 정비 예정 트럭 3대"처럼 정상적으로 답함

방금 그 질문이 NO_DATA로 분류되면, 파이프라인은 이런 형태로 결과를 만들어냅니다.

{
"table": "maintenance_schedule",
"label_ko": "정비 일정",
"blocked_tcs": ["FMS-DE-A-014"],
"columns": [{ "column": "scheduled_date", "label_ko": "예정일" }]
}

사람이 로그를 뒤져서 "이 질문이 왜 안 풀리지"를 역추적하던 일이, 실행 결과가 곧바로 "무엇을 적재해야 하는가" 리스트로 나오는 형태로 바뀐 겁니다.

처음엔 판정 하나면 충분한 줄 알았습니다

이 세 단계는 처음부터 이렇게 설계된 게 아니었습니다. 처음 설계는 훨씬 단순했습니다. Rubric Judge 하나로 PASS/FAIL만 나누면 될 거라고 생각했습니다.

그런데 같은 질문에서 두 가지 문제가 곧 보였습니다.

첫째, "이번 달 정비 예정인 트럭이 몇 대야?"에 대해 SPARQL이 "지난달 정비 완료" 조건으로 완전히 엉뚱하게 짜인 적이 있었습니다. 그런데 마침 그달 정비 완료 대수와 이번 달 정비 예정 대수가 우연히 똑같아서, 답변만 본 Rubric Judge는 PASS를 줬습니다.

둘째, 반대로 SPARQL이 ScheduledMaintenance 클래스 대신 의미상 동등한 MaintenancePlan 클래스로 조회한 적이 있었습니다. 결과는 맞았지만 rubric이 ScheduledMaintenance만 정답 기준으로 잡아둔 탓에 FAIL로 떨어졌습니다. 답변 내용만 보는 판정으로는 "어떤 방식으로 답에 도달했는가"를 신뢰할 수 없었습니다.

그래서 SPARQL 자체를 채점하는 두 번째 판정(SPARQL Judge)을 추가했습니다. 그런데 이것도 완벽하지 않았습니다. 두 번째 사례처럼 SPARQL Judge가 "기대 경로에 못 미친다(SPARQL_GAP)"고 판정했는데 Rubric Judge는 PASS를 준, 애매한 경계 케이스가 남았습니다. 이 경계 케이스만 따로 모아 세 번째 판정(Alt SPARQL Verify)이 "의미적으로 유효한 대체 경로인지, 아니면 우연히 통과한 것인지"를 한 번 더 검증하도록 구조를 다시 짰습니다.

판정을 하나에서 셋으로 늘린 이유

판정을 하나에서 셋으로 늘린 이유는 더 정교해 보이고 싶어서가 아니라, 각 단계가 서로 다른 종류의 오판을 잡아내지 못했기 때문입니다.

사람에게 남은 두 가지

이 루프에서 사람이 손을 뗀 건 아닙니다. 다만 손을 대는 지점이 바뀌었습니다.

첫째, 정답 목록을 정하는 일입니다. 방금 그 질문이라면 "정비 예정"이라는 데이터를 부르는 이름이 여러 개일 수 있는데(ScheduledMaintenance 또는 MaintenancePlan), 둘 중 뭘 정답으로 쓸지는 사람이 먼저 정해둬야 합니다. 이 기준이 없으면 SPARQL Judge는 애초에 무엇과 비교해야 할지 모릅니다. AI가 판정하는 게 아니라, 사람이 세운 기준 안에서 AI가 판정을 대신 실행하는 구조에 가깝습니다.

둘째, 경계 케이스를 표본으로 확인하는 일입니다. 방금 MaintenancePlan으로 조회해 SPARQL_GAP이 떴던 사례처럼, "의미적으로는 맞을 수도 있다"고 LLM이 판단한 ALT 후보는 사람이 주기적으로 표본을 확인합니다. 판정 체인을 무조건 믿지 않기 위해서입니다.

반복할수록 좋아지는 구조

이 루프는 한 번 돌리고 끝나지 않습니다. NO_DATA로 분류된 케이스가 데이터 적재 백로그가 되고, 백로그가 처리되면 같은 TC를 다시 돌렸을 때 PASS로 바뀝니다. "이번 달 정비 예정인 트럭이 몇 대야?"도 정비 일정 테이블이 적재되고 나면 다음 실행에서 PASS로 바뀝니다. SPARQL_GAP이 ALT로 확인되면 expected_classesMaintenancePlan이 추가되고, 다음부터는 이 표현으로 조회해도 바로 정답으로 인정됩니다.

이 루프는 반복될수록 정확해집니다

즉 평가를 반복할수록 온톨로지 커버리지와 데이터 적재 상태가 조금씩 채워지고, 그 결과가 다시 다음 평가에 반영되는 구조입니다. 사람이 매번 원인을 추적하고 우선순위를 매기던 반복 작업이 사라지면서, 이 사이클을 훨씬 자주 돌릴 수 있게 됐습니다. 사람이 매번 손대지 않아도 데이터 품질이 스스로 자라는 구조는, 판정을 AI 루프에 맡기고 나서야 가능해졌습니다.

이 루프는 질문이 만들어지는 순간부터 시작됩니다

지금까지 설명한 평가·진단·개선 루프는 TC가 이미 있다는 가정에서 시작했습니다. 그런데 TC 자체도 같은 방식으로 만들어집니다.

"이번 달 정비 예정인 트럭이 몇 대야?"라는 질문도 그냥 떠오른 게 아닙니다. 만들 때부터 정해진 순서를 따릅니다: Stage 결정 → ID 발급 → 질문·채점 기준 작성.

먼저 이 질문은 "왜 그랬는지"를 묻는 게 아니라 "현재 상태"를 묻고 있으므로 Descriptive로 분류됩니다. 두 개 이상의 데이터를 연계해야만 답할 수 있는 질문이었다면 Diagnostic으로 분류가 달라졌을 겁니다.

그다음 질문 문장과 채점 기준을 씁니다. 여기에도 지켜야 하는 규칙이 있습니다.

  • 질문에는 ScheduledMaintenance 같은 클래스명을 노출하지 않습니다. 기준은 "실제 사용자가 챗봇에 물을 법한 표현인가"입니다.
  • rubric에는 "트럭 3대"처럼 구체적 수치를 고정하지 않습니다. 데이터 기간이 바뀌면 rubric 자체가 틀려지기 때문입니다. 대신 "정비 예정 대수(정수)"처럼 씁니다.
  • expected_classes는 "이 질문에 답하는 최소 SPARQL"을 머릿속으로 스케치해서, 실제로 등장하는 클래스만 넣습니다. 그리고 데이터가 저장된 그래프 데이터베이스인 Fuseki에 실제로 그 데이터가 몇 건 있는지 확인하고서야 TC로 확정합니다.

질문을 쓰는 일도 판정을 설계하는 일과 똑같습니다. 사람은 "무엇을 기준으로 삼을 것인가"만 정하고, 그 기준을 지키는 역할을 합니다. 그리고 이 기준은 고정되어 있지 않습니다. 앞서 나온 SPARQL_GAP과 ALT 케이스가 확인되면 그 TC의 expected_classes가 갱신됩니다. TC를 만드는 일과 TC를 검증하는 일이 서로의 결과를 되먹임하는 구조입니다.

이 팀이 데이터로 풀고 있는 문제들

이 글에서 다룬 평가 루프는 하나의 사례일 뿐입니다. 데이터 관점에서 마주치는 문제는 이보다 넓습니다.

AI 에이전트를 어떻게 평가할 것인가. 이 글에서 다룬 판정 체인이 커버하는 범위는 전체 지능 단계 중 일부입니다. 저희는 챗봇의 지능을 Reactive(Descriptive·Diagnostic) → Proactive(Projective·Predictive·Prescriptive) → Adaptive 순으로 나눠 보고 있는데, 지금 이 루프는 Reactive 단계, 즉 현재 상태를 조회하고 원인을 역추적하는 수준까지만 다룹니다. "이대로 가면 언제 부족해지는가"를 계산하는 Projective, "이 차량이 고장 날 확률은?"을 추론하는 Predictive, "그럼 어떻게 해야 하는가"까지 권고하는 Prescriptive로 올라가려면 지금 같은 rubric·SPARQL 매칭만으로는 판정할 수 없습니다. 답이 맞았는지를 넘어 추론 과정 자체가 타당한지를 평가하는 새로운 판정 체계가 필요합니다.

지금 우리는 사다리 몇 단에 있는가

온톨로지가 실제 데이터를 정확히 반영하고 있는가. TBox(온톨로지 설계)와 실제로 적재된 데이터(ABox)는 항상 같지 않습니다. 설계는 있는데 데이터가 아직 없거나, 설계와 다른 이름으로 적재된 경우도 있습니다. 이 글에 나온 Fuseki COUNT 검증처럼, 설계상 존재해야 하는 것과 실제로 존재하는 것의 차이를 끊임없이 실측해야 판정 기준 자체를 믿을 수 있습니다.

물리적 운영을 온톨로지로 얼마나 충실하게 옮길 것인가. 창고, 차량, 경로 같은 실제 운영이 온톨로지 안에서 얼마나 정확하게 표현되는지가 챗봇이 답할 수 있는 범위의 상한선이 됩니다. 현실의 상태 변화를 놓치지 않고 온톨로지에 반영하는 일, 즉 디지털 트윈을 촘촘하게 유지하는 일이 곧 챗봇의 정확도를 결정합니다.

서로 다른 도메인의 데이터를 하나의 지식그래프로 자산화할 것인가. Fleet Management System(FMS), Warehouse Management System(WMS), Transportation Management System(TMS), TAP은 원래 따로 운영되던 도메인입니다. 각자의 온톨로지로만 두면 "차량 문제가 배차에 미친 영향" 같은 질문에는 답할 수 없습니다. 도메인을 넘나드는 질의가 가능하려면, 서로 다른 팀이 만든 데이터를 하나의 지식그래프 문법으로 자산화하는 일이 먼저 필요합니다.

마치며

AI 루프가 판정·진단·개선안 도출까지 이어서 처리하면서, 온톨로지를 넓히는 작업의 병목은 사람의 판단 속도에서 루프가 도는 속도로 옮겨갔습니다. 그렇다고 AI가 데이터 분석가를 대체했다고 말하고 싶지는 않습니다. 정확히는, 분석가가 매번 반복하던 판정·진단·우선순위 작업을 기준만 명확히 세워두면 반복 가능한 형태로 바꿔둔 것에 가깝습니다. 기준을 세우는 일과 그 기준을 감사하는 일은 여전히 사람의 몫으로 남아 있고, 그 사이의 반복 노동만 사라졌습니다.

앞으로도 이 판정 체인이 놓치는 경계 케이스를 계속 찾아내면서, 사람이 감사해야 할 지점과 AI에게 맡길 수 있는 지점을 다시 나누는 작업을 이어갈 예정입니다.