Jump to content

๐Ÿฆœ๐Ÿ”— ๋žญ์ฒด์ธ(Langchain)์œผ๋กœ Naive RAG ๊ตฌํ˜„ํ•˜๊ธฐ cookbook


Recommended Posts

๋ณธ ๊ฐ€์ด๋“œ๋Š” ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค(CLOVA Studio)์™€ ๋žญ์ฒด์ธ(Langchain)์„ ์ด์šฉํ•ด RAG(Retrieval Augmented Generation; ๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ) ์‹œ์Šคํ…œ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

RAG๋Š” AI๊ฐ€ ์ฃผ์–ด์ง„ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋” ์ •ํ™•ํ•˜๊ณ  ๊ด€๋ จ์„ฑ ๋†’์€ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋„๋ก ๋•๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์ด ์˜ˆ์ œ์—์„œ ์šฐ๋ฆฌ๋Š” HTML ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ(ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค ๊ฐ€์ด๋“œ)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ์ถœ๋ ฅํ•˜๋Š” RAG ์‹œ์Šคํ…œ์„ ๋žญ์ฒด์ธ์„ ํ™œ์šฉํ•ด ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.ย ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” RAG ์‹œ์Šคํ…œ์˜ ๊ตฌ์กฐ๋„๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

spacer.png

ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค์˜ HyperCLOVA X ๋ชจ๋ธ (์ฑ— ๋ชจ๋“œ), ์ž„๋ฒ ๋”ฉ API, ๋ฌธ๋‹จ ๋‚˜๋ˆ„๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋žญ์ฒด์ธ์„ ํ†ตํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ ๋‹จ๊ณ„๋กœ ์ง„ํ–‰ํ•ด ๊ฐ„๋‹จํ•œ RAG ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

spacer.png

ย 

๋ฒ„์ „ ์ •๋ณด
์•„๋ž˜ ์˜ˆ์ œ ์ฝ”๋“œ๋Š” Python 3.12.4์—์„œ ์‹คํ–‰ ํ™•์ธํ•˜์˜€์œผ๋ฉฐ, ์ตœ์†Œ Python3.9๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค.ย 
์•„๋ž˜ ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด ํ•„์š”ํ•œ ๋ชจ๋“ˆ์„ ๋ชจ๋‘ ์„ค์น˜ํ•ด์ค๋‹ˆ๋‹ค.
requirements.txt

ย 

1. ์‚ฌ์ „ ์ค€๋น„

1)ย Langchain ํŒจํ‚ค์ง€ ์„ค์น˜

langchain-community >= 0.3.4๋ถ€ํ„ฐ langchain_community ์•ˆ์— ChatClovaX, ClovaXEmbeddings๊ฐ€ ํฌํ•จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.ย ๋žญ์ฒด์ธ์€ ์•„๋ž˜ ๋ช…๋ น๋ฌธ์„ ํ†ตํ•ด ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย 

pip install langchain
pip install langchain-community~=0.3.4

ย 2) ์ฐธ์กฐํ•  ๋ฌธ์„œ (HTML ๋ฐ์ดํ„ฐ) ์ค€๋น„

๋ณธ ์˜ˆ์ œ์—์„œ๋Š” ๋„ค์ด๋ฒ„ํด๋ผ์šฐ๋“œํ”Œ๋žซํผ(NCP)์˜ ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค ์‚ฌ์šฉ ๊ฐ€์ด๋“œ์ค‘ย CLOVA Studio ๊ฐœ๋…,ย CLOVA Studio ์‹œ๋‚˜๋ฆฌ์˜คย ํŽ˜์ด์ง€๋ฅผ HTML ๋ฐ์ดํ„ฐ๋กœ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ํ•  ์›น์‚ฌ์ดํŠธ์˜ ๋งํฌ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด txt ํŒŒ์ผ๋กœ ์ค€๋น„ํ–ˆ์œผ๋ฉฐ, ํ•ด๋‹น ๋‚ด์šฉ์€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์— ๋งž๊ฒŒ ์ˆ˜์ •๋˜์–ด๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค.

  • (์ฃผ์˜)ย ย txtํŒŒ์ผ์„ ์ž‘์„ฑํ•  ๋•Œ๋Š” ํ•œ ์ค„์— ํ•˜๋‚˜์˜ URL์„ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๊ฐ URL์€ ์ค„๋ฐ”๊ฟˆ์œผ๋กœ ๊ตฌ๋ถ„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
https://guide.ncloud-docs.com/docs/clovastudio-info
https://guide.ncloud-docs.com/docs/clovastudio-procedure

ย 

3) ์ฝ”๋“œ์— ๊ณตํ†ต์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ํ•„์š” ๋ชจ๋“ˆ import

importย json
importย getpass
importย os
fromย tqdmย importย tqdm
importย requests
importย time
importย uuid
fromย uuidย importย uuid4
fromย pathlibย importย Path

ย 

4) ํ…Œ์ŠคํŠธ์•ฑ ๋ฐ APIํ‚ค ๋ฏธ๋ฆฌ ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ

๊ฐ ๋ชจ๋“ˆ ํ˜ธ์ถœ์‹œ ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ์•ฑ ๋ฐ APIํ‚ค๋ฅผ ๋ฏธ๋ฆฌ ๋ฐœ๊ธ‰๋ฐ›๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ €์žฅํ•ด๋‘์„ธ์š”.

Quote

ํ‚ค ๊ฐ’ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ• (ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค ์•ฑ ์‹ ์ฒญ ํ˜„ํ™ฉ ํŽ˜์ด์ง€)

1.APIํ‚ค, Gatewayํ‚ค ์„ค์ •ํ•˜๊ธฐ
ํ…Œ์ŠคํŠธ ์•ฑ์„ ์ด์šฉํ•˜๋Š” ๊ฒฝ์šฐ, APIํ‚ค์™€ Gatewayํ‚ค๋Š” ๊ณ„์ •๋‹น ๋ฐœ๊ธ‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ํ…Œ์ŠคํŠธ์•ฑ์œผ๋กœ ๋ฐœ๊ธ‰๋ฐ›์•„๋„ ํ‚ค ๊ฐ’์ด ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
๋ณธ ์˜ˆ์ œ์—์„  ํ…Œ์ŠคํŠธ ์•ฑ์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ API ํ‚ค์™€ Gateway ํ‚ค๋ฅผ ์‚ฌ์ „ ๋ฐœ๊ธ‰๋ฐ›์•„ ์ •์˜ํ•ด๋‘๊ณ , ๊ฐ ๋ชจ๋“ˆ ํ˜ธ์ถœ์‹œ ๋ถˆ๋Ÿฌ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.ย 

๋‹จ, ์•ฑ์˜ API Key/API GW Key๊ฐ€ย ์ „๋ถ€ ๋™์ผํ•œ ๊ฑด ํ…Œ์ŠคํŠธ์•ฑ๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค.ย ์„œ๋น„์Šค ์•ฑ์„ ์ด์šฉํ•˜๊ณ ์ž ํ•  ๊ฒฝ์šฐ ์‚ฌ์ „ ์„ค์ •์ด ์•„๋‹ˆ๋ผ ๊ฐœ๋ณ„ ๋ชจ๋“ˆ ์ •์˜ ์‹œ๋งˆ๋‹ค ํ•ด๋‹น API Key๋ฅผ ์ž…๋ ฅํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

  • NCP_CLOVASTUDIO_API_KEY

  • NCP_APIGW_API_KEY

spacer.png

ย 

2.๋ฌธ๋‹จ๋‚˜๋ˆ„๊ธฐ ํ…Œ์ŠคํŠธ ์•ฑ ๋ฐœ๊ธ‰๋ฐ›๊ณ , app_id ๊ฐ’์„ ์„ค์ •ํ•˜๊ธฐ
(ClovaStudio > ์ต์Šคํ”Œ๋กœ๋Ÿฌ > ๋ฌธ๋‹จ ๋‚˜๋ˆ„๊ธฐ > ํ…Œ์ŠคํŠธ์•ฑ ๋ฐœ๊ธ‰)

  • NCP_CLOVASTUDIO_APP_ID_SEGMENTATION

spacer.png

3. ์ž„๋ฒ ๋”ฉV2 ํ…Œ์ŠคํŠธ์•ฑ ๋ฐœ๊ธ‰๋ฐ›๊ณ ,ย  app_id ๊ฐ’์„ ์„ค์ •ํ•˜๊ธฐ
(ClovaStudio > ์ต์Šคํ”Œ๋กœ๋Ÿฌ > ์ž„๋ฒ ๋”ฉV2 > ํ…Œ์ŠคํŠธ์•ฑ ๋ฐœ๊ธ‰)
*์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ emb,sts๋ฅผ ์“ฐ๋Š”๊ฒฝ์šฐ V1์œผ๋กœ ๋ฐœ๊ธ‰ ๋ฐ›์•„์•ผํ•ฉ๋‹ˆ๋‹ค.

  • NCP_CLOVASTUDIO_APP_ID

spacer.png

ย 

# API ํ‚ค์™€ Gateway API ํ‚ค๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.
os.environ["NCP_CLOVASTUDIO_API_KEY"]ย =ย getpass.getpass("NCP CLOVA Studio API Key: ")
os.environ["NCP_APIGW_API_KEY"]ย =ย getpass.getpass("NCP API Gateway API Key: ")
NCP CLOVA Studio API Key:  ยทยทยทยทยทยทยทยท
NCP API Gateway API Key:  ยทยทยทยทยทยทยทยท

ย 

# ๋ฌธ๋‹จ๋‚˜๋ˆ„๊ธฐ ํ…Œ์ŠคํŠธ์•ฑ์˜ ์•ฑ ID๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.
os.environ["NCP_CLOVASTUDIO_APP_ID_SEGMENTATION"]ย =ย input("NCP CLOVA Studio Segmentation App ID: ")
NCP CLOVA Studio Segmentation App ID:  <your Segmentation App ID>

ย 

# ์ž„๋ฒ ๋”ฉ ํ…Œ์ŠคํŠธ์•ฑ์˜ ์•ฑ ID๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.
os.environ["NCP_CLOVASTUDIO_APP_ID"]ย =ย input("NCP CLOVA Studio App ID: ")
NCP CLOVA Studio App ID:  <your App ID>


2. Load

spacer.png

RAG์˜ ์ฒซ๋ฒˆ์งธ ์ž‘์—…์€ ์ถ”ํ›„ LLM์ด ์‘๋‹ต์˜ ๋ฌธ๋งฅ์œผ๋กœ ์‚ฌ์šฉํ•  ๋•Œ RAG ์‹œ์Šคํ…œ์ด ์ฐธ๊ณ ํ•  HTML ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•˜๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. txt ํŒŒ์ผ(clovastudiourl.txt)์— ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ์‚ฌ์ดํŠธ URL์„ ์ž‘์„ฑํ•œ ํ›„, wget ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ LangChain์˜ Document Loader ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•˜์—ฌ HTML ๋ฐ์ดํ„ฐ๋ฅผ ์ž‘์—…์— ํŽธํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋กœ๋”ฉํ•œ ๋ฐ์ดํ„ฐ ์•ˆ์— ์‚ฌ์ดํŠธ URL์„ ๋„ฃ์–ด ์ถ”ํ›„ ๋‹ต๋ณ€๊ณผ ํ•จ๊ป˜ ์‚ฌ์ดํŠธ ์ฃผ์†Œ๊ฐ€ ์ถœ๋ ฅ๋˜๊ฒŒ ํ•˜์—ฌ ์งˆ๋ฌธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ต๋ณ€์˜ ๋‚ด์šฉ์ด ์žˆ๋Š” ์‹ค์ œ URL์„ ํ•จ๊ป˜ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

ย 

1)ย txtย โ†’ย html ๋ณ€ํ™˜ ๋ฐ ์›๋ณธ ์‚ฌ์ดํŠธ ์ฃผ์†Œ mapping

