4. RAG์˜ ๋ชจ๋“  ๊ฒƒ (Retrieval-Augmented Generation)

๐ŸŽฏ ์ด ์ฑ•ํ„ฐ์—์„œ ๋ฐฐ์šธ ๊ฒƒ

  • RAG(๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ)์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ์ „์ฒด ํ๋ฆ„(Load, Split, Embed, Store, Retrieve) ์ดํ•ดํ•˜๊ธฐ
  • UnstructuredFileLoader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ํ˜•์‹์˜ ๋ฌธ์„œ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•
  • CharacterTextSplitter์™€ Tiktoken์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์„œ๋ฅผ ์˜๋ฏธ ์žˆ๋Š” ์กฐ๊ฐ(chunk)์œผ๋กœ ๋ถ„ํ• ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • OpenAIEmbeddings์™€ VectorStore(FAISS/Chroma)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์„œ ์กฐ๊ฐ์„ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•
  • RetrievalQA ์ฒด์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ RAG ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • LCEL์„ ์‚ฌ์šฉํ•˜์—ฌ Stuff ๋ฐ Map-Reduce์™€ ๊ฐ™์€ RAG ์ฒด์ธ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•

Load & Split (feat. Tiktoken)

๐ŸŽฏ ์ด๋ฒˆ ๋‹จ๊ณ„์—์„œ ๋ฐฐ์šธ ๊ฒƒ

  • RAG์˜ ์ฒซ ๋‘ ๋‹จ๊ณ„์ธ **Load(๋กœ๋“œ)**์™€ **Split(๋ถ„ํ• )**์˜ ๊ฐœ๋…์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.
  • UnstructuredFileLoader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ .docx, .txt ๋“ฑ ๋‹ค์–‘ํ•œ ํŒŒ์ผ์„ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
  • CharacterTextSplitter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๋“œ๋œ ๋ฌธ์„œ๋ฅผ ์ž‘์€ ์กฐ๊ฐ์œผ๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
  • from_tiktoken_encoder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ OpenAI ๋ชจ๋ธ์˜ ํ† ํฐ ๊ณ„์‚ฐ ๋ฐฉ์‹์— ๋งž์ถฐ ๋” ์ •ํ™•ํ•˜๊ฒŒ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ 1๋‹จ๊ณ„: ๋ฌธ์„œ ๋กœ๋“œ ๋ฐ ๋ถ„ํ• 

