article · 2026-06-16

하네스를 직접 짜려다 깨달았다: 그 고민은 이미 시맨틱 웹 언어에 있었다

evidence 검증 하네스를 hook과 스크립트로 짜려다, PROV-O와 SHACL이 더 나은 하네스라는 걸 알게 됐다. 데이터 구조에 대해 내가 새로 고민하던 것들은 이미 시맨틱 웹 언어에 녹아 있었다. 그 경험을 MSM(markdown-scaffolding-multihop) 스킬팩 v0.13.1에 녹여낸 과정을 정리한다.

요약

시장분석 copilot의 온톨로지에서, 모델이 내놓은 주장이 "어떤 근거에서, 어떤 과정으로, 누가 만들었는가"를 강제하는 evidence 검증 하네스가 필요했다. 처음엔 익숙한 방식으로 접근했다. 검증 스크립트를 짜고, 그걸 commit·CI hook에 걸어 변경마다 돌리는 일반적인 하네스 기법이다. 그런데 짜면 짤수록, 내가 스크립트 안에서 다시 정의하고 있는 것들(근거란 무엇인가, 파생이란 무엇인가, 무엇을 위반으로 볼 것인가)이 낯익었다. 그건 이미 시맨틱 웹이 PROV-O와 SHACL로 풀어둔 문제였다.

결론부터 말하면, hook과 스크립트로 쌓던 하네스보다 PROV-O와 SHACL을 쓴 쪽이 더 나은 하네스였다. 데이터 구조에 대한 사람들의 깊은 고민이 이미 그 언어들에 응축돼 있었기 때문이다. 그래서 나는 이 방식을 일회성으로 두지 않고 MSM(markdown-scaffolding-multihop) 스킬팩 v0.13.1에 녹여냈다. 이 글은 그 전환의 기록이다.


1. 원래 하려던 것: hook과 스크립트로 짠 하네스

검증 하네스를 짜는 가장 익숙한 길은 이렇다. "모든 주장은 출처를 가져야 한다", "숫자는 원문에 있어야 한다" 같은 규칙을 스크립트로 구현하고, 그 스크립트를 pre-commit hook이나 CI에 걸어 변경이 들어올 때마다 돌린다. 위반하면 빨간불이 켜진다. 도구로서는 멀쩡히 작동한다.

문제는 규칙이 늘어나는 방식이었다. 주장 유형이 하나 생기면 검사 함수가 하나 붙고, 출처 형태가 달라지면 파서 분기가 하나 늘었다. "이 출처가 주장의 직접 근거인지, 배경 참고인지"를 코드가 매번 추측해야 했고, 그 추측 로직이 스크립트마다 조금씩 달랐다. 근거와 주장 사이의 관계가 데이터에 없으니, 그 관계를 검사 시점에 코드로 매번 복원하고 있었던 셈이다. 하네스가 커질수록 검사의 진실은 데이터가 아니라 흩어진 스크립트 안에 있었다.


2. 그런데 PROV-O와 SHACL이 더 나은 하네스였다

hook + 스크립트 하네스PROV-O + SHACL 하네스
관계 위치검사 코드가 매번 복원데이터(그래프)에 명시
검증 형태명령형 함수선언형 제약(shape)
규칙 추가검사 함수가 늘어남노드/shape 한 번 선언 후 재사용
위반 판정스크립트마다 제각각제약 위반으로 일관

PROV-O는 출처를 적기 위한 W3C 어휘다. 결과물(Entity), 그걸 만든 과정(Activity), 책임지는 주체(Agent), 그리고 이 셋을 잇는 동사로 이뤄진다. 근거 발췌는 원천 문서에서 파생됐고(wasDerivedFrom), 분석은 그 근거를 사용했고(used), 주장은 그 분석으로 생성됐으며(wasGeneratedBy), 그 책임은 특정 모델에 귀속된다(wasAttributedTo). 전에 코드가 추측하던 "이 출처가 이 주장의 무엇인가"가, 이제 그래프에 그대로 적힌다.

SHACL은 그 그래프가 만족해야 할 모양(shape)을 선언하는 언어다. 검증이 명령형 함수에서 선언형 제약으로 바뀐다. 예를 들어 "source 귀속이 없는 owl:Class 정의는 통과시키지 않는다"를 shape 하나로 못박을 수 있다. 새 주장 유형이 생겨도 노드가 늘 뿐, 제약은 그대로 재사용된다. 검사의 진실이 스크립트가 아니라 데이터와 shape으로 돌아온다.

핵심은 둘의 분업이다. PROV-O가 "무엇을 어떻게 기록할지"를 주고, SHACL이 "그 기록이 무엇을 만족해야 하는지"를 준다. 내가 스크립트로 뒤섞어 짜던 두 가지가, 표준 안에서는 이미 깔끔하게 나뉘어 있었다.


3. 깨달음: 그 고민은 이미 언어에 녹아 있었다

여기서 든 생각이 이 글의 진짜 주제다. 하네스를 짜며 내가 새로 고민하던 것들(근거의 정체, 파생의 의미, 동일성, 무엇을 위반으로 볼 것인가)은 새 문제가 아니었다. 데이터 구조를 오래 다뤄온 사람들이 이미 깊이 파고든 문제였고, 그 결론이 PROV-O와 SHACL 같은 시맨틱 웹 언어에 응축돼 있었다.

