Mistral-7b를 사용해서 정보에 입각한 응답 생성을 위한 정보 합성을 최적화하기 위한 Neo4j Vector Index 및 GraphCypherQAChain
정보 검색 및 지식 추출은 점점 더 중요해지는 분야인데요, 특히 multi-hop 질문 응답과 관련해서요. LLM(Large Language Model)과 Knowledge Graph의 등장은 정말 눈에 띄는 변화를 가져왔죠.
이 시스템은 Vector Similarity Search를 사용해서 구조화되지 않은 정보를 검색하고, 동시에 Graph Database에 접근해서 구조화된 데이터를 검색해요. 이렇게 함으로써 응답이 포괄적일 뿐만 아니라 검증된 지식에 기반하도록 보장하는 거죠.
이 접근 방식은 단일 `Query`가 여러 하위 질문으로 나뉠 수 있고, 정확한 답변을 생성하기 위해 수많은 문서의 정보가 필요할 수 있는 multi-hop 질문을 해결하는 데 특히 중요해요.

데이터가 정말 풍부하고 복잡한 시대에, 이 시스템은 사용자 쿼리에 광범위한 지식과 검증된 정확성을 완벽하게 섞어서 답변을 제공함으로써 비정형 데이터와 정형 Knowledge Graph 간의 간극을 해소하는 중요한 도구에요.
마지막 단계로, 시스템은 검색된 비구조화 정보와 구조화 정보를 새로운 Large Language Model에 전달해요. 미스트랄-7b는 텍스트 생성을 담당하죠. 이러한 통합을 통해 생성된 응답은 모델에 캡슐화된 방대한 지식을 통해 정보를 얻을 뿐만 아니라, Vector Embedding 및 Graph Database에서 검색된 특정 실시간 데이터를 통해 Fine-tuning 및 강화되어 미묘하고 정확하며 상황에 맞는 사용자 경험을 제공할 수 있어요.
Neo4j Vector Index
Neo4j Vector Index는 특히 구조화된 데이터와 구조화되지 않은 데이터를 모두 처리하는 데 있어 Retrieval-Augmented Generation(RAG) 애플리케이션 영역에서 중요한 도구로 떠오르고 있어요. Langchain 라이브러리는 LLM(Large Language Model) 애플리케이션 구축을 위한 훌륭한 프레임워크인데, Neo4j Vector Index에 대한 완전한 지원을 통합해서 RAG 애플리케이션에서 데이터 수집 및 쿼리를 간소화하죠.
이러한 통합은 Neo4j Vector Index에 대한 효율적인 데이터 수집을 촉진하고, 구조화된 데이터와 구조화되지 않은 데이터를 모두 활용해서 실시간, 정확하고 상황에 맞는 답변을 제공하는 효과적인 RAG 애플리케이션의 구축을 가능하게 해요.
GraphCypherQAChain
The GraphCypherQAChain 클래스는 Natural Language 질문을 사용해서 Graph Database, 특히 Neo4j를 쿼리하는 데 유용한 기능을 제공해요. LLM을 사용해서 입력 질문에서 Cypher 쿼리를 생성하고 Neo4j Graph Database에 대해 실행하며, 쿼리 결과를 기반으로 답변을 제공하죠.
이 기능을 사용하면 사용자는 복잡한 Cypher 쿼리를 작성할 필요 없이 특정 데이터를 검색할 수 있으므로, Graph Database에 저장된 데이터에 더 쉽게 접근하고 상호작용할 수 있어요.
미스트랄 7B
미스트랄 7B는 아래 그림에서 볼 수 있듯이 다양한 벤치마크에서 놀라운 성능을 인정받은 최신 Large Language Model이에요. 다양한 언어 작업 및 쿼리를 처리하는 데 능숙함을 보여주죠.
Retrieval-Augmented Generation(RAG) 아키텍처에서 Mistral 7B는 Vector Embedding 및 그래프 검색에서 검색된 정보를 기반으로 텍스트를 합성하고 생성해서, 출력이 컨텍스트가 풍부할 뿐만 아니라 사용자 쿼리에 정확하게 맞춰지도록 하는 중요한 역할을 해요. 이는 비정형 데이터와 정형 Knowledge Graph 사이의 간극을 효과적으로 메워서, 사전 훈련된 지식과 실시간으로 검증된 데이터가 혼합된 답변을 제공하죠.

