
점술가에게 '아니오'라고 말하는 온톨로지(Ontology) 구축하기
요약
AWS의 Context Ontology Accelerator(COA)를 활용하여 AI 에이전트가 추측 대신 근거에 기반한 답변을 하도록 만드는 온톨로지 구축 과정을 다룹니다. 사주명리학이라는 독특한 도메인을 예시로 들어, 온톨로지가 어떻게 답변의 추적 가능성과 설명 가능성을 높이는지 실험합니다.
핵심 포인트
- 온톨로지는 도메인의 개념, 속성, 관계를 정의하는 기계 판독 가능한 모델임
- COA를 통해 AI 에이전트의 답변을 추측에서 검증 가능한 검색 기반으로 전환 가능
- 온톨로지 기반 시스템은 답변의 출처를 역으로 추적할 수 있는 설명 가능성을 제공함
- AWS 엔터프라이즈 아키텍처(Neptune, OpenSearch 등) 사용 시 높은 유휴 비용 발생 주의
AWS는 7월 말에 Context Ontology Accelerator (COA)라고 불리는 것을 조용히 출시했습니다. 핵심 제안은 이렇습니다. 이를 회사의 데이터베이스와 문서에 연결하면, 공식적인 온톨로지(Ontology)(클래스, 속성, 그래프)를 초안으로 작성하여 AI 에이전트가 추측을 멈추고 인용을 시작하게 만든다는 것입니다. 실제 적용 전에는 사람이 초안을 검토합니다. 일단 승인되면, 이를 쿼리하는 모든 에이전트는 모델이 하고 싶은 대로 말하는 답변 대신, 통제 가능하고 설명 가능한 (explainable) 답변을 받게 됩니다.
저는 이것을 실제로 구축할 때 어떤 느낌인지 확인해보고 싶었습니다. 다만 비즈니스 목적은 아니었습니다. 통제될 이유가 전혀 없는 무언가를 위해서였습니다. 바로 수백 년 동안 누구도 부정할 수 없는 선언들을 바탕으로 구축된 점술 시스템인 사주명리학(Shichuu Suimei, the Four Pillars of Destiny)입니다. 만약 온톨로지가 특정 도메인에 증거(receipts)를 제시하도록 강제한다면, 그 도메인의 증거가 존재하지 않을 때는 어떤 일이 벌어질까요?
온톨로지(Ontology)란 실제로 무엇인가
점술 이야기를 하기 전에, 이 단어 자체가 느슨하게 사용되는 경향이 있으므로 명확히 정의할 가치가 있습니다. 공학적 의미에서의 온톨로지는 철학 용어가 아닙니다. 그것은 도메인에 대한 공식적이고 기계가 읽을 수 있는 모델입니다. 여러분은 도메인의 개념들인 클래스(classes) 세트("오행(Five Elements)", "천간(Heavenly Stem)", "일간(Day Master)"), 속성(properties) 세트, 그리고 이들 사이의 타입화된 관계("생성한다(generates)", "~의 하위 분류이다(is classified under)", "~에 대응한다(corresponds to)") 세트, 그리고 이 모든 것을 연결하는 그래프(graph)를 정의합니다.
도메인이 그런 방식으로 표현되면, 이에 대한 질문은 더 이상 자유 형식의 LLM 추측이 아니게 됩니다. 그것은 특정하고 검사 가능한 노드(nodes)에 대한 검색(retrieval)이 되며, 답변은 정확히 어떤 노드를 사용했는지 역으로 가리킬 수 있습니다. 그것이 전체 가치 제안(value proposition)입니다. 더 똑똑한 답변이 아니라, 추적 가능한(traceable) 답변입니다.
월 930달러의 문제
월 930달러의 문제
COA의 참조 아키텍처(reference architecture)는 실제 엔터프라이즈 인프라입니다: 16개의 CDK 스택, 그래프를 위한 Amazon Neptune, 벡터를 위한 Amazon OpenSearch Serverless가 포함됩니다. 저는 무언가를 실행하기 전에 비용을 확인해 보았고, 프로젝트 자체 문서에는 다음과 같이 명확하게 명시되어 있었습니다: 유휴 비용(idle cost)은 대략 월 930달러입니다. 이를 운영 환경(production)에서 실행하는 기업에게는 완전히 합리적인 가격입니다. 하지만 몇 시간만 실행하는 주말 프로젝트용으로는 터무니없는 금액입니다.
그래서 코드를 작성하기 전에, 저는 더 작은 질문에 답해야 했습니다. 하부 인프라에 대한 비용을 지불하지 않고도, 문서로부터 온톨로지(ontology)를 초안 작성하는 Python 패키지인 COA의 실제 유도 파이프라인(induction pipeline)을 사용할 수 있을까?
답은 '아니오'였습니다. 비용과는 전혀 상관없는 이유 때문이었습니다. 초안 작성을 수행하는 패키지인 coa-ontology는 uv 워크스페이스 참조(workspace references)를 통해 AWS의 모노레포(monorepo) 내에 있는 두 개의 형제 패키지에 의존하고 있으며, 이 참조들은 모노레포 내부에서만 해결(resolve)됩니다. 유도 로직(induction logic)만 따로 pip install 할 수 있는 방법은 없습니다. 저장소 전체를 가져오거나, 아니면 아예 사용하지 못하거나 둘 중 하나입니다. 단 하나의 모듈을 벤더링(vendor)하기 위해 전체를 포크(fork)하는 것은, 작고 검사 가능한 상태를 유지하려는 프로젝트에 제가 원치 않는 바로 그 종류의 의존성처럼 느껴졌습니다.
제가 깔끔하게 가져올 수 있었던 것은 훨씬 더 작은 것이었습니다: 자체적인 의존성 없이 단일 파일에 위치한 두 개의 Protocol 정의인 GraphStore와 VectorStore였습니다. 이 파일은 하부에 무엇이 있는지 상관하지 않고 계약(contract)(클래스 저장, 속성 저장, 임베딩 검색, 정점의 이웃 가져오기)을 정의합니다. 그래서 저는 출처를 밝히고 이를 그대로 가져왔으며, 이를 바탕으로 나머지 모든 것을 처음부터 구축했습니다.
비싼 절반을 교체하기
벡터 저장소(vector store)는 Amazon S3 Vectors를 활용한 처음부터 직접 구현한(from-scratch) 방식으로 변경되었습니다. S3 Vectors는 2025년 12월에야 일반 사용 가능(GA) 상태가 되었으며, 일반적인 관리형 옵션과 비교했을 때 벡터 저장 및 쿼리 비용을 최대 90%까지 절감합니다. 유휴 비용(idle floor) 없이 요청당 비용을 지불합니다. 이를 구축하는 과정에서 몇 가지 실제 API 제한 사항을 해결해야 했습니다. 쓰기 호출당 500개의 벡터 제한, 목록 작업(list operation) 시 메타데이터 필터링 기능의 부재(쿼리 작업에서만 필터링이 가능하므로, 온톨로지(ontology)별 목록화는 클라이언트 측에서 페이지네이션과 필터링을 수행해야 함), 그리고 유사도 검색(similarity search) 시 distance 필드가 프로토콜(Protocol)이 기대하는 원시 유사도 점수가 아닌 1 - cosine_similarity를 의미한다는 점 등이 있었습니다. 사소한 부분이지만, 실제 응답과 대조하여 확인하지 않으면 소리 없이 오류를 일으키는 바로 그런 종류의 디테일입니다.
그래프 저장소(graph store)는 디스크의 단일 TriG 파일에 기록하는 rdflib로 변경되었습니다. 관리형 그래프 데이터베이스를 사용하지 않고, git에 직접 커밋합니다. 이는 상위 단계의 멀티 테넌트(multi-tenant) 카탈로그 UI가 필요로 하는 전체 인터페이스가 아니라, 제 파이프라인이 실제로 호출하는 소수의 프로토콜(Protocol) 메서드만을 구현합니다. 이는 실수로 누락된 것이 아니라, 명확하게 문서화된 범위 축소(scope cut)입니다.
최종 결과: 남은 AWS 비용은 Bedrock 토큰과 S3 Vectors 사용량뿐입니다. 삭제하는 것을 잊어버릴 걱정을 해야 하는 상시 인프라(standing infrastructure)는 없습니다.
온톨로지(ontology) 구축하기, 그리고 불공정한 질문 던지기
저는 십주수명(Shichuu Suimei)에 관한 다섯 가지 짧은 참조 문서를 작성했습니다: 오행(Five Elements)과 그 상생 및 상극 주기, 십간십이지(ten Heavenly Stems and twelve Earthly Branches), 사주(Four Pillars)와 일간(Day Master), 십신(Ten Gods), 그리고 천중살(Tenchūsatsu, 60갑자 주기 내 일주(day-pillar)의 위치와 관련된 "공망" 기간)입니다. 이를 Bedrock(Claude Sonnet 4.6)에 입력하자, JSON 제안서 형태로 59개의 클래스(class)와 16개의 속성(property)이 유도되었습니다. 아직 커밋된 것은 없으며, 검토를 위해 파일에 담겨 있는 초안 상태입니다.
나는 무엇인가를 승인하기 전에 수작업으로 직접 읽어보았습니다. 결과는 견고했습니다. 클래스 계층 구조(class hierarchy)는 소스 문서와 일치했고, 관계(relationships: generates, is-classified-under, corresponds-to)들은 내가 실제로 작성했던 것들이었으며, 무언가 지어낸 것은 없었습니다. 승인된 데이터는 Titan Text Embeddings V2를 거쳐 S3 Vectors에 75개의 벡터로 저장되었습니다.
그다음 나는 그것에게 불공정할 수도 있는 질문을 던졌습니다: '천살(Tenchūsatsu) 기간 동안에는 무엇이 일어나기로 되어 있는가?' 전통적으로 이 시기는 점술 텍스트가 가장 자신만만해지는 지점입니다. 공망(Void periods)은 노력이 아무 데도 닿지 않는 시기이며, 더 이상 밀어붙이지 말고 기다리라고 말하는 시기입니다. 나의 소스 문서도 그렇게 말하고 있었습니다. 하지만 해당 기간이 무엇인지에 대한 공식적인 정의와는 별개로, 그 기간에 무슨 일이 일어나는지에 대한 민속적 믿음(folk belief)이라는 그 구체적인 주장은 유도(induction) 과정 중 어떤 클래스의 comment 필드에도 포함되지 않았습니다. 그저 추출되지 않았을 뿐입니다.
에이전트(agent)의 답변:
천살(Tenchūsatsu)은 육십갑자(sixty-term cycle)에서 일주(day-pillar)의 십간(ten-term group)에 속하지 않는 두 지지(Earthly Branches)와 관련된 기간으로 정의됩니다. 하지만 제공된 컨텍스트에는 이 기간 동안 구체적으로 어떤 일이 일어나는지에 대한 설명이 없으므로, 가용한 증거를 바탕으로는 답변할 수 없습니다.
그것은 정의를 알고 있었습니다. 하지만 민속 신앙을 지어내지는 않았습니다. 이러한 거절이야말로 이 모든 연습의 실제 결과물입니다. 즉, 확신에 차 있지만 검증 불가능한 선언들로만 이루어진 도메인이 통제된 온톨로지(ontology)를 통해 경로를 설정하고, 명시적으로 그러한 선언을 거부하는 모습을 보여준 것입니다.
과정에서 무엇이 잘못되었나
다른 누군가의 시간을 한 시간 정도 아껴줄 수 있도록, 제가 겪었던 두 가지 문제점을 기록해 두려 합니다. 첫째, Bedrock Claude 모델을 모델 ID만으로 호출하는 방식은 더 이상 온디맨드 처리량 (on-demand throughput)에 작동하지 않습니다. 대신 추론 프로필 ID (inference profile ID)를 사용해야 합니다 (예: anthropic.claude-sonnet-4-6이 아니라 us.anthropic.claude-sonnet-4-6). 이 사실을 알려주는 에러 메시지는 실제로 부딪혀 보기 전까지는 명확하게 나타나지 않습니다. 둘째, boto3의 기본 읽기 제한 시간 (read timeout)은 60초인데, 이는 8,192개 토큰의 구조화된 JSON 응답을 요청하는 단일 유도 호출 (induction call)을 수행하기에는 너무 짧습니다. 첫 번째 실행 시, 제한 시간을 300초로 설정하기 전까지는 아무런 경고 없이 조용히 타임아웃이 발생했습니다.
두 가지 모두 생소한 문제는 아닙니다. 둘 다 실제 계정에서 직접 실행해 보아야만 발견할 수 있는 종류의 문제들이며, 이것이 바로 실제로 실행 가능한 수준으로 작게 무언가를 구축하는 이유이기도 합니다.
코드는 GitHub에 있습니다: yama3133/fortune-ontology. Apache 2.0 라이선스이며, 상위 COA와 동일합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