importย subprocess
ย 
url_to_filename_mapย =ย {}
ย ย 
withย open("clovastudiourl.txt",ย "r") asย file:
ย ย ย ย urlsย =ย [url.strip()ย forย urlย inย file.readlines()]
ย ย 
folder_pathย =ย "clovastudioguide"
ย ย 
ifย notย os.path.exists(folder_path):
ย ย ย ย os.makedirs(folder_path)
ย ย 
forย urlย inย urls:
ย ย ย ย filenameย =ย url.split("/")[-1]ย +ย ".html"
ย ย ย ย file_pathย =ย os.path.join(folder_path, filename)
ย ย ย ย subprocess.run(["wget",ย "--user-agent=RAGCookbook-Crawler/1.0",ย "-O", file_path, url], check=True)
ย ย ย ย url_to_filename_map[url]ย =ย filename
ย ย 
withย open("url_to_filename_map.json",ย "w") as map_file:
ย ย ย ย json.dump(url_to_filename_map, map_file)
#Output
--2024-08-26ย 13:37:02--ย  https://guide.ncloud-docs.com/docs/clovastudio-info
Resolving guide.ncloud-docs.com (guide.ncloud-docs.com)...ย 104.18.6.159,ย 104.18.7.159
Connecting to guide.ncloud-docs.com (guide.ncloud-docs.com)|104.18.6.159|:443... connected.
HTTP request sent, awaiting response...ย 200ย OK
Length: unspecified [text/html]
Saving to: โ€˜clovastudioguide_0822/clovastudio-info.htmlโ€™

ย ย ย ย ย 0K .......... .......... .......... .......... ..........ย 21.4M
ย ย ย ย 50K .......... .......... .......... ..........ย ย ย ย ย ย ย ย ย ย ย ย 83.3M=0.003s

2024-08-26ย 13:37:02ย (32.1ย MB/s) - โ€˜clovastudioguide_0822/clovastudio-info.htmlโ€™ saved [92957]

--2024-08-26ย 13:37:02--ย  https://guide.ncloud-docs.com/docs/clovastudio-procedure
Resolving guide.ncloud-docs.com (guide.ncloud-docs.com)...ย 104.18.6.159,ย 104.18.7.159
Connecting to guide.ncloud-docs.com (guide.ncloud-docs.com)|104.18.6.159|:443... connected.
HTTP request sent, awaiting response...ย 200ย OK
Length: unspecified [text/html]
Saving to: โ€˜clovastudioguide_0822/clovastudio-procedure.htmlโ€™

ย ย ย ย ย 0K .......... .......... .......... .......... ..........ย 18.0M
ย ย ย ย 50K .......... .......... ..........ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 21.6M=0.004s

2024-08-26ย 13:37:03ย (19.2ย MB/s) - โ€˜clovastudioguide_0822/clovastudio-procedure.htmlโ€™ saved [82265]

LangChain์„ ํ™œ์šฉํ•ด ๋กœ๋”ฉํ•œ html์€, ํŒŒ์ผ์„ ์ €์žฅํ•œ ๋””๋ ‰ํ† ๋ฆฌ์˜ ์ฃผ์†Œ๋ฅผ metadata์˜ 'source'๋กœ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ถ”ํ›„ ๋‹ต๋ณ€ ์ œ๊ณต์‹œ, ๋””๋ ‰ํ† ๋ฆฌ ์ฃผ์†Œ๊ฐ€ ์•„๋‹Œ ์‹ค์ œ URL์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด, LangChain์ด ๋กœ๋”ฉํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. html์„ ๋กœ๋”ฉํ•  ๋•Œ ํŒŒ์ผ๋ช…์„ URL๋กœ ํ•  ๊ฒฝ์šฐ, /์™€ : ๊ธฐํ˜ธ๋กœ ์ธํ•ด ํŒŒ์ผ๋ช…์ด ๊นจ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.ย ๋”ฐ๋ผ์„œ ์›๋ณธ URL๊ณผ ๋กœ๋”ฉํ•œ HTML ํŒŒ์ผ์„ ์Œ์œผ๋กœ mappingํ•˜๊ณ , ์ด ์ •๋ณด๋ฅผ json ํ˜•์‹์œผ๋กœ "url_to_filename_map"์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.ย ์ดํ›„,ย html ํŒŒ์ผ์˜ ์ €์žฅ ๊ฒฝ๋กœ๊ฐ€ ๋‹ด๊ธด 'source'๋ฅผ, ์ด json ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด ์‹ค์ œ URL๋กœ ๋ณ€ํ™˜ํ•ด์ค๋‹ˆ๋‹ค.ย ์œ„ ์ฝ”๋“œ๋Š”, json ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๋‹จ๊ณ„๊นŒ์ง€์ž…๋‹ˆ๋‹ค.ย 

์™ธ๋ถ€ ์‚ฌ์ดํŠธ ์ ‘๊ทผ ํ—ˆ์šฉ ์ฝ”๋“œ

  • ย wget ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด HTML ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋”ฉํ•˜๊ธฐ ์ „, ํ•ด๋‹น ์›น์‚ฌ์ดํŠธ์˜ robots.txt๋ฅผ ํ™•์ธํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋กœ ํ™œ์šฉํ•  ์›นํŽ˜์ด์ง€์˜ ํฌ๋กค๋Ÿฌ ์ ‘๊ทผ ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. NCP์˜ ํด๋กœ๋ฐ”์ŠคํŠœ๋””์˜ค ์‚ฌ์šฉ ๊ฐ€์ด๋“œ์˜ ๊ฒฝ์šฐย User-agent: * Allow:/ ๋กœ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • ์ผ๋ถ€ ์‚ฌ์ดํŠธ๋Š” User-agent๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์„ ์ถ”๊ฐ€ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ์›น ์„œ๋ฒ„์˜ ์š”์ฒญ ์ฐจ๋‹จ์ด๋‚˜ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย ๋Œ€๋ถ€๋ถ„ ์„œ๋ฒ„์˜ --user-agent๋Š” Mozilla/5.0๋ฅผ ๋”ฐ๋ฅด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.ย ํ•˜์ง€๋งŒย ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋ถˆ๋Ÿฌ์˜ค๊ณ ์ž ํ•˜๋Š” ์‚ฌ์ดํŠธ์˜ --user-agent ์ •๋ณด๋ฅผ ์ง์ ‘ ์ฐพ์•„์„œ ์ ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.


2)ย LangChain ํ™œ์šฉ HTML ๋กœ๋”ฉ

2-1)ย BSHTMLLoader ์‚ฌ์šฉ

#!pip install beautifulsoup4 lxml #ํ•„์š”์‹œ beautifulsoup4 ์„ค์น˜
fromย langchain_community.document_loadersย importย BSHTMLLoader
ย 
# ํด๋” ์ด๋ฆ„์— ๋งž๊ฒŒ ์ˆ˜์ •
html_files_dirย =ย Path('clovastudioguide')
ย ย 
html_filesย =ย list(html_files_dir.glob("*.html"))
ย ย 
# ๋ชจ๋“  ๋ฌธ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋ฆฌ์ŠคํŠธ ์ดˆ๊ธฐํ™”
clovastudiodatasย =ย []
ย ย 
# ์ฐพ์€ HTML ํŒŒ์ผ๋“ค์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌ
forย html_fileย inย html_files:
ย ย ย ย # ๊ฐ ํŒŒ์ผ์— ๋Œ€ํ•ด BSHTMLLoader ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
ย ย ย ย loaderย =ย BSHTMLLoader(str(html_file))
ย ย ย ย document_dataย =ย loader.load()
ย ย ย ย # ๋กœ๋“œ๋œ ๋ฌธ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€
ย ย ย ย clovastudiodatas.append(document_data)
ย ย ย ย print(f"Processed {html_file}")
#Output
Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-info.html
Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-procedure.html

ย 

2-2)ย ย UnstructuredHTMLLoader ์‚ฌ์šฉ
์ด์ „ ๋‹จ๊ณ„์—์„œ txt ํŒŒ์ผ์„ HTML๋กœ ๋ณ€ํ™˜ํ•œ ํ›„, LangChain์˜ UnstructuredHTMLLoader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ดํ›„ ๋ฌธ๋‹จ ๋‚˜๋ˆ„๊ธฐ์™€ ์ž„๋ฒ ๋”ฉ์„ ์œ„ํ•ด machine readableํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด ์ค๋‹ˆ๋‹ค.

#!pip install unstructured #ํ•„์š”์‹œ unstructured ์„ค์น˜
fromย langchain_community.document_loadersย importย UnstructuredHTMLLoader
ย 
# ํด๋” ์ด๋ฆ„์— ๋งž๊ฒŒ ์ˆ˜์ •
html_files_dirย =ย Path('./clovastudioguide')
ย ย 
html_filesย =ย list(html_files_dir.glob("*.html"))
ย ย 
clovastudiodatasย =ย []
ย ย 
forย html_fileย inย html_files:
ย ย ย ย loaderย =ย UnstructuredHTMLLoader(str(html_file))
ย ย ย ย document_dataย =ย loader.load()
ย ย ย ย clovastudiodatas.append(document_data)
ย ย ย ย print(f"Processed {html_file}")
#Output
Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-info.html
Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-procedure.html

๊ฐ HTML์—๋Š” page_content์— ๋ชจ๋“  ํ…์ŠคํŠธ๊ฐ€, metadata์˜ 'source'์—๋Š” ์ €์žฅ ๊ฒฝ๋กœ๊ฐ€ ๊ธฐ๋ก๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.ย ์œ„๋Š”, ๋กœ๋”ฉ๋œ ๋ฐ์ดํ„ฐ ์ค‘ ํ•˜๋‚˜์ธ clovastudio-info.html์˜ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค. metadata์˜ 'source'์— ์‹ค์ œ URL์ด ์•„๋‹Œ html ํŒŒ์ผ์ด ์ €์žฅ๋œ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ๊ฐ€ ๋‹ด๊ธด ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย Mapping์„ ํ†ตํ•ด ์ด 'source'๋ฅผ ์‹ค์ œ URL๋กœ ๋ฐ”๊พธ๋Š” ์ž‘์—…์„ ๋‹ค์Œ ๋‹จ๊ณ„์— ์ง„ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


3)ย Mapping ์ •๋ณด๋ฅผ ํ™œ์šฉํ•ด 'source'๋ฅผ ์‹ค์ œ URL๋กœ ๋Œ€์ฒด

์•„๋ž˜์˜ output์€ ์•ž์„  ์˜ˆ์‹œ์ธ clovastudio-info.html์˜ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.ย Mapping ์ •๋ณด๋ฅผ ํ™œ์šฉํ•ด 'source'๋ฅผ ์‹ค์ œ url๋กœ ๋ฐ”๊พผ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย 

