Ontotext GraphDBRDFSPARQL을 준수하는 그래프 데이터베이스이자 지식 발견 도구입니다.
이 노트북은 LLM을 사용하여 Ontotext GraphDB에 대한 자연어 쿼리(NLQ to SPARQL, text2sparql이라고도 함)를 제공하는 방법을 보여줍니다.

GraphDB LLM 기능

GraphDB여기에 설명된 일부 LLM 통합 기능을 지원합니다: gpt-queries
  • 지식 그래프(KG)의 데이터를 사용하여 텍스트, 목록 또는 테이블을 LLM에 요청하는 magic predicates
  • 쿼리 설명
  • 결과 설명, 요약, 재구성, 번역
retrieval-graphdb-connector
  • 벡터 데이터베이스에서 KG 엔티티 인덱싱
  • 모든 텍스트 임베딩 알고리즘 및 벡터 데이터베이스 지원
  • GraphDB가 Elastic, Solr, Lucene에 사용하는 것과 동일한 강력한 connector(인덱싱) 언어 사용
  • RDF 데이터의 변경 사항을 KG 엔티티 인덱스에 자동 동기화
  • 중첩 객체 지원 (GraphDB 버전 10.5에서는 UI 지원 없음)
  • KG 엔티티를 다음과 같이 텍스트로 직렬화 (예: Wines 데이터셋의 경우):
Franvino:
- is a RedWine.
- made from grape Merlo.
- made from grape Cabernet Franc.
- has sugar dry.
- has year 2012.
talk-to-graph
  • 정의된 KG 엔티티 인덱스를 사용하는 간단한 챗봇
이 튜토리얼에서는 GraphDB LLM 통합을 사용하지 않고 NLQ에서 SPARQL 생성을 사용합니다. 여기에서 확인할 수 있는 Star Wars API(SWAPI) 온톨로지와 데이터셋을 사용합니다.

설정하기

실행 중인 GraphDB 인스턴스가 필요합니다. 이 튜토리얼은 GraphDB Docker 이미지를 사용하여 데이터베이스를 로컬에서 실행하는 방법을 보여줍니다. Star Wars 데이터셋으로 GraphDB를 채우는 docker compose 설정을 제공합니다. 이 노트북을 포함한 모든 필요한 파일은 GitHub 저장소 langchain-graphdb-qa-chain-demo에서 다운로드할 수 있습니다.
  • Docker를 설치합니다. 이 튜토리얼은 Docker Compose가 번들로 제공되는 Docker 버전 24.0.7을 사용하여 작성되었습니다. 이전 Docker 버전의 경우 Docker Compose를 별도로 설치해야 할 수 있습니다.
  • GitHub 저장소 langchain-graphdb-qa-chain-demo를 로컬 머신의 폴더에 클론합니다.
  • 동일한 폴더에서 다음 스크립트를 실행하여 GraphDB를 시작합니다
docker build --tag graphdb .
docker compose up -d graphdb
데이터베이스가 http://localhost:7200/에서 시작되기까지 몇 초 정도 기다려야 합니다. Star Wars 데이터셋 starwars-data.trig가 자동으로 langchain 저장소에 로드됩니다. 로컬 SPARQL 엔드포인트 http://localhost:7200/repositories/langchain을 사용하여 쿼리를 실행할 수 있습니다. 또한 선호하는 웹 브라우저에서 GraphDB Workbench http://localhost:7200/sparql을 열어 대화형으로 쿼리를 수행할 수 있습니다.
  • 작업 환경 설정
conda를 사용하는 경우 새로운 conda 환경을 생성하고 활성화합니다. 예:
conda create -n graph_ontotext_graphdb_qa python=3.12
conda activate graph_ontotext_graphdb_qa
다음 라이브러리를 설치합니다:
pip install jupyter==1.1.1
pip install rdflib==7.1.1
pip install langchain-community==0.3.4
pip install langchain-openai==0.2.4
다음 명령으로 Jupyter를 실행합니다
jupyter notebook

