Let’s improve RAG using Knowledge Graph

What is RAG?

RAG, or Retrieval Augmented Generation, is a cutting-edge technique that combines the power of large language models (LLMs) with external knowledge sources. It allows LLMs to access and process information from another knowledge base, enhancing their ability to generate more informative, accurate, and contextually relevant responses.

What is Retriever in RAG

A crucial component of RAG is the retriever, responsible for fetching relevant information from the knowledge base based on the user’s query. The retriever’s effectiveness significantly impacts the overall quality of the response generated.

How can we improve the Retrieving Process

There are a few different strategies that can be used to optimize the retriever.

  • Vector Search: Employ semantic search techniques to understand the underlying meaning of the query, enabling more accurate information retrieval.
  • Contextual Understanding: Leverage contextual information to refine search results and prioritize relevant documents.
  • Feedback Loop: Implement a feedback mechanism to learn from user interactions and improve future retrievals.
  • Hybrid Retrieval: Combine different retrieval methods, such as keyword-based, semantic search, and graph search to achieve better results.

Graph Databases

Graph databases are specialized database systems designed to store, manage, and query data represented as graphs. In a graph database, data is modeled as nodes, edges, and properties.

  • Nodes represent entities or objects, such as people, products, or locations.
  • Edges represent relationships or connections between nodes, such as “friends with”, “bought”, or “located in”.
  • Properties are attributes or key-value pairs associated with nodes or edges, providing additional information like names, timestamps, or weights.

Knowledge Graphs

A knowledge graph is a structured representation of information, where entities and their relationships are interconnected. It provides a comprehensive view of the domain, making it easier to reason about and extract insights from the data.

Knowledge Graph RAG: Practical Guide

By integrating knowledge graphs with RAG, we can unlock the full potential of both technologies. Knowledge graphs can serve as a rich and structured knowledge base, enabling the retriever to efficiently locate relevant information. LLM can then leverage this information to generate more comprehensive and informative responses. postgres as a vector database

Setup and Installation

Installs the required Python libraries for working with LangChain, OpenAI, Neo4j, and other necessary tools.

!pip install langchain -q
!pip install langchain-community -q
!pip install langchain-openai -q
!pip install langchain-experimental -q
!pip install neo4j -q
Initialize OpenAI LLM

Sets up the OpenAI LLM model to generate responses.

from langchain_openai import ChatOpenAI

# set OpenAI API key
os.environ['OPENAI_API_KEY'] = "xxxxxxxxxxxxxxxxxxxxxxx"

# initialize the ChatOpenAI model
llm = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0
)
Initialize Embedding Model

Configures the OpenAI text embedding model for converting text data into vector representations.

from langchain_openai import OpenAIEmbeddings

embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
Load Data
from langchain.schema import Document

text = Document(page_content="""
Three students, A, B, and C, are tackling two subjects, X and Y. Each has a unique perspective, weaving their experiences into a shared academic journey. A, gifted in Mathematics, thrives on solving equations but struggles with the abstract world of poetry and storytelling. On the other hand, B shines in Literature, captivating others with a flair for creative writing, yet finds numbers daunting and formulas perplexing.
C, a generalist, performs decently in both subjects but often bridges gaps between A and B. While A helps B understand mathematical concepts, B guides A through essay writing. Meanwhile, C organizes group study sessions, offering real-world examples to connect ideas from X and Y, making both subjects more relatable. Their collaboration not only enhances their learning but fosters a sense of camaraderie, demonstrating the power of teamwork in overcoming challenges.
""")
Split Documents into Chunks

Divides the document into smaller chunks for processing using LangChain’s text splitter.

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(chunk_size=250,chunk_overlap=30)

chunks = splitter.split_documents([text])
Graph Initialization and Transformation

Sets up a Neo4j graph database, processes document chunks into nodes and relationships, and adds them to the graph.

from langchain_community.graphs import Neo4jGraph

os.environ["NEO4J_URI"] = "xxxxxxxxxxxxxxxxxx"
os.environ["NEO4J_USERNAME"] = "xxxxxxxxxxxxx"
os.environ["NEO4J_PASSWORD"] = "xxxxxxxxxxxxx"

# initialize Neo4j graph database
graph = Neo4jGraph()
from langchain_experimental.graph_transformers import LLMGraphTransformer