withย open("url_to_filename_map.json",ย "r") as map_file:
ย ย ย ย url_to_filename_mapย =ย json.load(map_file)
ย ย 
filename_to_url_mapย =ย {v: kย forย k, vย inย url_to_filename_map.items()}
ย ย 
# clovastudiodatas ๋ฆฌ์ŠคํŠธ์˜ ๊ฐ Document ๊ฐ์ฒด์˜ 'source' ์ˆ˜์ •
forย doc_listย inย clovastudiodatas:
ย ย ย ย forย docย inย doc_list:
ย ย ย ย ย ย ย ย extracted_filenameย =ย doc.metadata["source"].split("/")[-1]
ย ย ย ย ย ย ย ย ifย extracted_filenameย inย filename_to_url_map:
ย ย ย ย ย ย ย ย ย ย ย ย doc.metadata["source"]ย =ย filename_to_url_map[extracted_filename]
ย ย ย ย ย ย ย ย else:
ย ย ย ย ย ย ย ย ย ย ย ย print(f"Warning: {extracted_filename}์— ํ•ด๋‹นํ•˜๋Š” URL์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
print(doc.metadata["source"])
#Output
https://guide.ncloud-docs.com/docs/clovastudio-procedure
# ์ด์ค‘ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ’€์–ด์„œ ํ•˜๋‚˜์˜ ๋ฆฌ์ŠคํŠธ๋กœ ๋งŒ๋“œ๋Š” ์ž‘์—…
clovastudiodatas_flattenedย =ย [itemย forย sublistย inย clovastudiodatasย forย itemย inย sublist]

HTML๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ ๋กœ๋”ฉ๋˜์—ˆ๋Š”์ง€ ๊ฒฐ๊ณผ๋ฌผ์„ ์ถœ๋ ฅํ•ด ํ™•์ธํ•ด๋ด…๋‹ˆ๋‹ค.

# ์ฒ˜์Œ 3๊ฐœ ๋ฌธ์„œ์˜ URL๊ณผ ๋‚ด์šฉ ์ผ๋ถ€ ์ถœ๋ ฅ
forย docย inย clovastudiodatas_flattened[:2]:
ย ย ย ย print(f"URL: {doc.metadata['source']}")
ย ย ย ย print(f"{doc.page_content[:100]}...")
ย ย ย ย print("-"ย *ย 50)
#Output
URL: https://guide.ncloud-docs.com/docs/clovastudio-info
Login
Korean
English
Ja - ๆ—ฅๆœฌ่ชž
Korean
API ๊ฐ€์ด๋“œ
(...์ดํ•˜ ์ค‘๋žต)


3. Chunking

spacer.png

์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์ด ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ ๋‹นํ•œ ํฌ๊ธฐ๋กœ raw data๋ฅผ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ๋งˆ๋‹ค ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ† ํฐ ์ˆ˜์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.ย CLOVA Studio์˜ ๋ฌธ๋‹จ ๋‚˜๋ˆ„๊ธฐ API๋ฅผ ํ™œ์šฉํ•ด ์•ž์„œ ๋งŒ๋“  raw data๋ฅผ ๋‚˜๋ˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ์ด ์ง์ ‘ ๋ฌธ์žฅ๋“ค๊ฐ„์˜ ์˜๋ฏธ ์œ ์‚ฌ๋„๋ฅผ ์ฐพ์•„ ์ตœ์ ์˜ ์ฒญํฌ(chunk) ๊ฐœ์ˆ˜์™€ ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” 1๊ฐœ ์ฒญํฌ ํฌ๊ธฐ(๊ธ€์ž ์ˆ˜)๋ฅผ ์ง์ ‘ ์„ค์ •ํ•˜์—ฌ ๋ฌธ๋‹จ์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€๋กœ, ํ›„์ฒ˜๋ฆฌ(postProcess = True)๋ฅผ ํ†ตํ•ด chunk๋‹น ๊ธ€์ž ์ˆ˜์˜ ์ƒํ•œ์„ ๊ณผ ํ•˜ํ•œ์„ ์„ postProcessMaxSize์™€ postProcessMinSize๋กœ ์กฐ์ ˆํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

importย http.client
fromย httpย importย HTTPStatus
ย ย 
classย CLOVAStudioExecutor:
ย ย ย ย defย __init__(self, host, request_id=None):
ย ย ย ย ย ย ย ย self._hostย =ย host
ย ย ย ย ย ย ย ย self._api_keyย =ย os.environ["NCP_CLOVASTUDIO_API_KEY"]
ย ย ย ย ย ย ย ย self._api_key_primary_valย =ย os.environ["NCP_APIGW_API_KEY"]
ย ย ย ย ย ย ย ย self._request_idย =ย request_idย orย str(uuid.uuid4())
ย ย 
ย ย ย ย defย _send_request(self, completion_request, endpoint):
ย ย ย ย ย ย ย ย headersย =ย {
ย ย ย ย ย ย ย ย ย ย ย ย 'Content-Type':ย 'application/json; charset=utf-8',
ย ย ย ย ย ย ย ย ย ย ย ย 'X-NCP-CLOVASTUDIO-API-KEY':ย self._api_key,
ย ย ย ย ย ย ย ย ย ย ย ย 'X-NCP-APIGW-API-KEY':ย self._api_key_primary_val,
ย ย ย ย ย ย ย ย ย ย ย ย 'X-NCP-CLOVASTUDIO-REQUEST-ID':ย self._request_id
ย ย ย ย ย ย ย ย }
ย ย 
ย ย ย ย ย ย ย ย connย =ย http.client.HTTPSConnection(self._host)
ย ย ย ย ย ย ย ย conn.request('POST', endpoint, json.dumps(completion_request), headers)
ย ย ย ย ย ย ย ย responseย =ย conn.getresponse()
ย ย ย ย ย ย ย ย statusย =ย response.status
ย ย ย ย ย ย ย ย resultย =ย json.loads(response.read().decode(encoding='utf-8'))
ย ย ย ย ย ย ย ย conn.close()
ย ย ย ย ย ย ย ย returnย result, status
ย ย 
ย ย ย ย defย execute(self, completion_request, endpoint):
ย ย ย ย ย ย ย ย res, statusย =ย self._send_request(completion_request, endpoint)
ย ย ย ย ย ย ย ย ifย statusย ==ย HTTPStatus.OK:
ย ย ย ย ย ย ย ย ย ย ย ย returnย res, status
ย ย ย ย ย ย ย ย else:
ย ย ย ย ย ย ย ย ย ย ย ย error_messageย =ย res.get("status", {}).get("message",ย "Unknown error")ย ifย isinstance(res,ย dict)ย elseย "Unknown error"
ย ย ย ย ย ย ย ย ย ย ย ย raiseย ValueError(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: HTTP {status}, ๋ฉ”์‹œ์ง€: {error_message}")
ย ย 
classย SegmentationExecutor(CLOVAStudioExecutor):
ย ย ย ย defย execute(self, completion_request):
ย ย ย ย ย ย ย ย app_idย =ย os.environ["NCP_CLOVASTUDIO_APP_ID_SEGMENTATION"]
ย ย ย ย ย ย ย ย endpointย =ย f'/testapp/v1/api-tools/segmentation/{app_id}'
ย ย ย ย ย ย ย ย res, statusย =ย super().execute(completion_request, endpoint)
ย ย ย ย ย ย ย ย ifย statusย ==ย HTTPStatus.OKย andย "result"ย inย res:
ย ย ย ย ย ย ย ย ย ย ย ย returnย res["result"]["topicSeg"]
ย ย ย ย ย ย ย ย else:
ย ย ย ย ย ย ย ย ย ย ย ย error_messageย =ย res.get("status", {}).get("message",ย "Unknown error")ย ifย isinstance(res,ย dict)ย elseย "Unknown error"
ย ย ย ย ย ย ย ย ย ย ย ย raiseย ValueError(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: HTTP {status}, ๋ฉ”์‹œ์ง€: {error_message}")
ย ย ย ย ย ย ย ย ย 
ifย __name__ย ==ย "__main__":
ย ย ย ย # ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ
ย ย ย ย required_env_varsย =ย [
ย ย ย ย ย ย ย ย "NCP_CLOVASTUDIO_API_KEY",
ย ย ย ย ย ย ย ย "NCP_APIGW_API_KEY",
ย ย ย ย ย ย ย ย "NCP_CLOVASTUDIO_APP_ID_SEGMENTATION"
ย ย ย ย ]
ย ย ย ย ย 
ย ย ย ย missing_varsย =ย [varย forย varย inย required_env_varsย ifย notย os.environ.get(var)]
ย ย ย ย ifย missing_vars:
ย ย ย ย ย ย ย ย raiseย ValueError(f"Missing required environment variables: {', '.join(missing_vars)}")
ย ย ย ย ย 
ย ย ย ย segmentation_executorย =ย SegmentationExecutor(
ย ย ย ย ย ย ย ย host="clovastudio.apigw.ntruss.com"
ย ย ย ย )
ย ย 
ย ย ย ย chunked_htmlย =ย []
ย ย 
ย ย ย ย forย htmldataย inย tqdm(clovastudiodatas_flattened):
ย ย ย ย ย ย ย ย try:
ย ย ย ย ย ย ย ย ย ย ย ย request_dataย =ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "postProcessMaxSize":ย 100,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "alpha":ย -100,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "segCnt":ย -1,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "postProcessMinSize":ย -1,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "text": htmldata.page_content,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "postProcess":ย True
ย ย ย ย ย ย ย ย ย ย ย ย }
ย ย ย ย ย ย ย ย ย ย ย ย ย ย 
ย ย ย ย ย ย ย ย ย ย ย ย response_dataย =ย segmentation_executor.execute(request_data)
ย ย ย ย ย ย ย ย ย ย ย ย result_dataย =ย [' '.join(segment)ย forย segmentย inย response_data]
ย ย ย ย ย ย 
ย ย ย ย ย ย ย ย ย ย ย ย forย paragraphย inย result_data:
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย chunked_documentย =ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "metadata": htmldata.metadata["source"],
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย "page_content": paragraph
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย }
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย chunked_html.append(chunked_document)
ย ย ย ย ย ย 
ย ย ย ย ย ย ย ย exceptย Exception as e:
ย ย ย ย ย ย ย ย ย ย ย ย print(f"Error processing data from {htmldata.metadata['source']}: {e}")
ย ย ย ย ย ย ย ย ย ย ย ย # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ํ˜„์žฌ ๋ฐ˜๋ณต์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋‹ค์Œ์œผ๋กœ ์ง„ํ–‰
ย ย ย ย ย ย ย ย ย ย ย ย continue
ย ย ย 
ย ย ย ย print(len(chunked_html))
#Output
100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ|ย 2/2ย [00:04<00:00,ย ย 2.06s/it]
108

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด 108๊ฐœ์˜ chunk๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.ย  ์ƒ˜ํ”Œ ์ฒญํฌ๋ฅผ 3๊ฐœ ์ถœ๋ ฅํ•ด ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

# ์ฒญํฌ ๋ถ„์„ ๋ฐ ์ถœ๋ ฅ
print(f"\n์ด ์ฒญํฌ ์ˆ˜: {len(chunked_html)}")
ย 
# ์ƒ˜ํ”Œ ์ฒญํฌ ์ถœ๋ ฅ
print("\n์ƒ˜ํ”Œ ์ฒญํฌ (์ฒ˜์Œ 3๊ฐœ):")
forย i, chunkย inย enumerate(chunked_html[:3],ย 1):
ย ย ย ย print(f"\n์ฒญํฌ {i}:")
ย ย ย ย print(f"๋ฉ”ํƒ€๋ฐ์ดํ„ฐ: {chunk['metadata']}")
ย ย ย ย print(f"๋‚ด์šฉ: {chunk['page_content']}")
ย ย ย ย print(f"๊ธธ์ด: {len(chunk['page_content'])} ๋ฌธ์ž")
#Output
์ด ์ฒญํฌ ์ˆ˜:ย 108

์ƒ˜ํ”Œ ์ฒญํฌ (์ฒ˜์Œย 3๊ฐœ):

์ฒญํฌย 1:
๋ฉ”ํƒ€๋ฐ์ดํ„ฐ: https://guide.ncloud-docs.com/docs/clovastudio-info
๋‚ด์šฉ: Login Korean English Ja - ๆ—ฅๆœฌ่ชž Korean API ๊ฐ€์ด๋“œ CLI ๊ฐ€์ด๋“œ ๋‚ด์šฉ x HOME ํฌํ„ธ ๋ฐ ์ฝ˜์†” ๋„ค์ด๋ฒ„ ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ ์‚ฌ์šฉ ํ™˜๊ฒฝ Compute Containers Storage Networking Database Security
๊ธธ์ด:ย 145ย ๋ฌธ์ž

์ฒญํฌย 2:
๋ฉ”ํƒ€๋ฐ์ดํ„ฐ: https://guide.ncloud-docs.com/docs/clovastudio-info
๋‚ด์šฉ: AI Services AIยทNAVER API Application Services Big Data & Analytics
๊ธธ์ด:ย 66ย ๋ฌธ์ž

์ฒญํฌย 3:
๋ฉ”ํƒ€๋ฐ์ดํ„ฐ: https://guide.ncloud-docs.com/docs/clovastudio-info
๋‚ด์šฉ: Blockchain Business Applications Content Delivery Developer Tools Digital Twin Gaming
๊ธธ์ด:ย 85ย ๋ฌธ์ž


4. Embedding

spacer.png

์ž„๋ฒ ๋”ฉ ๋‹จ๊ณ„๋Š” ์•ž์„œ Chunking ๋‹จ๊ณ„์—์„œ ์ƒ์„ฑ๋œ chunk๋“ค์„ย ๊ธฐ๊ณ„๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜์น˜์  ํ˜•ํƒœ์ธ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ๋ฌธ์„œ์˜ ์˜๋ฏธ๋ฅผ ๋ฒกํ„ฐ(์ˆซ์ž์˜ ๋ฐฐ์—ด) ํ˜•ํƒœ๋กœ ํ‘œํ˜„ํ•จ์œผ๋กœ์จ, ์œ ์ €์˜ ์งˆ๋ฌธ์— ๋Œ€ํ•ด ๊ด€๋ จ์žˆ๋Š” chunk๋ฅผ ๊ฒ€์ƒ‰ํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๋Š” ์œ ์‚ฌ๋„ ๊ณ„์‚ฐ์‹œ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.


1)ย LangChain์„ ํ†ตํ•ด CLOVA Studio Embedding ํ™œ์šฉํ•˜๊ธฐ