온톨로지 지정

LLM이 SPARQL을 생성할 수 있으려면 지식 그래프 스키마(온톨로지)를 알아야 합니다. OntotextGraphDBGraph 클래스의 두 가지 매개변수 중 하나를 사용하여 제공할 수 있습니다:
  • query_ontology: SPARQL 엔드포인트에서 실행되어 KG 스키마 문장을 반환하는 CONSTRUCT 쿼리입니다. 온톨로지를 자체 named graph에 저장하는 것을 권장하며, 이렇게 하면 관련 문장만 더 쉽게 가져올 수 있습니다(아래 예시 참조). DESCRIBE 쿼리는 지원되지 않습니다. DESCRIBE는 Symmetric Concise Bounded Description(SCBD), 즉 들어오는 클래스 링크도 반환하기 때문입니다. 수백만 개의 인스턴스가 있는 대규모 그래프의 경우 효율적이지 않습니다. github.com/eclipse-rdf4j/rdf4j/issues/4857을 확인하세요
  • local_file: 로컬 RDF 온톨로지 파일입니다. 지원되는 RDF 형식은 Turtle, RDF/XML, JSON-LD, N-Triples, Notation-3, Trig, Trix, N-Quads입니다.
어느 경우든 온톨로지 덤프는 다음을 포함해야 합니다:
  • 클래스, 속성, 클래스에 대한 속성 연결(rdfs:domain, schema:domainIncludes 또는 OWL 제약 사용), 분류 체계(중요한 개체)에 대한 충분한 정보를 포함합니다.
  • SPARQL 구성에 도움이 되지 않는 지나치게 장황하고 관련 없는 정의 및 예제는 포함하지 않습니다.
from langchain_community.graphs import OntotextGraphDBGraph

# feeding the schema using a user construct query

graph = OntotextGraphDBGraph(
    query_endpoint="http://localhost:7200/repositories/langchain",
    query_ontology="CONSTRUCT {?s ?p ?o} FROM [swapi.co/ontology/](https://swapi.co/ontology/) WHERE {?s ?p ?o}",
)
# feeding the schema using a local RDF file

