RAG, MMR, 임베딩 심화

This commit is contained in:
2026-05-29 18:35:36 +09:00
parent b23b5e9b5f
commit d1db36883d
44 changed files with 3579 additions and 70 deletions
+2 -4
View File
@@ -2,17 +2,15 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/ollama/.venv" />
</content>
<orderEntry type="jdk" jdkName="~/Source/.venv" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.12 (Source)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PackageRequirementsSettings" />
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="ReSTService" />
<component name="TestRunnerService" />
</module>
+10
View File
@@ -0,0 +1,10 @@
# 디폴트 무시된 파일
/shelf/
/workspace.xml
# 에디터 기반 HTTP 클라이언트 요청
/httpRequests/
# 쿼리 파일을 포함한 무시된 디폴트 폴더
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+6
View File
@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12 (ollama)" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/ollama.iml" filepath="$PROJECT_DIR$/.idea/ollama.iml" />
</modules>
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (ollama)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+13 -3
View File
@@ -9,11 +9,13 @@ from langchain_core.output_parsers import (
from pydantic import BaseModel, Field
from typing import Literal
from dotenv import load_dotenv
from langchain_core.prompts import MessagesPlaceholder # 추가 필수
import os
import gradio as gr
qwen_llm = ChatOllama(model="qwen3.5:4b")
exaone_llm = ChatOllama(model="exaone3.5:2.4b")
gemma_llm = ChatOllama(model="gemma4:e2b")
system_prompt = """\
당신은 20년 경력의 전문 셰프이자 요리 연구가입니다.
@@ -26,15 +28,23 @@ system_prompt = """\
template = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
MessagesPlaceholder(variable_name="history"), # 대화 이력이 삽입될 위치
("human", "{question}"),
]
)
chain = template | exaone_llm | StrOutputParser()
chain = template | gemma_llm | StrOutputParser()
def chat(question, history):
response = chain.invoke({"question": question})
chat_history = []
# history 를 이용한 직접 관리
for msg in history:
if msg["role"] == "user":
chat_history.append(["human", msg["content"]])
elif msg["role"] == "assistant":
chat_history.append(["ai", msg["content"]])
response = chain.invoke({"history": chat_history, "question": question})
return response
+50 -33
View File
@@ -1,55 +1,72 @@
from langchain_ollama import ChatOllama
from langchain_ibm import ChatWatsonx
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import (
StrOutputParser,
JsonOutputParser,
PydanticOutputParser,
)
from langchain_core.chat_history import InMemoryChatMessageHistory, BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from pydantic import BaseModel, Field
from typing import Literal
from dotenv import load_dotenv
import os
import gradio as gr
import uuid
qwen_llm = ChatOllama(model="qwen3.5:4b")
exaone_llm = ChatOllama(model="exaone3.5:2.4b")\
gemma_llm = ChatOllama(model="gemma4:e2b")
# qwen_llm = ChatOllama(model="qwen3.5:4b")
# exaone_llm = ChatOllama(model="exaone3.5:2.4b")
system_prompt = """\
당신은 20년 경력의 전문 셰프이자 요리 연구가입니다.
사용자의 요리 질문에 대해
재료, 조리방법, 실패 방지 팁, 대체 재료를
포함하여 답변하세요.
항상 한국어로 답변하세요
"""
store = {}
template = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
("human", "{question}"),
]
)
def get_sesstion_history(session_id) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
chain = template | gemma_llm | StrOutputParser()
def create_chain():
def chat(question, history):
chat_history = []
# history 를 이용한 직접 관리
for msg in history:
if msg["role"] == "user":
chat_history.append(["human", msg["content"]])
elif msg["role"] == "assistant":
chat_history.append(["ai", msg["content"]])
gemma_llm = ChatOllama(model="gemma4:e2b")
response = chain.invoke({"history": chat_history, "question": question})
return response
system_prompt = """\
당신은 20년 경력의 전문 셰프이자 요리 연구가입니다.
사용자의 요리 질문에 대해
재료, 조리방법, 실패 방지 팁, 대체 재료를
포함하여 답변하세요.
항상 한국어로 답변하세요
"""
template = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
MessagesPlaceholder(variable_name="history"), # 대화 이력이 삽입될 위치
("human", "{question}"),
]
)
chatbot = gr.ChatInterface(
fn=chat,
title="🍝 요리 전문가",
description="요리경력 20년의 전문가입니다. 요리에 대한 궁금증을 해결하세요.",
)
chain = template | gemma_llm | StrOutputParser()
return RunnableWithMessageHistory(chain, get_sesstion_history, input_messages_key="question", history_messages_key="history")
chain = create_chain()
def chat(question, history, session_id):
full_response = ""
for chunk in chain.stream({"question": question}, config={"configurable": {"session_id":session_id}}):
full_response+=chunk
yield full_response
with gr.Blocks() as chatbot:
# 세션ID uuid 사용
# gr.State() : 사용자별 데이터를 서버 메모리에 저장하는 컴포넌트
session_state = gr.State(str(uuid.uuid4()))
gr.ChatInterface(
fn=chat,
additional_inputs=[session_state],
title="🍝 요리 전문가",
description="요리경력 20년의 전문가입니다. 요리에 대한 궁금증을 해결하세요.",
)
chatbot.launch()
+557 -15
View File
@@ -23,34 +23,40 @@
"id": "ceaff2f6",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-27T08:50:09.993923078Z",
"start_time": "2026-05-27T08:50:09.983490192Z"
"end_time": "2026-05-28T01:43:20.987484992Z",
"start_time": "2026-05-28T01:43:20.978670755Z"
}
},
"source": [
"import selectors\n",
"\n",
"import langchain_core\n",
"#라이브러리 로드\n",
"\n",
"from langchain_ollama import ChatOllama\n",
"from langchain_ibm import ChatWatsonx\n",
"from langchain_core.prompts import PromptTemplate, ChatPromptTemplate\n",
"from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder\n",
"from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser\n",
"from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda\n",
"from langchain_core.messages import HumanMessage, SystemMessage, AIMessage\n",
"from langchain_core.chat_history import InMemoryChatMessageHistory, BaseChatMessageHistory\n",
"from langchain_core.runnables.history import RunnableWithMessageHistory\n",
"from prompt_toolkit.history import InMemoryHistory\n",
"from pydantic import BaseModel, Field\n",
"from typing import Literal\n",
"from dotenv import load_dotenv\n",
"import os"
],
"outputs": [],
"execution_count": 56
"execution_count": 39
},
{
"cell_type": "code",
"id": "2e83941f",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-27T08:50:11.888281707Z",
"start_time": "2026-05-27T08:50:11.876005807Z"
"end_time": "2026-05-28T01:01:25.315262434Z",
"start_time": "2026-05-28T01:01:25.303683341Z"
}
},
"source": [
@@ -59,18 +65,19 @@
"\n",
"apikey = os.getenv(\"WATSONX_API_KEY\")\n",
"project_id = os.getenv(\"WATSONX_PROJECT_ID\")\n",
"watsonx_ai_url = os.getenv(\"WATSONX_URL\")\n"
"watsonx_ai_url = os.getenv(\"WATSONX_URL\")\n",
"hf_token = os.getenv(\"HF_TOKEN\")\n"
],
"outputs": [],
"execution_count": 57
"execution_count": 26
},
{
"cell_type": "code",
"id": "7c0a9354",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-27T08:50:16.189743767Z",
"start_time": "2026-05-27T08:50:13.584144045Z"
"end_time": "2026-05-28T01:01:29.753326236Z",
"start_time": "2026-05-28T01:01:26.671654629Z"
}
},
"source": [
@@ -87,7 +94,7 @@
"gemma_llm = ChatOllama(model=\"gemma4:e2b\")"
],
"outputs": [],
"execution_count": 58
"execution_count": 27
},
{
"cell_type": "code",
@@ -1965,11 +1972,546 @@
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
" - InMemoryChatMessageHistory\n",
" - 메모리에 대화 저장 후 대화 기록 관리\n"
],
"id": "84cc5b7e61f2a23d"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-28T00:54:22.590632509Z",
"start_time": "2026-05-28T00:52:55.700793745Z"
}
},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": "",
"id": "fb66178274d6381"
"source": [
"history = InMemoryChatMessageHistory()\n",
"\n",
"\n",
"def chat(user_input):\n",
" history.add_user_message(user_input)\n",
" response = gemma_llm.invoke(history.messages) # 전체 대화 이력 같이 보냄\n",
" history.add_ai_message(response.content)\n",
" return response.content\n",
"\n",
"print(chat(\"파이썬이란?\"))\n",
"print(chat(\"방금 말한 내용의 장점 3가지는?\"))\n",
"print(chat(\"그 중 첫번째 장점에 대한 예시 코드 작성해줘\"))\n",
"print(chat(\"두 번째 장점에 대한 예시 코드 작성해줘\"))\n",
"\n",
"for m in history.messages:\n",
" print(f\"{m.content[:40]}...\")"
],
"id": "fb66178274d6381",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"파이썬(Python)은 오늘날 가장 인기 있고 널리 사용되는 프로그래밍 언어 중 하나입니다.\n",
"\n",
"쉽게 말해, **사람이 읽고 쓰기 쉬운 문법**을 사용하여 컴퓨터에게 원하는 작업을 지시할 수 있게 해주는 **고급(High-level) 프로그래밍 언어**입니다.\n",
"\n",
"파이썬이 왜 그렇게 유명하고 많은 분야에서 사용되는지, 그리고 어떤 특징을 가지고 있는지 자세히 설명해 드리겠습니다.\n",
"\n",
"---\n",
"\n",
"## 1. 파이썬의 핵심 특징\n",
"\n",
"### 1. 쉬운 문법 (Readability)\n",
"파이썬의 가장 큰 장점은 문법이 매우 간결하고 영어와 유사해서 초보자도 비교적 쉽게 배울 수 있다는 점입니다. 복잡한 명령어를 적는 대신, 자연어처럼 코드를 작성할 수 있어 코드의 이해와 유지보수가 매우 쉽습니다.\n",
"\n",
"### 2. 인터프리터 방식 (Interpreted Language)\n",
"파이썬은 코드를 한 줄씩 읽어서 즉시 실행하는 **인터프리터(Interpreter)** 방식으로 작동합니다. 이는 개발자가 코드를 컴파일(Compile)하는 복잡한 과정을 거치지 않고도 빠르게 결과를 확인하고 테스트할 수 있게 해줍니다.\n",
"\n",
"### 3. 방대한 라이브러리 및 생태계 (Vast Ecosystem)\n",
"파이썬의 가장 강력한 힘은 방대하고 활발한 **라이브러리(Library)**와 **프레임워크(Framework)** 생태계에 있습니다. 이미 수많은 개발자들이 만들어 놓은 모듈(라이브러리)을 가져와서 복잡한 기능을 아주 쉽게 구현할 수 있습니다. (예: NumPy, Pandas, Django, TensorFlow 등)\n",
"\n",
"### 4. 다목적성 (Versatility)\n",
"파이썬은 특정 분야에 국한되지 않고, 데이터 분석, 웹 개발, 인공지능, 자동화 등 거의 모든 종류의 프로그래밍 작업에 사용될 수 있습니다.\n",
"\n",
"---\n",
"\n",
"## 2. 파이썬은 어디에 사용되나요? (주요 활용 분야)\n",
"\n",
"파이썬의 높은 범용성 덕분에 현재 가장 활발하게 사용되는 분야는 다음과 같습니다.\n",
"\n",
"### 1. 데이터 과학 및 분석 (Data Science & Analytics)\n",
"* **활용:** 대량의 데이터를 처리, 분석하고 패턴을 찾는 데 압도적으로 많이 사용됩니다.\n",
"* **주요 라이브러리:** **Pandas** (데이터 조작), **NumPy** (수치 계산), **Matplotlib** (시각화).\n",
"\n",
"### 2. 인공지능 (AI) 및 머신러닝 (Machine Learning)\n",
"* **활용:** 인공지능 모델을 개발하고 훈련하는 데 핵심적인 언어입니다.\n",
"* **주요 라이브러리:** **TensorFlow**, **PyTorch**, **Scikit-learn**.\n",
"\n",
"### 3. 웹 개발 (Web Development)\n",
"* **활용:** 웹사이트의 백엔드(서버 로직) 개발에 사용됩니다.\n",
"* **주요 프레임워크:** **Django**, **Flask**.\n",
"\n",
"### 4. 자동화 및 스크립팅 (Automation & Scripting)\n",
"* **활용:** 반복적이거나 지루한 시스템 관리 작업을 자동화하는 데 매우 효율적입니다.\n",
"* **예시:** 파일 정리, 이메일 자동 발송, 웹 크롤링(정보 수집).\n",
"\n",
"### 5. 소프트웨어 테스트 및 QA\n",
"* **활용:** 소프트웨어의 오류를 찾고 테스트하는 자동화 스크립트를 작성하는 데 사용됩니다.\n",
"\n",
"---\n",
"\n",
"## 💡 요약 정리\n",
"\n",
"| 구분 | 내용 | 비고 |\n",
"| :--- | :--- | :--- |\n",
"| **정의** | 사람이 읽기 쉬운 문법을 사용하는 고급 프로그래밍 언어 | |\n",
"| **특징** | 문법이 간결하고, 라이브러리가 풍부하며, 다목적성이 높음 | |\n",
"| **주요 강점** | 배우기 쉽고, 코드를 빠르게 작성할 수 있음 | |\n",
"| **주요 분야** | 데이터 과학, AI/머신러닝, 웹 개발, 시스템 자동화 | |\n",
"\n",
"**결론적으로,** 만약 프로그래밍을 처음 시작하거나, 데이터 분석, AI 등 미래 기술 분야에 관심이 있다면 **파이썬은 가장 먼저 배워야 할 강력한 언어**라고 할 수 있습니다.\n",
"방금 설명드린 내용을 바탕으로 파이썬의 가장 핵심적이고 강력한 장점 3가지를 정리해 드리겠습니다.\n",
"\n",
"---\n",
"\n",
"### 🥇 장점 1: 배우기 매우 쉽고 직관적이다 (쉬운 문법)\n",
"\n",
"파이썬의 가장 큰 장점은 **코드를 읽고 쓰는 것이 매우 쉽다**는 점입니다.\n",
"\n",
"* **설명:** 다른 언어들에 비해 문법이 간결하고 영어와 유사하여, 프로그래밍을 처음 시작하는 사람도 비교적 적은 시간 투자로 코드를 이해하고 작성할 수 있습니다.\n",
"* **효과:** 코딩에 대한 진입 장벽이 낮아 초보자가 빠르게 성취감을 느끼고 실제 문제 해결에 집중할 수 있습니다.\n",
"\n",
"### 🥈 장점 2: 방대하고 강력한 생태계 (풍부한 라이브러리)\n",
"\n",
"파이썬은 혼자 모든 것을 다 할 필요가 없습니다. 이미 수많은 사람들이 만들어 놓은 강력한 도구들(라이브러리, 프레임워크)이 존재합니다.\n",
"\n",
"* **설명:** 데이터 분석, 인공지능, 웹 개발 등 특정 분야에서 필요로 하는 수많은 기능을 이미 잘 만들어진 모듈(라이브러리) 형태로 제공합니다.\n",
"* **효과:** 개발자가 기본적인 기능 구현에 시간을 낭비하지 않고, **이미 검증된 강력한 도구들을 가져와서** 복잡하고 전문적인 작업(예: AI 모델 훈련, 대규모 데이터 분석)을 훨씬 빠르고 효율적으로 구현할 수 있게 해줍니다.\n",
"\n",
"### 🥉 장점 3: 뛰어난 범용성 (다목적성)\n",
"\n",
"파이썬은 특정 분야에 갇혀 있지 않고, 거의 모든 종류의 프로그래밍 작업에 활용될 수 있습니다.\n",
"\n",
"* **설명:** 웹사이트를 만들거나, 데이터를 분석하거나, 자동화 스크립트를 작성하거나, 인공지능 모델을 만들고, 게임을 만드는 등 원하는 거의 모든 분야에 적용이 가능합니다.\n",
"* **효과:** 하나의 언어만 배우면 여러 분야를 커버할 수 있어, 개발 분야를 바꾸거나 새로운 프로젝트를 시도할 때 유연하게 대처할 수 있는 큰 이점이 있습니다.\n",
"네, 첫 번째 장점인 **\"배우기 쉽고 직관적인 문법\"**을 보여주는 예시 코드를 작성해 드리겠습니다.\n",
"\n",
"이 코드는 파이썬의 가장 기본적인 개념(변수 할당, 출력)만으로도 하나의 작업을 수행하는 예시입니다.\n",
"\n",
"### 📝 예시 코드: 간단한 인사말 출력 및 계산\n",
"\n",
"이 코드는 사용자의 이름을 입력받아 환영 메시지를 출력하고, 나이를 5살 더한 나이를 계산하는 아주 간단한 작업을 수행합니다.\n",
"\n",
"```python\n",
"# 1. 사용자에게 이름 입력받기 (input 함수 사용)\n",
"name = input(\"당신의 이름은 무엇인가요? \")\n",
"\n",
"# 2. 사용자에게 나이 입력받기 (input을 받고 숫자로 변환)\n",
"age_str = input(\"당신의 나이는 몇 살인가요? \")\n",
"age = int(age_str) # 문자열을 숫자로 변환\n",
"\n",
"# 3. 계산 결과 만들기\n",
"new_age = age + 5\n",
"\n",
"# 4. 결과 출력하기 (print 함수 사용)\n",
"print(\"안녕하세요, \" + name + \"님!\")\n",
"print(\"당신은 \" + str(new_age) + \"살이고, 5살을 더하면 \" + str(new_age + 5) + \"살이 됩니다.\")\n",
"```\n",
"\n",
"### ✨ 이 코드가 첫 번째 장점을 보여주는 이유\n",
"\n",
"1. **직관적인 명령:** 코드가 마치 우리가 일상에서 사용하는 영어 문장처럼 읽히기 때문에, 프로그래밍 경험이 없어도 코드의 흐름을 쉽게 파악할 수 있습니다. (예: `print(...)`는 '화면에 이것을 보여줘'라는 뜻으로 매우 직관적입니다.)\n",
"2. **간결한 문법:** 복잡한 기호나 괄호 사용이 최소화되어 있어, 코드가 길어지더라도 논리적인 흐름을 따라가기 쉽습니다.\n",
"3. **쉬운 흐름:** 코드가 위에서 아래로 순서대로 실행되므로, 어떤 명령이 언제 실행되는지 예측하기가 매우 쉽습니다.\n",
"\n",
"**💡 핵심:** 파이썬 코드는 \"무엇을 하라\"고 명령하는 방식이 아니라, **\"이렇게 하라\"**는 자연스러운 문장으로 지시하는 방식에 가깝기 때문에, 초보자가 코딩의 논리 자체에 집중할 수 있도록 돕습니다.\n",
"두 번째 장점인 **\"방대하고 강력한 생태계 (풍부한 라이브러리)\"**를 보여주는 예시 코드를 작성해 드리겠습니다.\n",
"\n",
"이 예시에서는 데이터 분석에서 가장 강력한 라이브러리 중 하나인 **Pandas**를 사용하여, 복잡한 데이터 처리 작업을 단 몇 줄의 코드로 해결하는 모습을 보여줍니다.\n",
"\n",
"### 🛠️ 예시 코드: Pandas를 이용한 간단한 데이터 분석\n",
"\n",
"이 코드는 실제로 데이터를 읽어와서 평균을 계산하고, 단순히 명령 한 줄로 데이터를 처리하는 과정을 보여줍니다.\n",
"\n",
"**(참고: 이 코드를 실행하려면 컴퓨터에 `pandas` 라이브러리가 설치되어 있어야 합니다. `pip install pandas` 명령으로 설치할 수 있습니다.)**\n",
"\n",
"```python\n",
"# 1. 필요한 라이브러리 불러오기 (외부 도구 사용)\n",
"import pandas as pd\n",
"\n",
"# 2. 데이터 준비 (실제로는 파일에서 불러오지만, 예시를 위해 직접 데이터를 만듭니다.)\n",
"data = {\n",
" '제품': ['사과', '바나나', '오렌지', '포도', '수박'],\n",
" '판매량': [150, 200, 120, 350, 180],\n",
" '가격': [1000, 800, 1200, 1500, 2000]\n",
"}\n",
"\n",
"# 3. 데이터프레임(DataFrame) 생성 (Pandas 라이브러리 사용)\n",
"df = pd.DataFrame(data)\n",
"\n",
"print(\"--- 원본 데이터 테이블 ---\")\n",
"print(df)\n",
"print(\"\\n\" + \"=\"*30 + \"\\n\")\n",
"\n",
"\n",
"# 4. 강력한 라이브러리 기능을 사용한 데이터 분석 (복잡한 로직 대신 함수 호출)\n",
"# '판매량' 열의 평균을 계산\n",
"average_sales = df['판매량'].mean()\n",
"\n",
"# '가격' 열의 총합을 계산\n",
"total_price = df['가격'].sum()\n",
"\n",
"\n",
"# 5. 결과 출력\n",
"print(f\"🚀 계산 결과:\")\n",
"print(f\"모든 제품의 평균 판매량: {average_sales:.2f} 개\")\n",
"print(f\"모든 제품의 총 판매 가격: {total_price:,} 원\")\n",
"```\n",
"\n",
"### 💡 이 코드가 두 번째 장점을 보여주는 이유\n",
"\n",
"1. **단순한 명령으로 복잡한 작업 수행:**\n",
" * 만약 Pandas 라이브러리가 없었다면, 우리는 위에서 만든 데이터(`df`)를 반복문(Loop)을 사용하여 하나씩 꺼내서, 판매량을 더하고, 가격을 나누고, 평균을 구하는 **복잡한 수백 줄의 코드**를 작성해야 했을 것입니다.\n",
" * 하지만 Pandas라는 **도구(라이브러리)**를 사용함으로써, 우리는 단지 `df['판매량'].mean()`이라는 **단 한 줄의 명령**만으로 원하는 결과를 얻어냈습니다.\n",
"\n",
"2. **시간 절약 및 효율성:**\n",
" * 라이브러리는 이미 수많은 전문가들이 테스트하고 만든 최적화된 코드를 담고 있습니다. 개발자는 이러한 기본 코드를 다시 만들 필요 없이, **필요한 기능만 가져와서** 자신의 목적에 맞게 조합하기만 하면 됩니다.\n",
"\n",
"**핵심:** 파이썬은 기본적으로 강력한 '도구 상자'이며, 우리는 이 도구 상자에 이미 만들어진 수많은 전문가의 지혜(라이브러리)를 쉽게 빌려와서, 남들이 하기 어려운 전문적인 작업까지 손쉽게 처리할 수 있게 해줍니다.\n",
"파이썬이란?...\n",
"파이썬(Python)은 오늘날 가장 인기 있고 널리 사용되는 프로그래밍 ...\n",
"방금 말한 내용의 장점 3가지는?...\n",
"방금 설명드린 내용을 바탕으로 파이썬의 가장 핵심적이고 강력한 장점 3가...\n",
"그 중 첫번째 장점에 대한 예시 코드 작성해줘...\n",
"네, 첫 번째 장점인 **\"배우기 쉽고 직관적인 문법\"**을 보여주는 예...\n",
"두 번째 장점에 대한 예시 코드 작성해줘...\n",
"두 번째 장점인 **\"방대하고 강력한 생태계 (풍부한 라이브러리)\"**를...\n"
]
}
],
"execution_count": 22
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-28T01:44:33.443082277Z",
"start_time": "2026-05-28T01:44:16.764525701Z"
}
},
"cell_type": "code",
"source": [
"# 여러 사람과 대화하는 부분 저장 => 세선별로 저장\n",
"\n",
"# 세션별 이력 저장\n",
"store = {}\n",
"\n",
"def get_sesstion_history(session_id):\n",
" if session_id not in store:\n",
" store[session_id] = InMemoryChatMessageHistory()\n",
" return store[session_id]\n",
"\n",
"# 프롬프트 생성\n",
"prompt = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"당신은 {role}입니다., 한국어로 답변하세요.\"),\n",
" MessagesPlaceholder(variable_name=\"history\"), # 대화이력 삽입될 위치임\n",
" (\"human\", \"{input}\")\n",
" # (\"human\", \"{input}\\n {history}\\n\")\n",
"])\n",
"\n",
"# LLM 생성\n",
"# 체인 생성\n",
"chain = prompt | gemma_llm | StrOutputParser()\n",
"\n",
"# 1. sesstion_id 이력 조회\n",
"# 2. history 키에 이력 삽입 -> prompt -> llm 실행 -> 대화 나눈 이력을 history 추가\n",
"\n",
"with_history = RunnableWithMessageHistory(chain, get_sesstion_history, input_messages_key=\"input\", history_messages_key=\"history\")\n",
"\n",
"# 세션 생성\n",
"cfg_a = {\"configurable\":{\"session_id\":\"user_alice\"}}\n",
"cfg_b = {\"configurable\":{\"session_id\":\"user_bob\"}}\n",
"\n",
"# Alice 대화\n",
"res1 = with_history.invoke({\"role\":\"파이썬 튜더\", \"input\":\"안녕\"}, config=cfg_a)\n",
"res2 = with_history.invoke({\"role\":\"파이썬 튜더\", \"input\":\"내 이름은 Alice야\"}, config=cfg_a)\n",
"res5 = with_history.invoke({\"role\":\"파이썬 튜더\", \"input\":\"내 이름은 기억해?\"}, config=cfg_a)\n",
"\n",
"# Bob 대화\n",
"res3 = with_history.invoke({\"role\":\"파이썬 튜더\", \"input\":\"안녕\"}, config=cfg_b)\n",
"res4 = with_history.invoke({\"role\":\"파이썬 튜더\", \"input\":\"내 이름은 Bob야\"}, config=cfg_b)\n",
"\n",
"print(\"alice \", res2)\n",
"print(\"bob \", res5)\n",
"\n",
"# # 세션 생성\n",
"# def create_session():\n",
"# sesstion_id = uuid.uuid4().hex\n",
"# return sesstion_id\n",
"#\n",
"# # 세션별 이력 조회\n",
"# def get_sesstion_history(sesstion_id):\n",
"# if sesstion_id not in store:\n",
"# store[sesstion_id] = InMemoryChatMessageHistory()\n",
"# return store[sesstion_id]\n"
],
"id": "a2ade04f6a51a29",
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/cooney/Source/.venv/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3748: LangChainDeprecationWarning: RunnableWithMessageHistory is deprecated. Use LangGraph's built-in persistence instead.\n",
" exec(code_obj, self.user_global_ns, self.user_ns)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"alice Alice님, 만나서 반갑습니다! 😊\n",
"\n",
"앞으로 파이썬 학습에 있어서 궁금한 점이나 도움이 필요한 부분이 있다면 언제든지 저에게 질문해주세요. 제가 알고 있는 모든 것을 동원해서 최대한 자세하고 친절하게 도와드리겠습니다!\n",
"\n",
"이제 파이썬으로 무엇을 해보고 싶으신가요? 예를 들어, 기본적인 문법부터 시작해 볼까요?\n",
"bob 네, 기억하고 있습니다! 😊\n",
"\n",
"당신이 **Alice**라는 이름을 알려주셨기 때문에, 지금 이 대화에서는 Alice님이시라는 것을 기억하고 있습니다.\n",
"\n",
"저는 대화의 맥락(context)을 기억하여 이전 내용을 바탕으로 더 자연스럽게 소통하려고 노력합니다.\n",
"\n",
"이제 Alice님과 함께 파이썬 공부를 계속할 준비가 되었습니다! 다음 질문이나 학습하고 싶은 주제가 있으신가요?\n"
]
}
],
"execution_count": 41
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"- 대화가 길어지면 이력 전체를 LLM에 넘기는 경우 ContextWindowExceededError 발생 = 즉 Context Window를 초과\n",
"- memory에 어떤 정보를 얼마나 보존할 지 결정\n",
"- 전략\n",
" - 최근 K 턴만 유지\n",
" - 오래된 대화 요약\n",
" - 요약 + 최근대화\n"
],
"id": "c83d8f23c50214c"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-28T01:55:05.424326080Z",
"start_time": "2026-05-28T01:54:33.248920182Z"
}
},
"cell_type": "code",
"source": [
"# 세션별 이력 저장\n",
"store = {}\n",
"\n",
"def get_sesstion_history(session_id):\n",
" if session_id not in store:\n",
" store[session_id] = InMemoryChatMessageHistory()\n",
" return store[session_id]\n",
"\n",
" # 최근 몇 개만 유지\n",
" history = store[session_id]\n",
"\n",
" if len(history.messages) > 8:\n",
" history.messages[:] = history.messages[-8:]\n",
"\n",
" return history\n",
"\n",
"with_history = RunnableWithMessageHistory(chain, get_sesstion_history, input_messages_key=\"input\", history_messages_key=\"history\")\n",
"\n",
"for i in range(10):\n",
" with_history.invoke({\"role\":\"파이썬 튜터\", \"input\":f\"{i}번째 질문\"}, config=cfg_a)\n",
"\n",
"history = get_sesstion_history(\"user_alice\")\n",
"\n",
"for msg in history.messages:\n",
" print(msg)\n",
"\n",
"print(len(history.messages))"
],
"id": "4464efadb9ea0fc7",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"content='0번째 질문' additional_kwargs={} response_metadata={}\n",
"content='안녕하세요! 파이썬 튜터입니다. 😊\\n\\n**첫 번째 질문을 해주세요!** 어떤 것에 대해 궁금하신가요? 프로그래밍, 특정 개념, 코드 작성, 문제 해결 등 무엇이든 좋습니다. 편하게 질문해 주세요!' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='1번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, 준비되었습니다! 😊\\n\\n**첫 번째 질문을 말씀해 주세요!** 제가 아는 범위 내에서 최대한 자세하고 명확하게 답변해 드리겠습니다.' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='2번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, 이제 **두 번째 질문**을 해 주시면 됩니다! 😊\\n\\n어떤 주제가 궁금하신가요? 코딩, 라이브러리 사용법, 특정 개념 설명 등 무엇이든 좋습니다.\\n\\n**준비되셨다면, 편하게 질문해 주세요!** 기다리고 있겠습니다. ✨' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='3번째 질문' additional_kwargs={} response_metadata={}\n",
"content='세 번째 질문을 기다리고 있겠습니다! 😊\\n\\n이제 **세 번째 질문**을 해주시면 됩니다. 어떤 것에 대해 알고 싶으신가요?\\n\\n궁금한 점을 구체적으로 말씀해 주시면, 제가 파이썬 지식을 활용하여 친절하게 설명해 드릴게요! 편하게 질문해 주세요. ✨' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='4번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, **네 번째 질문**을 기다리고 있겠습니다! 😊\\n\\n지금까지 질문해주신 내용을 종합해 볼 때, 파이썬과 관련된 질문이 많을 것 같습니다.\\n\\n**마음 편하게 네 번째 질문을 해 주세요!** 제가 최선을 다해 답변해 드리겠습니다. 😊' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='5번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, 이제 **다섯 번째 질문**을 기다리고 있겠습니다! 😊\\n\\n어떤 내용이 궁금하신가요?\\n\\n언제든지 편하게 질문해 주세요. 파이썬에 관한 것이든, 다른 것이든 제가 아는 선에서 최선을 다해 답변해 드리겠습니다! ✨' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='6번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, **여섯 번째 질문**을 기다리고 있겠습니다! 😊\\n\\n이제 궁금한 것을 질문해 주시면 됩니다. 😊\\n\\n**질문해 주세요!** 기다리고 있겠습니다. 😄' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='7번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, **일곱 번째 질문**을 기다리고 있겠습니다! 😊\\n\\n궁금한 점이 있으시면 언제든지 질문해 주세요.\\n\\n**편하게 질문해 주세요!** 제가 아는 모든 것을 동원해서 명쾌하게 답변해 드리겠습니다. 😊' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='8번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, 이제 **여덟 번째 질문**을 기다리고 있겠습니다! 😊\\n\\n마지막 질문까지 모두 드려주셔서 감사합니다. 이제 **실제로 궁금한 내용을 질문해 주세요!**\\n\\n어떤 것이든 좋습니다. 파이썬에 관한 것이든, 알고 싶은 프로그래밍 개념이든, 어떤 것이든 좋습니다.\\n\\n**질문해 주세요!** 제가 자세히 설명해 드리겠습니다. 😊' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"content='9번째 질문' additional_kwargs={} response_metadata={}\n",
"content='네, **아홉 번째 질문**을 기다리고 있겠습니다! 😊\\n\\n이제 모든 질문 요청을 마쳤으니, **정말로 알고 싶은 질문**을 던져주시면 됩니다!\\n\\n파이썬에 대해 궁금한 점, 특정 코드에 대한 설명, 개념 이해 등 무엇이든 좋습니다.\\n\\n**주저하지 마시고 지금 바로 질문해 주세요!** 제가 최고의 답변을 드리기 위해 준비하고 있겠습니다. ✨' additional_kwargs={} response_metadata={} tool_calls=[] invalid_tool_calls=[]\n",
"20\n"
]
}
],
"execution_count": 44
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-28T03:05:41.353949975Z",
"start_time": "2026-05-28T03:02:10.585619509Z"
}
},
"cell_type": "code",
"source": [
"# 오래된 대화 요약\n",
"\n",
"# history 요약\n",
"summary_prompt = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"다음 대화 내용을 핵심만 3문장 이내로 한국어로 요약하세요.\"),\n",
" (\"human\", \"{converstaion}\"),\n",
"])\n",
"\n",
"summary_chain = summary_prompt | gemma_llm | StrOutputParser()\n",
"\n",
"# 요약 기능이 포함된 클래스\n",
"class SummarizedChatHistory(InMemoryChatMessageHistory):\n",
" \"\"\"대화가 max_turns 초과 시 오래된 메시지 요약하기\"\"\"\n",
" max_turns:int = Field(default=6)\n",
" summary:str = Field(default=\"\")\n",
"\n",
"def _maybe_summarize(self):\n",
" if len(self.add_message) > self.max_turns * 2:\n",
" # 오래된 내용 찾기\n",
" cutoff = len(self.messages)\n",
" old_megs = self.message[:cutoff]\n",
" new_msgs = self.message[cutoff:]\n",
"\n",
" # 요약할 텍스트 구성\n",
" conv_text = \"\\n\".join(f\"{'사용자' if isinstance(m, HumanMessage) else 'AI'}:{m.content}\" for m in old_megs)\n",
"\n",
" # 기존 요약이 있으면 함께 포함\n",
" if self.summary:\n",
" conv_text = f\"[이전 요약]\\n{self.summary}\\n\\n[새 대화]\\n{conv_text}\"\n",
"\n",
" # 요약 실행\n",
" self.summary = summary_chain.invoke({'converstation':conv_text})\n",
"\n",
" # 오래된 메시지 제거 후 요약본 SystemMessage 로 앞에 삽입\n",
" self.messages.clear()\n",
" self.messages.append(SystemMessage(content=f\"[이전 대화 요약]\\n{self.summary}\"))\n",
" self.messages.extend(new_msgs)\n",
"\n",
"def add_message(self, message):\n",
" super().add_message(message)\n",
" self._maybe_summarize()\n",
"\n",
"store = {}\n",
"\n",
"def get_sesstion_history(session_id) -> SummarizedChatHistory:\n",
" if session_id not in store:\n",
" store[session_id] = SummarizedChatHistory(max_turns=6)\n",
"\n",
" return store[session_id]\n",
"\n",
"# 프롬프트 생성\n",
"prompt = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"친절한 ai 어시스턴트입니다., 한국어로 답변하세요.\"),\n",
" MessagesPlaceholder(variable_name=\"history\"), # 대화이력 삽입될 위치임\n",
" (\"human\", \"{input}\")\n",
" # (\"human\", \"{input}\\n {history}\\n\")\n",
"])\n",
"\n",
"# LLM 생성\n",
"# 체인 생성\n",
"chain = prompt | gemma_llm | StrOutputParser()\n",
"\n",
"with_history = RunnableWithMessageHistory(chain, get_sesstion_history, input_messages_key=\"input\", history_messages_key=\"history\")\n",
"\n",
"# 세션 생성\n",
"cfg_a = {\"configurable\":{\"session_id\":\"user_alice\"}}\n",
"\n",
"# 임의 대화 생성\n",
"question = [\n",
" \"파이썬이란?\",\n",
" \"방금 설명한 파이썬의 장점은?\",\n",
" \"단점은?\",\n",
" \"어떤 분야에 많이 쓰여?\",\n",
" \"입문자에게 추천하는 학습 순서는?\",\n",
" \"좋은 파이썬 책 추천해줘?\",\n",
" \"무료로 배울 수 있는 사이트는?\",\n",
" \"이전에 내가 뭘 물어봤는지 기억해?\",\\\n",
"]\n",
"\n",
"for i, q in enumerate(question, 1):\n",
" resonse = with_history.invoke({\"input\":q}, config=cfg_a)\n",
" print(f\"\\n{i}턴 Q: {q}\")\n",
" print(f\"\\n A: {resonse[:60]}....\")\n",
"\n",
" # 요약 여부 확인\n",
" history = get_sesstion_history(\"user_alice\")\n",
" if history.summary:\n",
" print(f\"\\n 요약 처리! 현재 메시지 수 {len(history.messages)}\")\n",
" print(f\"\\n 요약 내용 {history.summary[:200]}....\")"
],
"id": "eefbcb9dd8c9af32",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"1턴 Q: 파이썬이란?\n",
"\n",
" A: 파이썬(Python)은 오늘날 가장 인기 있고 널리 사용되는 **프로그래밍 언어** 중 하나입니다.\n",
"\n",
"쉽게 ....\n",
"\n",
"2턴 Q: 방금 설명한 파이썬의 장점은?\n",
"\n",
" A: 방금 설명드린 내용을 바탕으로 파이썬이 가진 **핵심적인 장점**들을 정리해 드리겠습니다. 이 장점들 때문에....\n",
"\n",
"3턴 Q: 단점은?\n",
"\n",
" A: 파이썬은 그 장점들이 매우 강력한 만큼, 모든 프로그래밍 언어와 마찬가지로 **단점**도 존재합니다. 단점은....\n",
"\n",
"4턴 Q: 어떤 분야에 많이 쓰여?\n",
"\n",
" A: 파이썬은 그 **뛰어난 범용성** 덕분에 현재 IT 업계에서 **가장 활발하게 사용되는 분야** 중 하나입니....\n",
"\n",
"5턴 Q: 입문자에게 추천하는 학습 순서는?\n",
"\n",
" A: 파이썬을 처음 배우시는 분이라면, **기초 문법을 탄탄하게 다진 후, 관심 있는 분야로 확장해 나가는 순서*....\n",
"\n",
"6턴 Q: 좋은 파이썬 책 추천해줘?\n",
"\n",
" A: 파이썬 학습을 시작하는 분들을 위한 책은 정말 많습니다. 어떤 책이 '가장 좋다'고 단정하기보다는, **학습....\n",
"\n",
"7턴 Q: 무료로 배울 수 있는 사이트는?\n",
"\n",
" A: 파이썬을 무료로 배울 수 있는 훌륭한 온라인 플랫폼과 자료들이 정말 많습니다! 어떤 학습 스타일(인터랙티브 ....\n",
"\n",
"8턴 Q: 이전에 내가 뭘 물어봤는지 기억해?\n",
"\n",
" A: 네, 저는 **이전 대화의 맥락(Context)**을 기억하고 있습니다.\n",
"\n",
"지금까지의 대화 내용을 바탕으로,....\n"
]
}
],
"execution_count": 52
}
],
"metadata": {
+2037 -9
View File
File diff suppressed because one or more lines are too long
+669
View File
@@ -0,0 +1,669 @@
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": [
"#### RAG 심화\n",
"- 중복 문서 문제 : 비슷한 내용의 청크가 여러개 반환되어 컨텍스트 낭비 발생\n",
"- 검색 : 시맨틱 유사도만으로는 정확한 키워드 매칭이 어려움\n",
"- 구조적 질의 불가 : '2024년 이후 계약 금액이 1억이상인 제품 찾기' 같은 메타 필터 처리 불가\n",
"- 노이즈 청크 : 관련성이 낮은 청크가 LLM 에게 전달되어 환각 유발"
],
"id": "e7f0623c946dacf1"
},
{
"cell_type": "code",
"id": "7ed47758",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:33.118160997Z",
"start_time": "2026-05-29T09:32:30.827718072Z"
}
},
"source": [
"from langchain_ollama import ChatOllama\n",
"from langchain_ibm import ChatWatsonx\n",
"from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder\n",
"from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser\n",
"from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda\n",
"from langchain_core.messages import HumanMessage, SystemMessage, AIMessage\n",
"from langchain_core.chat_history import InMemoryChatMessageHistory, BaseChatMessageHistory\n",
"from langchain_core.runnables.history import RunnableWithMessageHistory\n",
"from pydantic import BaseModel, Field\n",
"from typing import Literal\n",
"from dotenv import load_dotenv\n",
"import os\n",
"\n",
"from langchain_community.document_loaders import PyPDFLoader, CSVLoader, WebBaseLoader, DirectoryLoader\n",
"from youtube_transcript_api import YouTubeTranscriptApi\n",
"from langchain_core.documents import Document\n",
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
"from langchain_ollama import OllamaEmbeddings\n",
"from langchain_ibm import WatsonxEmbeddings\n",
"from langchain_chroma import Chroma\n",
"from langchain_community.vectorstores import FAISS\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"from langchain_classic.chains.query_constructor.base import AttributeInfo\n",
"from langchain_classic.retrievers import EnsembleRetriever, ContextualCompressionRetriever,BM25Retriever\n",
"from langchain_classic.retrievers.self_query.chroma import ChromaTranslator\n",
"from langchain_classic.retrievers.self_query.base import SelfQueryRetriever\n",
"from langchain_community.cross_encoders import HuggingFaceCrossEncoder"
],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_117554/2207901163.py:14: DeprecationWarning: `langchain-community` is being sunset and is no longer actively maintained. See https://github.com/langchain-ai/langchain-community/issues/674 for details and migration guidance toward standalone integration packages.\n",
" from langchain_community.document_loaders import PyPDFLoader, CSVLoader, WebBaseLoader, DirectoryLoader\n",
"USER_AGENT environment variable not set, consider setting it to identify your requests.\n"
]
}
],
"execution_count": 7
},
{
"cell_type": "code",
"id": "5e8adec3",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:33.131396679Z",
"start_time": "2026-05-29T09:32:33.120394257Z"
}
},
"source": [
"# .env 내용 가져오기\n",
"load_dotenv()\n",
"\n",
"apikey = os.getenv(\"WATSONX_API_KEY\")\n",
"project_id = os.getenv(\"WATSONX_PROJECT_ID\")\n",
"watsonx_ai_url = os.getenv(\"WATSONX_URL\")\n",
"hf_token = os.getenv(\"HF_TOKEN\")"
],
"outputs": [],
"execution_count": 8
},
{
"cell_type": "code",
"id": "38308ea9",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:37.470030012Z",
"start_time": "2026-05-29T09:32:33.133843674Z"
}
},
"source": [
"ollama_embedding = OllamaEmbeddings(model=\"nomic-embed-text-v2-moe\")\n",
"watson_embedding = WatsonxEmbeddings(\n",
" model_id=\"ibm/granite-embedding-278m-multilingual\",\n",
" url = f\"{watsonx_ai_url}\",\n",
" api_key = f\"{apikey}\",\n",
" project_id=f\"{project_id}\"\n",
")\n",
"\n",
"hugging_llm = ChatOpenAI(\n",
" model=\"Qwen/Qwen2.5-7B-Instruct:together\",\n",
" api_key=hf_token,\n",
" base_url=\"https://router.huggingface.co/v1\",\n",
" temperature=0\n",
")\n",
"\n",
"watson_llm = ChatWatsonx(\n",
" model_id=\"ibm/granite-4-h-small\",\n",
" url = f\"{watsonx_ai_url}\",\n",
" api_key = f\"{apikey}\",\n",
" project_id=f\"{project_id}\",\n",
" max_tokens = 2000,\n",
" params = {\n",
" \"temperature\":0\n",
" }\n",
")\n",
"\n",
"# 로컬 LLM\n",
"qwen_llm = ChatOllama(model=\"qwen3.5:4b\",temperature=0)\n",
"exaone_llm = ChatOllama(model=\"exaone3.5:2.4b\",temperature=0)"
],
"outputs": [],
"execution_count": 9
},
{
"cell_type": "code",
"id": "f0ea314f",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:37.519275275Z",
"start_time": "2026-05-29T09:32:37.482764023Z"
}
},
"source": [
"# pdf => chunks 반환 함수\n",
"def create_chunks_from_pdf(pdf_path,chunk_size=500,chunk_overlap=50):\n",
" loader = PyPDFLoader(pdf_path)\n",
" splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)\n",
" chunks = splitter.split_documents(loader.load())\n",
"\n",
" # 공백 제거\n",
" chunks = [chunk for chunk in chunks if chunk.page_content.strip()]\n",
" return chunks\n",
"\n",
"def create_vectorstore(chunks,embeddings,collection_name,persist_directory='./db/chroma_db'):\n",
" return Chroma.from_documents(\n",
" documents=chunks, \n",
" embedding=embeddings, \n",
" persist_directory=persist_directory, \n",
" collection_name=collection_name)\n",
"\n",
"\n",
"def create_retriever(vectorstore,search_type=\"similarity\",k=3,fetch_k=20,lambda_mult=0.5):\n",
" kwargs = {\"k\":k}\n",
"\n",
" if search_type==\"mmr\":\n",
" kwargs['fetch_k'] = fetch_k\n",
" kwargs['lambda_mult'] = lambda_mult \n",
"\n",
"\n",
" return vectorstore.as_retriever(search_type=search_type, search_kwargs=kwargs)\n",
"\n",
"\n",
"def print_retrieved_docs(title, retriever, query):\n",
" docs = retriever.invoke(query)\n",
"\n",
" print(\"\\n\"+\"=\"*50)\n",
" print(title)\n",
" print(\"=\"*50)\n",
"\n",
" for i, doc in enumerate(docs):\n",
" print(f\"\\n[chunk {i}]\")\n",
" print(doc.page_content)\n",
" print(f\"\\nPage: {doc.metadata.get(\"page\")}\")"
],
"outputs": [],
"execution_count": 10
},
{
"cell_type": "markdown",
"id": "d74ac88b",
"metadata": {},
"source": [
"### 1. 임베딩 모델, 청크 사이즈, 오버랩 "
]
},
{
"cell_type": "code",
"id": "9bdff4a0",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:38.289513200Z",
"start_time": "2026-05-29T09:32:37.531732515Z"
}
},
"source": [
"chunks1 = create_chunks_from_pdf(\"./data/Summary of ChatGPTGPT-4 Research.pdf\",1000, 100)\n",
"chunks2 = create_chunks_from_pdf(\"./data/Summary of ChatGPTGPT-4 Research.pdf\",300, 30)\n",
"\n",
"print(f\"분할된 청크 수 : {len(chunks1)}\")\n",
"print(f\"분할된 청크 수 : {len(chunks2)}\")"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"분할된 청크 수 : 118\n",
"분할된 청크 수 : 378\n"
]
}
],
"execution_count": 11
},
{
"cell_type": "code",
"id": "09b02dbc",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:51.735295861Z",
"start_time": "2026-05-29T09:32:38.305096987Z"
}
},
"source": [
"vectorestore1 = create_vectorstore(chunks1,watson_embedding,collection_name=\"gpt_research_watson3\")\n",
"vectorestore2 = create_vectorstore(chunks1,ollama_embedding,collection_name=\"gpt_research_watson4\")\n",
"\n",
"watson1_retriever = create_retriever(vectorestore1)\n",
"watson2_retriever = create_retriever(vectorestore2)\n",
"\n",
"\n",
"query = 'where can i use chatGPT?'\n",
"\n",
"print_retrieved_docs('watson embedding',watson1_retriever, query)\n",
"print_retrieved_docs('ollama embedding',watson2_retriever,query)"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"==================================================\n",
"watson embedding\n",
"==================================================\n",
"\n",
"[chunk 0]\n",
"development.\n",
"2 Related work of ChatGPT\n",
"In this section, we review the latest research related to the application, ethics,\n",
"and evaluation of ChatGPT.\n",
"2.1 Application of ChatGPT\n",
"2.1.1 Question And Answering\n",
"In the education field\n",
"ChatGPT is commonly used for question and answers testing in the edu-\n",
"cation sector. Users can use ChatGPT to learn, compare and verify answers\n",
"for different academic subjects such as physics, mathematics, and chemistry,\n",
"4\n",
"\n",
"Page: 3\n",
"\n",
"[chunk 1]\n",
"development.\n",
"2 Related work of ChatGPT\n",
"In this section, we review the latest research related to the application, ethics,\n",
"and evaluation of ChatGPT.\n",
"2.1 Application of ChatGPT\n",
"2.1.1 Question And Answering\n",
"In the education field\n",
"ChatGPT is commonly used for question and answers testing in the edu-\n",
"cation sector. Users can use ChatGPT to learn, compare and verify answers\n",
"for different academic subjects such as physics, mathematics, and chemistry,\n",
"4\n",
"\n",
"Page: 3\n",
"\n",
"[chunk 2]\n",
"development.\n",
"2 Related work of ChatGPT\n",
"In this section, we review the latest research related to the application, ethics,\n",
"and evaluation of ChatGPT.\n",
"2.1 Application of ChatGPT\n",
"2.1.1 Question And Answering\n",
"In the education field\n",
"ChatGPT is commonly used for question and answers testing in the edu-\n",
"cation sector. Users can use ChatGPT to learn, compare and verify answers\n",
"for different academic subjects such as physics, mathematics, and chemistry,\n",
"4\n",
"\n",
"Page: 3\n",
"\n",
"==================================================\n",
"ollama embedding\n",
"==================================================\n",
"\n",
"[chunk 0]\n",
"development.\n",
"2 Related work of ChatGPT\n",
"In this section, we review the latest research related to the application, ethics,\n",
"and evaluation of ChatGPT.\n",
"2.1 Application of ChatGPT\n",
"2.1.1 Question And Answering\n",
"In the education field\n",
"ChatGPT is commonly used for question and answers testing in the edu-\n",
"cation sector. Users can use ChatGPT to learn, compare and verify answers\n",
"for different academic subjects such as physics, mathematics, and chemistry,\n",
"4\n",
"\n",
"Page: 3\n",
"\n",
"[chunk 1]\n",
"development.\n",
"2 Related work of ChatGPT\n",
"In this section, we review the latest research related to the application, ethics,\n",
"and evaluation of ChatGPT.\n",
"2.1 Application of ChatGPT\n",
"2.1.1 Question And Answering\n",
"In the education field\n",
"ChatGPT is commonly used for question and answers testing in the edu-\n",
"cation sector. Users can use ChatGPT to learn, compare and verify answers\n",
"for different academic subjects such as physics, mathematics, and chemistry,\n",
"4\n",
"\n",
"Page: 3\n",
"\n",
"[chunk 2]\n",
"development.\n",
"2 Related work of ChatGPT\n",
"In this section, we review the latest research related to the application, ethics,\n",
"and evaluation of ChatGPT.\n",
"2.1 Application of ChatGPT\n",
"2.1.1 Question And Answering\n",
"In the education field\n",
"ChatGPT is commonly used for question and answers testing in the edu-\n",
"cation sector. Users can use ChatGPT to learn, compare and verify answers\n",
"for different academic subjects such as physics, mathematics, and chemistry,\n",
"4\n",
"\n",
"Page: 3\n"
]
}
],
"execution_count": 12
},
{
"cell_type": "markdown",
"id": "85b489ff",
"metadata": {},
"source": [
"### 2. MMR(Maximal Marginal Relevace) Retriever\n",
"- 관련성(Relevace)과 다양성(Diversity) 고려\n",
"- 법률 문서, 기술 매뉴얼처럼 유사 내용이 반복되는 문서에서 효과적\n",
"- 동작과정\n",
" - 질문 => 임베딩\n",
" - 벡터 스토어에서 질문과 유사한 상위 fetch_K(후보 문서) 추출\n",
" - 후보문서에서 MMR 점수 계산 => 가장 높은 문서 추출\n",
" - 남은 후보에서 MMR 점수 계산 => 높은 문서 추출\n",
" - 추출한 높은 문서에서 최종 K 반환"
]
},
{
"cell_type": "code",
"id": "f6d1c603",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:54.345812917Z",
"start_time": "2026-05-29T09:32:51.753992471Z"
}
},
"source": [
"chunks1 = create_chunks_from_pdf(\"./data/2026 상 삼성전자 DX부문 직무기술서.pdf\")\n",
"vectorestore1 = create_vectorstore(chunks1,watson_embedding,collection_name=\"samsung_watson1\",persist_directory=\"./db/watson_chroma\")\n",
"\n",
"\n",
"mmr_retriever = create_retriever(vectorestore1,search_type=\"mmr\",k=5)\n",
"similarity_retriever = create_retriever(vectorestore1,k=5)\n",
"\n",
"query = '마케팅 - 제품/서비스 마케팅 포지션은?'\n",
"\n",
"print_retrieved_docs('MMR',mmr_retriever, query)\n",
"print_retrieved_docs('similarity',similarity_retriever, query)\n"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"==================================================\n",
"MMR\n",
"==================================================\n",
"\n",
"[chunk 0]\n",
"마케팅\n",
"마켓 센싱 및 정보 분석 결과를 바탕으로\n",
" 당사 제품과 서비스의 차별화 가치를 소비자에게 효과적인\n",
"커뮤니케이션 방법으로 전달하여 목표한 경영성과를 창출하고 브랜드 가치를 제고합니다\n",
"\n",
"Page: 14\n",
"\n",
"[chunk 1]\n",
"국내영업마케팅\n",
"국내의 각 분야별 영업 채널을 발굴\n",
" 지원하여 성과 창출과 지속 성장을 추구하는 동시에\n",
" 한국 시장에\n",
"대한 심도있는 분석을 통해 삼성전자\n",
" 부문 제품의 마케팅 전략을 수립 ⋅ 적용하고 글로벌\n",
"시장으로의 확산 기반을 마련합니다\n",
"\n",
"Page: 24\n",
"\n",
"[chunk 2]\n",
"해외영업\n",
"고객과 시장\n",
" 제품에 대한 이해를 바탕으로 시장 수요와 경쟁환경을 분석하여 국가\n",
" 거래선별 목표 설정\n",
"영업전략 수립\n",
" 신규 제품\n",
" 영업 채널을 발굴하고 판매전략 수립 및 실행을 통해 매출 극대화에\n",
"기여합니다\n",
"\n",
"Page: 18\n",
"\n",
"[chunk 3]\n",
"구매\n",
"제품 생산에 필요한 자원\n",
" 부품\n",
" 설비 및 제품\n",
" 을 최적의 품질과 가격으로\n",
"협상\n",
" 구매하고 시장의 수요 및 생산 계획에 맞춰 적기 공급하여 회사 경영에 기여합니다\n",
"\n",
"Page: 26\n",
"\n",
"[chunk 4]\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"•\n",
"\n",
"Page: 7\n",
"\n",
"==================================================\n",
"similarity\n",
"==================================================\n",
"\n",
"[chunk 0]\n",
"마케팅\n",
"마켓 센싱 및 정보 분석 결과를 바탕으로\n",
" 당사 제품과 서비스의 차별화 가치를 소비자에게 효과적인\n",
"커뮤니케이션 방법으로 전달하여 목표한 경영성과를 창출하고 브랜드 가치를 제고합니다\n",
"\n",
"Page: 14\n",
"\n",
"[chunk 1]\n",
"마케팅\n",
"마켓 센싱 및 정보 분석 결과를 바탕으로\n",
" 당사 제품과 서비스의 차별화 가치를 소비자에게 효과적인\n",
"커뮤니케이션 방법으로 전달하여 목표한 경영성과를 창출하고 브랜드 가치를 제고합니다\n",
"\n",
"Page: 14\n",
"\n",
"[chunk 2]\n",
"국내영업마케팅\n",
"국내의 각 분야별 영업 채널을 발굴\n",
" 지원하여 성과 창출과 지속 성장을 추구하는 동시에\n",
" 한국 시장에\n",
"대한 심도있는 분석을 통해 삼성전자\n",
" 부문 제품의 마케팅 전략을 수립 ⋅ 적용하고 글로벌\n",
"시장으로의 확산 기반을 마련합니다\n",
"\n",
"Page: 24\n",
"\n",
"[chunk 3]\n",
"국내영업마케팅\n",
"국내의 각 분야별 영업 채널을 발굴\n",
" 지원하여 성과 창출과 지속 성장을 추구하는 동시에\n",
" 한국 시장에\n",
"대한 심도있는 분석을 통해 삼성전자\n",
" 부문 제품의 마케팅 전략을 수립 ⋅ 적용하고 글로벌\n",
"시장으로의 확산 기반을 마련합니다\n",
"\n",
"Page: 24\n",
"\n",
"[chunk 4]\n",
"해외영업\n",
"고객과 시장\n",
" 제품에 대한 이해를 바탕으로 시장 수요와 경쟁환경을 분석하여 국가\n",
" 거래선별 목표 설정\n",
"영업전략 수립\n",
" 신규 제품\n",
" 영업 채널을 발굴하고 판매전략 수립 및 실행을 통해 매출 극대화에\n",
"기여합니다\n",
"\n",
"Page: 18\n"
]
}
],
"execution_count": 13
},
{
"cell_type": "markdown",
"id": "82d43df6",
"metadata": {},
"source": [
"### 3. SelfQuery Retriever\n",
"- 자연어 질문을 분석하여 시맨틱 검색쿼리와 메타데이터필터를 LLM이 자동으로 생성하게 하는 고급 Retriever\n",
"- 질문 : 2023년 이후 계약금액이 1억이상인 계약 찾아줘 => LLM \n",
" - 시맨틱 검색쿼리 : 계약\n",
" - filter : {year >= 2023, 계약금액 >= 100000 ~}"
]
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:55.741393823Z",
"start_time": "2026-05-29T09:32:54.370104104Z"
}
},
"cell_type": "code",
"source": "!pip install lark",
"id": "6b910cf56efea998",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: lark in ./.venv/lib/python3.12/site-packages (1.3.1)\r\n"
]
}
],
"execution_count": 14
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:55.778912756Z",
"start_time": "2026-05-29T09:32:55.758410087Z"
}
},
"cell_type": "code",
"source": [
"docs = [\n",
" Document(\n",
" page_content=\"삼성전자 제품 마케팅 직무입니다.\",\n",
" metadata={\n",
" \"year\":2025,\n",
" \"department\":\"marketing\"\n",
" }\n",
" ),\n",
" Document(\n",
" page_content=\"AI 연구 개발 직무입니다.\",\n",
" metadata={\n",
" \"year\":2024,\n",
" \"department\":\"ai\"\n",
" }\n",
" ),\n",
" Document(\n",
" page_content=\"백엔드 개발 직무입니다.\",\n",
" metadata={\n",
" \"year\":2025,\n",
" \"department\":\"developer\"\n",
" }\n",
" ),\n",
"]\n",
"\n",
"\n",
"metadata_field_info = [\n",
" AttributeInfo(name=\"year\", description=\"문서 작성 연도\", type=\"integer\"),\n",
" AttributeInfo(name=\"department\", description=\"직무 부서\", type=\"string\"),\n",
"]\n",
"\n",
"document_content_description = \"회사 내부 문서 및 직무 자료\"\n"
],
"id": "b3a603a71973fd7b",
"outputs": [],
"execution_count": 15
},
{
"cell_type": "code",
"id": "c0c3f024",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:56.283073377Z",
"start_time": "2026-05-29T09:32:55.782236223Z"
}
},
"source": [
"vectorestore1 = create_vectorstore(docs,watson_embedding,collection_name=\"selfquery\",persist_directory=\"./db/watson_chroma\")\n",
"\n",
"self_query_retriever = SelfQueryRetriever.from_llm(\n",
" llm=watson_llm,\n",
" vectorstore=vectorestore1,\n",
" document_contents=document_content_description,\n",
" metadata_field_info=metadata_field_info,\n",
" verbose=True,\n",
" enable_limit=True,\n",
" structured_query_translator=ChromaTranslator()\n",
")"
],
"outputs": [],
"execution_count": 16
},
{
"cell_type": "code",
"id": "6eab886a",
"metadata": {
"ExecuteTime": {
"end_time": "2026-05-29T09:32:58.437857021Z",
"start_time": "2026-05-29T09:32:56.299326951Z"
}
},
"source": [
"question = \"2024년 이후 ai 부서 직무를 찾아줘\"\n",
"self_query_retriever.invoke(question)"
],
"outputs": [
{
"data": {
"text/plain": [
"[Document(id='7180400a-6f3a-4519-b4c8-b384e20e3159', metadata={'year': 2024, 'department': 'ai'}, page_content='AI 연구 개발 직무입니다.'),\n",
" Document(id='f2508f12-f99b-4084-88dd-8777a65e4596', metadata={'department': 'ai', 'year': 2024}, page_content='AI 연구 개발 직무입니다.'),\n",
" Document(id='cb43ff6b-94dc-4e4b-afbe-a025875ed087', metadata={'year': 2024, 'department': 'ai'}, page_content='AI 연구 개발 직무입니다.'),\n",
" Document(id='3fad052f-fabc-4417-88b8-fd0abf471385', metadata={'department': 'ai', 'year': 2024}, page_content='AI 연구 개발 직무입니다.')]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 17
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+8 -6
View File
@@ -10,7 +10,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Hello! 👋 It's great to meet you. How can I assist you today? Feel free to ask me anything or let me know if you need help with something specific!\n"
"Hello! 👋 How can I help you today?\n"
]
}
],
@@ -26,7 +26,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"id": "851a76dc",
"metadata": {},
"outputs": [
@@ -34,7 +34,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
"현재 대한민국 대통령은 윤석열입니다. 그는 2022년 3월 9일 실시된 제20대 대통령 선거에서 당선되어 2022년 5월 10일부터 임기를 시작했습니다. 윤석열 대통령의 임기는 2027년까지 예정되어 있습니다.\n"
"현재 대한민국 대통령은 **윤석열** (Yoon Suk-yeol)입니다.\n",
"\n",
"2022 년 5 월 10 일 10 대 대통령으로 취임하여 재직 중입니다. 다음 대선은 2027 년이 예정되어 있습니다.\n"
]
}
],
@@ -42,7 +44,7 @@
"from ollama import chat\n",
"\n",
"response = chat(\n",
" model='qwen2.5',\n",
" model='qwen3.5:4b',\n",
" messages=[{'role': 'user', 'content': '대한민국 대통령은 누구야?'}],\n",
")\n",
"print(response.message.content)"
@@ -1047,9 +1049,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python (Ollama Venv)",
"display_name": ".venv (3.12.3)",
"language": "python",
"name": "ollama-venv"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
+197
View File
@@ -0,0 +1,197 @@
from langchain_ollama import ChatOllama
from langchain_ibm import ChatWatsonx
from langchain_core.prompts import (
PromptTemplate,
ChatPromptTemplate,
MessagesPlaceholder,
)
from langchain_core.output_parsers import (
StrOutputParser,
JsonOutputParser,
PydanticOutputParser,
)
from langchain_core.runnables import (
RunnablePassthrough,
RunnableParallel,
RunnableLambda,
)
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from langchain_core.chat_history import (
InMemoryChatMessageHistory,
BaseChatMessageHistory,
)
from langchain_core.runnables.history import RunnableWithMessageHistory
from pydantic import BaseModel, Field
from typing import Literal
from dotenv import load_dotenv
import os
import gradio as gr
from langchain_community.document_loaders import (
PyPDFLoader,
CSVLoader,
WebBaseLoader,
DirectoryLoader,
)
from youtube_transcript_api import YouTubeTranscriptApi
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_ollama import OllamaEmbeddings
from langchain_ibm import WatsonxEmbeddings
from langchain_chroma import Chroma
from langchain_community.vectorstores import FAISS
# 모델(LLM, Embeddding)
load_dotenv()
apikey = os.getenv("WATSONX_API_KEY")
project_id = os.getenv("WATSONX_PROJECT_ID")
watsonx_ai_url = os.getenv("WATSONX_URL")
ollama_embedding = OllamaEmbeddings(model="nomic-embed-text-v2-moe")
watson_llm = ChatWatsonx(
model_id="ibm/granite-4-h-small",
url=f"{watsonx_ai_url}",
api_key=f"{apikey}",
project_id=f"{project_id}",
max_tokens=2000,
params={"temperature": 0},
)
# qwen_llm = ChatOllama(model="qwen3.5:4b", temperature=0)
# 1단계
def process_pdf(pdf_file):
if pdf_file is None:
return ("PDF 파일을 업로드 해주세요.", "", "", "", "")
# PDF 로드
loader = PyPDFLoader(pdf_file)
docs = loader.load()
# 총 페이지수
total_pages = len(docs)
# 첫 페이지 내용
first_page_content = docs[0].page_content[:1000]
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
chunks = splitter.split_documents(docs)
# 총 chunk 수
total_chunks = len(chunks)
# 첫 번째 chunk
first_chunk_content = chunks[0].page_content
# 첫 번째 Chunk Metadata
first_chunk_metadata = chunks[0].metadata
return (
f"총 페이지 수 : {total_pages}",
first_page_content,
total_chunks,
first_chunk_content,
first_chunk_metadata,
)
# 2단계
def rag_chat(pdf_file, question):
if pdf_file is None:
return ("PDF 파일을 업로드 해주세요.", "")
# 1. PDF 로드
loader = PyPDFLoader(pdf_file)
docs = loader.load()
# 2. 분할
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
split_docs = splitter.split_documents(docs)
# 3. 임베딩
faiss_store = FAISS.from_documents(documents=split_docs, embedding=ollama_embedding)
# 4. 검색(retriever)
retriever = faiss_store.as_retriever(search_kwargs={"k": 3})
retriever_docs = retriever.invoke(question)
# 5. Context 생성
context = "\n\n".join([doc.page_content for doc in retriever_docs])
### LLM
# 1. prompt
message = """\
당신은 PDF 기반 RAG AI 입니다.
다음 문서를 참고해서 질문에 답변하세요.
문서:
{context}
질문:
{question}
"""
rag_prompt = ChatPromptTemplate.from_template(message)
# 2. chain
chain = rag_prompt | watson_llm | StrOutputParser()
# 3. answer
answer = chain.invoke({"context": context, "question": question})
# 4. 답변, rag 결과 반환
retrieved_text = ""
for i, doc in enumerate(retriever_docs, 1):
retrieved_text += f"""
[검색 문서 {i}]
내용:
{doc.page_content}
metadata:
{doc.metadata}
{'='*50}
"""
return retrieved_text, answer
with gr.Blocks() as demo:
gr.Markdown("# PDF RAG 학습 앱")
with gr.Tabs():
with gr.Tab("1단계 - PDF & Chunk 확인"):
# 파일 업로드 컴포넌트
pdf_input = gr.File(label="PDF 업로드", file_types=[".pdf"])
btn1 = gr.Button("분석 시작")
# textbox 5개
page_output = gr.Textbox(label="총 페이지 수")
first_output = gr.Textbox(label="첫 페이지 내용", lines=10)
chunk_output = gr.Textbox(label="총 chunk 수")
first_chunk_output = gr.Textbox(label="첫 번째 chunk", lines=10)
metadata_output = gr.Textbox(label="첫 번째 Chunk Metadata", lines=5)
btn1.click(
fn=process_pdf,
inputs=[pdf_input],
outputs=[
page_output,
first_output,
chunk_output,
first_chunk_output,
metadata_output,
],
)
with gr.Tab("2단계 - RAG QA"):
# 파일 업로드 컴포넌트
pdf_input = gr.File(label="PDF 업로드", file_types=[".pdf"])
question_input = gr.Textbox(label="질문 입력")
btn1 = gr.Button("질문하기")
retrieved_output = gr.Textbox(label="검색된 chunk", lines=20)
answer_output = gr.Textbox(label="최종 답변", lines=10)
btn1.click(
fn=rag_chat,
inputs=[pdf_input, question_input],
outputs=[retrieved_output, answer_output],
)
demo.launch()