From 1a74f51cbd59ce749b996f6724ae423937d1fd57 Mon Sep 17 00:00:00 2001 From: ParkGyeongTae Date: Sun, 21 Sep 2025 19:42:50 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=ED=8A=9C=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EC=96=BC=20=EA=B4=80=EB=A0=A8=20=ED=8C=8C=EC=9D=BC=EC=9D=98=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- README.md | 2 +- cli/__init__.py | 4 +- dev/create_faiss.py | 53 +++++++++++++++ dev/create_pgvector.py | 54 +++++++++++++++ docker/docker-compose.yml | 2 +- .../getting-started-without-datahub.md | 65 +++++++++++++------ llm_utils/README.md | 2 +- llm_utils/vectordb/faiss_db.py | 7 +- 9 files changed, 163 insertions(+), 29 deletions(-) create mode 100644 dev/create_faiss.py create mode 100644 dev/create_pgvector.py diff --git a/.gitignore b/.gitignore index c66353f..831ec5d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,4 @@ test_lhm/ .vscode table_info_db ko_reranker_local -create_faiss.py -*.csv \ No newline at end of file +*.csv diff --git a/README.md b/README.md index fa641c1..f7df946 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ lang2sql --vectordb-type pgvector run-streamlit # 위치 지정 예시 # FAISS: 인덱스 디렉토리 경로 지정 -lang2sql --vectordb-type faiss --vectordb-location ./table_info_db run-streamlit +lang2sql --vectordb-type faiss --vectordb-location ./dev/table_info_db run-streamlit # pgvector: 연결 문자열 지정 lang2sql --vectordb-type pgvector --vectordb-location "postgresql://user:pass@host:5432/db" run-streamlit diff --git a/cli/__init__.py b/cli/__init__.py index d3dce14..6f9ee8a 100644 --- a/cli/__init__.py +++ b/cli/__init__.py @@ -76,7 +76,7 @@ "VectorDB 위치 설정\n" "- FAISS: 디렉토리 경로 (예: ./my_vectordb)\n" "- pgvector: 연결 문자열 (예: postgresql://user:pass@host:port/db)\n" - "기본값: FAISS는 './table_info_db', pgvector는 환경변수 사용" + "기본값: FAISS는 './dev/table_info_db', pgvector는 환경변수 사용" ), ) # pylint: disable=redefined-outer-name @@ -278,7 +278,7 @@ def run_streamlit_cli_command(port: int) -> None: "VectorDB 위치 설정\n" "- FAISS: 디렉토리 경로 (예: ./my_vectordb)\n" "- pgvector: 연결 문자열 (예: postgresql://user:pass@host:port/db)\n" - "기본값: FAISS는 './table_info_db', pgvector는 환경변수 사용" + "기본값: FAISS는 './dev/table_info_db', pgvector는 환경변수 사용" ), ) def query_command( diff --git a/dev/create_faiss.py b/dev/create_faiss.py new file mode 100644 index 0000000..6547d41 --- /dev/null +++ b/dev/create_faiss.py @@ -0,0 +1,53 @@ +""" +dev/create_faiss.py + +CSV 파일에서 테이블과 컬럼 정보를 불러와 OpenAI 임베딩으로 벡터화한 뒤, +FAISS 인덱스를 생성하고 로컬 디렉토리에 저장한다. + +환경 변수: + OPEN_AI_KEY: OpenAI API 키 + OPEN_AI_EMBEDDING_MODEL: 사용할 임베딩 모델 이름 + +출력: + 지정된 OUTPUT_DIR 경로에 FAISS 인덱스 저장 +""" + +import csv +import os +from collections import defaultdict + +from dotenv import load_dotenv +from langchain_community.vectorstores import FAISS +from langchain_openai import OpenAIEmbeddings + +load_dotenv() +# CSV 파일 경로 +CSV_PATH = "./dev/table_catalog.csv" +# .env의 VECTORDB_LOCATION과 동일하게 맞추세요 +OUTPUT_DIR = "./dev/table_info_db" + +tables = defaultdict(lambda: {"desc": "", "columns": []}) +with open(CSV_PATH, newline="", encoding="utf-8") as f: + reader = csv.DictReader(f) + for row in reader: + t = row["table_name"].strip() + tables[t]["desc"] = row["table_description"].strip() + col = row["column_name"].strip() + col_desc = row["column_description"].strip() + tables[t]["columns"].append((col, col_desc)) + +docs = [] +for t, info in tables.items(): + cols = "\n".join([f"{c}: {d}" for c, d in info["columns"]]) + page = f"{t}: {info['desc']}\nColumns:\n {cols}" + from langchain.schema import Document + + docs.append(Document(page_content=page)) + +emb = OpenAIEmbeddings( + model=os.getenv("OPEN_AI_EMBEDDING_MODEL"), openai_api_key=os.getenv("OPEN_AI_KEY") +) +db = FAISS.from_documents(docs, emb) +os.makedirs(OUTPUT_DIR, exist_ok=True) +db.save_local(OUTPUT_DIR) +print(f"FAISS index saved to: {OUTPUT_DIR}") diff --git a/dev/create_pgvector.py b/dev/create_pgvector.py new file mode 100644 index 0000000..77edd9f --- /dev/null +++ b/dev/create_pgvector.py @@ -0,0 +1,54 @@ +""" +dev/create_pgvector.py + +CSV 파일에서 테이블과 컬럼 정보를 불러와 OpenAI 임베딩으로 벡터화한 뒤, +pgvector에 적재한다. + +환경 변수: + OPEN_AI_KEY: OpenAI API 키 + OPEN_AI_EMBEDDING_MODEL: 사용할 임베딩 모델 이름 + VECTORDB_LOCATION: pgvector 연결 문자열 + PGVECTOR_COLLECTION: pgvector 컬렉션 이름 +""" + +import csv +import os +from collections import defaultdict + +from dotenv import load_dotenv +from langchain.schema import Document +from langchain_openai import OpenAIEmbeddings +from langchain_postgres.vectorstores import PGVector + +load_dotenv() +# CSV 파일 경로 +CSV_PATH = "./dev/table_catalog.csv" +# .env의 VECTORDB_LOCATION과 동일하게 맞추세요 +CONN = ( + os.getenv("VECTORDB_LOCATION") + or "postgresql://pgvector:pgvector@localhost:5432/postgres" +) +COLLECTION = os.getenv("PGVECTOR_COLLECTION", "table_info_db") + +tables = defaultdict(lambda: {"desc": "", "columns": []}) +with open(CSV_PATH, newline="", encoding="utf-8") as f: + reader = csv.DictReader(f) + for row in reader: + t = row["table_name"].strip() + tables[t]["desc"] = row["table_description"].strip() + col = row["column_name"].strip() + col_desc = row["column_description"] + tables[t]["columns"].append((col, col_desc)) + +docs = [] +for t, info in tables.items(): + cols = "\n".join([f"{c}: {d}" for c, d in info["columns"]]) + docs.append(Document(page_content=f"{t}: {info['desc']}\nColumns:\n {cols}")) + +emb = OpenAIEmbeddings( + model=os.getenv("OPEN_AI_EMBEDDING_MODEL"), openai_api_key=os.getenv("OPEN_AI_KEY") +) +PGVector.from_documents( + documents=docs, embedding=emb, connection=CONN, collection_name=COLLECTION +) +print(f"pgvector collection populated: {COLLECTION}") diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ed343f3..115575a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -13,7 +13,7 @@ services: - ../.env environment: - STREAMLIT_SERVER_PORT=8501 - - DATABASE_URL=postgresql://pgvector:pgvector@pgvector:5432/streamlit + - DATABASE_URL=postgresql://pgvector:pgvector@localhost:5432/streamlit depends_on: - pgvector diff --git a/docs/tutorials/getting-started-without-datahub.md b/docs/tutorials/getting-started-without-datahub.md index f0894e7..b2e4e7c 100644 --- a/docs/tutorials/getting-started-without-datahub.md +++ b/docs/tutorials/getting-started-without-datahub.md @@ -33,11 +33,11 @@ OPEN_AI_EMBEDDING_MODEL=text-embedding-3-large # 권장 # VectorDB (선택: 명시하지 않으면 기본값 동작) VECTORDB_TYPE=faiss -VECTORDB_LOCATION=./table_info_db # FAISS 디렉토리 경로 +VECTORDB_LOCATION=dev/table_info_db # FAISS 디렉토리 경로 # (pgvector를 쓰는 경우) # VECTORDB_TYPE=pgvector -# VECTORDB_LOCATION=postgresql://user:pass@host:5432/db +# VECTORDB_LOCATION=postgresql://pgvector:pgvector@localhost:5432/postgres # PGVECTOR_COLLECTION=table_info_db # DB 타입 @@ -46,9 +46,8 @@ DB_TYPE=clickhouse 중요: 코드상 OpenAI 키는 `OPEN_AI_KEY` 환경변수를 사용합니다. `.example.env`의 `OPENAI_API_KEY`는 사용되지 않으니 혼동에 주의하세요. -### 2) 테이블/컬럼 메타데이터 준비(CSV 예시) -- table_catalog.csv - +### 2) 테이블/컬럼 메타데이터 준비 (CSV 예시) +- dev/table_catalog.csv 파일을 생성합니다. ```csv table_name,table_description,column_name,column_description customers,고객 정보 테이블,customer_id,고객 고유 ID @@ -60,11 +59,12 @@ orders,주문 정보 테이블,amount,결제 금액 orders,주문 정보 테이블,status,주문 상태 ``` -### 3) FAISS 인덱스 생성(로컬) - +### 3) FAISS 인덱스 생성 (로컬) +- dev/create_faiss.py 파일을 실행합니다. +- `python dev/create_faiss.py` ```python """ -create_faiss.py +dev/create_faiss.py CSV 파일에서 테이블과 컬럼 정보를 불러와 OpenAI 임베딩으로 벡터화한 뒤, FAISS 인덱스를 생성하고 로컬 디렉토리에 저장한다. @@ -86,8 +86,10 @@ from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings load_dotenv() -CSV_PATH = "./table_catalog.csv" # 위 CSV 파일 경로 -OUTPUT_DIR = "./table_info_db" # .env 파일의 VECTORDB_LOCATION 값과 동일하게 맞추세요. +# CSV 파일 경로 +CSV_PATH = "./dev/table_catalog.csv" +# .env의 VECTORDB_LOCATION과 동일하게 맞추세요 +OUTPUT_DIR = "./dev/table_info_db" tables = defaultdict(lambda: {"desc": "", "columns": []}) with open(CSV_PATH, newline="", encoding="utf-8") as f: @@ -120,26 +122,47 @@ print(f"FAISS index saved to: {OUTPUT_DIR}") ```bash # Streamlit UI -lang2sql --vectordb-type faiss --vectordb-location ./table_info_db run-streamlit +lang2sql --vectordb-type faiss --vectordb-location ./dev/table_info_db run-streamlit # CLI 예시 -lang2sql query "주문 수를 집계하는 SQL을 만들어줘" --vectordb-type faiss --vectordb-location ./table_info_db +lang2sql query "주문 수를 집계하는 SQL을 만들어줘" --vectordb-type faiss --vectordb-location ./dev/table_info_db # CLI 예시 (pgvector) -lang2sql query "주문 수를 집계하는 SQL을 만들어줘" --vectordb-type pgvector --vectordb-location "postgresql://postgres:postgres@localhost:5431/postgres" +lang2sql query "주문 수를 집계하는 SQL을 만들어줘" --vectordb-type pgvector --vectordb-location "postgresql://pgvector:pgvector@localhost:5432/postgres" ``` ### 5) (선택) pgvector로 적재하기 +- dev/create_pgvector.py 파일을 실행합니다. +- `python dev/create_pgvector.py` ```python +""" +dev/create_pgvector.py + +CSV 파일에서 테이블과 컬럼 정보를 불러와 OpenAI 임베딩으로 벡터화한 뒤, +pgvector에 적재한다. + +환경 변수: + OPEN_AI_KEY: OpenAI API 키 + OPEN_AI_EMBEDDING_MODEL: 사용할 임베딩 모델 이름 + VECTORDB_LOCATION: pgvector 연결 문자열 + PGVECTOR_COLLECTION: pgvector 컬렉션 이름 +""" + +import csv +import os from collections import defaultdict -import csv, os + +from langchain.schema import Document from langchain_openai import OpenAIEmbeddings from langchain_postgres.vectorstores import PGVector -from langchain.schema import Document -CSV_PATH = "./table_catalog.csv" -CONN = os.getenv("VECTORDB_LOCATION") or "postgresql://user:pass@host:5432/db" +# CSV 파일 경로 +CSV_PATH = "./dev/table_catalog.csv" +# .env의 VECTORDB_LOCATION과 동일하게 맞추세요 +CONN = ( + os.getenv("VECTORDB_LOCATION") or "postgresql://pgvector:pgvector@localhost:5432/postgres" +) COLLECTION = os.getenv("PGVECTOR_COLLECTION", "table_info_db") tables = defaultdict(lambda: {"desc": "", "columns": []}) @@ -157,8 +180,12 @@ for t, info in tables.items(): cols = "\n".join([f"{c}: {d}" for c, d in info["columns"]]) docs.append(Document(page_content=f"{t}: {info['desc']}\nColumns:\n {cols}")) -emb = OpenAIEmbeddings(model=os.getenv("OPEN_AI_EMBEDDING_MODEL"), openai_api_key=os.getenv("OPEN_AI_KEY")) -PGVector.from_documents(documents=docs, embedding=emb, connection=CONN, collection_name=COLLECTION) +emb = OpenAIEmbeddings( + model=os.getenv("OPEN_AI_EMBEDDING_MODEL"), openai_api_key=os.getenv("OPEN_AI_KEY") +) +PGVector.from_documents( + documents=docs, embedding=emb, connection=CONN, collection_name=COLLECTION +) print(f"pgvector collection populated: {COLLECTION}") ``` diff --git a/llm_utils/README.md b/llm_utils/README.md index bcef425..182e35c 100644 --- a/llm_utils/README.md +++ b/llm_utils/README.md @@ -27,7 +27,7 @@ Lang2SQL 파이프라인에서 LLM, 검색(RAG), 그래프 워크플로우, DB ### Depth 1.5: 벡터DB - **`vectordb/factory.py` → `get_vector_db()`**: `VECTORDB_TYPE`(`faiss`|`pgvector`)에 따라 인스턴스 반환. -- **`vectordb/faiss_db.py`**: 로컬 디스크 `table_info_db` 로드/없으면 `tools.get_info_from_db()`로 빌드 후 저장. +- **`vectordb/faiss_db.py`**: 로컬 디스크 `dev/table_info_db` 로드/없으면 `tools.get_info_from_db()`로 빌드 후 저장. - **`vectordb/pgvector_db.py`**: PGVector 컬렉션 연결, 없거나 비면 `from_documents`로 재구성. ### Depth 2: 데이터 소스/메타 수집 diff --git a/llm_utils/vectordb/faiss_db.py b/llm_utils/vectordb/faiss_db.py index f78f323..02fc2d1 100644 --- a/llm_utils/vectordb/faiss_db.py +++ b/llm_utils/vectordb/faiss_db.py @@ -3,11 +3,12 @@ """ import os -from langchain_community.vectorstores import FAISS from typing import Optional -from llm_utils.tools import get_info_from_db +from langchain_community.vectorstores import FAISS + from llm_utils.llm import get_embeddings +from llm_utils.tools import get_info_from_db def get_faiss_vector_db(vectordb_path: Optional[str] = None): @@ -16,7 +17,7 @@ def get_faiss_vector_db(vectordb_path: Optional[str] = None): # 기본 경로 설정 if vectordb_path is None: - vectordb_path = os.path.join(os.getcwd(), "table_info_db") + vectordb_path = os.path.join(os.getcwd(), "dev/table_info_db") try: db = FAISS.load_local(