1-1)ย ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์„ค์ •ํ•˜๊ธฐ
๋ฌธ๋‹จ ๋‚˜๋ˆ„๊ธฐ API๋ฅผ ํ†ตํ•ด ๋ถ„ํ• ๋œ ์ฒญํฌ(chunked_html)๋ฅผ CLOVA Studio์˜ ์ž„๋ฒ ๋”ฉ API๋ฅผ ์‚ฌ์šฉํ•ดย 1024์ฐจ์› ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. <1. ์‚ฌ์ „ ์ค€๋น„> ๋‹จ๊ณ„์—์„œ ๋ฐœ๊ธ‰ํ•œ ์ž„๋ฒ ๋”ฉ V2 ํ…Œ์ŠคํŠธ์•ฑ์„ ์‚ฌ์šฉํ–ˆ์œผ๋ฉฐ, ์•ž์„œ ์ €์žฅํ•ด๋‘” APIํ‚ค ๋ฐ app id ๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ž„๋ฒ ๋”ฉ V2๋Š” bge-m3 ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋ฉฐ,ย ์ด ๋ชจ๋ธ์€ย ์ž„๋ฒ ๋”ฉ ๊ณผ์ •์—์„œ ์œ ์‚ฌ๋„ ํŒ๋‹จ์„ ์œ„ํ•ด ์ฝ”์‚ฌ์ธ ๊ฑฐ๋ฆฌ(Cosine)๋ฅผ ๊ฑฐ๋ฆฌ ๋‹จ์œ„๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์„ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด clir-emb-dolphin ๋ชจ๋ธ์ด ๊ธฐ๋ณธ ์„ค์ •๋˜๋ฏ€๋กœ,ย ย ClovaXEmbeddings์˜ย ๋ชจ๋ธ์„ย bge-m3๋กœ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

fromย langchain_community.embeddingsย importย ClovaXEmbeddings
ย 
clovax_embeddingsย =ย ClovaXEmbeddings(model='bge-m3')ย # ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ์„ค์ •ํ•ด์ฃผ์„ธ์š”

์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์€ ๋‹ˆ์ฆˆ์— ๋”ฐ๋ผ ์„ธ๊ฐ€์ง€ ๋ชจ๋ธ ์ค‘, ์„ ํƒํ•˜์—ฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย ๊ฑฐ๋ฆฌ ๋‹จ์œ„๋ฅผ ์ž„๋ฒ ๋”ฉ๋ถ€ํ„ฐ ์ธ๋ฑ์‹ฑ, ๊ฒ€์ƒ‰๊นŒ์ง€ ์ผ์น˜์‹œ์ผœ์•ผ ๋ฐ์ดํ„ฐ์˜ ํ’ˆ์งˆ์ด ํ–ฅ์ƒ๋˜๋ฏ€๋กœ, ์ž„๋ฒ ๋”ฉ ๊ณผ์ •์—์„œ ์‚ฌ์šฉํ•œ ๋ชจ๋ธ(emb / sts / bge-m3)์ด ๋ฌด์—‡์ธ์ง€ ๊ธฐ์–ตํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. (์ž์„ธํ•œ ๋‚ด์šฉ์€ย ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค ๊ฐ€์ด๋“œย ์ฐธ๊ณ )ย 

๋„๊ตฌ๋ช…
๋ชจ๋ธ๋ช…
์ตœ๋Œ€ ํ† ํฐ ์ˆ˜
๋ฒกํ„ฐ ์ฐจ์›
๊ถŒ์žฅ ๊ฑฐ๋ฆฌ ์ง€ํ‘œ
์ž„๋ฒ ๋”ฉ clir-emb-dolphinย (๋žญ์ฒด์ธ ๊ธฐ๋ณธ์„ค์ •) 500 ํ† ํฐ 1024 IP (Inner/Dot/Scalar Product; ๋‚ด์ )
์ž„๋ฒ ๋”ฉ clir-sts-dolphin 500 ํ† ํฐ 1024 Cosine Similarity (์ฝ”์‚ฌ์ธ ์œ ์‚ฌ๋„)
์ž„๋ฒ ๋”ฉ v2 bge-m3 8192 ํ† ํฐ 1024 Cosine Similarity (์ฝ”์‚ฌ์ธ ์œ ์‚ฌ๋„)

ย 

ย 

1-2)ย ์ž„๋ฒ ๋”ฉ ๊ฒฐ๊ณผ ํ™•์ธํ•˜๊ธฐ
๋‹ค์Œ ์˜ˆ์ œ ์ฝ”๋“œ๋Š” "ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค"๋ผ๋Š” ๊ธ€์ž์— ๋Œ€ํ•ด CLOVA Studio์˜ ์ž„๋ฒ ๋”ฉ API๋ฅผ ์‚ฌ์šฉํ•ด ์ž„๋ฒ ๋”ฉ์„ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ž„๋ฒ ๋”ฉ์€ ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ์˜ ์˜๋ฏธ๋ฅผ ๋ฒกํ„ฐ ๊ณต๊ฐ„์—์„œ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.ย 

textย =ย "ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค"
ย 
clovax_embeddings.embed_query(text)

๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด "ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค"๋ผ๋Š” ํ…์ŠคํŠธ์— ๋Œ€ํ•œ ์ž„๋ฒ ๋”ฉ ๋ฒกํ„ฐ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฒกํ„ฐ์˜ ํฌ๊ธฐ๋Š” 1024๋กœ, ์‹ค์ˆ˜ ๋ฐฐ์—ด์˜ ํ˜•ํƒœ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

#Output
[-0.13467026,
ย -0.54031295,
ย -0.5842034,
ย 1.6036854,
ย -1.2513447,
ย -0.8417246,
ย -0.51857895,
...์ƒ๋žต]


2)ย ๋ฆฌ์ŠคํŠธ ํ˜•์‹์˜ documents๋กœ ๋งŒ๋“ค๊ธฐ

์ž„๋ฒ ๋”ฉ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์•ž์„œ ๋งŒ๋“  ์ฒญํฌ๋ฅผย Documentย ๊ฐ์ฒด๋“ค์˜ ๋ฆฌ์ŠคํŠธ ํ˜•์‹์˜ย documents๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

fromย langchain_core.documentsย importย Document
ย 
documentsย =ย []
ย 
forย index, itemย inย enumerate(chunked_html):
ย ย ย ย docย =ย Document(
ย ย ย ย ย ย ย ย page_content=str(item['page_content']),
ย ย ย ย ย ย ย ย metadata={"source": item['metadata']},
ย ย ย ย ย ย ย ย id=str(uuid4())
ย ย ย ย )
ย ย ย ย documents.append(doc)

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜๋œ documents์˜ ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# documents ๊ตฌ์กฐ ์ฒดํฌ
forย i, docย inย enumerate(documents):
ย ย ย ย print(f"Document {i}:")
ย ย ย ย print(f"ย  Page Content: {doc.page_content[:100]}...")ย ย # ์ฒซ 100์ž๋งŒ ์ถœ๋ ฅ
ย ย ย ย print(f"ย  Metadata: {doc.metadata}")
ย ย ย ย print(f"ย  Page Content Type: {type(doc.page_content)}")
ย ย ย ย print(f"ย  Metadata Type: {type(doc.metadata)}")
ย ย ย ย print("-"ย *ย 40)
#Output
Documentย 0:
ย ย Page Content: CLOVA Studio ๊ฐœ๋… Login Korean English Ja - ๆ—ฅๆœฌ่ชž Korean API ๊ฐ€์ด๋“œ CLI ๊ฐ€์ด๋“œ๋‚ด์šฉ x HOME ํฌํ„ธ ๋ฐ ์ฝ˜์†” ๋„ค์ด๋ฒ„ ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ ์‚ฌ...
ย ย Metadata: {'source':ย 'https://guide.ncloud-docs.com/docs/clovastudio-info',ย 'title':ย 'CLOVA Studio ๊ฐœ๋…'}
ย ย Page Content Type: <classย 'str'>
ย ย Metadata Type: <classย 'dict'>
----------------------------------------
Documentย 1:
ย ย Page Content: ์˜๊ฒฌ์„ ๋ณด๋‚ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.Classic/VPC ํ™˜๊ฒฝ์—์„œ ์ด์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.CLOVA Studio๋ฅผ ์ด์šฉํ•˜๋Š” ์ „์ฒด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ•™์Šตํ•˜๊ธฐ์— ์•ž์„œ CLOVA Studio์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€...
ย ย Metadata: {'source':ย 'https://guide.ncloud-docs.com/docs/clovastudio-info',ย 'title':ย 'CLOVA Studio ๊ฐœ๋…'}
ย ย Page Content Type: <classย 'str'>
ย ย Metadata Type: <classย 'dict'>
----------------------------------------
...(์ดํ•˜ ์ค‘๋žต)

ย 

5. Vector store

spacer.png

์ด ๋‹จ๊ณ„๋Š” ์•ž์„œ ์ž„๋ฒ ๋”ฉํ•œ ๊ฒฐ๊ณผ๋ฅผ Vector Store์— ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.ย ย ๋ฒกํ„ฐ DB๋Š” ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ž…๋‹ˆ๋‹ค.ย ์ด ์˜ˆ์ œ์—์„œ๋Š” ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Chroma์™€ FAISS๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.ย Langchain์˜ย Vector DB ๋น„๊ต ๋ฌธ์„œ๋ฅผ ๋ณด๊ณ , ์ž์‹ ์˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— ๋งž๋Š” ์ œํ’ˆ์„ ํ™œ์šฉํ•˜์„ธ์š”.


1)ย Vector DB์— ์ €์žฅํ•˜๊ธฐ

1-1) Chroma ํ™œ์šฉ

Chroma๋Š” ์˜คํ”ˆ์†Œ์Šค Vector DB์ด๋ฉฐ, ํด๋ผ์šฐ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€์•Š๊ณ  ํด๋ผ์ด์–ธํŠธ์šฉ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉดย Apache 2.0 ๋ผ์ด์„ผ์Šค์— ๋ฌด๋ฃŒ๋กœ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย ๋ณธ ์˜ˆ์ œ์—์„œ๋Š” chroma๋ฅผ ํ™œ์šฉํ•ด ์ปฌ๋ ‰์…˜์„ ์ƒ์„ฑํ•˜๊ณ , ์ปฌ๋ ‰์…˜์— ์•ž์„œ ๋งŒ๋“  ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.ย hnsw:space์˜ ๊ฐ’์„ ์„ค์ •ํ•จ์œผ๋กœ์จ ์ž„๋ฒ ๋”ฉ ๊ณต๊ฐ„์˜ ๊ฑฐ๋ฆฌ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉ์ž ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž„๋ฒ ๋”ฉ์— ์‚ฌ์šฉํ•  ๋ชจ๋ธ์€ ์œ ์‚ฌ๋„ ํŒ๋‹จ์„ ์œ„ํ•ด ๋ฒกํ„ฐ์˜ cosine ๊ฑฐ๋ฆฌ ๋‹จ์œ„๋กœ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์ปฌ๋ ‰์…˜ ๋˜ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ "cosine"์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.ย 

*๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ip, cosine, l2(๊ธฐ๋ณธ๊ฐ’) ์ค‘์— ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, ์ž„๋ฒ ๋”ฉ ํด๋ž˜์Šค์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ์‹œ ๋ฉˆ์ถ”๊ฒŒ๋” ํ•˜๋Š” ๋กœ์ง์„ ์ผ๋ถ€ ์ถ”๊ฐ€ํ•ด, ๋‹ค๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ์žฌ์‹คํ–‰์˜ ๋ถ€๋‹ด์„ ์ค„์˜€์Šต๋‹ˆ๋‹ค.ย LangChain ๊ณต์‹ ๋ฌธ์„œ์—์„œ Chroma๋ฅผ ํ™œ์šฉํ•˜๋Š” ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#chroma ๋‹ค์šด๋ฐ›๊ธฐ
%pip installย -qUย "langchain-chroma>=0.1.2"
importย chromadb
fromย langchain_chromaย importย Chroma
ย 
# ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์ •์˜
clovax_embeddingsย =ย ClovaXEmbeddings(model='bge-m3')
ย 
# ๋กœ์ปฌ ํด๋ผ์ด์–ธํŠธ ๊ฒฝ๋กœ ์ง€์ •
clientย =ย chromadb.PersistentClient(path="./chroma_langchain_db")ย #์ €์žฅํ•  ๋กœ์ปฌ ๊ฒฝ๋กœ
ย 
# Chroma ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ
chroma_collectionย =ย client.get_or_create_collection(
ย ย ย ย name="clovastudiodatas_docs",ย #collection์ด ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ์ด๋ฆ„๋„ ๊ผญ ๋ณ€๊ฒฝํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ย ย ย ย metadata={"hnsw:space":ย "cosine"}ย #์‚ฌ์šฉํ•˜๋Š” ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์— ๋”ฐ๋ผ โ€˜l2โ€™, 'ip', โ€˜cosineโ€™ ์ค‘์— ์‚ฌ์šฉ
)
ย 
# Chroma ๋ฒกํ„ฐ ์ €์žฅ์†Œ ์ƒ์„ฑ
vectorstoreย =ย Chroma(
ย ย ย ย client=client,
ย ย ย ย collection_name="clovastudiodatas_docs",
ย ย ย ย embedding_function=clovax_embeddings
)
ย 
# tqdm์œผ๋กœ for ๋ฃจํ”„ ๊ฐ์‹ธ๊ธฐ
forย docย inย tqdm(documents, desc="Adding documents", total=len(documents)):
ย ย ย ย embeddingsย =ย clovax_embeddings.embed_documents([doc.page_content])[0]
ย ย ย ย # ๋ฌธ์„œ ์ถ”๊ฐ€
ย ย ย ย chroma_collection.add(
ย ย ย ย ย ย ย ย ids=[str(uuid.uuid4())],ย ย # ๊ณ ์œ ํ•œ ID ์ƒ์„ฑ
ย ย ย ย ย ย ย ย documents=[doc.page_content],
ย ย ย ย ย ย ย ย embeddings=[embeddings],
ย ย ย ย ย ย ย ย metadatas=[doc.metadata]
ย ย ย ย )
ย ย ย ย time.sleep(1.1)ย ย # ์ด์šฉ๋Ÿ‰ ์ œ์–ด๋ฅผ ๊ณ ๋ คํ•œ 1์ดˆ ์ด์ƒ์˜ ๋”œ๋ ˆ์ด, ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ • ๊ฐ€๋Šฅ
ย 
ย 
print("All documents have been added to the vectorstore.")
#Output
Adding documents:ย 100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ|ย 108/108ย [03:17<00:00,ย ย 2.46s/it]
All documents have been added to the vectorstore.
Quote

