#!/usr/bin/env python3
"""실습 2 (6-7): 자연어 → SPARQL 체인.
구조: ① 용어 연결(라벨·altLabel → IRI) ② 질문 유형 분류 → 질의 틀 선택
③ 틀 채우기 ④ 실행. 분류기는 규칙 기반 축소 구현 — 실제 체인에서는
②를 LLM이 맡는다(프롬프트는 6-7 본문). ①③④는 실전과 동일하다."""
import rdflib, re
g = rdflib.Graph().parse("psymap.owl")
PRE = """PREFIX :
PREFIX rdfs:
PREFIX owl:
PREFIX skos:
"""
# ---------- ① 용어 연결: 라벨과 altLabel로 IRI 사전 구축 ----------
LEX = {}
for p in ("prefLabel", "altLabel"):
for c, l in g.query(PRE + f'SELECT ?c ?l WHERE {{ ?c skos:{p} ?l . FILTER(lang(?l)="ko") }}'):
LEX[str(l).replace(" ", "")] = c
def link(question):
found = [(t, iri) for t, iri in LEX.items() if t in question.replace(" ", "")]
return max(found, key=lambda x: len(x[0])) if found else (None, None)
# ---------- ② 질문 유형 → ③ 질의 틀 ----------
TEMPLATES = {
"측정과제": ("측정하는 과제",
"SELECT ?x WHERE {{ ?x rdfs:subClassOf ?r . ?r owl:onProperty :measures ; owl:someValuesFrom <{iri}> . FILTER(isIRI(?x)) }}"),
"구성요소": ("구성 요소",
"SELECT ?x WHERE {{ ?x rdfs:subClassOf ?r . ?r owl:onProperty :component_of ; owl:someValuesFrom <{iri}> . FILTER(isIRI(?x)) }}"),
"하위유형": ("하위 유형|종류",
"SELECT ?x WHERE {{ ?x rdfs:subClassOf+ <{iri}> . FILTER(isIRI(?x)) }}"),
"정의": ("정의|무엇인가|뭐야",
"SELECT ?x WHERE {{ <{iri}> skos:definition ?x }}"),
}
def classify(question):
for kind, (pat, tpl) in TEMPLATES.items():
if re.search(pat, question):
return kind, tpl
return None, None
def label_of(iri):
r = list(g.query(PRE + f'SELECT ?l WHERE {{ <{iri}> skos:prefLabel ?l . FILTER(lang(?l)="ko") }}'))
return str(r[0][0]) if r else str(iri).split("/")[-1]
# ---------- ④ 체인 실행 ----------
def answer(question):
term, iri = link(question)
if iri is None:
return "거절: 질문의 용어가 온톨로지에 없다 — 답을 지어내지 않는다"
kind, tpl = classify(question)
if kind is None:
return f"거절: '{term}'은 알지만 질문 유형이 질의 틀 밖이다"
rows = list(g.query(PRE + tpl.format(iri=iri)))
vals = sorted(label_of(r[0]) if isinstance(r[0], rdflib.URIRef) else str(r[0]) for r in rows)
return f"[{kind}·{term}] {vals}"
for q in ["작업기억을 측정하는 과제는?",
"작동기억의 구성 요소를 알려줘", # altLabel 경유
"서술기억의 하위 유형은?",
"응고화가 뭐야?",
"작업기억 용량을 늘리는 훈련법은?"]: # 범위 밖
print(f"Q: {q}\nA: {answer(q)}\n")