graph = OntotextGraphDBGraph(
    query_endpoint="http://localhost:7200/repositories/langchain",
    local_file="/path/to/langchain_graphdb_tutorial/starwars-ontology.nt",  # change the path here
)
어느 방법이든 온톨로지(스키마)는 Turtle로 LLM에 제공됩니다. 적절한 접두사가 있는 Turtle이 가장 간결하고 LLM이 기억하기 가장 쉽기 때문입니다. Star Wars 온톨로지는 클래스에 대한 많은 특정 트리플을 포함한다는 점에서 다소 특이합니다. 예를 들어 종족 :Aleena<planet/38>에 살고, :Reptile의 하위 클래스이며, 특정 전형적인 특성(평균 키, 평균 수명, 피부색)을 가지고 있으며, 특정 개체(캐릭터)가 해당 클래스의 대표입니다:
@prefix : [swapi.co/vocabulary/](https://swapi.co/vocabulary/) .
@prefix owl: [www.w3.org/2002/07/owl#](http://www.w3.org/2002/07/owl#) .
@prefix rdfs: [www.w3.org/2000/01/rdf-schema#](http://www.w3.org/2000/01/rdf-schema#) .
@prefix xsd: [www.w3.org/2001/XMLSchema#](http://www.w3.org/2001/XMLSchema#) .

:Aleena a owl:Class, :Species ;
    rdfs:label "Aleena" ;
    rdfs:isDefinedBy [swapi.co/ontology/](https://swapi.co/ontology/) ;
    rdfs:subClassOf :Reptile, :Sentient ;
    :averageHeight 80.0 ;
    :averageLifespan "79" ;
    :character [swapi.co/resource/aleena/47](https://swapi.co/resource/aleena/47) ;
    :film [swapi.co/resource/film/4](https://swapi.co/resource/film/4) ;
    :language "Aleena" ;
    :planet [swapi.co/resource/planet/38](https://swapi.co/resource/planet/38) ;
    :skinColor "blue", "gray" .

    ...

이 튜토리얼을 간단하게 유지하기 위해 보안되지 않은 GraphDB를 사용합니다. GraphDB가 보안된 경우 OntotextGraphDBGraph 초기화 전에 환경 변수 ‘GRAPHDB_USERNAME’과 ‘GRAPHDB_PASSWORD’를 설정해야 합니다.
os.environ["GRAPHDB_USERNAME"] = "graphdb-user"
os.environ["GRAPHDB_PASSWORD"] = "graphdb-password"

graph = OntotextGraphDBGraph(
    query_endpoint=...,
    query_ontology=...
)

StarWars 데이터셋에 대한 질의응답

이제 OntotextGraphDBQAChain을 사용하여 몇 가지 질문을 할 수 있습니다.
import os

from langchain.chains import OntotextGraphDBQAChain
from langchain_openai import ChatOpenAI

# We'll be using an OpenAI model which requires an OpenAI API Key.
# However, other models are available as well:
# https://python.langchain.com/docs/integrations/chat/

# Set the environment variable `OPENAI_API_KEY` to your OpenAI API key
os.environ["OPENAI_API_KEY"] = "sk-***"

# Any available OpenAI model can be used here.
# We use 'gpt-4-1106-preview' because of the bigger context window.
# The 'gpt-4-1106-preview' model_name will deprecate in the future and will change to 'gpt-4-turbo' or similar,
# so be sure to consult with the OpenAI API https://platform.openai.com/docs/models for the correct naming.

chain = OntotextGraphDBQAChain.from_llm(
    ChatOpenAI(temperature=0, model_name="gpt-4-1106-preview"),
    graph=graph,
    verbose=True,
    allow_dangerous_requests=True,
)
간단한 질문을 해봅시다.
chain.invoke({chain.input_key: "What is the climate on Tatooine?"})[chain.output_key]


> Entering new OntotextGraphDBQAChain chain...
Generated SPARQL:
PREFIX : [swapi.co/vocabulary/](https://swapi.co/vocabulary/)
PREFIX rdfs: [www.w3.org/2000/01/rdf-schema#](http://www.w3.org/2000/01/rdf-schema#)

SELECT ?climate
WHERE {
  ?planet rdfs:label "Tatooine" ;
          :climate ?climate .
}

> Finished chain.
'The climate on Tatooine is arid.'
그리고 조금 더 복잡한 질문입니다.
chain.invoke({chain.input_key: "What is the climate on Luke Skywalker's home planet?"})[
    chain.output_key
]
> Entering new OntotextGraphDBQAChain chain...
Generated SPARQL:
PREFIX : [swapi.co/vocabulary/](https://swapi.co/vocabulary/)
PREFIX owl: [www.w3.org/2002/07/owl#](http://www.w3.org/2002/07/owl#)
PREFIX rdfs: [www.w3.org/2000/01/rdf-schema#](http://www.w3.org/2000/01/rdf-schema#)
PREFIX xsd: [www.w3.org/2001/XMLSchema#](http://www.w3.org/2001/XMLSchema#)

SELECT ?climate
WHERE {
  ?character rdfs:label "Luke Skywalker" .
  ?character :homeworld ?planet .
  ?planet :climate ?climate .
}

> Finished chain.
"The climate on Luke Skywalker's home planet is arid."
다음과 같이 더 복잡한 질문도 할 수 있습니다
chain.invoke(
    {
        chain.input_key: "What is the average box office revenue for all the Star Wars movies?"
    }
)[chain.output_key]
> Entering new OntotextGraphDBQAChain chain...
Generated SPARQL:
PREFIX : [swapi.co/vocabulary/](https://swapi.co/vocabulary/)
PREFIX xsd: [www.w3.org/2001/XMLSchema#](http://www.w3.org/2001/XMLSchema#)

SELECT (AVG(?boxOffice) AS ?averageBoxOfficeRevenue)
WHERE {
  ?film a :Film .
  ?film :boxOffice ?boxOfficeValue .
  BIND(xsd:decimal(?boxOfficeValue) AS ?boxOffice)
}


> Finished chain.
'The average box office revenue for all the Star Wars movies is approximately 754.1 million dollars.'

Chain 수정자

Ontotext GraphDB QA chain은 QA chain의 추가 개선과 앱의 전반적인 사용자 경험 향상을 위한 프롬프트 개선을 허용합니다.

”SPARQL Generation” 프롬프트

이 프롬프트는 사용자 질문과 KG 스키마를 기반으로 SPARQL 쿼리 생성에 사용됩니다.
  • sparql_generation_prompt 기본값:
      GRAPHDB_SPARQL_GENERATION_TEMPLATE = """
      Write a SPARQL SELECT query for querying a graph database.
      The ontology schema delimited by triple backticks in Turtle format is:
    
    Use only the classes and properties provided in the schema to construct the SPARQL query.
    Do not use any classes or properties that are not explicitly provided in the SPARQL query.
    Include all necessary prefixes.
    Do not include any explanations or apologies in your responses.
    Do not wrap the query in backticks.
    Do not include any text except the SPARQL query generated.
    The question delimited by triple backticks is:
    
    """
    GRAPHDB_SPARQL_GENERATION_PROMPT = PromptTemplate(
        input_variables=["schema", "prompt"],
        template=GRAPHDB_SPARQL_GENERATION_TEMPLATE,
    )
    

“SPARQL Fix” 프롬프트

때때로 LLM이 구문 오류나 누락된 접두사 등이 있는 SPARQL 쿼리를 생성할 수 있습니다. chain은 LLM에게 일정 횟수만큼 수정하도록 프롬프트하여 이를 수정하려고 시도합니다.
  • sparql_fix_prompt 기본값:
      GRAPHDB_SPARQL_FIX_TEMPLATE = """
      This following SPARQL query delimited by triple backticks
    
    is not valid.
    The error delimited by triple backticks is
    
    Give me a correct version of the SPARQL query.
    Do not change the logic of the query.
    Do not include any explanations or apologies in your responses.
    Do not wrap the query in backticks.
    Do not include any text except the SPARQL query generated.
    The ontology schema delimited by triple backticks in Turtle format is:
    
    """
    
    GRAPHDB_SPARQL_FIX_PROMPT = PromptTemplate(
        input_variables=["error_message", "generated_sparql", "schema"],
        template=GRAPHDB_SPARQL_FIX_TEMPLATE,
    )
    
  • max_fix_retries 기본값: 5

”Answering” 프롬프트

이 프롬프트는 데이터베이스에서 반환된 결과와 초기 사용자 질문을 기반으로 질문에 답변하는 데 사용됩니다. 기본적으로 LLM은 반환된 결과의 정보만 사용하도록 지시받습니다. 결과 집합이 비어 있으면 LLM은 질문에 답할 수 없다고 알려야 합니다.
  • qa_prompt 기본값:
      GRAPHDB_QA_TEMPLATE = """Task: Generate a natural language response from the results of a SPARQL query.
      You are an assistant that creates well-written and human understandable answers.
      The information part contains the information provided, which you can use to construct an answer.
      The information provided is authoritative, you must never doubt it or try to use your internal knowledge to correct it.
      Make your response sound like the information is coming from an AI assistant, but don't add any information.
      Don't use internal knowledge to answer the question, just say you don't know if no information is available.
      Information:
      {context}
    
      Question: {prompt}
      Helpful Answer:"""
      GRAPHDB_QA_PROMPT = PromptTemplate(
          input_variables=["context", "prompt"], template=GRAPHDB_QA_TEMPLATE
      )
    
GraphDB를 사용한 QA 작업을 마치면 Docker compose 파일이 있는 디렉토리에서 docker compose down -v --remove-orphans 를 실행하여 Docker 환경을 종료할 수 있습니다.
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I