deepseek_done
Browse files- app.py +70 -15
- config.py +35 -28
- custom_rag_chain.py +224 -0
- direct_deepseek.py +255 -0
- fallback_rag_chain.py +62 -58
- monitoring.py +0 -136
- rag_chain.py +208 -317
- simple_rag_chain.py +99 -42
- test_deepseek.py +0 -123
app.py
CHANGED
@@ -562,15 +562,15 @@ class AutoRAGChatApp:
|
|
562 |
# PDF ํ์ผ ๋ชฉ๋ก ์์ง์ ๊ฐ์ ํ์ฌ ๋ค์ํ ๊ฒฝ๋ก ์ฒ๋ฆฌ
|
563 |
try:
|
564 |
pdf_files = []
|
565 |
-
|
566 |
# ์ค์ ๋ ๋๋ ํ ๋ฆฌ์์ PDF ํ์ผ ์ฐพ๊ธฐ
|
567 |
logger.info(f"PDF ํ์ผ ๊ฒ์ ๊ฒฝ๋ก: {self.pdf_directory}")
|
568 |
-
|
569 |
if os.path.exists(self.pdf_directory) and os.path.isdir(self.pdf_directory):
|
570 |
# ๋๋ ํ ๋ฆฌ ๋ด์ฉ ์ถ๋ ฅ (๋๋ฒ๊น
์ฉ)
|
571 |
dir_contents = os.listdir(self.pdf_directory)
|
572 |
logger.info(f"๋๋ ํ ๋ฆฌ ๋ด์ฉ: {dir_contents}")
|
573 |
-
|
574 |
# PDF ํ์ผ๋ง ํํฐ๋ง
|
575 |
for filename in os.listdir(self.pdf_directory):
|
576 |
if filename.lower().endswith('.pdf'):
|
@@ -578,10 +578,10 @@ class AutoRAGChatApp:
|
|
578 |
if os.path.isfile(file_path): # ์ค์ ํ์ผ์ธ์ง ํ์ธ
|
579 |
pdf_files.append(file_path)
|
580 |
logger.info(f"PDF ํ์ผ ์ฐพ์: {file_path}")
|
581 |
-
|
582 |
# ๋ฐ๊ฒฌ๋ ๋ชจ๋ ํ์ผ ๋ก๊ทธ
|
583 |
logger.info(f"๋ฐ๊ฒฌ๋ ๋ชจ๋ PDF ํ์ผ: {pdf_files}")
|
584 |
-
|
585 |
except FileNotFoundError:
|
586 |
logger.error(f"PDF ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์: {self.pdf_directory}")
|
587 |
return f"'{self.pdf_directory}' ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ๋๋ ํ ๋ฆฌ๊ฐ ์กด์ฌํ๋์ง ํ์ธํ์ธ์."
|
@@ -668,32 +668,87 @@ class AutoRAGChatApp:
|
|
668 |
# RAG ์ฒด์ธ ์ด๊ธฐํ
|
669 |
if RAG_CHAIN_AVAILABLE:
|
670 |
try:
|
|
|
671 |
self.rag_chain = RAGChain(self.vector_store)
|
672 |
self.is_initialized = True
|
|
|
673 |
except Exception as e:
|
674 |
logger.error(f"RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {e}", exc_info=True)
|
|
|
|
|
675 |
try:
|
676 |
-
|
677 |
from fallback_rag_chain import FallbackRAGChain
|
678 |
-
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ์ผ๋ก ์ ํํฉ๋๋ค...")
|
679 |
self.rag_chain = FallbackRAGChain(self.vector_store)
|
680 |
self.is_initialized = True
|
681 |
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
682 |
except Exception as fallback_e:
|
683 |
logger.error(f"ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {fallback_e}", exc_info=True)
|
684 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
685 |
else:
|
|
|
686 |
try:
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
693 |
except Exception as e:
|
694 |
-
logger.error(f"
|
695 |
return f"๋ฌธ์์ ๋ฒกํฐ ์ธ๋ฑ์ค๋ ์ฒ๋ฆฌ๋์์ผ๋ RAG ์ฒด์ธ ์ด๊ธฐํ์ ์คํจํ์ต๋๋ค: {str(e)}"
|
696 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
697 |
except Exception as e:
|
698 |
error_message = f"๋ฌธ์ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}"
|
699 |
logger.error(error_message, exc_info=True)
|
|
|
562 |
# PDF ํ์ผ ๋ชฉ๋ก ์์ง์ ๊ฐ์ ํ์ฌ ๋ค์ํ ๊ฒฝ๋ก ์ฒ๋ฆฌ
|
563 |
try:
|
564 |
pdf_files = []
|
565 |
+
|
566 |
# ์ค์ ๋ ๋๋ ํ ๋ฆฌ์์ PDF ํ์ผ ์ฐพ๊ธฐ
|
567 |
logger.info(f"PDF ํ์ผ ๊ฒ์ ๊ฒฝ๋ก: {self.pdf_directory}")
|
568 |
+
|
569 |
if os.path.exists(self.pdf_directory) and os.path.isdir(self.pdf_directory):
|
570 |
# ๋๋ ํ ๋ฆฌ ๋ด์ฉ ์ถ๋ ฅ (๋๋ฒ๊น
์ฉ)
|
571 |
dir_contents = os.listdir(self.pdf_directory)
|
572 |
logger.info(f"๋๋ ํ ๋ฆฌ ๋ด์ฉ: {dir_contents}")
|
573 |
+
|
574 |
# PDF ํ์ผ๋ง ํํฐ๋ง
|
575 |
for filename in os.listdir(self.pdf_directory):
|
576 |
if filename.lower().endswith('.pdf'):
|
|
|
578 |
if os.path.isfile(file_path): # ์ค์ ํ์ผ์ธ์ง ํ์ธ
|
579 |
pdf_files.append(file_path)
|
580 |
logger.info(f"PDF ํ์ผ ์ฐพ์: {file_path}")
|
581 |
+
|
582 |
# ๋ฐ๊ฒฌ๋ ๋ชจ๋ ํ์ผ ๋ก๊ทธ
|
583 |
logger.info(f"๋ฐ๊ฒฌ๋ ๋ชจ๋ PDF ํ์ผ: {pdf_files}")
|
584 |
+
|
585 |
except FileNotFoundError:
|
586 |
logger.error(f"PDF ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์: {self.pdf_directory}")
|
587 |
return f"'{self.pdf_directory}' ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ๋๋ ํ ๋ฆฌ๊ฐ ์กด์ฌํ๋์ง ํ์ธํ์ธ์."
|
|
|
668 |
# RAG ์ฒด์ธ ์ด๊ธฐํ
|
669 |
if RAG_CHAIN_AVAILABLE:
|
670 |
try:
|
671 |
+
logger.info("RAGChain์ผ๋ก ์ด๊ธฐํ๋ฅผ ์๋ํฉ๋๋ค.")
|
672 |
self.rag_chain = RAGChain(self.vector_store)
|
673 |
self.is_initialized = True
|
674 |
+
logger.info("RAG ์ฒด์ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
675 |
except Exception as e:
|
676 |
logger.error(f"RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {e}", exc_info=True)
|
677 |
+
|
678 |
+
# FallbackRAGChain์ผ๋ก ๋์ฒด ์๋
|
679 |
try:
|
680 |
+
logger.info("FallbackRAGChain์ผ๋ก ๋์ฒดํฉ๋๋ค...")
|
681 |
from fallback_rag_chain import FallbackRAGChain
|
|
|
682 |
self.rag_chain = FallbackRAGChain(self.vector_store)
|
683 |
self.is_initialized = True
|
684 |
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
685 |
except Exception as fallback_e:
|
686 |
logger.error(f"ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {fallback_e}", exc_info=True)
|
687 |
+
|
688 |
+
# SimpleRAGChain ์๋ (์ตํ์ ์๋จ)
|
689 |
+
try:
|
690 |
+
logger.info("SimpleRAGChain์ผ๋ก ๋์ฒดํฉ๋๋ค...")
|
691 |
+
from simple_rag_chain import SimpleRAGChain
|
692 |
+
|
693 |
+
# API ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
694 |
+
try:
|
695 |
+
from config import DEEPSEEK_API_KEY, DEEPSEEK_MODEL, DEEPSEEK_ENDPOINT
|
696 |
+
logger.info(f"์ค์ ํ์ผ์์ DeepSeek API ์ ๋ณด๋ฅผ ๋ก๋ํ์ต๋๋ค: ๋ชจ๋ธ={DEEPSEEK_MODEL}")
|
697 |
+
except ImportError:
|
698 |
+
# ์ค์ ํ์ผ์์ ๊ฐ์ ธ์ฌ ์ ์๋ ๊ฒฝ์ฐ ํ๊ฒฝ ๋ณ์ ํ์ธ
|
699 |
+
DEEPSEEK_API_KEY = os.environ.get("DEEPSEEK_API_KEY", "")
|
700 |
+
DEEPSEEK_MODEL = os.environ.get("DEEPSEEK_MODEL", "deepseek-chat")
|
701 |
+
DEEPSEEK_ENDPOINT = os.environ.get("DEEPSEEK_ENDPOINT",
|
702 |
+
"https://api.deepseek.com/v1/chat/completions")
|
703 |
+
logger.info(f"ํ๊ฒฝ ๋ณ์์์ DeepSeek API ์ ๋ณด๋ฅผ ๋ก๋ํ์ต๋๋ค: ๋ชจ๋ธ={DEEPSEEK_MODEL}")
|
704 |
+
|
705 |
+
# SimpleRAGChain ์ด๊ธฐํ ์๋
|
706 |
+
self.rag_chain = SimpleRAGChain(self.vector_store)
|
707 |
+
self.is_initialized = True
|
708 |
+
logger.info("SimpleRAGChain ์ด๊ธฐํ ์ฑ๊ณต")
|
709 |
+
except Exception as simple_e:
|
710 |
+
logger.error(f"๋ชจ๋ RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {simple_e}", exc_info=True)
|
711 |
+
return f"๋ฌธ์์ ๋ฒกํฐ ์ธ๋ฑ์ค๋ ์ฒ๋ฆฌ๋์์ผ๋ RAG ์ฒด์ธ ์ด๊ธฐํ์ ์คํจํ์ต๋๋ค: {str(e)}"
|
712 |
else:
|
713 |
+
# RAGChain์ ์ฌ์ฉํ ์ ์๋ ๊ฒฝ์ฐ
|
714 |
try:
|
715 |
+
logger.info("๊ธฐ๋ณธ RAG Chain์ ์ฌ์ฉํ ์ ์์ด ๋์ฒด ๋ฒ์ ์ ์๋ํฉ๋๋ค...")
|
716 |
+
|
717 |
+
# FallbackRAGChain ์๋
|
718 |
+
try:
|
719 |
+
from fallback_rag_chain import FallbackRAGChain
|
720 |
+
self.rag_chain = FallbackRAGChain(self.vector_store)
|
721 |
+
self.is_initialized = True
|
722 |
+
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
723 |
+
except Exception as fallback_e:
|
724 |
+
logger.error(f"ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {fallback_e}", exc_info=True)
|
725 |
+
|
726 |
+
# SimpleRAGChain ์๋ (์ตํ์ ์๋จ)
|
727 |
+
try:
|
728 |
+
from simple_rag_chain import SimpleRAGChain
|
729 |
+
self.rag_chain = SimpleRAGChain(self.vector_store)
|
730 |
+
self.is_initialized = True
|
731 |
+
logger.info("SimpleRAGChain ์ด๊ธฐํ ์ฑ๊ณต")
|
732 |
+
except Exception as simple_e:
|
733 |
+
logger.error(f"๋ชจ๋ RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {simple_e}", exc_info=True)
|
734 |
+
return f"๋ฌธ์์ ๋ฒกํฐ ์ธ๋ฑ์ค๋ ์ฒ๋ฆฌ๋์์ผ๋ RAG ์ฒด์ธ ์ด๊ธฐํ์ ์คํจํ์ต๋๋ค"
|
735 |
except Exception as e:
|
736 |
+
logger.error(f"RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {e}", exc_info=True)
|
737 |
return f"๋ฌธ์์ ๋ฒกํฐ ์ธ๋ฑ์ค๋ ์ฒ๋ฆฌ๋์์ผ๋ RAG ์ฒด์ธ ์ด๊ธฐํ์ ์คํจํ์ต๋๋ค: {str(e)}"
|
738 |
|
739 |
+
# ์ฑ๊ณต ๋ฉ์์ง ์์ฑ
|
740 |
+
result_message = f"""๋ฌธ์ ์ฒ๋ฆฌ ์๋ฃ!
|
741 |
+
- ์ฒ๋ฆฌ๋ ํ์ผ: {len(pdf_files)}๊ฐ
|
742 |
+
- ์บ์๋ ํ์ผ: {len(cached_files)}๊ฐ
|
743 |
+
- ์ ํ์ผ: {len(new_files)}๊ฐ
|
744 |
+
- ์
๋ฐ์ดํธ๋ ํ์ผ: {len(updated_files)}๊ฐ
|
745 |
+
- ์คํจํ ํ์ผ: {len(failed_files)}๊ฐ
|
746 |
+
- ์ด ์ฒญํฌ ์: {len(all_chunks)}๊ฐ
|
747 |
+
- ์ฒ๋ฆฌ ์๊ฐ: {processing_time:.2f}์ด
|
748 |
+
์ด์ ์ง๋ฌธํ ์ค๋น๊ฐ ๋์์ต๋๋ค!"""
|
749 |
+
|
750 |
+
return result_message
|
751 |
+
|
752 |
except Exception as e:
|
753 |
error_message = f"๋ฌธ์ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}"
|
754 |
logger.error(error_message, exc_info=True)
|
config.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
"""
|
2 |
๋ฒกํฐ ์คํ ์ด, ์๋ฒ ๋ฉ ๋ชจ๋ธ, LLM ๋ฑ ๊ตฌ์ฑ ์์ ์ค์
|
3 |
-
ํ๊ฒฝ ๋ณ์ ๋ฐ .env ํ์ผ ํ์ฉ ๊ฐ์ ๋ฒ์ -
|
4 |
"""
|
5 |
import os
|
6 |
import logging
|
@@ -21,26 +21,35 @@ logger.info(f"์คํฌ๋ฆฝํธ ๋๋ ํ ๋ฆฌ: {script_dir}")
|
|
21 |
logger.info(f"ํ์ฌ ์์
๋๋ ํ ๋ฆฌ: {os.getcwd()}")
|
22 |
logger.info(f"์ด์ ์ฒด์ : {os.name}")
|
23 |
|
24 |
-
# .env ํ์ผ
|
25 |
-
|
26 |
-
"
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
# .env ํ์ผ
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
43 |
logger.warning(".env ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ๊ฐ ๋๋ ์์คํ
ํ๊ฒฝ ๋ณ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.")
|
|
|
|
|
|
|
|
|
44 |
|
45 |
# ํ๊ฒฝ ๊ฐ์ง
|
46 |
IS_HUGGINGFACE = os.getenv('SPACE_ID') is not None
|
@@ -143,8 +152,8 @@ CHUNK_OVERLAP = int(get_env("CHUNK_OVERLAP", "200"))
|
|
143 |
|
144 |
# API ํค ๋ฐ ํ๊ฒฝ ์ค์
|
145 |
OPENAI_API_KEY = get_env("OPENAI_API_KEY", "")
|
146 |
-
LANGFUSE_PUBLIC_KEY = get_env("LANGFUSE_PUBLIC_KEY", "")
|
147 |
-
LANGFUSE_SECRET_KEY = get_env("LANGFUSE_SECRET_KEY", "")
|
148 |
LANGFUSE_HOST = get_env("LANGFUSE_HOST", "https://cloud.langfuse.com")
|
149 |
|
150 |
# DeepSeek ๊ด๋ จ ์ค์ ์ถ๊ฐ
|
@@ -165,6 +174,9 @@ RERANKER_MODEL = get_env("RERANKER_MODEL", "Alibaba-NLP/gte-multilingual-reranke
|
|
165 |
USE_OPENAI = get_env("USE_OPENAI", "False").lower() == "true"
|
166 |
USE_DEEPSEEK = get_env("USE_DEEPSEEK", "False").lower() == "true"
|
167 |
|
|
|
|
|
|
|
168 |
# DeepSeek API ํ
์คํธ ํจ์
|
169 |
def test_deepseek_connection():
|
170 |
"""
|
@@ -301,7 +313,6 @@ else:
|
|
301 |
logger.info(f"OpenAI ๋ชจ๋ธ ์ฌ์ฉ")
|
302 |
else:
|
303 |
LLM_MODEL = get_env("LLM_MODEL", "gemma3:latest")
|
304 |
-
OLLAMA_HOST = get_env("OLLAMA_HOST", "http://localhost:11434")
|
305 |
logger.info(f"Ollama ๋ชจ๋ธ ์ฌ์ฉ")
|
306 |
|
307 |
# API ํค ๊ฒ์ฆ
|
@@ -336,8 +347,7 @@ def print_config():
|
|
336 |
logger.info(f"OpenAI ์ฌ์ฉ: {USE_OPENAI}")
|
337 |
logger.info(f"DeepSeek ์ฌ์ฉ: {USE_DEEPSEEK}")
|
338 |
logger.info(f"LLM ๋ชจ๋ธ: {LLM_MODEL}")
|
339 |
-
|
340 |
-
logger.info(f"Ollama ํธ์คํธ: {OLLAMA_HOST}")
|
341 |
logger.info(f"์๋ฒ ๋ฉ ๋ชจ๋ธ: {EMBEDDING_MODEL}")
|
342 |
logger.info(f"๋ฆฌ๋ญ์ปค ๋ชจ๋ธ: {RERANKER_MODEL}")
|
343 |
logger.info(f"TOP_K ๊ฒ์: {TOP_K_RETRIEVAL}, ๋ฆฌ๋ญํน: {TOP_K_RERANK}")
|
@@ -410,7 +420,6 @@ def list_directory_contents():
|
|
410 |
except Exception as e:
|
411 |
logger.error(f"PDF ๋๋ ํ ๋ฆฌ ๋ด์ฉ ํ์ธ ์คํจ: {e}")
|
412 |
|
413 |
-
|
414 |
# ์ง์ ์ฃผ์ด์ง ๊ฒฝ๋ก์์ PDF ์ฐพ๊ธฐ (๋๋ฒ๊น
์ฉ)
|
415 |
def find_pdf_files_in_path(path: str) -> list:
|
416 |
"""
|
@@ -434,7 +443,6 @@ def find_pdf_files_in_path(path: str) -> list:
|
|
434 |
logger.error(f"๊ฒฝ๋ก '{path}'์์ PDF ํ์ผ ๊ฒ์ ์ค ์ค๋ฅ: {e}")
|
435 |
return []
|
436 |
|
437 |
-
|
438 |
# ์๋์ฐ์ฆ ์ฃผ์ ๊ฒฝ๋ก์์ PDF ํ์ผ ๊ฒ์ (๋๋ฒ๊น
์ฉ)
|
439 |
def find_pdfs_in_windows_paths():
|
440 |
"""์๋์ฐ์ฆ์์ ์ฃผ์ ๊ฒฝ๋ก์ PDF ํ์ผ์ด ์๋์ง ํ์ธ"""
|
@@ -454,7 +462,6 @@ def find_pdfs_in_windows_paths():
|
|
454 |
for path in common_paths:
|
455 |
find_pdf_files_in_path(path)
|
456 |
|
457 |
-
|
458 |
# ์ค์ ์ ๋ณด ์ถ๋ ฅ ๋ฐ ๊ฒ์ฆ (๋ชจ๋ ์ํฌํธ ์ ์คํ)
|
459 |
print_config()
|
460 |
config_status = validate_config()
|
|
|
1 |
"""
|
2 |
๋ฒกํฐ ์คํ ์ด, ์๋ฒ ๋ฉ ๋ชจ๋ธ, LLM ๋ฑ ๊ตฌ์ฑ ์์ ์ค์
|
3 |
+
ํ๊ฒฝ ๋ณ์ ๋ฐ .env ํ์ผ ํ์ฉ ๊ฐ์ ๋ฒ์ - ํ์ ์๋ ์ต์ ํ
|
4 |
"""
|
5 |
import os
|
6 |
import logging
|
|
|
21 |
logger.info(f"ํ์ฌ ์์
๋๋ ํ ๋ฆฌ: {os.getcwd()}")
|
22 |
logger.info(f"์ด์ ์ฒด์ : {os.name}")
|
23 |
|
24 |
+
# .env ํ์ผ ๊ฒ์ ์ต์ ํ
|
25 |
+
def fast_env_load():
|
26 |
+
"""
|
27 |
+
.env ํ์ผ์ ๋น ๋ฅด๊ฒ ์ฐพ์ ๋ก๋ํ๋ ํจ์
|
28 |
+
|
29 |
+
Returns:
|
30 |
+
bool: ํ๊ฒฝ ๋ณ์ ๋ก๋ ์ฑ๊ณต ์ฌ๋ถ
|
31 |
+
"""
|
32 |
+
# .env ํ์ผ ์์น ํ๋ณด๋ค (.env ํ์ผ์ ์ผ๋ฐ์ ์ผ๋ก ํ๋ก์ ํธ ๋ฃจํธ์ ์์)
|
33 |
+
env_paths = [
|
34 |
+
".env", # ํ์ฌ ๋๋ ํ ๋ฆฌ
|
35 |
+
os.path.join(script_dir, ".env"), # ์คํฌ๋ฆฝํธ ๋๋ ํ ๋ฆฌ
|
36 |
+
]
|
37 |
+
|
38 |
+
# ์์์ ๋น ๋ฅด๊ฒ ์ํํ๋ฉฐ ์ฐพ๊ธฐ
|
39 |
+
for env_path in env_paths:
|
40 |
+
if os.path.isfile(env_path):
|
41 |
+
logger.info(f".env ํ์ผ ๋ฐ๊ฒฌ: {env_path}")
|
42 |
+
loaded = load_dotenv(env_path, verbose=False, override=True)
|
43 |
+
if loaded:
|
44 |
+
logger.info(f".env ํ์ผ ๋ก๋ ์ฑ๊ณต: {env_path}")
|
45 |
+
return True
|
46 |
+
|
47 |
+
# ๊ฒ์ ์คํจ ์ ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ ๋ฉ์์ง ์ถ๋ ฅ
|
48 |
logger.warning(".env ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ๊ฐ ๋๋ ์์คํ
ํ๊ฒฝ ๋ณ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.")
|
49 |
+
return False
|
50 |
+
|
51 |
+
# ํ๊ฒฝ ๋ณ์ ๋ก๋
|
52 |
+
env_loaded = fast_env_load()
|
53 |
|
54 |
# ํ๊ฒฝ ๊ฐ์ง
|
55 |
IS_HUGGINGFACE = os.getenv('SPACE_ID') is not None
|
|
|
152 |
|
153 |
# API ํค ๋ฐ ํ๊ฒฝ ์ค์
|
154 |
OPENAI_API_KEY = get_env("OPENAI_API_KEY", "")
|
155 |
+
LANGFUSE_PUBLIC_KEY = get_env("LANGFUSE_PUBLIC_KEY", "pk-lf-cd6248e2-59ad-496d-a4cb-487bb3ecfcd5")
|
156 |
+
LANGFUSE_SECRET_KEY = get_env("LANGFUSE_SECRET_KEY", "sk-lf-61460a1d-e637-4c22-b5e9-9250ac2579ba")
|
157 |
LANGFUSE_HOST = get_env("LANGFUSE_HOST", "https://cloud.langfuse.com")
|
158 |
|
159 |
# DeepSeek ๊ด๋ จ ์ค์ ์ถ๊ฐ
|
|
|
174 |
USE_OPENAI = get_env("USE_OPENAI", "False").lower() == "true"
|
175 |
USE_DEEPSEEK = get_env("USE_DEEPSEEK", "False").lower() == "true"
|
176 |
|
177 |
+
# Ollama ํธ์คํธ ์ค์ (๊ธฐ๋ณธ๊ฐ)
|
178 |
+
OLLAMA_HOST = get_env("OLLAMA_HOST", "http://localhost:11434")
|
179 |
+
|
180 |
# DeepSeek API ํ
์คํธ ํจ์
|
181 |
def test_deepseek_connection():
|
182 |
"""
|
|
|
313 |
logger.info(f"OpenAI ๋ชจ๋ธ ์ฌ์ฉ")
|
314 |
else:
|
315 |
LLM_MODEL = get_env("LLM_MODEL", "gemma3:latest")
|
|
|
316 |
logger.info(f"Ollama ๋ชจ๋ธ ์ฌ์ฉ")
|
317 |
|
318 |
# API ํค ๊ฒ์ฆ
|
|
|
347 |
logger.info(f"OpenAI ์ฌ์ฉ: {USE_OPENAI}")
|
348 |
logger.info(f"DeepSeek ์ฌ์ฉ: {USE_DEEPSEEK}")
|
349 |
logger.info(f"LLM ๋ชจ๋ธ: {LLM_MODEL}")
|
350 |
+
logger.info(f"Ollama ํธ์คํธ: {OLLAMA_HOST}")
|
|
|
351 |
logger.info(f"์๋ฒ ๋ฉ ๋ชจ๋ธ: {EMBEDDING_MODEL}")
|
352 |
logger.info(f"๋ฆฌ๋ญ์ปค ๋ชจ๋ธ: {RERANKER_MODEL}")
|
353 |
logger.info(f"TOP_K ๊ฒ์: {TOP_K_RETRIEVAL}, ๋ฆฌ๋ญํน: {TOP_K_RERANK}")
|
|
|
420 |
except Exception as e:
|
421 |
logger.error(f"PDF ๋๋ ํ ๋ฆฌ ๋ด์ฉ ํ์ธ ์คํจ: {e}")
|
422 |
|
|
|
423 |
# ์ง์ ์ฃผ์ด์ง ๊ฒฝ๋ก์์ PDF ์ฐพ๊ธฐ (๋๋ฒ๊น
์ฉ)
|
424 |
def find_pdf_files_in_path(path: str) -> list:
|
425 |
"""
|
|
|
443 |
logger.error(f"๊ฒฝ๋ก '{path}'์์ PDF ํ์ผ ๊ฒ์ ์ค ์ค๋ฅ: {e}")
|
444 |
return []
|
445 |
|
|
|
446 |
# ์๋์ฐ์ฆ ์ฃผ์ ๊ฒฝ๋ก์์ PDF ํ์ผ ๊ฒ์ (๋๋ฒ๊น
์ฉ)
|
447 |
def find_pdfs_in_windows_paths():
|
448 |
"""์๋์ฐ์ฆ์์ ์ฃผ์ ๊ฒฝ๋ก์ PDF ํ์ผ์ด ์๋์ง ํ์ธ"""
|
|
|
462 |
for path in common_paths:
|
463 |
find_pdf_files_in_path(path)
|
464 |
|
|
|
465 |
# ์ค์ ์ ๋ณด ์ถ๋ ฅ ๋ฐ ๊ฒ์ฆ (๋ชจ๋ ์ํฌํธ ์ ์คํ)
|
466 |
print_config()
|
467 |
config_status = validate_config()
|
custom_rag_chain.py
ADDED
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
DeepSeek API๋ฅผ ํ์ฉํ ์ปค์คํ
RAG ์ฒด์ธ ๊ตฌํ
|
3 |
+
"""
|
4 |
+
import os
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
from typing import List, Dict, Any, Optional, Tuple
|
8 |
+
|
9 |
+
from langchain.schema import Document
|
10 |
+
from langchain.prompts import PromptTemplate
|
11 |
+
from langchain_core.output_parsers import StrOutputParser
|
12 |
+
from langchain_core.runnables import RunnablePassthrough
|
13 |
+
|
14 |
+
# DeepSeek ์ปค์คํ
LLM ์ํฌํธ
|
15 |
+
from deepseek_llm import DeepSeekLLM, DeepSeekChat
|
16 |
+
|
17 |
+
# ์ค์ ๊ฐ์ ธ์ค๊ธฐ
|
18 |
+
try:
|
19 |
+
from config import (
|
20 |
+
DEEPSEEK_API_KEY, DEEPSEEK_MODEL, DEEPSEEK_ENDPOINT,
|
21 |
+
TOP_K_RETRIEVAL, TOP_K_RERANK
|
22 |
+
)
|
23 |
+
except ImportError:
|
24 |
+
# ์ค์ ๋ชจ๋์ ๊ฐ์ ธ์ฌ ์ ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ ์ค์
|
25 |
+
DEEPSEEK_API_KEY = os.environ.get("DEEPSEEK_API_KEY", "")
|
26 |
+
DEEPSEEK_MODEL = os.environ.get("DEEPSEEK_MODEL", "deepseek-chat")
|
27 |
+
DEEPSEEK_ENDPOINT = os.environ.get("DEEPSEEK_ENDPOINT", "https://api.deepseek.com/v1/chat/completions")
|
28 |
+
TOP_K_RETRIEVAL = int(os.environ.get("TOP_K_RETRIEVAL", "5"))
|
29 |
+
TOP_K_RERANK = int(os.environ.get("TOP_K_RERANK", "3"))
|
30 |
+
|
31 |
+
# ๋ก๊น
์ค์
|
32 |
+
logger = logging.getLogger("CustomRAGChain")
|
33 |
+
|
34 |
+
|
35 |
+
class CustomRAGChain:
|
36 |
+
"""
|
37 |
+
DeepSeek API๋ฅผ ํ์ฉํ ์ปค์คํ
RAG ์ฒด์ธ
|
38 |
+
"""
|
39 |
+
|
40 |
+
def __init__(self, vector_store, use_reranker=False):
|
41 |
+
"""
|
42 |
+
RAG ์ฒด์ธ ์ด๊ธฐํ
|
43 |
+
|
44 |
+
Args:
|
45 |
+
vector_store: ๋ฒกํฐ ์คํ ์ด ์ธ์คํด์ค
|
46 |
+
use_reranker: ๋ฆฌ๋ญ์ปค ์ฌ์ฉ ์ฌ๋ถ (ํ์ฌ ๋ฏธ์ง์)
|
47 |
+
"""
|
48 |
+
logger.info("์ปค์คํ
RAG ์ฒด์ธ ์ด๊ธฐํ...")
|
49 |
+
self.vector_store = vector_store
|
50 |
+
self.use_reranker = use_reranker
|
51 |
+
|
52 |
+
# API ํค ํ์ธ
|
53 |
+
if not DEEPSEEK_API_KEY:
|
54 |
+
logger.error("DeepSeek API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.")
|
55 |
+
raise ValueError("DeepSeek API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.")
|
56 |
+
|
57 |
+
# DeepSeek LLM ์ด๊ธฐํ
|
58 |
+
try:
|
59 |
+
self.llm = DeepSeekLLM(
|
60 |
+
api_key=DEEPSEEK_API_KEY,
|
61 |
+
model=DEEPSEEK_MODEL,
|
62 |
+
endpoint=DEEPSEEK_ENDPOINT,
|
63 |
+
temperature=0.3,
|
64 |
+
max_tokens=1000,
|
65 |
+
request_timeout=120,
|
66 |
+
max_retries=5
|
67 |
+
)
|
68 |
+
logger.info(f"DeepSeek LLM ์ด๊ธฐํ ์ฑ๊ณต: {DEEPSEEK_MODEL}")
|
69 |
+
except Exception as e:
|
70 |
+
logger.error(f"DeepSeek LLM ์ด๊ธฐํ ์คํจ: {e}")
|
71 |
+
raise ValueError(f"DeepSeek LLM ์ด๊ธฐํ ์คํจ: {str(e)}")
|
72 |
+
|
73 |
+
# ์ฑ ์ธํฐํ์ด์ค ์ด๊ธฐํ (๋์ฒด์ฉ)
|
74 |
+
self.chat = DeepSeekChat(
|
75 |
+
api_key=DEEPSEEK_API_KEY,
|
76 |
+
model=DEEPSEEK_MODEL,
|
77 |
+
endpoint=DEEPSEEK_ENDPOINT
|
78 |
+
)
|
79 |
+
|
80 |
+
# RAG ํ๋กฌํํธ ํ
ํ๋ฆฟ
|
81 |
+
self.prompt = PromptTemplate.from_template("""
|
82 |
+
๋ค์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ฌธ์ ์ ํํ๊ฒ ๋ต๋ณํด์ฃผ์ธ์.
|
83 |
+
|
84 |
+
์ง๋ฌธ: {question}
|
85 |
+
|
86 |
+
์ฐธ๊ณ ์ ๋ณด:
|
87 |
+
{context}
|
88 |
+
|
89 |
+
์ฐธ๊ณ ์ ๋ณด์ ๋ต์ด ์์ผ๋ฉด ๋ฐ๋์ ๊ทธ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณํ์ธ์.
|
90 |
+
์ฐธ๊ณ ์ ๋ณด์ ๋ต์ด ์๋ ๊ฒฝ์ฐ์๋ ์ผ๋ฐ์ ์ธ ์ง์์ ํ์ฉํ์ฌ ๋ต๋ณํ ์ ์์ง๋ง, "์ ๊ณต๋ ๋ฌธ์์๋ ์ด ์ ๋ณด๊ฐ ์์ผ๋, ์ผ๋ฐ์ ์ผ๋ก๋..." ์์ผ๋ก ์์ํ์ธ์.
|
91 |
+
๋ต๋ณ์ ์ ํํ๊ณ ๊ฐ๊ฒฐํ๊ฒ ์ ๊ณตํ๋, ๊ฐ๋ฅํ ์ฐธ๊ณ ์ ๋ณด์์ ๊ทผ๊ฑฐ๋ฅผ ์ฐพ์ ์ค๋ช
ํด์ฃผ์ธ์.
|
92 |
+
์ฐธ๊ณ ์ ๋ณด์ ์ถ์ฒ๋ ํจ๊ป ์๋ ค์ฃผ์ธ์.
|
93 |
+
""")
|
94 |
+
|
95 |
+
# RAG ์ฒด์ธ ๊ตฌ์ฑ
|
96 |
+
self.chain = (
|
97 |
+
{"context": self._retrieve, "question": RunnablePassthrough()}
|
98 |
+
| self.prompt
|
99 |
+
| self.llm
|
100 |
+
| StrOutputParser()
|
101 |
+
)
|
102 |
+
|
103 |
+
logger.info("์ปค์คํ
RAG ์ฒด์ธ ์ด๊ธฐํ ์๋ฃ")
|
104 |
+
|
105 |
+
def _retrieve(self, query: str) -> str:
|
106 |
+
"""
|
107 |
+
์ฟผ๋ฆฌ์ ๋ํ ๊ด๋ จ ๋ฌธ์ ๊ฒ์ ๋ฐ ์ปจํ
์คํธ ๊ตฌ์ฑ
|
108 |
+
|
109 |
+
Args:
|
110 |
+
query: ์ฌ์ฉ์ ์ง๋ฌธ
|
111 |
+
|
112 |
+
Returns:
|
113 |
+
๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ํฌํจํ ์ปจํ
์คํธ ๋ฌธ์์ด
|
114 |
+
"""
|
115 |
+
if not query or not query.strip():
|
116 |
+
logger.warning("๋น ์ฟผ๋ฆฌ๋ก ๊ฒ์ ์๋")
|
117 |
+
return "๊ฒ์ ์ฟผ๋ฆฌ๊ฐ ๋น์ด์์ต๋๋ค."
|
118 |
+
|
119 |
+
try:
|
120 |
+
# ๋ฒกํฐ ๊ฒ์ ์ํ
|
121 |
+
logger.info(f"๋ฒกํฐ ๊ฒ์ ์ํ: '{query[:50]}{'...' if len(query) > 50 else ''}'")
|
122 |
+
docs = self.vector_store.similarity_search(query, k=TOP_K_RETRIEVAL)
|
123 |
+
|
124 |
+
if not docs:
|
125 |
+
logger.warning("๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค")
|
126 |
+
return "๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."
|
127 |
+
|
128 |
+
# ๊ฒ์ ๊ฒฐ๊ณผ ์ปจํ
์คํธ ๊ตฌ์ฑ
|
129 |
+
context_parts = []
|
130 |
+
for i, doc in enumerate(docs, 1):
|
131 |
+
source = doc.metadata.get("source", "์ ์ ์๋ ์ถ์ฒ")
|
132 |
+
page = doc.metadata.get("page", "")
|
133 |
+
source_info = f"{source}"
|
134 |
+
if page:
|
135 |
+
source_info += f" (ํ์ด์ง: {page})"
|
136 |
+
|
137 |
+
context_parts.append(f"[์ฐธ๊ณ ์๋ฃ {i}] - ์ถ์ฒ: {source_info}\n{doc.page_content}\n")
|
138 |
+
|
139 |
+
context = "\n".join(context_parts)
|
140 |
+
|
141 |
+
# ๏ฟฝ๏ฟฝํ
์คํธ ๊ธธ์ด ์ ํ (ํ ํฐ ์ ์ ํ)
|
142 |
+
if len(context) > 6000:
|
143 |
+
logger.warning(f"์ปจํ
์คํธ๊ฐ ๋๋ฌด ๊น๋๋ค ({len(context)} ๋ฌธ์). ์ ํํฉ๋๋ค.")
|
144 |
+
context = context[:2500] + "\n...(์ค๋ต)...\n" + context[-2500:]
|
145 |
+
|
146 |
+
logger.info(f"์ปจํ
์คํธ ์์ฑ ์๋ฃ: {len(context_parts)}๊ฐ ๋ฌธ์, {len(context)} ๋ฌธ์")
|
147 |
+
return context
|
148 |
+
|
149 |
+
except Exception as e:
|
150 |
+
logger.error(f"๊ฒ์ ์ค ์ค๋ฅ: {e}")
|
151 |
+
return f"๊ฒ์ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}"
|
152 |
+
|
153 |
+
def run(self, query: str) -> str:
|
154 |
+
"""
|
155 |
+
์ฌ์ฉ์ ์ฟผ๋ฆฌ์ ๋ํ RAG ํ์ดํ๋ผ์ธ ์คํ
|
156 |
+
|
157 |
+
Args:
|
158 |
+
query: ์ฌ์ฉ์ ์ง๋ฌธ
|
159 |
+
|
160 |
+
Returns:
|
161 |
+
๋ชจ๋ธ ์๋ต ๋ฌธ์์ด
|
162 |
+
"""
|
163 |
+
if not query or not query.strip():
|
164 |
+
logger.warning("๋น ์ฟผ๋ฆฌ๋ก ์คํ ์๋")
|
165 |
+
return "์ง๋ฌธ์ด ๋น์ด์์ต๋๋ค. ์ง๋ฌธ์ ์
๋ ฅํด ์ฃผ์ธ์."
|
166 |
+
|
167 |
+
try:
|
168 |
+
logger.info(f"RAG ์ฒด์ธ ์คํ: '{query[:50]}{'...' if len(query) > 50 else ''}'")
|
169 |
+
start_time = time.time()
|
170 |
+
|
171 |
+
# ๋ฒกํฐ ๊ฒ์ ์คํ
|
172 |
+
context = self._retrieve(query)
|
173 |
+
|
174 |
+
# ์ง์ LLM ํธ์ถ (์ฒด์ธ ์ฌ์ฉ)
|
175 |
+
try:
|
176 |
+
response = self.chain.invoke(query)
|
177 |
+
logger.info(f"LangChain ์ฒด์ธ ํธ์ถ ์ฑ๊ณต")
|
178 |
+
except Exception as chain_error:
|
179 |
+
logger.error(f"์ฒด์ธ ํธ์ถ ์คํจ: {chain_error}, ๋์ฒด ๋ฐฉ์ ์๋")
|
180 |
+
|
181 |
+
# ๋์ฒด ๋ฐฉ์: ์ง์ ์ฑํ
API ํธ์ถ
|
182 |
+
try:
|
183 |
+
prompt = self.prompt.format(question=query, context=context)
|
184 |
+
response = self.chat.generate([{"role": "user", "content": prompt}])
|
185 |
+
logger.info("๋์ฒด ์ฑํ
API ํธ์ถ ์ฑ๊ณต")
|
186 |
+
except Exception as chat_error:
|
187 |
+
logger.error(f"๋์ฒด ์ฑํ
API ํธ์ถ ์คํจ: {chat_error}")
|
188 |
+
|
189 |
+
# ๋ฏธ๋ฆฌ ์ ์๋ ์๋ต์ผ๋ก ํด๋ฐฑ
|
190 |
+
predefined_answers = {
|
191 |
+
"๋ํ๋ฏผ๊ตญ์ ์๋": "๋ํ๋ฏผ๊ตญ์ ์๋๋ ์์ธ์
๋๋ค.",
|
192 |
+
"์๋": "๋ํ๋ฏผ๊ตญ์ ์๋๋ ์์ธ์
๋๋ค.",
|
193 |
+
"๋๊ตฌ์ผ": "์ ๋ RAG ๊ธฐ๋ฐ ์ง์์๋ต ์์คํ
์
๋๋ค. ๋ฌธ์๋ฅผ ๊ฒ์ํ๊ณ ๊ด๋ จ ์ ๋ณด๋ฅผ ์ฐพ์๋๋ฆฝ๋๋ค.",
|
194 |
+
"์๋
": "์๋
ํ์ธ์! ๋ฌด์์ ๋์๋๋ฆด๊น์?",
|
195 |
+
"๋ญํด": "์ฌ์ฉ์์ ์ง๋ฌธ์ ๋ต๋ณํ๊ธฐ ์ํด ๋ฌธ์๋ฅผ ๊ฒ์ํ๊ณ ์์ต๋๋ค. ๋ฌด์์ ์๋ ค๋๋ฆด๊น์?"
|
196 |
+
}
|
197 |
+
|
198 |
+
# ์ง๋ฌธ์ ๋ง๋ ๋ฏธ๋ฆฌ ์ ์๋ ์๋ต์ด ์๋์ง ํ์ธ
|
199 |
+
for key, answer in predefined_answers.items():
|
200 |
+
if key in query.lower():
|
201 |
+
response = answer
|
202 |
+
logger.info(f"๋ฏธ๋ฆฌ ์ ์๋ ์๋ต ์ ๊ณต: {key}")
|
203 |
+
break
|
204 |
+
else:
|
205 |
+
# ๊ฒ์ ๊ฒฐ๊ณผ๋ง ํ์
|
206 |
+
response = f"""
|
207 |
+
API ์ฐ๊ฒฐ ์ค๋ฅ๋ก ์ธํด ๊ฒ์ ๊ฒฐ๊ณผ๋ง ํ์ํฉ๋๋ค.
|
208 |
+
|
209 |
+
์ง๋ฌธ: {query}
|
210 |
+
|
211 |
+
๊ฒ์๋ ๊ด๋ จ ๋ฌธ์:
|
212 |
+
{context}
|
213 |
+
|
214 |
+
[์ฐธ๊ณ ] API ์ฐ๊ฒฐ ๋ฌธ์ ๋ก ์ธํด ์๋ ์์ฝ์ด ์ ๊ณต๋์ง ์์ต๋๋ค. ๋ค์ ์๋ํ๊ฑฐ๋ ๋ค๋ฅธ ์ง๋ฌธ์ ํด๋ณด์ธ์.
|
215 |
+
"""
|
216 |
+
logger.info("๊ฒ์ ๊ฒฐ๊ณผ๋ง ํ์")
|
217 |
+
|
218 |
+
end_time = time.time()
|
219 |
+
logger.info(f"RAG ์ฒด์ธ ์คํ ์๋ฃ: {end_time - start_time:.2f}์ด")
|
220 |
+
return response
|
221 |
+
|
222 |
+
except Exception as e:
|
223 |
+
logger.error(f"RAG ์ฒด์ธ ์คํ ์ค ์ค๋ฅ: {e}")
|
224 |
+
return f"์ง๋ฌธ ์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}"
|
direct_deepseek.py
ADDED
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
์ง์ DeepSeek API ํธ์ถ์ ์ํ ํด๋ผ์ด์ธํธ ๊ตฌํ
|
3 |
+
"""
|
4 |
+
import os
|
5 |
+
import time
|
6 |
+
import logging
|
7 |
+
import requests
|
8 |
+
import json
|
9 |
+
from typing import Dict, Any, Optional, List
|
10 |
+
|
11 |
+
# ๋ก๊น
์ค์
|
12 |
+
logger = logging.getLogger("DirectDeepSeek")
|
13 |
+
|
14 |
+
class DirectDeepSeekClient:
|
15 |
+
"""
|
16 |
+
DeepSeek API๋ฅผ ์ง์ ํธ์ถํ๋ ํด๋ผ์ด์ธํธ
|
17 |
+
OpenAI ํด๋ผ์ด์ธํธ๋ฅผ ์ฐํํ๊ณ ์ง์ HTTP ์์ฒญ ์ฌ์ฉ
|
18 |
+
"""
|
19 |
+
def __init__(self, api_key: str, model_name: str = "deepseek-chat"):
|
20 |
+
"""
|
21 |
+
ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
|
22 |
+
|
23 |
+
Args:
|
24 |
+
api_key: DeepSeek API ํค
|
25 |
+
model_name: ์ฌ์ฉํ ๋ชจ๋ธ ์ด๋ฆ (๊ธฐ๋ณธ๊ฐ: "deepseek-chat")
|
26 |
+
"""
|
27 |
+
self.api_key = api_key
|
28 |
+
self.model_name = model_name
|
29 |
+
self.endpoint = os.getenv("DEEPSEEK_ENDPOINT", "https://api.deepseek.com/v1/chat/completions")
|
30 |
+
logger.info(f"DirectDeepSeekClient ์ด๊ธฐํ: ๋ชจ๋ธ={model_name}, ์๋ํฌ์ธํธ={self.endpoint}")
|
31 |
+
|
32 |
+
def generate(self,
|
33 |
+
prompt: str,
|
34 |
+
temperature: float = 0.3,
|
35 |
+
max_tokens: int = 1000,
|
36 |
+
max_retries: int = 3,
|
37 |
+
timeout: int = 60) -> Dict[str, Any]:
|
38 |
+
"""
|
39 |
+
ํ
์คํธ ์์ฑ ์์ฒญ
|
40 |
+
|
41 |
+
Args:
|
42 |
+
prompt: ์
๋ ฅ ํ๋กฌํํธ
|
43 |
+
temperature: ์์ฑ ์จ๋ (0.0 ~ 1.0)
|
44 |
+
max_tokens: ์ต๋ ์์ฑ ํ ํฐ ์
|
45 |
+
max_retries: ์ฌ์๋ ํ์
|
46 |
+
timeout: ์์ฒญ ํ์์์ (์ด)
|
47 |
+
|
48 |
+
Returns:
|
49 |
+
์์ฑ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ (success, response, message ๋ฑ)
|
50 |
+
"""
|
51 |
+
# ๋ฉ์์ง ๊ตฌ์ฑ (๋จ์ผ ์ฌ์ฉ์ ๋ฉ์์ง)
|
52 |
+
messages = [{"role": "user", "content": prompt}]
|
53 |
+
return self.chat(messages, temperature, max_tokens, max_retries, timeout)
|
54 |
+
|
55 |
+
def chat(self,
|
56 |
+
messages: List[Dict[str, str]],
|
57 |
+
temperature: float = 0.3,
|
58 |
+
max_tokens: int = 1000,
|
59 |
+
max_retries: int = 3,
|
60 |
+
timeout: int = 60) -> Dict[str, Any]:
|
61 |
+
"""
|
62 |
+
์ฑํ
API ํธ์ถ
|
63 |
+
|
64 |
+
Args:
|
65 |
+
messages: ์ฑํ
๋ฉ์์ง ๋ฆฌ์คํธ (role, content ํค๋ฅผ ๊ฐ์ง ๋์
๋๋ฆฌ ๋ฆฌ์คํธ)
|
66 |
+
temperature: ์์ฑ ์จ๋ (0.0 ~ 1.0)
|
67 |
+
max_tokens: ์ต๋ ์์ฑ ํ ํฐ ์
|
68 |
+
max_retries: ์ฌ์๋ ํ์
|
69 |
+
timeout: ์์ฒญ ํ์์์ (์ด)
|
70 |
+
|
71 |
+
Returns:
|
72 |
+
์์ฑ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ (success, response, message ๋ฑ)
|
73 |
+
"""
|
74 |
+
# API ์์ฒญ ํค๋ ๋ฐ ๋ฐ์ดํฐ
|
75 |
+
headers = {
|
76 |
+
"Content-Type": "application/json",
|
77 |
+
"Authorization": f"Bearer {self.api_key}"
|
78 |
+
}
|
79 |
+
|
80 |
+
payload = {
|
81 |
+
"model": self.model_name,
|
82 |
+
"messages": messages,
|
83 |
+
"temperature": temperature,
|
84 |
+
"max_tokens": max_tokens
|
85 |
+
}
|
86 |
+
|
87 |
+
# ์ฌ์๋ ๋ก์ง
|
88 |
+
retry_delay = 1.0
|
89 |
+
attempt = 0
|
90 |
+
|
91 |
+
while attempt < max_retries:
|
92 |
+
attempt += 1
|
93 |
+
try:
|
94 |
+
logger.info(f"DeepSeek API ์์ฒญ ์๋ ({attempt}/{max_retries})...")
|
95 |
+
|
96 |
+
# API ์์ฒญ ์ ์ก
|
97 |
+
response = requests.post(
|
98 |
+
self.endpoint,
|
99 |
+
headers=headers,
|
100 |
+
json=payload,
|
101 |
+
timeout=timeout
|
102 |
+
)
|
103 |
+
|
104 |
+
# ์๋ต ํ์ธ
|
105 |
+
if response.status_code == 200:
|
106 |
+
result = response.json()
|
107 |
+
|
108 |
+
# ์๋ต ๋ด์ฉ ์ถ์ถ
|
109 |
+
if "choices" in result and len(result["choices"]) > 0:
|
110 |
+
message_content = result["choices"][0].get("message", {}).get("content", "")
|
111 |
+
logger.info(f"DeepSeek API ์๋ต ์ฑ๊ณต (๊ธธ์ด: {len(message_content)})")
|
112 |
+
|
113 |
+
return {
|
114 |
+
"success": True,
|
115 |
+
"response": message_content,
|
116 |
+
"status_code": response.status_code,
|
117 |
+
"raw_response": result
|
118 |
+
}
|
119 |
+
else:
|
120 |
+
logger.warning(f"DeepSeek API ์๋ต์ ์ฑ๊ณตํ์ผ๋ ์์์น ๋ชปํ ์๋ต ํ์: {result}")
|
121 |
+
return {
|
122 |
+
"success": False,
|
123 |
+
"message": "์๋ต์์ ๋ฉ์์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค",
|
124 |
+
"status_code": response.status_code,
|
125 |
+
"raw_response": result
|
126 |
+
}
|
127 |
+
else:
|
128 |
+
logger.error(f"DeepSeek API ์ค๋ฅ: ์ํ ์ฝ๋ {response.status_code}")
|
129 |
+
|
130 |
+
# ์ค๋ฅ ๋ฉ์์ง ์ถ์ถ
|
131 |
+
error_message = ""
|
132 |
+
try:
|
133 |
+
error_data = response.json()
|
134 |
+
error_message = error_data.get("error", {}).get("message", str(error_data))
|
135 |
+
except:
|
136 |
+
error_message = response.text
|
137 |
+
|
138 |
+
# ์์ฒญ ํ๋ ๏ฟฝ๏ฟฝ๊ณผ์ ๋ ์ค๋ ๋๊ธฐ
|
139 |
+
if response.status_code == 429:
|
140 |
+
retry_delay = min(retry_delay * 3, 15)
|
141 |
+
else:
|
142 |
+
retry_delay = min(retry_delay * 2, 10)
|
143 |
+
|
144 |
+
if attempt < max_retries:
|
145 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
146 |
+
time.sleep(retry_delay)
|
147 |
+
else:
|
148 |
+
# ๋ชจ๋ ์๋ ์คํจ
|
149 |
+
return {
|
150 |
+
"success": False,
|
151 |
+
"message": f"API ์ค๋ฅ: {error_message}",
|
152 |
+
"status_code": response.status_code
|
153 |
+
}
|
154 |
+
|
155 |
+
except requests.exceptions.Timeout:
|
156 |
+
logger.error("DeepSeek API ์์ฒญ ์๊ฐ ์ด๊ณผ")
|
157 |
+
|
158 |
+
if attempt < max_retries:
|
159 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
160 |
+
time.sleep(retry_delay)
|
161 |
+
retry_delay = min(retry_delay * 2, 10)
|
162 |
+
else:
|
163 |
+
return {
|
164 |
+
"success": False,
|
165 |
+
"message": "API ์์ฒญ ์๊ฐ ์ด๊ณผ",
|
166 |
+
"status_code": None
|
167 |
+
}
|
168 |
+
|
169 |
+
except requests.exceptions.ConnectionError:
|
170 |
+
logger.error("DeepSeek API ์ฐ๊ฒฐ ์คํจ")
|
171 |
+
|
172 |
+
if attempt < max_retries:
|
173 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
174 |
+
time.sleep(retry_delay)
|
175 |
+
retry_delay = min(retry_delay * 2, 10)
|
176 |
+
else:
|
177 |
+
return {
|
178 |
+
"success": False,
|
179 |
+
"message": "API ์๋ฒ ์ฐ๊ฒฐ ์คํจ",
|
180 |
+
"status_code": None
|
181 |
+
}
|
182 |
+
|
183 |
+
except Exception as e:
|
184 |
+
logger.error(f"DeepSeek API ์์ฒญ ์ค ์์์น ๋ชปํ ์ค๋ฅ: {e}")
|
185 |
+
|
186 |
+
if attempt < max_retries:
|
187 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
188 |
+
time.sleep(retry_delay)
|
189 |
+
retry_delay = min(retry_delay * 2, 10)
|
190 |
+
else:
|
191 |
+
return {
|
192 |
+
"success": False,
|
193 |
+
"message": f"์์์น ๋ชปํ ์ค๋ฅ: {str(e)}",
|
194 |
+
"status_code": None
|
195 |
+
}
|
196 |
+
|
197 |
+
# ๋ชจ๋ ์๋ ์คํจ
|
198 |
+
return {
|
199 |
+
"success": False,
|
200 |
+
"message": "์ต๋ ์ฌ์๋ ํ์ ์ด๊ณผ",
|
201 |
+
"status_code": None
|
202 |
+
}
|
203 |
+
|
204 |
+
def system_prompt_chat(self,
|
205 |
+
system_prompt: str,
|
206 |
+
user_prompt: str,
|
207 |
+
temperature: float = 0.3,
|
208 |
+
max_tokens: int = 1000,
|
209 |
+
max_retries: int = 3,
|
210 |
+
timeout: int = 60) -> Dict[str, Any]:
|
211 |
+
"""
|
212 |
+
์์คํ
ํ๋กฌํํธ์ ์ฌ์ฉ์ ํ๋กฌํํธ๋ฅผ ์ด์ฉํ ์ฑํ
API ํธ์ถ
|
213 |
+
|
214 |
+
Args:
|
215 |
+
system_prompt: ์์คํ
ํ๋กฌํํธ
|
216 |
+
user_prompt: ์ฌ์ฉ์ ํ๋กฌํํธ
|
217 |
+
temperature: ์์ฑ ์จ๋ (0.0 ~ 1.0)
|
218 |
+
max_tokens: ์ต๋ ์์ฑ ํ ํฐ ์
|
219 |
+
max_retries: ์ฌ์๋ ํ์
|
220 |
+
timeout: ์์ฒญ ํ์์์ (์ด)
|
221 |
+
|
222 |
+
Returns:
|
223 |
+
์์ฑ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ
|
224 |
+
"""
|
225 |
+
messages = [
|
226 |
+
{"role": "system", "content": system_prompt},
|
227 |
+
{"role": "user", "content": user_prompt}
|
228 |
+
]
|
229 |
+
|
230 |
+
return self.chat(messages, temperature, max_tokens, max_retries, timeout)
|
231 |
+
|
232 |
+
|
233 |
+
# ๋จ๋
์คํ์ ์ํ ํ
์คํธ ์ฝ๋
|
234 |
+
if __name__ == "__main__":
|
235 |
+
# ๋ก๊น
์ค์
|
236 |
+
logging.basicConfig(level=logging.INFO)
|
237 |
+
|
238 |
+
# API ํค ํ์ธ
|
239 |
+
api_key = os.environ.get("DEEPSEEK_API_KEY")
|
240 |
+
if not api_key:
|
241 |
+
print("ํ๊ฒฝ ๋ณ์ DEEPSEEK_API_KEY๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.")
|
242 |
+
exit(1)
|
243 |
+
|
244 |
+
# ํด๋ผ์ด์ธํธ ์์ฑ
|
245 |
+
client = DirectDeepSeekClient(api_key)
|
246 |
+
|
247 |
+
# ๊ฐ๋จํ ํ
์คํธ
|
248 |
+
response = client.generate("Hello, what can you do?")
|
249 |
+
|
250 |
+
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
|
251 |
+
if response["success"]:
|
252 |
+
print("์๋ต ์ฑ๊ณต!")
|
253 |
+
print(response["response"])
|
254 |
+
else:
|
255 |
+
print(f"์๋ต ์คํจ: {response['message']}")
|
fallback_rag_chain.py
CHANGED
@@ -1,53 +1,29 @@
|
|
1 |
"""
|
2 |
-
ํด๋ฐฑ RAG ์ฒด์ธ ๊ตฌํ (๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ๋ง ํฌํจ)
|
3 |
"""
|
4 |
import os
|
5 |
import logging
|
6 |
import time
|
7 |
-
import requests
|
8 |
-
import json
|
9 |
from typing import List, Dict, Any, Optional, Tuple
|
10 |
from langchain.schema import Document
|
11 |
-
from langchain.prompts import PromptTemplate
|
12 |
-
from langchain_core.output_parsers import StrOutputParser
|
13 |
-
from langchain_core.runnables import RunnablePassthrough
|
14 |
|
15 |
-
# DeepSeek
|
16 |
from direct_deepseek import DirectDeepSeekClient
|
17 |
|
18 |
# ์ค์ ๊ฐ์ ธ์ค๊ธฐ
|
19 |
from config import (
|
20 |
LLM_MODEL, USE_OPENAI, USE_DEEPSEEK,
|
21 |
-
|
22 |
TOP_K_RETRIEVAL
|
23 |
)
|
24 |
|
25 |
# ๋ก๊น
์ค์
|
26 |
logger = logging.getLogger("FallbackRAGChain")
|
27 |
|
28 |
-
class CustomDeepSeekLLM:
|
29 |
-
"""
|
30 |
-
OpenAI ํด๋ผ์ด์ธํธ๋ฅผ ์ฐํํ๋ ์ปค์คํ
DeepSeek LLM ํด๋์ค
|
31 |
-
"""
|
32 |
-
def __init__(self, api_key, model, temperature=0.3, request_timeout=120):
|
33 |
-
self.client = DirectDeepSeekClient(api_key, model)
|
34 |
-
self.temperature = temperature
|
35 |
-
self.request_timeout = request_timeout
|
36 |
-
logger.info(f"์ปค์คํ
DeepSeek LLM ์ด๊ธฐํ: ๋ชจ๋ธ={model}, ์จ๋={temperature}")
|
37 |
-
|
38 |
-
def invoke(self, prompt):
|
39 |
-
"""
|
40 |
-
ํ
์คํธ ์์ฑ ์์ฒญ
|
41 |
-
"""
|
42 |
-
result = self.client.generate(prompt, max_retries=3, timeout=self.request_timeout)
|
43 |
-
if result["success"]:
|
44 |
-
return result["response"]
|
45 |
-
else:
|
46 |
-
raise Exception(f"DeepSeek API ํธ์ถ ์คํจ: {result['message']}")
|
47 |
-
|
48 |
class FallbackRAGChain:
|
49 |
"""
|
50 |
๊ธฐ๋ณธ์ ์ธ RAG ์ฒด์ธ ๊ตฌํ (๋จ์ํ๋ ๋ฒ์ , ๋ฌธ์ ํด๊ฒฐ์ฉ)
|
|
|
51 |
"""
|
52 |
|
53 |
def __init__(self, vector_store):
|
@@ -60,41 +36,24 @@ class FallbackRAGChain:
|
|
60 |
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ...")
|
61 |
self.vector_store = vector_store
|
62 |
|
63 |
-
# DeepSeek ๋ชจ๋ธ ์ง์ ์ด๊ธฐํ
|
64 |
if USE_DEEPSEEK and DEEPSEEK_API_KEY:
|
65 |
logger.info(f"DeepSeek ๋ชจ๋ธ ์ง์ ์ด๊ธฐํ: {DEEPSEEK_MODEL}")
|
66 |
try:
|
67 |
-
self.
|
68 |
api_key=DEEPSEEK_API_KEY,
|
69 |
-
|
70 |
-
temperature=0.3,
|
71 |
-
request_timeout=120
|
72 |
)
|
73 |
logger.info("DeepSeek ๋ชจ๋ธ ์ง์ ์ด๊ธฐํ ์ฑ๊ณต")
|
74 |
except Exception as e:
|
75 |
logger.error(f"DeepSeek ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {e}")
|
76 |
# ์คํ๋ผ์ธ ๋ชจ๋๋ก ํด๋ฐฑ
|
77 |
-
self.
|
78 |
logger.warning("LLM์ด ์ด๊ธฐํ๋์ง ์์ ์คํ๋ผ์ธ ๋ชจ๋๋ก ๋์ํฉ๋๋ค.")
|
79 |
else:
|
80 |
# LLM์ด ์ค์ ๋์ง ์์
|
81 |
logger.warning("LLM์ด ์ค์ ๋์ง ์์ ์คํ๋ผ์ธ ๋ชจ๋๋ก ๋์ํฉ๋๋ค.")
|
82 |
-
self.
|
83 |
-
|
84 |
-
# ํ๋กฌํํธ ์ค์
|
85 |
-
self.prompt_template = """
|
86 |
-
๋ค์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ฌธ์ ์ ํํ๊ฒ ๋ต๋ณํด์ฃผ์ธ์.
|
87 |
-
|
88 |
-
์ง๋ฌธ: {question}
|
89 |
-
|
90 |
-
์ฐธ๊ณ ์ ๋ณด:
|
91 |
-
{context}
|
92 |
-
|
93 |
-
์ฐธ๊ณ ์ ๋ณด์ ๋ต์ด ์์ผ๋ฉด ๋ฐ๋์ ๊ทธ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณํ์ธ์.
|
94 |
-
์ฐธ๊ณ ์ ๋ณด์ ๋ต์ด ์๋ ๊ฒฝ์ฐ์๋ ์ผ๋ฐ์ ์ธ ์ง์์ ํ์ฉํ์ฌ ๋ต๋ณํ ์ ์์ง๋ง, "์ ๊ณต๋ ๋ฌธ์์๋ ์ด ์ ๋ณด๊ฐ ์์ผ๋, ์ผ๋ฐ์ ์ผ๋ก๋..." ์์ผ๋ก ์์ํ์ธ์.
|
95 |
-
๋ต๋ณ์ ์ ํํ๊ณ ๊ฐ๊ฒฐํ๊ฒ ์ ๊ณตํ๋, ๊ฐ๋ฅํ ์ฐธ๊ณ ์ ๋ณด์์ ๊ทผ๊ฑฐ๋ฅผ ์ฐพ์ ์ค๋ช
ํด์ฃผ์ธ์.
|
96 |
-
์ฐธ๊ณ ์ ๋ณด์ ์ถ์ฒ๋ ํจ๊ป ์๋ ค์ฃผ์ธ์.
|
97 |
-
"""
|
98 |
|
99 |
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์๋ฃ")
|
100 |
|
@@ -143,6 +102,38 @@ class FallbackRAGChain:
|
|
143 |
logger.error(f"๊ฒ์ ์ค ์ค๋ฅ: {e}")
|
144 |
return f"๊ฒ์ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}"
|
145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
def _generate_simple_response(self, query: str, context: str) -> str:
|
147 |
"""
|
148 |
๊ฐ๋จํ ์คํ๋ผ์ธ ์๋ต ์์ฑ (LLM์ด ์์ ๋ ์ฌ์ฉ)
|
@@ -193,12 +184,12 @@ class FallbackRAGChain:
|
|
193 |
context = self._retrieve(query)
|
194 |
|
195 |
# LLM์ด ์ด๊ธฐํ๋์ง ์์ ๊ฒฝ์ฐ ์คํ๋ผ์ธ ์๋ต
|
196 |
-
if self.
|
197 |
logger.warning("LLM์ด ์ด๊ธฐํ๋์ง ์์ ์คํ๋ผ์ธ ์๋ต ์์ฑ")
|
198 |
return self._generate_simple_response(query, context)
|
199 |
|
200 |
# ํ๋กฌํํธ ๊ตฌ์ฑ
|
201 |
-
|
202 |
|
203 |
# ์๋ต ์์ฑ (์ต๋ 3ํ ์๋)
|
204 |
max_retries = 3
|
@@ -207,18 +198,31 @@ class FallbackRAGChain:
|
|
207 |
for attempt in range(max_retries):
|
208 |
try:
|
209 |
logger.info(f"์๋ต ์์ฑ ์๋ ({attempt+1}/{max_retries})")
|
210 |
-
|
211 |
-
|
212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
except Exception as e:
|
214 |
-
logger.error(f"์๋ต ์์ฑ
|
215 |
if attempt < max_retries - 1:
|
216 |
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
217 |
time.sleep(retry_delay)
|
218 |
retry_delay *= 2
|
219 |
else:
|
220 |
-
# ๋ชจ๋ ์๋ ์คํจ ์ ์คํ๋ผ์ธ ์๋ต
|
221 |
-
logger.warning("์ต๋ ์ฌ์๋ ํ์ ์ด๊ณผ, ์คํ๋ผ์ธ ์๋ต ์์ฑ")
|
222 |
return self._generate_simple_response(query, context)
|
223 |
|
224 |
except Exception as e:
|
|
|
1 |
"""
|
2 |
+
ํด๋ฐฑ RAG ์ฒด์ธ ๊ตฌํ (๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ๋ง ํฌํจ) - ์ง์ DeepSeek API ํธ์ถ ๋ฐฉ์
|
3 |
"""
|
4 |
import os
|
5 |
import logging
|
6 |
import time
|
|
|
|
|
7 |
from typing import List, Dict, Any, Optional, Tuple
|
8 |
from langchain.schema import Document
|
|
|
|
|
|
|
9 |
|
10 |
+
# ์ง์ DeepSeek ํด๋ผ์ด์ธํธ ์ฌ์ฉ
|
11 |
from direct_deepseek import DirectDeepSeekClient
|
12 |
|
13 |
# ์ค์ ๊ฐ์ ธ์ค๊ธฐ
|
14 |
from config import (
|
15 |
LLM_MODEL, USE_OPENAI, USE_DEEPSEEK,
|
16 |
+
DEEPSEEK_API_KEY, DEEPSEEK_ENDPOINT, DEEPSEEK_MODEL,
|
17 |
TOP_K_RETRIEVAL
|
18 |
)
|
19 |
|
20 |
# ๋ก๊น
์ค์
|
21 |
logger = logging.getLogger("FallbackRAGChain")
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
class FallbackRAGChain:
|
24 |
"""
|
25 |
๊ธฐ๋ณธ์ ์ธ RAG ์ฒด์ธ ๊ตฌํ (๋จ์ํ๋ ๋ฒ์ , ๋ฌธ์ ํด๊ฒฐ์ฉ)
|
26 |
+
์ง์ DeepSeek API ํธ์ถ ๋ฐฉ์ ์ฌ์ฉ
|
27 |
"""
|
28 |
|
29 |
def __init__(self, vector_store):
|
|
|
36 |
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ...")
|
37 |
self.vector_store = vector_store
|
38 |
|
39 |
+
# DeepSeek ๋ชจ๋ธ ์ง์ ์ด๊ธฐํ
|
40 |
if USE_DEEPSEEK and DEEPSEEK_API_KEY:
|
41 |
logger.info(f"DeepSeek ๋ชจ๋ธ ์ง์ ์ด๊ธฐํ: {DEEPSEEK_MODEL}")
|
42 |
try:
|
43 |
+
self.client = DirectDeepSeekClient(
|
44 |
api_key=DEEPSEEK_API_KEY,
|
45 |
+
model_name=DEEPSEEK_MODEL
|
|
|
|
|
46 |
)
|
47 |
logger.info("DeepSeek ๋ชจ๋ธ ์ง์ ์ด๊ธฐํ ์ฑ๊ณต")
|
48 |
except Exception as e:
|
49 |
logger.error(f"DeepSeek ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {e}")
|
50 |
# ์คํ๋ผ์ธ ๋ชจ๋๋ก ํด๋ฐฑ
|
51 |
+
self.client = None
|
52 |
logger.warning("LLM์ด ์ด๊ธฐํ๋์ง ์์ ์คํ๋ผ์ธ ๋ชจ๋๋ก ๋์ํฉ๋๋ค.")
|
53 |
else:
|
54 |
# LLM์ด ์ค์ ๋์ง ์์
|
55 |
logger.warning("LLM์ด ์ค์ ๋์ง ์์ ์คํ๋ผ์ธ ๋ชจ๋๋ก ๋์ํฉ๋๋ค.")
|
56 |
+
self.client = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
logger.info("ํด๋ฐฑ RAG ์ฒด์ธ ์ด๊ธฐํ ์๋ฃ")
|
59 |
|
|
|
102 |
logger.error(f"๊ฒ์ ์ค ์ค๋ฅ: {e}")
|
103 |
return f"๊ฒ์ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}"
|
104 |
|
105 |
+
def _generate_prompt(self, query: str, context: str) -> List[Dict[str, str]]:
|
106 |
+
"""
|
107 |
+
ํ๋กฌํํธ ์์ฑ (DeepSeek API ํ์)
|
108 |
+
|
109 |
+
Args:
|
110 |
+
query: ์ฌ์ฉ์ ์ง๋ฌธ
|
111 |
+
context: ๊ฒ์ ๊ฒฐ๊ณผ ์ปจํ
์คํธ
|
112 |
+
|
113 |
+
Returns:
|
114 |
+
DeepSeek API์ฉ messages ํ์
|
115 |
+
"""
|
116 |
+
# ์์คํ
ํ๋กฌํํธ
|
117 |
+
system_prompt = """๋ค์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ฌธ์ ์ ํํ๊ฒ ๋ต๋ณํด์ฃผ์ธ์.
|
118 |
+
์ฐธ๊ณ ์ ๋ณด์ ๋ต์ด ์์ผ๋ฉด ๋ฐ๋์ ๊ทธ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณํ์ธ์.
|
119 |
+
์ฐธ๊ณ ์ ๋ณด์ ๋ต์ด ์๋ ๊ฒฝ์ฐ์๋ ์ผ๋ฐ์ ์ธ ์ง์์ ํ์ฉํ์ฌ ๋ต๋ณํ ์ ์์ง๋ง, "์ ๊ณต๋ ๋ฌธ์์๋ ์ด ์ ๋ณด๊ฐ ์์ผ๋, ์ผ๋ฐ์ ์ผ๋ก๋..." ์์ผ๋ก ์์ํ์ธ์.
|
120 |
+
๋ต๋ณ์ ์ ํํ๊ณ ๊ฐ๊ฒฐํ๊ฒ ์ ๊ณตํ๋, ๊ฐ๋ฅํ ์ฐธ๊ณ ์ ๋ณด์์ ๊ทผ๏ฟฝ๏ฟฝ๋ฅผ ์ฐพ์ ์ค๋ช
ํด์ฃผ์ธ์.
|
121 |
+
์ฐธ๊ณ ์ ๋ณด์ ์ถ์ฒ๋ ํจ๊ป ์๋ ค์ฃผ์ธ์."""
|
122 |
+
|
123 |
+
# ์ฌ์ฉ์ ํ๋กฌํํธ (์ง๋ฌธ๊ณผ ์ปจํ
์คํธ ํฌํจ)
|
124 |
+
user_prompt = f"""์ง๋ฌธ: {query}
|
125 |
+
|
126 |
+
์ฐธ๊ณ ์ ๋ณด:
|
127 |
+
{context}"""
|
128 |
+
|
129 |
+
# DeepSeek API์ ๋ง๋ ๋ฉ์์ง ํฌ๋งท
|
130 |
+
messages = [
|
131 |
+
{"role": "system", "content": system_prompt},
|
132 |
+
{"role": "user", "content": user_prompt}
|
133 |
+
]
|
134 |
+
|
135 |
+
return messages
|
136 |
+
|
137 |
def _generate_simple_response(self, query: str, context: str) -> str:
|
138 |
"""
|
139 |
๊ฐ๋จํ ์คํ๋ผ์ธ ์๋ต ์์ฑ (LLM์ด ์์ ๋ ์ฌ์ฉ)
|
|
|
184 |
context = self._retrieve(query)
|
185 |
|
186 |
# LLM์ด ์ด๊ธฐํ๋์ง ์์ ๊ฒฝ์ฐ ์คํ๋ผ์ธ ์๋ต
|
187 |
+
if self.client is None:
|
188 |
logger.warning("LLM์ด ์ด๊ธฐํ๋์ง ์์ ์คํ๋ผ์ธ ์๋ต ์์ฑ")
|
189 |
return self._generate_simple_response(query, context)
|
190 |
|
191 |
# ํ๋กฌํํธ ๊ตฌ์ฑ
|
192 |
+
messages = self._generate_prompt(query, context)
|
193 |
|
194 |
# ์๋ต ์์ฑ (์ต๋ 3ํ ์๋)
|
195 |
max_retries = 3
|
|
|
198 |
for attempt in range(max_retries):
|
199 |
try:
|
200 |
logger.info(f"์๋ต ์์ฑ ์๋ ({attempt+1}/{max_retries})")
|
201 |
+
|
202 |
+
# ์ง์ DeepSeek API ํธ์ถ
|
203 |
+
response = self.client.chat(messages)
|
204 |
+
|
205 |
+
if response["success"]:
|
206 |
+
logger.info(f"์๋ต ์์ฑ ์ฑ๊ณต (๊ธธ์ด: {len(response['response'])})")
|
207 |
+
return response["response"]
|
208 |
+
else:
|
209 |
+
logger.error(f"์๋ต ์์ฑ ์คํจ: {response['message']}")
|
210 |
+
if attempt < max_retries - 1:
|
211 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
212 |
+
time.sleep(retry_delay)
|
213 |
+
retry_delay *= 2
|
214 |
+
else:
|
215 |
+
# ๋ชจ๋ ์๋ ์คํจ ์ ์คํ๋ผ์ธ ์๋ต
|
216 |
+
logger.warning("์ต๋ ์ฌ์๋ ํ์ ์ด๊ณผ, ์คํ๋ผ์ธ ์๋ต ์์ฑ")
|
217 |
+
return self._generate_simple_response(query, context)
|
218 |
except Exception as e:
|
219 |
+
logger.error(f"์๋ต ์์ฑ ์ค ์ค๋ฅ: {e}")
|
220 |
if attempt < max_retries - 1:
|
221 |
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
222 |
time.sleep(retry_delay)
|
223 |
retry_delay *= 2
|
224 |
else:
|
225 |
+
# ๋ชจ๋ ์๋ ์คํจ ์ ์คํ๋ผ์ธ ์๋ต ์์ฑ
|
|
|
226 |
return self._generate_simple_response(query, context)
|
227 |
|
228 |
except Exception as e:
|
monitoring.py
DELETED
@@ -1,136 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
Langfuse๋ฅผ ํ์ฉํ ๋ชจ๋ํฐ๋ง ๊ตฌํ (์ ํ์ )
|
3 |
-
"""
|
4 |
-
from typing import Dict, Any, Optional
|
5 |
-
import time
|
6 |
-
import os
|
7 |
-
from dotenv import load_dotenv
|
8 |
-
|
9 |
-
# ํ๊ฒฝ ๋ณ์ ๋ก๋
|
10 |
-
load_dotenv()
|
11 |
-
|
12 |
-
# ์ค์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
|
13 |
-
LANGFUSE_SECRET_KEY = os.getenv("LANGFUSE_SECRET_KEY", "")
|
14 |
-
LANGFUSE_PUBLIC_KEY = os.getenv("LANGFUSE_PUBLIC_KEY", "")
|
15 |
-
LANGFUSE_HOST = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
|
16 |
-
|
17 |
-
class LangfuseMonitoring:
|
18 |
-
def __init__(self):
|
19 |
-
"""
|
20 |
-
Langfuse ๋ชจ๋ํฐ๋ง ์ด๊ธฐํ (์ ํ์ ๊ธฐ๋ฅ)
|
21 |
-
"""
|
22 |
-
self.enabled = False
|
23 |
-
print("๋ชจ๋ํฐ๋ง ๊ธฐ๋ฅ์ ์ด๊ธฐํํฉ๋๋ค...")
|
24 |
-
|
25 |
-
# Langfuse๊ฐ ์ค์น๋์ด ์๋์ง ํ์ธ
|
26 |
-
try:
|
27 |
-
from langfuse import Langfuse
|
28 |
-
|
29 |
-
if LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY:
|
30 |
-
try:
|
31 |
-
self.langfuse = Langfuse(
|
32 |
-
public_key=LANGFUSE_PUBLIC_KEY,
|
33 |
-
secret_key=LANGFUSE_SECRET_KEY,
|
34 |
-
host=LANGFUSE_HOST,
|
35 |
-
)
|
36 |
-
self.enabled = True
|
37 |
-
print("Langfuse ๋ชจ๋ํฐ๋ง์ด ํ์ฑํ๋์์ต๋๋ค.")
|
38 |
-
except Exception as e:
|
39 |
-
print(f"Langfuse ์ด๊ธฐํ ์คํจ: {e}")
|
40 |
-
else:
|
41 |
-
print("Langfuse API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค. ๋ชจ๋ํฐ๋ง์ ๋นํ์ฑํ๋ฉ๋๋ค.")
|
42 |
-
except ImportError:
|
43 |
-
print("langfuse ํจํค์ง๊ฐ ์ค์น๋์ง ์์์ต๋๋ค. ๋ชจ๋ํฐ๋ง ๊ธฐ๋ฅ์ด ๋นํ์ฑํ๋ฉ๋๋ค.")
|
44 |
-
print("pip install langfuse ๋ช
๋ น์ผ๋ก ์ค์นํ ์ ์์ต๋๋ค.")
|
45 |
-
|
46 |
-
def start_trace(self, name: str, user_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> Any:
|
47 |
-
"""
|
48 |
-
์ ํธ๋ ์ด์ค ์์
|
49 |
-
|
50 |
-
Args:
|
51 |
-
name: ํธ๋ ์ด์ค ์ด๋ฆ
|
52 |
-
user_id: ์ฌ์ฉ์ ID (์ ํ์ )
|
53 |
-
metadata: ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ (์ ํ์ )
|
54 |
-
|
55 |
-
Returns:
|
56 |
-
ํธ๋ ์ด์ค ๊ฐ์ฒด ๋๋ None
|
57 |
-
"""
|
58 |
-
if not self.enabled:
|
59 |
-
return None
|
60 |
-
|
61 |
-
try:
|
62 |
-
return self.langfuse.trace(
|
63 |
-
name=name,
|
64 |
-
user_id=user_id,
|
65 |
-
metadata=metadata or {},
|
66 |
-
)
|
67 |
-
except Exception as e:
|
68 |
-
print(f"ํธ๋ ์ด์ค ์์ฑ ์คํจ: {e}")
|
69 |
-
return None
|
70 |
-
|
71 |
-
def log_generation(self, trace: Any, name: str, prompt: str, response: str, metadata: Optional[Dict[str, Any]] = None) -> None:
|
72 |
-
"""
|
73 |
-
LLM ์์ฑ ๋ก๊น
|
74 |
-
|
75 |
-
Args:
|
76 |
-
trace: ํธ๋ ์ด์ค ๊ฐ์ฒด
|
77 |
-
name: ์์ฑ ์ด๋ฆ
|
78 |
-
prompt: ์
๋ ฅ ํ๋กฌํํธ
|
79 |
-
response: ๋ชจ๋ธ ์๋ต
|
80 |
-
metadata: ์ถ๊ฐ ๋ฉํ๋ฐ์ดํฐ (์ ํ์ )
|
81 |
-
"""
|
82 |
-
if not self.enabled or trace is None:
|
83 |
-
return
|
84 |
-
|
85 |
-
try:
|
86 |
-
trace.generation(
|
87 |
-
name=name,
|
88 |
-
model="user-defined-model",
|
89 |
-
prompt=prompt,
|
90 |
-
completion=response,
|
91 |
-
metadata=metadata or {},
|
92 |
-
)
|
93 |
-
except Exception as e:
|
94 |
-
print(f"์์ฑ ๋ก๊น
์คํจ: {e}")
|
95 |
-
|
96 |
-
def log_span(self, trace: Any, name: str, input_data: Any, output_data: Any, start_time: float, end_time: float) -> None:
|
97 |
-
"""
|
98 |
-
์ฒ๋ฆฌ ๊ตฌ๊ฐ ๋ก๊น
|
99 |
-
|
100 |
-
Args:
|
101 |
-
trace: ํธ๋ ์ด์ค ๊ฐ์ฒด
|
102 |
-
name: ๊ตฌ๊ฐ ์ด๋ฆ
|
103 |
-
input_data: ์
๋ ฅ ๋ฐ์ดํฐ
|
104 |
-
output_data: ์ถ๋ ฅ ๋ฐ์ดํฐ
|
105 |
-
start_time: ์์ ์๊ฐ
|
106 |
-
end_time: ์ข
๋ฃ ์๊ฐ
|
107 |
-
"""
|
108 |
-
if not self.enabled or trace is None:
|
109 |
-
return
|
110 |
-
|
111 |
-
try:
|
112 |
-
trace.span(
|
113 |
-
name=name,
|
114 |
-
start_time=start_time,
|
115 |
-
end_time=end_time,
|
116 |
-
input=input_data,
|
117 |
-
output=output_data,
|
118 |
-
metadata={"duration_ms": (end_time - start_time) * 1000},
|
119 |
-
)
|
120 |
-
except Exception as e:
|
121 |
-
print(f"๊ตฌ๊ฐ ๋ก๊น
์คํจ: {e}")
|
122 |
-
|
123 |
-
def end_trace(self, trace: Any) -> None:
|
124 |
-
"""
|
125 |
-
ํธ๋ ์ด์ค ์ข
๋ฃ
|
126 |
-
|
127 |
-
Args:
|
128 |
-
trace: ์ข
๋ฃํ ํธ๋ ์ด์ค ๊ฐ์ฒด
|
129 |
-
"""
|
130 |
-
if not self.enabled or trace is None:
|
131 |
-
return
|
132 |
-
|
133 |
-
try:
|
134 |
-
trace.update(status="success")
|
135 |
-
except Exception as e:
|
136 |
-
print(f"ํธ๋ ์ด์ค ์ข
๋ฃ ์คํจ: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rag_chain.py
CHANGED
@@ -1,163 +1,133 @@
|
|
1 |
"""
|
2 |
-
|
3 |
"""
|
4 |
import os
|
|
|
5 |
import logging
|
6 |
-
|
7 |
-
|
8 |
-
from
|
9 |
-
from langchain_core.output_parsers import StrOutputParser
|
10 |
-
from langchain_core.runnables import RunnablePassthrough
|
11 |
-
from langchain_community.chat_models import ChatOllama
|
12 |
-
from langchain_openai import ChatOpenAI
|
13 |
-
|
14 |
-
from config import (
|
15 |
-
OLLAMA_HOST, LLM_MODEL, USE_OPENAI, USE_DEEPSEEK,
|
16 |
-
OPENAI_API_KEY, DEEPSEEK_API_KEY, DEEPSEEK_ENDPOINT, DEEPSEEK_MODEL,
|
17 |
-
TOP_K_RETRIEVAL, TOP_K_RERANK
|
18 |
-
)
|
19 |
-
from vector_store import VectorStore
|
20 |
-
from reranker import Reranker
|
21 |
-
|
22 |
-
# DeepSeek ์ ํธ๋ฆฌํฐ ์ํฌํธ
|
23 |
-
try:
|
24 |
-
from deepseek_utils import test_deepseek_api, create_deepseek_client, DeepSeekError
|
25 |
-
DEEPSEEK_UTILS_AVAILABLE = True
|
26 |
-
except ImportError:
|
27 |
-
DEEPSEEK_UTILS_AVAILABLE = False
|
28 |
|
29 |
# ๋ก๊น
์ค์
|
30 |
-
logger = logging.getLogger("
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
pass
|
39 |
-
|
40 |
-
class LLMInitError(Exception):
|
41 |
-
"""LLM ๋ชจ๋ธ ์ด๊ธฐํ ๊ด๋ จ ์ค๋ฅ"""
|
42 |
-
pass
|
43 |
-
|
44 |
-
class RerankerInitError(Exception):
|
45 |
-
"""๋ฆฌ๋ญ์ปค ์ด๊ธฐํ ๊ด๋ จ ์ค๋ฅ"""
|
46 |
-
pass
|
47 |
-
|
48 |
-
class QueryProcessingError(Exception):
|
49 |
-
"""์ฟผ๋ฆฌ ์ฒ๋ฆฌ ์ค ๋ฐ์ํ ์ค๋ฅ"""
|
50 |
-
pass
|
51 |
-
|
52 |
-
|
53 |
-
class RAGChain:
|
54 |
-
def __init__(self, vector_store: VectorStore, use_reranker: bool = True):
|
55 |
"""
|
56 |
-
|
57 |
|
58 |
Args:
|
59 |
-
|
60 |
-
|
61 |
"""
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
# ๋ฆฌ๋ญ์ปค ์ด๊ธฐํ
|
74 |
-
if use_reranker:
|
75 |
-
try:
|
76 |
-
self.reranker = Reranker()
|
77 |
-
logger.info("๋ฆฌ๋ญ์ปค ์ด๊ธฐํ ์ฑ๊ณต")
|
78 |
-
except Exception as e:
|
79 |
-
logger.error(f"๋ฆฌ๋ญ์ปค ์ด๊ธฐํ ์คํจ: {e}", exc_info=True)
|
80 |
-
self.reranker = None
|
81 |
-
self.use_reranker = False
|
82 |
-
logger.warning("๋ฆฌ๋ญ์ปค ์ฌ์ฉ์ด ๋นํ์ฑํ๋์์ต๋๋ค")
|
83 |
-
else:
|
84 |
-
self.reranker = None
|
85 |
-
|
86 |
-
# LLM ๋ชจ๋ธ ์ด๊ธฐํ
|
87 |
-
self._initialize_llm()
|
88 |
-
|
89 |
-
# RAG ์ฒด์ธ ๊ตฌ์ฑ ๋ฐ ํ๋กฌํํธ ์ค์
|
90 |
-
logger.info("RAG ์ฒด์ธ ์ค์ ์์...")
|
91 |
-
self.setup_chain()
|
92 |
-
logger.info("RAG ์ฒด์ธ ์ค์ ์๋ฃ")
|
93 |
-
|
94 |
-
except RAGChainInitError:
|
95 |
-
# ์ด๋ฏธ ๋ก๊น
๋จ
|
96 |
-
raise
|
97 |
-
except Exception as e:
|
98 |
-
logger.error(f"RAGChain ์ด๊ธฐํ ์ค ์์์น ๋ชปํ ์ค๋ฅ: {e}", exc_info=True)
|
99 |
-
raise RAGChainInitError(f"RAG ์ฒด์ธ ์ด๊ธฐํ ์คํจ: {str(e)}")
|
100 |
-
|
101 |
-
def _test_deepseek_api(self):
|
102 |
"""
|
103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
Returns:
|
106 |
-
|
107 |
"""
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
if not DEEPSEEK_API_KEY:
|
121 |
-
return {
|
122 |
-
"success": False,
|
123 |
-
"message": "API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค."
|
124 |
-
}
|
125 |
-
|
126 |
-
logger.info(f"DeepSeek API ์ฐ๊ฒฐ ํ
์คํธ: {DEEPSEEK_ENDPOINT}, ๋ชจ๋ธ: {DEEPSEEK_MODEL}")
|
127 |
-
|
128 |
-
# ํ
์คํธ์ฉ ๊ฐ๋จํ ํ๋กฌํํธ
|
129 |
-
test_prompt = "Hello, please respond with a short greeting."
|
130 |
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
|
|
136 |
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
# API ์์ฒญ ์ ์ก
|
145 |
response = requests.post(
|
146 |
-
|
147 |
headers=headers,
|
148 |
-
|
149 |
-
timeout=
|
150 |
)
|
151 |
|
152 |
# ์๋ต ํ์ธ
|
153 |
if response.status_code == 200:
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
else:
|
160 |
logger.error(f"DeepSeek API ์ค๋ฅ: ์ํ ์ฝ๋ {response.status_code}")
|
|
|
|
|
161 |
error_message = ""
|
162 |
try:
|
163 |
error_data = response.json()
|
@@ -165,200 +135,121 @@ class RAGChain:
|
|
165 |
except:
|
166 |
error_message = response.text
|
167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
return {
|
169 |
"success": False,
|
170 |
-
"message":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
}
|
172 |
-
except Exception as e:
|
173 |
-
logger.error(f"DeepSeek API ํ
์คํธ ์ค ์ค๋ฅ: {e}", exc_info=True)
|
174 |
-
return {
|
175 |
-
"success": False,
|
176 |
-
"message": f"์ฐ๊ฒฐ ํ
์คํธ ์ค๋ฅ: {str(e)}"
|
177 |
-
}
|
178 |
-
|
179 |
-
def _initialize_llm(self):
|
180 |
-
"""LLM ๋ชจ๋ธ ์ด๊ธฐํ (ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค๋ฅธ ๋ชจ๋ธ ์ ํ)"""
|
181 |
-
# DeepSeek ์ฌ์ฉ์ด ์ค์ ๋ ๊ฒฝ์ฐ
|
182 |
-
if USE_DEEPSEEK:
|
183 |
-
logger.info(f"DeepSeek ๋ชจ๋ธ ์ด๊ธฐํ: {DEEPSEEK_MODEL}")
|
184 |
-
|
185 |
-
if not DEEPSEEK_API_KEY:
|
186 |
-
logger.error("DeepSeek API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค")
|
187 |
-
raise LLMInitError("DeepSeek API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค")
|
188 |
-
|
189 |
-
# API ์ฐ๊ฒฐ ํ
์คํธ
|
190 |
-
test_result = self._test_deepseek_api()
|
191 |
-
if not test_result["success"]:
|
192 |
-
logger.error(f"DeepSeek API ์ฐ๊ฒฐ ์คํจ: {test_result['message']}")
|
193 |
-
raise LLMInitError(f"DeepSeek API ์ฐ๊ฒฐ ์คํจ: {test_result['message']}")
|
194 |
|
195 |
-
try:
|
196 |
-
# DeepSeek API๋ OpenAI ํธํ API๋ฅผ ์ ๊ณต
|
197 |
-
self.llm = ChatOpenAI(
|
198 |
-
model=DEEPSEEK_MODEL,
|
199 |
-
temperature=0.2,
|
200 |
-
api_key=DEEPSEEK_API_KEY,
|
201 |
-
base_url=DEEPSEEK_ENDPOINT.rstrip("/v1/chat/completions") # ํธํ์ฑ์ ์ํด ๊ฒฝ๋ก ์กฐ์
|
202 |
-
)
|
203 |
-
logger.info("DeepSeek ๋ชจ๋ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
204 |
-
except Exception as e:
|
205 |
-
logger.error(f"DeepSeek ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {e}", exc_info=True)
|
206 |
-
raise LLMInitError(f"DeepSeek ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {str(e)}")
|
207 |
-
|
208 |
-
# OpenAI ์ฌ์ฉ์ด ์ค์ ๋ ๊ฒฝ์ฐ
|
209 |
-
elif USE_OPENAI or IS_HUGGINGFACE:
|
210 |
-
logger.info(f"OpenAI ๋ชจ๋ธ ์ด๊ธฐํ: {LLM_MODEL}")
|
211 |
-
api_key = OPENAI_API_KEY
|
212 |
-
logger.info(f"API ํค ์กด์ฌ ์ฌ๋ถ: {'์์' if api_key else '์์'}")
|
213 |
-
|
214 |
-
if not api_key:
|
215 |
-
logger.error("OpenAI API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค")
|
216 |
-
raise LLMInitError("OpenAI API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค")
|
217 |
-
|
218 |
-
try:
|
219 |
-
self.llm = ChatOpenAI(
|
220 |
-
model_name=LLM_MODEL,
|
221 |
-
temperature=0.2,
|
222 |
-
api_key=api_key,
|
223 |
-
)
|
224 |
-
logger.info("OpenAI ๋ชจ๋ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
225 |
-
except Exception as e:
|
226 |
-
logger.error(f"OpenAI ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {e}", exc_info=True)
|
227 |
-
raise LLMInitError(f"OpenAI ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {str(e)}")
|
228 |
-
else:
|
229 |
-
try:
|
230 |
-
logger.info(f"Ollama ๋ชจ๋ธ ์ด๊ธฐํ: {LLM_MODEL}")
|
231 |
-
|
232 |
-
# Ollama ํธ์คํธ ์ ํจ์ฑ ๊ฒ์ฌ
|
233 |
-
if not OLLAMA_HOST:
|
234 |
-
logger.error("Ollama ํธ์คํธ๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค")
|
235 |
-
raise LLMInitError("Ollama ํธ์คํธ๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค")
|
236 |
-
|
237 |
-
self.llm = ChatOllama(
|
238 |
-
model=LLM_MODEL,
|
239 |
-
temperature=0.2,
|
240 |
-
base_url=OLLAMA_HOST,
|
241 |
-
)
|
242 |
-
logger.info("Ollama ๋ชจ๋ธ ์ด๊ธฐํ ์ฑ๊ณต")
|
243 |
except Exception as e:
|
244 |
-
logger.error(f"
|
245 |
-
raise LLMInitError(f"Ollama ๋ชจ๋ธ ์ด๊ธฐํ ์คํจ: {str(e)}. Ollama๊ฐ ์คํ ์ค์ธ์ง ํ์ธํ์ธ์.")
|
246 |
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
| self.prompt
|
273 |
-
| self.llm
|
274 |
-
| StrOutputParser()
|
275 |
-
)
|
276 |
-
|
277 |
-
logger.info("RAG ์ฒด์ธ ์ค์ ์๋ฃ")
|
278 |
-
except Exception as e:
|
279 |
-
logger.error(f"RAG ์ฒด์ธ ์ค์ ์คํจ: {e}", exc_info=True)
|
280 |
-
raise RAGChainInitError(f"RAG ์ฒด์ธ ์ค์ ์คํจ: {str(e)}")
|
281 |
-
|
282 |
-
def _retrieve(self, query: str) -> str:
|
283 |
"""
|
284 |
-
|
285 |
|
286 |
Args:
|
287 |
-
|
|
|
|
|
|
|
|
|
|
|
288 |
|
289 |
Returns:
|
290 |
-
|
291 |
-
"""
|
292 |
-
if not query or not query.strip():
|
293 |
-
logger.warning("๋น ์ฟผ๋ฆฌ๋ก ๊ฒ์ ์๋")
|
294 |
-
return "๊ฒ์ ์ฟผ๋ฆฌ๊ฐ ๋น์ด์์ต๋๋ค."
|
295 |
-
|
296 |
-
try:
|
297 |
-
# ๋ฒกํฐ ๊ฒ์ ์ํ
|
298 |
-
logger.info(f"๋ฒกํฐ ๊ฒ์ ์ํ: '{query[:50]}{'...' if len(query) > 50 else ''}'")
|
299 |
-
docs = self.vector_store.similarity_search(query, k=TOP_K_RETRIEVAL)
|
300 |
-
|
301 |
-
if not docs:
|
302 |
-
logger.warning("๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค")
|
303 |
-
return "๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."
|
304 |
-
|
305 |
-
# ๋ฆฌ๋ญ์ปค ์ ์ฉ (์ ํ์ )
|
306 |
-
if self.use_reranker and self.reranker and docs:
|
307 |
-
try:
|
308 |
-
logger.info(f"๋ฆฌ๋ญํน ์ํ: {len(docs)}๊ฐ ๋ฌธ์")
|
309 |
-
docs = self.reranker.rerank(query, docs, top_k=TOP_K_RERANK)
|
310 |
-
logger.info(f"๋ฆฌ๋ญํน ์๋ฃ: {len(docs)}๊ฐ ๋ฌธ์ ์ ํ๋จ")
|
311 |
-
except Exception as e:
|
312 |
-
logger.error(f"๋ฆฌ๋ญํน ์คํจ: {e}", exc_info=True)
|
313 |
-
# ๋ฆฌ๋ญํน ์คํจ ์ ์๋ณธ ๋ฌธ์ ์ฌ์ฉ
|
314 |
-
logger.warning("๋ฆฌ๋ญํน ์คํจ๋ก ์๋ณธ ๊ฒ์ ๊ฒฐ๊ณผ ์ฌ์ฉ")
|
315 |
-
|
316 |
-
# ๊ฒ์ ๊ฒฐ๊ณผ ์ปจํ
์คํธ ๊ตฌ์ฑ
|
317 |
-
context_parts = []
|
318 |
-
for i, doc in enumerate(docs, 1):
|
319 |
-
source = doc.metadata.get("source", "์ ์ ์๋ ์ถ์ฒ")
|
320 |
-
page = doc.metadata.get("page", "")
|
321 |
-
source_info = f"{source}"
|
322 |
-
if page:
|
323 |
-
source_info += f" (ํ์ด์ง: {page})"
|
324 |
-
|
325 |
-
context_parts.append(f"[์ฐธ๊ณ ์๋ฃ {i}] - ์ถ์ฒ: {source_info}\n{doc.page_content}\n")
|
326 |
-
|
327 |
-
context = "\n".join(context_parts)
|
328 |
-
logger.info(f"์ปจํ
์คํธ ์์ฑ ์๋ฃ: {len(context_parts)}๊ฐ ๋ฌธ์, {len(context)} ๋ฌธ์")
|
329 |
-
return context
|
330 |
-
|
331 |
-
except Exception as e:
|
332 |
-
logger.error(f"๊ฒ์ ์ค ์ค๋ฅ: {e}", exc_info=True)
|
333 |
-
raise QueryProcessingError(f"๋ฌธ์ ๊ฒ์ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}")
|
334 |
-
|
335 |
-
def run(self, query: str) -> str:
|
336 |
"""
|
337 |
-
|
|
|
|
|
|
|
338 |
|
339 |
-
|
340 |
-
query: ์ฌ์ฉ์ ์ง๋ฌธ
|
341 |
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
|
|
|
1 |
"""
|
2 |
+
์ง์ DeepSeek API ํธ์ถ์ ์ํ ํด๋ผ์ด์ธํธ ๊ตฌํ
|
3 |
"""
|
4 |
import os
|
5 |
+
import time
|
6 |
import logging
|
7 |
+
import requests
|
8 |
+
import json
|
9 |
+
from typing import Dict, Any, Optional, List
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
# ๋ก๊น
์ค์
|
12 |
+
logger = logging.getLogger("DirectDeepSeek")
|
13 |
+
|
14 |
+
class DirectDeepSeekClient:
|
15 |
+
"""
|
16 |
+
DeepSeek API๋ฅผ ์ง์ ํธ์ถํ๋ ํด๋ผ์ด์ธํธ
|
17 |
+
OpenAI ํด๋ผ์ด์ธํธ๋ฅผ ์ฐํํ๊ณ ์ง์ HTTP ์์ฒญ ์ฌ์ฉ
|
18 |
+
"""
|
19 |
+
def __init__(self, api_key: str, model_name: str = "deepseek-chat"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
"""
|
21 |
+
ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
|
22 |
|
23 |
Args:
|
24 |
+
api_key: DeepSeek API ํค
|
25 |
+
model_name: ์ฌ์ฉํ ๋ชจ๋ธ ์ด๋ฆ (๊ธฐ๋ณธ๊ฐ: "deepseek-chat")
|
26 |
"""
|
27 |
+
self.api_key = api_key
|
28 |
+
self.model_name = model_name
|
29 |
+
self.endpoint = os.getenv("DEEPSEEK_ENDPOINT", "https://api.deepseek.com/v1/chat/completions")
|
30 |
+
logger.info(f"DirectDeepSeekClient ์ด๊ธฐํ: ๋ชจ๋ธ={model_name}, ์๋ํฌ์ธํธ={self.endpoint}")
|
31 |
+
|
32 |
+
def generate(self,
|
33 |
+
prompt: str,
|
34 |
+
temperature: float = 0.3,
|
35 |
+
max_tokens: int = 1000,
|
36 |
+
max_retries: int = 3,
|
37 |
+
timeout: int = 60) -> Dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
"""
|
39 |
+
ํ
์คํธ ์์ฑ ์์ฒญ
|
40 |
+
|
41 |
+
Args:
|
42 |
+
prompt: ์
๋ ฅ ํ๋กฌํํธ
|
43 |
+
temperature: ์์ฑ ์จ๋ (0.0 ~ 1.0)
|
44 |
+
max_tokens: ์ต๋ ์์ฑ ํ ํฐ ์
|
45 |
+
max_retries: ์ฌ์๋ ํ์
|
46 |
+
timeout: ์์ฒญ ํ์์์ (์ด)
|
47 |
|
48 |
Returns:
|
49 |
+
์์ฑ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ (success, response, message ๋ฑ)
|
50 |
"""
|
51 |
+
# ๋ฉ์์ง ๊ตฌ์ฑ (๋จ์ผ ์ฌ์ฉ์ ๋ฉ์์ง)
|
52 |
+
messages = [{"role": "user", "content": prompt}]
|
53 |
+
return self.chat(messages, temperature, max_tokens, max_retries, timeout)
|
54 |
+
|
55 |
+
def chat(self,
|
56 |
+
messages: List[Dict[str, str]],
|
57 |
+
temperature: float = 0.3,
|
58 |
+
max_tokens: int = 1000,
|
59 |
+
max_retries: int = 3,
|
60 |
+
timeout: int = 60) -> Dict[str, Any]:
|
61 |
+
"""
|
62 |
+
์ฑํ
API ํธ์ถ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
+
Args:
|
65 |
+
messages: ์ฑํ
๋ฉ์์ง ๋ฆฌ์คํธ (role, content ํค๋ฅผ ๊ฐ์ง ๋์
๋๋ฆฌ ๋ฆฌ์คํธ)
|
66 |
+
temperature: ์์ฑ ์จ๋ (0.0 ~ 1.0)
|
67 |
+
max_tokens: ์ต๋ ์์ฑ ํ ํฐ ์
|
68 |
+
max_retries: ์ฌ์๋ ํ์
|
69 |
+
timeout: ์์ฒญ ํ์์์ (์ด)
|
70 |
|
71 |
+
Returns:
|
72 |
+
์์ฑ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ (success, response, message ๋ฑ)
|
73 |
+
"""
|
74 |
+
# API ์์ฒญ ํค๋ ๋ฐ ๋ฐ์ดํฐ
|
75 |
+
headers = {
|
76 |
+
"Content-Type": "application/json",
|
77 |
+
"Authorization": f"Bearer {self.api_key}"
|
78 |
+
}
|
79 |
+
|
80 |
+
payload = {
|
81 |
+
"model": self.model_name,
|
82 |
+
"messages": messages,
|
83 |
+
"temperature": temperature,
|
84 |
+
"max_tokens": max_tokens
|
85 |
+
}
|
86 |
+
|
87 |
+
# ์ฌ์๋ ๋ก์ง
|
88 |
+
retry_delay = 1.0
|
89 |
+
attempt = 0
|
90 |
+
|
91 |
+
while attempt < max_retries:
|
92 |
+
attempt += 1
|
93 |
+
try:
|
94 |
+
logger.info(f"DeepSeek API ์์ฒญ ์๋ ({attempt}/{max_retries})...")
|
95 |
|
96 |
# API ์์ฒญ ์ ์ก
|
97 |
response = requests.post(
|
98 |
+
self.endpoint,
|
99 |
headers=headers,
|
100 |
+
json=payload,
|
101 |
+
timeout=timeout
|
102 |
)
|
103 |
|
104 |
# ์๋ต ํ์ธ
|
105 |
if response.status_code == 200:
|
106 |
+
result = response.json()
|
107 |
+
|
108 |
+
# ์๋ต ๋ด์ฉ ์ถ์ถ
|
109 |
+
if "choices" in result and len(result["choices"]) > 0:
|
110 |
+
message_content = result["choices"][0].get("message", {}).get("content", "")
|
111 |
+
logger.info(f"DeepSeek API ์๋ต ์ฑ๊ณต (๊ธธ์ด: {len(message_content)})")
|
112 |
+
|
113 |
+
return {
|
114 |
+
"success": True,
|
115 |
+
"response": message_content,
|
116 |
+
"status_code": response.status_code,
|
117 |
+
"raw_response": result
|
118 |
+
}
|
119 |
+
else:
|
120 |
+
logger.warning(f"DeepSeek API ์๋ต์ ์ฑ๊ณตํ์ผ๋ ์์์น ๋ชปํ ์๋ต ํ์: {result}")
|
121 |
+
return {
|
122 |
+
"success": False,
|
123 |
+
"message": "์๋ต์์ ๋ฉ์์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค",
|
124 |
+
"status_code": response.status_code,
|
125 |
+
"raw_response": result
|
126 |
+
}
|
127 |
else:
|
128 |
logger.error(f"DeepSeek API ์ค๋ฅ: ์ํ ์ฝ๋ {response.status_code}")
|
129 |
+
|
130 |
+
# ์ค๋ฅ ๋ฉ์์ง ์ถ์ถ
|
131 |
error_message = ""
|
132 |
try:
|
133 |
error_data = response.json()
|
|
|
135 |
except:
|
136 |
error_message = response.text
|
137 |
|
138 |
+
# ์์ฒญ ํ๋ ์ด๊ณผ์ ๋ ์ค๋ ๋๊ธฐ
|
139 |
+
if response.status_code == 429:
|
140 |
+
retry_delay = min(retry_delay * 3, 15)
|
141 |
+
else:
|
142 |
+
retry_delay = min(retry_delay * 2, 10)
|
143 |
+
|
144 |
+
if attempt < max_retries:
|
145 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
146 |
+
time.sleep(retry_delay)
|
147 |
+
else:
|
148 |
+
# ๋ชจ๋ ์๋ ์คํจ
|
149 |
+
return {
|
150 |
+
"success": False,
|
151 |
+
"message": f"API ์ค๋ฅ: {error_message}",
|
152 |
+
"status_code": response.status_code
|
153 |
+
}
|
154 |
+
|
155 |
+
except requests.exceptions.Timeout:
|
156 |
+
logger.error("DeepSeek API ์์ฒญ ์๊ฐ ์ด๊ณผ")
|
157 |
+
|
158 |
+
if attempt < max_retries:
|
159 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
160 |
+
time.sleep(retry_delay)
|
161 |
+
retry_delay = min(retry_delay * 2, 10)
|
162 |
+
else:
|
163 |
return {
|
164 |
"success": False,
|
165 |
+
"message": "API ์์ฒญ ์๊ฐ ์ด๊ณผ",
|
166 |
+
"status_code": None
|
167 |
+
}
|
168 |
+
|
169 |
+
except requests.exceptions.ConnectionError:
|
170 |
+
logger.error("DeepSeek API ์ฐ๊ฒฐ ์คํจ")
|
171 |
+
|
172 |
+
if attempt < max_retries:
|
173 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
174 |
+
time.sleep(retry_delay)
|
175 |
+
retry_delay = min(retry_delay * 2, 10)
|
176 |
+
else:
|
177 |
+
return {
|
178 |
+
"success": False,
|
179 |
+
"message": "API ์๋ฒ ์ฐ๊ฒฐ ์คํจ",
|
180 |
+
"status_code": None
|
181 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
except Exception as e:
|
184 |
+
logger.error(f"DeepSeek API ์์ฒญ ์ค ์์์น ๋ชปํ ์ค๋ฅ: {e}")
|
|
|
185 |
|
186 |
+
if attempt < max_retries:
|
187 |
+
logger.info(f"{retry_delay}์ด ํ ์ฌ์๋...")
|
188 |
+
time.sleep(retry_delay)
|
189 |
+
retry_delay = min(retry_delay * 2, 10)
|
190 |
+
else:
|
191 |
+
return {
|
192 |
+
"success": False,
|
193 |
+
"message": f"์์์น ๋ชปํ ์ค๋ฅ: {str(e)}",
|
194 |
+
"status_code": None
|
195 |
+
}
|
196 |
+
|
197 |
+
# ๋ชจ๋ ์๋ ์คํจ
|
198 |
+
return {
|
199 |
+
"success": False,
|
200 |
+
"message": "์ต๋ ์ฌ์๋ ํ์ ์ด๊ณผ",
|
201 |
+
"status_code": None
|
202 |
+
}
|
203 |
+
|
204 |
+
def system_prompt_chat(self,
|
205 |
+
system_prompt: str,
|
206 |
+
user_prompt: str,
|
207 |
+
temperature: float = 0.3,
|
208 |
+
max_tokens: int = 1000,
|
209 |
+
max_retries: int = 3,
|
210 |
+
timeout: int = 60) -> Dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
"""
|
212 |
+
์์คํ
ํ๋กฌํํธ์ ์ฌ์ฉ์ ํ๋กฌํํธ๋ฅผ ์ด์ฉํ ์ฑํ
API ํธ์ถ
|
213 |
|
214 |
Args:
|
215 |
+
system_prompt: ์์คํ
ํ๋กฌํํธ
|
216 |
+
user_prompt: ์ฌ์ฉ์ ํ๋กฌํํธ
|
217 |
+
temperature: ์์ฑ ์จ๋ (0.0 ~ 1.0)
|
218 |
+
max_tokens: ์ต๋ ์์ฑ ํ ํฐ ์
|
219 |
+
max_retries: ์ฌ์๋ ํ์
|
220 |
+
timeout: ์์ฒญ ํ์์์ (์ด)
|
221 |
|
222 |
Returns:
|
223 |
+
์์ฑ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
"""
|
225 |
+
messages = [
|
226 |
+
{"role": "system", "content": system_prompt},
|
227 |
+
{"role": "user", "content": user_prompt}
|
228 |
+
]
|
229 |
|
230 |
+
return self.chat(messages, temperature, max_tokens, max_retries, timeout)
|
|
|
231 |
|
232 |
+
|
233 |
+
# ๋จ๋
์คํ์ ์ํ ํ
์คํธ ์ฝ๋
|
234 |
+
if __name__ == "__main__":
|
235 |
+
# ๋ก๊น
์ค์
|
236 |
+
logging.basicConfig(level=logging.INFO)
|
237 |
+
|
238 |
+
# API ํค ํ์ธ
|
239 |
+
api_key = os.environ.get("DEEPSEEK_API_KEY")
|
240 |
+
if not api_key:
|
241 |
+
print("ํ๊ฒฝ ๋ณ์ DEEPSEEK_API_KEY๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.")
|
242 |
+
exit(1)
|
243 |
+
|
244 |
+
# ํด๋ผ์ด์ธํธ ์์ฑ
|
245 |
+
client = DirectDeepSeekClient(api_key)
|
246 |
+
|
247 |
+
# ๊ฐ๋จํ ํ
์คํธ
|
248 |
+
response = client.generate("Hello, what can you do?")
|
249 |
+
|
250 |
+
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
|
251 |
+
if response["success"]:
|
252 |
+
print("์๋ต ์ฑ๊ณต!")
|
253 |
+
print(response["response"])
|
254 |
+
else:
|
255 |
+
print(f"์๋ต ์คํจ: {response['message']}")
|
simple_rag_chain.py
CHANGED
@@ -1,66 +1,123 @@
|
|
1 |
"""
|
2 |
-
๊ฐ๋จํ RAG ์ฒด์ธ ๊ตฌํ (๋๋ฒ๊น
์ฉ)
|
3 |
"""
|
4 |
import os
|
5 |
-
|
6 |
-
|
7 |
-
from
|
8 |
-
from langchain_core.runnables import RunnablePassthrough
|
9 |
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
class SimpleRAGChain:
|
12 |
-
def __init__(self, vector_store):
|
13 |
"""๊ฐ๋จํ RAG ์ฒด์ธ ์ด๊ธฐํ"""
|
14 |
-
|
15 |
self.vector_store = vector_store
|
16 |
|
17 |
-
#
|
18 |
-
|
19 |
-
|
|
|
20 |
|
21 |
-
#
|
22 |
-
self.
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
|
29 |
-
template = """
|
30 |
-
๋ค์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ฌธ์ ์ ํํ๊ฒ ๋ต๋ณํด์ฃผ์ธ์.
|
31 |
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
-
|
38 |
-
"""
|
39 |
|
40 |
-
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
| self.prompt
|
46 |
-
| self.llm
|
47 |
-
| StrOutputParser()
|
48 |
-
)
|
49 |
-
print("๊ฐ๋จํ RAG ์ฒด์ธ ์ด๊ธฐํ ์๋ฃ")
|
50 |
|
51 |
-
|
52 |
-
"""๋ฌธ์ ๊ฒ์"""
|
53 |
-
try:
|
54 |
-
docs = self.vector_store.similarity_search(query, k=3)
|
55 |
-
return "\n\n".join(doc.page_content for doc in docs)
|
56 |
except Exception as e:
|
57 |
-
|
58 |
return "๋ฌธ์ ๊ฒ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."
|
59 |
|
60 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
"""์ฟผ๋ฆฌ ์ฒ๋ฆฌ"""
|
62 |
try:
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
except Exception as e:
|
65 |
-
|
66 |
return f"์ค๋ฅ ๋ฐ์: {str(e)}"
|
|
|
1 |
"""
|
2 |
+
๊ฐ๋จํ RAG ์ฒด์ธ ๊ตฌํ (๋๋ฒ๊น
์ฉ) - ์ง์ DeepSeek API ํธ์ถ ๋ฐฉ์
|
3 |
"""
|
4 |
import os
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
from typing import Dict, Any, List
|
|
|
8 |
|
9 |
+
# ์ง์ DeepSeek ํด๋ผ์ด์ธํธ ์ฌ์ฉ
|
10 |
+
from direct_deepseek import DirectDeepSeekClient
|
11 |
+
|
12 |
+
# ๋ก๊น
์ค์
|
13 |
+
logger = logging.getLogger("SimpleRAGChain")
|
14 |
|
15 |
class SimpleRAGChain:
|
16 |
+
def __init__(self, vector_store, api_key=None, model="deepseek-chat", endpoint=None):
|
17 |
"""๊ฐ๋จํ RAG ์ฒด์ธ ์ด๊ธฐํ"""
|
18 |
+
logger.info("๊ฐ๋จํ RAG ์ฒด์ธ ์ด๊ธฐํ ์ค...")
|
19 |
self.vector_store = vector_store
|
20 |
|
21 |
+
# DeepSeek API ํค ํ์ธ
|
22 |
+
self.api_key = api_key or os.environ.get("DEEPSEEK_API_KEY", "")
|
23 |
+
self.model = model or os.environ.get("DEEPSEEK_MODEL", "deepseek-chat")
|
24 |
+
logger.info(f"API ํค ์ค์ ๋จ: {bool(self.api_key)}")
|
25 |
|
26 |
+
# DeepSeek ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
|
27 |
+
if self.api_key:
|
28 |
+
try:
|
29 |
+
self.client = DirectDeepSeekClient(
|
30 |
+
api_key=self.api_key,
|
31 |
+
model_name=self.model
|
32 |
+
)
|
33 |
+
logger.info(f"DeepSeek ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์ฑ๊ณต: {self.model}")
|
34 |
+
except Exception as e:
|
35 |
+
logger.error(f"DeepSeek ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์คํจ: {e}")
|
36 |
+
self.client = None
|
37 |
+
else:
|
38 |
+
logger.warning("API ํค๊ฐ ์ค์ ๋์ง ์์ ํด๋ผ์ด์ธํธ๋ฅผ ์ด๊ธฐํํ ์ ์์ต๋๋ค.")
|
39 |
+
self.client = None
|
40 |
|
41 |
+
logger.info("๊ฐ๋จํ RAG ์ฒด์ธ ์ด๊ธฐํ ์๋ฃ")
|
|
|
|
|
42 |
|
43 |
+
def _retrieve(self, query: str) -> str:
|
44 |
+
"""๋ฌธ์ ๊ฒ์ ๋ฐ ์ปจํ
์คํธ ๊ตฌ์ฑ"""
|
45 |
+
try:
|
46 |
+
docs = self.vector_store.similarity_search(query, k=3)
|
47 |
+
if not docs:
|
48 |
+
return "๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."
|
49 |
|
50 |
+
# ๊ฒ์ ๊ฒฐ๊ณผ ์ปจํ
์คํธ ๊ตฌ์ฑ
|
51 |
+
context_parts = []
|
52 |
+
for i, doc in enumerate(docs, 1):
|
53 |
+
source = doc.metadata.get("source", "์ ์ ์๋ ์ถ์ฒ")
|
54 |
+
page = doc.metadata.get("page", "")
|
55 |
+
source_info = f"{source}"
|
56 |
+
if page:
|
57 |
+
source_info += f" (ํ์ด์ง: {page})"
|
58 |
|
59 |
+
context_parts.append(f"[์ฐธ๊ณ ์๋ฃ {i}] - ์ถ์ฒ: {source_info}\n{doc.page_content}\n")
|
|
|
60 |
|
61 |
+
context = "\n".join(context_parts)
|
62 |
|
63 |
+
# ๊ธธ์ด ์ ํ
|
64 |
+
if len(context) > 6000:
|
65 |
+
context = context[:2500] + "\n...(์ค๋ต)...\n" + context[-2500:]
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
+
return context
|
|
|
|
|
|
|
|
|
68 |
except Exception as e:
|
69 |
+
logger.error(f"๊ฒ์ ์ค ์ค๋ฅ: {e}")
|
70 |
return "๋ฌธ์ ๊ฒ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."
|
71 |
|
72 |
+
def _generate_prompt(self, query: str, context: str) -> List[Dict[str, str]]:
|
73 |
+
"""DeepSeek API์ฉ ํ๋กฌํํธ ์์ฑ"""
|
74 |
+
# ์์คํ
ํ๋กฌํํธ
|
75 |
+
system_prompt = """๋ค์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ฌธ์ ์ ํํ๊ฒ ๋ต๋ณํด์ฃผ์ธ์.
|
76 |
+
์ฐธ๊ณ ์ ๋ณด์์ ๋ต์ ์ฐพ์ ์ ์๋ ๊ฒฝ์ฐ "์ ๊ณต๋ ๋ฌธ์์์ ํด๋น ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."๋ผ๊ณ ๋ต๋ณํ์ธ์.
|
77 |
+
์ ๋ณด ์ถ์ฒ๋ฅผ ํฌํจํด์ ๋๋ตํ์ธ์."""
|
78 |
+
|
79 |
+
# ์ฌ์ฉ์ ํ๋กฌํํธ
|
80 |
+
user_prompt = f"""์ง๋ฌธ: {query}
|
81 |
+
|
82 |
+
์ฐธ๊ณ ์ ๋ณด:
|
83 |
+
{context}"""
|
84 |
+
|
85 |
+
# DeepSeek API ํ๋กฌํํธ ํฌ๋งท
|
86 |
+
messages = [
|
87 |
+
{"role": "system", "content": system_prompt},
|
88 |
+
{"role": "user", "content": user_prompt}
|
89 |
+
]
|
90 |
+
|
91 |
+
return messages
|
92 |
+
|
93 |
+
def run(self, query: str) -> str:
|
94 |
"""์ฟผ๋ฆฌ ์ฒ๋ฆฌ"""
|
95 |
try:
|
96 |
+
logger.info(f"SimpleRAGChain ์คํ: {query[:50]}...")
|
97 |
+
|
98 |
+
# ๋ฌธ์ ๊ฒ์
|
99 |
+
context = self._retrieve(query)
|
100 |
+
|
101 |
+
# ํด๋ผ์ด์ธํธ๊ฐ ์ด๊ธฐํ๋์ง ์์ ๊ฒฝ์ฐ
|
102 |
+
if self.client is None:
|
103 |
+
logger.warning("DeepSeek ํด๋ผ์ด์ธํธ๊ฐ ์ด๊ธฐํ๋์ง ์์. ๊ฒ์ ๊ฒฐ๊ณผ๋ง ๋ฐํ.")
|
104 |
+
return f"API ์ฐ๊ฒฐ์ด ์ค์ ๋์ง ์์์ต๋๋ค. ๊ฒ์ ๊ฒฐ๊ณผ:\n\n{context}"
|
105 |
+
|
106 |
+
# ํ๋กฌํํธ ์์ฑ
|
107 |
+
messages = self._generate_prompt(query, context)
|
108 |
+
|
109 |
+
# API ํธ์ถ
|
110 |
+
start_time = time.time()
|
111 |
+
response = self.client.chat(messages)
|
112 |
+
logger.info(f"API ์๋ต ์๊ฐ: {time.time() - start_time:.2f}์ด")
|
113 |
+
|
114 |
+
if response["success"]:
|
115 |
+
logger.info("์๋ต ์์ฑ ์ฑ๊ณต")
|
116 |
+
return response["response"]
|
117 |
+
else:
|
118 |
+
logger.error(f"์๋ต ์์ฑ ์คํจ: {response['message']}")
|
119 |
+
return f"์๋ต ์์ฑ ์คํจ: {response['message']}\n\n๊ฒ์ ๊ฒฐ๊ณผ:\n{context}"
|
120 |
+
|
121 |
except Exception as e:
|
122 |
+
logger.error(f"์คํ ์ค ์ค๋ฅ: {e}")
|
123 |
return f"์ค๋ฅ ๋ฐ์: {str(e)}"
|
test_deepseek.py
DELETED
@@ -1,123 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
DeepSeek API ์ฐ๊ฒฐ ํ
์คํธ ์คํฌ๋ฆฝํธ
|
3 |
-
์ฌ์ฉ๋ฒ: python test_deepseek.py
|
4 |
-
"""
|
5 |
-
import os
|
6 |
-
import sys
|
7 |
-
import requests
|
8 |
-
import json
|
9 |
-
import time
|
10 |
-
from dotenv import load_dotenv
|
11 |
-
|
12 |
-
|
13 |
-
def main():
|
14 |
-
# .env ํ์ผ ๋ก๋ ์๋
|
15 |
-
load_dotenv()
|
16 |
-
|
17 |
-
# API ํค ํ์ธ
|
18 |
-
api_key = os.getenv("DEEPSEEK_API_KEY", "")
|
19 |
-
if not api_key:
|
20 |
-
print("์ค๋ฅ: DEEPSEEK_API_KEY๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.")
|
21 |
-
print("๋ค์ ๋ฐฉ๋ฒ ์ค ํ๋๋ก API ํค๋ฅผ ์ค์ ํด์ฃผ์ธ์:")
|
22 |
-
print("1. .env ํ์ผ์ DEEPSEEK_API_KEY=your_api_key ์ถ๊ฐ")
|
23 |
-
print("2. ํ๊ฒฝ ๋ณ์๋ก ์ค์ (export DEEPSEEK_API_KEY=your_api_key)")
|
24 |
-
return False
|
25 |
-
|
26 |
-
# ์๋ํฌ์ธํธ ๋ฐ ๋ชจ๋ธ ์ค์
|
27 |
-
endpoint = os.getenv("DEEPSEEK_ENDPOINT", "https://api.deepseek.com/v1/chat/completions")
|
28 |
-
model = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
|
29 |
-
|
30 |
-
print(f"DeepSeek API ํ
์คํธ๋ฅผ ์์ํฉ๋๋ค...")
|
31 |
-
print(f"์๋ํฌ์ธํธ: {endpoint}")
|
32 |
-
print(f"๋ชจ๋ธ: {model}")
|
33 |
-
print(f"API ํค: {api_key[:5]}...{api_key[-4:] if len(api_key) > 8 else '****'}")
|
34 |
-
|
35 |
-
# ๊ฐ๋จํ ํ๋กฌํํธ
|
36 |
-
test_prompt = "Hello, please respond with a short greeting in Korean."
|
37 |
-
|
38 |
-
# API ์์ฒญ ํค๋ ๋ฐ ๋ฐ์ดํฐ
|
39 |
-
headers = {
|
40 |
-
"Content-Type": "application/json",
|
41 |
-
"Authorization": f"Bearer {api_key}"
|
42 |
-
}
|
43 |
-
|
44 |
-
payload = {
|
45 |
-
"model": model,
|
46 |
-
"messages": [{"role": "user", "content": test_prompt}],
|
47 |
-
"temperature": 0.7,
|
48 |
-
"max_tokens": 100
|
49 |
-
}
|
50 |
-
|
51 |
-
print("\n์์ฒญ ๋ฐ์ดํฐ:")
|
52 |
-
print(json.dumps(payload, indent=2, ensure_ascii=False))
|
53 |
-
|
54 |
-
# ํ์ด๋จธ ์์
|
55 |
-
start_time = time.time()
|
56 |
-
|
57 |
-
try:
|
58 |
-
print("\nAPI ์์ฒญ ์ ์ก ์ค...")
|
59 |
-
response = requests.post(
|
60 |
-
endpoint,
|
61 |
-
headers=headers,
|
62 |
-
data=json.dumps(payload),
|
63 |
-
timeout=30 # 30์ด ํ์์์
|
64 |
-
)
|
65 |
-
|
66 |
-
elapsed_time = time.time() - start_time
|
67 |
-
|
68 |
-
print(f"์๋ต ์์ (์์ ์๊ฐ: {elapsed_time:.2f}์ด)")
|
69 |
-
print(f"HTTP ์ํ ์ฝ๋: {response.status_code}")
|
70 |
-
|
71 |
-
# ์๋ต ํ์ธ
|
72 |
-
if response.status_code == 200:
|
73 |
-
print("\n์ฑ๊ณต! DeepSeek API๊ฐ ์ ์ ์๋ํฉ๋๋ค.")
|
74 |
-
|
75 |
-
try:
|
76 |
-
response_data = response.json()
|
77 |
-
print("\n์๋ต ๋ฐ์ดํฐ:")
|
78 |
-
print(json.dumps(response_data, indent=2, ensure_ascii=False))
|
79 |
-
|
80 |
-
# ์๋ต ํ
์คํธ ์ถ์ถ
|
81 |
-
if "choices" in response_data and len(response_data["choices"]) > 0:
|
82 |
-
message = response_data["choices"][0].get("message", {})
|
83 |
-
content = message.get("content", "")
|
84 |
-
print("\n์๋ต ๋ด์ฉ:")
|
85 |
-
print("-" * 40)
|
86 |
-
print(content)
|
87 |
-
print("-" * 40)
|
88 |
-
else:
|
89 |
-
print("\n๊ฒฝ๊ณ : ์๋ต์์ 'choices' ํ๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.")
|
90 |
-
|
91 |
-
return True
|
92 |
-
except json.JSONDecodeError:
|
93 |
-
print("\n๊ฒฝ๊ณ : ์๋ต์ JSON์ผ๋ก ํ์ฑํ ์ ์์ต๋๋ค.")
|
94 |
-
print("์๋ณธ ์๋ต ๋ด์ฉ:")
|
95 |
-
print(response.text)
|
96 |
-
return False
|
97 |
-
else:
|
98 |
-
print("\n์ค๋ฅ: DeepSeek API ํธ์ถ ์คํจ")
|
99 |
-
|
100 |
-
try:
|
101 |
-
error_data = response.json()
|
102 |
-
print("\n์ค๋ฅ ์ ๋ณด:")
|
103 |
-
print(json.dumps(error_data, indent=2, ensure_ascii=False))
|
104 |
-
except:
|
105 |
-
print("\n์๋ณธ ์ค๋ฅ ์๋ต:")
|
106 |
-
print(response.text)
|
107 |
-
|
108 |
-
return False
|
109 |
-
|
110 |
-
except requests.exceptions.Timeout:
|
111 |
-
print("\n์ค๋ฅ: API ์์ฒญ ์๊ฐ ์ด๊ณผ")
|
112 |
-
return False
|
113 |
-
except requests.exceptions.ConnectionError:
|
114 |
-
print("\n์ค๋ฅ: API ์๋ฒ ์ฐ๊ฒฐ ์คํจ")
|
115 |
-
return False
|
116 |
-
except Exception as e:
|
117 |
-
print(f"\n์ค๋ฅ: ์์์น ๋ชปํ ์ค๋ฅ ๋ฐ์ - {str(e)}")
|
118 |
-
return False
|
119 |
-
|
120 |
-
|
121 |
-
if __name__ == "__main__":
|
122 |
-
success = main()
|
123 |
-
sys.exit(0 if success else 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|