구현
종속성을 설치하는 것부터 시작해 볼까요?
제 GitHub 저장소에서 완전한 Jupyter Notebook을 확인해 보세요.
%pip install langchain openai wikipedia tiktoken neo4j python-dotenv transformers
%pip install -U sagemaker
Neo4j Vector Index
먼저 필수 라이브러리 및 모듈을 가져오고, 데이터 세트 준비, Neo4j 벡터 인덱스와의 인터페이스를 위한 기반을 설정하고 Mistral 7B의 텍스트 생성 기능을 사용합니다. dotenv를 사용하면 환경 변수를 안전하게 로드하여 OpenAI API 및 Neo4j 데이터베이스에 대한 민감한 자격 증명을 보호할 수 있어요.
import os
import re
from langchain.vectorstores.neo4j_vector import Neo4jVector
from langchain.document_loaders import WikipediaLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
from dotenv import load_dotenv
load_dotenv()
os.environ["OPENAI_API_KEY"] = os.getenv('OPENAI_API_KEY')
os.environ["NEO4J_URI"] = os.getenv('NEO4J_URI')
os.environ["NEO4J_USERNAME"] = os.getenv('NEO4J_USERNAME')
os.environ["NEO4J_PASSWORD"] = os.getenv('NEO4J_PASSWORD')
여기서는 실험을 위해 Wikipedia 페이지를 사용하기로 했어요. 텍스트를 토큰화하기 위해 bert-base-uncased 모델을 사용하는데요. WikipediaLoader는 지정된 페이지의 원시 콘텐츠를 로드한 다음 LangChain의 RecursiveCharacterTextSplitter를 사용하여 더 작은 텍스트 조각으로 나눕니다.
이 스플리터는 각 청크가 20개의 토큰이 겹치는 200개의 토큰으로 최대화되어 모델 삽입에 대한 컨텍스트 창 제한을 준수하고 컨텍스트의 연속성이 손실되지 않도록 해줘요.
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
def bert_len(text):
tokens = tokenizer.encode(text)
return len(tokens)
raw_documents = WikipediaLoader(query="Leonhard Euler").load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 200,
chunk_overlap = 20,
length_function = bert_len,
separators=['nn', 'n', ' ', ''],
)
documents = text_splitter.create_documents([raw_documents[0].page_content])
청크된 문서는 Neo4j 벡터 인덱스에 Node로 인스턴스화됩니다. Neo4j Graph Database와 OpenAI 임베딩의 핵심 기능을 사용하여 이 벡터 인덱스를 구성하는 거죠.
# Instantiate Neo4j vector from documents
neo4j_vector = Neo4jVector.from_documents(
documents,
OpenAIEmbeddings(),
url=os.environ["NEO4J_URI"],
username=os.environ["NEO4J_USERNAME"],
password=os.environ["NEO4J_PASSWORD"]
)
벡터 인덱스에 문서를 수집한 후 샘플 사용자 Query에 대한 벡터 유사성 검색을 수행하고 가장 유사한 상위 2개 문서를 검색합니다.
query = "Who were the siblings of Leonhard Euler?"
vector_results = neo4j_vector.similarity_search(query, k=2)
for i, res in enumerate(vector_results):
print(res.page_content)
if i != len(vector_results)-1:
print()
vector_result = vector_results[0].page_content

Knowledge Graph 구축
다음에서 큰 영감을 받았는데요. NaLLM 프로젝트에서 우리는 오픈 소스 프로젝트를 사용하여 구조화되지 않은 데이터로부터 Knowledge Graph를 구성합니다.
다음은 Leonhard Euler의 Wikipedia 기사에서 가져온 단일 문서 청크를 사용하여 구성된 Knowledge Graph입니다.

