#!/usr/bin/env python3 """실습 3 (6-8): 에이전트의 도구로서의 온톨로지. 구조: 도구 4종(온톨로지 조회) + 계획-실행 루프 + 근거 붙은 답 합성. 계획기는 규칙 기반 축소 구현 — 실전에서는 LLM이 도구 설명을 읽고 스스로 호출을 결정한다(6-8 본문). 도구 층과 추적 로그는 실전과 동일하다.""" import rdflib, json, re g = rdflib.Graph().parse("psymap.owl") PRE = """PREFIX : PREFIX rdfs: PREFIX owl: PREFIX skos: PREFIX dct: """ 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 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] # ---------- 도구 층: 에이전트에게 공개되는 함수 4종 ---------- def tool_define(term): "용어의 정의와 출처를 돌려준다" iri = LEX.get(term.replace(" ", "")) if not iri: return {"error": "용어 없음"} d = list(g.query(PRE + f'SELECT ?d WHERE {{ <{iri}> skos:definition ?d }}')) s = list(g.query(PRE + f'SELECT ?s WHERE {{ <{iri}> dct:source ?s }}')) return {"정의": str(d[0][0]) if d else None, "출처": str(s[0][0])[:40] + "…" if s else "기재 없음"} def tool_tasks_for(term): "구성개념을 측정하는 과제 목록" iri = LEX.get(term.replace(" ", "")) if not iri: return {"error": "용어 없음"} rows = g.query(PRE + f"""SELECT ?t WHERE {{ ?t rdfs:subClassOf ?r . ?r owl:onProperty :measures ; owl:someValuesFrom <{iri}> . FILTER(isIRI(?t)) }}""") return {"과제": sorted(label_of(r[0]) for r in rows)} def tool_components(term): "구성 요소 목록" iri = LEX.get(term.replace(" ", "")) if not iri: return {"error": "용어 없음"} rows = g.query(PRE + f"""SELECT ?c WHERE {{ ?c rdfs:subClassOf ?r . ?r owl:onProperty :component_of ; owl:someValuesFrom <{iri}> . FILTER(isIRI(?c)) }}""") return {"구성 요소": sorted(label_of(r[0]) for r in rows)} def tool_ca_link(term): "Cognitive Atlas 대응 레코드(공용 좌표)" iri = LEX.get(term.replace(" ", "")) if not iri: return {"error": "용어 없음"} rows = list(g.query(PRE + f'SELECT ?m WHERE {{ <{iri}> skos:exactMatch ?m }}')) return {"CA": str(rows[0][0]).rsplit("/id/", 1)[-1].rstrip("/") if rows else "매핑 없음(미확인 기록)"} TOOLS = {"define": tool_define, "tasks_for": tool_tasks_for, "components": tool_components, "ca_link": tool_ca_link} # ---------- 계획-실행 루프 (계획기는 규칙 기반 축소 구현) ---------- def plan(question): term = next((t for t in sorted(LEX, key=len, reverse=True) if t in question.replace(" ", "")), None) steps = [] if term: steps.append(("define", term)) if re.search("측정|과제|어떻게 재", question): steps.append(("tasks_for", term)) if re.search("구성|요소", question): steps.append(("components", term)) if re.search("측정|과제", question): steps.append(("ca_link", term)) return term, steps def run(question): print(f"질문: {question}") term, steps = plan(question) evidence = {} for i, (tool, arg) in enumerate(steps, 1): out = TOOLS[tool](arg) evidence[tool] = out print(f" [{i}] 호출 {tool}({arg}) → {json.dumps(out, ensure_ascii=False)}") if not evidence: print(" 답: 온톨로지에 없는 용어 — 도구 없이는 답하지 않는다\n"); return parts = [] if "define" in evidence: parts.append(f"{term}은(는) {evidence['define']['정의']}") if "tasks_for" in evidence: parts.append(f"대표 측정 과제는 {', '.join(evidence['tasks_for']['과제'])}이다") if "ca_link" in evidence: parts.append(f"공용 좌표는 Cognitive Atlas {evidence['ca_link']['CA']}") print(" 답(근거 부착):", ". ".join(parts), f"[출처: {evidence['define']['출처']}]\n") run("작업기억이 무엇이고, 어떻게 측정하는가?") run("기억 응고화란?")