From f24594c4dd38f7cf827bac9eee26f3135cc86cea Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Thu, 14 Nov 2024 16:22:46 +0200 Subject: [PATCH 01/15] configure-prompts --- graphrag_sdk/chat_session.py | 16 ++++++++++++++-- graphrag_sdk/kg.py | 8 +++++--- graphrag_sdk/steps/graph_query_step.py | 23 ++++++++++++++--------- graphrag_sdk/steps/qa_step.py | 8 +++++--- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 6e392804..8ad865be 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -25,7 +25,9 @@ class ChatSession: >>> chat_session.send_message("What is the capital of France?") """ - def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, graph: Graph): + def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, graph: Graph, + cypher_system_instruction: str = None, qa_system_instruction: str = None, + cypher_gen_prompt: str = None, qa_prompt: str = None): """ Initializes a new ChatSession object. @@ -44,13 +46,21 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, self.model_config = model_config self.graph = graph self.ontology = ontology + if cypher_system_instruction is None: + cypher_system_instruction = CYPHER_GEN_SYSTEM.replace("#ONTOLOGY", str(ontology.to_json())) + else: + cypher_system_instruction = cypher_system_instruction + "\nOntology:\n" + str(ontology.to_json()) + + self.cypher_prompt = cypher_gen_prompt + self.qa_prompt = qa_prompt + self.cypher_chat_session = ( model_config.cypher_generation.with_system_instruction( CYPHER_GEN_SYSTEM.replace("#ONTOLOGY", str(ontology.to_json())) ).start_chat() ) self.qa_chat_session = model_config.qa.with_system_instruction( - GRAPH_QA_SYSTEM + qa_system_instruction or GRAPH_QA_SYSTEM ).start_chat() self.last_answer = None @@ -69,6 +79,7 @@ def send_message(self, message: str): chat_session=self.cypher_chat_session, ontology=self.ontology, last_answer=self.last_answer, + cypher_prompt=self.cypher_prompt, ) (context, cypher) = cypher_step.run(message) @@ -78,6 +89,7 @@ def send_message(self, message: str): qa_step = QAStep( chat_session=self.qa_chat_session, + qa_prompt=self.qa_prompt, ) answer = qa_step.run(message, cypher, context) diff --git a/graphrag_sdk/kg.py b/graphrag_sdk/kg.py index 42562fa1..3c3e53fa 100644 --- a/graphrag_sdk/kg.py +++ b/graphrag_sdk/kg.py @@ -134,9 +134,11 @@ def delete(self) -> None: for key in self.__dict__.keys(): setattr(self, key, None) - def chat_session(self) -> ChatSession: - return ChatSession(self._model_config, self.ontology, self.graph) - + def chat_session(self, cypher_system_instruction: str = None, qa_system_instruction: str = None, + cypher_gen_prompt: str = None, qa_prompt: str = None) -> ChatSession: + chat_session = ChatSession(self._model_config, self.ontology, self.graph, cypher_system_instruction, + qa_system_instruction, cypher_gen_prompt, qa_prompt) + return chat_session def add_node(self, entity: str, attributes: dict): """ Add a node to the knowledge graph, checking if it matches the ontology diff --git a/graphrag_sdk/steps/graph_query_step.py b/graphrag_sdk/steps/graph_query_step.py index 9b538461..d21a1fc2 100644 --- a/graphrag_sdk/steps/graph_query_step.py +++ b/graphrag_sdk/steps/graph_query_step.py @@ -33,12 +33,14 @@ def __init__( chat_session: GenerativeModelChatSession, config: dict = None, last_answer: str = None, + cypher_prompt: str = None, ) -> None: self.ontology = ontology self.config = config or {} self.graph = graph self.chat_session = chat_session self.last_answer = last_answer + self.cypher_prompt = cypher_prompt def run(self, question: str, retries: int = 5): error = False @@ -46,15 +48,18 @@ def run(self, question: str, retries: int = 5): cypher = "" while error is not None and retries > 0: try: - cypher_prompt = ( - (CYPHER_GEN_PROMPT.format(question=question) - if self.last_answer is None - else CYPHER_GEN_PROMPT_WITH_HISTORY.format(question=question, last_answer=self.last_answer)) - if error is False - else CYPHER_GEN_PROMPT_WITH_ERROR.format( - question=question, error=error - ) - ) + if self.cypher_prompt is not None: + cypher_prompt = self.cypher_prompt + else: + cypher_prompt = ( + (CYPHER_GEN_PROMPT.format(question=question) + if self.last_answer is None + else CYPHER_GEN_PROMPT_WITH_HISTORY.format(question=question, last_answer=self.last_answer)) + if error is False + else CYPHER_GEN_PROMPT_WITH_ERROR.format( + question=question, error=error + ) + ) logger.debug(f"Cypher Prompt: {cypher_prompt}") cypher_statement_response = self.chat_session.send_message( cypher_prompt, diff --git a/graphrag_sdk/steps/qa_step.py b/graphrag_sdk/steps/qa_step.py index 45e4624b..0bd81f06 100644 --- a/graphrag_sdk/steps/qa_step.py +++ b/graphrag_sdk/steps/qa_step.py @@ -17,17 +17,19 @@ def __init__( self, chat_session: GenerativeModelChatSession, config: dict = None, + qa_prompt: str = None, ) -> None: self.config = config or {} self.chat_session = chat_session + self.qa_prompt = qa_prompt def run(self, question: str, cypher: str, context: str): - - qa_prompt = GRAPH_QA_PROMPT.format( + graph_qa_prompt = self.qa_prompt or GRAPH_QA_SYSTEM + qa_prompt = graph_qa_prompt.format( context=context, cypher=cypher, question=question ) - # logger.debug(f"QA Prompt: {qa_prompt}") + logger.debug(f"QA Prompt: {qa_prompt}") qa_response = self.chat_session.send_message(qa_prompt) return qa_response.text From d07f97e1ab459657f82471bbe07873636c61370f Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Thu, 14 Nov 2024 17:02:13 +0200 Subject: [PATCH 02/15] fix-qa-prompt --- graphrag_sdk/steps/qa_step.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphrag_sdk/steps/qa_step.py b/graphrag_sdk/steps/qa_step.py index 0bd81f06..20d97b67 100644 --- a/graphrag_sdk/steps/qa_step.py +++ b/graphrag_sdk/steps/qa_step.py @@ -1,7 +1,7 @@ from graphrag_sdk.steps.Step import Step from graphrag_sdk.models import GenerativeModelChatSession -from graphrag_sdk.fixtures.prompts import GRAPH_QA_SYSTEM, GRAPH_QA_PROMPT +from graphrag_sdk.fixtures.prompts import GRAPH_QA_PROMPT import logging logger = logging.getLogger(__name__) @@ -24,7 +24,7 @@ def __init__( self.qa_prompt = qa_prompt def run(self, question: str, cypher: str, context: str): - graph_qa_prompt = self.qa_prompt or GRAPH_QA_SYSTEM + graph_qa_prompt = self.qa_prompt or GRAPH_QA_PROMPT qa_prompt = graph_qa_prompt.format( context=context, cypher=cypher, question=question ) From 2496eb388450c302b213c06361c47e292a66875c Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Wed, 20 Nov 2024 14:58:27 +0200 Subject: [PATCH 03/15] fix-system-prompt --- graphrag_sdk/chat_session.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 8ad865be..1ae36645 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -56,12 +56,12 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, self.cypher_chat_session = ( model_config.cypher_generation.with_system_instruction( - CYPHER_GEN_SYSTEM.replace("#ONTOLOGY", str(ontology.to_json())) + cypher_system_instruction ).start_chat() ) self.qa_chat_session = model_config.qa.with_system_instruction( - qa_system_instruction or GRAPH_QA_SYSTEM - ).start_chat() + qa_system_instruction or GRAPH_QA_SYSTEM + ).start_chat() self.last_answer = None def send_message(self, message: str): From df0c3349db899cf3e66b9b0c9138b2eb5b6bfd5c Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Thu, 21 Nov 2024 18:06:54 +0200 Subject: [PATCH 04/15] kg-agent-prompts-docs --- README.md | 43 ++++++++++++++++++++++++-- graphrag_sdk/chat_session.py | 19 ++++++------ graphrag_sdk/fixtures/prompts.py | 4 +-- graphrag_sdk/kg.py | 36 ++++++++++++++------- graphrag_sdk/steps/graph_query_step.py | 23 ++++++-------- graphrag_sdk/steps/qa_step.py | 7 ++--- 6 files changed, 87 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 6e9f6649..c90164eb 100644 --- a/README.md +++ b/README.md @@ -170,9 +170,9 @@ After generating the initial ontology, you can review it and make any necessary Once you are satisfied with the ontology, you can proceed to use it for creating and managing your Knowledge Graph (KG). -### Knowledge Graph +### Knowledge Graph Agent -Now, you can use the SDK to create a Knowledge Graph (KG) from your sources and ontology. +Now, you can use the SDK to create a Knowledge Graph (KG) from your sources and ontology for Q&A. ```python # After approving the ontology, load it from disk. @@ -191,6 +191,45 @@ kg.process_sources(sources) You can update the KG at any time by processing more sources with the `process_sources` method. +#### Configurable Prompts +When creating your Knowledge Graph (KG) agent, you can customize the prompts to tailor its behavior. This step is optional but can enhance functionality. + +There are five types of prompts: + +1. **`cypher_system_instruction`** + - System instructions for the Cypher generation step. + - **Note:** Ensure your prompt includes `#ONTOLOGY`. + +2. **`qa_system_instruction`** + - System instructions for the Q&A step. + +3. **`cypher_gen_prompt`** + - The prompt used during the Cypher generation step. + - **Note:** Include `{question}` in your prompt. + +4. **`cypher_gen_prompt_history`** + - The prompt for Cypher generation when history needs to be considered. + - **Note:** Include `{question}` and `{last_answer}` in your prompt. + +5. **`qa_prompt`** + - The prompt used during the Q&A step. + - **Note:** Include `{question}`, `{context}`, and `{cypher}` in your prompt. + +Here’s an example configuration: + +```python +kg = KnowledgeGraph( + name="kg_name", + model_config=KnowledgeGraphModelConfig.with_model(model), + ontology=ontology, + cypher_system_instruction=cypher_system_instruction, + qa_system_instruction=qa_system_instruction, + cypher_gen_prompt=cypher_gen_prompt, + cypher_gen_prompt_history=cypher_gen_prompt_history, + qa_prompt=qa_prompt +) +``` + ### Graph RAG At this point, you have a Knowledge Graph that can be queried using this SDK. Use the method `chat_session` for start a conversation. diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 1ae36645..4b13633d 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -1,9 +1,8 @@ +from falkordb import Graph from graphrag_sdk.ontology import Ontology +from graphrag_sdk.steps.qa_step import QAStep from graphrag_sdk.model_config import KnowledgeGraphModelConfig from graphrag_sdk.steps.graph_query_step import GraphQueryGenerationStep -from graphrag_sdk.steps.qa_step import QAStep -from graphrag_sdk.fixtures.prompts import GRAPH_QA_SYSTEM, CYPHER_GEN_SYSTEM -from falkordb import Graph class ChatSession: @@ -26,8 +25,8 @@ class ChatSession: """ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, graph: Graph, - cypher_system_instruction: str = None, qa_system_instruction: str = None, - cypher_gen_prompt: str = None, qa_prompt: str = None): + cypher_system_instruction: str, qa_system_instruction: str, + cypher_gen_prompt: str, qa_prompt: str, cypher_gen_prompt_history: str): """ Initializes a new ChatSession object. @@ -46,13 +45,12 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, self.model_config = model_config self.graph = graph self.ontology = ontology - if cypher_system_instruction is None: - cypher_system_instruction = CYPHER_GEN_SYSTEM.replace("#ONTOLOGY", str(ontology.to_json())) - else: - cypher_system_instruction = cypher_system_instruction + "\nOntology:\n" + str(ontology.to_json()) + cypher_system_instruction = cypher_system_instruction.replace("#ONTOLOGY", str(ontology.to_json())) + self.cypher_prompt = cypher_gen_prompt self.qa_prompt = qa_prompt + self.cypher_prompt_with_history = cypher_gen_prompt_history self.cypher_chat_session = ( model_config.cypher_generation.with_system_instruction( @@ -60,7 +58,7 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, ).start_chat() ) self.qa_chat_session = model_config.qa.with_system_instruction( - qa_system_instruction or GRAPH_QA_SYSTEM + qa_system_instruction ).start_chat() self.last_answer = None @@ -80,6 +78,7 @@ def send_message(self, message: str): ontology=self.ontology, last_answer=self.last_answer, cypher_prompt=self.cypher_prompt, + cypher_prompt_with_history=self.cypher_prompt_with_history ) (context, cypher) = cypher_step.run(message) diff --git a/graphrag_sdk/fixtures/prompts.py b/graphrag_sdk/fixtures/prompts.py index 1fde8768..373a57c6 100644 --- a/graphrag_sdk/fixtures/prompts.py +++ b/graphrag_sdk/fixtures/prompts.py @@ -442,7 +442,7 @@ CYPHER_GEN_PROMPT = """ Using the ontology provided, generate an OpenCypher statement to query the graph database returning all relevant entities, relationships, and attributes to answer the question below. -If you cannot generate a OpenCypher statement for any reason, return an empty string. +If you cannot generate a OpenCypher statement for any reason, return an empty response. Respect the order of the relationships, the arrows should always point from the "source" to the "target". Please think if your answer is a valid Cypher query, and correct it if it is not. @@ -482,8 +482,8 @@ The provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it. Make the answer sound as a response to the question. Do not mention that you based the result on the given information. Do not answer more than the question asks for. -Here is an example: +Here is an example: Question: Which managers own Neo4j stocks? Context:[manager:CTL LLC, manager:JANE STREET GROUP LLC] Helpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks. diff --git a/graphrag_sdk/kg.py b/graphrag_sdk/kg.py index 3c3e53fa..bcdaebe7 100644 --- a/graphrag_sdk/kg.py +++ b/graphrag_sdk/kg.py @@ -1,16 +1,14 @@ import logging -from graphrag_sdk.ontology import Ontology from falkordb import FalkorDB +from graphrag_sdk.ontology import Ontology from graphrag_sdk.source import AbstractSource -from graphrag_sdk.model_config import KnowledgeGraphModelConfig -from graphrag_sdk.steps.extract_data_step import ExtractDataStep -from graphrag_sdk.steps.graph_query_step import GraphQueryGenerationStep -from graphrag_sdk.fixtures.prompts import GRAPH_QA_SYSTEM, CYPHER_GEN_SYSTEM -from graphrag_sdk.steps.qa_step import QAStep from graphrag_sdk.chat_session import ChatSession -from graphrag_sdk.helpers import map_dict_to_cypher_properties from graphrag_sdk.attribute import AttributeType, Attribute -from graphrag_sdk.models import GenerativeModelChatSession +from graphrag_sdk.helpers import map_dict_to_cypher_properties +from graphrag_sdk.model_config import KnowledgeGraphModelConfig +from graphrag_sdk.steps.extract_data_step import ExtractDataStep +from graphrag_sdk.fixtures.prompts import (GRAPH_QA_SYSTEM, CYPHER_GEN_SYSTEM, + CYPHER_GEN_PROMPT, GRAPH_QA_PROMPT, CYPHER_GEN_PROMPT_WITH_HISTORY) logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -31,6 +29,11 @@ def __init__( port: int = 6379, username: str | None = None, password: str | None = None, + cypher_system_instruction: str = None, + qa_system_instruction: str = None, + cypher_gen_prompt: str = None, + qa_prompt: str = None, + cypher_gen_prompt_history: str = None, ): """ Initialize Knowledge Graph @@ -43,6 +46,11 @@ def __init__( username (str|None): FalkorDB username. password (str|None): FalkorDB password. ontology (Ontology|None): Ontology to use. + cypher_system_instruction (str|None): Cypher system instruction. Make sure you have #ONTOLOGY in the instruction. + qa_system_instruction (str|None): QA system instruction. + cypher_gen_prompt (str|None): Cypher generation prompt. Make sure you have {question} in the prompt. + qa_prompt (str|None): QA prompt. Make sure you have {question}, {context} and {cypher} in the prompt. + cypher_gen_prompt_history (str|None): Cypher generation prompt with history. Make sure you have {question} and {last_answer} in the prompt. """ if not isinstance(name, str) or name == "": @@ -56,6 +64,11 @@ def __init__( self._ontology = ontology self._model_config = model_config self.sources = set([]) + self.cypher_system_instruction = cypher_system_instruction or CYPHER_GEN_SYSTEM + self.qa_system_instruction = qa_system_instruction or GRAPH_QA_SYSTEM + self.cypher_gen_prompt = cypher_gen_prompt or CYPHER_GEN_PROMPT + self.qa_prompt = qa_prompt or GRAPH_QA_PROMPT + self.cypher_gen_prompt_history = cypher_gen_prompt_history or CYPHER_GEN_PROMPT_WITH_HISTORY # Attributes @@ -134,10 +147,9 @@ def delete(self) -> None: for key in self.__dict__.keys(): setattr(self, key, None) - def chat_session(self, cypher_system_instruction: str = None, qa_system_instruction: str = None, - cypher_gen_prompt: str = None, qa_prompt: str = None) -> ChatSession: - chat_session = ChatSession(self._model_config, self.ontology, self.graph, cypher_system_instruction, - qa_system_instruction, cypher_gen_prompt, qa_prompt) + def chat_session(self) -> ChatSession: + chat_session = ChatSession(self._model_config, self.ontology, self.graph, self.cypher_system_instruction, + self.qa_system_instruction, self.cypher_gen_prompt, self.qa_prompt, self.cypher_gen_prompt_history) return chat_session def add_node(self, entity: str, attributes: dict): """ diff --git a/graphrag_sdk/steps/graph_query_step.py b/graphrag_sdk/steps/graph_query_step.py index 615844c1..a9ac8bb2 100644 --- a/graphrag_sdk/steps/graph_query_step.py +++ b/graphrag_sdk/steps/graph_query_step.py @@ -1,19 +1,15 @@ +import logging +from falkordb import Graph from graphrag_sdk.steps.Step import Step from graphrag_sdk.ontology import Ontology from graphrag_sdk.models import ( GenerativeModelChatSession, ) -from graphrag_sdk.fixtures.prompts import ( - CYPHER_GEN_PROMPT, - CYPHER_GEN_PROMPT_WITH_HISTORY, -) -import logging from graphrag_sdk.helpers import ( extract_cypher, validate_cypher, stringify_falkordb_response, ) -from falkordb import Graph logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -32,6 +28,7 @@ def __init__( config: dict = None, last_answer: str = None, cypher_prompt: str = None, + cypher_prompt_with_history: str = None, ) -> None: self.ontology = ontology self.config = config or {} @@ -39,19 +36,17 @@ def __init__( self.chat_session = chat_session self.last_answer = last_answer self.cypher_prompt = cypher_prompt + self.cypher_prompt_with_history = cypher_prompt_with_history def run(self, question: str, retries: int = 10): cypher = "" for i in range(retries): try: - if self.cypher_prompt is not None: - cypher_prompt = self.cypher_prompt - else: - cypher_prompt = ( - (CYPHER_GEN_PROMPT.format(question=question) - if self.last_answer is None - else CYPHER_GEN_PROMPT_WITH_HISTORY.format(question=question, last_answer=self.last_answer)) - ) + cypher_prompt = ( + (self.cypher_prompt.format(question=question) + if self.last_answer is None + else self.cypher_prompt_with_history.format(question=question, last_answer=self.last_answer)) + ) logger.debug(f"Cypher Prompt: {cypher_prompt}") cypher_statement_response = self.chat_session.send_message( cypher_prompt, diff --git a/graphrag_sdk/steps/qa_step.py b/graphrag_sdk/steps/qa_step.py index 20d97b67..286cfd8b 100644 --- a/graphrag_sdk/steps/qa_step.py +++ b/graphrag_sdk/steps/qa_step.py @@ -1,9 +1,7 @@ +import logging from graphrag_sdk.steps.Step import Step from graphrag_sdk.models import GenerativeModelChatSession -from graphrag_sdk.fixtures.prompts import GRAPH_QA_PROMPT -import logging - logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -24,8 +22,7 @@ def __init__( self.qa_prompt = qa_prompt def run(self, question: str, cypher: str, context: str): - graph_qa_prompt = self.qa_prompt or GRAPH_QA_PROMPT - qa_prompt = graph_qa_prompt.format( + qa_prompt = self.qa_prompt.format( context=context, cypher=cypher, question=question ) From 57a5c186eda182f1dc916904ce1a3da6df6f6e6f Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Thu, 21 Nov 2024 18:10:20 +0200 Subject: [PATCH 05/15] spell --- .wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.wordlist.txt b/.wordlist.txt index c2a21baf..06a88c1d 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -27,3 +27,4 @@ www faq Ollama ollama +Cyhper \ No newline at end of file From c8b9928ac660bbeeac35c8bd4ee7730ab28512c9 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Thu, 21 Nov 2024 18:12:00 +0200 Subject: [PATCH 06/15] spell --- .wordlist.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.wordlist.txt b/.wordlist.txt index 06a88c1d..593d0568 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -27,4 +27,4 @@ www faq Ollama ollama -Cyhper \ No newline at end of file +Cypher \ No newline at end of file From c3e03ecad3a0fb1d223b108f82dea855766e863f Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 10:07:23 +0200 Subject: [PATCH 07/15] rem-unused-imports --- tests/test_multi_agent.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/test_multi_agent.py b/tests/test_multi_agent.py index c445a382..02ae5978 100644 --- a/tests/test_multi_agent.py +++ b/tests/test_multi_agent.py @@ -1,25 +1,20 @@ +import logging +import unittest +from json import loads from dotenv import load_dotenv - -load_dotenv() -from graphrag_sdk.ontology import Ontology from graphrag_sdk.entity import Entity from graphrag_sdk.relation import Relation +from graphrag_sdk.ontology import Ontology +from graphrag_sdk.agents.kg_agent import KGAgent +from graphrag_sdk.orchestrator import Orchestrator from graphrag_sdk.attribute import Attribute, AttributeType -import unittest from graphrag_sdk.models.gemini import GeminiGenerativeModel from graphrag_sdk import KnowledgeGraph, KnowledgeGraphModelConfig -from graphrag_sdk.orchestrator import Orchestrator -from graphrag_sdk.agents.kg_agent import KGAgent -import vertexai -import os -import logging -from json import loads +load_dotenv() logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) -vertexai.init(project=os.getenv("PROJECT_ID"), location=os.getenv("REGION")) - class TestMultiAgent(unittest.TestCase): From 1b5e19295f01721ff7f75f046087e87cf2f78966 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 10:32:18 +0200 Subject: [PATCH 08/15] ollama-test --- tests/test_kg_ollama.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_kg_ollama.py b/tests/test_kg_ollama.py index 52429e27..29146aa5 100644 --- a/tests/test_kg_ollama.py +++ b/tests/test_kg_ollama.py @@ -90,7 +90,7 @@ def test_kg_creation(self): self.kg.process_sources(sources) chat = self.kg.chat_session() - answer = chat.send_message("How many actors acted in a movie?") + answer = chat.send_message("Count the number of actors acted in a movie?") logger.info(f"Answer: {answer}") From f052dec3eb74043a60d1dc462356e7798dc09ef1 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 12:40:21 +0200 Subject: [PATCH 09/15] rev-context --- graphrag_sdk/chat_session.py | 3 +++ graphrag_sdk/kg.py | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 4b13633d..63f7ffd9 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -61,6 +61,7 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, qa_system_instruction ).start_chat() self.last_answer = None + self.context = [] def send_message(self, message: str): """ @@ -85,6 +86,8 @@ def send_message(self, message: str): if not cypher or len(cypher) == 0: return "I am sorry, I could not find the answer to your question" + else: + self.context.append({"message": message, "cypher": cypher, "context": context}) qa_step = QAStep( chat_session=self.qa_chat_session, diff --git a/graphrag_sdk/kg.py b/graphrag_sdk/kg.py index bcdaebe7..bbc8c63a 100644 --- a/graphrag_sdk/kg.py +++ b/graphrag_sdk/kg.py @@ -64,11 +64,11 @@ def __init__( self._ontology = ontology self._model_config = model_config self.sources = set([]) - self.cypher_system_instruction = cypher_system_instruction or CYPHER_GEN_SYSTEM - self.qa_system_instruction = qa_system_instruction or GRAPH_QA_SYSTEM - self.cypher_gen_prompt = cypher_gen_prompt or CYPHER_GEN_PROMPT - self.qa_prompt = qa_prompt or GRAPH_QA_PROMPT - self.cypher_gen_prompt_history = cypher_gen_prompt_history or CYPHER_GEN_PROMPT_WITH_HISTORY + self.cypher_system_instruction = CYPHER_GEN_SYSTEM if cypher_system_instruction is None else cypher_system_instruction + self.qa_system_instruction = GRAPH_QA_SYSTEM if qa_system_instruction is None else qa_system_instruction + self.cypher_gen_prompt = CYPHER_GEN_PROMPT if cypher_gen_prompt is None else cypher_gen_prompt + self.qa_prompt = GRAPH_QA_PROMPT if qa_prompt is None else qa_prompt + self.cypher_gen_prompt_history = CYPHER_GEN_PROMPT_WITH_HISTORY if cypher_gen_prompt_history is None else cypher_gen_prompt_history # Attributes From 85fdacf15bda5b05ad5a192d811da642eb7c6736 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 13:15:29 +0200 Subject: [PATCH 10/15] prompt-ont-var --- README.md | 2 +- graphrag_sdk/chat_session.py | 2 +- graphrag_sdk/fixtures/prompts.py | 2 +- graphrag_sdk/kg.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c90164eb..fcc3804c 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ There are five types of prompts: 1. **`cypher_system_instruction`** - System instructions for the Cypher generation step. - - **Note:** Ensure your prompt includes `#ONTOLOGY`. + - **Note:** Ensure your prompt includes `{ontology}`. 2. **`qa_system_instruction`** - System instructions for the Q&A step. diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 63f7ffd9..6b8748b8 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -45,7 +45,7 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, self.model_config = model_config self.graph = graph self.ontology = ontology - cypher_system_instruction = cypher_system_instruction.replace("#ONTOLOGY", str(ontology.to_json())) + cypher_system_instruction = cypher_system_instruction.format(ontology=str(ontology.to_json())) self.cypher_prompt = cypher_gen_prompt diff --git a/graphrag_sdk/fixtures/prompts.py b/graphrag_sdk/fixtures/prompts.py index 373a57c6..929af0af 100644 --- a/graphrag_sdk/fixtures/prompts.py +++ b/graphrag_sdk/fixtures/prompts.py @@ -429,7 +429,7 @@ Single-Source minimal-weight paths: Find minimal-weight paths from a given source entity using algo.SSpaths(). Ontology: -#ONTOLOGY +{ontology} For example, given the question "Which managers own Neo4j stocks?", the OpenCypher statement should look like this: diff --git a/graphrag_sdk/kg.py b/graphrag_sdk/kg.py index bbc8c63a..22e80abb 100644 --- a/graphrag_sdk/kg.py +++ b/graphrag_sdk/kg.py @@ -46,7 +46,7 @@ def __init__( username (str|None): FalkorDB username. password (str|None): FalkorDB password. ontology (Ontology|None): Ontology to use. - cypher_system_instruction (str|None): Cypher system instruction. Make sure you have #ONTOLOGY in the instruction. + cypher_system_instruction (str|None): Cypher system instruction. Make sure you have {ontology} in the instruction. qa_system_instruction (str|None): QA system instruction. cypher_gen_prompt (str|None): Cypher generation prompt. Make sure you have {question} in the prompt. qa_prompt (str|None): QA prompt. Make sure you have {question}, {context} and {cypher} in the prompt. From 5e5500b54cf469cf670fe4f48dc61c8ad919963f Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 15:44:34 +0200 Subject: [PATCH 11/15] update-chat-output --- graphrag_sdk/agents/kg_agent.py | 2 +- graphrag_sdk/chat_session.py | 17 +++++++++---- graphrag_sdk/kg.py | 44 +++++++++++++++++++++++++++++---- tests/test_kg_gemini.py | 1 + tests/test_kg_ollama.py | 1 + tests/test_kg_openai.py | 1 + 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/graphrag_sdk/agents/kg_agent.py b/graphrag_sdk/agents/kg_agent.py index 0a3ea1de..908303ac 100644 --- a/graphrag_sdk/agents/kg_agent.py +++ b/graphrag_sdk/agents/kg_agent.py @@ -136,7 +136,7 @@ def run(self, params: dict) -> str: """ output = self.chat_session.send_message(params["prompt"]) - return output + return output['response'] def __repr__(self): """ diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 6b8748b8..6d5e113c 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -61,7 +61,6 @@ def __init__(self, model_config: KnowledgeGraphModelConfig, ontology: Ontology, qa_system_instruction ).start_chat() self.last_answer = None - self.context = [] def send_message(self, message: str): """ @@ -71,7 +70,11 @@ def send_message(self, message: str): message (str): The message to send. Returns: - str: The response to the message. + dict: The response to the message in the following format: + {"question": message, + "response": answer, + "context": context, + "cypher": cypher} """ cypher_step = GraphQueryGenerationStep( graph=self.graph, @@ -86,8 +89,6 @@ def send_message(self, message: str): if not cypher or len(cypher) == 0: return "I am sorry, I could not find the answer to your question" - else: - self.context.append({"message": message, "cypher": cypher, "context": context}) qa_step = QAStep( chat_session=self.qa_chat_session, @@ -96,4 +97,10 @@ def send_message(self, message: str): answer = qa_step.run(message, cypher, context) self.last_answer = answer - return answer \ No newline at end of file + + return { + "question": message, + "response": answer, + "context": context, + "cypher": cypher + } \ No newline at end of file diff --git a/graphrag_sdk/kg.py b/graphrag_sdk/kg.py index 22e80abb..831a05d3 100644 --- a/graphrag_sdk/kg.py +++ b/graphrag_sdk/kg.py @@ -1,4 +1,5 @@ import logging +import warnings from falkordb import FalkorDB from graphrag_sdk.ontology import Ontology from graphrag_sdk.source import AbstractSource @@ -64,11 +65,44 @@ def __init__( self._ontology = ontology self._model_config = model_config self.sources = set([]) - self.cypher_system_instruction = CYPHER_GEN_SYSTEM if cypher_system_instruction is None else cypher_system_instruction - self.qa_system_instruction = GRAPH_QA_SYSTEM if qa_system_instruction is None else qa_system_instruction - self.cypher_gen_prompt = CYPHER_GEN_PROMPT if cypher_gen_prompt is None else cypher_gen_prompt - self.qa_prompt = GRAPH_QA_PROMPT if qa_prompt is None else qa_prompt - self.cypher_gen_prompt_history = CYPHER_GEN_PROMPT_WITH_HISTORY if cypher_gen_prompt_history is None else cypher_gen_prompt_history + + if cypher_system_instruction is None: + cypher_system_instruction = CYPHER_GEN_SYSTEM + else: + if "{ontology}" not in cypher_system_instruction: + warnings.warn("Cypher system instruction should contain {ontology}", category=UserWarning) + + if qa_system_instruction is None: + qa_system_instruction = GRAPH_QA_SYSTEM + + if cypher_gen_prompt is None: + cypher_gen_prompt = CYPHER_GEN_PROMPT + else: + if "{question}" not in cypher_gen_prompt: + raise Exception("Cypher generation prompt should contain {question}") + + if qa_prompt is None: + qa_prompt = GRAPH_QA_PROMPT + else: + if "{question}" not in qa_prompt or "{context}" not in qa_prompt: + raise Exception("QA prompt should contain {question} and {context}") + if "{cypher}" not in qa_prompt: + warnings.warn("QA prompt should contain {cypher}", category=UserWarning) + + if cypher_gen_prompt_history is None: + cypher_gen_prompt_history = CYPHER_GEN_PROMPT_WITH_HISTORY + else: + if "{question}" not in cypher_gen_prompt_history: + raise Exception("Cypher generation prompt with history should contain {question}") + if "{last_answer}" not in cypher_gen_prompt_history: + warnings.warn("Cypher generation prompt with history should contain {last_answer}", category=UserWarning) + + # Assign the validated values + self.cypher_system_instruction = cypher_system_instruction + self.qa_system_instruction = qa_system_instruction + self.cypher_gen_prompt = cypher_gen_prompt + self.qa_prompt = qa_prompt + self.cypher_gen_prompt_history = cypher_gen_prompt_history # Attributes diff --git a/tests/test_kg_gemini.py b/tests/test_kg_gemini.py index 8778fe91..cbfdf2b1 100644 --- a/tests/test_kg_gemini.py +++ b/tests/test_kg_gemini.py @@ -91,6 +91,7 @@ def test_kg_creation(self): chat = self.kg.chat_session() answer = chat.send_message("How many actors acted in a movie?") + answer = answer['response'] logger.info(f"Answer: {answer}") diff --git a/tests/test_kg_ollama.py b/tests/test_kg_ollama.py index 29146aa5..1acc6ecb 100644 --- a/tests/test_kg_ollama.py +++ b/tests/test_kg_ollama.py @@ -91,6 +91,7 @@ def test_kg_creation(self): chat = self.kg.chat_session() answer = chat.send_message("Count the number of actors acted in a movie?") + answer = answer['response'] logger.info(f"Answer: {answer}") diff --git a/tests/test_kg_openai.py b/tests/test_kg_openai.py index d7b25c4f..785a28df 100644 --- a/tests/test_kg_openai.py +++ b/tests/test_kg_openai.py @@ -85,6 +85,7 @@ def test_kg_creation(self): chat = self.kg.chat_session() answer = chat.send_message("How many actors acted in a movie?") + answer = answer['response'] logger.info(f"Answer: {answer}") From 572d03b7d4c8b9dc801c21cc646aa4f732c9c55d Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 16:01:16 +0200 Subject: [PATCH 12/15] ret-no-cypher --- graphrag_sdk/chat_session.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/graphrag_sdk/chat_session.py b/graphrag_sdk/chat_session.py index 6d5e113c..ea1676d6 100644 --- a/graphrag_sdk/chat_session.py +++ b/graphrag_sdk/chat_session.py @@ -88,7 +88,12 @@ def send_message(self, message: str): (context, cypher) = cypher_step.run(message) if not cypher or len(cypher) == 0: - return "I am sorry, I could not find the answer to your question" + return { + "question": message, + "response": "I am sorry, I could not find the answer to your question", + "context": None, + "cypher": None + } qa_step = QAStep( chat_session=self.qa_chat_session, From 2dcd12be1d1365cd035c16451e95af784c07f8ef Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 16:55:56 +0200 Subject: [PATCH 13/15] update-examples --- examples/movies/demo-movies.ipynb | 22 ++-- examples/ufc/demo-ufc.ipynb | 128 +++++++++++-------- ontology.json | 199 ++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 64 deletions(-) create mode 100644 ontology.json diff --git a/examples/movies/demo-movies.ipynb b/examples/movies/demo-movies.ipynb index ecbec637..440d91e1 100644 --- a/examples/movies/demo-movies.ipynb +++ b/examples/movies/demo-movies.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -29,7 +29,7 @@ "True" ] }, - "execution_count": 11, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -59,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -84,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -113,7 +113,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -136,17 +136,17 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "The directors of the movie \"The Matrix\" are Lilly Wachowski and Lana Wachowski.\n", - "The directors, Lilly Wachowski and Lana Wachowski, are connected to Keanu Reeves through their collaboration on the movies \"The Matrix,\" \"The Matrix Reloaded,\" and \"The Matrix Revolutions,\" in which Keanu Reeves acted.\n", - "The director of the movie \"Side by Side\" is Chris Kenneally.\n", - "The directors, ordered lexically, are Chris Kenneally, Lana Wachowski, and Lilly Wachowski.\n" + "{'question': 'Who is the director of the movie The Matrix?', 'response': 'The directors of the movie \"The Matrix\" are Lilly Wachowski and Lana Wachowski.', 'context': '[[\\'(:Movie{box_office_gross_usa:0,duration:\"2h 18m\",genre:\"Sci-Fi, Action\",popcornmeter_score:72,rating:\"R\",release_date:\"2003-05-15\",runtime:\"2h 18m\",title:\"The Matrix Reloaded\",tomatometer_score:74})\\', \\'(:Person{name:\"Lilly Wachowski\"})\\'], [\\'(:Movie{box_office_gross_usa:0,duration:\"2h 18m\",genre:\"Sci-Fi, Action\",popcornmeter_score:72,rating:\"R\",release_date:\"2003-05-15\",runtime:\"2h 18m\",title:\"The Matrix Reloaded\",tomatometer_score:74})\\', \\'(:Person{name:\"Lana Wachowski\"})\\'], [\\'(:Movie{box_office_gross_usa:0,duration:\"2h 16m\",genre:\"Sci-Fi, Action, Mystery & Thriller\",popcornmeter_score:85,rating:\"R\",release_date:\"1999-03-31\",title:\"The Matrix\",tomatometer_score:83})\\', \\'(:Person{name:\"Lilly Wachowski\"})\\'], [\\'(:Movie{box_office_gross_usa:0,duration:\"2h 16m\",genre:\"Sci-Fi, Action, Mystery & Thriller\",popcornmeter_score:85,rating:\"R\",release_date:\"1999-03-31\",title:\"The Matrix\",tomatometer_score:83})\\', \\'(:Person{name:\"Lana Wachowski\"})\\']]', 'cypher': \"\\nMATCH (m:Movie)-[:DIRECTED_BY]->(p:Person)\\nWHERE m.title CONTAINS 'The Matrix'\\nRETURN m, p\\n\"}\n", + "{'question': 'How this director connected to Keanu Reeves?', 'response': 'The directors Lilly Wachowski and Lana Wachowski are connected to Keanu Reeves through the movie \"The Matrix,\" where Keanu Reeves acted in the film.', 'context': '[[\\'(:Person{name:\"Lilly Wachowski\"})\\', \\'(:Movie{box_office_gross_usa:0,duration:\"2h 16m\",genre:\"Sci-Fi, Action, Mystery & Thriller\",popcornmeter_score:85,rating:\"R\",release_date:\"1999-03-31\",title:\"The Matrix\",tomatometer_score:83})\\', \\'(:Person{name:\"Keanu Reeves\",role:\"John Wick\"})\\'], [\\'(:Person{name:\"Lana Wachowski\"})\\', \\'(:Movie{box_office_gross_usa:0,duration:\"2h 16m\",genre:\"Sci-Fi, Action, Mystery & Thriller\",popcornmeter_score:85,rating:\"R\",release_date:\"1999-03-31\",title:\"The Matrix\",tomatometer_score:83})\\', \\'(:Person{name:\"Keanu Reeves\",role:\"John Wick\"})\\']]', 'cypher': \"\\nMATCH (p:Person)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(k:Person)\\nWHERE p.name IN ['Lilly Wachowski', 'Lana Wachowski'] AND k.name CONTAINS 'Keanu Reeves'\\nRETURN p, m, k\\n\"}\n", + "{'question': 'Who is the director of the movie Side by Side?', 'response': 'The director of the movie \"Side by Side\" is Chris Kenneally.', 'context': '[[\\'(:Movie{box_office_gross_usa:0,duration:\"1h 38m\",genre:\"Documentary\",popcornmeter_score:86,rating:\"Not Rated\",release_date:\"2012-08-17\",runtime:\"1h 38m\",title:\"Side by Side\",tomatometer_score:92})\\', \\'(:Person{name:\"Chris Kenneally\"})\\']]', 'cypher': \"\\nMATCH (m:Movie)-[:DIRECTED_BY]->(p:Person)\\nWHERE m.title CONTAINS 'Side by Side'\\nRETURN m, p\\n\"}\n", + "{'question': 'Order the directors that you mentioned in all of our conversation by lexical order.', 'response': 'The directors, ordered lexically, are Chris Kenneally, Lana Wachowski, and Lilly Wachowski.', 'context': '[[\\'(:Person{name:\"Chris Kenneally\"})\\'], [\\'(:Person{name:\"Lana Wachowski\"})\\'], [\\'(:Person{name:\"Lilly Wachowski\"})\\']]', 'cypher': \"\\nMATCH (p:Person)\\nWHERE p.name IN ['Lilly Wachowski', 'Lana Wachowski', 'Chris Kenneally']\\nRETURN p\\nORDER BY p.name\\n\"}\n" ] } ], diff --git a/examples/ufc/demo-ufc.ipynb b/examples/ufc/demo-ufc.ipynb index 51728e8e..43eca283 100644 --- a/examples/ufc/demo-ufc.ipynb +++ b/examples/ufc/demo-ufc.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -29,7 +29,7 @@ "True" ] }, - "execution_count": 1, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -62,7 +62,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -86,7 +86,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -124,7 +124,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -152,73 +152,67 @@ " \"description\": \"\"\n", " },\n", " {\n", - " \"label\": \"Fight\",\n", + " \"label\": \"FightEvent\",\n", " \"attributes\": [\n", " {\n", - " \"name\": \"date\",\n", + " \"name\": \"name\",\n", " \"type\": \"string\",\n", " \"unique\": true,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"location\",\n", + " \"name\": \"date\",\n", " \"type\": \"string\",\n", " \"unique\": false,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"method\",\n", + " \"name\": \"location\",\n", " \"type\": \"string\",\n", " \"unique\": false,\n", " \"required\": true\n", - " },\n", + " }\n", + " ],\n", + " \"description\": \"\"\n", + " },\n", + " {\n", + " \"label\": \"Bout\",\n", + " \"attributes\": [\n", " {\n", - " \"name\": \"round\",\n", - " \"type\": \"number\",\n", + " \"name\": \"weight_class\",\n", + " \"type\": \"string\",\n", " \"unique\": false,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"time\",\n", + " \"name\": \"method\",\n", " \"type\": \"string\",\n", " \"unique\": false,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"time_format\",\n", - " \"type\": \"string\",\n", + " \"name\": \"round\",\n", + " \"type\": \"number\",\n", " \"unique\": false,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"referee\",\n", + " \"name\": \"time\",\n", " \"type\": \"string\",\n", " \"unique\": false,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"details\",\n", + " \"name\": \"time_format\",\n", " \"type\": \"string\",\n", " \"unique\": false,\n", - " \"required\": false\n", - " }\n", - " ],\n", - " \"description\": \"\"\n", - " },\n", - " {\n", - " \"label\": \"Event\",\n", - " \"attributes\": [\n", - " {\n", - " \"name\": \"title\",\n", - " \"type\": \"string\",\n", - " \"unique\": true,\n", " \"required\": true\n", " },\n", " {\n", - " \"name\": \"date\",\n", + " \"name\": \"referee\",\n", " \"type\": \"string\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " }\n", " ],\n", " \"description\": \"\"\n", @@ -226,12 +220,12 @@ " ],\n", " \"relations\": [\n", " {\n", - " \"label\": \"PARTICIPATED_IN\",\n", + " \"label\": \"FOUGHT_IN\",\n", " \"source\": {\n", " \"label\": \"Person\"\n", " },\n", " \"target\": {\n", - " \"label\": \"Fight\"\n", + " \"label\": \"Bout\"\n", " },\n", " \"attributes\": [\n", " {\n", @@ -241,68 +235,96 @@ " \"required\": true\n", " },\n", " {\n", - " \"name\": \"total_knockdowns\",\n", + " \"name\": \"knockdowns\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " },\n", " {\n", " \"name\": \"significant_strikes\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " },\n", " {\n", - " \"name\": \"significant_strike_percentage\",\n", + " \"name\": \"significant_strikes_attempted\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", + " },\n", + " {\n", + " \"name\": \"significant_strikes_percentage\",\n", + " \"type\": \"string\",\n", + " \"unique\": false,\n", + " \"required\": false\n", " },\n", " {\n", " \"name\": \"total_strikes\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", + " },\n", + " {\n", + " \"name\": \"total_strikes_attempted\",\n", + " \"type\": \"number\",\n", + " \"unique\": false,\n", + " \"required\": false\n", " },\n", " {\n", " \"name\": \"takedowns\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " },\n", " {\n", - " \"name\": \"takedown_percentage\",\n", + " \"name\": \"takedowns_attempted\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", + " },\n", + " {\n", + " \"name\": \"takedown_percentage\",\n", + " \"type\": \"string\",\n", + " \"unique\": false,\n", + " \"required\": false\n", " },\n", " {\n", " \"name\": \"submissions_attempted\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " },\n", " {\n", " \"name\": \"passes\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " },\n", " {\n", " \"name\": \"reversals\",\n", " \"type\": \"number\",\n", " \"unique\": false,\n", - " \"required\": true\n", + " \"required\": false\n", " }\n", " ]\n", " },\n", " {\n", - " \"label\": \"ORGANIZED_AT\",\n", + " \"label\": \"PART_OF_EVENT\",\n", " \"source\": {\n", - " \"label\": \"Fight\"\n", + " \"label\": \"Bout\"\n", " },\n", " \"target\": {\n", - " \"label\": \"Event\"\n", + " \"label\": \"FightEvent\"\n", + " },\n", + " \"attributes\": []\n", + " },\n", + " {\n", + " \"label\": \"PART_OF\",\n", + " \"source\": {\n", + " \"label\": \"Bout\"\n", + " },\n", + " \"target\": {\n", + " \"label\": \"FightEvent\"\n", " },\n", " \"attributes\": []\n", " }\n", @@ -326,7 +348,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -354,15 +376,15 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Waldo Cortes-Acosta is known as Salsa Boy.\n", - "One of Salsa Boy's fights took place on August 26, 2023, in Kallang, Singapore. In this fight, he achieved a win by KO/TKO through punches to the head at distance. The fight was officiated by referee Mark Craig and ended in the first round at 3:01.\n" + "{'question': 'Who is Salsa Boy?', 'response': 'Waldo Cortes-Acosta is Salsa Boy.', 'context': '[[\\'(:Person{name:\"Waldo Cortes-Acosta\",nickname:\"Salsa Boy\"})\\']]', 'cypher': \"MATCH (p:Person) WHERE p.nickname CONTAINS 'Salsa Boy' RETURN p\"}\n", + "{'question': 'Tell me about one of his fights?', 'response': 'Waldo Cortes-Acosta, also known as Salsa Boy, fought in a bout at UFC 284: Makhachev vs. Volkanovski, held on February 11, 2023, in Perth, Western Australia, Australia. He won the fight by submission in the second round, at 4:05. In this fight, he displayed impressive skills with 52 significant strikes out of 101 attempted, achieving a 51.49% success rate. He also attempted 4 takedowns, successfully completing 3 for a 75.00% success rate, and attempted 1 submission.', 'context': '[[\\'(:Person{name:\"Waldo Cortes-Acosta\",nickname:\"Salsa Boy\"})\\', \\'()-[:FOUGHT_IN{knockdowns:0,passes:0,result:\"Win\",reversals:0,significant_strikes:52,significant_strikes_attempted:101,significant_strikes_percentage:\"51.49%\",submissions_attempted:1,takedown_percentage:\"75.00%\",takedowns:3,takedowns_attempted:4,total_strikes:130,total_strikes_attempted:204}]->()\\', \\'(:Bout{method:\"Submission\",referee:\"Osiris Maia\",round:2,time:\"4:05\",time_format:\"3 Rounds (5-5-5)\",weight_class:\"Lightweight\"})\\', \\'(:FightEvent{date:\"2023-02-11\",location:\"Perth, Western Australia, Australia\",name:\"UFC 284: Makhachev vs. Volkanovski\"})\\']]', 'cypher': \"MATCH (p:Person)-[f:FOUGHT_IN]->(b:Bout)-[:PART_OF_EVENT]->(e:FightEvent) WHERE p.nickname CONTAINS 'Salsa Boy' RETURN p, f, b, e LIMIT 1\"}\n" ] } ], diff --git a/ontology.json b/ontology.json new file mode 100644 index 00000000..f9c16cc3 --- /dev/null +++ b/ontology.json @@ -0,0 +1,199 @@ +{ + "entities": [ + { + "label": "Person", + "attributes": [ + { + "name": "name", + "type": "string", + "unique": true, + "required": true + }, + { + "name": "nickname", + "type": "string", + "unique": false, + "required": false + } + ], + "description": "" + }, + { + "label": "FightEvent", + "attributes": [ + { + "name": "name", + "type": "string", + "unique": true, + "required": true + }, + { + "name": "date", + "type": "string", + "unique": false, + "required": true + }, + { + "name": "location", + "type": "string", + "unique": false, + "required": true + } + ], + "description": "" + }, + { + "label": "Bout", + "attributes": [ + { + "name": "weight_class", + "type": "string", + "unique": false, + "required": true + }, + { + "name": "method", + "type": "string", + "unique": false, + "required": true + }, + { + "name": "round", + "type": "number", + "unique": false, + "required": true + }, + { + "name": "time", + "type": "string", + "unique": false, + "required": true + }, + { + "name": "time_format", + "type": "string", + "unique": false, + "required": true + }, + { + "name": "referee", + "type": "string", + "unique": false, + "required": false + } + ], + "description": "" + } + ], + "relations": [ + { + "label": "FOUGHT_IN", + "source": { + "label": "Person" + }, + "target": { + "label": "Bout" + }, + "attributes": [ + { + "name": "result", + "type": "string", + "unique": false, + "required": true + }, + { + "name": "knockdowns", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "significant_strikes", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "significant_strikes_attempted", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "significant_strikes_percentage", + "type": "string", + "unique": false, + "required": false + }, + { + "name": "total_strikes", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "total_strikes_attempted", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "takedowns", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "takedowns_attempted", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "takedown_percentage", + "type": "string", + "unique": false, + "required": false + }, + { + "name": "submissions_attempted", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "passes", + "type": "number", + "unique": false, + "required": false + }, + { + "name": "reversals", + "type": "number", + "unique": false, + "required": false + } + ] + }, + { + "label": "PART_OF_EVENT", + "source": { + "label": "Bout" + }, + "target": { + "label": "FightEvent" + }, + "attributes": [] + }, + { + "label": "PART_OF", + "source": { + "label": "Bout" + }, + "target": { + "label": "FightEvent" + }, + "attributes": [] + } + ] +} \ No newline at end of file From 4a194411a35b9361c31c9f65b975b74e65d272d1 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 17:28:39 +0200 Subject: [PATCH 14/15] erv-ollama-q --- tests/test_kg_ollama.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_kg_ollama.py b/tests/test_kg_ollama.py index 1acc6ecb..06ec03ee 100644 --- a/tests/test_kg_ollama.py +++ b/tests/test_kg_ollama.py @@ -90,7 +90,7 @@ def test_kg_creation(self): self.kg.process_sources(sources) chat = self.kg.chat_session() - answer = chat.send_message("Count the number of actors acted in a movie?") + answer = chat.send_message("How many actors acted in a movie?") answer = answer['response'] logger.info(f"Answer: {answer}") From 7c671ecfd32d6587bf24e5a4839eb598a5458157 Mon Sep 17 00:00:00 2001 From: Gal Shubeli Date: Sun, 24 Nov 2024 17:38:11 +0200 Subject: [PATCH 15/15] del-ontology-json --- ontology.json | 199 -------------------------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 ontology.json diff --git a/ontology.json b/ontology.json deleted file mode 100644 index f9c16cc3..00000000 --- a/ontology.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "entities": [ - { - "label": "Person", - "attributes": [ - { - "name": "name", - "type": "string", - "unique": true, - "required": true - }, - { - "name": "nickname", - "type": "string", - "unique": false, - "required": false - } - ], - "description": "" - }, - { - "label": "FightEvent", - "attributes": [ - { - "name": "name", - "type": "string", - "unique": true, - "required": true - }, - { - "name": "date", - "type": "string", - "unique": false, - "required": true - }, - { - "name": "location", - "type": "string", - "unique": false, - "required": true - } - ], - "description": "" - }, - { - "label": "Bout", - "attributes": [ - { - "name": "weight_class", - "type": "string", - "unique": false, - "required": true - }, - { - "name": "method", - "type": "string", - "unique": false, - "required": true - }, - { - "name": "round", - "type": "number", - "unique": false, - "required": true - }, - { - "name": "time", - "type": "string", - "unique": false, - "required": true - }, - { - "name": "time_format", - "type": "string", - "unique": false, - "required": true - }, - { - "name": "referee", - "type": "string", - "unique": false, - "required": false - } - ], - "description": "" - } - ], - "relations": [ - { - "label": "FOUGHT_IN", - "source": { - "label": "Person" - }, - "target": { - "label": "Bout" - }, - "attributes": [ - { - "name": "result", - "type": "string", - "unique": false, - "required": true - }, - { - "name": "knockdowns", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "significant_strikes", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "significant_strikes_attempted", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "significant_strikes_percentage", - "type": "string", - "unique": false, - "required": false - }, - { - "name": "total_strikes", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "total_strikes_attempted", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "takedowns", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "takedowns_attempted", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "takedown_percentage", - "type": "string", - "unique": false, - "required": false - }, - { - "name": "submissions_attempted", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "passes", - "type": "number", - "unique": false, - "required": false - }, - { - "name": "reversals", - "type": "number", - "unique": false, - "required": false - } - ] - }, - { - "label": "PART_OF_EVENT", - "source": { - "label": "Bout" - }, - "target": { - "label": "FightEvent" - }, - "attributes": [] - }, - { - "label": "PART_OF", - "source": { - "label": "Bout" - }, - "target": { - "label": "FightEvent" - }, - "attributes": [] - } - ] -} \ No newline at end of file