emb ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ
ํ•„์š”์— ๋”ฐ๋ผ ํด๋กœ๋ฐ” ์ŠคํŠœ๋””์˜ค์˜ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์ค‘ย clir-emb-dolphinย  ์„ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒฝ์šฐ, ๋ชจ๋ธ์— ์ ํ•ฉํ•œ ๊ฑฐ๋ฆฌ ์ง€ํ‘œ์ธ IP(๋‚ด์ )์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Chroma ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ ์‹œ ์„ค์ • ์—ญ์‹œ 'ip'๋กœ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜๋ฉฐ, ๋žญ์ฒด์ธ ์—ฐ๋™์„ ์œ„ํ•ด ์ •๊ทœํ™”๋œ(normalized) ์ž„๋ฒ ๋”ฉ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€์ ์ธ ์กฐ์น˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

clir-emb-dolphin๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ „์ฒด์ฝ”๋“œ

fromย langchain_community.embeddingsย importย ClovaXEmbeddings
fromย langchain_chromaย importย Chroma
ย 
clientย =ย chromadb.PersistentClient(path="./chroma_langchain_db")
ย 
chroma_collectionย =ย client.get_or_create_collection(
ย ย ย ย name="clovastudiodatas_rag_cosine",
ย ย ย ย metadata={"hnsw:space":ย "ip"}ย # ip๊ฑฐ๋ฆฌ๋กœ ์„ค์ •
)
ย 
# IP๊ฑฐ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ธ(clir-emb-dolphin)์˜ ์ž„๋ฒ ๋”ฉ ๊ฒฐ๊ณผ ์ •๊ทœํ™” ํ•„์š”
importย numpy as np
classย NormalizedClovaXEmbeddings(ClovaXEmbeddings) :
ย ย ย ย defย _embed_text(self, text:ย str) :
ย ย ย ย ย ย ย ย # Call the parent class method to get the original embedding
ย ย ย ย ย ย ย ย original_embeddingsย =ย super()._embed_text(text)
ย ย ย ย ย ย ย ย ย ย 
ย ย ย ย ย ย ย ย # Postprocessing: apply normalization function to each element
ย ย ย ย ย ย ย ย try:
ย ย ย ย ย ย ย ย ย ย ย ย normalized_embeddingsย =ย list(original_embeddings/np.linalg.norm(original_embeddings))
ย ย ย ย ย ย ย ย except:
ย ย ย ย ย ย ย ย ย ย ย ย raiseย ValueError(f"normalization failed on text: {text}")
ย ย ย ย ย ย ย ย returnย normalized_embeddings
ย ย 
ย ย ย ย asyncย defย _aembed_text(self, text:ย str) :
ย ย ย ย ย ย ย ย original_embeddingsย =ย awaitย super()._aembed_text(text)
ย ย ย ย ย ย ย ย ย ย 
ย ย ย ย ย ย ย ย try:
ย ย ย ย ย ย ย ย ย ย ย ย normalized_embeddingsย =ย list(original_embeddings/np.linalg.norm(original_embeddings))
ย ย ย ย ย ย ย ย except:
ย ย ย ย ย ย ย ย ย ย ย ย raiseย ValueError(f"normalization failed on text: {text}")
ย ย ย ย ย ย ย ย returnย normalized_embeddings
ย 
clova_embeddingsย =ย ClovaXEmbeddings(model="clir-emb-dolphin")
vectorstoreย =ย Chroma(
ย ย ย ย client=client,
ย ย ย ย collection_name="clovastudiodatas_rag_cosine",ย #collection์ด ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ์ด๋ฆ„๋„ ๊ผญ ๋ณ€๊ฒฝํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ย ย ย ย embedding_function=clova_embeddings
)
ย 
# tqdm์œผ๋กœ for ๋ฃจํ”„ ๊ฐ์‹ธ๊ธฐ
forย docย inย tqdm(documents, desc="Adding documents", total=len(documents)) :
ย ย ย ย embeddingsย =ย clova_embeddings.embed_documents([doc.page_content])[0]
ย ย ย ย # ๋ฌธ์„œ ์ถ”๊ฐ€
ย ย ย ย chroma_collection.add(
ย ย ย ย ย ย ย ย ids=[str(uuid.uuid4())],
ย ย ย ย ย ย ย ย documents=[doc.page_content],
ย ย ย ย ย ย ย ย embeddings=[embeddings],
ย ย ย ย ย ย ย ย metadatas=[doc.metadata]
ย ย ย ย )
ย ย ย ย time.sleep(1.4) ย # ์ด์šฉ๋Ÿ‰ ์ œ์–ด๋ฅผ ๊ณ ๋ คํ•œ 1์ดˆ ์ด์ƒ์˜ ๋”œ๋ ˆ์ด, ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ • ๊ฐ€๋Šฅ
ย 
ย 
print("All documents have been added to the vector store.")

1-2) FAISS ํ™œ์šฉ

Facebook AI Similarity Search (Faiss)๋Š” ๋ฐ€์ง‘ ๋ฒกํ„ฐ์˜ ํšจ์œจ์ ์ธ ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰๊ณผ ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.ย ๋ณธ ์˜ˆ์ œ์—์„œ๋Š” FAISS๋ฅผ ํ™œ์šฉํ•ด vectorstore๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. vectorstrore๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ์ž„๋ฒ ๋”ฉ ์ฐจ์› ํฌ๊ธฐ๋ฅผ 1024์ฐจ์›์œผ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.ย ๋˜ํ•œ, ์ž„๋ฒ ๋”ฉ ํด๋ž˜์Šค์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ์‹œ ๋ฉˆ์ถ”๊ฒŒ๋” ํ•˜๋Š” ๋กœ์ง์„ ์ผ๋ถ€ ์ถ”๊ฐ€ํ•ด, ๋‹ค๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ์žฌ์‹คํ–‰์˜ ๋ถ€๋‹ด์„ ์ค„์˜€์Šต๋‹ˆ๋‹ค.ย Langchain ๊ณต์‹ ๋ฌธ์„œ์—์„œ FAISS ํ™œ์šฉ์— ๋Œ€ํ•œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#FAISS ๋‹ค์šด๋กœ๋“œ
%pip installย -qU langchain-community faiss-cpu
importย faiss
fromย langchain_community.vectorstoresย importย FAISS
fromย langchain_community.docstore.in_memoryย importย InMemoryDocstore
ย 
# ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์ •์˜
clovax_embeddingsย =ย ClovaXEmbeddings(model='bge-m3')
ย 
# FAISS ๋ฒกํ„ฐ ์ €์žฅ์†Œ ์ƒ์„ฑ
vectorstore_FAISSย =ย FAISS(
ย ย ย ย embedding_function=clovax_embeddings,
ย ย ย ย index=faiss.IndexFlatIP(1024),ย # ์ž„๋ฒ ๋”ฉ ์ฐจ์› ํฌ๊ธฐ
ย ย ย ย docstore=InMemoryDocstore(),
ย ย ย ย index_to_docstore_id={},
)
ย 
# tqdm์œผ๋กœ for ๋ฃจํ”„ ๊ฐ์‹ธ๊ธฐ
forย docย inย tqdm(documents, desc="Adding documents", total=len(documents)):
ย ย ย ย embeddingsย =ย clovax_embeddings.embed_documents([doc.page_content])[0]
ย ย ย ย # ๋ฌธ์„œ ์ถ”๊ฐ€
ย ย ย ย vectorstore_FAISS.add_documents(
ย ย ย ย ย ย ย ย ids=[str(uuid.uuid4())],ย ย # ๊ณ ์œ ํ•œ ID ์ƒ์„ฑ
ย ย ย ย ย ย ย ย documents=[doc],ย ย # ๋ฌธ์ž์—ด๋งŒ ์ „๋‹ฌ
ย ย ย ย ย ย ย ย embeddings=[embeddings]
ย ย ย ย )
ย ย ย ย time.sleep(1.4) ย # ์ด์šฉ๋Ÿ‰ ์ œ์–ด๋ฅผ ๊ณ ๋ คํ•œ 1์ดˆ ์ด์ƒ์˜ ๋”œ๋ ˆ์ด, ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ • ๊ฐ€๋Šฅ
ย 
ย 
print("All documents have been added to the vectorstore.")
#Output
Adding documents: 100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 108/108 [03:17<00:00,  2.46s/it]
All documents have been added to the vectorstore.


2)ย ๋‹ค์–‘ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ™œ์šฉํ•ด Retriever ์ƒ์„ฑํ•˜๊ธฐ

๊ฒ€์ƒ‰๊ธฐ(Retriever) ๋‹จ๊ณ„๋Š” ์ €์žฅ๋œ ๋ฒกํ„ฐ DB์—์„œย ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.ย vectorstore.as_retriever()๋ฅผ ํ†ตํ•ด ๊ฒ€์ƒ‰๊ธฐ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ˆ์ œ์—์„œ๋Š”ย retrieverย ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •์„ ํ†ตํ•ดย 3๊ฐœ์˜ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด ๋‹ต๋ณ€์„ ํ•˜๋„๋ก ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

retrieverย =ย vectorstore.as_retriever(kwargs={"k":ย 3})

๋งŒ๋“ค๊ณ ์ž ํ•˜๋Š” RAG ์‹œ์Šคํ…œ์˜ ๋ชฉ์  ๋ฐ ํ™œ์šฉ์— ๋งž๊ฒŒย ์•„๋ž˜์˜ 1~4๋ฒˆ์˜ ํ•ญ๋ชฉ์˜ย ๋‹ค์–‘ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด ์ปค์Šคํ…€ํ•ด๋ณด์„ธ์š”. ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‹ค์–‘ํ•˜๊ฒŒ ์กฐ์ ˆํ•ด๊ฐ€๋ฉฐ ์ œํ’ˆ์˜ ๋ชฉ์ ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋‹ต๋ณ€์„ ํ•˜๋Š” ๋ฐฉ์‹์„ ์ฐพ์•„๋‚˜๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ย 

2-1)ย ์œ ์‚ฌ๋„ ์ ์ˆ˜ ์ž„๊ณ„๊ฐ’ ์„ค์ •

์ด ์„ค์ •์€ ์œ ์‚ฌ๋„ ์ ์ˆ˜๊ฐ€ 0.5 ์ด์ƒ์ธ ๋ฌธ์„œ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ์„ฑ์ด ๋†’์€ ๋ฌธ์„œ๋งŒ์„ ์„ ํƒ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž„๊ณ„๊ฐ’์„ ๋†’๊ฒŒ ์„ค์ •ํ• ์ˆ˜๋ก ๋” ๊ด€๋ จ์„ฑ ๋†’์€ ๋ฌธ์„œ๋งŒ ๋ฐ˜ํ™˜๋˜์ง€๋งŒ, ๊ด€๋ จ ์ •๋ณด๋ฅผ ๋†“์น  ๊ฐ€๋Šฅ์„ฑ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์ž„๊ณ„๊ฐ’์„ ๋‚ฎ์ถ”๋ฉด ๋” ๋งŽ์€ ๋ฌธ์„œ๊ฐ€ ๋ฐ˜ํ™˜๋˜์ง€๋งŒ, ๊ด€๋ จ์„ฑ์ด ๋–จ์–ด์ง€๋Š” ๋ฌธ์„œ๋„ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย Langchain ๊ณต์‹ ๋ฌธ์„œ์—์„œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