프로젝트를 깊이 있게 공부한 후 LLM을 사용하여 Knowledge Graph를 구축하는 방법에 대해 많은 것을 배웠어요. 예를 들어, 구조화되지 않은 텍스트에서 엔터티와 관계를 캡처하라는 메시지는 다음과 같아요.
"""
You are a data scientist working for a company that is building a graph database. Your task is to extract information from data and convert it into a graph database.
Provide a set of Nodes in the form [ENTITY_ID, TYPE, PROPERTIES] and a set of relationships in the form [ENTITY_ID_1, RELATIONSHIP, ENTITY_ID_2, PROPERTIES].
It is important that the ENTITY_ID_1 and ENTITY_ID_2 exists as nodes with a matching ENTITY_ID. If you can't pair a relationship with a pair of nodes don't add it.
When you find a node or relationship you want to add try to create a generic TYPE for it that describes the entity you can also think of it as a label.
Example:
Data: Alice lawyer and is 25 years old and Bob is her roommate since 2001. Bob works as a journalist. Alice owns a the webpage www.alice.com and Bob owns the webpage www.bob.com.
Nodes: ["alice", "Person", {"age": 25, "occupation": "lawyer", "name":"Alice"}], ["bob", "Person", {"occupation": "journalist", "name": "Bob"}], ["alice.com", "Webpage", {"url": "www.alice.com"}], ["bob.com", "Webpage", {"url": "www.bob.com"}]
Relationships: ["alice", "roommate", "bob", {"start": 2021}], ["alice", "owns", "alice.com", {}], ["bob", "owns", "bob.com", {}]
"""
흥미롭고 동시에 개선할 수 있는 기능이 많이 있죠?
Neo4j DB QA 체인
다음으로 Neo4j DB QA 체인을 설정하는 데 필요한 라이브러리를 가져와 볼게요.
from langchain.chat_models import ChatOpenAI
from langchain.chains import GraphCypherQAChain
from langchain.graphs import Neo4jGraph
그래프가 구성되면 Neo4jGraph 인스턴스에 연결하고 스키마를 시각화해야 해요.
graph = Neo4jGraph(
url=os.environ["NEO4J_URI"], username=os.environ["NEO4J_USERNAME"], password=os.environ["NEO4J_PASSWORD"]
)
print(graph.schema)
Node properties are the following:
[{'labels': 'Person', 'properties': [{'property': 'name', 'type': 'STRING'},
{'property': 'nationality', 'type': 'STRING'},
{'property': 'death_date', 'type': 'STRING'},
{'property': 'birth_date', 'type': 'STRING'}]},
{'labels': 'Location', 'properties': [{'property': 'name', 'type': 'STRING'}]},
{'labels': 'Organization', 'properties': [{'property': 'name', 'type': 'STRING'}]},
{'labels': 'Publication', 'properties': [{'property': 'name', 'type': 'STRING'}]}]
Relationship properties are the following:
[]
The relationships are the following:
['(:Person)-[:worked_at]->(:Organization)',
'(:Person)-[:influenced_by]->(:Person)',
'(:Person)-[:born_in]->(:Location)',
'(:Person)-[:lived_in]->(:Location)',
'(:Person)-[:child_of]->(:Person)',
'(:Person)-[:sibling_of]->(:Person)',
'(:Person)-[:published]->(:Publication)']
GraphCypherQAChain은 모든 세부 사항을 추상화하고 Natural Language Question (NLQ)에 대한 자연어 응답을 출력합니다. 하지만 내부적으로는 LLM을 사용해서 해당 질문에 대한 Cypher 쿼리를 생성하고, Graph Database에서 결과를 검색한 다음, 마지막으로 해당 결과를 사용해서 다시 LLM을 사용해 최종 자연어 응답을 생성하는 방식이에요.
chain = GraphCypherQAChain.from_llm(
ChatOpenAI(temperature=0), graph=graph, verbose=True
)
graph_result = chain.run("Who were the siblings of Leonhard Euler?")

