Hybrid search - Retrieve from Graph and Embeddings.

Hybrid search - Retrieve from Graph and Embeddings.


Introduction

In today's digital world, the ability to extract and interact with information from various documents is crucial for businesses and individuals alike. Leveraging cutting-edge technologies like Streamlit, LangChain, and Neo4j, we can build sophisticated chatbots that not only retrieve information from documents but also integrate with knowledge graphs for enhanced insights. This article explores two powerful implementations of such chatbots. In earlier article link, I have provided information on how to build the Streamlit chatbot and integrate with Neo4J.

Take away from the article:-

After this article and the earlier article, readers will understand the concept on:-

  1. How to build Streamlit chatbot to create Neo4J graph
  2. How to perform Hybrid search RAG from Neo4J graph.
  3. How to build a chatbot to implement Hybrid search.
  4. Video tutorial of entire implementation.

1. How to build Streamlit chatbot to create Neo4J graph:-

This following highlighted implementation is already covered in another article. Please read that article and video URL to implement the chatbot, setup Neo4J, create Graph and show graph in chatbot.

Video URL of the the first implementation:-

2. Advanced RAG Chatbot with Neo4j Knowledge Graph

The second implementation enhances the basic RAG chatbot by integrating a Neo4j knowledge graph. This allows for more sophisticated data retrieval and visualization. We will implement Hybrid search techniques on the already created graph in Neo4J. This approach will combine search technique of Cypher query and Embedding similarity search.

Setting Up the Environment

To start, we load necessary libraries and environment variables, and set up Neo4j credentials. This ensures that the environment is correctly configured for the subsequent operations.

import os
import tempfile
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_community.graphs import Neo4jGraph
from dotenv import load_dotenv, find_dotenv
from langchain_groq import ChatGroq

class RAG_Graph:
    load_dotenv(find_dotenv())

    def __init__(self):
        os.environ["NEO4J_URI"] = "bolt://localhost:7687"
        os.environ["NEO4J_USERNAME"] = "neo4j"
        os.environ["NEO4J_PASSWORD"] = "password"
        self.graph = Neo4jGraph()
        self.llm = ChatGroq(temperature=0.5, groq_api_key=os.getenv("GROQ_API_KEY"), model_name="llama3-70b-8192")
        

Embedding with Vector Index

The create_vector_index function retrieves unstructured content from the index name 'vector' using HuggingFace embeddings.

def create_vector_index(self):
    model_name = 'sentence-transformers/all-mpnet-base-v2'
    self.vector_index = Neo4jVector.from_existing_index(
        HuggingFaceEmbeddings(model_name=model_name, model_kwargs={'device': 'cpu'}),
        url=os.environ["NEO4J_URI"],
        username=os.environ["NEO4J_USERNAME"],
        password=os.environ["NEO4J_PASSWORD"],
        index_name="vector",
    )
        

Preparing the Chat Template

The prepare_chat_template function sets up a chat prompt template, defining how the chatbot should extract and present information from the documents.

def prepare_chat_template(self):
    prompt = ChatPromptTemplate.from_messages(
        [
            ("system", "You are extracting fields and business rules from the text"),
            ("human", "Use this given format to extract the information from the following input: {question}"),
        ]
    )
    self.entity_chain = prompt | self.llm.with_structured_output(Entities)
        

Retrieving Data

The retriever function handles user queries by retrieving both structured and unstructured data. It combines the results into a final response.

def retriever(self, question: str):
    structure_data = self.structured_retriever(question)
    unstructured_data = [el.page_content for el in self.vector_index.similarity_search(question)]
    final_data = f"""Structured data:\n{structure_data}\nUnstructured data:\n{"#Document ".join(unstructured_data)}"""
    return final_data
        

Structured Data Retrieval

The structured_retriever function queries the Neo4j knowledge graph to retrieve structured data based on the user's question.

def structured_retriever(self, question: str) -> str:
    result = ""
    entities = self.entity_chain.invoke({"question": question})
    
    for entity in entities.names:
        response = self.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 50""",
            {"query": self.generate_full_text_query(entity)},
        )
        result += "\n".join([el['output'] for el in response])
    return result
        

Asking Questions

The ask_question_chain function orchestrates the entire process of creating vector indexes, preparing chat templates, and retrieving data to answer user questions. This function will be called from Streamlit UI python file.

def ask_question_chain(self, query):
    self.graph.query("CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]")
    self.create_vector_index()
    self.prepare_chat_template()

    template = """Answer the question based only on the following context\n{context}\n\nQuestion: {question}\nUser natural language and be concise.\nAnswer: """
    prompt = ChatPromptTemplate.from_template(template)

    chain = (
        RunnableParallel({"context": self.retriever, "question": RunnablePassthrough()})
        | prompt
        | self.llm
        | StrOutputParser()
    )
    result = chain.invoke(query)
    return result
        

Video tutorial:-

The video tutorial has explanations and entire coding. While watching the video you can also implement in your favorite editor.

Conclusion

By combining Streamlit, LangChain, and Neo4j, we can create a powerful chatbot capable of extracting and interacting with information from documents. This approach leverages both structured and unstructured data retrieval to provide comprehensive answers to user queries. Explore the full code and customize it to fit your specific needs. Happy coding!


Aditya Bagwadkar

Machine Learning Operations practitioner

4 个月

Thanks for sharing this. Very much useful to understand about RAG!

要查看或添加评论,请登录

社区洞察

其他会员也浏览了