retrieverย =ย db.as_retriever(
ย ย ย ย # ๊ฒ€์ƒ‰ ์œ ํ˜•์„ "์œ ์‚ฌ๋„ ์ ์ˆ˜ ์ž„๊ณ„๊ฐ’"์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
ย ย ย ย search_type="similarity_score_threshold",
ย ย ย ย # ๊ฒ€์ƒ‰ ์ธ์ž๋กœ ์ ์ˆ˜ ์ž„๊ณ„๊ฐ’์„ 0.5๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
ย ย ย ย search_kwargs={"score_threshold":ย 0.5},
)

์œ ์‚ฌ๋„ ์ž„๊ณ„๊ฐ’์€ 0์—์„œ 1 ์‚ฌ์ด์˜ ๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ์ด๋Š” ์ฝ”์‚ฌ์ธ ๊ฑฐ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” `clir-sts-dolphin`ย ๋ชจ๋ธ๊ณผ `bge-m3`ย ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•  ๋•Œ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
IP(Inner Product) ๊ฑฐ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” `clir-emb-dolphin`ย ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์˜ ๊ฒฝ์šฐ ์œ ์‚ฌ๋„ ์ ์ˆ˜ ๋ฒ”์œ„๊ฐ€ ๋‹ฌ๋ผ ์•ž์„œ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ •๊ทœํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

ย 

2-2)ย ๋ฐ˜ํ™˜ ๋ฌธ์„œ ์ˆ˜ ์ œํ•œ

์ด ์„ค์ •์€ ์ƒ์œ„ 3๊ฐœ์˜ ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ ๋†’์€ ๋ฌธ์„œ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.ย k ๊ฐ’์„ ๋‚ฎ๊ฒŒ ์„ค์ •ํ•˜๋ฉด ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ๋นจ๋ผ์ง€๊ณ  ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ ๋†’์€ ๋ฌธ์„œ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ๋†“์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. k ๊ฐ’์„ ๋†’๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๋” ๋งŽ์€ ๋งฅ๋ฝ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๊ณ  ๊ด€๋ จ์„ฑ์ด ๋‚ฎ์€ ์ •๋ณด๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

search_kwargs์˜ k์™€ kwargs์˜ k์˜ ์ฐจ์ด

  • kwargs์˜ k ๊ฐ’์˜ ์˜๋ฏธ๋Š” ๊ฒ€์ƒ‰๊ธฐ(retriever)๊ฐ€ ๋‹ต๋ณ€ ์ƒ์„ฑย ์ตœ์ข…์ ์œผ๋กœ ์ฐธ์กฐํ•˜๋Š” ์ƒ์œ„ ๋ฌธ์„œ์˜ ๊ฐœ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์•„๋ž˜์˜ ์˜ˆ์ œ์™€ ๊ฐ™์ด '3'์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฌธ์„œ ์ค‘์—์„œ ์œ ์‚ฌ๋„๊ฐ€ ๋†’์€ ์ƒ์œ„ 3๊ฐœ์˜ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • search_kwargs์˜ k ๊ฐ’์˜ ์˜๋ฏธ๋Š” ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฌธ์„œ์˜ ๊ฐœ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์˜ kwargs์˜ k ๊ฐ’์€ ๊ฒ€์ƒ‰ํ•œ ๋ฌธ์„œ์—์„œ ๋‹ต๋ณ€์„ ํ• ๋•Œ ์ฐธ์กฐํ•˜๋Š” ์ƒ์œ„ ๋ฌธ์„œ์˜ ์ˆ˜ ์˜€๋‹ค๋ฉด search_kwargs์˜ k๋Š” ๊ฒ€์ƒ‰ํ•˜๋ ค๋Š” ๋ฌธ์„œ์˜ ๊ฐœ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ search_kwargs์˜ k๊ฐ’์ด kwargs์˜ k ๊ฐ’๋ณด๋‹ค ์ ์€ ์ˆ˜๋กœ ์„ค์ •๋œ๋‹ค๋ฉด, ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฌธ์„œ์˜ ์ˆ˜๊ฐ€ ๋‹ต๋ณ€ ์ƒ์„ฑ ์‹œ์— ์ฐธ์กฐํ•˜๋Š” ๋ฌธ์„œ์˜ ๊ฐœ์ˆ˜๋ณด๋‹ค ์ ์€ ๊ฐœ์ˆ˜๋กœ ์„ค์ •๋œ ๊ฒƒ์œผ๋กœ, search_kwargs์˜ k๊ฐ’์˜ ์ˆ˜๋กœ ๋ฌธ์„œ์˜ ๊ฐœ์ˆ˜๊ฐ€ ์ œํ•œ๋˜์–ด ๋‹ต๋ณ€๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, search_kwargs์˜ k ๊ฐ’์€ ๋„์„œ๊ด€์—์„œ ์ •๋ณด๋ฅผ ์–ป์œผ๋ ค๊ณ  ๋นŒ๋ฆฐ ์ „์ฒด ์ฑ…์˜ ๊ถŒ ์ˆ˜๊ฐ€ ๋˜๊ณ , kwargs์˜ k ๊ฐ’์€ ๋นŒ๋ฆฐ ์ฑ… ์ „์ฒด์—์„œ ์‹ค์ œ ์ •๋ณด๋ฅผ ์–ป์€ ์ฑ…์˜ ๊ถŒ์ˆ˜๋ผ๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย 

2-3)ย MMR (Maximum Marginal Relevance) ๊ฒ€์ƒ‰

๋Œ€๋ถ€๋ถ„์˜ ๊ฒ€์ƒ‰ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋ฌธ์„œ์™€ ์ฟผ๋ฆฌ ๊ฐ„์˜ ์œ ์‚ฌ๋„๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ๊ฐ€์žฅ ๋†’์€ ์ ์ˆ˜๋ฅผ ๊ฐ€์ง„ ๋ฌธ์„œ๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ, ๋‚ด์šฉ์ด ๋งค์šฐ ์œ ์‚ฌํ•˜๊ฑฐ๋‚˜ ์‹ฌ์ง€์–ด ๋™์ผํ•œ ๋ฌธ์„œ๋“ค์ด ์—ฐ์†ํ•ด์„œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ๋ฌธ์ œ์ ์„ MMR(Maximal Marginal Relevance) ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๊ด€๋ จ ํ•ญ๋ชฉ์„ ๊ฒ€์ƒ‰ํ•  ๋•Œ ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ์˜ ์ค‘๋ณต์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ์ˆœํžˆ ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ ๋†’์€ ํ•ญ๋ชฉ๋“ค๋งŒ์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋Œ€์‹ , ์ด๋ฏธ ์„ ํƒ๋œ ๋ฌธ์„œ๋“ค๊ณผ์˜ ์ฐจ๋ณ„์„ฑ๋„ ๊ณ ๋ คํ•ด ๋ฌธ์„œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์ •๋ณด ๊ฒ€์ƒ‰์ด๋‚˜ ์ถ”์ฒœ ์‹œ์Šคํ…œ์ฒ˜๋Ÿผ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์˜ ๋‹ค์–‘์„ฑ์ด ์ค‘์š”ํ•˜๊ฑฐ๋‚˜ ์ค‘๋ณต๋œ ์ •๋ณด๋ฅผ ์ค„์ด๊ณ  ์‹ถ์„ ๋•Œ ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜(parameters)

  • k:ย ์ด ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ตœ์ข…์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•  ๋ฌธ์„œ์˜ ์ด ๊ฐœ์ˆ˜์ž…๋‹ˆ๋‹ค. (๊ธฐ๋ณธ๊ฐ’: 4)
  • fetch_k:ย MMR ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ๊ณ ๋ คํ•  ์ดˆ๊ธฐ ํ›„๋ณด ๋ฌธ์„œ ์ง‘ํ•ฉ์˜ ํฌ๊ธฐ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ด‘๋ฒ”์œ„ํ•œ ํ›„๋ณด๊ตฐ์—์„œ ๋ฌธ์„œ๋ฅผ ์„ ํƒํ•˜๋ฉด ์ข‹์„ ๋•Œ ์ด ๊ฐ’์„ ๋†’์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ, ๋น ๋ฅธ ์‘๋‹ต ์‹œ๊ฐ„์ด ์šฐ์„ ์ ์ด๋ผ๋ฉด ์ด ๊ฐ’์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.(๊ธฐ๋ณธ๊ฐ’: 20)
  • lambda_mult: ์ฟผ๋ฆฌ์™€์˜ ์œ ์‚ฌ์„ฑ๊ณผ ์„ ํƒ๋œ ๋ฌธ์„œ ๊ฐ„์˜ ๋‹ค์–‘์„ฑ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ์กฐ์ ˆํ•˜๋Š” ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค. 1์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์ฟผ๋ฆฌ์™€์˜ ์œ ์‚ฌ๋„ ์ ์ˆ˜๋งŒ ๊ณ ๋ คํ•˜๊ณ , 0์— ๊ฐ€๊นŒ์›Œ์งˆ์ˆ˜๋ก ๋ฌธ์„œ๊ฐ„์˜ ๋‹ค์–‘์„ฑ๋งŒ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค. (0~1, ๊ธฐ๋ณธ๊ฐ’: 0.5)

ย 

2-4)ย ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง

๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ํŠน์ • ํ‚ค์›Œ๋“œ๊ฐ€ ํฌํ•จ๋œ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ document์— ์ €์žฅํ•  ๋•Œ, metadata์˜ title์„ ํ•จ๊ป˜ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•ด ๋ฌธ์„œ๋ฅผ ํ•„ํ„ฐ๋งํ•ด ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง
filtered_retrieverย =ย db.as_retriever(
ย ย ย ย search_kwargs={"filter": {'title':ย 'CLOVA Studio ์‹œ๋‚˜๋ฆฌ์˜ค'}}
)

ย 

6. RAG ์‹œ์Šคํ…œ ์™„์„ฑ

1)ย LCEL์„ ์‚ฌ์šฉํ•ด Chain ๊ตฌํ˜„ํ•˜๊ธฐ

LCEL(LangChain Expression Language)์€ ๋žญ์ฒด์ธ์—์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ”„๋กฌํ”„ํŠธ์™€ LLM ๋ชจ๋ธ์„ ์ •์˜ํ•ด๋‘๊ณ ,ย |ย ๊ธฐํ˜ธ๋กœ ๊ฐ ์š”์†Œ๋ฅผ ์—ฐ๊ฒฐํ•ด ํ•œ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ถœ๋ ฅ์„ ๋‹ค์Œ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ Chain์„ ์ƒ์„ฑํ•ด ๋ณต์žกํ•œ AI ์‹œ์Šคํ…œ์˜ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ณ  ์ง๊ด€์ ์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ย 

1-1)ย ์‚ฌ์šฉํ•  LLM ๋ชจ๋ธ ์ •์˜
๋จผ์ €, HyperCLOVA X ๋ชจ๋ธ (HCX-003) ์„ ๋žญ์ฒด์ธ Chat model ์ปดํฌ๋„ŒํŠธ์˜ ChatClovaX๋ฅผ ํ†ตํ•ด ๋ถˆ๋Ÿฌ์™€ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.ย 

fromย langchain_community.chat_modelsย importย ChatClovaX
ย 
llmย =ย ChatClovaX(
ย ย ย ย model="HCX-003",
)

ย 

1-2)ย Prompt ์ •์˜

PromptTemplate ์‚ฌ์šฉํ•˜๊ธฐ
์ฟผ๋ฆฌ์— ์‚ฌ์šฉํ•  Prompt๋Š” StringPromptTemplates ํ˜น์€ ChatPromptTemplates์„ ์‚ฌ์šฉํ•ด ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย Langchain ๊ณต์‹ ๋ฌธ์„œ์—์„œ ๊ด€๋ จ ๋‚ด์šฉ์„ ํ™•์ธํ•ด๋ณด์„ธ์š”.
PromptTemplate์€ ๋‹จ์ผ ๋ฌธ์ž์—ด์˜ ์„œ์‹์„ ์ง€์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ„๋‹จํ•œ ์ž…๋ ฅ์— ์‚ฌ์šฉ๋˜๋Š” ํ…œํ”Œ๋ฆฟ์ž…๋‹ˆ๋‹ค.ย PromptTemplate์€ Python์˜ ๋ฌธ์ž์—ด ํฌ๋งทํŒ…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ ์œผ๋กœ ํŠน์ •ํ•œ ์œ„์น˜์— ์ž…๋ ฅ ๊ฐ’์„ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด, ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ {question}, {context} ์™€ ๊ฐ™์€ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ฐ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์–ธ์–ด ๋ชจ๋ธ์— ์ „๋‹ฌํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ย 

