랭체인 심화2

- 뉴스 크롤링 후 필요 내용 가공
- 유사 단어 추출
- 각종 문서안 필요 내용 가공
This commit is contained in:
2026-06-02 18:11:05 +09:00
parent 516d44081d
commit 096222c64f
47 changed files with 1679 additions and 276 deletions
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CsvFileAttributes">
<option name="attributeMap">
<map>
<entry key="/ollama/data/myData.csv">
<value>
<Attribute>
<option name="separator" value="," />
</Attribute>
</value>
</entry>
</map>
</option>
</component>
</project>
+5
View File
@@ -3,6 +3,11 @@
<component name="PyToolsState">
<option name="tools">
<map>
<entry key="black">
<value>
<ToolEntry />
</value>
</entry>
<entry key="pyrefly">
<value>
<ToolEntry />
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
+113
View File
@@ -0,0 +1,113 @@
Words
Elephant
Lion
Tiger
Dog
Cricket
Football
Tennis
Basketball
Apple
Orange
Banana
word
애플
사과
바나나
오렌지
포도
딸기
수박
멜론
복숭아
체리
파인애플
망고
블루베리
데스크탑
노트북
컴퓨터
모니터
키보드
마우스
프린터
스마트폰
아이폰
갤럭시
태블릿
아이패드
이어폰
헤드폰
스피커
맥북
충전기
강아지
고양이
토끼
햄스터
물고기
거북이
다람쥐
치타
호랑이
사자
늑대
여우
판다
자동차
기차
비행기
자전거
오토바이
버스
지하철
택시
트럭
헬리콥터
축구
야구
농구
배구
테니스
배드민턴
골프
수영
달리기
탁구
선생님
학생
의사
간호사
경찰
소방관
요리사
변호사
판사
의사
개발자
디자이너
엔지니어
군인
공무원
피아노
기타
드럼
바이올린
플루트
노래
음악
악기
작곡
가수
연필
볼펜
지우개
공책
필통
가위
스케치북
1 Words
2 Elephant
3 Lion
4 Tiger
5 Dog
6 Cricket
7 Football
8 Tennis
9 Basketball
10 Apple
11 Orange
12 Banana
13 word
14 애플
15 사과
16 바나나
17 오렌지
18 포도
19 딸기
20 수박
21 멜론
22 복숭아
23 체리
24 파인애플
25 망고
26 블루베리
27 데스크탑
28 노트북
29 컴퓨터
30 모니터
31 키보드
32 마우스
33 프린터
34 스마트폰
35 아이폰
36 갤럭시
37 태블릿
38 아이패드
39 이어폰
40 헤드폰
41 스피커
42 맥북
43 충전기
44 강아지
45 고양이
46 토끼
47 햄스터
48
49 물고기
50 거북이
51 다람쥐
52 치타
53 호랑이
54 사자
55 늑대
56 여우
57
58 판다
59 자동차
60 기차
61 비행기
62 자전거
63 오토바이
64 버스
65 지하철
66 택시
67 트럭
68 헬리콥터
69 축구
70 야구
71 농구
72 배구
73 테니스
74 배드민턴
75 골프
76 수영
77 달리기
78 탁구
79 선생님
80 학생
81 의사
82 간호사
83 경찰
84 소방관
85 요리사
86 변호사
87 판사
88 의사
89 개발자
90 디자이너
91 엔지니어
92 군인
93 공무원
94 피아노
95 기타
96 드럼
97 바이올린
98 플루트
99 노래
100 음악
101 악기
102 작곡
103 가수
104
105 연필
106 볼펜
107 지우개
108 공책
109 필통
110
111 가위
112
113 스케치북
Binary file not shown.
Binary file not shown.
Binary file not shown.
+63
View File
@@ -0,0 +1,63 @@
# 사용자가 입력하는 단어와 유사한 2개의 단어 추출
# csv 파일에서 단어를 읽어와서 유사도를 계산하고, 유사한 단어를 추출하는 함수
import csv
import gradio as gr
from langchain_community.document_loaders import CSVLoader
from dotenv import load_dotenv
import os
from langchain_core.output_parsers import StrOutputParser
from langchain_community.vectorstores import FAISS
from langchain_ibm import WatsonxEmbeddings
from langchain_ollama import OllamaEmbeddings
# 모델(LLM, Embeddding)
load_dotenv()
apikey = os.getenv("WATSONX_API_KEY")
project_id = os.getenv("WATSONX_PROJECT_ID")
watsonx_ai_url = os.getenv("WATSONX_URL")
watson_embedding = WatsonxEmbeddings(
model_id="ibm/granite-embedding-278m-multilingual",
url = f"{watsonx_ai_url}",
api_key = f"{apikey}",
project_id=f"{project_id}"
)
ollama_embedding = OllamaEmbeddings(model="nomic-embed-text-v2-moe")
# loader : csv
csv_loader = CSVLoader("./data/myData.csv",encoding="utf-8",csv_args={'delimiter':',', "fieldnames": ["Words"]})
csv_docs = csv_loader.load()
# 벡터스토어
vectorstore = FAISS.from_documents(documents=csv_docs, embedding=watson_embedding)
def find_similar(query):
"""
query 랑 유사한 단어 찾아서 리턴
"""
if not query.strip():
return "검색어 확인", ""
docs = vectorstore.similarity_search(query, k = 2)
result1 = docs[0].page_content if len(docs) > 0 else ""
result2 = docs[1].page_content if len(docs) > 0 else ""
return result1, result2
with gr.Blocks() as app:
gr.Markdown("🐴 Educate Kids")
gr.Markdown("비슷한 단어와 또는 문장을 찾아드립니다.")
query = gr.Textbox(label="단어 입력", placeholder="단어를 입력하세요. 예) 애플")
btn = gr.Button("Find Similar Things")
output1 = gr.Textbox(label="Top Match 1")
output2 = gr.Textbox(label="Top Match 2")
btn.click(find_similar, inputs=query, outputs=[output1, output2])
app.launch()
+236 -214
View File
@@ -15,8 +15,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:37.895131774Z",
"start_time": "2026-06-01T06:01:37.884544613Z"
"end_time": "2026-06-02T00:38:49.652822616Z",
"start_time": "2026-06-02T00:38:40.039385151Z"
}
},
"cell_type": "code",
@@ -47,7 +47,7 @@
"from langchain_classic.chains.query_constructor.base import AttributeInfo\n",
"\n",
"# 1. BM25Retriever를 올바른 커뮤니티 경로로 분리 (쉼표 오타 및 경로 수정)\n",
"from langchain_classic.retrievers import EnsembleRetriever, ContextualCompressionRetriever\n",
"from langchain_classic.retrievers import EnsembleRetriever, ContextualCompressionRetriever, BM25Retriever\n",
"from langchain_community.retrievers import BM25Retriever\n",
"\n",
"from langchain_classic.retrievers.self_query.chroma import ChromaTranslator\n",
@@ -57,14 +57,26 @@
"from langchain_classic.retrievers.document_compressors import LLMChainExtractor, EmbeddingsFilter, DocumentCompressorPipeline"
],
"id": "266323db794d81bc",
"outputs": [],
"execution_count": 24
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/cooney/Source/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n",
"/tmp/ipykernel_10169/2607715662.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": 1
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:37.911093918Z",
"start_time": "2026-06-01T06:01:37.896068864Z"
"end_time": "2026-06-02T00:38:49.667368566Z",
"start_time": "2026-06-02T00:38:49.655109157Z"
}
},
"cell_type": "code",
@@ -80,15 +92,15 @@
],
"id": "be9e3b7dfc34f47f",
"outputs": [],
"execution_count": 25
"execution_count": 2
},
{
"cell_type": "code",
"id": "38308ea9",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:40.671443632Z",
"start_time": "2026-06-01T06:01:37.911995272Z"
"end_time": "2026-06-02T00:38:54.969896546Z",
"start_time": "2026-06-02T00:38:49.670559612Z"
}
},
"source": [
@@ -124,15 +136,15 @@
"gemma_llm = ChatOllama(model=\"gemma4:e2b\")"
],
"outputs": [],
"execution_count": 26
"execution_count": 3
},
{
"cell_type": "code",
"id": "f0ea314f",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:40.706617108Z",
"start_time": "2026-06-01T06:01:40.682276918Z"
"end_time": "2026-06-02T00:38:55.009258425Z",
"start_time": "2026-06-02T00:38:54.994086163Z"
}
},
"source": [
@@ -178,7 +190,7 @@
" print(f\"\\nPage: {doc.metadata.get(\"page\")}\")"
],
"outputs": [],
"execution_count": 27
"execution_count": 4
},
{
"cell_type": "markdown",
@@ -193,8 +205,8 @@
"id": "9bdff4a0",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:41.478718891Z",
"start_time": "2026-06-01T06:01:40.709216173Z"
"end_time": "2026-06-02T00:38:55.821089156Z",
"start_time": "2026-06-02T00:38:55.014167237Z"
}
},
"source": [
@@ -214,15 +226,15 @@
]
}
],
"execution_count": 28
"execution_count": 5
},
{
"cell_type": "code",
"id": "09b02dbc",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:52.225911444Z",
"start_time": "2026-06-01T06:01:41.487950012Z"
"end_time": "2026-06-02T00:39:06.978655069Z",
"start_time": "2026-06-02T00:38:55.837812970Z"
}
},
"source": [
@@ -264,6 +276,21 @@
"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",
"of LLM for a variety of transportation tasks.\n",
"Nowadays, ChatGPT shows a wide range of applications in data visualiza-\n",
"tion, information extraction, data enhancement, quality assessment, and multi-\n",
@@ -273,17 +300,6 @@
"\n",
"Page: 17\n",
"\n",
"[chunk 2]\n",
"have been widely used for text classification, recent advances in natural lan-\n",
"guage processing have led to the development of more advanced techniques.\n",
"ChatGPT has shown immense potential in this field. Its ability to accurately\n",
"classify text, flexibility in handling various classification tasks, and potential for\n",
"customization make it a valuable tool for text classification, as evidenced by\n",
"several studies in the literature.\n",
"8\n",
"\n",
"Page: 7\n",
"\n",
"==================================================\n",
"ollama embedding\n",
"==================================================\n",
@@ -304,6 +320,21 @@
"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",
"While ChatGPT did not perform as well as commercial systems on biomedical\n",
"abstracts or Reddit comments, it may be a good speech translator. Prieto et\n",
"al. [29] evaluated the use of ChatGPT in developing an automated construction\n",
@@ -318,22 +349,11 @@
"ChatGPT. The specific operation is to input tweets with intimacy rating tags\n",
"into ChatGPT and then output similar tweets.\n",
"\n",
"Page: 10\n",
"\n",
"[chunk 2]\n",
"have been widely used for text classification, recent advances in natural lan-\n",
"guage processing have led to the development of more advanced techniques.\n",
"ChatGPT has shown immense potential in this field. Its ability to accurately\n",
"classify text, flexibility in handling various classification tasks, and potential for\n",
"customization make it a valuable tool for text classification, as evidenced by\n",
"several studies in the literature.\n",
"8\n",
"\n",
"Page: 7\n"
"Page: 10\n"
]
}
],
"execution_count": 29
"execution_count": 6
},
{
"cell_type": "markdown",
@@ -356,8 +376,8 @@
"id": "f6d1c603",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:55.524813761Z",
"start_time": "2026-06-01T06:01:52.235129529Z"
"end_time": "2026-06-02T00:39:10.309479368Z",
"start_time": "2026-06-02T00:39:06.998428714Z"
}
},
"source": [
@@ -436,8 +456,9 @@
"•\n",
"•\n",
"•\n",
"•\n",
"\n",
"Page: 16\n",
"Page: 4\n",
"\n",
"==================================================\n",
"similarity\n",
@@ -452,6 +473,14 @@
"Page: 14\n",
"\n",
"[chunk 1]\n",
"마케팅\n",
"마켓 센싱 및 정보 분석 결과를 바탕으로\n",
" 당사 제품과 서비스의 차별화 가치를 소비자에게 효과적인\n",
"커뮤니케이션 방법으로 전달하여 목표한 경영성과를 창출하고 브랜드 가치를 제고합니다\n",
"\n",
"Page: 14\n",
"\n",
"[chunk 2]\n",
"국내영업마케팅\n",
"국내의 각 분야별 영업 채널을 발굴\n",
" 지원하여 성과 창출과 지속 성장을 추구하는 동시에\n",
@@ -462,7 +491,18 @@
"\n",
"Page: 24\n",
"\n",
"[chunk 2]\n",
"[chunk 3]\n",
"국내영업마케팅\n",
"국내의 각 분야별 영업 채널을 발굴\n",
" 지원하여 성과 창출과 지속 성장을 추구하는 동시에\n",
" 한국 시장에\n",
"대한 심도있는 분석을 통해 삼성전자\n",
" 부문 제품의 마케팅 전략을 수립 ⋅ 적용하고 글로벌\n",
"시장으로의 확산 기반을 마련합니다\n",
"\n",
"Page: 24\n",
"\n",
"[chunk 4]\n",
"해외영업\n",
"고객과 시장\n",
" 제품에 대한 이해를 바탕으로 시장 수요와 경쟁환경을 분석하여 국가\n",
@@ -472,32 +512,11 @@
" 영업 채널을 발굴하고 판매전략 수립 및 실행을 통해 매출 극대화에\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",
"Page: 12\n"
"Page: 18\n"
]
}
],
"execution_count": 30
"execution_count": 7
},
{
"cell_type": "markdown",
@@ -514,21 +533,21 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:55.561174427Z",
"start_time": "2026-06-01T06:01:55.548538686Z"
"end_time": "2026-06-02T00:39:10.354728792Z",
"start_time": "2026-06-02T00:39:10.323419955Z"
}
},
"cell_type": "code",
"source": "# !pip install lark",
"id": "6b910cf56efea998",
"outputs": [],
"execution_count": 31
"execution_count": 8
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:55.578983775Z",
"start_time": "2026-06-01T06:01:55.562562234Z"
"end_time": "2026-06-02T00:39:10.379517355Z",
"start_time": "2026-06-02T00:39:10.363143821Z"
}
},
"cell_type": "code",
@@ -567,15 +586,15 @@
],
"id": "b3a603a71973fd7b",
"outputs": [],
"execution_count": 32
"execution_count": 9
},
{
"cell_type": "code",
"id": "c0c3f024",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:56.039825217Z",
"start_time": "2026-06-01T06:01:55.579865933Z"
"end_time": "2026-06-02T00:39:10.830552895Z",
"start_time": "2026-06-02T00:39:10.383461062Z"
}
},
"source": [
@@ -592,15 +611,15 @@
")"
],
"outputs": [],
"execution_count": 33
"execution_count": 10
},
{
"cell_type": "code",
"id": "6eab886a",
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:57.071606870Z",
"start_time": "2026-06-01T06:01:56.040788182Z"
"end_time": "2026-06-02T00:39:11.872173033Z",
"start_time": "2026-06-02T00:39:10.860932404Z"
}
},
"source": [
@@ -611,22 +630,23 @@
{
"data": {
"text/plain": [
"[Document(id='332a384f-1b79-4f7e-ab61-639c5a1bf1b6', metadata={'department': 'ai', 'year': 2024}, page_content='AI 연구 개발 직무입니다.'),\n",
" Document(id='bc41054e-39de-4e78-9597-493201c869e8', metadata={'department': 'ai', 'year': 2024}, page_content='AI 연구 개발 직무입니다.')]"
"[Document(id='332a384f-1b79-4f7e-ab61-639c5a1bf1b6', metadata={'year': 2024, 'department': 'ai'}, page_content='AI 연구 개발 직무입니다.'),\n",
" Document(id='bc41054e-39de-4e78-9597-493201c869e8', metadata={'year': 2024, 'department': 'ai'}, page_content='AI 연구 개발 직무입니다.'),\n",
" Document(id='c6040e27-f3d2-4d48-9f70-7835f32c25d2', metadata={'year': 2024, 'department': 'ai'}, page_content='AI 연구 개발 직무입니다.')]"
]
},
"execution_count": 34,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 34
"execution_count": 11
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:01:59.388963998Z",
"start_time": "2026-06-01T06:01:57.081329932Z"
"end_time": "2026-06-02T00:39:12.635032714Z",
"start_time": "2026-06-02T00:39:11.893548831Z"
}
},
"cell_type": "code",
@@ -703,13 +723,13 @@
],
"id": "fb4c8d00756e3188",
"outputs": [],
"execution_count": 35
"execution_count": 12
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:02:00.353047278Z",
"start_time": "2026-06-01T06:01:59.400345248Z"
"end_time": "2026-06-02T00:39:15.110354021Z",
"start_time": "2026-06-02T00:39:12.652654064Z"
}
},
"cell_type": "code",
@@ -721,22 +741,22 @@
"text/plain": [
"[Document(id='29b45694-d674-4b55-a219-cbec31c2acb3', metadata={'year': 2024, 'user_rating': 4, 'category': '메이크업'}, page_content='롱래스팅 립스틱, 선명한 발색과 촉촉한 사용감으로 하루종일 편안하게 사용 가능합니다.'),\n",
" Document(id='92fc368a-9463-41dd-93fb-875935baecfc', metadata={'year': 2024, 'user_rating': 4, 'category': '메이크업'}, page_content='롱래스팅 립스틱, 선명한 발색과 촉촉한 사용감으로 하루종일 편안하게 사용 가능합니다.'),\n",
" Document(id='b5ed0349-3d3f-43d7-b068-26453a40c5e3', metadata={'year': 2024, 'user_rating': 4, 'category': '메이크업'}, page_content='롱래스팅 립스틱, 선명한 발색과 촉촉한 사용감으로 하루종일 편안하게 사용 가능합니다.'),\n",
" Document(id='b0cb742e-89e6-444c-a73b-297b15499edb', metadata={'user_rating': 4, 'category': '메이크업', 'year': 2024}, page_content='롱래스팅 립스틱, 선명한 발색과 촉촉한 사용감으로 하루종일 편안하게 사용 가능합니다.')]"
" Document(id='b0cb742e-89e6-444c-a73b-297b15499edb', metadata={'user_rating': 4, 'year': 2024, 'category': '메이크업'}, page_content='롱래스팅 립스틱, 선명한 발색과 촉촉한 사용감으로 하루종일 편안하게 사용 가능합니다.'),\n",
" Document(id='03c5e2ec-4887-4f57-a401-f365e8c901b8', metadata={'year': 2024, 'user_rating': 4, 'category': '메이크업'}, page_content='롱래스팅 립스틱, 선명한 발색과 촉촉한 사용감으로 하루종일 편안하게 사용 가능합니다.')]"
]
},
"execution_count": 36,
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 36
"execution_count": 13
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:02:01.459237247Z",
"start_time": "2026-06-01T06:02:00.364375827Z"
"end_time": "2026-06-02T00:39:16.109816938Z",
"start_time": "2026-06-02T00:39:15.126701645Z"
}
},
"cell_type": "code",
@@ -746,23 +766,23 @@
{
"data": {
"text/plain": [
"[Document(id='73206df1-3067-4695-99c8-ae114a9a150f', metadata={'user_rating': 5, 'category': '선케어', 'year': 2025}, page_content='자외선 차단 기능이 있는 톤업 선크림, SPF50+/PA+++ 높은 자외선 차단 지수로 피부를 보호합니다.'),\n",
" Document(id='46277a86-d6d7-4b0f-b0e9-234037e24c7f', metadata={'year': 2025, 'user_rating': 5, 'category': '선케어'}, page_content='자외선 차단 기능이 있는 톤업 선크림, SPF50+/PA+++ 높은 자외선 차단 지수로 피부를 보호합니다.'),\n",
" Document(id='54a8a7dd-41fa-41b0-8909-2dff86f20edf', metadata={'user_rating': 5, 'year': 2025, 'category': '선케어'}, page_content='자외선 차단 기능이 있는 톤업 선크림, SPF50+/PA+++ 높은 자외선 차단 지수로 피부를 보호합니다.')]"
"[Document(id='73206df1-3067-4695-99c8-ae114a9a150f', metadata={'year': 2025, 'user_rating': 5, 'category': '선케어'}, page_content='자외선 차단 기능이 있는 톤업 선크림, SPF50+/PA+++ 높은 자외선 차단 지수로 피부를 보호합니다.'),\n",
" Document(id='68eaaa43-d8c6-4348-bc7b-b02581db5ebd', metadata={'user_rating': 5, 'category': '선케어', 'year': 2025}, page_content='자외선 차단 기능이 있는 톤업 선크림, SPF50+/PA+++ 높은 자외선 차단 지수로 피부를 보호합니다.'),\n",
" Document(id='54a8a7dd-41fa-41b0-8909-2dff86f20edf', metadata={'category': '선케어', 'year': 2025, 'user_rating': 5}, page_content='자외선 차단 기능이 있는 톤업 선크림, SPF50+/PA+++ 높은 자외선 차단 지수로 피부를 보호합니다.')]"
]
},
"execution_count": 37,
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 37
"execution_count": 14
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:02:02.583400765Z",
"start_time": "2026-06-01T06:02:01.468480998Z"
"end_time": "2026-06-02T00:39:17.402896437Z",
"start_time": "2026-06-02T00:39:16.139386682Z"
}
},
"cell_type": "code",
@@ -801,26 +821,26 @@
]
}
],
"execution_count": 38
"execution_count": 15
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:02:02.604875068Z",
"start_time": "2026-06-01T06:02:02.595986461Z"
"end_time": "2026-06-02T00:39:17.438770596Z",
"start_time": "2026-06-02T00:39:17.418100891Z"
}
},
"cell_type": "code",
"source": "# !pip install easyocr pymupdf",
"id": "b69c53f3cad97652",
"outputs": [],
"execution_count": 39
"execution_count": 16
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:02:12.517537371Z",
"start_time": "2026-06-01T06:02:02.606079923Z"
"end_time": "2026-06-02T00:39:26.941862357Z",
"start_time": "2026-06-02T00:39:17.440484055Z"
}
},
"cell_type": "code",
@@ -836,13 +856,13 @@
],
"id": "a1b6e2d07785856c",
"outputs": [],
"execution_count": 40
"execution_count": 17
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:02:32.941472721Z",
"start_time": "2026-06-01T06:02:12.532495528Z"
"end_time": "2026-06-02T00:40:05.375160693Z",
"start_time": "2026-06-02T00:39:26.958881638Z"
}
},
"cell_type": "code",
@@ -860,6 +880,8 @@
"name": "stderr",
"output_type": "stream",
"text": [
"/home/cooney/Source/.venv/lib/python3.12/site-packages/torch/cuda/__init__.py:187: UserWarning: CUDA initialization: The NVIDIA driver on your system is too old (found version 12020). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver. (Triggered internally at /pytorch/c10/cuda/CUDAFunctions.cpp:119.)\n",
" return torch._C._cuda_getDeviceCount() > 0\n",
"Neither CUDA nor MPS are available - defaulting to CPU. Note: This module is much faster with a GPU.\n",
"/home/cooney/Source/.venv/lib/python3.12/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n",
" super().__init__(loader)\n"
@@ -886,13 +908,13 @@
]
}
],
"execution_count": 41
"execution_count": 18
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:22.345757243Z",
"start_time": "2026-06-01T06:02:32.966935973Z"
"end_time": "2026-06-02T00:49:24.188537816Z",
"start_time": "2026-06-02T00:40:05.530931311Z"
}
},
"cell_type": "code",
@@ -980,7 +1002,7 @@
]
}
],
"execution_count": 42
"execution_count": 19
},
{
"metadata": {},
@@ -999,21 +1021,21 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:22.373312793Z",
"start_time": "2026-06-01T06:11:22.360301676Z"
"end_time": "2026-06-02T00:49:24.236000469Z",
"start_time": "2026-06-02T00:49:24.204889072Z"
}
},
"cell_type": "code",
"source": "# !pip install rank-bm25",
"id": "f183e2922317e17e",
"outputs": [],
"execution_count": 43
"execution_count": 20
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:22.390318200Z",
"start_time": "2026-06-01T06:11:22.374466640Z"
"end_time": "2026-06-02T00:49:24.265639045Z",
"start_time": "2026-06-02T00:49:24.238260865Z"
}
},
"cell_type": "code",
@@ -1034,13 +1056,13 @@
],
"id": "31092e4f4e4e36a4",
"outputs": [],
"execution_count": 44
"execution_count": 21
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:25.537705779Z",
"start_time": "2026-06-01T06:11:22.392052726Z"
"end_time": "2026-06-02T00:49:27.025679698Z",
"start_time": "2026-06-02T00:49:24.268751872Z"
}
},
"cell_type": "code",
@@ -1055,13 +1077,13 @@
],
"id": "c43289aff540dd23",
"outputs": [],
"execution_count": 45
"execution_count": 22
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:25.910014053Z",
"start_time": "2026-06-01T06:11:25.549877300Z"
"end_time": "2026-06-02T00:49:27.475999389Z",
"start_time": "2026-06-02T00:49:27.039390529Z"
}
},
"cell_type": "code",
@@ -1113,11 +1135,6 @@
"Page: 7\n",
"\n",
"[chunk 2]\n",
"S/W개발 소프트웨어 기술에 대한 전문적인 지식올 기반으로 창의적이고 분석적인 사고름 통해 신기술올 선도하고 당사 제품에 반영함으로써 제품 및 슬루선의 학신적인 가치름 창출합니다.\n",
"\n",
"Page: 6\n",
"\n",
"[chunk 3]\n",
"커리어 비전 Career Vision\n",
"휴머노이드 로봇의 초기 개발 단계부터 참여하여 핵심 개발자로 성장할 수 있습니다: 실시간 제어; 시스템 아키택처 , 로봇 미들웨어 등 다양한 경험올 통해 로보텍스 시스템 SW 전문가, 나아가 시스템 아키넥트로의 커리어 확장이 가능합니다.\n",
"66 휴머노이드의 미래틀 함께 설계할 분올 찾습니다\"\n",
@@ -1125,7 +1142,7 @@
"\n",
"Page: 7\n",
"\n",
"[chunk 4]\n",
"[chunk 3]\n",
"삼성전자 DX부문 2026년 상반기 3급 신입사원 채용 공고\n",
"직무\n",
"포지선\n",
@@ -1190,7 +1207,7 @@
]
}
],
"execution_count": 46
"execution_count": 23
},
{
"metadata": {},
@@ -1206,21 +1223,21 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:25.921258082Z",
"start_time": "2026-06-01T06:11:25.912352627Z"
"end_time": "2026-06-02T00:49:27.485106716Z",
"start_time": "2026-06-02T00:49:27.477613705Z"
}
},
"cell_type": "code",
"source": "# !pip install cohere langchain-cohere sentence-transformers",
"id": "5b6a77f06c3e8d",
"outputs": [],
"execution_count": 47
"execution_count": 24
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:27.283337982Z",
"start_time": "2026-06-01T06:11:25.924604161Z"
"end_time": "2026-06-02T00:49:31.034751822Z",
"start_time": "2026-06-02T00:49:27.485893002Z"
}
},
"cell_type": "code",
@@ -1274,24 +1291,25 @@
"Page: 28\n",
"\n",
"[chunk 2]\n",
"MX사업부(수원 근무) 마켓팅 제품) 서비스 마게팅\n",
"네트위크사업부(수원 근무) 구매\n",
"포지션 소개 Job Overview\n",
"디바이스(Smartphone Watch Buds Tablet 등) 경험 및 핵심 차별화지 기획/상품화하고 고객과의 효과적인 커류니키이선 전락으로 경영성과 및 브랜드가치틀 제고하는 직무입니다:\n",
"글로벌 SCM 데이터지 기반으로 구매 프로세스트 학신하고 최적의 파트너십올 통해 사업의 이익과 경쟁력올 극대화합니다.\n",
"수행업무 Job Details\n",
"브랜드 관리 신제품 런칭 등 마켓팅 커류니키이선 전락올 수립하고 실행합니다: 온라인-디지털 마켓팅 전락올 수립하고 캠페인올 개발합니다. 런칭 이번트 전시, 홍보 영상 손렌즈틀 기획합니다: 시장 소비자 경쟁사 기술 분석올 통한 기회 요인올 발굴하고 제품 전락올 수립합니다:\n",
"부품별 구매 Lead Time올 효율적으로 관리해 제품의 적시 생산과 공급올 지원하고 원자재 적정 재고 운영으로 사업부 실적올 극대화합니다 부품의 핵심 원자재와 생산 공정올 분석하여 공급 Lead Time 및 가용량울 최적화합니다. 제품의 생산/판매 계획 및 중장기 물동 변화에 긴밀하게 대응하다, 이와 연계하여 부품 Forecast틀 관리합니다:\n",
"자격요건 Requirements\n",
"해외 협력사와의 소통올 위한 글로벌 커류니키이선 능력올 보유하신 분(영어 및 제2외국어) 최신 네트위크 기술 동향 파악, 비즈니스 현상 전락 공급망 관리(SCM)에 대한 높은 이해도와 관심올 보유하신 분 내부 팀위크 및 협력사 간 원활한 현업으로 목표름 달성하는 협력적 태도와 실행력올 보유하신 분\n",
"\n",
"Page: 18\n",
"Page: 28\n",
"\n",
"[chunk 3]\n",
"MX사업부(수원 근무) 마켓팅 제품) 서비스 마게팅\n",
"포지션 소개 Job Overview\n",
"디바이스(Smartphone Watch Buds Tablet 등) 경험 및 핵심 차별화지 기획/상품화하고 고객과의 효과적인 커류니키이선 전락으로 경영성과 및 브랜드가치틀 제고하는 직무입니다:\n",
"수행업무 Job Details\n",
"브랜드 관리 신제품 런칭 등 마켓팅 커류니키이선 전락올 수립하고 실행합니다: 온라인-디지털 마켓팅 전락올 수립하고 캠페인올 개발합니다. 런칭 이번트 전시, 홍보 영상 손렌즈틀 기획합니다: 시장 소비자 경쟁사 기술 분석올 통한 기회 요인올 발굴하고 제품 전락올 수립합니다:\n",
"자격요건 Requirements\n",
"우대사항 Preferences\n",
"설비 직무 관련 국가기술자격올 보유하신 분(기사 이상) 직무 관련 대내외 경험흘 보유하신 분(프로적트 논문 특히, 경진대회 등) 해외법인과 커유니키이선이 가능한 수준의 외국어(영어, 중국어) 회화 역량울 보유하신 분\n",
"커리어 비전 Career Vision\n",
"인프라 분야의 전문역량울 통한 사고 예방 및 잠재 리스크트 개선하는 환경안전 전문가로 성장합니다.\n",
"66 '인프라틀 지팅하다\"\n",
"안정적인 시스템 운영으로 조직의 기반을 함께 만들어칼 인재틀 찾고 있습니다. 책임감과 실행력올 갖춘 인프라 운영 인재틀 기다럽니다!\n",
"\n",
"Page: 18\n",
"Page: 31\n",
"\n",
"[chunk 4]\n",
"우대사항 Preferences\n",
@@ -1305,7 +1323,7 @@
]
}
],
"execution_count": 48
"execution_count": 25
},
{
"metadata": {},
@@ -1320,8 +1338,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:40.147062119Z",
"start_time": "2026-06-01T06:11:27.294726241Z"
"end_time": "2026-06-02T00:49:47.805214037Z",
"start_time": "2026-06-02T00:49:31.058423510Z"
}
},
"cell_type": "code",
@@ -1614,34 +1632,32 @@
"제주어 뜻풀이\n",
"무신걸 먹으코? 무엇을 먹을까?\n",
"----------\n",
"생활 속 제주어\n",
"제주는 타 지역보다 한국어의 고형(古形)을 많이 유지하고 있는 동시에 \n",
"제주도만의 고유한 어휘나 문법적 특성을 가지고 있다.\n",
"다른 지역 사람이 못 알아듣는 제주어\n",
"제주어 뜻풀이\n",
"솔쩨기 살짝\n",
"안네다 드리다\n",
"베지근허다 입안에 기름기가 감돌아 맛이 있다.\n",
"엄불랑허다 어마어마하다\n",
"코시롱허다 고소하다\n",
"산도록허다 시원하다 예) 물이 산도록헌 게 좋다.\n",
"두령청이 우두망찰\n",
"무사 왜\n",
"영, 경, 정 이렇게, 그렇게, 저렇게\n",
"게메 글쎄\n",
"인사말\n",
"제주어 뜻풀이\n",
"펜안허우꽈? 편안(안녕)하십니까?\n",
"제주도 오난 어떵허우꽈? 제주도에 오니 어떠십니까?\n",
"차말로 좋수다. 참말로 좋습니다.\n",
"공기도 마고, 산이영 바다잉여 마딱 좋은게마씀 공기도 맑고, 산이랑 바다랑 모두 좋네요.\n",
"서울 갈 때랑 하영 다앙 갑서. 서울 갈 때는 많이 담아서 가십시오.\n",
"게메양. 경 헤시민 얼마나 좋코마씀? 글쎄요. 그렇게 했으면 얼마나 좋겠습니까?\n",
"식당에서\n",
"제주어 뜻풀이\n",
"무신걸 먹으코? 무엇을 먹을까?\n",
"게메양, 제주도에만 이신 거 먹게마씀. 글쎄요, 제주도에만 있는 것 먹게요.\n",
"구젱기영 무꾸럭이영 오토미영 하간 거 다 잇수다. 소라랑 문어랑 옥돔이랑 온갖 거 다 있습니다.\n",
"모멀펌벅에 자리젯! 메밀범벅에 자리젓!\n",
"먹을 것도 잘도 하신게. 먹을 것도 정말(엄청) 많네.\n",
"타 지역과 의미가 다른 제주어\n",
"제주어 뜻풀이\n",
"감저 고구마\n",
"지슬, 지실 감자\n",
"-허게 하자(청유의 의미) 공부허게: 공부하자\n",
"글라 가자 예) 장에 글라.(장에 가자.)\n",
"호미 낫\n",
"가겡이, 가각지, 가게 호미\n",
"폭삭 속앗수다 무척 수고하셨습니다.\n",
"삼춘 ①삼촌 ②나이 든 남자 어른이나 여자 어른을 부르는 말.\n",
"요망지다 야무지다\n",
"아 까다 아깝다(사랑스럽고 귀엽다)\n",
"젊은 층도 자주 사용하는 제주어\n",
"제주어 뜻풀이\n",
"뭐허멘? 뭐하니?\n",
"가이, 자이, 야이 걔, 쟤, 얘\n",
"기 그래\n",
"뒌 됐어\n",
"허쿠다 하겠습니다\n",
"잘도 정말, 엄청\n",
"벨라지다 바라지다\n",
"----------\n",
"식당에서\n",
"제주어 뜻풀이\n",
@@ -1907,13 +1923,13 @@
]
}
],
"execution_count": 49
"execution_count": 26
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:11:49.408671064Z",
"start_time": "2026-06-01T06:11:40.158244697Z"
"end_time": "2026-06-02T00:49:59.175454898Z",
"start_time": "2026-06-02T00:49:47.825460785Z"
}
},
"cell_type": "code",
@@ -1953,11 +1969,13 @@
"엄불랑허다 어마어마하다\n",
"----------\n",
"엄불랑허다 어마어마하다\n",
"----------\n",
"엄불랑허다 어마어마하다\n",
"----------\n"
]
}
],
"execution_count": 50
"execution_count": 27
},
{
"metadata": {},
@@ -1971,8 +1989,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:12:01.533718730Z",
"start_time": "2026-06-01T06:11:49.425220960Z"
"end_time": "2026-06-02T00:50:09.276173628Z",
"start_time": "2026-06-02T00:49:59.190701406Z"
}
},
"cell_type": "code",
@@ -2013,17 +2031,19 @@
"엄불랑허다 어마어마하다\n",
"----------\n",
"엄불랑허다 어마어마하다\n",
"----------\n",
"엄불랑허다 어마어마하다\n",
"----------\n"
]
}
],
"execution_count": 51
"execution_count": 28
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:42:11.875385837Z",
"start_time": "2026-06-01T06:42:09.608650607Z"
"end_time": "2026-06-02T00:50:10.288867811Z",
"start_time": "2026-06-02T00:50:09.287890271Z"
}
},
"cell_type": "code",
@@ -2039,12 +2059,12 @@
"0"
]
},
"execution_count": 62,
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 62
"execution_count": 29
},
{
"metadata": {},
@@ -2065,8 +2085,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:42:46.871672538Z",
"start_time": "2026-06-01T06:42:11.907350725Z"
"end_time": "2026-06-02T00:50:44.214523942Z",
"start_time": "2026-06-02T00:50:10.304249553Z"
}
},
"cell_type": "code",
@@ -2089,18 +2109,18 @@
"0"
]
},
"execution_count": 63,
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 63
"execution_count": 30
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:17:18.796393174Z",
"start_time": "2026-06-01T06:16:46.321999203Z"
"end_time": "2026-06-02T00:51:18.008958897Z",
"start_time": "2026-06-02T00:50:44.231442765Z"
}
},
"cell_type": "code",
@@ -2114,13 +2134,13 @@
],
"id": "e6cdbf960d07d310",
"outputs": [],
"execution_count": 57
"execution_count": 31
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:17:41.563476638Z",
"start_time": "2026-06-01T06:17:22.716593783Z"
"end_time": "2026-06-02T00:51:38.353190588Z",
"start_time": "2026-06-02T00:51:18.020849964Z"
}
},
"cell_type": "code",
@@ -2136,13 +2156,13 @@
],
"id": "5e7e9f9389a952fb",
"outputs": [],
"execution_count": 58
"execution_count": 32
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:18:14.356911918Z",
"start_time": "2026-06-01T06:17:54.471197025Z"
"end_time": "2026-06-02T00:52:03.209537974Z",
"start_time": "2026-06-02T00:51:38.370120389Z"
}
},
"cell_type": "code",
@@ -2184,13 +2204,13 @@
]
}
],
"execution_count": 59
"execution_count": 33
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:42:09.606591214Z",
"start_time": "2026-06-01T06:19:04.795058972Z"
"end_time": "2026-06-02T01:22:56.350924696Z",
"start_time": "2026-06-02T00:52:03.223503871Z"
}
},
"cell_type": "code",
@@ -2350,17 +2370,19 @@
"/home/cooney/Source/.venv/lib/python3.12/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n",
" super().__init__(loader)\n",
"/home/cooney/Source/.venv/lib/python3.12/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n",
" super().__init__(loader)\n",
"/home/cooney/Source/.venv/lib/python3.12/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n",
" super().__init__(loader)\n"
]
}
],
"execution_count": 61
"execution_count": 34
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:43:57.332143726Z",
"start_time": "2026-06-01T06:43:57.323464186Z"
"end_time": "2026-06-02T01:22:56.379584524Z",
"start_time": "2026-06-02T01:22:56.364430340Z"
}
},
"cell_type": "code",
@@ -2381,13 +2403,13 @@
],
"id": "ca49b61be9b3a884",
"outputs": [],
"execution_count": 67
"execution_count": 35
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:44:20.562275714Z",
"start_time": "2026-06-01T06:44:14.780140981Z"
"end_time": "2026-06-02T01:23:03.094530621Z",
"start_time": "2026-06-02T01:22:56.381266Z"
}
},
"cell_type": "code",
@@ -2402,13 +2424,13 @@
],
"id": "3b6e9435b199c2f2",
"outputs": [],
"execution_count": 69
"execution_count": 36
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:50:20.276558921Z",
"start_time": "2026-06-01T06:50:20.261334580Z"
"end_time": "2026-06-02T01:23:03.149472417Z",
"start_time": "2026-06-02T01:23:03.110209373Z"
}
},
"cell_type": "code",
@@ -2420,7 +2442,7 @@
],
"id": "7b39c5cb6fa77996",
"outputs": [],
"execution_count": 71
"execution_count": 37
}
],
"metadata": {
+169
View File
@@ -0,0 +1,169 @@
import gradio as gr
from langchain_community.document_loaders import PyPDFLoader, CSVLoader, TextLoader, UnstructuredWordDocumentLoader, \
Docx2txtLoader, UnstructuredExcelLoader
from dotenv import load_dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_ibm import WatsonxEmbeddings
from langchain_ollama import OllamaEmbeddings
from pathlib import Path
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
import os
import shutil
# 모델(LLM, Embeddding)
load_dotenv()
apikey = os.getenv("WATSONX_API_KEY")
project_id = os.getenv("WATSONX_PROJECT_ID")
watsonx_ai_url = os.getenv("WATSONX_URL")
watson_embedding = WatsonxEmbeddings(
model_id="ibm/granite-embedding-278m-multilingual",
url = f"{watsonx_ai_url}",
api_key = f"{apikey}",
project_id=f"{project_id}"
)
ollama_embedding = OllamaEmbeddings(model="nomic-embed-text-v2-moe")
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
LOADERS = {
".pdf" : PyPDFLoader,
".csv" : CSVLoader,
".docx" : UnstructuredWordDocumentLoader,
".xlsx" : UnstructuredExcelLoader,
".txt" : TextLoader,
}
CHROMA_DIR = "./db/chroma"
COLLECTION_NAME = "job_rag"
CHUNKS_PATH = "./db/chunks.pkl"
DOCUMENTS = []
CHUNKS = []
VECTORSTORE = None
# ==========
# Tap 1 - 기능 구현
# ==========
def extract_metadata(file_path):
# 2026 상 삼성 E&A 직무기술서
# {year:2026, recruitment_period:상반기, company:삼성E&A, file_name:2026 상 삼성E&A 직무기술서}
# 확장자를 제외한 파일명
name = file_path.name
datas = name.split()
return {
"year": int(datas[0]),
"recruitment_period": datas[1] + "반기",
"company": datas[2],
"file_name": name
}
def upload_files(files):
"""
여러 개의 파일(pdf, csv)이 업로드 될 때 각 파일을 load() 한 결과는 DOCUMENTS 추가
몇 개의 문서가 업로드 되었는지 리턴
확장자 분리
"""
global DOCUMENTS
all_docs = []
for file in files:
# 파일명 가져오기
path = Path(file.name)
# 확장자 가져오기
ext = path.suffix.lower()
loader = LOADERS[ext](file.name)
docs = loader.load()
# metadata 정리
meta_info = extract_metadata(path)
# metadata 업데이트
for doc in docs:
doc.metadata.update(meta_info)
all_docs.extend(docs)
DOCUMENTS = all_docs
return f"문서 수 : {len(all_docs)}"
def preview_chunks():
global DOCUMENTS
global CHUNKS
if not DOCUMENTS:
return "문서가 없음."
# 전체문서는 DOCUMENTS 안에 있음
# 분리
CHUNKS = splitter.split_documents(DOCUMENTS)
# 청크 10개 까지만 내용 출력
preview = []
for i, chunk in enumerate(CHUNKS[:10]):
preview.append(f"""[CHUNK {i + 1}]{chunk.page_content[:100]}\n
""")
return "\n\n".join(preview)
def build_vectorstore():
global VECTORSTORE
global CHUNKS
if not CHUNKS:
return "먼저 CHUNK를 생성하세요."
# 기존의 VECTORSTORE가 있다면 제거
if Path(CHROMA_DIR).exists():
shutil.rmtree(CHROMA_DIR)
VECTORSTORE = Chroma.from_documents(documents=CHUNKS,
embedding=watson_embedding,
persist_directory=CHROMA_DIR,
collection_name=COLLECTION_NAME
)
return f"""
생성 완료
Chunk: {len(CHUNKS)}
Vector: {VECTORSTORE._collection.count()}
"""
# ==========
# Gradio UI
# ==========
with gr.Blocks() as app:
gr.Markdown("# 사내 문서 RAG")
with gr.Tab("문서관리"):
files = gr.File(file_count = "multiple")
upload_btn = gr.Button("문서 업로드")
upload_status = gr.Textbox()
upload_btn.click(upload_files, files, upload_status)
chunk_btn = gr.Button("chunk 확인")
chunk_preview = gr.Textbox(lines = 20)
chunk_btn.click(preview_chunks, outputs = chunk_preview)
vector_btn = gr.Button("vector DB 생성")
vector_status = gr.Textbox()
vector_btn.click(build_vectorstore, outputs = vector_status)
with gr.Tab("검색 테스트"):
pass
with gr.Tab("RAG 채팅"):
pass
pass
if __name__ =="__main__":
app.launch()
+422
View File
@@ -0,0 +1,422 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2026-06-02T03:50:47.852125092Z",
"start_time": "2026-06-02T03:50:47.832396803Z"
}
},
"source": [
"from langchain_community.document_loaders import WebBaseLoader\n",
"from langchain_core.output_parsers import StrOutputParser\n",
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
"from langchain_ollama import OllamaEmbeddings\n",
"from langchain_ibm import WatsonxEmbeddings\n",
"from langchain_community.vectorstores import FAISS\n",
"from dotenv import load_dotenv\n",
"from langchain_core.prompts import ChatPromptTemplate\n",
"import os\n",
"\n",
"from langchain_ibm import ChatWatsonx\n",
"from langchain_openai import ChatOpenAI\n",
"from langchain_ollama import OllamaEmbeddings\n",
"from langchain_ollama import ChatOllama\n",
"\n",
"import re\n",
"from pprint import pprint\n",
"\n",
"from langchain_core.runnables import RunnablePassthrough\n",
"\n",
"import bs4\n",
"from langchain_core.output_parsers import StrOutputParser\n",
"\n",
"from ollama.langchain2 import response"
],
"outputs": [],
"execution_count": 43
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:44:09.707045677Z",
"start_time": "2026-06-02T03:44:09.697000370Z"
}
},
"cell_type": "code",
"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\")\n",
"COHERE_API_KEY = os.getenv(\"COHERE_API_KEY\")"
],
"id": "d4bc0a026e912906",
"outputs": [],
"execution_count": 36
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:44:14.700002071Z",
"start_time": "2026-06-02T03:44:11.309956458Z"
}
},
"cell_type": "code",
"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)\n",
"gemma_llm = ChatOllama(model=\"gemma4:e2b\")"
],
"id": "48539ccea85e6efa",
"outputs": [],
"execution_count": 37
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:05:38.186413975Z",
"start_time": "2026-06-02T03:05:37.171649717Z"
}
},
"cell_type": "code",
"source": "# !pip install beautifulsoup4",
"id": "2ebcb60fb5e10905",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: beautifulsoup4 in /home/cooney/Source/.venv/lib/python3.12/site-packages (4.14.3)\n",
"Requirement already satisfied: soupsieve>=1.6.1 in /home/cooney/Source/.venv/lib/python3.12/site-packages (from beautifulsoup4) (2.8.4)\n",
"Requirement already satisfied: typing-extensions>=4.0.0 in /home/cooney/Source/.venv/lib/python3.12/site-packages (from beautifulsoup4) (4.15.0)\n"
]
}
],
"execution_count": 3
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:11:27.807059823Z",
"start_time": "2026-06-02T03:11:27.632888644Z"
}
},
"cell_type": "code",
"source": [
"web_loader = WebBaseLoader(web_paths=['https://n.news.naver.com/mnews/article/138/0002229649'], bs_kwargs=dict(parse_only=bs4.SoupStrainer('article', attrs= {\"id\" : \"dic_area\"})))\n",
"web_docs = web_loader.load()\n",
"\n",
"print(f\"총 페이지 수 : {len(web_docs)}\")\n",
"print(web_docs)\n",
"# print(f\"첫 페이지 텍스트 : {web_docs[0].page_content[:200]}\")\n",
"# print(f\"메타 데이터 : {web_docs[0].metadata}\")"
],
"id": "9365295ce3f0b42e",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"총 페이지 수 : 1\n",
"[Document(metadata={'source': 'https://n.news.naver.com/mnews/article/138/0002229649'}, page_content='\\n\\n\\n\\n\\n송경희 개인정보보호위원회 위원장이 5월13일 서울 종로구 정부서울청사에서 전체회의를 열고 모두발언을 하고 있다. [사진=개인정보위][디지털데일리 김보민기자] 개인정보 유출사고 예방과 대응을 강화하는 개인정보보호법 시행령 일부개정령안이 입법 예고됐다.개인정보보호위원회는 개인정보 유출 사전 예방을 강화하기 위해 개정안을 마련해 7월13일까지 입법예고한다고 2일 밝혔다.이번 시행령 개정은 지난 3월 공포된 개정 개인정보보호법 후속조치다. 개정 법률은 개인정보보호책임자(CPO) 권한과 독립성을 강화하고, 주요 개인정보처리자에 대한 개인정보 보호 인증(ISMS-P)을 의무화하는 내용을 담고 있다.개정안은 CPO 지정·변경·해제 시 이사회 의결과 개인정보위 신고 의무가 적용되는 기준을 마련했다.적용 대상은 현행 전문 CPO 지정 의무 대상과 동일하게 정했다. 연 매출액 또는 수입이 1800억원 이상이면서 5만명 이상 민감·고유식별정보 또는 100만명 이상 개인정보를 처리하는 사업자가 대상이다. 재학생 2만명 이상 대학, 상급종합병원, 공공시스템운영기관도 포함된다.신고 의무 대상 기관은 의무 발생일로부터 1개월 이내 개인정보위에 신고서를 제출해야 하고 부득이한 경우 1개월 연장이 가능하다.ISMS-P 인증 의무 대상도 구체화했다. 공공시스템운영기관 중 개인정보위가 정해 고시하는 기관, 이동통신사업자, 본인확인기관, 전년도 매출액 1조원 이상이면서 정보통신서비스 부문 매출 100억원 이상, 국내 정보주체 수가 최근 3개월 일평균 3000만명 이상인 사업자는 2028년 12월31일까지 인증을 받아야 한다.개인정보 유출 통지·신고 기준도 정비됐다. 개인정보 처리시스템에 대한 불법적 접근 사실이나 개인정보 불법 거래·유통 사실을 알게 된 경우 72시간 이내 정보주체에게 통지해야 한다. 기존 분실·도난·유출 외에도 위조·변조·훼손까지 통지·신고 대상에 포함했다.과태료 부과 기준도 손질했다. 경미한 위반행위에 대해 과태료를 면제하고 경고한 경우에도 동일 위반이 재발하면 가중 기준에 반영하도록 했고, 위반 횟수별 과태료 부과금액도 법제처 지침에 맞춰 정비했다.개정안에 대한 의견은 7월13일까지 국민참여입법센터와 개인정보위 전자우편, 일반우편 등을 통해 제출할 수 있다.\\n')]\n"
]
}
],
"execution_count": 8
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:11:54.287952620Z",
"start_time": "2026-06-02T03:11:54.280188113Z"
}
},
"cell_type": "code",
"source": [
"pprint(web_docs[0].page_content)\n",
"pprint(web_docs[0].metadata)"
],
"id": "e70fce3c64af0110",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('\\n'\n",
" '\\n'\n",
" '\\n'\n",
" '\\n'\n",
" '\\n'\n",
" '송경희 개인정보보호위원회 위원장이 5월13일 서울 종로구 정부서울청사에서 전체회의를 열고 모두발언을 하고 있다. '\n",
" '[사진=개인정보위][디지털데일리 김보민기자] 개인정보 유출사고 예방과 대응을 강화하는 개인정보보호법 시행령 일부개정령안이 입법 '\n",
" '예고됐다.개인정보보호위원회는 개인정보 유출 사전 예방을 강화하기 위해 개정안을 마련해 7월13일까지 입법예고한다고 2일 밝혔다.이번 '\n",
" '시행령 개정은 지난 3월 공포된 개정 개인정보보호법 후속조치다. 개정 법률은 개인정보보호책임자(CPO) 권한과 독립성을 강화하고, 주요 '\n",
" '개인정보처리자에 대한 개인정보 보호 인증(ISMS-P)을 의무화하는 내용을 담고 있다.개정안은 CPO 지정·변경·해제 시 이사회 의결과 '\n",
" '개인정보위 신고 의무가 적용되는 기준을 마련했다.적용 대상은 현행 전문 CPO 지정 의무 대상과 동일하게 정했다. 연 매출액 또는 수입이 '\n",
" '1800억원 이상이면서 5만명 이상 민감·고유식별정보 또는 100만명 이상 개인정보를 처리하는 사업자가 대상이다. 재학생 2만명 이상 '\n",
" '대학, 상급종합병원, 공공시스템운영기관도 포함된다.신고 의무 대상 기관은 의무 발생일로부터 1개월 이내 개인정보위에 신고서를 제출해야 '\n",
" '하고 부득이한 경우 1개월 연장이 가능하다.ISMS-P 인증 의무 대상도 구체화했다. 공공시스템운영기관 중 개인정보위가 정해 고시하는 '\n",
" '기관, 이동통신사업자, 본인확인기관, 전년도 매출액 1조원 이상이면서 정보통신서비스 부문 매출 100억원 이상, 국내 정보주체 수가 최근 '\n",
" '3개월 일평균 3000만명 이상인 사업자는 2028년 12월31일까지 인증을 받아야 한다.개인정보 유출 통지·신고 기준도 정비됐다. '\n",
" '개인정보 처리시스템에 대한 불법적 접근 사실이나 개인정보 불법 거래·유통 사실을 알게 된 경우 72시간 이내 정보주체에게 통지해야 한다. '\n",
" '기존 분실·도난·유출 외에도 위조·변조·훼손까지 통지·신고 대상에 포함했다.과태료 부과 기준도 손질했다. 경미한 위반행위에 대해 과태료를 '\n",
" '면제하고 경고한 경우에도 동일 위반이 재발하면 가중 기준에 반영하도록 했고, 위반 횟수별 과태료 부과금액도 법제처 지침에 맞춰 '\n",
" '정비했다.개정안에 대한 의견은 7월13일까지 국민참여입법센터와 개인정보위 전자우편, 일반우편 등을 통해 제출할 수 있다.\\n')\n",
"{'source': 'https://n.news.naver.com/mnews/article/138/0002229649'}\n"
]
}
],
"execution_count": 11
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:14:37.688022045Z",
"start_time": "2026-06-02T03:14:37.662564550Z"
}
},
"cell_type": "code",
"source": [
"content = web_docs[0].page_content.strip()\n",
"content"
],
"id": "532ccf19321dbb55",
"outputs": [
{
"data": {
"text/plain": [
"'송경희 개인정보보호위원회 위원장이 5월13일 서울 종로구 정부서울청사에서 전체회의를 열고 모두발언을 하고 있다. [사진=개인정보위][디지털데일리 김보민기자] 개인정보 유출사고 예방과 대응을 강화하는 개인정보보호법 시행령 일부개정령안이 입법 예고됐다.개인정보보호위원회는 개인정보 유출 사전 예방을 강화하기 위해 개정안을 마련해 7월13일까지 입법예고한다고 2일 밝혔다.이번 시행령 개정은 지난 3월 공포된 개정 개인정보보호법 후속조치다. 개정 법률은 개인정보보호책임자(CPO) 권한과 독립성을 강화하고, 주요 개인정보처리자에 대한 개인정보 보호 인증(ISMS-P)을 의무화하는 내용을 담고 있다.개정안은 CPO 지정·변경·해제 시 이사회 의결과 개인정보위 신고 의무가 적용되는 기준을 마련했다.적용 대상은 현행 전문 CPO 지정 의무 대상과 동일하게 정했다. 연 매출액 또는 수입이 1800억원 이상이면서 5만명 이상 민감·고유식별정보 또는 100만명 이상 개인정보를 처리하는 사업자가 대상이다. 재학생 2만명 이상 대학, 상급종합병원, 공공시스템운영기관도 포함된다.신고 의무 대상 기관은 의무 발생일로부터 1개월 이내 개인정보위에 신고서를 제출해야 하고 부득이한 경우 1개월 연장이 가능하다.ISMS-P 인증 의무 대상도 구체화했다. 공공시스템운영기관 중 개인정보위가 정해 고시하는 기관, 이동통신사업자, 본인확인기관, 전년도 매출액 1조원 이상이면서 정보통신서비스 부문 매출 100억원 이상, 국내 정보주체 수가 최근 3개월 일평균 3000만명 이상인 사업자는 2028년 12월31일까지 인증을 받아야 한다.개인정보 유출 통지·신고 기준도 정비됐다. 개인정보 처리시스템에 대한 불법적 접근 사실이나 개인정보 불법 거래·유통 사실을 알게 된 경우 72시간 이내 정보주체에게 통지해야 한다. 기존 분실·도난·유출 외에도 위조·변조·훼손까지 통지·신고 대상에 포함했다.과태료 부과 기준도 손질했다. 경미한 위반행위에 대해 과태료를 면제하고 경고한 경우에도 동일 위반이 재발하면 가중 기준에 반영하도록 했고, 위반 횟수별 과태료 부과금액도 법제처 지침에 맞춰 정비했다.개정안에 대한 의견은 7월13일까지 국민참여입법센터와 개인정보위 전자우편, 일반우편 등을 통해 제출할 수 있다.'"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 13
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:36:15.015584588Z",
"start_time": "2026-06-02T03:36:15.006843845Z"
}
},
"cell_type": "code",
"source": [
"cleaned_content = re.sub(r'\\[[^\\]]*\\]', '', content)\n",
"processed_content = re.sub(r'\\.\\s*', '.\\n', cleaned_content)\n",
"\n",
"lines = [line.strip() for line in processed_content.split('\\n') if line.strip()]\n",
"\n",
"lines\n",
"title = lines[0]\n",
"\n",
"# [......] 사진 설명 제거 / 당신의 ~~\n",
"\n",
"# page_content 업데이트\n",
"# metadata 업데이트\n",
"\n",
"web_docs[0].page_content = \"\\n\\n\".join(lines[0:2])\n",
"web_docs[0].metadata['title'] = title\n"
],
"id": "469e0b8c4cc20aac",
"outputs": [],
"execution_count": 26
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:36:16.864533058Z",
"start_time": "2026-06-02T03:36:16.856743922Z"
}
},
"cell_type": "code",
"source": [
"print(web_docs[0].metadata)\n",
"print(web_docs[0].page_content)"
],
"id": "45b2ef5fd614e135",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'source': 'https://n.news.naver.com/mnews/article/138/0002229649', 'title': '송경희 개인정보보호위원회 위원장이 5월13일 서울 종로구 정부서울청사에서 전체회의를 열고 모두발언을 하고 있다.'}\n",
"송경희 개인정보보호위원회 위원장이 5월13일 서울 종로구 정부서울청사에서 전체회의를 열고 모두발언을 하고 있다.\n",
"\n",
"개인정보 유출사고 예방과 대응을 강화하는 개인정보보호법 시행령 일부개정령안이 입법 예고됐다.\n"
]
}
],
"execution_count": 27
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:52:13.099155594Z",
"start_time": "2026-06-02T03:52:13.091998776Z"
}
},
"cell_type": "code",
"source": [
"# 분할\n",
"\n",
"splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)\n",
"chunks = splitter.split_documents(web_docs)\n",
"\n",
"len(chunks)"
],
"id": "c889351af85f3ec5",
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 46
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:52:56.443370091Z",
"start_time": "2026-06-02T03:52:56.101549384Z"
}
},
"cell_type": "code",
"source": [
"# 벡터스토어\n",
"\n",
"vectorstore = FAISS.from_documents(documents=chunks, embedding=watson_embedding)\n",
"\n",
"# 디버깅용\n",
"# vectorstore.similarity_search()\n",
"\n",
"retriever = vectorstore.as_retriever(k=3)\n",
"\n",
"rag_prompt = ChatPromptTemplate.from_template(\n",
" \"\"\"\\\n",
" 당신은 뉴스 기사 QA 시스템입니다.\n",
"\n",
" 규칙 :\n",
" 1. 제공된 기사 내용만 사용하세요.2\n",
" 2. 기사 내용에 없는 정보는 추측하지 말고 ' 기사에서 확인할 수 없습니다.'라고 답하세요.\n",
"\n",
" 뉴스 기사?\n",
" {context}\n",
"\n",
" 질문:\n",
" {question}\n",
"\n",
" 답변:\n",
" \"\"\"\n",
")"
],
"id": "663a1120c84272f9",
"outputs": [],
"execution_count": 50
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T03:54:30.858385078Z",
"start_time": "2026-06-02T03:54:29.332531659Z"
}
},
"cell_type": "code",
"source": [
"# 체인생성 질의\n",
"def format_docs(docs):\n",
" \"\"\"Document 객체에서 page_content 추출\"\"\"\n",
" return \"\\n\\n\".join([doc.page_content for doc in docs])\n",
"\n",
"\n",
"chain = {\"context\" : retriever | format_docs, \"question\" : RunnablePassthrough()} | rag_prompt | watson_llm | StrOutputParser()\n",
"\n",
"question = \"sw\"\n",
"response = chain.invoke(question)\n",
"print(response)"
],
"id": "20b7f9afa21406e7",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"질문: 개인정보 유출사고 예방과 대응을 강화하는 개인정보보호법 시행령 일부개정령안이 입법 예고됐다는 내용은 기사에서 확인할 수 있지만, 송경희 위원장의 발언 내용이나 전체회의의 구체적인 내용에 대해서는 기사에서 확인할 수 없습니다.\n"
]
}
],
"execution_count": 54
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": "",
"id": "25d8f09962895ee5"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+651 -62
View File
@@ -2,19 +2,23 @@
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "initial_id",
"metadata": {
"collapsed": true
"collapsed": true,
"ExecuteTime": {
"end_time": "2026-06-02T00:41:20.159251711Z",
"start_time": "2026-06-02T00:41:20.028795255Z"
}
},
"source": "#### 소비자보호원 서비스집단 분쟁조정 사례집 RAG",
"outputs": [],
"source": "#### 소비자보호원 서비스집단 분쟁조정 사례집 RAG"
"execution_count": 1
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:59:17.174441968Z",
"start_time": "2026-06-01T06:59:17.163725951Z"
"end_time": "2026-06-02T02:13:35.759595376Z",
"start_time": "2026-06-02T02:13:35.211936875Z"
}
},
"cell_type": "code",
@@ -29,22 +33,29 @@
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
"from langchain_ibm import WatsonxEmbeddings\n",
"from langchain_chroma import Chroma\n",
"from networkx.algorithms import similarity\n",
"\n",
"from pydantic import BaseModel, Field\n",
"from langchain_core.runnables import RunnablePassthrough\n",
"\n",
"import re\n",
"from pprint import pprint"
"from pprint import pprint\n",
"\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_classic.chains.query_constructor.base import AttributeInfo\n",
"from langchain_classic.retrievers.document_compressors import LLMChainExtractor"
],
"id": "89613f4fba03e277",
"outputs": [],
"execution_count": 2
"execution_count": 41
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T06:59:53.576580670Z",
"start_time": "2026-06-01T06:59:53.567626272Z"
"end_time": "2026-06-02T00:41:29.646687589Z",
"start_time": "2026-06-02T00:41:29.607549464Z"
}
},
"cell_type": "code",
@@ -63,8 +74,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:00:17.195193931Z",
"start_time": "2026-06-01T07:00:12.976471076Z"
"end_time": "2026-06-02T00:41:32.823321578Z",
"start_time": "2026-06-02T00:41:29.650161670Z"
}
},
"cell_type": "code",
@@ -100,8 +111,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:04:30.219884334Z",
"start_time": "2026-06-01T07:04:25.149502303Z"
"end_time": "2026-06-02T00:41:38.188128946Z",
"start_time": "2026-06-02T00:41:32.848865047Z"
}
},
"cell_type": "code",
@@ -121,18 +132,18 @@
"200"
]
},
"execution_count": 6,
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 6
"execution_count": 5
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:06:54.314061593Z",
"start_time": "2026-06-01T07:06:54.303104782Z"
"end_time": "2026-06-02T00:41:38.239702354Z",
"start_time": "2026-06-02T00:41:38.215459067Z"
}
},
"cell_type": "code",
@@ -162,13 +173,13 @@
]
}
],
"execution_count": 9
"execution_count": 6
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:07:22.227432695Z",
"start_time": "2026-06-01T07:07:22.209750745Z"
"end_time": "2026-06-02T00:41:38.267420142Z",
"start_time": "2026-06-02T00:41:38.241437174Z"
}
},
"cell_type": "code",
@@ -232,7 +243,7 @@
]
}
],
"execution_count": 10
"execution_count": 7
},
{
"metadata": {},
@@ -246,8 +257,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:16:39.554724455Z",
"start_time": "2026-06-01T07:16:39.540073666Z"
"end_time": "2026-06-02T00:41:38.303657115Z",
"start_time": "2026-06-02T00:41:38.276957718Z"
}
},
"cell_type": "code",
@@ -265,18 +276,18 @@
"['사\\n례 01 사건번호 2018일나565 | 결정일자 2018. 8. 7.']"
]
},
"execution_count": 12,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 12
"execution_count": 8
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:19:05.532627365Z",
"start_time": "2026-06-01T07:19:05.519326765Z"
"end_time": "2026-06-02T00:41:38.337227895Z",
"start_time": "2026-06-02T00:41:38.309285473Z"
}
},
"cell_type": "code",
@@ -296,18 +307,18 @@
" '\\n세탁 후 갑피 마모 및 경화된 가죽 \\n운동화에 대한 손해배상 요구\\n주 문\\n1. 신청인은 2018. 10. 16.까지 피신청인에게 이 사건 제품(제품명 : ○○○○ 가죽 \\n운동화, 색상 : 흰색) 1켤레를 반환한다. \\n2. 피신청인은 신청인으로부터 제1항 제품을 반환받음과 동시에 신청인에게 71,000원\\n을 지급한다.\\n이 유\\n1. 기초사실\\n가. 신청인은 2017. 6. 6. 가죽 운동화(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색, \\n이하 ‘이 사건 제품’) 1켤레를 160,200원에 구매하여 착화하였고, 2018. 1. 10. \\n피신청인에게 이 사건 제품의 세탁을 의뢰(세탁비 4,000원)하였는데 수령 후 갑피 \\n마모 및 경화된 사실(이하 ‘이 사건 현상’)을 확인하여 피신청인이 재세탁을 하였\\n으나, 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않아 피신\\n청인에게 손해배상(세탁비 환급 포함)을 요구하였으며, 피신청인은 세탁과실이 없\\n다는 이유로 이를 거부하였다.\\n나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\\n \\n신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만\\n으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제\\n인지 단정하기 어려운바, 판단 불가하다.']"
]
},
"execution_count": 14,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 14
"execution_count": 9
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:22:27.264658276Z",
"start_time": "2026-06-01T07:22:27.257468833Z"
"end_time": "2026-06-02T00:41:38.379559905Z",
"start_time": "2026-06-02T00:41:38.342223586Z"
}
},
"cell_type": "code",
@@ -328,18 +339,18 @@
"'01'"
]
},
"execution_count": 21,
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 21
"execution_count": 10
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:52:06.045867563Z",
"start_time": "2026-06-01T07:52:06.002473096Z"
"end_time": "2026-06-02T00:41:38.413957950Z",
"start_time": "2026-06-02T00:41:38.387331264Z"
}
},
"cell_type": "code",
@@ -351,13 +362,13 @@
],
"id": "3307d7b5484784ad",
"outputs": [],
"execution_count": 22
"execution_count": 11
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T07:59:40.356630127Z",
"start_time": "2026-06-01T07:59:38.540410130Z"
"end_time": "2026-06-02T00:41:40.384715024Z",
"start_time": "2026-06-02T00:41:38.419259931Z"
}
},
"cell_type": "code",
@@ -392,7 +403,7 @@
]
}
],
"execution_count": 24
"execution_count": 12
},
{
"metadata": {},
@@ -403,8 +414,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:04:16.872277043Z",
"start_time": "2026-06-01T08:04:16.863977311Z"
"end_time": "2026-06-02T00:41:40.456254203Z",
"start_time": "2026-06-02T00:41:40.420701445Z"
}
},
"cell_type": "code",
@@ -422,13 +433,13 @@
],
"id": "9f78f350f0583c6b",
"outputs": [],
"execution_count": 40
"execution_count": 13
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:06:05.158453784Z",
"start_time": "2026-06-01T08:06:05.144784923Z"
"end_time": "2026-06-02T00:41:40.492951890Z",
"start_time": "2026-06-02T00:41:40.457708042Z"
}
},
"cell_type": "code",
@@ -465,13 +476,13 @@
]
}
],
"execution_count": 42
"execution_count": 14
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:43:23.992589505Z",
"start_time": "2026-06-01T08:43:23.973999681Z"
"end_time": "2026-06-02T00:42:10.626266965Z",
"start_time": "2026-06-02T00:41:40.499189125Z"
}
},
"cell_type": "code",
@@ -532,18 +543,18 @@
"188"
]
},
"execution_count": 68,
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 68
"execution_count": 15
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:43:27.932428458Z",
"start_time": "2026-06-01T08:43:27.925707032Z"
"end_time": "2026-06-02T00:42:10.675760564Z",
"start_time": "2026-06-02T00:42:10.652738035Z"
}
},
"cell_type": "code",
@@ -578,7 +589,7 @@
]
}
],
"execution_count": 69
"execution_count": 16
},
{
"metadata": {},
@@ -589,8 +600,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:46:16.624643520Z",
"start_time": "2026-06-01T08:46:16.607920862Z"
"end_time": "2026-06-02T00:42:10.720885669Z",
"start_time": "2026-06-02T00:42:10.679400312Z"
}
},
"cell_type": "code",
@@ -615,13 +626,13 @@
]
}
],
"execution_count": 74
"execution_count": 17
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:47:24.940997646Z",
"start_time": "2026-06-01T08:47:24.928360960Z"
"end_time": "2026-06-02T00:42:10.765133710Z",
"start_time": "2026-06-02T00:42:10.729124659Z"
}
},
"cell_type": "code",
@@ -644,13 +655,13 @@
]
}
],
"execution_count": 76
"execution_count": 18
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-01T08:51:45.142963478Z",
"start_time": "2026-06-01T08:51:45.132501776Z"
"end_time": "2026-06-02T00:42:10.791997662Z",
"start_time": "2026-06-02T00:42:10.773706380Z"
}
},
"cell_type": "code",
@@ -694,7 +705,585 @@
]
}
],
"execution_count": 85
"execution_count": 19
},
{
"metadata": {},
"cell_type": "markdown",
"source": "#### Indexing - 임베딩",
"id": "376508e91a927dca"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T00:58:09.556024470Z",
"start_time": "2026-06-02T00:58:09.483285302Z"
}
},
"cell_type": "code",
"source": [
"from copy import deepcopy\n",
"\n",
"final_docs = []\n",
"for doc in splite_docs:\n",
" new_doc = deepcopy(doc)\n",
"\n",
" text = re.sub(r\"(?<!\\.)\\n\", \"\", new_doc.page_content)\n",
"\n",
" new_doc.page_content = (\n",
" f\"### 이 사건은 '{new_doc.metadata['title']} 에 대한 사례입니다.\\n\\n\"\n",
" f\"{text}\"\n",
" )\n",
"\n",
" final_docs.append(new_doc)"
],
"id": "dc310d37f63d8776",
"outputs": [],
"execution_count": 20
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T00:59:24.417556447Z",
"start_time": "2026-06-02T00:59:24.391613585Z"
}
},
"cell_type": "code",
"source": "final_docs[18].page_content",
"id": "4260f688dbcef056",
"outputs": [
{
"data": {
"text/plain": [
"\"### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\\n\\n주 문1. 신청인은 2018. 10. 16.까지 피신청인에게 이 사건 제품(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색) 1켤레를 반환한다. 2. 피신청인은 신청인으로부터 제1항 제품을 반환받음과 동시에 신청인에게 71,000원을 지급한다.\\n이 유1. 기초사실가. 신청인은 2017. 6. 6. 가죽 운동화(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색, 이하 ‘이 사건 제품’) 1켤레를 160,200원에 구매하여 착화하였고, 2018. 1. 10. 피신청인에게 이 사건 제품의 세탁을 의뢰(세탁비 4,000원)하였는데 수령 후 갑피 마모 및 경화된 사실(이하 ‘이 사건 현상’)을 확인하여 피신청인이 재세탁을 하였으나, 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않아 피신청인에게 손해배상(세탁비 환급 포함)을 요구하였으며, 피신청인은 세탁과실이 없다는 이유로 이를 거부하였다.\\n나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\""
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 22
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T01:03:01.313455313Z",
"start_time": "2026-06-02T01:02:52.011343879Z"
}
},
"cell_type": "code",
"source": [
"# 로컬저장 : Chroma\n",
"# 메모리 : FAISS (로컬저장 가능)\n",
"\n",
"vectorstore = Chroma.from_documents(documents = final_docs, embedding = watson_embedding, persist_directory=\"./db/chroma_db\", collection_name=\"customer_dispute_cases\")\n"
],
"id": "f3efc78688286680",
"outputs": [],
"execution_count": 25
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T01:10:42.524019313Z",
"start_time": "2026-06-02T01:10:41.266305801Z"
}
},
"cell_type": "code",
"source": [
"query = \"세탁 후 오염에 대한 손해배상은 어떻게 이루어지나요?\"\n",
"similarity_docs = vectorstore.similarity_search(query, k=5)\n",
"\n",
"pprint(similarity_docs)"
],
"id": "e04c653ebe746d83",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Document(id='3b03787a-114d-45f4-8b2e-7386178e951b', metadata={'page_label': '12', 'author': 'PC_A2', 'case_id': '01', 'creationdate': '2019-06-05T11:33:24+09:00', 'title': '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구', 'total_pages': 200, 'page': 11, 'decision_data': '2018.8.7.', 'moddate': '2019-06-05T11:58:31+09:00', 'creator': 'PScript5.dll Version 5.2.2', 'source': './data/2018 서비스·집단 분쟁조정 사례집.pdf', 'case_number': '2018일나565', 'producer': 'Acrobat Distiller 9.0.0 (Windows)'}, page_content=\"### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\\n\\n탁과실로 단정할 수 없다고 판단된 점, 신청인이 착화 과정에서 이 사건 제품을 훼손하여 그 손해가 발생 및 확대되었을 가능성을 배제할 수 없는 점 등에 비추어 볼 때, 손해의 공평·타당한 분담이라는 손해배상 제도의 지도이념과 상호 양보를 통한 분쟁의 원만한 해결이라는 조정의 취지를 고려하여, 피신청인의 책임을 60%로 제한함이 상당하다.\\n한편, 세탁비와 관련하여,「세탁업 표준약관」제9조 제1항 및 제2항에서는 세탁업자의 책임있는 사유로 세탁물이 손상, 색상변화, 얼룩 등의 하자가 발생였을 때에는 해당 세탁물에 대하여 세탁업자는 고객에게 세탁요금을 청구하지 못하므로, 세탁업자인 피신청인이 세탁비 4,000원을 신청인에게 환급이 상당하다.\\n이상을 종합하면, 신청인은 피신청인에게 이 사건 제품을 반환하고 피신청인은 손해배상액 67,000원(112,140원 × 60%, 1,000원 미만 버림) 및 세탁비 4,000원을 합산한\"),\n",
" Document(id='2c01274e-f67c-408d-b3b2-f31b70ffe43c', metadata={'title': '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구', 'case_id': '01', 'producer': 'Acrobat Distiller 9.0.0 (Windows)', 'creator': 'PScript5.dll Version 5.2.2', 'page': 11, 'creationdate': '2019-06-05T11:33:24+09:00', 'page_label': '12', 'decision_data': '2018.8.7.', 'moddate': '2019-06-05T11:58:31+09:00', 'source': './data/2018 서비스·집단 분쟁조정 사례집.pdf', 'author': 'PC_A2', 'case_number': '2018일나565', 'total_pages': 200}, page_content=\"### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\\n\\n살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나, 다음과 같은 사정들, 즉 ① 피신청인이 세탁을 위해 이 사건 제품을 인수하면서 세탁물의 하자유무가 작성된 인수증을 교부하지 않은 점, ② 「세탁업 표준약관」 제3조 제1항은 인수증 미교부로 인해 발생한 손해 및 그에 따른 손해배상책임은 세탁업자에게 귀속되는 것으로 규정하고 있는 점, ③ 인수 당시 이 사건 제품에 이미 하자가 있었음을 입증할 만한 객관적인 자료가 없는 점 등에 비추어 볼 때, 피신청인의 위 주장은 이유 없으므로, 피신청인은 「소비자분쟁해결기준」에 따라 이 사건 제품의 잔존가치 112,140원(= 구매대금 160,200원 × 70%)을 배상함이 상당하다.\\n다만, 한국소비자원 신발제품심의위원회의 심의 결과, 이 사건 현상이 피신청인의 세탁과실로 단정할 수 없다고 판단된 점, 신청인이 착화 과정에서 이 사건 제품을 훼손\"),\n",
" Document(id='b717127e-5fe7-414f-8d96-bceea3536b02', metadata={'page': 10, 'title': '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구', 'source': './data/2018 서비스·집단 분쟁조정 사례집.pdf', 'creationdate': '2019-06-05T11:33:24+09:00', 'moddate': '2019-06-05T11:58:31+09:00', 'decision_data': '2018.8.7.', 'author': 'PC_A2', 'case_id': '01', 'case_number': '2018일나565', 'page_label': '11', 'total_pages': 200, 'producer': 'Acrobat Distiller 9.0.0 (Windows)', 'creator': 'PScript5.dll Version 5.2.2'}, page_content=\"### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\\n\\n나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\\n 신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제인지 단정하기 어려운바, 판단 불가하다.\"),\n",
" Document(id='79d8a177-3643-4370-9272-c8238999ad7f', metadata={'case_id': '01', 'case_number': '2018일나565', 'creationdate': '2019-06-05T11:33:24+09:00', 'moddate': '2019-06-05T11:58:31+09:00', 'source': './data/2018 서비스·집단 분쟁조정 사례집.pdf', 'title': '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구', 'total_pages': 200, 'creator': 'PScript5.dll Version 5.2.2', 'page_label': '12', 'producer': 'Acrobat Distiller 9.0.0 (Windows)', 'decision_data': '2018.8.7.', 'author': 'PC_A2', 'page': 11}, page_content=\"### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\\n\\n4 ● 2018 서비스·집단 분쟁조정 사례집2 . 판 단신청인은 피신청인의 세탁 후 신발의 갑피가 마모되고 경화되는 현상이 발생하였고, 재세탁 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않았으며, 이 사건 제품보다 더 오래신은 비슷한 흰색 가죽 운동화의 상태보다 이 사건 제품의 상태가 더 좋지 않기 때문에 본인의 착화습관이 이 사건 현상의 원인이라고 볼 수도 없으므로, 세탁과실에 따른 손해배상 및 세탁비 환급을 요구한다.\\n이에 대하여 피신청인은 이 사건 제품을 인수하였을 당시 이미 제품 상태가 좋지 않았고, 한국소비자원의 신발제품심의위원회에서도 세탁과실로 인정하지 않았기 때문에 신청인의 요구를 수용할 수 없으나, 다만 원만한 해결을 위해 피해구제 담당자가 제시한 배상산정액 112,140원의 50%인 50,670원을 환급할 의사는 있다고 주장한다.\\n살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나,\"),\n",
" Document(id='8ec16046-9181-4abf-897e-1f4d548150d4', metadata={'case_id': '05', 'author': 'PC_A2', 'creationdate': '2019-06-05T11:33:24+09:00', 'page_label': '23', 'page': 22, 'total_pages': 200, 'case_number': '2018일나138', 'creator': 'PScript5.dll Version 5.2.2', 'decision_data': '2018.7.2.', 'source': './data/2018 서비스·집단 분쟁조정 사례집.pdf', 'producer': 'Acrobat Distiller 9.0.0 (Windows)', 'title': '항공 수하물 운송 지연으로 인한 손해배상 요구', 'moddate': '2019-06-05T11:58:31+09:00'}, page_content=\"### 이 사건은 '항공 수하물 운송 지연으로 인한 손해배상 요구 에 대한 사례입니다.\\n\\n생활에 필수적인 용품을 긴급하게 구입하는 비용 상당으로 볼 수 있는 점, 피신청인이 이 사건 수하물을 신청인에게 배송하고자 노력하였으나 신청인이 계속 이동하여 배송이 쉽지 않았던 점 등을 종합적으로 고려하여 손해배상액을 400,000원으로 산정하기로 한다.\\n이상을 종합하여 볼 때, 피신청인은 2018. 10. 1.까지 신청인에게 400,000원을 지급하고, 만일 피신청인이 위 지급을 지체하면 2018. 10. 2.부터 다 갚는 날까지 「상법」제54조에 따라 연 6%의 비율에 의한 지연손해금을 가산하여 지급함이 상당하다. [관련 법규 및 고시] 상법 제54조, 제909조 이상과 같은 이유로 주문과 같이 결정한다.\")]\n"
]
}
],
"execution_count": 26
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T01:16:12.946983722Z",
"start_time": "2026-06-02T01:16:12.934801294Z"
}
},
"cell_type": "code",
"source": [
"for doc in similarity_docs:\n",
" print(doc.metadata['case_id'], doc.metadata['page'], doc.page_content[:500])\n",
" print(\"-----\")"
],
"id": "493de84a37391d6a",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"탁과실로 단정할 수 없다고 판단된 점, 신청인이 착화 과정에서 이 사건 제품을 훼손하여 그 손해가 발생 및 확대되었을 가능성을 배제할 수 없는 점 등에 비추어 볼 때, 손해의 공평·타당한 분담이라는 손해배상 제도의 지도이념과 상호 양보를 통한 분쟁의 원만한 해결이라는 조정의 취지를 고려하여, 피신청인의 책임을 60%로 제한함이 상당하다.\n",
"한편, 세탁비와 관련하여,「세탁업 표준약관」제9조 제1항 및 제2항에서는 세탁업자의 책임있는 사유로 세탁물이 손상, 색상변화, 얼룩 등의 하자가 발생였을 때에는 해당 세탁물에 대하여 세탁업자는 고객에게 세탁요금을 청구하지 못하므로, 세탁업자인 피신청인이 세탁비 4,000원을 신청인에게 환급이 상당하다.\n",
"이상을 종합하면, 신청인은 피신청인에게 이 사건 제품을 반환하고 피신청인은 손해배상액 67,000원(112,140원 × 60%, 1,000원 미\n",
"-----\n",
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나, 다음과 같은 사정들, 즉 ① 피신청인이 세탁을 위해 이 사건 제품을 인수하면서 세탁물의 하자유무가 작성된 인수증을 교부하지 않은 점, ② 「세탁업 표준약관」 제3조 제1항은 인수증 미교부로 인해 발생한 손해 및 그에 따른 손해배상책임은 세탁업자에게 귀속되는 것으로 규정하고 있는 점, ③ 인수 당시 이 사건 제품에 이미 하자가 있었음을 입증할 만한 객관적인 자료가 없는 점 등에 비추어 볼 때, 피신청인의 위 주장은 이유 없으므로, 피신청인은 「소비자분쟁해결기준」에 따라 이 사건 제품의 잔존가치 112,140원(= 구매대금 160,200원 × 70%)을 배상함이 상당하다.\n",
"다만, 한국소비자원 신발제품심의위원회의 심의 결과, 이 사건 현상이 피신청인의 세탁과실로 단정할 수 없다고 판단된 점, 신청인이 \n",
"-----\n",
"01 10 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\n",
" 신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제인지 단정하기 어려운바, 판단 불가하다.\n",
"-----\n",
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"4 ● 2018 서비스·집단 분쟁조정 사례집2 . 판 단신청인은 피신청인의 세탁 후 신발의 갑피가 마모되고 경화되는 현상이 발생하였고, 재세탁 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않았으며, 이 사건 제품보다 더 오래신은 비슷한 흰색 가죽 운동화의 상태보다 이 사건 제품의 상태가 더 좋지 않기 때문에 본인의 착화습관이 이 사건 현상의 원인이라고 볼 수도 없으므로, 세탁과실에 따른 손해배상 및 세탁비 환급을 요구한다.\n",
"이에 대하여 피신청인은 이 사건 제품을 인수하였을 당시 이미 제품 상태가 좋지 않았고, 한국소비자원의 신발제품심의위원회에서도 세탁과실로 인정하지 않았기 때문에 신청인의 요구를 수용할 수 없으나, 다만 원만한 해결을 위해 피해구제 담당자가 제시한 배상산정액 112,140원의 50%인 50,670원을 환급할 의사는 있다고 주장한다.\n",
"살피건\n",
"-----\n",
"05 22 ### 이 사건은 '항공 수하물 운송 지연으로 인한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"생활에 필수적인 용품을 긴급하게 구입하는 비용 상당으로 볼 수 있는 점, 피신청인이 이 사건 수하물을 신청인에게 배송하고자 노력하였으나 신청인이 계속 이동하여 배송이 쉽지 않았던 점 등을 종합적으로 고려하여 손해배상액을 400,000원으로 산정하기로 한다.\n",
"이상을 종합하여 볼 때, 피신청인은 2018. 10. 1.까지 신청인에게 400,000원을 지급하고, 만일 피신청인이 위 지급을 지체하면 2018. 10. 2.부터 다 갚는 날까지 「상법」제54조에 따라 연 6%의 비율에 의한 지연손해금을 가산하여 지급함이 상당하다. [관련 법규 및 고시] 상법 제54조, 제909조 이상과 같은 이유로 주문과 같이 결정한다.\n",
"-----\n"
]
}
],
"execution_count": 28
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T01:42:33.576799961Z",
"start_time": "2026-06-02T01:42:33.221140621Z"
}
},
"cell_type": "code",
"source": [
"# 랭체인을 통한 검색\n",
"# filter : filter by document metadata\n",
"retriever = vectorstore.as_retriever(search_kwargs={\"k\": 5, \"filter\": {\"case_id\" : \"01\"}})\n",
"\n",
"retriever_docs = retriever.invoke(query)\n",
"\n",
"for doc in retriever_docs:\n",
" print(doc.metadata['case_id'], doc.metadata['page'], doc.page_content[:500])\n",
" print(\"-----\")"
],
"id": "719d395d713176d5",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"탁과실로 단정할 수 없다고 판단된 점, 신청인이 착화 과정에서 이 사건 제품을 훼손하여 그 손해가 발생 및 확대되었을 가능성을 배제할 수 없는 점 등에 비추어 볼 때, 손해의 공평·타당한 분담이라는 손해배상 제도의 지도이념과 상호 양보를 통한 분쟁의 원만한 해결이라는 조정의 취지를 고려하여, 피신청인의 책임을 60%로 제한함이 상당하다.\n",
"한편, 세탁비와 관련하여,「세탁업 표준약관」제9조 제1항 및 제2항에서는 세탁업자의 책임있는 사유로 세탁물이 손상, 색상변화, 얼룩 등의 하자가 발생였을 때에는 해당 세탁물에 대하여 세탁업자는 고객에게 세탁요금을 청구하지 못하므로, 세탁업자인 피신청인이 세탁비 4,000원을 신청인에게 환급이 상당하다.\n",
"이상을 종합하면, 신청인은 피신청인에게 이 사건 제품을 반환하고 피신청인은 손해배상액 67,000원(112,140원 × 60%, 1,000원 미\n",
"-----\n",
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나, 다음과 같은 사정들, 즉 ① 피신청인이 세탁을 위해 이 사건 제품을 인수하면서 세탁물의 하자유무가 작성된 인수증을 교부하지 않은 점, ② 「세탁업 표준약관」 제3조 제1항은 인수증 미교부로 인해 발생한 손해 및 그에 따른 손해배상책임은 세탁업자에게 귀속되는 것으로 규정하고 있는 점, ③ 인수 당시 이 사건 제품에 이미 하자가 있었음을 입증할 만한 객관적인 자료가 없는 점 등에 비추어 볼 때, 피신청인의 위 주장은 이유 없으므로, 피신청인은 「소비자분쟁해결기준」에 따라 이 사건 제품의 잔존가치 112,140원(= 구매대금 160,200원 × 70%)을 배상함이 상당하다.\n",
"다만, 한국소비자원 신발제품심의위원회의 심의 결과, 이 사건 현상이 피신청인의 세탁과실로 단정할 수 없다고 판단된 점, 신청인이 \n",
"-----\n",
"01 10 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\n",
" 신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제인지 단정하기 어려운바, 판단 불가하다.\n",
"-----\n",
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"4 ● 2018 서비스·집단 분쟁조정 사례집2 . 판 단신청인은 피신청인의 세탁 후 신발의 갑피가 마모되고 경화되는 현상이 발생하였고, 재세탁 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않았으며, 이 사건 제품보다 더 오래신은 비슷한 흰색 가죽 운동화의 상태보다 이 사건 제품의 상태가 더 좋지 않기 때문에 본인의 착화습관이 이 사건 현상의 원인이라고 볼 수도 없으므로, 세탁과실에 따른 손해배상 및 세탁비 환급을 요구한다.\n",
"이에 대하여 피신청인은 이 사건 제품을 인수하였을 당시 이미 제품 상태가 좋지 않았고, 한국소비자원의 신발제품심의위원회에서도 세탁과실로 인정하지 않았기 때문에 신청인의 요구를 수용할 수 없으나, 다만 원만한 해결을 위해 피해구제 담당자가 제시한 배상산정액 112,140원의 50%인 50,670원을 환급할 의사는 있다고 주장한다.\n",
"살피건\n",
"-----\n",
"01 10 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"주 문1. 신청인은 2018. 10. 16.까지 피신청인에게 이 사건 제품(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색) 1켤레를 반환한다. 2. 피신청인은 신청인으로부터 제1항 제품을 반환받음과 동시에 신청인에게 71,000원을 지급한다.\n",
"이 유1. 기초사실가. 신청인은 2017. 6. 6. 가죽 운동화(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색, 이하 ‘이 사건 제품’) 1켤레를 160,200원에 구매하여 착화하였고, 2018. 1. 10. 피신청인에게 이 사건 제품의 세탁을 의뢰(세탁비 4,000원)하였는데 수령 후 갑피 마모 및 경화된 사실(이하 ‘이 사건 현상’)을 확인하여 피신청인이 재세탁을 하였으나, 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않아 피신청인에게 손해배상(세탁비 환급 포함)을 요구하였으며, 피신청인은 세탁과실이 없다는 이유\n",
"-----\n"
]
}
],
"execution_count": 32
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T01:44:29.595993205Z",
"start_time": "2026-06-02T01:44:29.204752689Z"
}
},
"cell_type": "code",
"source": [
"retriever = vectorstore.as_retriever(search_kwargs={\"k\": 5, \"where_document\": {\"$contains\" : \"세탁\"}})\n",
"\n",
"retriever_docs = retriever.invoke(query)\n",
"\n",
"for doc in retriever_docs:\n",
" print(doc.metadata['case_id'], doc.metadata['page'], doc.page_content[:500])\n",
" print(\"-----\")"
],
"id": "61880ba2b42d92e0",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"탁과실로 단정할 수 없다고 판단된 점, 신청인이 착화 과정에서 이 사건 제품을 훼손하여 그 손해가 발생 및 확대되었을 가능성을 배제할 수 없는 점 등에 비추어 볼 때, 손해의 공평·타당한 분담이라는 손해배상 제도의 지도이념과 상호 양보를 통한 분쟁의 원만한 해결이라는 조정의 취지를 고려하여, 피신청인의 책임을 60%로 제한함이 상당하다.\n",
"한편, 세탁비와 관련하여,「세탁업 표준약관」제9조 제1항 및 제2항에서는 세탁업자의 책임있는 사유로 세탁물이 손상, 색상변화, 얼룩 등의 하자가 발생였을 때에는 해당 세탁물에 대하여 세탁업자는 고객에게 세탁요금을 청구하지 못하므로, 세탁업자인 피신청인이 세탁비 4,000원을 신청인에게 환급이 상당하다.\n",
"이상을 종합하면, 신청인은 피신청인에게 이 사건 제품을 반환하고 피신청인은 손해배상액 67,000원(112,140원 × 60%, 1,000원 미\n",
"-----\n",
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나, 다음과 같은 사정들, 즉 ① 피신청인이 세탁을 위해 이 사건 제품을 인수하면서 세탁물의 하자유무가 작성된 인수증을 교부하지 않은 점, ② 「세탁업 표준약관」 제3조 제1항은 인수증 미교부로 인해 발생한 손해 및 그에 따른 손해배상책임은 세탁업자에게 귀속되는 것으로 규정하고 있는 점, ③ 인수 당시 이 사건 제품에 이미 하자가 있었음을 입증할 만한 객관적인 자료가 없는 점 등에 비추어 볼 때, 피신청인의 위 주장은 이유 없으므로, 피신청인은 「소비자분쟁해결기준」에 따라 이 사건 제품의 잔존가치 112,140원(= 구매대금 160,200원 × 70%)을 배상함이 상당하다.\n",
"다만, 한국소비자원 신발제품심의위원회의 심의 결과, 이 사건 현상이 피신청인의 세탁과실로 단정할 수 없다고 판단된 점, 신청인이 \n",
"-----\n",
"01 10 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\n",
" 신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제인지 단정하기 어려운바, 판단 불가하다.\n",
"-----\n",
"01 11 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"4 ● 2018 서비스·집단 분쟁조정 사례집2 . 판 단신청인은 피신청인의 세탁 후 신발의 갑피가 마모되고 경화되는 현상이 발생하였고, 재세탁 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않았으며, 이 사건 제품보다 더 오래신은 비슷한 흰색 가죽 운동화의 상태보다 이 사건 제품의 상태가 더 좋지 않기 때문에 본인의 착화습관이 이 사건 현상의 원인이라고 볼 수도 없으므로, 세탁과실에 따른 손해배상 및 세탁비 환급을 요구한다.\n",
"이에 대하여 피신청인은 이 사건 제품을 인수하였을 당시 이미 제품 상태가 좋지 않았고, 한국소비자원의 신발제품심의위원회에서도 세탁과실로 인정하지 않았기 때문에 신청인의 요구를 수용할 수 없으나, 다만 원만한 해결을 위해 피해구제 담당자가 제시한 배상산정액 112,140원의 50%인 50,670원을 환급할 의사는 있다고 주장한다.\n",
"살피건\n",
"-----\n",
"01 10 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"주 문1. 신청인은 2018. 10. 16.까지 피신청인에게 이 사건 제품(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색) 1켤레를 반환한다. 2. 피신청인은 신청인으로부터 제1항 제품을 반환받음과 동시에 신청인에게 71,000원을 지급한다.\n",
"이 유1. 기초사실가. 신청인은 2017. 6. 6. 가죽 운동화(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색, 이하 ‘이 사건 제품’) 1켤레를 160,200원에 구매하여 착화하였고, 2018. 1. 10. 피신청인에게 이 사건 제품의 세탁을 의뢰(세탁비 4,000원)하였는데 수령 후 갑피 마모 및 경화된 사실(이하 ‘이 사건 현상’)을 확인하여 피신청인이 재세탁을 하였으나, 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않아 피신청인에게 손해배상(세탁비 환급 포함)을 요구하였으며, 피신청인은 세탁과실이 없다는 이유\n",
"-----\n"
]
}
],
"execution_count": 34
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Generation",
"id": "88fb2ec4e1636901"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T02:03:08.613732181Z",
"start_time": "2026-06-02T02:02:54.296483224Z"
}
},
"cell_type": "code",
"source": [
"def format_docs(docs):\n",
" \"\"\"Document 객체에서 page_content 추출\"\"\"\n",
" return \"\\n\\n\".join([doc.page_content for doc in docs])\n",
"\n",
"\n",
"\n",
"retriever = vectorstore.as_retriever(search_kwargs={\"k\": 5})\n",
"\n",
"rag_prompt = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"다음 컨텍스트를 참고하여 질문에 답하세요.\\n. 컨텍스트에 없는 내용은 모른다 라고 답하세요.\\n\\n 컨텍스트:\\n{context}\"),\n",
" (\"human\", \"{query}\"),\n",
"])\n",
"\n",
"# 질의 => 벡터화 => 가장 가까운 chunks 찾기 => Document 객체 => format_docs => context => llm context 기반으로 답변 정리\n",
"\n",
"chain = {\"context\" : retriever | format_docs, \"query\" : RunnablePassthrough()} | rag_prompt | watson_llm | StrOutputParser()\n",
"\n",
"query = \"세탁 후 오염에 대한 손해배상은 어떻게 이루어지나요?\"\n",
"response = chain.invoke(query)"
],
"id": "e9b4f6c7b79d2f54",
"outputs": [],
"execution_count": 35
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T02:05:45.881280476Z",
"start_time": "2026-06-02T02:05:45.871725901Z"
}
},
"cell_type": "code",
"source": "response",
"id": "3dc22580df212897",
"outputs": [
{
"data": {
"text/plain": [
"'세탁 후 오염에 대한 손해배상은 일반적으로 세탁업자와 고객 간의 계약 조건, 세탁업 표준약관, 그리고 관련 법규에 따라 이루어집니다. 세탁 후 오염이 발생한 경우, 손해배상 여부와 그 범위는 다음과 같은 요소들을 고려하여 결정됩니다:\\n\\n1. **계약 조건**: 고객과 세탁업자 간의 계약서나 약관에 명시된 조건이 손해배상 여부를 결정하는 데 중요한 역할을 합니다. 예를 들어, 계약서에 세탁 중 발생하는 손상에 대한 책임이 세탁업자에게 있다고 명시되어 있다면, 세탁업자는 해당 손해에 대해 배상해야 할 수 있습니다.\\n\\n2. **세탁업 표준약관**: 많은 세탁업체들은 세탁업 표준약관을 통해 세탁 서비스의 범위와 책임을 명확히 합니다. 이 약관에는 세탁 중 발생할 수 있는 손상에 대한 처리 방법과 책임 소재가 규정되어 있습니다. 예를 들어, \"세탁업 표준약관\"에서는 세탁업자의 책임있는 사유로 세탁물이 손상, 색상변화, 얼룩 등의 하자가 발생했을 때 세탁업자가 해당 세탁물에 대하여 세탁요금을 청구하지 못한다고 규정하고 있습니다.\\n\\n3. **법적 규정**: 소비자 보호를 위한 법률이나 규정도 손해배상 여부에 영향을 미칠 수 있습니다. 소비자분쟁해결기준 등 관련 법규는 소비자와 세탁업자 간의 분쟁 해결을 위한 기준을 제공하며, 이를 통해 손해배상의 범위와 조건이 결정될 수 있습니다.\\n\\n4. **손해의 원인**: 손해가 세탁 과정에서 발생한 것인지, 아니면 고객의 착화 과정에서 발생한 것인지에 따라 책임 소재가 달라질 수 있습니다. 세탁업자가 세탁 과정에서 발생한 손해에 대해 책임을 지게 될 경우, 손해배상이 이루어질 수 있습니다.\\n\\n5. **손해의 정도**: 손해의 정도와 범위도 손해배상 여부와 그 범위를 결정하는 데 중요한 요소입니다. 손해가 미미한 경우에는 배상이 면제될 수 있으며, 심각한 손해의 경우에는 전액 또는 일부의 배상이 이루어질 수 있습니다.\\n\\n세탁 후 오염에 대한 손해배상이 이루어지려면, 고객은 세탁업자에게 손해의 사실과 원인을 명확히 밝히고, 관련 증거를 제시해야 합니다. 세탁업자는 이러한 요청에 대해 적절히 대응하며, 법적 책임이 있다고 판단되는 경우에는 손해배상을 진행해야 합니다.'"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 40
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"### BM25 (SParse Retrieval)\n",
"- 키워드 검색"
],
"id": "d5307bef36609201"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T02:16:09.701751350Z",
"start_time": "2026-06-02T02:16:09.659449106Z"
}
},
"cell_type": "code",
"source": [
"bm25_retriever = BM25Retriever.from_documents(final_docs)\n",
"bm25_retriever.k = 5\n",
"\n",
"docs = bm25_retriever.invoke(\"가죽 운동화 세탁 손해배상\")\n",
"\n",
"for doc in docs:\n",
" print(doc.metadata['case_id'], doc.page_content[:500])\n",
" print(\"-----\")\n"
],
"id": "45f404869943b3a9",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"4 ● 2018 서비스·집단 분쟁조정 사례집2 . 판 단신청인은 피신청인의 세탁 후 신발의 갑피가 마모되고 경화되는 현상이 발생하였고, 재세탁 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않았으며, 이 사건 제품보다 더 오래신은 비슷한 흰색 가죽 운동화의 상태보다 이 사건 제품의 상태가 더 좋지 않기 때문에 본인의 착화습관이 이 사건 현상의 원인이라고 볼 수도 없으므로, 세탁과실에 따른 손해배상 및 세탁비 환급을 요구한다.\n",
"이에 대하여 피신청인은 이 사건 제품을 인수하였을 당시 이미 제품 상태가 좋지 않았고, 한국소비자원의 신발제품심의위원회에서도 세탁과실로 인정하지 않았기 때문에 신청인의 요구를 수용할 수 없으나, 다만 원만한 해결을 위해 피해구제 담당자가 제시한 배상산정액 112,140원의 50%인 50,670원을 환급할 의사는 있다고 주장한다.\n",
"살피건\n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\n",
" 신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제인지 단정하기 어려운바, 판단 불가하다.\n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나, 다음과 같은 사정들, 즉 ① 피신청인이 세탁을 위해 이 사건 제품을 인수하면서 세탁물의 하자유무가 작성된 인수증을 교부하지 않은 점, ② 「세탁업 표준약관」 제3조 제1항은 인수증 미교부로 인해 발생한 손해 및 그에 따른 손해배상책임은 세탁업자에게 귀속되는 것으로 규정하고 있는 점, ③ 인수 당시 이 사건 제품에 이미 하자가 있었음을 입증할 만한 객관적인 자료가 없는 점 등에 비추어 볼 때, 피신청인의 위 주장은 이유 없으므로, 피신청인은 「소비자분쟁해결기준」에 따라 이 사건 제품의 잔존가치 112,140원(= 구매대금 160,200원 × 70%)을 배상함이 상당하다.\n",
"다만, 한국소비자원 신발제품심의위원회의 심의 결과, 이 사건 현상이 피신청인의 세탁과실로 단정할 수 없다고 판단된 점, 신청인이 \n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"주 문1. 신청인은 2018. 10. 16.까지 피신청인에게 이 사건 제품(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색) 1켤레를 반환한다. 2. 피신청인은 신청인으로부터 제1항 제품을 반환받음과 동시에 신청인에게 71,000원을 지급한다.\n",
"이 유1. 기초사실가. 신청인은 2017. 6. 6. 가죽 운동화(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색, 이하 ‘이 사건 제품’) 1켤레를 160,200원에 구매하여 착화하였고, 2018. 1. 10. 피신청인에게 이 사건 제품의 세탁을 의뢰(세탁비 4,000원)하였는데 수령 후 갑피 마모 및 경화된 사실(이하 ‘이 사건 현상’)을 확인하여 피신청인이 재세탁을 하였으나, 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않아 피신청인에게 손해배상(세탁비 환급 포함)을 요구하였으며, 피신청인은 세탁과실이 없다는 이유\n",
"-----\n",
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"의 비율에 의한 지연손해금을 가산하여 지급함이 상당하다.\n",
"[소비자분쟁해결기준에 따른 배상액] 운동화 사용 일수(2016. 9. 13. ~ 2016. 12. 13.) : 92일 배상비율표에 의한 배상비율 : 60% 배상액 : 운동화 구입 금액(109,000원) × 배상비율(60%) = 65,400원[관련 법규 및 고시] 상법 제54조, 제152조, 소비자기본법 시행령 제9조, 소비자분쟁해결기준 별표 II 품목별 해결기준 23. 세탁업, 별표 III 품목별 품질보증기간 및 부품보유기간이상과 같은 이유로 주문과 같이 결정한다.\n",
"-----\n"
]
}
],
"execution_count": 42
},
{
"metadata": {},
"cell_type": "markdown",
"source": "- 시맨틱 검색 + 키워드 검색",
"id": "34083fa980d37f7e"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T02:20:44.719245581Z",
"start_time": "2026-06-02T02:20:42.173247275Z"
}
},
"cell_type": "code",
"source": [
"ensemble_retriever = EnsembleRetriever(retrievers =[bm25_retriever, retriever], weight=[0.7, 0.3])\n",
"\n",
"docs = ensemble_retriever.invoke(\"가죽 운동화 세탁 손해배상\")\n",
"\n",
"for doc in docs:\n",
" print(doc.metadata['case_id'], doc.page_content[:500])\n",
" print(\"-----\")\n"
],
"id": "d53f475849d789a1",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"4 ● 2018 서비스·집단 분쟁조정 사례집2 . 판 단신청인은 피신청인의 세탁 후 신발의 갑피가 마모되고 경화되는 현상이 발생하였고, 재세탁 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않았으며, 이 사건 제품보다 더 오래신은 비슷한 흰색 가죽 운동화의 상태보다 이 사건 제품의 상태가 더 좋지 않기 때문에 본인의 착화습관이 이 사건 현상의 원인이라고 볼 수도 없으므로, 세탁과실에 따른 손해배상 및 세탁비 환급을 요구한다.\n",
"이에 대하여 피신청인은 이 사건 제품을 인수하였을 당시 이미 제품 상태가 좋지 않았고, 한국소비자원의 신발제품심의위원회에서도 세탁과실로 인정하지 않았기 때문에 신청인의 요구를 수용할 수 없으나, 다만 원만한 해결을 위해 피해구제 담당자가 제시한 배상산정액 112,140원의 50%인 50,670원을 환급할 의사는 있다고 주장한다.\n",
"살피건\n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"주 문1. 신청인은 2018. 10. 16.까지 피신청인에게 이 사건 제품(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색) 1켤레를 반환한다. 2. 피신청인은 신청인으로부터 제1항 제품을 반환받음과 동시에 신청인에게 71,000원을 지급한다.\n",
"이 유1. 기초사실가. 신청인은 2017. 6. 6. 가죽 운동화(제품명 : ○○○○ 가죽 운동화, 색상 : 흰색, 이하 ‘이 사건 제품’) 1켤레를 160,200원에 구매하여 착화하였고, 2018. 1. 10. 피신청인에게 이 사건 제품의 세탁을 의뢰(세탁비 4,000원)하였는데 수령 후 갑피 마모 및 경화된 사실(이하 ‘이 사건 현상’)을 확인하여 피신청인이 재세탁을 하였으나, 이후에도 경화현상만 다소 개선될 뿐 갑피 마모 현상이 개선되지 않아 피신청인에게 손해배상(세탁비 환급 포함)을 요구하였으며, 피신청인은 세탁과실이 없다는 이유\n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"나. 한국소비자원 신발제품심의위원회 심의 결과는 다음과 같다.\n",
" 신청인이 주장하는 갑피 벗겨짐(스크래치 등) 증상은 관찰되나 현 제품 상태만으로는 제품 훼손의 원인이 세탁 과정상 발생한 것인지 착화 환경에 따른 문제인지 단정하기 어려운바, 판단 불가하다.\n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"살피건대, 피신청인은 세탁 전부터 이 사건 제품의 상태가 좋지 않았다고 주장하나, 다음과 같은 사정들, 즉 ① 피신청인이 세탁을 위해 이 사건 제품을 인수하면서 세탁물의 하자유무가 작성된 인수증을 교부하지 않은 점, ② 「세탁업 표준약관」 제3조 제1항은 인수증 미교부로 인해 발생한 손해 및 그에 따른 손해배상책임은 세탁업자에게 귀속되는 것으로 규정하고 있는 점, ③ 인수 당시 이 사건 제품에 이미 하자가 있었음을 입증할 만한 객관적인 자료가 없는 점 등에 비추어 볼 때, 피신청인의 위 주장은 이유 없으므로, 피신청인은 「소비자분쟁해결기준」에 따라 이 사건 제품의 잔존가치 112,140원(= 구매대금 160,200원 × 70%)을 배상함이 상당하다.\n",
"다만, 한국소비자원 신발제품심의위원회의 심의 결과, 이 사건 현상이 피신청인의 세탁과실로 단정할 수 없다고 판단된 점, 신청인이 \n",
"-----\n",
"01 ### 이 사건은 '세탁 후 갑피 마모 및 경화된 가죽 운동화에 대한 손해배상 요구 에 대한 사례입니다.\n",
"\n",
"탁과실로 단정할 수 없다고 판단된 점, 신청인이 착화 과정에서 이 사건 제품을 훼손하여 그 손해가 발생 및 확대되었을 가능성을 배제할 수 없는 점 등에 비추어 볼 때, 손해의 공평·타당한 분담이라는 손해배상 제도의 지도이념과 상호 양보를 통한 분쟁의 원만한 해결이라는 조정의 취지를 고려하여, 피신청인의 책임을 60%로 제한함이 상당하다.\n",
"한편, 세탁비와 관련하여,「세탁업 표준약관」제9조 제1항 및 제2항에서는 세탁업자의 책임있는 사유로 세탁물이 손상, 색상변화, 얼룩 등의 하자가 발생였을 때에는 해당 세탁물에 대하여 세탁업자는 고객에게 세탁요금을 청구하지 못하므로, 세탁업자인 피신청인이 세탁비 4,000원을 신청인에게 환급이 상당하다.\n",
"이상을 종합하면, 신청인은 피신청인에게 이 사건 제품을 반환하고 피신청인은 손해배상액 67,000원(112,140원 × 60%, 1,000원 미\n",
"-----\n",
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"의 비율에 의한 지연손해금을 가산하여 지급함이 상당하다.\n",
"[소비자분쟁해결기준에 따른 배상액] 운동화 사용 일수(2016. 9. 13. ~ 2016. 12. 13.) : 92일 배상비율표에 의한 배상비율 : 60% 배상액 : 운동화 구입 금액(109,000원) × 배상비율(60%) = 65,400원[관련 법규 및 고시] 상법 제54조, 제152조, 소비자기본법 시행령 제9조, 소비자분쟁해결기준 별표 II 품목별 해결기준 23. 세탁업, 별표 III 품목별 품질보증기간 및 부품보유기간이상과 같은 이유로 주문과 같이 결정한다.\n",
"-----\n"
]
}
],
"execution_count": 43
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### SelfQuery",
"id": "9be2a3648e816d7b"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T02:44:44.850642254Z",
"start_time": "2026-06-02T02:44:41.061100519Z"
}
},
"cell_type": "code",
"source": [
"metadata_field_info = [\n",
" AttributeInfo(\n",
" name=\"case_id\", description=\"사건번호\", type =\"string\"\n",
" ),\n",
" AttributeInfo(\n",
" name=\"title\", description=\"사건제목\", type =\"string\"\n",
" ),\n",
" AttributeInfo(\n",
" name=\"decision_date\", description=\"결정일자\", type =\"string\"\n",
" ),\n",
"]\n",
"\n",
"self_retriever = SelfQueryRetriever.from_llm(llm = watson_llm, vectorstore=vectorstore, document_contents=\"소비자 분쟁 사례\", metadata_field_info=metadata_field_info, structured_query_translator = ChromaTranslator())\n",
"\n",
"docs = self_retriever.invoke(\"32번 사례를 보여줘\")\n",
"\n",
"for doc in docs:\n",
" print(doc.metadata['case_id'], doc.page_content[:500])\n",
" print(\"-----\")"
],
"id": "2f14e6821c8047ea",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"대물에 대해 책임이 없음을 알린 경우에도 물건의 멸실로 인한 손해를 배상할 책임을 면하지 못하므로, 신청인에게 운동화 분실에 따른 손해를 배상하여야 한다.\n",
"다만, 신청인은 자신의 신발을 누구나 접근할 수 있는 개방된 신발장에 비치하면서 분실 가능성이 있음을 충분히 예상할 수 있었고, 피신청인이 비치한 비닐봉지를 이용하여 자신의 신발을 다른 신발과 구분하는 등 주의를 기울일 필요가 있었음에도 어떠한 조치도 취하지 아니하였으므로, 이러한 신청인의 부주의를 고려하여 피신청인의 책임을 50%로 제한하기로 한다. 그렇다면, 피신청인은 2017. 12. 26.까지 신청인에게 「소비자분쟁해결기준」에 따라 산정한 배상액 65,400원의 50%에 해당하는 32,700원을 지급하고, 만일 지급을 지체하면 위 돈에 대하여 2017. 12. 27.부터 다 갚는 날까지 「상법」제54조에 따라 연 6%의 비율에 의한 지연손해금\n",
"-----\n",
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"주 문1. 피신청인은 2017. 12. 26.까지 신청인에게 32,700원을 지급한다.\n",
"2. 만일 피신청인이 제1항의 지급을 지체하면 2017. 12. 27.부터 다 갚는 날까지 연 6% 비율에 의한 돈을 가산하여 지급한다.\n",
"이 유1. 기초사실신청인은 2016. 12. 13. 피신청인의 식당에서 식사 후 신발(구입일자 : 2016. 9. 13., 구입가격 : 109,000원, 이하 ‘이 사건 신발’이라고 함)을 분실하여 이에 대한 손해배상을 요구하였으나 피신청인이 이를 거부하였다.\n",
"2 . 판 단신청인은 피신청인이 이 사건 신발의 분실을 인정하면서도 책임을 회피하고 있는바, 이에 대한 배상을 요구한다. 이에 대하여 피신청인은 식당 입구에 신발 분실을 주의할 것을 고지하고 신발을 보관하기 위한 비닐 봉투 등을 비치하였는바, 배상 책임이 없다고 주장한다.\n",
"살피건대, 공중접객업자는 자기 또는 그 사용인이\n",
"-----\n",
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"의 비율에 의한 지연손해금을 가산하여 지급함이 상당하다.\n",
"[소비자분쟁해결기준에 따른 배상액] 운동화 사용 일수(2016. 9. 13. ~ 2016. 12. 13.) : 92일 배상비율표에 의한 배상비율 : 60% 배상액 : 운동화 구입 금액(109,000원) × 배상비율(60%) = 65,400원[관련 법규 및 고시] 상법 제54조, 제152조, 소비자기본법 시행령 제9조, 소비자분쟁해결기준 별표 II 품목별 해결기준 23. 세탁업, 별표 III 품목별 품질보증기간 및 부품보유기간이상과 같은 이유로 주문과 같이 결정한다.\n",
"-----\n",
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"제1장일반분쟁조정 사례 (서비스 )제1장 일반분쟁조정 사례(서비스) ● 89피신청인이 운영하는 식당은 이용하는 고객들이 자신의 신발을 벗어 신발장에 둔 다음 식당 내로 들어가는 구조로 이루어져 있어 위 신발장은 고객들이 관리할 수 있는 영역이라기보다 피신청인의 관리 영역이라고 볼 수 있고, 따라서 신발장에 둔 신발에 대해서는 공중접객업자인 피신청인이 고객인 신청인으로부터 임치받았다고 봄이 상당하다.\n",
"피신청인은 고객으로부터 임치받은 물건이 멸실 또는 훼손되지 않도록 관리할 책임이 있음에도 시건장치를 갖춘 신발장을 설치하는 등의 조치 없이 식당 입구에 비닐봉지를 비치하고 주의 문구를 표시한 정도만으로는 임치 받은 신발의 보관에 관하여 주의를 게을리 하지 아니하였다고 보기에 부족하고, 「상법」제152조 제3항에 따라 고객의 휴대물에 대해 책임이 없음을 알린 경우에도 물건의 멸실로 인한 손해를 배상할 책임을\n",
"-----\n"
]
}
],
"execution_count": 44
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"일반 검색 : k=5\n",
"5 * 500chunk = 2500자 전달\n",
"2500 전달 대신 질문과 관련있는 한두문장만 찾기 => LLMChainExtractor"
],
"id": "f801b49c654e900d"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2026-06-02T02:54:24.781272089Z",
"start_time": "2026-06-02T02:54:21.055451974Z"
}
},
"cell_type": "code",
"source": [
"compressor = LLMChainExtractor.from_llm(watson_llm)\n",
"\n",
"compression_retriever = (ContextualCompressionRetriever(\n",
" base_compressor=compressor,\n",
" base_retriever=ensemble_retriever,\n",
"))\n",
"\n",
"docs = compression_retriever.invoke(\"식당에서 분실된 신발에 대한 손해배상은 어떻게?\")\n",
"\n",
"for doc in docs:\n",
" print(doc.metadata['case_id'], doc.page_content[:500])\n",
" print(\"-----\")"
],
"id": "e66e9916bf610511",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"32 ### 이 사건은 '식당에서 분실된 신발에 대한 배상 요구 에 대한 사례입니다.\n",
"\n",
"살피건대, 공중접객업자는 자기 또는 그 사용인이 고객으로부터 임치(任置)받은 물건의 보관에 관하여 주의를 게을리하지 아니하였음을 증명하지 아니하면 그 물건의 멸실 또는 훼손으로 인한 손해를 배상할 책임이 있다.\n",
"-----\n"
]
}
],
"execution_count": 49
},
{
"metadata": {},
@@ -702,7 +1291,7 @@
"outputs": [],
"execution_count": null,
"source": "",
"id": "dc310d37f63d8776"
"id": "c499013efca7c5a9"
}
],
"metadata": {