PROV-O는 "출처를 어떻게 표현하는가"에 대한 누적된 합의이고, SHACL은 "그래프가 만족해야 할 제약을 어떻게 선언하고 검사하는가"에 대한 누적된 합의다. 내가 스크립트로 재발명하려던 게 정확히 이 둘이었다. 표준을 쓴다는 건 라이브러리를 가져다 쓰는 차원이 아니라, 그 문제에 대한 수년치 사고를 통째로 빌려 쓰는 것에 가까웠다.

이 엄밀함이 사람 중심 웹에서는 과해 보였다. 사람은 출처를 눈으로 보고 적당히 믿으니까. 그 전제가 뒤집힌 게 지금이다. 주장을 대량으로 그럴듯하게 만들어내는 주체가 사람에서 모델로 옮겨가면서, 검증도 생성 속도에 맞춰 기계화돼야 하고, 그러려면 출처가 텍스트 옆 각주가 아니라 기계가 따라갈 수 있는 구조여야 한다. 시맨틱 웹이 알고리즘 차원에서 미리 해둔 고민이, AI 시대에 와서 오히려 가장 실용적인 기본기가 된다.


4. 그래서 MSM 스킬팩에 녹였다 (v0.13.1)

이 방식을 copilot 한 곳의 일회성 처리로 두면 다음에 또 스크립트부터 짜게 된다. 그래서 markdown-scaffolding-multihop(MSM, https://github.com/WMJOON/markdown-scaffolding-multihop) 스킬팩에 하네스로 박아 넣었다. MSM은 마크다운 지식베이스를 그래프로 보고 검색·추론·구조화·유지보수를 묶어 다루는 시스템인데, v0.13.1에서 PROV-O 출력 레이어를 도입했다.

  • 출처 연결 강제: 개념 정의(classes.ttl, dct:identifier)를 근거(entities.jsonlsource_refs)와 조인한다. 정의와 근거가 데이터에서 묶인다.
  • provenance 산출: 그래프와 함께 *.prov.ttl(provenance)과 *.prov.shapes.ttl(검증 shape)을 생성한다.
  • SHACL 게이트: source 귀속이 없는 owl:Class 정의는 SHACL 검증에서 막힌다. 근거 없는 개념이 통과하지 못한다.

이때 하네스가 무겁기만 하면 안 된다. 솔직히 말하면, 온톨로지를 제대로 된 시스템으로 만드는 일과 그걸 내 목적에 맞게 활용하는 일 사이에는 분명한 간극이 있다. 학술적으로 완결된 온톨로지를 짓는 게 목표가 아니라, Agentic Workflow에서 쓸 만한 경량 온톨로지를 만드는 게 목표였다. 그 선택의 이유를 MSM의 설계 원칙이 그대로 담고 있다.

Bounded Rationality, Calibrated Validation 우리는 언제나 제한된 정보와 시간 안에서 판단한다. 즉, 모든 의사결정은 제한된 합리성(Bounded Rationality) 위에서 이루어진다. MSM은 이 전제를 기반으로, 무조건 깊은 검증이 아니라 인지 비용을 최소화하면서도 충분히 신뢰 가능한 판단을 가능하게 하는 구조를 지향한다. 검증 깊이를 고정하지 않고 Light · Medium · Deep 수준으로 조정 가능한 파라미터로 두며, 문제의 스케일과 의사결정 중요도에 따라 최적의 검증 수준을 선택한다.

이 원칙이 PROV-O·SHACL과 맞물리는 지점이 핵심이다. 두 표준은 검증의 형태를 흩어진 스크립트에서 데이터와 shape으로 옮겨주고, Bounded Rationality는 그 검증을 어디까지 강제할지를 상황에 맞게 정한다. 모든 개념을 Deep으로 검증하면 인지 비용이 폭발하고, 다 Light로 두면 신뢰가 무너진다. 그래서 검증 깊이를 끄고 켜는 파라미터로 둔다. 하네스가 부담이 아니라 기본기가 되는 건 바로 이 조절 가능성 덕분이다.


마무리

검증 하네스를 hook과 스크립트로 쌓고 있다면, 한 번 뒤집어 보면 좋겠다. 규칙을 코드로 구현하기 전에, 출처와 파생 관계를 데이터 모델에 먼저 새기는 것. 그 표현은 PROV-O가, 그 위의 검사는 SHACL이 이미 잘 다듬어 두었다. 우리가 새로 할 고민의 상당 부분은 데이터 구조를 오래 다뤄온 사람들이 시맨틱 웹 언어에 이미 녹여 두었고, AI 시대는 그 고민이 빛을 보는 시기다.

다만 표준을 가져다 쓴다고 끝이 아니다. 그걸 학술적 완결이 아니라 Agentic Workflow에서 굴러가는 경량 구조로 앉히는 것, 그리고 검증 깊이를 제한된 합리성 위에서 조절하는 것이 실제 활용의 몫이다. 나는 그 결론을 MSM v0.13.1에 박아 넣었고(https://github.com/WMJOON/markdown-scaffolding-multihop), 다음 프로젝트에서는 스크립트가 아니라 shape부터 적을 생각이다.