Skip to content

Instantly share code, notes, and snippets.

@genecyber
Last active February 1, 2025 13:51
Show Gist options
  • Save genecyber/87cda6da30178d556b65e80ba413b41c to your computer and use it in GitHub Desktop.
Save genecyber/87cda6da30178d556b65e80ba413b41c to your computer and use it in GitHub Desktop.
Symbolic alternative to RAG with infinite scale

Thesis:

By representing prior conversations as symbols within a knowledge graph ($conversations), and using an internal method to traverse this graph, an LLM can provide contextually rich answers with lower overhead. This approach aims to mimic long-term memory more effectively than current methods.

Enhancements in AI/LLM Applications Using Knowledge Graphs

  • Contextual Understanding: By integrating knowledge graphs, an AI or LLM can leverage the structured relationships and properties of entities within the graph to better understand the context of user queries or content, leading to more accurate and relevant responses.
  • Data Efficiency: Knowledge graphs can streamline the data processing by directly using relationships between nodes (entities) rather than parsing large volumes of text to extract meaning. This can reduce computational overhead and speed up response times.
  • Continual Learning: A knowledge graph can be dynamically updated with new information, allowing the AI system to continually learn and adapt to new data without needing to retrain the model extensively. This makes the system more robust and up-to-date with current knowledge.
  • Multimodality: In applications that require the integration of different types of data (like text, images, and numbers), knowledge graphs can serve as a central framework that links these modalities together. For example, in a health AI, symptoms described in text can be linked to diagnostic images and quantitative test results.
  • Explainability and Transparency: AI systems powered by knowledge graphs can offer better explainability of their decisions and recommendations, as the decision-making process can be traced through the connections and nodes of the graph. This is particularly useful in domains requiring high trust and accountability, such as healthcare or finance.

This process ensures that the LLM dynamically informs the application about where to expand the knowledge graph, leveraging its capabilities to guide the expansion effectively and efficiently. This strategy supports the goal of utilizing a "long-term memory" style of interaction without the overhead of managing vast text directly within the LLM's operational context.

Benefits of Dynamic Linking

Flexibility: This approach allows the knowledge graph to remain flexible and adaptable, able to respond to the needs of the moment without requiring a rigidly predefined structure. Scalability: It reduces the overhead associated with manually linking every possible connection between conversations when the graph is initially built or updated. As the volume of data grows, the system can continue to discover new and relevant links without extensive reengineering. Relevance and Context-Awareness: By dynamically linking conversations based on the current query or context, the system can provide more precise and contextually appropriate responses. It avoids the clutter of irrelevant links that might arise from a more static linking approach.

Specification of the Interrogation Process

To interrogate the graph using natural language, the LLM could:

  • Parse the Query: Understand the intent and entities involved in the query.
  • Graph Query Initialization: Start at nodes most closely related to the query’s entities or concepts.
  • Traversal Decision Making: Use heuristic or learned strategies to decide the next nodes to visit based on relevance to the query, minimizing irrelevant expansions.
  • Inference and Synthesis: Infer connections and synthesize information from the visited nodes to formulate a response.

Applying Embeddings Over the Graph

Embeddings can be used to enhance the graph traversal process by:

  • Node Embedding: Assign embeddings to each node that encapsulate the semantic meaning of the conversations they represent. This allows for quicker similarity comparisons between the query’s semantic intent and node contents.
  • Edge Embedding: Embeddings on edges could represent the strength or nature of relationships, helping the model decide traversal paths.
  • Enhanced Traversal Algorithms: Use embedding-based similarity scores to prioritize which nodes to expand during graph traversal, potentially integrating techniques from neural network graph traversal algorithms.

1. Embedding Creation and Maintenance

Embeddings will be generated for each node and edge in the knowledge graph to capture the semantic essence of the conversations and their relationships. This process typically occurs during the graph's initial setup and as new nodes (conversations) are added.

# Pseudo-code for creating embeddings for nodes and edges
def create_embeddings(conversations):
    for conversation in conversations:
        node.embedding = embedding_model.encode(conversation.text)
        for relation in conversation.relations:
            edge.embedding = embedding_model.encode(relation.description)

2. Query Processing

When a new query arrives, it's first processed to understand its intent and extract relevant entities. This parsed query is then converted into an embedding.

