From 65b159db8eb6f9798c15646b643ca03492a18bbf Mon Sep 17 00:00:00 2001 From: nonegom Date: Sat, 22 Mar 2025 13:35:20 +0900 Subject: [PATCH 01/15] feat: prompt load from prompt file --- llm_utils/chains.py | 35 ++++++----------------------------- prompt/system_prompt.yaml | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 prompt/system_prompt.yaml diff --git a/llm_utils/chains.py b/llm_utils/chains.py index d9e5e6c..cb9073d 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -1,5 +1,5 @@ import os -from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, load_prompt, SystemMessagePromptTemplate from .llm_factory import get_llm @@ -74,36 +74,10 @@ def create_query_refiner_chain(llm): # QueryMakerChain def create_query_maker_chain(llm): + prompt = load_prompt("../prompt/system_prompt.yaml", encoding="utf-8") query_maker_prompt = ChatPromptTemplate.from_messages( [ - ( - "system", - """ - 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. - 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. - - 주의사항: - - 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. - - 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. - - 최종 출력 형식은 반드시 아래와 같아야 합니다. - - 최종 형태 예시: - - - ```sql - SELECT COUNT(DISTINCT user_id) - FROM stg_users - ``` - - <해석> - ```plaintext (max_length_per_line=100) - 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. - 사용자는 유니크한 user_id를 가지고 있으며 - 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. - ``` - - """, - ), + SystemMessagePromptTemplate.from_template(prompt.template), ( "system", "아래는 사용자의 질문 및 구체화된 질문입니다:", @@ -127,3 +101,6 @@ def create_query_maker_chain(llm): query_refiner_chain = create_query_refiner_chain(llm) query_maker_chain = create_query_maker_chain(llm) + +if __name__ == "__main__": + query_refiner_chain.invoke() \ No newline at end of file diff --git a/prompt/system_prompt.yaml b/prompt/system_prompt.yaml new file mode 100644 index 0000000..cc45149 --- /dev/null +++ b/prompt/system_prompt.yaml @@ -0,0 +1,27 @@ +_type: prompt +template: | + 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. + 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. + + 주의사항: + - 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. + - 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. + - 최종 출력 형식은 반드시 아래와 같아야 합니다. + + 최종 형태 예시: + + + ```sql + SELECT COUNT(DISTINCT user_id) + FROM stg_users + ``` + + <해석> + ```plaintext (max_length_per_line=100) + 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. + 사용자는 유니크한 user_id를 가지고 있으며 + 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. + ``` +template_format: f-string +name: query chain 시스템 프롬프트 +description: query의 기본이 되는 시스템 프롬프트입니다 \ No newline at end of file From c1b39d7a10f69f817ac323c42c5589d8ed138c45 Mon Sep 17 00:00:00 2001 From: nonegom Date: Sat, 22 Mar 2025 13:36:15 +0900 Subject: [PATCH 02/15] feat: SQL Prompt class and update prompt from file function --- llm_utils/prompts_class.py | 33 +++++++++++++++++++++++++++++++++ prompt/mysql.yaml | 21 +++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 llm_utils/prompts_class.py create mode 100644 prompt/mysql.yaml diff --git a/llm_utils/prompts_class.py b/llm_utils/prompts_class.py new file mode 100644 index 0000000..d8bb6c2 --- /dev/null +++ b/llm_utils/prompts_class.py @@ -0,0 +1,33 @@ +from langchain.chains.sql_database.prompt import SQL_PROMPTS +import os + +from langchain_core.prompts import load_prompt + + +class SQLPrompt(): + def __init__(self): + # os library를 확인해서 SQL_PROMPTS key에 해당하는ㅁ prompt가 있으면, 이를 교체 + self.sql_prompts = SQL_PROMPTS + self.target_db_list = list(SQL_PROMPTS.keys()) + self.prompt_path = '../prompt' + + def update_prompt_from_path(self): + if os.path.exists(self.prompt_path): + path_list = os.listdir(self.prompt_path) + # yaml 파일만 가져옴 + file_list = [file for file in path_list if file.endswith('.yaml')] + key_path_dict = {key.split('.')[0]: os.path.join(self.prompt_path, key) for key in file_list if key.split('.')[0] in self.target_db_list} + # file_list에서 sql_prompts의 key에 해당하는 파일이 있는 것만 가져옴 + for key, path in key_path_dict.items(): + self.sql_prompts[key] = load_prompt(path, encoding='utf-8') + else: + raise FileNotFoundError(f"Prompt file not found in {self.prompt_path}") + return False + +if __name__ == '__main__': + sql_prompts_class = SQLPrompt() + print(sql_prompts_class.sql_prompts['mysql']) + print(sql_prompts_class.update_prompt_from_path()) + + print(sql_prompts_class.sql_prompts['mysql']) + print(sql_prompts_class.sql_prompts) \ No newline at end of file diff --git a/prompt/mysql.yaml b/prompt/mysql.yaml new file mode 100644 index 0000000..ebe7223 --- /dev/null +++ b/prompt/mysql.yaml @@ -0,0 +1,21 @@ +_type: prompt +template: | + 커스텀 MySQL 프롬프트입니다. + You are a MySQL expert. Given an input question, first create a syntactically correct MySQL query to run, then look at the results of the query and return the answer to the input question. + Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per MySQL. You can order the results to return the most informative data in the database. + Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in backticks (`) to denote them as delimited identifiers. + Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table. + Pay attention to use CURDATE() function to get the current date, if the question involves "today". + + Use the following format: + + Question: Question here + SQLQuery: SQL Query to run + SQLResult: Result of the SQLQuery + Answer: Final answer here + + Only use the following tables: + {table_info} + + Question: {input} +input_variables: ["input", "table_info", "top_k"] From 453f825247ac585bc67f807db6efb9a790807f46 Mon Sep 17 00:00:00 2001 From: nonegom Date: Sun, 6 Apr 2025 21:04:28 +0900 Subject: [PATCH 03/15] feat: chat prompt sample example --- llm_utils/chains.py | 17 ++++++++++++ prompt/create_query_maker_chain.yaml | 40 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 prompt/create_query_maker_chain.yaml diff --git a/llm_utils/chains.py b/llm_utils/chains.py index cb9073d..05568c1 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -4,6 +4,7 @@ from .llm_factory import get_llm from dotenv import load_dotenv +import yaml env_path = os.path.join(os.getcwd(), ".env") @@ -74,6 +75,7 @@ def create_query_refiner_chain(llm): # QueryMakerChain def create_query_maker_chain(llm): + # SystemPrompt만 yaml 파일로 불러와서 사용 prompt = load_prompt("../prompt/system_prompt.yaml", encoding="utf-8") query_maker_prompt = ChatPromptTemplate.from_messages( [ @@ -99,6 +101,21 @@ def create_query_maker_chain(llm): return query_maker_prompt | llm +def create_query_maker_chain_from_chat_promt(llm): + """ + ChatPromptTemplate 형식으로 저장된 yaml 파일을 불러와서 사용 (코드가 간소화되지만, 별도의 후처리 작업이 필요) + """ + with open("../prompt/create_query_maker_chain.yaml", "r", encoding="utf-8") as f: + chat_prompt_dict = yaml.safe_load(f) + + messages = chat_prompt_dict['messages'] + template = messages[0]["prompt"].pop("template") if messages else None + template = [tuple(item) for item in template] + query_maker_prompt = ChatPromptTemplate.from_messages(template) + + return query_maker_prompt | llm + + query_refiner_chain = create_query_refiner_chain(llm) query_maker_chain = create_query_maker_chain(llm) diff --git a/prompt/create_query_maker_chain.yaml b/prompt/create_query_maker_chain.yaml new file mode 100644 index 0000000..b0e76ce --- /dev/null +++ b/prompt/create_query_maker_chain.yaml @@ -0,0 +1,40 @@ +_type: chat +messages: + - prompt: + template: + - ["system", " + 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. + 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. + + <<주의사항>> + > 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. + > 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. + > 최종 출력 형식은 반드시 아래와 같아야 합니다. + + <<최종 형태 예시>> + + + ```sql + SELECT COUNT(DISTINCT user_id) + FROM stg_users + ``` + + <해석> + ```plaintext (max_length_per_line=100) + 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. + 사용자는 유니크한 user_id를 가지고 있으며 + 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. + ``` + "] + - ["placeholder", "{user_input}" ] + - ["placeholder", "{refined_input}" ] + - ["system", "다음은 사용자의 db 환경정보와 사용 가능한 테이블 및 컬럼 정보입니다" ] + - ["placeholder", "{user_database_env}" ] + - ["placeholder", "{searched_tables}" ] + - ["system", "위 정보를 바탕으로 사용자 질문에 대한 최적의 SQL 쿼리를 최종 형태 예시와 같은 형태로 생성하세요." ] + +input_variables: + - user_input + - refined_input + - user_database_env + - searched_tables From 68bc8fd5a1003e25b3a8b3eaa90ed149e897b2d6 Mon Sep 17 00:00:00 2001 From: nonegom Date: Sun, 6 Apr 2025 21:04:43 +0900 Subject: [PATCH 04/15] feat: markdown prompt example --- prompt/prompt_md_sample.md | 26 ++++++++++++++++++++++++++ prompt/template.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 prompt/prompt_md_sample.md create mode 100644 prompt/template.py diff --git a/prompt/prompt_md_sample.md b/prompt/prompt_md_sample.md new file mode 100644 index 0000000..22b8def --- /dev/null +++ b/prompt/prompt_md_sample.md @@ -0,0 +1,26 @@ +--- +CURRENT_TIME: <> +--- + +You are a web browser interaction specialist. Your task is to understand natural language instructions and translate them into browser actions. + +# Steps + +When given a natural language task, you will: +1. Navigate to websites (e.g., 'Go to example.com') +2. Perform actions like clicking, typing, and scrolling (e.g., 'Click the login button', 'Type hello into the search box') +3. Extract information from web pages (e.g., 'Find the price of the first product', 'Get the title of the main article') + +# Examples + +Examples of valid instructions: +- 'Go to google.com and search for Python programming' +- 'Navigate to GitHub, find the trending repositories for Python' +- 'Visit twitter.com and get the text of the top 3 trending topics' + +# Notes + +- Always respond with clear, step-by-step actions in natural language that describe what you want the browser to do. +- Do not do any math. +- Do not do any file operations. +- Always use the same language as the initial question. \ No newline at end of file diff --git a/prompt/template.py b/prompt/template.py new file mode 100644 index 0000000..e714c2f --- /dev/null +++ b/prompt/template.py @@ -0,0 +1,32 @@ +import os +import re +from datetime import datetime + +from langchain_core.prompts import PromptTemplate +from langgraph.prebuilt.chat_agent_executor import AgentState + + +def get_prompt_template(prompt_name: str) -> str: + template = open(os.path.join(os.path.dirname(__file__), f"{prompt_name}.md")).read() + + # Escape curly braces using backslash (중괄호를 문자로 처리) + template = template.replace("{", "{{").replace("}", "}}") + + # Replace `<>` with `{VAR}` + template = re.sub(r"<<([^>>]+)>>", r"{\1}", template) + return template + + +def apply_prompt_template(prompt_name: str, state: AgentState) -> list: + system_prompt = PromptTemplate( + input_variables=["CURRENT_TIME"], + template=get_prompt_template(prompt_name), + ).format(CURRENT_TIME=datetime.now().strftime("%a %b %d %Y %H:%M:%S %z"), **state) + + # system prompt template 설정 + return [{"role": "system", "content": system_prompt}] + state["messages"] + + +if __name__ == "__main__": + print(get_prompt_template("prompt_md_sample")) + # print(apply_prompt_template("prompt_md_sample", {"messages": []})) \ No newline at end of file From a204bbbd4beffc505c474f4469f5302f63e3d1a9 Mon Sep 17 00:00:00 2001 From: nonegom Date: Sat, 22 Mar 2025 13:35:20 +0900 Subject: [PATCH 05/15] feat: prompt load from prompt file --- llm_utils/chains.py | 35 ++++++----------------------------- prompt/system_prompt.yaml | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 prompt/system_prompt.yaml diff --git a/llm_utils/chains.py b/llm_utils/chains.py index d9e5e6c..cb9073d 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -1,5 +1,5 @@ import os -from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, load_prompt, SystemMessagePromptTemplate from .llm_factory import get_llm @@ -74,36 +74,10 @@ def create_query_refiner_chain(llm): # QueryMakerChain def create_query_maker_chain(llm): + prompt = load_prompt("../prompt/system_prompt.yaml", encoding="utf-8") query_maker_prompt = ChatPromptTemplate.from_messages( [ - ( - "system", - """ - 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. - 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. - - 주의사항: - - 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. - - 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. - - 최종 출력 형식은 반드시 아래와 같아야 합니다. - - 최종 형태 예시: - - - ```sql - SELECT COUNT(DISTINCT user_id) - FROM stg_users - ``` - - <해석> - ```plaintext (max_length_per_line=100) - 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. - 사용자는 유니크한 user_id를 가지고 있으며 - 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. - ``` - - """, - ), + SystemMessagePromptTemplate.from_template(prompt.template), ( "system", "아래는 사용자의 질문 및 구체화된 질문입니다:", @@ -127,3 +101,6 @@ def create_query_maker_chain(llm): query_refiner_chain = create_query_refiner_chain(llm) query_maker_chain = create_query_maker_chain(llm) + +if __name__ == "__main__": + query_refiner_chain.invoke() \ No newline at end of file diff --git a/prompt/system_prompt.yaml b/prompt/system_prompt.yaml new file mode 100644 index 0000000..cc45149 --- /dev/null +++ b/prompt/system_prompt.yaml @@ -0,0 +1,27 @@ +_type: prompt +template: | + 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. + 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. + + 주의사항: + - 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. + - 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. + - 최종 출력 형식은 반드시 아래와 같아야 합니다. + + 최종 형태 예시: + + + ```sql + SELECT COUNT(DISTINCT user_id) + FROM stg_users + ``` + + <해석> + ```plaintext (max_length_per_line=100) + 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. + 사용자는 유니크한 user_id를 가지고 있으며 + 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. + ``` +template_format: f-string +name: query chain 시스템 프롬프트 +description: query의 기본이 되는 시스템 프롬프트입니다 \ No newline at end of file From 96c5fd94da6f77a23c0865c2072e8cfdcece0f2b Mon Sep 17 00:00:00 2001 From: nonegom Date: Sat, 22 Mar 2025 13:36:15 +0900 Subject: [PATCH 06/15] feat: SQL Prompt class and update prompt from file function --- llm_utils/prompts_class.py | 33 +++++++++++++++++++++++++++++++++ prompt/mysql.yaml | 21 +++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 llm_utils/prompts_class.py create mode 100644 prompt/mysql.yaml diff --git a/llm_utils/prompts_class.py b/llm_utils/prompts_class.py new file mode 100644 index 0000000..d8bb6c2 --- /dev/null +++ b/llm_utils/prompts_class.py @@ -0,0 +1,33 @@ +from langchain.chains.sql_database.prompt import SQL_PROMPTS +import os + +from langchain_core.prompts import load_prompt + + +class SQLPrompt(): + def __init__(self): + # os library를 확인해서 SQL_PROMPTS key에 해당하는ㅁ prompt가 있으면, 이를 교체 + self.sql_prompts = SQL_PROMPTS + self.target_db_list = list(SQL_PROMPTS.keys()) + self.prompt_path = '../prompt' + + def update_prompt_from_path(self): + if os.path.exists(self.prompt_path): + path_list = os.listdir(self.prompt_path) + # yaml 파일만 가져옴 + file_list = [file for file in path_list if file.endswith('.yaml')] + key_path_dict = {key.split('.')[0]: os.path.join(self.prompt_path, key) for key in file_list if key.split('.')[0] in self.target_db_list} + # file_list에서 sql_prompts의 key에 해당하는 파일이 있는 것만 가져옴 + for key, path in key_path_dict.items(): + self.sql_prompts[key] = load_prompt(path, encoding='utf-8') + else: + raise FileNotFoundError(f"Prompt file not found in {self.prompt_path}") + return False + +if __name__ == '__main__': + sql_prompts_class = SQLPrompt() + print(sql_prompts_class.sql_prompts['mysql']) + print(sql_prompts_class.update_prompt_from_path()) + + print(sql_prompts_class.sql_prompts['mysql']) + print(sql_prompts_class.sql_prompts) \ No newline at end of file diff --git a/prompt/mysql.yaml b/prompt/mysql.yaml new file mode 100644 index 0000000..ebe7223 --- /dev/null +++ b/prompt/mysql.yaml @@ -0,0 +1,21 @@ +_type: prompt +template: | + 커스텀 MySQL 프롬프트입니다. + You are a MySQL expert. Given an input question, first create a syntactically correct MySQL query to run, then look at the results of the query and return the answer to the input question. + Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per MySQL. You can order the results to return the most informative data in the database. + Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in backticks (`) to denote them as delimited identifiers. + Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table. + Pay attention to use CURDATE() function to get the current date, if the question involves "today". + + Use the following format: + + Question: Question here + SQLQuery: SQL Query to run + SQLResult: Result of the SQLQuery + Answer: Final answer here + + Only use the following tables: + {table_info} + + Question: {input} +input_variables: ["input", "table_info", "top_k"] From 9917d74e9dceef26eef4bcb3e6587ed9dedbd2fb Mon Sep 17 00:00:00 2001 From: nonegom Date: Sun, 6 Apr 2025 21:04:28 +0900 Subject: [PATCH 07/15] feat: chat prompt sample example --- llm_utils/chains.py | 17 ++++++++++++ prompt/create_query_maker_chain.yaml | 40 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 prompt/create_query_maker_chain.yaml diff --git a/llm_utils/chains.py b/llm_utils/chains.py index cb9073d..05568c1 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -4,6 +4,7 @@ from .llm_factory import get_llm from dotenv import load_dotenv +import yaml env_path = os.path.join(os.getcwd(), ".env") @@ -74,6 +75,7 @@ def create_query_refiner_chain(llm): # QueryMakerChain def create_query_maker_chain(llm): + # SystemPrompt만 yaml 파일로 불러와서 사용 prompt = load_prompt("../prompt/system_prompt.yaml", encoding="utf-8") query_maker_prompt = ChatPromptTemplate.from_messages( [ @@ -99,6 +101,21 @@ def create_query_maker_chain(llm): return query_maker_prompt | llm +def create_query_maker_chain_from_chat_promt(llm): + """ + ChatPromptTemplate 형식으로 저장된 yaml 파일을 불러와서 사용 (코드가 간소화되지만, 별도의 후처리 작업이 필요) + """ + with open("../prompt/create_query_maker_chain.yaml", "r", encoding="utf-8") as f: + chat_prompt_dict = yaml.safe_load(f) + + messages = chat_prompt_dict['messages'] + template = messages[0]["prompt"].pop("template") if messages else None + template = [tuple(item) for item in template] + query_maker_prompt = ChatPromptTemplate.from_messages(template) + + return query_maker_prompt | llm + + query_refiner_chain = create_query_refiner_chain(llm) query_maker_chain = create_query_maker_chain(llm) diff --git a/prompt/create_query_maker_chain.yaml b/prompt/create_query_maker_chain.yaml new file mode 100644 index 0000000..b0e76ce --- /dev/null +++ b/prompt/create_query_maker_chain.yaml @@ -0,0 +1,40 @@ +_type: chat +messages: + - prompt: + template: + - ["system", " + 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. + 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. + + <<주의사항>> + > 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. + > 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. + > 최종 출력 형식은 반드시 아래와 같아야 합니다. + + <<최종 형태 예시>> + + + ```sql + SELECT COUNT(DISTINCT user_id) + FROM stg_users + ``` + + <해석> + ```plaintext (max_length_per_line=100) + 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. + 사용자는 유니크한 user_id를 가지고 있으며 + 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. + ``` + "] + - ["placeholder", "{user_input}" ] + - ["placeholder", "{refined_input}" ] + - ["system", "다음은 사용자의 db 환경정보와 사용 가능한 테이블 및 컬럼 정보입니다" ] + - ["placeholder", "{user_database_env}" ] + - ["placeholder", "{searched_tables}" ] + - ["system", "위 정보를 바탕으로 사용자 질문에 대한 최적의 SQL 쿼리를 최종 형태 예시와 같은 형태로 생성하세요." ] + +input_variables: + - user_input + - refined_input + - user_database_env + - searched_tables From 402f4a3dfa3f2ba23c1dc2c42224c6494eb60e17 Mon Sep 17 00:00:00 2001 From: nonegom Date: Sun, 6 Apr 2025 21:04:43 +0900 Subject: [PATCH 08/15] feat: markdown prompt example --- prompt/prompt_md_sample.md | 26 ++++++++++++++++++++++++++ prompt/template.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 prompt/prompt_md_sample.md create mode 100644 prompt/template.py diff --git a/prompt/prompt_md_sample.md b/prompt/prompt_md_sample.md new file mode 100644 index 0000000..22b8def --- /dev/null +++ b/prompt/prompt_md_sample.md @@ -0,0 +1,26 @@ +--- +CURRENT_TIME: <> +--- + +You are a web browser interaction specialist. Your task is to understand natural language instructions and translate them into browser actions. + +# Steps + +When given a natural language task, you will: +1. Navigate to websites (e.g., 'Go to example.com') +2. Perform actions like clicking, typing, and scrolling (e.g., 'Click the login button', 'Type hello into the search box') +3. Extract information from web pages (e.g., 'Find the price of the first product', 'Get the title of the main article') + +# Examples + +Examples of valid instructions: +- 'Go to google.com and search for Python programming' +- 'Navigate to GitHub, find the trending repositories for Python' +- 'Visit twitter.com and get the text of the top 3 trending topics' + +# Notes + +- Always respond with clear, step-by-step actions in natural language that describe what you want the browser to do. +- Do not do any math. +- Do not do any file operations. +- Always use the same language as the initial question. \ No newline at end of file diff --git a/prompt/template.py b/prompt/template.py new file mode 100644 index 0000000..e714c2f --- /dev/null +++ b/prompt/template.py @@ -0,0 +1,32 @@ +import os +import re +from datetime import datetime + +from langchain_core.prompts import PromptTemplate +from langgraph.prebuilt.chat_agent_executor import AgentState + + +def get_prompt_template(prompt_name: str) -> str: + template = open(os.path.join(os.path.dirname(__file__), f"{prompt_name}.md")).read() + + # Escape curly braces using backslash (중괄호를 문자로 처리) + template = template.replace("{", "{{").replace("}", "}}") + + # Replace `<>` with `{VAR}` + template = re.sub(r"<<([^>>]+)>>", r"{\1}", template) + return template + + +def apply_prompt_template(prompt_name: str, state: AgentState) -> list: + system_prompt = PromptTemplate( + input_variables=["CURRENT_TIME"], + template=get_prompt_template(prompt_name), + ).format(CURRENT_TIME=datetime.now().strftime("%a %b %d %Y %H:%M:%S %z"), **state) + + # system prompt template 설정 + return [{"role": "system", "content": system_prompt}] + state["messages"] + + +if __name__ == "__main__": + print(get_prompt_template("prompt_md_sample")) + # print(apply_prompt_template("prompt_md_sample", {"messages": []})) \ No newline at end of file From 7130b1e11e7cbdd75e0ea63bdafdb0cfefde7fd2 Mon Sep 17 00:00:00 2001 From: changuk Date: Sun, 20 Apr 2025 19:07:10 +0900 Subject: [PATCH 09/15] feat: prompt loader --- llm_utils/tools.py | 1 + prompt/system_prompt.md | 26 ++++++++++++++++++++++ prompt/{template.py => template_loader.py} | 14 +++++------- 3 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 prompt/system_prompt.md rename prompt/{template.py => template_loader.py} (66%) diff --git a/llm_utils/tools.py b/llm_utils/tools.py index 6b8a9ac..59b54e0 100644 --- a/llm_utils/tools.py +++ b/llm_utils/tools.py @@ -32,6 +32,7 @@ def _get_table_info() -> Dict[str, str]: table_description = fetcher.get_table_description(urn) if table_name and table_description: table_info[table_name] = table_description + print(f'table_name {urn}') return table_info diff --git a/prompt/system_prompt.md b/prompt/system_prompt.md new file mode 100644 index 0000000..567adf9 --- /dev/null +++ b/prompt/system_prompt.md @@ -0,0 +1,26 @@ +# Role + +당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. +사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. + +# 주의사항: +- 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. +- 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. +- 최종 출력 형식은 반드시 아래와 같아야 합니다. + +# Output Example +최종 형태 예시: + +```sql + SELECT COUNT(DISTINCT user_id) + FROM stg_users +``` + +<해석> +```plaintext (max_length_per_line=100) + 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. + 사용자는 유니크한 user_id를 가지고 있으며 + 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. + +# Input Example +{user_query} \ No newline at end of file diff --git a/prompt/template.py b/prompt/template_loader.py similarity index 66% rename from prompt/template.py rename to prompt/template_loader.py index e714c2f..01f1940 100644 --- a/prompt/template.py +++ b/prompt/template_loader.py @@ -7,13 +7,11 @@ def get_prompt_template(prompt_name: str) -> str: - template = open(os.path.join(os.path.dirname(__file__), f"{prompt_name}.md")).read() - - # Escape curly braces using backslash (중괄호를 문자로 처리) - template = template.replace("{", "{{").replace("}", "}}") - - # Replace `<>` with `{VAR}` - template = re.sub(r"<<([^>>]+)>>", r"{\1}", template) + try: + with open(os.path.join(os.path.dirname(__file__), f"{prompt_name}.md"), "r", encoding="utf-8") as f: + template = f.read() + except FileNotFoundError: + raise FileNotFoundError(f"경고: '{prompt_name}.md' 파일을 찾을 수 없습니다.") return template @@ -28,5 +26,5 @@ def apply_prompt_template(prompt_name: str, state: AgentState) -> list: if __name__ == "__main__": - print(get_prompt_template("prompt_md_sample")) + print(get_prompt_template("system_prompt")) # print(apply_prompt_template("prompt_md_sample", {"messages": []})) \ No newline at end of file From ee7911f8eb0f85dc0539ac199ffc33e6ac130152 Mon Sep 17 00:00:00 2001 From: nonegom Date: Mon, 21 Apr 2025 01:23:31 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20query=20prompt=EB=A5=BC=20?= =?UTF-8?q?=EB=A7=88=ED=81=AC=EB=8B=A4=EC=9A=B4=20=ED=98=95=ED=83=9C?= =?UTF-8?q?=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface/streamlit_app.py | 4 +- llm_utils/chains.py | 60 +++---------------- llm_utils/graph.py | 10 ++-- ...system_prompt.md => query_maker_prompt.md} | 7 +-- prompt/query_refiner_prompt.md | 32 ++++++++++ 5 files changed, 49 insertions(+), 64 deletions(-) rename prompt/{system_prompt.md => query_maker_prompt.md} (93%) create mode 100644 prompt/query_refiner_prompt.md diff --git a/interface/streamlit_app.py b/interface/streamlit_app.py index 395b2b7..76fbec1 100644 --- a/interface/streamlit_app.py +++ b/interface/streamlit_app.py @@ -44,8 +44,8 @@ def summarize_total_tokens(data): # 결과 출력 st.write("총 토큰 사용량:", total_tokens) - # st.write("결과:", res["generated_query"].content) - st.write("결과:", "\n\n```sql\n" + res["generated_query"] + "\n```") + st.write("결과:", res["generated_query"].content) + # st.write("결과:", "\n\n```sql\n" + res["generated_query"] + "\n```") st.write("결과 설명:\n\n", res["messages"][-1].content) st.write("AI가 재해석한 사용자 질문:\n", res["refined_input"].content) st.write("참고한 테이블 목록:", res["searched_tables"]) diff --git a/llm_utils/chains.py b/llm_utils/chains.py index 05568c1..9ae22b3 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -4,6 +4,7 @@ from .llm_factory import get_llm from dotenv import load_dotenv +from prompt.template_loader import get_prompt_template import yaml env_path = os.path.join(os.getcwd(), ".env") @@ -13,6 +14,7 @@ else: print(f"⚠️ 환경변수 파일(.env)이 {os.getcwd()}에 없습니다!") + llm = get_llm( model_type="openai", model_name="gpt-4o-mini", @@ -21,45 +23,12 @@ def create_query_refiner_chain(llm): + prompt = get_prompt_template('query_refiner_prompt') tool_choice_prompt = ChatPromptTemplate.from_messages( [ - ( - "system", - """ - 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. - 현재 subscription_activities, contract_activities, marketing_activities, - sales_activities, success_activities, support_activities, trial_activities 데이터를 - 보유하고 있으며, 사용자의 질문이 모호할 경우에도 우리가 가진 데이터를 기반으로 - 충분히 답변 가능한 형태로 질문을 구체화해 주세요. - - 주의: - - 사용자에게 추가 정보를 요구하는 ‘재질문(추가 질문)’을 하지 마세요. - - 질문에 포함해야 할 요소(예: 특정 기간, 대상 유저 그룹, 분석 대상 로그 종류 등)가 - 불충분하더라도, 합리적으로 추론해 가정한 뒤 - 답변에 충분한 질문 형태로 완성해 주세요. - - 예시: - 사용자가 "유저 이탈 원인이 궁금해요"라고 했다면, - 재질문 형식이 아니라 - "최근 1개월 간의 접속·결제 로그를 기준으로, - 주로 어떤 사용자가 어떤 과정을 거쳐 이탈하는지를 분석해야 한다"처럼 - 분석 방향이 명확해진 질문 한 문장(또는 한 문단)으로 정리해 주세요. - - 최종 출력 형식 예시: - ------------------------------ - 구체화된 질문: - "최근 1개월 동안 고액 결제 경험이 있는 유저가 - 행동 로그에서 이탈 전 어떤 패턴을 보였는지 분석" - - 가정한 조건: - - 최근 1개월치 행동 로그와 결제 로그 중심 - - 고액 결제자(월 결제액 10만 원 이상) 그룹 대상으로 한정 - ------------------------------ - """, - ), + SystemMessagePromptTemplate.from_template(prompt), MessagesPlaceholder(variable_name="user_input"), - ( - "system", + SystemMessagePromptTemplate.from_template( """ 위 사용자의 입력을 바탕으로 분석 관점에서 **충분히 답변 가능한 형태**로 @@ -76,10 +45,10 @@ def create_query_refiner_chain(llm): # QueryMakerChain def create_query_maker_chain(llm): # SystemPrompt만 yaml 파일로 불러와서 사용 - prompt = load_prompt("../prompt/system_prompt.yaml", encoding="utf-8") + prompt = get_prompt_template('query_maker_prompt') query_maker_prompt = ChatPromptTemplate.from_messages( [ - SystemMessagePromptTemplate.from_template(prompt.template), + SystemMessagePromptTemplate.from_template(prompt), ( "system", "아래는 사용자의 질문 및 구체화된 질문입니다:", @@ -101,21 +70,6 @@ def create_query_maker_chain(llm): return query_maker_prompt | llm -def create_query_maker_chain_from_chat_promt(llm): - """ - ChatPromptTemplate 형식으로 저장된 yaml 파일을 불러와서 사용 (코드가 간소화되지만, 별도의 후처리 작업이 필요) - """ - with open("../prompt/create_query_maker_chain.yaml", "r", encoding="utf-8") as f: - chat_prompt_dict = yaml.safe_load(f) - - messages = chat_prompt_dict['messages'] - template = messages[0]["prompt"].pop("template") if messages else None - template = [tuple(item) for item in template] - query_maker_prompt = ChatPromptTemplate.from_messages(template) - - return query_maker_prompt | llm - - query_refiner_chain = create_query_refiner_chain(llm) query_maker_chain = create_query_maker_chain(llm) diff --git a/llm_utils/graph.py b/llm_utils/graph.py index 0aef51d..10fe0e5 100644 --- a/llm_utils/graph.py +++ b/llm_utils/graph.py @@ -35,6 +35,7 @@ class QueryMakerState(TypedDict): # 노드 함수: QUERY_REFINER 노드 def query_refiner_node(state: QueryMakerState): + print('query_refiner_node 진입 [md]') res = query_refiner_chain.invoke( input={ "user_input": [state["messages"][0].content], @@ -60,6 +61,7 @@ def get_table_info_node(state: QueryMakerState): ) except: documents = get_info_from_db() + print("db_embedding 진입") db = FAISS.from_documents(documents, embeddings) db.save_local(os.getcwd() + "/table_info_db") print("table_info_db not found") @@ -139,10 +141,10 @@ def query_maker_node_with_db_guide(state: QueryMakerState): # 노드 추가 builder.add_node(QUERY_REFINER, query_refiner_node) builder.add_node(GET_TABLE_INFO, get_table_info_node) -# builder.add_node(QUERY_MAKER, query_maker_node) # query_maker_node_with_db_guide -builder.add_node( - QUERY_MAKER, query_maker_node_with_db_guide -) # query_maker_node_with_db_guide +builder.add_node(QUERY_MAKER, query_maker_node) # query_maker_node_with_db_guide +# builder.add_node( +# QUERY_MAKER, query_maker_node_with_db_guide +# ) # query_maker_node_with_db_guide # 기본 엣지 설정 builder.add_edge(QUERY_REFINER, GET_TABLE_INFO) diff --git a/prompt/system_prompt.md b/prompt/query_maker_prompt.md similarity index 93% rename from prompt/system_prompt.md rename to prompt/query_maker_prompt.md index 567adf9..f08cd14 100644 --- a/prompt/system_prompt.md +++ b/prompt/query_maker_prompt.md @@ -3,7 +3,7 @@ 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. -# 주의사항: +# 주의사항 - 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. - 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. - 최종 출력 형식은 반드시 아래와 같아야 합니다. @@ -20,7 +20,4 @@ ```plaintext (max_length_per_line=100) 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. 사용자는 유니크한 user_id를 가지고 있으며 - 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. - -# Input Example -{user_query} \ No newline at end of file + 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. \ No newline at end of file diff --git a/prompt/query_refiner_prompt.md b/prompt/query_refiner_prompt.md new file mode 100644 index 0000000..fac74dd --- /dev/null +++ b/prompt/query_refiner_prompt.md @@ -0,0 +1,32 @@ +# Role + +당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. +현재 subscription_activities, contract_activities, marketing_activities, +sales_activities, success_activities, support_activities, trial_activities 데이터를 +보유하고 있으며, 사용자의 질문이 모호할 경우에도 우리가 가진 데이터를 기반으로 +충분히 답변 가능한 형태로 질문을 구체화해 주세요. + +# 주의사항: +- 사용자에게 추가 정보를 요구하는 ‘재질문(추가 질문)’을 하지 마세요. +- 질문에 포함해야 할 요소(예: 특정 기간, 대상 유저 그룹, 분석 대상 로그 종류 등)가 + 불충분하더라도, 합리적으로 추론해 가정한 뒤 + 답변에 충분한 질문 형태로 완성해 주세요. +예시: +사용자가 "유저 이탈 원인이 궁금해요"라고 했다면, +재질문 형식이 아니라 +"최근 1개월 간의 접속·결제 로그를 기준으로, +주로 어떤 사용자가 어떤 과정을 거쳐 이탈하는지를 분석해야 한다"처럼 +분석 방향이 명확해진 질문 한 문장(또는 한 문단)으로 정리해 주세요. + +# Output Example + +최종 출력 형식 예시: +------------------------------ +구체화된 질문: +"최근 1개월 동안 고액 결제 경험이 있는 유저가 +행동 로그에서 이탈 전 어떤 패턴을 보였는지 분석" + +가정한 조건: +- 최근 1개월치 행동 로그와 결제 로그 중심 +- 고액 결제자(월 결제액 10만 원 이상) 그룹 대상으로 한정 +------------------------------ \ No newline at end of file From 5e9a8ff2a84015541816c4ca8db3eb3df3fc8d5b Mon Sep 17 00:00:00 2001 From: nonegom Date: Mon, 21 Apr 2025 01:24:35 +0900 Subject: [PATCH 11/15] feat: remove unuse files --- prompt/prompt_md_sample.md | 26 -------------------------- prompt/system_prompt.yaml | 27 --------------------------- prompt/template.py | 32 -------------------------------- prompt/template_loader.py | 11 ----------- 4 files changed, 96 deletions(-) delete mode 100644 prompt/prompt_md_sample.md delete mode 100644 prompt/system_prompt.yaml delete mode 100644 prompt/template.py diff --git a/prompt/prompt_md_sample.md b/prompt/prompt_md_sample.md deleted file mode 100644 index 22b8def..0000000 --- a/prompt/prompt_md_sample.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -CURRENT_TIME: <> ---- - -You are a web browser interaction specialist. Your task is to understand natural language instructions and translate them into browser actions. - -# Steps - -When given a natural language task, you will: -1. Navigate to websites (e.g., 'Go to example.com') -2. Perform actions like clicking, typing, and scrolling (e.g., 'Click the login button', 'Type hello into the search box') -3. Extract information from web pages (e.g., 'Find the price of the first product', 'Get the title of the main article') - -# Examples - -Examples of valid instructions: -- 'Go to google.com and search for Python programming' -- 'Navigate to GitHub, find the trending repositories for Python' -- 'Visit twitter.com and get the text of the top 3 trending topics' - -# Notes - -- Always respond with clear, step-by-step actions in natural language that describe what you want the browser to do. -- Do not do any math. -- Do not do any file operations. -- Always use the same language as the initial question. \ No newline at end of file diff --git a/prompt/system_prompt.yaml b/prompt/system_prompt.yaml deleted file mode 100644 index cc45149..0000000 --- a/prompt/system_prompt.yaml +++ /dev/null @@ -1,27 +0,0 @@ -_type: prompt -template: | - 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. - 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. - - 주의사항: - - 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. - - 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. - - 최종 출력 형식은 반드시 아래와 같아야 합니다. - - 최종 형태 예시: - - - ```sql - SELECT COUNT(DISTINCT user_id) - FROM stg_users - ``` - - <해석> - ```plaintext (max_length_per_line=100) - 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. - 사용자는 유니크한 user_id를 가지고 있으며 - 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. - ``` -template_format: f-string -name: query chain 시스템 프롬프트 -description: query의 기본이 되는 시스템 프롬프트입니다 \ No newline at end of file diff --git a/prompt/template.py b/prompt/template.py deleted file mode 100644 index e714c2f..0000000 --- a/prompt/template.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import re -from datetime import datetime - -from langchain_core.prompts import PromptTemplate -from langgraph.prebuilt.chat_agent_executor import AgentState - - -def get_prompt_template(prompt_name: str) -> str: - template = open(os.path.join(os.path.dirname(__file__), f"{prompt_name}.md")).read() - - # Escape curly braces using backslash (중괄호를 문자로 처리) - template = template.replace("{", "{{").replace("}", "}}") - - # Replace `<>` with `{VAR}` - template = re.sub(r"<<([^>>]+)>>", r"{\1}", template) - return template - - -def apply_prompt_template(prompt_name: str, state: AgentState) -> list: - system_prompt = PromptTemplate( - input_variables=["CURRENT_TIME"], - template=get_prompt_template(prompt_name), - ).format(CURRENT_TIME=datetime.now().strftime("%a %b %d %Y %H:%M:%S %z"), **state) - - # system prompt template 설정 - return [{"role": "system", "content": system_prompt}] + state["messages"] - - -if __name__ == "__main__": - print(get_prompt_template("prompt_md_sample")) - # print(apply_prompt_template("prompt_md_sample", {"messages": []})) \ No newline at end of file diff --git a/prompt/template_loader.py b/prompt/template_loader.py index 01f1940..6f231bc 100644 --- a/prompt/template_loader.py +++ b/prompt/template_loader.py @@ -14,17 +14,6 @@ def get_prompt_template(prompt_name: str) -> str: raise FileNotFoundError(f"경고: '{prompt_name}.md' 파일을 찾을 수 없습니다.") return template - -def apply_prompt_template(prompt_name: str, state: AgentState) -> list: - system_prompt = PromptTemplate( - input_variables=["CURRENT_TIME"], - template=get_prompt_template(prompt_name), - ).format(CURRENT_TIME=datetime.now().strftime("%a %b %d %Y %H:%M:%S %z"), **state) - - # system prompt template 설정 - return [{"role": "system", "content": system_prompt}] + state["messages"] - - if __name__ == "__main__": print(get_prompt_template("system_prompt")) # print(apply_prompt_template("prompt_md_sample", {"messages": []})) \ No newline at end of file From d8a5cd507659f1bb3cfe93a99ee7a5f1fe4ce76d Mon Sep 17 00:00:00 2001 From: nonegom Date: Mon, 21 Apr 2025 01:24:45 +0900 Subject: [PATCH 12/15] chore: prompt init setting --- prompt/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 prompt/__init__.py diff --git a/prompt/__init__.py b/prompt/__init__.py new file mode 100644 index 0000000..e69de29 From ab5d4aebde05aff7145a9522aa70c6ff14123d57 Mon Sep 17 00:00:00 2001 From: nonegom Date: Mon, 21 Apr 2025 01:26:49 +0900 Subject: [PATCH 13/15] feat: remove unuse prompy .yaml --- prompt/create_query_maker_chain.yaml | 40 ---------------------------- prompt/mysql.yaml | 21 --------------- 2 files changed, 61 deletions(-) delete mode 100644 prompt/create_query_maker_chain.yaml delete mode 100644 prompt/mysql.yaml diff --git a/prompt/create_query_maker_chain.yaml b/prompt/create_query_maker_chain.yaml deleted file mode 100644 index b0e76ce..0000000 --- a/prompt/create_query_maker_chain.yaml +++ /dev/null @@ -1,40 +0,0 @@ -_type: chat -messages: - - prompt: - template: - - ["system", " - 당신은 데이터 분석 전문가(데이터 분석가 페르소나)입니다. - 사용자의 질문을 기반으로, 주어진 테이블과 컬럼 정보를 활용하여 적절한 SQL 쿼리를 생성하세요. - - <<주의사항>> - > 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요. - > 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요. - > 최종 출력 형식은 반드시 아래와 같아야 합니다. - - <<최종 형태 예시>> - - - ```sql - SELECT COUNT(DISTINCT user_id) - FROM stg_users - ``` - - <해석> - ```plaintext (max_length_per_line=100) - 이 쿼리는 stg_users 테이블에서 고유한 사용자의 수를 계산합니다. - 사용자는 유니크한 user_id를 가지고 있으며 - 중복을 제거하기 위해 COUNT(DISTINCT user_id)를 사용했습니다. - ``` - "] - - ["placeholder", "{user_input}" ] - - ["placeholder", "{refined_input}" ] - - ["system", "다음은 사용자의 db 환경정보와 사용 가능한 테이블 및 컬럼 정보입니다" ] - - ["placeholder", "{user_database_env}" ] - - ["placeholder", "{searched_tables}" ] - - ["system", "위 정보를 바탕으로 사용자 질문에 대한 최적의 SQL 쿼리를 최종 형태 예시와 같은 형태로 생성하세요." ] - -input_variables: - - user_input - - refined_input - - user_database_env - - searched_tables diff --git a/prompt/mysql.yaml b/prompt/mysql.yaml deleted file mode 100644 index ebe7223..0000000 --- a/prompt/mysql.yaml +++ /dev/null @@ -1,21 +0,0 @@ -_type: prompt -template: | - 커스텀 MySQL 프롬프트입니다. - You are a MySQL expert. Given an input question, first create a syntactically correct MySQL query to run, then look at the results of the query and return the answer to the input question. - Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per MySQL. You can order the results to return the most informative data in the database. - Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in backticks (`) to denote them as delimited identifiers. - Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table. - Pay attention to use CURDATE() function to get the current date, if the question involves "today". - - Use the following format: - - Question: Question here - SQLQuery: SQL Query to run - SQLResult: Result of the SQLQuery - Answer: Final answer here - - Only use the following tables: - {table_info} - - Question: {input} -input_variables: ["input", "table_info", "top_k"] From ec6f086b4d1bce6784c7fca14de48852bd180f6d Mon Sep 17 00:00:00 2001 From: nonegom Date: Mon, 21 Apr 2025 20:25:20 +0900 Subject: [PATCH 14/15] docs: apply ruff --- data_utils/datahub_source.py | 2 -- llm_utils/chains.py | 3 +-- prompt/template_loader.py | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/data_utils/datahub_source.py b/data_utils/datahub_source.py index 8b8e89f..fddb10d 100644 --- a/data_utils/datahub_source.py +++ b/data_utils/datahub_source.py @@ -1,5 +1,3 @@ -import datahub.emitter.mce_builder as builder -from datahub.emitter.mcp import MetadataChangeProposalWrapper from datahub.metadata.schema_classes import DatasetPropertiesClass, SchemaMetadataClass from datahub.emitter.rest_emitter import DatahubRestEmitter from datahub.ingestion.graph.client import DatahubClientConfig, DataHubGraph diff --git a/llm_utils/chains.py b/llm_utils/chains.py index 9ae22b3..3e23d93 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -1,11 +1,10 @@ import os -from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, load_prompt, SystemMessagePromptTemplate +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate from .llm_factory import get_llm from dotenv import load_dotenv from prompt.template_loader import get_prompt_template -import yaml env_path = os.path.join(os.getcwd(), ".env") diff --git a/prompt/template_loader.py b/prompt/template_loader.py index 6f231bc..c45956a 100644 --- a/prompt/template_loader.py +++ b/prompt/template_loader.py @@ -1,9 +1,5 @@ import os -import re -from datetime import datetime -from langchain_core.prompts import PromptTemplate -from langgraph.prebuilt.chat_agent_executor import AgentState def get_prompt_template(prompt_name: str) -> str: From 03c029210173dce58eeec1a9e57d4def96fba46c Mon Sep 17 00:00:00 2001 From: ehddnr301 Date: Thu, 24 Apr 2025 10:52:22 +0000 Subject: [PATCH 15/15] style: apply Black formatter to Python files --- interface/streamlit_app.py | 1 - llm_utils/chains.py | 13 +++++++++---- llm_utils/graph.py | 2 -- llm_utils/prompts_class.py | 24 ++++++++++-------------- prompt/template_loader.py | 11 +++++------ 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/interface/streamlit_app.py b/interface/streamlit_app.py index 383465e..83c85b1 100644 --- a/interface/streamlit_app.py +++ b/interface/streamlit_app.py @@ -8,4 +8,3 @@ ) pg.run() - diff --git a/llm_utils/chains.py b/llm_utils/chains.py index 5017374..81d957e 100644 --- a/llm_utils/chains.py +++ b/llm_utils/chains.py @@ -1,5 +1,9 @@ import os -from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate +from langchain_core.prompts import ( + ChatPromptTemplate, + MessagesPlaceholder, + SystemMessagePromptTemplate, +) from .llm_factory import get_llm @@ -15,8 +19,9 @@ llm = get_llm() + def create_query_refiner_chain(llm): - prompt = get_prompt_template('query_refiner_prompt') + prompt = get_prompt_template("query_refiner_prompt") tool_choice_prompt = ChatPromptTemplate.from_messages( [ SystemMessagePromptTemplate.from_template(prompt), @@ -38,7 +43,7 @@ def create_query_refiner_chain(llm): # QueryMakerChain def create_query_maker_chain(llm): # SystemPrompt만 yaml 파일로 불러와서 사용 - prompt = get_prompt_template('query_maker_prompt') + prompt = get_prompt_template("query_maker_prompt") query_maker_prompt = ChatPromptTemplate.from_messages( [ SystemMessagePromptTemplate.from_template(prompt), @@ -67,4 +72,4 @@ def create_query_maker_chain(llm): query_maker_chain = create_query_maker_chain(llm) if __name__ == "__main__": - query_refiner_chain.invoke() \ No newline at end of file + query_refiner_chain.invoke() diff --git a/llm_utils/graph.py b/llm_utils/graph.py index 3a328b7..a6f5137 100644 --- a/llm_utils/graph.py +++ b/llm_utils/graph.py @@ -35,7 +35,6 @@ class QueryMakerState(TypedDict): # 노드 함수: QUERY_REFINER 노드 def query_refiner_node(state: QueryMakerState): - print('query_refiner_node 진입 [md]') res = query_refiner_chain.invoke( input={ "user_input": [state["messages"][0].content], @@ -61,7 +60,6 @@ def get_table_info_node(state: QueryMakerState): ) except: documents = get_info_from_db() - print("db_embedding 진입") db = FAISS.from_documents(documents, embeddings) db.save_local(os.getcwd() + "/table_info_db") doc_res = db.similarity_search(state["messages"][-1].content) diff --git a/llm_utils/prompts_class.py b/llm_utils/prompts_class.py index d8bb6c2..ceeadbd 100644 --- a/llm_utils/prompts_class.py +++ b/llm_utils/prompts_class.py @@ -4,30 +4,26 @@ from langchain_core.prompts import load_prompt -class SQLPrompt(): +class SQLPrompt: def __init__(self): - # os library를 확인해서 SQL_PROMPTS key에 해당하는ㅁ prompt가 있으면, 이를 교체 + # os library를 확인해서 SQL_PROMPTS key에 해당하는 prompt가 있으면, 이를 교체 self.sql_prompts = SQL_PROMPTS self.target_db_list = list(SQL_PROMPTS.keys()) - self.prompt_path = '../prompt' + self.prompt_path = "../prompt" def update_prompt_from_path(self): if os.path.exists(self.prompt_path): path_list = os.listdir(self.prompt_path) # yaml 파일만 가져옴 - file_list = [file for file in path_list if file.endswith('.yaml')] - key_path_dict = {key.split('.')[0]: os.path.join(self.prompt_path, key) for key in file_list if key.split('.')[0] in self.target_db_list} + file_list = [file for file in path_list if file.endswith(".yaml")] + key_path_dict = { + key.split(".")[0]: os.path.join(self.prompt_path, key) + for key in file_list + if key.split(".")[0] in self.target_db_list + } # file_list에서 sql_prompts의 key에 해당하는 파일이 있는 것만 가져옴 for key, path in key_path_dict.items(): - self.sql_prompts[key] = load_prompt(path, encoding='utf-8') + self.sql_prompts[key] = load_prompt(path, encoding="utf-8") else: raise FileNotFoundError(f"Prompt file not found in {self.prompt_path}") return False - -if __name__ == '__main__': - sql_prompts_class = SQLPrompt() - print(sql_prompts_class.sql_prompts['mysql']) - print(sql_prompts_class.update_prompt_from_path()) - - print(sql_prompts_class.sql_prompts['mysql']) - print(sql_prompts_class.sql_prompts) \ No newline at end of file diff --git a/prompt/template_loader.py b/prompt/template_loader.py index c45956a..ba397cd 100644 --- a/prompt/template_loader.py +++ b/prompt/template_loader.py @@ -1,15 +1,14 @@ import os - def get_prompt_template(prompt_name: str) -> str: try: - with open(os.path.join(os.path.dirname(__file__), f"{prompt_name}.md"), "r", encoding="utf-8") as f: + with open( + os.path.join(os.path.dirname(__file__), f"{prompt_name}.md"), + "r", + encoding="utf-8", + ) as f: template = f.read() except FileNotFoundError: raise FileNotFoundError(f"경고: '{prompt_name}.md' 파일을 찾을 수 없습니다.") return template - -if __name__ == "__main__": - print(get_prompt_template("system_prompt")) - # print(apply_prompt_template("prompt_md_sample", {"messages": []})) \ No newline at end of file