fromย langchain_core.promptsย importย PromptTemplate
ย 
prompt1ย =ย PromptTemplate.from_template(
ย ย ย ย """๋‹น์‹ ์€ ์งˆ๋ฌธ-๋‹ต๋ณ€(Question-Answering)์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์นœ์ ˆํ•œ AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์ž„๋ฌด๋Š” ์›๋ž˜ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ์ง€์‹์€ ๋ชจ๋‘ ๋ฐฐ์ œํ•˜๊ณ , ์ฃผ์–ด์ง„ ๋ฌธ๋งฅ(context) ์—์„œ ์ฃผ์–ด์ง„ ์งˆ๋ฌธ(question) ์— ๋‹ตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ฒ€์ƒ‰๋œ ๋‹ค์Œ ๋ฌธ๋งฅ(context) ์„ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ(question) ์— ๋‹ตํ•˜์„ธ์š”. ๋งŒ์•ฝ, ์ฃผ์–ด์ง„ ๋ฌธ๋งฅ(context) ์—์„œ ๋‹ต์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋ฉด, ๋‹ต์„ ๋ชจ๋ฅธ๋‹ค๋ฉด `์ฃผ์–ด์ง„ ์ •๋ณด์—์„œ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค` ๋ผ๊ณ  ๋‹ตํ•˜์„ธ์š”.
ย 
#Question:
{question}
ย 
#Context:
{context}
ย 
#Answer:"""
)

ย 

ChatPromptTemplate ์‚ฌ์šฉํ•˜๊ธฐ
ChatPromptTemplate ์€ ๋Œ€ํ™”๋ชฉ๋ก์„ ํ”„๋กฌํ”„ํŠธ๋กœ ์ฃผ์ž…ํ•˜๊ณ ์ž ํ•  ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย ๋ฉ”์‹œ์ง€๋Š” ํŠœํ”Œ(tuple) ํ˜•์‹์œผ๋กœ ๊ตฌ์„ฑํ•˜๋ฉฐ, (role, message) ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • "system": ์‹œ์Šคํ…œ ์„ค์ • ๋ฉ”์‹œ์ง€ ์ž…๋‹ˆ๋‹ค. ์ฃผ๋กœ ์ „์—ญ์„ค์ •๊ณผ ๊ด€๋ จ๋œ ํ”„๋กฌํ”„ํŠธ์ž…๋‹ˆ๋‹ค.
  • "human" : ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ฉ”์‹œ์ง€ ์ž…๋‹ˆ๋‹ค.
  • "ai": AI ์˜ ๋‹ต๋ณ€ ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค.

ย 

fromย langchain_core.promptsย importย ChatPromptTemplate
ย 
system_promptย =ย (
ย ย ย ย ย ย ย ย """๋‹น์‹ ์€ ์งˆ๋ฌธ-๋‹ต๋ณ€(Question-Answering)์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์นœ์ ˆํ•œ AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์ž„๋ฌด๋Š” ์›๋ž˜ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ์ง€์‹์€ ๋ชจ๋‘ ๋ฐฐ์ œํ•˜๊ณ , ์ฃผ์–ด์ง„ ๋ฌธ๋งฅ(context) ์—์„œ ์ฃผ์–ด์ง„ ์งˆ๋ฌธ(question) ์— ๋‹ตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ฒ€์ƒ‰๋œ ๋‹ค์Œ ๋ฌธ๋งฅ(context) ์„ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ(question) ์— ๋‹ตํ•˜์„ธ์š”. ๋งŒ์•ฝ, ์ฃผ์–ด์ง„ ๋ฌธ๋งฅ(context) ์—์„œ ๋‹ต์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋ฉด, ๋‹ต์„ ๋ชจ๋ฅธ๋‹ค๋ฉด `์ฃผ์–ด์ง„ ์ •๋ณด์—์„œ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค` ๋ผ๊ณ  ๋‹ตํ•˜์„ธ์š”.
"""
ย ย ย ย "\n\n"
ย ย ย ย "{context}"
)
ย 
prompt2ย =ย ChatPromptTemplate.from_messages(
ย ย ย ย [
ย ย ย ย ย ย ย ย ("system", system_prompt),
ย ย ย ย ย ย ย ย ("human",ย "{question}"),
ย ย ย ย ]
)

ย 

1-3)ย  Chain ์ƒ์„ฑ
์•ž์„œ ์ •์˜ํ•œ LLM ๋ชจ๋ธ๊ณผ prompt๋ฅผ LCEL ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ด chain์œผ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ,ย Langchain์˜ย StrOutputParser๋„ chain์œผ๋กœ ์—ฐ๊ฒฐํ•ดย ๋ชจ๋ธ์˜ ์ถœ๋ ฅ์„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์—ฐ๊ฒฐ๋œ chain์ด ์—ฐ์‡„์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. retriever๋ฅผ ํ†ตํ•ด ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ context๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ณ , context์™€ question์ด ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์ „๋‹ฌ๋˜๊ณ , llm ๋ชจ๋ธ์„ ํ†ตํ•ด ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์‹คํ–‰๋˜๊ณ , StrOutputParser๋ฅผ ํ†ตํ•ด ๋‹ต๋ณ€๋งŒ ํŒŒ์‹ฑํ•ด ๋ฌธ์ž์—ด๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

fromย langchain_core.runnablesย importย RunnablePassthrough
fromย langchain_core.output_parsersย importย StrOutputParser
ย 
# ์ฒด์ธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
rag_chainย =ย (
ย ย ย ย {"context": retriever,ย "question": RunnablePassthrough()}
ย ย ย ย | prompt1
ย ย ย ย | llm
ย ย ย ย | StrOutputParser()
)
rag_chain.invoke("ํ† ํฐ์ด ๋ญ”๊ฐ€์š”?")
#Output
ํ† ํฐ์€ ์ž์—ฐ์–ด ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ํ•˜๋‚˜์˜ ๋‹จ์–ด๋ฅผ ์„ธ๋ถ„ํ™”ํ•œ ๋‹จ์–ด ์กฐ๊ฐ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. 
์˜ˆ๋ฅผ ๋“ค์–ด, '๋ง›์žˆ์–ด'๋ผ๋Š” ํ‘œํ˜„์€ ๊ฐ๊ฐ '๋ง›'๊ณผ '์žˆ์–ด'๋ผ๋Š” ๋‘ ๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ๋‚˜๋‰  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‹ค๋ฅธ ์˜ˆ์‹œ๋กœ๋Š” '์›์ˆญ์ด ์—‰๋ฉ์ด๋Š” ๋นจ๊ฐœ' ๋ผ๋Š” ๋ฌธ์žฅ์€ '์›์ˆญ์ด', '์—‰๋ฉ์ด๋Š”', '๋นจ๊ฐœ' ์™€ ๊ฐ™์€ ์„ธ ๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ๋‚˜๋‰  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ย 

1-4)ย  ์ฐธ์กฐ ๋ฌธ์„œ ๋งํฌ์™€ ํ•จ๊ป˜ ๋‹ต๋ณ€ ์ถœ๋ ฅํ•˜๊ธฐ
๋ชจ๋ธ์ด ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ๋•Œ ์ฐธ์กฐํ•˜๋Š” ๋ฌธ์„œ์˜ ์ถœ์ฒ˜๋ฅผ ํ™•์ธํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.ย RunnablePassthrough.assign()์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์ƒ‰ ์ฒด์ธ์—์„œ ์†Œ์Šค ๋ฌธ์„œ๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ฐธ์กฐ ๋ฌธ์„œ ๋งํฌ์™€ ํ•จ๊ป˜ ๋‹ต๋ณ€์„ ์ถœ๋ ฅํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

fromย pprintย importย pprint
fromย langchain_core.runnablesย importย RunnablePassthrough
fromย langchain.schema.runnableย importย RunnableParallel
fromย langchain_core.output_parsersย importย StrOutputParser
ย 
# ์ฟผ๋ฆฌ ํ…์ŠคํŠธ
query_textย =ย "max_token์„ ๋ช‡์œผ๋กœ ์„ค์ •ํ•ด์•ผํ•ด?"
ย 
# 1. ์ฟผ๋ฆฌ ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ
clovax_embeddingsย =ย ClovaXEmbeddings(model='bge-m3')
query_embeddingย =ย clovax_embeddings.embed_query(query_text)
ย 
# 2. Chroma ์ปฌ๋ ‰์…˜์— ์ฟผ๋ฆฌ ์‹คํ–‰ํ•˜์—ฌ ์œ ์‚ฌํ•œ ๋ฌธ์„œ ๊ฒ€์ƒ‰
resultsย =ย chroma_collection.query(
ย ย ย ย query_embeddings=[query_embedding],
ย ย ย ย n_results=5,
ย ย ย ย include=["embeddings",ย "documents",ย "metadatas",ย "distances"]
)
ย 
# 3. ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌํŠธ๋ฆฌ๋ฒ„ ๊ตฌ์„ฑ
similarity_retrieverย =ย vectorstore.as_retriever(
ย ย ย ย kwargs={"k":ย 5},
ย ย ย ย search_type="similarity_score_threshold",
ย ย ย ย search_kwargs={"score_threshold":ย 0.1,ย "k":ย 3}
)
ย 
# 4. ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ๋ฅผ RAG ์ฒด์ธ์— ์—ฐ๊ฒฐํ•˜์—ฌ ๋‹ต๋ณ€ ์ƒ์„ฑ
rag_chain_from_docsย =ย (
ย ย ย ย RunnablePassthrough.assign(context=(lambdaย x: format_docs(x["context"])))
ย ย ย ย | prompt1
ย ย ย ย | llm
ย ย ย ย | StrOutputParser()
)
ย 
defย format_docs(docs):
ย ย ย ย returnย "\n\n".join(doc.page_contentย forย docย inย docs)
ย ย ย ย ย 
ย 
rag_chain_with_sourceย =ย RunnableParallel(
ย ย ย ย {"context": similarity_retriever,ย "question": RunnablePassthrough()}
).assign(answer=rag_chain_from_docs)
ย 
ย 
pprint(rag_chain_with_source)
Vector DB์— ์ €์žฅํ•œ ๋ฌธ์„œ๋“ค๊ณผ ๊ด€๋ จ์žˆ๋Š” ์งˆ๋ฌธ์— ๋Œ€ํ•ด์„œ๋Š” ๋ชจ๋ธ์ด ์•„๋ž˜์™€ ๊ฐ™์ด ๋‹ต๋ณ€์„ ์ž˜ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
#Output
{'answer': '์ผ๋ฐ˜ ๋ชจ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ธ์–ด ๋ชจ๋ธ์ธ ๊ฒฝ์šฐ์—๋Š” ์ตœ๋Œ€ 2048 ํ† ํฐ๊นŒ์ง€, ์ฑ— ๋ชจ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” HyperCLOVA X ์–ธ์–ด '
           '๋ชจ๋ธ์ธ ๊ฒฝ์šฐ์—๋Š” ์ตœ๋Œ€ 4096 ํ† ํฐ๊นŒ์ง€ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค. Maximum tokens๋Š” 300~500์„ ๊ถŒ์žฅํ•˜๋ฉฐ ์ž‘์—…์— ๋”ฐ๋ผ '
           '๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.',
 'context': [Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}, page_content='Maximum tokens Maximum tokens๋Š” ๊ฒฐ๊ด๊ฐ’์„ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ตœ๋Œ€ ํ† ํฐ ์ˆ˜์ž…๋‹ˆ๋‹ค. ํ† ํฐ ์ˆ˜๋ฅผ ๋†’๊ฒŒ ์„ค์ •ํ•  ์ˆ˜๋ก ๊ธด ๊ฒฐ๊ด๊ฐ’์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.'),
             Document(metadata={'source': 'clovastudioguide_0822/clovastudio-info.html'}, page_content='Maximum tokens Maximum tokens๋Š” ๊ฒฐ๊ด๊ฐ’์„ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ตœ๋Œ€ ํ† ํฐ ์ˆ˜์ž…๋‹ˆ๋‹ค. ํ† ํฐ ์ˆ˜๋ฅผ ๋†’๊ฒŒ ์„ค์ •ํ•  ์ˆ˜๋ก ๊ธด ๊ฒฐ๊ด๊ฐ’์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.'),
             Document(metadata={'source': 'clovastudioguide_0822/clovastudio-info.html'}, page_content='ํ”„๋กฌํ”„ํŠธ์™€ ๊ฒฐ๊ด๊ฐ’์„ ํฌํ•จํ•˜์—ฌ ์ผ๋ฐ˜ ๋ชจ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ธ์–ด ๋ชจ๋ธ์ธ ๊ฒฝ์šฐ์—๋Š” ์ตœ๋Œ€ 2048 ํ† ํฐ๊นŒ์ง€, ์ฑ— ๋ชจ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” HyperCLOVA X ์–ธ์–ด ๋ชจ๋ธ์ธ ๊ฒฝ์šฐ์—๋Š” ์ตœ๋Œ€ 4096 ํ† ํฐ๊นŒ์ง€ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค. Maximum tokens๋Š” 300~500์„ ๊ถŒ์žฅํ•˜๋ฉฐ ์ž‘์—…์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.')],
 'question': 'max_token์„ ๋ช‡์œผ๋กœ ์„ค์ •ํ•ด์•ผํ•ด?'}
์œ„์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋‹ค ๋ณด๊ธฐ ์ข‹๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
resultย =ย rag_chain_with_source.invoke(query_text)
model_answer=result['answer']
urlsย =ย {doc.metadata['source']ย forย docย inย result['context']}
ย 
print("- ์‚ฌ์šฉ์ž ์งˆ๋ฌธ: {0}\n- ๋ชจ๋ธ์˜ ๋‹ต๋ณ€: {1}\n- ์ฐธ์กฐํ•œ ๋ฌธ์„œ์˜ url: {2}".format(query_text, model_answer,ย list(urls)))
#Output
- ์‚ฌ์šฉ์ž ์งˆ๋ฌธ: max_token์„ ๋ช‡์œผ๋กœ ์„ค์ •ํ•ด์•ผํ•ด?
- ๋ชจ๋ธ์˜ ๋‹ต๋ณ€: ์ผ๋ฐ˜ ๋ชจ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ธ์–ด ๋ชจ๋ธ์ธ ๊ฒฝ์šฐ์—๋Š” ์ตœ๋Œ€ 2048 ํ† ํฐ๊นŒ์ง€, ์ฑ— ๋ชจ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” HyperCLOVA X ์–ธ์–ด ๋ชจ๋ธ์ธ ๊ฒฝ์šฐ์—๋Š” ์ตœ๋Œ€ 4096 ํ† ํฐ๊นŒ์ง€ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค. Maximum tokens๋Š” 300~500์„ ๊ถŒ์žฅํ•˜๋ฉฐ ์ž‘์—…์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
- ์ฐธ์กฐํ•œ ๋ฌธ์„œ์˜ url: ['clovastudioguide_0822/clovastudio-info.html', 'https://guide.ncloud-docs.com/docs/clovastudio-info']

์•„๋ž˜์™€ ๊ฐ™์ด ๋ฌธ์„œ์™€ ๊ด€๋ จ ์—†๋Š” ์งˆ๋ฌธ์— ๋Œ€ํ•ด์„  ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๊ณ  ๋‹ตํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

rag_chain_with_source.invoke("์˜ค๋Š˜ ๊ธฐ๋ถ„์ด ์–ด๋•Œ?")
#Output
{'context': [Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}, page_content='Temperature ๊ฐ’์ด ๋†’์€ ๊ฒฝ์šฐ'),
  Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}, page_content='Temperature ๊ฐ’์ด ๋†’์€ ๊ฒฝ์šฐ'),
  Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-procedure'}, page_content='์ด ๋ฌธ์„œ๊ฐ€ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๊นŒ?')],
 'question': '์˜ค๋Š˜ ๊ธฐ๋ถ„์ด ์–ด๋•Œ?',
 'answer': '์ฃผ์–ด์ง„ ์ •๋ณด์—์„œ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.'}


2)ย Built in chain ํ™œ์šฉ

์›ํ•˜๋Š” ๊ฒฝ์šฐ LangChain์—๋Š” RAG ์‹œ์Šคํ…œ์„ ์œ„์˜ LCEL์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ•จ์ˆ˜๋กœ๋„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • create_stuff_documents_chain
    ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ”„๋กฌํ”„ํŠธ์™€ LLM์— ๊ณต๊ธ‰๋˜๋Š” ๋ฐฉ๋ฒ•์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํ”„๋กฌํ”„ํŠธ์— ๋ฌธ์„œ์˜ ์š”์•ฝ์ด๋‚˜ ๊ธฐํƒ€ ์ฒ˜๋ฆฌ ์—†์ด ๊ฒ€์ƒ‰๋œ ๋ชจ๋“  ์ปจํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด {input}๊ณผ {context}์— ๋‚ด์šฉ์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
    ์ž…๋ ฅ ํ‚ค:ย context,ย input
  • create_retrieval_chain
    ๊ฒ€์ƒ‰ ๋‹จ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ๋ฅผ ์ตœ์ข… ๋‹ต๋ณ€๊ณผ ํ•จ๊ป˜ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    ์ž…๋ ฅ ํ‚ค:ย input
    ์ถœ๋ ฅ:ย input,ย context,ย answer

ย 

ย fromย langchain.chainsย importย create_retrieval_chain
fromย langchain.chains.combine_documentsย importย create_stuff_documents_chain
fromย langchain_core.promptsย importย ChatPromptTemplate
fromย langchain_community.chat_modelsย importย ChatClovaX
ย 
# chat๋ชจ๋ธ ์ •์˜
llmย =ย ChatClovaX(
ย ย ย ย model="HCX-003",
)
ย 
# ChatPromptTemplate ์‚ฌ์šฉ
system_promptย =ย (
ย ย ย ย ย ย ย ย """๋‹น์‹ ์€ ์งˆ๋ฌธ-๋‹ต๋ณ€(Question-Answering)์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์นœ์ ˆํ•œ AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์ž„๋ฌด๋Š” ์›๋ž˜ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ์ง€์‹์€ ๋ชจ๋‘ ๋ฐฐ์ œํ•˜๊ณ , ์ฃผ์–ด์ง„ ๋ฌธ๋งฅ(context) ์—์„œ ์ฃผ์–ด์ง„ ์งˆ๋ฌธ(question) ์— ๋‹ตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ฒ€์ƒ‰๋œ ๋‹ค์Œ ๋ฌธ๋งฅ(context) ์„ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ(question) ์— ๋‹ตํ•˜์„ธ์š”. ๋งŒ์•ฝ, ์ฃผ์–ด์ง„ ๋ฌธ๋งฅ(context) ์—์„œ ๋‹ต์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋ฉด, ๋‹ต์„ ๋ชจ๋ฅธ๋‹ค๋ฉด `์ฃผ์–ด์ง„ ์ •๋ณด์—์„œ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค` ๋ผ๊ณ  ๋‹ตํ•˜์„ธ์š”.
"""
ย ย ย ย "\n\n"
ย ย ย ย "{context}"
)
ย 
prompt2ย =ย ChatPromptTemplate.from_messages(
ย ย ย ย [
ย ย ย ย ย ย ย ย ("system", system_prompt),
ย ย ย ย ย ย ย ย ("human",ย "{input}"),
ย ย ย ย ]
)
ย 
# Built-in chains
question_answer_chainย =ย create_stuff_documents_chain(llm, prompt2)
rag_chainย =ย create_retrieval_chain(retriever, question_answer_chain)
ย 
resultย =ย rag_chain.invoke({"input":ย "ํ† ํฐ์ด ๋ญ์•ผ?"})
ย 
print(result)
#Output
{'input': 'ํ† ํฐ์ด ๋ญ์•ผ?', 'context': [Document(page_content='ํ† ํฐ', metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}), Document(page_content="<์˜ˆ์‹œ> '๋ง›์žˆ์–ด'๋ผ๋Š” ํ‘œํ˜„์€ ๊ฐ๊ฐ '๋ง›'๊ณผ ์žˆ์–ด'๋ผ๋Š” ๋‘ ๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ๋‚˜๋‰  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.", metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}), Document(page_content='Temperature๋ฅผ ๋‚ฎ๊ฒŒ ์„ค์ •ํ•˜๋ฉด ํ›„๋ณด์— ํฌํ•จ๋œ ํ† ํฐ์˜ ์ˆœ์œ„๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์ง€๋งŒ ํ™•๋ฅ ์ด ๋†’์•˜๋˜ ํ† ํฐ์€ ๋”์šฑ ํ™•๋ฅ  ๊ฐ’์ด ๋†’์•„์ง€๊ณ  ๋‚ฎ์•˜๋˜ ํ† ํฐ์€ ํ™•๋ฅ  ๊ฐ’์ด ๋”์šฑ ๋‚ฎ์•„์ง‘๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋†’์€ ์ˆœ์œ„์˜ ํ† ํฐ์ด ์„ ํƒ๋  ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ˜•์ ์ธ ๊ฒฐ๊ด๊ฐ’์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.', metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'})], 'answer': 'ํ† ํฐ์€ ๋ฌธ์žฅ์ด๋‚˜ ๋‹จ์–ด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ธฐ๋ณธ ๋‹จ์œ„๋กœ, ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๋Š” ์ตœ์†Œ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, "๋ง›์žˆ์–ด"๋ผ๋Š” ํ‘œํ˜„์€ ๊ฐ๊ฐ "๋ง›"๊ณผ "์žˆ์–ด"๋ผ๋Š” ๋‘ ๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ๋‚˜๋‰  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ธ๊ณต์ง€๋Šฅ ๋ถ„์•ผ์—์„œ๋Š” ์ž์—ฐ์–ด ์ฒ˜๋ฆฌ๋‚˜ ๊ธฐ๊ณ„ํ•™์Šต์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด ํ† ํฐํ™”(tokenization) ๊ณผ์ •์„ ๊ฑฐ์นฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ๊ฐ ๋‹จ์–ด๋‚˜ ๊ธฐํ˜ธ ๋“ฑ์„ ํ† ํฐ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.'}

์œ„์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋‹ค ๋ณด๊ธฐ ์ข‹๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

print("์งˆ๋ฌธ:")
print(result['input'])
print("\n๋‹ต๋ณ€:")
print(result['answer'])
print("\n์ฐธ์กฐ ๋ฌธ์„œ URL:")
unique_urlsย =ย set(doc.metadata['source']ย forย docย inย result['context'])
forย urlย inย unique_urls:
ย ย ย ย print(url)
#Output
์งˆ๋ฌธ:
ํ† ํฐ์ด ๋ญ์•ผ?
 
๋‹ต๋ณ€:
ํ† ํฐ์€ ๋ฌธ์žฅ์ด๋‚˜ ๋‹จ์–ด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ธฐ๋ณธ ๋‹จ์œ„๋กœ, ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๋Š” ์ตœ์†Œ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, "๋ง›์žˆ์–ด"๋ผ๋Š” ํ‘œํ˜„์€ ๊ฐ๊ฐ "๋ง›"๊ณผ "์žˆ์–ด"๋ผ๋Š” ๋‘ ๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ๋‚˜๋‰  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ธ๊ณต์ง€๋Šฅ ๋ถ„์•ผ์—์„œ๋Š” ์ž์—ฐ์–ด ์ฒ˜๋ฆฌ๋‚˜ ๊ธฐ๊ณ„ํ•™์Šต์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด ํ† ํฐํ™”(tokenization) ๊ณผ์ •์„ ๊ฑฐ์นฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ๊ฐ ๋‹จ์–ด๋‚˜ ๊ธฐํ˜ธ ๋“ฑ์„ ํ† ํฐ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
 
์ฐธ์กฐ ๋ฌธ์„œ URL:
https://guide.ncloud-docs.com/docs/clovastudio-info

ย 

7. ๋งบ์Œ๋ง

์ง€๊ธˆ๊นŒ์ง€ NAVER Cloud Platform(NCP)์—์„œ Langchain์„ ํ™œ์šฉํ•˜์—ฌ RAG ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” HCX ๋ชจ๋ธ์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ RAG ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๊ณผ์ •์„ ๋‹ค๋ค˜์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๋Š” RAG์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ์™€ ํ๋ฆ„์— ์ดˆ์ ์„ ๋งž์ถ”์—ˆ์ง€๋งŒ, RAG ๊ธฐ์ˆ ์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ๋‹ค์–‘ํ•œ RAG ์‹œ์Šคํ…œ ๊ตฌํ˜„์— ๊ด€ํ•œ ๋” ๊นŠ์ด ์žˆ๋Š” ์ฃผ์ œ๋“ค์„ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

ย 

ย 

image.png.b9da587841f5ad3f1b694d81ce098866.png

ย 

๋งํฌ ๋ณต์‚ฌ
๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์— ๊ณต์œ ํ•˜๊ธฐ

๊ฒŒ์‹œ๊ธ€ ๋ฐ ๋Œ“๊ธ€์„ ์ž‘์„ฑํ•˜๋ ค๋ฉด ๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”.



๋กœ๊ทธ์ธ
×
×
  • Create New...