graph_result
'The siblings of Leonhard Euler were Maria Magdalena and Anna Maria.'
Mistral-7b-Instruct
AWS SageMaker 환경 내 Hugging Face에서 Mistral-7B 엔드포인트를 설정했어요.
import json
import sagemaker
import boto3
from sagemaker.huggingface import HuggingFaceModel, get_huggingface_llm_image_uri
try:
role = sagemaker.get_execution_role()
except ValueError:
iam = boto3.client('iam')
role = iam.get_role(RoleName='sagemaker_execution_role')['Role']['Arn']
hub = {
'HF_MODEL_ID':'mistralai/Mistral-7B-Instruct-v0.1',
'SM_NUM_GPUS': json.dumps(1)
}
huggingface_model = HuggingFaceModel(
image_uri=get_huggingface_llm_image_uri("huggingface",version="1.1.0"),
env=hub,
role=role,
)
최종 응답은 지침, Vector Index의 관련 데이터, Graph Database의 관련 정보, 사용자 Query를 포함하는 Prompt를 구성해서 만들어져요.
그런 다음 이 Prompt는 제공된 정보를 기반으로 의미 있고 정확한 응답을 생성하는 Mistral-7b 모델로 전달되죠.
mistral7b_predictor = huggingface_model.deploy(
initial_instance_count=1,
instance_type="ml.g5.4xlarge",
container_startup_health_check_timeout=300,
)
query = "Who were the siblings of Leonhard Euler?"
final_prompt = f"""You are a helpful question-answering agent. Your task is to analyze
and synthesize information from two sources: the top result from a similarity search
(unstructured information) and relevant data from a graph database (structured information).
Given the user's query: {query}, provide a meaningful and efficient answer based
on the insights derived from the following data:
Unstructured information: {vector_result}.
Structured information: {graph_result}.
"""
response = mistral7b_predictor.predict({
"inputs": final_prompt,
})
print(re.search(r"Answer: (.+)", response[0]['generated_text']).group(1))
The siblings of Leonhard Euler were Maria Magdalena and Anna Maria.
주요 시사점
Neo4j Vector Index 및 GraphCypherQAChain을 Mistral-7b와 통합하면 방대한 비정형 데이터와 복잡한 그래프 지식 사이의 격차를 효과적으로 연결하는 복잡한 데이터 처리를 위한 강력한 시스템을 제공하고, 두 데이터 소스의 정보를 통합해서 사용자 Query에 대한 포괄적이고 정확한 응답을 제공해요.
마지막으로, 빠르게 성장하는 AI 분야에 적응하기 위해 초기에 모듈식 파이프라인을 구축해야 했기 때문에 향후 실험에서 멀티홉 Query를 실험하는 것을 목표로 하고 있어요.
요약
이 프로젝트는 Neo4j Vector Index와 LangChain의 GraphCypherQAChain의 강력한 조합을 강조하면서, 구조화되지 않은 데이터와 그래프 지식을 각각 탐색한 다음 Mistral-7b를 사용해서 정보에 입각한 정확한 응답을 생성해요.
관련 정보를 검색하기 위해 Neo4j를 사용함으로써Vector Index와 Graph Database 모두, 시스템은 생성된 응답이 상황적으로 풍부할 뿐만 아니라 검증된 실시간 지식에 기반을 두도록 보장하죠.
구현은 다양한 데이터 소스의 합성 정보를 활용해서 사전 훈련된 지식과 특정 실시간 데이터가 조화롭게 혼합된 응답을 생성함으로써 사용자 Query에 대한 응답의 정확성과 관련성을 향상시키는 Retrieval-Augmented Generation의 실제 적용을 보여주고 있어요.
참고자료
- Neo4j 및 Large Language Model(LLM)
- Knowledge Graph 및 LLM: Neo4j로 Large Language Model 활용
- Knowledge Graph 및 LLM: Fine-tuning과 비교. Retrieval-Augmented Generation
- Knowledge Graph 및 LLM: 다중 홉 질문 답변
- LangChain 라이브러리, Neo4j Vector Index에 대한 완벽한 지원 추가
- 미스트랄 7B
- LLM을 사용하여 원시 텍스트로 Knowledge Graph 구성 데모
- Langchain