graph_transformer = LLMGraphTransformer(llm = llm)

graph_documents = graph_transformer.convert_to_graph_documents(chunks)

# add nodes and relationships to graph
graph.add_graph_documents(
    graph_documents,
    baseEntityLabel=True,
    include_source=True
)
from langchain_experimental.graph_transformers import LLMGraphTransformer

# indexing enables fast searches within text-based properties
def create_fulltext_index(g):
  cypher = "CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]"
  g.query(cypher)

create_fulltext_index(graph)
Querying the Graph and Entity Retrieval

Uses a LangChain template to extract specific entities (e.g., students and subjects) from the text and retrieves relationships from the graph.

from langchain_core.prompts import ChatPromptTemplate

entity_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system","You are extracting entities from the text.",
        ),
        (
            "human","Use the following information to extract entities"
            "input: {question}",
        ),
    ]
)
from pydantic import BaseModel, Field
from typing import List

class Entities(BaseModel):
    names: List[str] = Field(
        ...,
        description="All the entities that appear in the text",
    )

entity_chain = (
  entity_prompt
  | llm.with_structured_output(Entities)
)
Graph Retriever

Implements a function to query the graph for relevant data using full-text search and retrieves relationships between entities.

from langchain_community.vectorstores.neo4j_vector import remove_lucene_chars

def generate_full_text_query(input):
    full_text_query = ""
    words = [el for el in remove_lucene_chars(input).split() if el]
    for word in words[:-1]:
        full_text_query += f" {word}~2 AND"
    full_text_query += f" {words[-1]}~2"
    return full_text_query.strip()

def graph_retriever(question: str) -> str:
    result = ""
    entities = entity_chain.invoke({"question": question})
    for entity in entities.names:
        response = graph.query(
            """CALL db.index.fulltext.queryNodes('entity', $query, {limit:2})
            YIELD node,score
            CALL {
              WITH node
              MATCH (node)-[r:!MENTIONS]->(neighbor)
              RETURN node.id + ' - ' + type(r) + ' -> ' + neighbor.id AS output
              UNION ALL
              WITH node
              MATCH (node)<-[r:!MENTIONS]-(neighbor)
              RETURN neighbor.id + ' - ' + type(r) + ' -> ' +  node.id AS output
            }
            RETURN output LIMIT 20
            """,
            {"query": generate_full_text_query(entity)},
        )
        result += "\n".join([el['output'] for el in response])
    return result
Semantic Search Retriever

Sets up a semantic search index with Neo4j to find documents and entities similar to a query.

from langchain_community.vectorstores import Neo4jVector

vector_index = Neo4jVector.from_existing_graph(
    embedding_model,
    search_type="hybrid",
    node_label="Document",
    text_node_properties=["text"],
    embedding_node_property="embedding"
)
Combined Retriever (Graph Retriever + Semantic Retriever)
def retriever(question):
    graph_search_result = graph_retriever(question)
    semantic_search_result = [data.page_content for data in vector_index.similarity_search(question, k=2)]
    final_data = f"Graph data:{graph_search_result}\nText data:{' '. join(semantic_search_result)}"
    return final_data
Define Prompt Template for RAG

Creates a prompt template for generating answers based on retrieved data.

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system","Answer this question using the provided context only.",
        ),
        (
            "human","Context: {context}"
            "Question: {question}",
        ),
    ]
)
Create RAG Chain

Combines the retriever, prompt, and LLM into a Retrieval-Augmented Generation (RAG) chain to answer questions using both graph data and semantic search results.

from langchain_core.runnables import RunnablePassthrough

chain = (
    {
      "context": retriever,
      "question": RunnablePassthrough()
    }
    | prompt
    | llm
)
Invoke RAG Chain with Example Questions
response = chain.invoke("who learn both X and Y subjects")

print(response.content)

Conclusion

Knowledge Graph RAG represents a significant advancement in the field of AI. By combining the power of LLMs with the structured knowledge of graph databases, we can create more intelligent and informative systems. As technology continues to evolve, we can expect to see even more innovative applications of Knowledge Graph RAG in the future.

Share your love

Newsletter Updates

Enter your email address below and subscribe to our newsletter

Leave a Reply

Your email address will not be published. Required fields are marked *