์ „์ฒด ์ฝ”๋“œ (notebook.ipynb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import CharacterTextSplitter

# 1. ๋ฌธ์„œ๋ฅผ ๋ถ„ํ• ํ•  ์Šคํ”Œ๋ฆฌํ„ฐ ์ •์˜
splitter = CharacterTextSplitter.from_tiktoken_encoder(
separator="\n", # ๋ถ„ํ•  ๊ธฐ์ค€
chunk_size=600, # ์กฐ๊ฐ์˜ ์ตœ๋Œ€ ํฌ๊ธฐ (ํ† ํฐ ๊ธฐ์ค€)
chunk_overlap=100, # ์กฐ๊ฐ ๊ฐ„์˜ ๊ฒน์นจ ํฌ๊ธฐ
)

# 2. ๋ฌธ์„œ๋ฅผ ๋กœ๋“œํ•  ๋กœ๋” ์ •์˜
loader = UnstructuredFileLoader("./files/chapter_one.docx")

# 3. ๋กœ๋“œํ•˜๊ณ  ๋ถ„ํ• ํ•˜๊ธฐ
loader.load_and_split(text_splitter=splitter)

๐Ÿ” ์ฝ”๋“œ ์ƒ์„ธ ์„ค๋ช…

1. RAG์˜ ์ฒซ๊ฑธ์Œ: Load & Split
LLM์€ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ…์ŠคํŠธ์˜ ์–‘(์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ)์— ์ œํ•œ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ธด ๋ฌธ์„œ๋ฅผ LLM์—๊ฒŒ ์ œ๊ณตํ•˜๋ ค๋ฉด, ๋จผ์ € ๋ฌธ์„œ๋ฅผ ๋ถˆ๋Ÿฌ์™€(Load) ์ž˜๊ฒŒ ์ชผ๊ฐœ๋Š”(Split) ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • UnstructuredFileLoader: ํ…์ŠคํŠธ, PDF, ์›Œ๋“œ ๋ฌธ์„œ ๋“ฑ ๋‹ค์–‘ํ•œ ํ˜•์‹์˜ ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ๋กœ๋”์ž…๋‹ˆ๋‹ค.
  • CharacterTextSplitter: ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์Šคํ”Œ๋ฆฌํ„ฐ๋กœ, ์ง€์ •๋œ ๋ฌธ์ž๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ…์ŠคํŠธ๋ฅผ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
  • from_tiktoken_encoder: ๋‹จ์ˆœํžˆ ๊ธ€์ž ์ˆ˜๊ฐ€ ์•„๋‹Œ, OpenAI์˜ Tiktoken ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ† ํฐ ์ˆ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ chunk_size๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋ธ์˜ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ๋ฅผ ๋” ์ •ํ™•ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
  • chunk_overlap: ๋ถ„ํ• ๋œ ์กฐ๊ฐ๋“ค์ด ์„œ๋กœ ์•ฝ๊ฐ„์˜ ๋‚ด์šฉ์„ ๊ฒน์น˜๊ฒŒ ๋งŒ๋“ค์–ด, ๋ฌธ๋งฅ์ด ์ž˜๋ฆฌ๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์˜๋ฏธ๊ฐ€ ์ž˜ ์œ ์ง€๋˜๋„๋ก ๋•์Šต๋‹ˆ๋‹ค.

โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • UnstructuredFileLoader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ์„ ๋กœ๋“œํ–ˆ๋‚˜์š”?
  • CharacterTextSplitter.from_tiktoken_encoder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ”Œ๋ฆฌํ„ฐ๋ฅผ ์ •์˜ํ–ˆ๋‚˜์š”?
  • load_and_split ๋ฉ”์†Œ๋“œ๋กœ ๋ฌธ์„œ ๋กœ๋“œ์™€ ๋ถ„ํ• ์„ ํ•œ ๋ฒˆ์— ์‹คํ–‰ํ–ˆ๋‚˜์š”?

Embed & Store (Vector Stores)

๐ŸŽฏ ์ด๋ฒˆ ๋‹จ๊ณ„์—์„œ ๋ฐฐ์šธ ๊ฒƒ

  • RAG์˜ ์„ธ ๋ฒˆ์งธ์™€ ๋„ค ๋ฒˆ์งธ ๋‹จ๊ณ„์ธ **Embed(์ž„๋ฒ ๋”ฉ)**์™€ **Store(์ €์žฅ)**์˜ ๊ฐœ๋…์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.
  • OpenAIEmbeddings๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ ์กฐ๊ฐ์„ ๋ฒกํ„ฐ(์ˆซ์ž์˜ ๋ฐฐ์—ด)๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • FAISS ๋˜๋Š” Chroma์™€ ๊ฐ™์€ ๋ฒกํ„ฐ ์Šคํ† ์–ด์— ์ž„๋ฒ ๋”ฉ๋œ ๋ฒกํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›๋‹ˆ๋‹ค.
  • CacheBackedEmbeddings๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž„๋ฒ ๋”ฉ ๊ณผ์ •์„ ์บ์‹ฑํ•˜๊ณ  ๋น„์šฉ์„ ์ ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ 1๋‹จ๊ณ„: ํ…์ŠคํŠธ๋ฅผ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅํ•˜๊ธฐ

์ „์ฒด ์ฝ”๋“œ (notebook.ipynb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from langchain.embeddings import OpenAIEmbeddings, CacheBackedEmbeddings
from langchain.vectorstores import FAISS # ๋˜๋Š” Chroma
from langchain.storage import LocalFileStore

# ... (loader, splitter, docs๋Š” ์ด์ „ ๋‹จ๊ณ„์™€ ๋™์ผ)

# 1. ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์ •์˜
embeddings = OpenAIEmbeddings()

# 2. ์ž„๋ฒ ๋”ฉ ์บ์‹œ ์„ค์ •
cache_dir = LocalFileStore("./.cache/")
cached_embeddings = CacheBackedEmbeddings.from_bytes_store(embeddings, cache_dir)

# 3. ๋ฒกํ„ฐ ์Šคํ† ์–ด ์ƒ์„ฑ ๋ฐ ์ €์žฅ
vectorstore = FAISS.from_documents(docs, cached_embeddings)

# 4. ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰
results = vectorstore.similarity_search("where does winston live")

๐Ÿ” ์ฝ”๋“œ ์ƒ์„ธ ์„ค๋ช…

1. ์ž„๋ฒ ๋”ฉ (Embedding)
์ž„๋ฒ ๋”ฉ์€ ํ…์ŠคํŠธ๋ฅผ ์ปดํ“จํ„ฐ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ˆซ์ž์˜ ๋ฐฐ์—ด, ์ฆ‰ ๋ฒกํ„ฐ(vector)๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด ๋ฒกํ„ฐ๋Š” ํ…์ŠคํŠธ์˜ ์˜๋ฏธ๋ฅผ ๋‹ค์ฐจ์› ๊ณต๊ฐ„์˜ ํ•œ ์ ์œผ๋กœ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์˜๋ฏธ๊ฐ€ ๋น„์Šทํ•œ ํ…์ŠคํŠธ๋“ค์€ ๊ณต๊ฐ„์ƒ์—์„œ ๊ฐ€๊นŒ์šด ์œ„์น˜์— ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  • OpenAIEmbeddings: OpenAI์˜ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ๋ฅผ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

2. ๋ฒกํ„ฐ ์Šคํ† ์–ด (Vector Store)
๋ฒกํ„ฐ ์Šคํ† ์–ด๋Š” ์ƒ์„ฑ๋œ ๋ฒกํ„ฐ๋“ค์„ ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ , ํŠน์ • ๋ฒกํ„ฐ์™€ ์˜๋ฏธ์ ์œผ๋กœ ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ๋‹ค๋ฅธ ๋ฒกํ„ฐ๋“ค์„ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ž…๋‹ˆ๋‹ค.

  • FAISS / Chroma: ๋Œ€ํ‘œ์ ์ธ ์˜คํ”ˆ์†Œ์Šค ๋ฒกํ„ฐ ์Šคํ† ์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • from_documents: ๋ฌธ์„œ ์กฐ๊ฐ(docs)๊ณผ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ(cached_embeddings)์„ ๋ฐ›์•„, ๋‚ด๋ถ€์ ์œผ๋กœ ๊ฐ ์กฐ๊ฐ์„ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ ๋ฒกํ„ฐ ์Šคํ† ์–ด์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • similarity_search: ์ฃผ์–ด์ง„ ์งˆ๋ฌธ์„ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค, ๋ฒกํ„ฐ ์Šคํ† ์–ด์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด(์œ ์‚ฌํ•œ) ๋ฒกํ„ฐ๋“ค์„ ๊ฐ€์ง„ ๋ฌธ์„œ ์กฐ๊ฐ๋“ค์„ ์ฐพ์•„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

3. CacheBackedEmbeddings
์ž„๋ฒ ๋”ฉ์€ ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜๋Š” API ํ˜ธ์ถœ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CacheBackedEmbeddings๋Š” ํ•œ ๋ฒˆ ์ž„๋ฒ ๋”ฉ๋œ ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์ผ ์‹œ์Šคํ…œ(LocalFileStore)์— ์ €์žฅํ•˜์—ฌ, ๋™์ผํ•œ ํ…์ŠคํŠธ์— ๋Œ€ํ•œ ์ž„๋ฒ ๋”ฉ ์š”์ฒญ์ด ๋‹ค์‹œ ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  ์บ์‹œ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์™€ ๋น„์šฉ๊ณผ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • OpenAIEmbeddings ๋ชจ๋ธ์„ ์ดˆ๊ธฐํ™”ํ–ˆ๋‚˜์š”?
  • CacheBackedEmbeddings๋ฅผ ์„ค์ •ํ•˜์—ฌ ๋น„์šฉ์„ ์ ˆ์•ฝํ–ˆ๋‚˜์š”?
  • FAISS.from_documents๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒกํ„ฐ ์Šคํ† ์–ด๋ฅผ ์ƒ์„ฑํ–ˆ๋‚˜์š”?
  • similarity_search๋กœ ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์„œ ์กฐ๊ฐ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฒ€์ƒ‰ํ–ˆ๋‚˜์š”?

RetrievalQA Chain

๐ŸŽฏ ์ด๋ฒˆ ๋‹จ๊ณ„์—์„œ ๋ฐฐ์šธ ๊ฒƒ

  • RAG์˜ ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„์ธ **Retrieve(๊ฒ€์ƒ‰)**์™€ **Generate(์ƒ์„ฑ)**๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ๋Š” ๋ฐฉ๋ฒ•
  • RetrievalQA ์ฒด์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ RAG ํŒŒ์ดํ”„๋ผ์ธ์„ ์‰ฝ๊ฒŒ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•

๐Ÿ“ 1๋‹จ๊ณ„: RAG ์ฒด์ธ์œผ๋กœ ์งˆ๋ฌธ-๋‹ต๋ณ€ํ•˜๊ธฐ

์ „์ฒด ์ฝ”๋“œ (notebook.ipynb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from langchain.chains import RetrievalQA

# ... (llm, vectorstore ์„ค์ •์€ ์ด์ „ ๋‹จ๊ณ„์™€ ๋™์ผ)

# 1. Retriever(๊ฒ€์ƒ‰๊ธฐ) ์ƒ์„ฑ
retriever = vectorstore.as_retriever()

# 2. RetrievalQA ์ฒด์ธ ์ƒ์„ฑ
chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="map_rerank", # ์ฒด์ธ ํƒ€์ž… (stuff, map_reduce, refine, map_rerank)
retriever=retriever,
)

# 3. ์ฒด์ธ ์‹คํ–‰
chain.run("Describe Victory Mansions")

๐Ÿ” ์ฝ”๋“œ ์ƒ์„ธ ์„ค๋ช…

1. RetrievalQA
RetrievalQA๋Š” RAG์˜ ์ „์ฒด ๊ณผ์ •์„ ์ถ”์ƒํ™”ํ•˜์—ฌ ์ œ๊ณตํ•˜๋Š” ํŽธ๋ฆฌํ•œ ์ฒด์ธ์ž…๋‹ˆ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  2. retriever๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์„œ ์กฐ๊ฐ๋“ค์„ ๋ฒกํ„ฐ ์Šคํ† ์–ด์—์„œ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.
  3. ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ ์กฐ๊ฐ๋“ค์„ ์งˆ๋ฌธ๊ณผ ํ•จ๊ป˜ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์–ด LLM์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  4. LLM์€ ์ฃผ์–ด์ง„ ๋ฌธ์„œ ์กฐ๊ฐ(context)์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • retriever: vectorstore.as_retriever()๋ฅผ ํ†ตํ•ด ๋ฒกํ„ฐ ์Šคํ† ์–ด๋ฅผ ๊ฒ€์ƒ‰๊ธฐ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • chain_type: ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ๋“ค์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. (์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ ์„น์…˜์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค.)

โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • vectorstore.as_retriever()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์ƒ‰๊ธฐ๋ฅผ ๋งŒ๋“ค์—ˆ๋‚˜์š”?
  • RetrievalQA.from_chain_type์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฒด์ธ์„ ์ƒ์„ฑํ–ˆ๋‚˜์š”?
  • chain.run์œผ๋กœ ์งˆ๋ฌธ์„ ํ•˜๊ณ , ๋ฌธ์„œ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋‹ต๋ณ€์„ ๋ฐ›์•˜๋‚˜์š”?

LCEL ๊ธฐ๋ฐ˜ RAG ์ฒด์ธ (Stuff & Map-Reduce)

๐ŸŽฏ ์ด๋ฒˆ ๋‹จ๊ณ„์—์„œ ๋ฐฐ์šธ ๊ฒƒ

  • LCEL์„ ์‚ฌ์šฉํ•˜์—ฌ RAG ํŒŒ์ดํ”„๋ผ์ธ์„ ์ง์ ‘ ์ œ์–ดํ•˜๊ณ  ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • Stuff: ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ RAG ์ฒด์ธ์œผ๋กœ, ๊ฒ€์ƒ‰๋œ ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ํ•˜๋‚˜์˜ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ๋Š” ๋ฐฉ์‹
  • Map-Reduce: ๋ฌธ์„œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„ ํ•˜๋‚˜์˜ ์ปจํ…์ŠคํŠธ์— ๋‹ค ๋“ค์–ด๊ฐ€์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•

๐Ÿ“ 1๋‹จ๊ณ„: Stuff ์ฒด์ธ ์ง์ ‘ ๋งŒ๋“ค๊ธฐ

์ „์ฒด ์ฝ”๋“œ (notebook.ipynb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from langchain.schema.runnable import RunnablePassthrough

# ... (llm, retriever, prompt ์„ค์ •)

prompt = ChatPromptTemplate.from_messages([
("system", "Answer questions using only the following context:\n\n{context}"),
("human", "{question}"),
])

# Stuff Chain with LCEL
chain = (
{
"context": retriever, # retriever๊ฐ€ ์งˆ๋ฌธ์„ ๋ฐ›์•„ ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ๋ฐ˜ํ™˜
"question": RunnablePassthrough(), # ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ
}
| prompt
| llm
)

chain.invoke("Describe Victory Mansions")

๐Ÿ” ์ฝ”๋“œ ์ƒ์„ธ ์„ค๋ช…

1. Stuff Chain

  • retriever๊ฐ€ ๊ฒ€์ƒ‰ํ•œ ๋ชจ๋“  ๋ฌธ์„œ ์กฐ๊ฐ์„ {context} ํ”Œ๋ ˆ์ด์Šคํ™€๋”์— โ€œ์šฑ์—ฌ๋„ฃ๋Š”(stuff)โ€ ๊ฐ€์žฅ ์ง๊ด€์ ์ธ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • ๊ฐ„๋‹จํ•˜๊ณ  ์„ฑ๋Šฅ์ด ์ข‹์ง€๋งŒ, ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ์˜ ์ด๋Ÿ‰์ด ๋ชจ๋ธ์˜ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

2. Map-Reduce Chain

  • Map ๋‹จ๊ณ„: ๊ฒ€์ƒ‰๋œ ๊ฐ ๋ฌธ์„œ ์กฐ๊ฐ์— ๋Œ€ํ•ด ๋…๋ฆฝ์ ์œผ๋กœ ์งˆ๋ฌธ์„ ๋˜์ ธ ์ค‘๊ฐ„ ์š”์•ฝ/๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. (map_doc_chain)
  • Reduce ๋‹จ๊ณ„: ์ƒ์„ฑ๋œ ๋ชจ๋“  ์ค‘๊ฐ„ ๋‹ต๋ณ€๋“ค์„ ๋ชจ์•„, ์ตœ์ข…์ ์œผ๋กœ ํ•˜๋‚˜์˜ ์ข…ํ•ฉ์ ์ธ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. (final_prompt | llm)
  • ๋ฌธ์„œ์˜ ์–‘์ด ๋งค์šฐ ๋งŽ์„ ๋•Œ ์œ ์šฉํ•˜์ง€๋งŒ, ์—ฌ๋Ÿฌ ๋ฒˆ์˜ LLM ํ˜ธ์ถœ๋กœ ์ธํ•ด ๋น„์šฉ์ด ๋” ๋งŽ์ด ๋“ค๊ณ  ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

LCEL๋กœ ๊ตฌํ˜„ํ•œ Map-Reduce Chain:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. Map ๋‹จ๊ณ„: ๊ฐ ๋ฌธ์„œ๋ฅผ ์š”์•ฝํ•˜๋Š” ์ฒด์ธ
map_doc_prompt = ChatPromptTemplate.from_template("Relevant text: {context}...")
map_doc_chain = map_doc_prompt | llm

# 2. ๊ฒ€์ƒ‰๋œ ๋ชจ๋“  ๋ฌธ์„œ์— map_doc_chain์„ ์ ์šฉํ•˜๋Š” ๋žŒ๋‹ค ํ•จ์ˆ˜
def map_docs(inputs):
# ...
return "\n\n".join(map_doc_chain.invoke(...) for doc in documents)

map_chain = {"documents": retriever, "question": RunnablePassthrough()} | RunnableLambda(map_docs)

# 3. Reduce ๋‹จ๊ณ„: ์š”์•ฝ๋œ ๋‚ด์šฉ๋“ค์„ ์ข…ํ•ฉํ•˜์—ฌ ์ตœ์ข… ๋‹ต๋ณ€ ์ƒ์„ฑ
final_prompt = ChatPromptTemplate.from_template("Create a final answer from: {context}...")
chain = {"context": map_chain, "question": RunnablePassthrough()} | final_prompt | llm

chain.invoke("How many ministries are mentioned")

โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • LCEL์„ ์‚ฌ์šฉํ•˜์—ฌ retriever์™€ prompt, llm์„ ์—ฐ๊ฒฐํ•œ Stuff ์ฒด์ธ์„ ๋งŒ๋“ค์–ด ๋ณด์•˜๋‚˜์š”?
  • Map-Reduce์˜ ๊ฐœ๋…์„ ์ดํ•ดํ•˜๊ณ , ์™œ Stuff ์ฒด์ธ๋ณด๋‹ค ๋” ๋งŽ์€ ๋ฌธ์„œ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํŒŒ์•…ํ–ˆ๋‚˜์š”?

์ถœ์ฒ˜ : https://nomadcoders.co/fullstack-gpt