def process_query(query):
    query_embedding = embedding_model.encode(query)
    return query_embedding

3. Graph Traversal Using Embeddings

The traversal process uses the query embedding to find the most relevant starting node(s) and explores the graph based on the similarity of node and edge embeddings to the query embedding.

def traverse_graph(query_embedding, graph):
    current_nodes = get_initial_nodes(query_embedding, graph)
    explored_info = []

    while current_nodes:
        next_nodes = []
        for node in current_nodes:
            if cosine_similarity(node.embedding, query_embedding) > threshold:
                explored_info.append(node)
                # Explore connected nodes
                for edge in node.edges:
                    if cosine_similarity(edge.embedding, query_embedding) > relation_threshold:
                        next_nodes.append(edge.target_node)
        current_nodes = next_nodes

    return synthesize_response(explored_info)

4. Natural Language LLM Prompts for Traversal and Exploration

To assist the traversal and exploration, we will define several LLM prompts that simulate natural language interactions, useful for querying and extracting information from the graph:

  • Initialize Query: "What are the main topics discussed in relation to [entity]?"
  • Expand Node: "Tell me more about [node-related topic]."
  • Compare Concepts: "How are [concept A] and [concept B] related?"
  • Summarize Information: "Summarize the discussions that involve [specific term]."

5. Example Scenario: Handling a New Query

Let's consider a scenario where the system handles a query about "recent advancements in AI". We'll mock prior conversations and show how the system uses the graph to respond.

# Mock prior conversations in the graph
graph.add_node("Discussion on AI basics", text="Basics of AI and machine learning.")
graph.add_node("AI advancements 2023", text="Recent advancements in AI as of 2023.")
graph.add_edge("Basics -> Advancements", source="Discussion on AI basics", target="AI advancements 2023", description="Follow-up on basics")

# New query arrives
query = "What are the latest advancements in AI?"
query_embedding = process_query(query)
response = LLM.completion(query, traverse_graph(query_embedding, graph))
print(response)

Synthesizing a Response

After graph traversal, the LLM synthesizes a response based on the explored nodes and their interrelations.

def synthesize_response(explored_info):
    # Combine information from all explored nodes
    combined_text = " ".join([node.text for node in explored_info])
    return llm.generate_response(combined_text, prompt="Summarize the key points discussed.")

outlines the embeddings' creation and use, natural language prompts for traversal, and a practical use case example. The approach is designed to make the LLM application smarter and more efficient in managing and utilizing extensive conversation histories.

Key Points of the Process:

  • Edge Content: Rather than containing full summaries, edges could include metadata like relationship type, main topics of connection, or even a brief descriptor that indicates the nature of the link between conversations (e.g., continuation, contradiction, elaboration).
  • Iterative Exploration: The system is designed to start with a broad, high-level view of the graph and iteratively delve deeper based on the relevance to the query. The LLM interacts with the graph by suggesting nodes and edges to expand based on initial and ongoing assessments of relevance to the query's intent.
  • Local Graph Management: The actual knowledge graph, which includes detailed nodes and edges, is maintained locally (not within the LLM). The LLM acts more as a guide and interpreter, directing which parts of the graph should be expanded based on the query and ongoing findings. This method limits the data scope actively considered at any point, reducing overhead.
  • No Full Passage of Conversations: At no point does the system require passing the entirety of all past conversations to the LLM. Instead, the LLM operates on embeddings and metadata, requesting more detailed information from specific parts of the graph as needed. This approach keeps the interaction manageable and focused.

Example of Iterative Process:

def handle_query(query, graph):
    query_embedding = process_query(query)
    current_nodes = get_initial_nodes(query_embedding, graph)
    
    # Initial exploration based on relevance
    relevant_info = initial_exploration(query_embedding, current_nodes)
    
    # Further exploration guided by LLM
    while LLM_suggests_expansion(relevant_info):
        new_focus = LLM_determine_focus(relevant_info)
        current_nodes = expand_nodes(new_focus, graph)
        relevant_info = update_exploration(query_embedding, current_nodes)
    
    return synthesize_response(relevant_info)

def LLM_determine_focus(relevant_info):
    # The LLM analyzes the current set of nodes and decides where to focus next
    focus_prompt = "Based on the current information, where should we focus next?"
    return llm.generate_decision(relevant_info, prompt=focus_prompt)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment