[๊ฐ์๋ ธํธ] RAG From Scratch : Overview
์๋ณธ ๊ฒ์๊ธ: https://velog.io/@euisuk-chung/RAG-From-Scratch-Overview
- ํด๋น ๋ธ๋ก๊ทธ ํฌ์คํธ๋ RAG From Scratch : Coursework ๊ฐ์ ํํธ 1 - 4 ๋ด์ฉ์ ๋ค๋ฃจ๊ณ ์์ต๋๋ค.
| ๋น๋์ค | ์์ฝ | ๊ฐ์ ๋งํฌ | ์ฌ๋ผ์ด๋ |
|---|---|---|---|
| Part 1 (๊ฐ์) | RAG๋ฅผ ์๊ฐํ๋ฉฐ, ์๋ฆฌ์ฆ๊ฐ ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ๊ณ ๊ธ ๊ธฐ์ ๊น์ง ๋ค๋ฃฐ ๊ฒ์์ ์ค๋ช ํฉ๋๋ค. | ๐ ๊ฐ์ | ๐ ์ฌ๋ผ์ด๋ |
| Part 2 (์ธ๋ฑ์ฑ) | ๊ฒ์์ ์ ํ์ฑ๊ณผ ์๋์ ์ค์ํ ์ธ๋ฑ์ฑ ๊ณผ์ ์ ์ด์ ์ ๋ง์ถฅ๋๋ค. | ๐ ๊ฐ์ | ๐ ์ฌ๋ผ์ด๋ |
| Part 3 (๊ฒ์) | ๊ฒ์์ ์ ๋ฐ์ฑ์ ์ํด ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํ ๋ฌธ์ ๊ฒ์์ ๋ค๋ฃน๋๋ค. | ๐ ๊ฐ์ | ๐ ์ฌ๋ผ์ด๋ |
| Part 4 (์์ฑ) | LLM์ ํตํ ๋ต๋ณ ์์ฑ์ ์ํ RAG ํ๋กฌํํธ ๊ตฌ์ฑ์ ํ๊ตฌํฉ๋๋ค. | ๐ ๊ฐ์ | ๐ ์ฌ๋ผ์ด๋ |
Part 1 (๊ฐ์)
- RAG์ ๊ธฐ๋ณธ ๊ฐ๋
์๊ฐ:
- ์ด ๋น๋์ค ์๋ฆฌ์ฆ๋ RAG(Retrieval-Augmented Generation)์ ๊ธฐ๋ณธ ์์น์ ๋ค๋ฃจ๊ณ , ๊ณ ๊ธ ์ฃผ์ ๊น์ง ํ์ฅํด ๋๊ฐ๋ ๊ณผ์ ์ ์ค๋ช ํฉ๋๋ค.
- RAG์ ์ฃผ์ ๋๊ธฐ๋ ๋ํ ์ธ์ด ๋ชจ๋ธ(LLM)์ด ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ์ง ์๋๋ค๋ ์ ์์ ์์๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๊ฐ์ธ ๋ฐ์ดํฐ๋ ์ต๊ทผ ๋ฐ์ดํฐ๋ LLM์ ์ฌ์ ํ์ต์ ํฌํจ๋์ง ์์ ์ ์์ต๋๋ค.

- ๋ํ, LLM์ ์ปจํ
์คํธ ์๋์ฐ(context windows)๋ผ๋ ์ ํ์ด ์์ต๋๋ค. ์ต๊ทผ์๋ ์ด ์ปจํ
์คํธ ์๋์ฐ๊ฐ ์ ์ ๋ ์ปค์ง๊ณ ์์ง๋ง, ์ฌ์ ํ ์ธ๋ถ ์์ค๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.

- RAG์ ์ธ ๊ฐ์ง ์ฃผ์ ๋จ๊ณ:
- ์ธ๋ฑ์ฑ(Indexing): ์ธ๋ถ ๋ฌธ์๋ฅผ ์ธ๋ฑ์ฑํ์ฌ ์ ๋ ฅ ์ฟผ๋ฆฌ์ ๋ฐ๋ผ ์ฝ๊ฒ ๊ฒ์ํ ์ ์๋๋ก ํฉ๋๋ค.
- ๊ฒ์(Retrieval): ์ง๋ฌธ์ ๋ํ ๋ฌธ์๋ฅผ ๊ฒ์ํ๊ณ , ์ด ๋ฌธ์๋ฅผ LLM์ ์ ๋ ฅํฉ๋๋ค.
- ์์ฑ(Generation): ๊ฒ์๋ ๋ฌธ์๋ฅผ ๋ฐํ์ผ๋ก LLM์ด ๋ต๋ณ์ ์์ฑํฉ๋๋ค.

์ฝ๋ ์์ฐ
1. Install Packages
- ํ์ ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค.
1
pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain bs4
- ์ฃผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ํฌํธํฉ๋๋ค:
1
2
3
4
5
6
7
8
import bs4
from langchain import hub
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
- API KEY ๋ฐ ์ฃผ์ ํ๊ฒฝ ๋ณ์๋ฅผ ์ ํ ํด์ค๋๋ค:
1
2
# OPEN_AI CHATGPT KEY ์ค์
os.environ['OPENAI_API_KEY'] = <your-api-key>
1
2
3
4
5
6
# langsmith์ฉ ํค ์ค์
# <https://docs.smith.langchain.com/>
import os
os.environ['LANGCHAIN_TRACING_V2'] = 'true'
os.environ['LANGCHAIN_ENDPOINT'] = '<https://api.smith.langchain.com>'
os.environ['LANGCHAIN_API_KEY'] = <your-api-key>
(์ฐธ๊ณ ) LangSmith API Key ๋ฐ๊ธ ๋ฐฉ๋ฒ
- ๋งํฌ : https://smith.langchain.com/
์ ๋งํฌ์์ Personal > Setting > Creating API Key์์ ๋ฐ๊ธ ๊ฐ๋ฅ
๐ก LangChain & LangSmith?
LangChain๊ณผLangSmith๋ ๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ(LLM) ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ์ ์ง์ํ๋ ๋ ๊ฐ์ง ๊ฐ๋ ฅํ ๋๊ตฌ๋ก, ๊ฐ๊ฐ์ ๋ชฉ์ ๊ณผ ์ฌ์ฉ ์ฌ๋ก๊ฐ ๋ค๋ฆ ๋๋ค. - ์ด ๋ ๋๊ตฌ๋ฅผ ์ ์ดํดํ๋ ๊ฒ์ LLM ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ํจ์จ์ ์ผ๋ก ๊ฐ๋ฐํ๊ณ ์ด์ํ๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
LangChain โ๏ธ
- LangChain์ ์ฃผ๋ก LLM์ ํ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ํ๊ฒ ๊ฐ๋ฐํ๊ณ ํ๋กํ ํ์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ํ๋ ์์ํฌ์ ๋๋ค. Python์ผ๋ก ์ ๊ณต๋๋ ์คํ ์์ค ํจํค์ง๋ก, ๋ค์ํ LLM์ ์ฌ์ฉํ์ฌ ๋ณต์กํ ์ธ์ด ์ฒ๋ฆฌ ์์ ์ ์ํํ ์ ์์ต๋๋ค. LangChain์ ๋ค์๊ณผ ๊ฐ์ ํน์ง์ ๊ฐ์ง๊ณ ์์ต๋๋ค:
- ์ฒด์ธ๊ณผ ์์ด์ ํธ: LangChain์ ์ฌ๋ฌ ์์ ์ ์ฒด์ธ(Chain) ํํ๋ก ์ฐ๊ฒฐํ๊ฑฐ๋, ๋ณต์กํ ์์ฌ ๊ฒฐ์ ๊ณผ์ ์ ์์ด์ ํธ(Agent)๋ก ๊ตฌํํ์ฌ ์๋ํํ ์ ์์ต๋๋ค.
- ํ๋กฌํํธ ํ ํ๋ฆฟ: ํน์ ์์ ์ ๋ง๋ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ์ฝ๊ฒ ์ ์ํ๊ณ ํ์ฉํ ์ ์์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์์คํ : ์์ ์ ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํด ๋ค์ํ ๋ฉ๋ชจ๋ฆฌ ์์คํ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
LangSmith โ๏ธ
- LangSmith๋ LangChain ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ, ํ ์คํธ, ๋ชจ๋ํฐ๋ง, ๊ทธ๋ฆฌ๊ณ ๋ฐฐํฌ๋ฅผ ์ํ ์ข ํฉ์ ์ธ DevOps ํ๋ซํผ์ ๋๋ค. ์ด๋ ๋๊ท๋ชจ LLM ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ด๋ฆฌํ๊ณ ์ต์ ํํ๋ ๋ฐ ํ์ํ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ฉฐ, ํนํ ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ด ๋๋ณด์ ๋๋ค:
- ๋๋ฒ๊น : ๋ชจ๋ธ์ ๋ชจ๋ ๋จ๊ณ์์ ์ ๋ ฅ๊ณผ ์ถ๋ ฅ์ ์ถ์ ํ ์ ์์ด, ์์์น ๋ชปํ ๊ฒฐ๊ณผ๋ ์ค๋ฅ๋ฅผ ์ฝ๊ฒ ์๋ณํ ์ ์์ต๋๋ค.
- ํ ์คํธ: ์๋ก์ด ์ฒด์ธ๊ณผ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ์คํํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํ์ฌ, ์์ ์ฑ๊ณผ ์ฑ๋ฅ์ ํ์ธํ ์ ์์ต๋๋ค.
- ๋ชจ๋ํฐ๋ง: ์ ํ๋ฆฌ์ผ์ด์ ์ ์ง์ฐ ์๊ฐ๊ณผ ํ ํฐ ์ฌ์ฉ๋์ ์ถ์ ํ์ฌ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์๋ ํธ์ถ์ ์๋ณํ ์ ์์ต๋๋ค.
- ํ๊ฐ: ๋ณต์กํ ํ๋กฌํํธ ์ฒด์ธ์ ํ๊ฐํ๊ณ , ์ฑ๋ฅ์ ์ต์ ํํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ํ๋ก์ ํธ ๋ถ์: ์คํ ์นด์ดํธ, ์ค๋ฅ ๋ฐ์๋ฅ , ํ ํฐ ์ฌ์ฉ๋ ๋ฑ์ ํ๋ก์ ํธ ๋จ์๋ก ๋ถ์ํ ์ ์์ต๋๋ค.
(์ฐธ๊ณ ) Langfuse ๐งฌ
- Langfuse๋ LLM(๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ) ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ์คํ์์ค ๊ด์ธก์ฑ ๋ฐ ๋ถ์ ํ๋ซํผ์ผ๋ก, ๋ค์๊ณผ ๊ฐ์ ์ฃผ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
- ๊ด์ธก์ฑ: ๋ณต์กํ LLM ์ฑ ์คํ์ ์๊ฐ์ UI๋ฅผ ํตํด ํ์ํ๊ณ ๋๋ฒ๊น ํ ์ ์์ต๋๋ค. ๋๊ธฐ ์๊ฐ, ๋น์ฉ, ์ฑ๋ฅ ์ ์ ๋ฑ์ ์์ธ ์ ๋ณด๋ฅผ ํฌํจํ ์ค์ฒฉ๋ ๋ทฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ๋ถ์: ๋น์ฉ, ์ง์ฐ ์๊ฐ, ์๋ต ํ์ง์ ์ธก์ ํ๊ณ ๊ฐ์ ํ ์ ์์ต๋๋ค. ๋ชจ๋ธ๋ณ ํ ํฐ ์ฌ์ฉ๋, ํ๊ฐ ์ ์ ๋ฑ์ ๋ฆฌํฌํธ๋ก ์ ๊ณตํฉ๋๋ค.
- ํ๋กฌํํธ ๊ด๋ฆฌ: Langfuse ๋ด์์ ํ๋กฌํํธ๋ฅผ ๊ด๋ฆฌ, ๋ฒ์ ๊ด๋ฆฌ, ๋ฐฐํฌํ ์ ์์ด ํจ์จ์ ์ธ ํ๋กฌํํธ ์์ง๋์ด๋ง์ด ๊ฐ๋ฅํฉ๋๋ค.
- ํ๊ฐ: LLM ์์ฑ์ ๋ํ ์ ์๋ฅผ ์์งํ๊ณ ๊ณ์ฐํฉ๋๋ค. ๋ชจ๋ธ ๊ธฐ๋ฐ ํ๊ฐ, ์ฌ์ฉ์ ํผ๋๋ฐฑ ์์ง, ์๋ ์ ์ ๋งค๊ธฐ๊ธฐ ๋ฑ ๋ค์ํ ํ๊ฐ ๋ฐฉ๋ฒ์ ์ง์ํฉ๋๋ค.
- ์คํ ๋ฐ ํ ์คํธ: ์ ๋ฒ์ ์ ๋ฐฐํฌํ๊ธฐ ์ ์ ์ฑ ๋์์ ์ถ์ ํ๊ณ ํ ์คํธํ ์ ์์ต๋๋ค. ๋ฐ์ดํฐ์ ์ ์ฌ์ฉํ์ฌ ์์ ์ ์ถ๋ ฅ ์์ ํ ์คํธํ๊ณ ์ฑ๋ฅ์ ๋ฒค์น๋งํฌํ ์ ์์ต๋๋ค.
- ํตํฉ: LlamaIndex, Langchain ๋ฑ ์ฃผ์ LLM ํ๋ ์์ํฌ์์ ํตํฉ์ ์ ๊ณตํ์ฌ ๋ค์ํ LLM ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
2. Indexing
- ์ด์ ํ์ํ ํ๊ฒฝ ์ค์ ์ ๋คํ์ผ๋, Indexing ์ฝ๋๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#### INDEXING ####
# Load Documents
loader = WebBaseLoader(
web_paths=("<https://velog.io/@euisuk-chung/๊ฟํ-Velog-๊ธ์จ๋ฅผ-๋ด-๋ง์๋๋ก-์์-ํ๊ดํ>",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header")
)
),
)
docs = loader.load()
# Split
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
# Embed
vectorstore = Chroma.from_documents(documents=splits,
embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
- ์ ์ฝ๋์์
Chroma๋ vectorstore์ ํ ์ข ๋ฅ๋ก, LangChain ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์์ฃผ ์ฌ์ฉ๋๋ ๊ตฌํ์ฒด์ ๋๋ค. -
Vectorstore๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ท๋ชจ ๋ฐ์ดํฐ์ ์์ ํจ์จ์ ์ธ ์ ์ฌ์ฑ ๊ฒ์์ ์ํํ ์ ์์ด, ์์ฐ์ด ์ฒ๋ฆฌ๋ ๊ธฐ๊ณํ์ต ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋๋ฆฌ ํ์ฉ๋ฉ๋๋ค.
- Chroma, Pinecone, Faiss ๋ฑ ๋ค์ํ ๊ตฌํ์ฒด๊ฐ ์์ต๋๋ค.
-
์ฃผ์ ํน์ง์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ๊ณ ์ฐจ์ ๋ฒกํฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๊ฒ์ํ๋ ๋ฐ ํนํ๋์ด ์์ต๋๋ค.
- ํ ์คํธ, ์ด๋ฏธ์ง, ์ค๋์ค ๋ฑ์ ๋ฐ์ดํฐ๋ฅผ ๋ฒกํฐ๋ก ๋ณํํ์ฌ ์ ์ฅํฉ๋๋ค.
- ์ ์ฌ์ฑ ๊ฒ์์ ๋น ๋ฅด๊ฒ ์ํํ ์ ์์ด ์ถ์ฒ ์์คํ , ์ด๋ฏธ์ง ๊ฒ์ ๋ฑ์ ํ์ฉ๋ฉ๋๋ค.
3. Retreive and Generate
- Indexing์ด ๋๋ ๋ค, ๊ฒ์๊ธฐ(retriever)๋ฅผ ์ ์ํ๊ณ , ๊ฒ์๋ ๋ฌธ์์ ํจ๊ป ์ง๋ฌธ์ LLM์ ์ ๋ฌํ์ฌ ๋ต๋ณ์ ์์ฑ(generate)ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#### RETRIEVAL and GENERATION ####
# Prompt
prompt = hub.pull("rlm/rag-prompt")
# LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# Post-processing
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# Chain
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# Question
rag_chain.invoke("What is Task Decomposition?")
## Answer Returned
'Task Decomposition is a technique used to break down complex tasks into smaller, more manageable steps.
It involves methods like Chain of Thought (CoT) and Tree of Thoughts, which guide models to think step by step and explore multiple reasoning possibilities.
This approach enhances model performance by simplifying and structuring tasks systematically.'
- langsmith ํด์ ์ฌ์ฉํ๋ฉด ์ฌ์ดํธ > ํ๋ก์ ํธ๋ก ์ ์ํ์ฌ ์๋์ ๊ฐ์ด ์ง๋ฌธ๊ณผ ๊ฒ์๋ ๋ฌธ์, ๊ทธ๋ฆฌ๊ณ ์์ฑ๋ ๋ต๋ณ์ ํ์ธํ ์ ์์ต๋๋ค.

Part 2 (์ธ๋ฑ์ฑ)
- ์ด๋ฒ ์์์ RAG(Retrieval-Augmented Generation) ํ์ดํ๋ผ์ธ์ ๋ ๋ฒ์งธ ํํธ๋ก, โ์ธ๋ฑ์ฑ(Indexing)โ์ ๋ํด ๋ค๋ฃน๋๋ค.
- ์ด์ ์์์์๋ RAG ํ์ดํ๋ผ์ธ์ ์ฃผ์ ๊ตฌ์ฑ ์์(์ธ๋ฑ์ฑ, ๊ฒ์, ์์ฑ)์ ๋ํด ๊ฐ๊ด์ ์ผ๋ก ์ค๋ช ํ์ผ๋ฉฐ, ์ด๋ฒ ์์์์๋ ๊ทธ ์ค ์ธ๋ฑ์ฑ์ ๋ํด ์ฌ๋ ์๊ฒ ์ค๋ช ํฉ๋๋ค.
-
์ธ๋ฑ์ฑ์ ์ญํ : ์ธ๋ฑ์ฑ์ ์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ์ธ๋ถ ๋ฌธ์๋ฅผ ๋ก๋ํ๊ณ ์ด๋ฅผ โ๋ฆฌํธ๋ฆฌ๋ฒ(Retriever)โ์ ๋ฃ๋ ๊ฒ์ ๋๋ค.
-
๋ฆฌํธ๋ฆฌ๋ฒ(Retriever)์ ๋ชฉํ๋ ์ ๋ ฅ๋ ์ง๋ฌธ์ ๋ํด ๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ์๋ด๋ ๊ฒ์ ๋๋ค.

โ๏ธ ์ฌ์ ์ ์๋ฏธ๋ก
retrieve๋ โํ์ํ๋คโ๋ผ๋ ๋ป์ผ๋ก, _โ์ฐ๋ฆฌ๊ฐ ์ง๋ฌธํ ์ง์๋ฌธ๊ณผ ์ ์ฌํ ๋ด์ฉ์ ๋ฌธ์๋ฅผ ์ ์ฅํด๋ VectorDB์์ ํ์ํด ์จ๋คโ_๋ผ๊ณ ์ง์ญํด๋ณผ ์๋ ์์ ๊ฒ ๊ฐ์ต๋๋ค.
-

- ๊ด๊ณ(์ ์ฌ์ฑ)์ ํ๋ฆฝํ๋ ๋ฐฉ๋ฒ์ ์ฃผ๋ก ๋ฌธ์์ ์์น์ ํํ(numerical representation)์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค. ์ด๋ ์์ ํ์์ ํ
์คํธ๋ณด๋ค๋ ๋ฒกํฐ ๋น๊ต๊ฐ ํจ์ฌ ์ฝ๊ธฐ ๋๋ฌธ์
๋๋ค.
-
๋ฒกํฐ ํํ ๋ฐฉ๋ฒ:

- Sparse Vectors: ๊ณผ๊ฑฐ์๋ ๊ตฌ๊ธ ๋ฑ์์ ๋ฌธ์์ ๋จ์ด ๋น๋์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ ํฌ์ ๋ฒกํฐ(sparse vectors)๋ฅผ ์์ฑํ๋ ํต๊ณ์ ๋ฐฉ๋ฒ์ด ๋ง์ด ์ฌ์ฉ๋์์ต๋๋ค. ์ด ๋ฒกํฐ์ ๊ฐ ์์น๋ ํฐ ์ดํ ์งํฉ ์ค ํน์ ๋จ์ด์ ๋ฐ์ ํ์๋ฅผ ๋ํ๋ด๋ฉฐ, ๋ฌธ์์ ํฌํจ๋์ง ์์ ๋จ์ด์ ๊ฒฝ์ฐ ๊ฐ์ด 0์ด ๋ฉ๋๋ค.
- Embedding Methods: ์ต๊ทผ์๋ ๋ฌธ์๋ฅผ ๊ณ ์ ๋ ๊ธธ์ด์ ๋ฒกํฐ๋ก ์์ถํ๋ ๋จธ์ ๋ฌ๋ ๊ธฐ๋ฐ ์๋ฒ ๋ฉ(embedding) ๋ฐฉ๋ฒ์ด ๊ฐ๋ฐ๋์์ต๋๋ค. ์ด ๋ฐฉ๋ฒ์ ๋ฌธ์์ ์๋ฏธ์ ๋ด์ฉ์ ๋ฒกํฐ์ ์์ถํด ๋ด์๋ด๋ฉฐ, ์ด๋ฌํ ๋ฒกํฐ๋ ๊ฒ์์ ๋งค์ฐ ํจ๊ณผ์ ์ ๋๋ค.
-
๋ฌธ์ ๋ถํ ๋ฐ ์๋ฒ ๋ฉ:

- ๋ฌธ์๋ ์๋ฒ ๋ฉ ๋ชจ๋ธ์ ์ ํ๋ ์ปจํ ์คํธ ์๋์ฐ(512~8000 ํ ํฐ) ๋๋ฌธ์ ๋ถํ ๋ฉ๋๋ค. ๊ฐ ๋ฌธ์ ์กฐ๊ฐ์ ๋ฒกํฐ๋ก ์์ถ๋๋ฉฐ, ์ด ๋ฒกํฐ๋ ๋ฌธ์์ ์๋ฏธ์ ์๋ฏธ๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
- ์ง๋ฌธ๋ ๋์ผํ ๋ฐฉ์์ผ๋ก ์๋ฒ ๋ฉ๋๋ฉฐ, ์ด๋ ๊ฒ ์์ฑ๋ ๋ฒกํฐ๋ค์ ๋น๊ตํ์ฌ ๊ด๋ จ ๋ฌธ์๋ฅผ ๊ฒ์ํ๊ฒ ๋ฉ๋๋ค.
-
์ฝ๋ ์์ฐ
1. tiktoken ํจํค์ง์ ํ ํฐ ๊ฐ์ ๊ณ์ฐ
1
2
3
4
5
6
7
8
9
10
import tiktoken
def num_tokens_from_string(string: str, encoding_name: str) -> int:
"""Returns the number of tokens in a text string."""
encoding = tiktoken.get_encoding(encoding_name)
num_tokens = len(encoding.encode(string))
return num_tokens
num_tokens_from_string(question, "cl100k_base")
์ค๋ช :
tiktokenํจํค์ง๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌธ์์ด์ ํ ํฐ ๊ฐ์๋ฅผ ๊ณ์ฐํฉ๋๋ค. ์ฌ๊ธฐ์ โํ ํฐโ์ ํ ์คํธ๋ฅผ ๊ตฌ์ฑํ๋ ์์ ๋จ์๋ก, ์ผ๋ฐ์ ์ผ๋ก ๋จ์ด ๋๋ ๋จ์ด์ ์ผ๋ถ๋ฅผ ์๋ฏธํฉ๋๋ค.num_tokens_from_stringํจ์๋ ์ฃผ์ด์ง ๋ฌธ์์ด๊ณผ ์ธ์ฝ๋ฉ ๋ฐฉ์์ ์ฌ์ฉํ์ฌ ํด๋น ๋ฌธ์์ด์ ํ ํฐ ๊ฐ์๋ฅผ ๋ฐํํฉ๋๋ค.tiktoken.get_encoding(encoding_name)์ ์ง์ ๋ ์ธ์ฝ๋ฉ ๋ฐฉ์์ ๋ถ๋ฌ์ค๊ณ ,encoding.encode(string)์ ๋ฌธ์์ด์ ํ ํฐ์ผ๋ก ์ธ์ฝ๋ฉํฉ๋๋ค.- ๋ง์ง๋ง์ผ๋ก
len(encoding.encode(string))์ ์ธ์ฝ๋ฉ๋ ํ ํฐ ๋ฆฌ์คํธ์ ๊ธธ์ด(ํ ํฐ ๊ฐ์)๋ฅผ ๋ฐํํฉ๋๋ค.
tiktoken ํจํค์ง ์ค๋ช :
tiktoken์ ์ฃผ๋ก OpenAI์ ๋ชจ๋ธ๋ค(์: GPT-3.5, GPT-4 ๋ฑ)์์ ์ฌ์ฉ๋๋ ํ ํฐํ(tokenization) ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ด ํจํค์ง๋ ํ ์คํธ๋ฅผ ํ ํฐ์ผ๋ก ๋ณํํ๊ณ , ์ด๋ฌํ ํ ํฐ์ด ๋ชจ๋ธ์ ์ ๋ ฅ๋ ๋์ ํ ํฐ ์๋ฅผ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๊ฐ ํ ํฐ์ ์ฝ 4์ ์ ๋๋ก ๊ตฌ์ฑ๋ ๋จ์์ด๋ฉฐ, ์ด๋ ๋ชจ๋ธ์ด ํ ์คํธ๋ฅผ ์ฒ๋ฆฌํ ๋์ ๊ธฐ๋ณธ์ ์ธ ๋จ์๊ฐ ๋ฉ๋๋ค.
2. ํ ์คํธ ์๋ฒ ๋ฉ ๋ชจ๋ธ
1
2
3
4
5
6
from langchain_openai import OpenAIEmbeddings
embd = OpenAIEmbeddings()
query_result = embd.embed_query(question)
document_result = embd.embed_query(document)
len(query_result)
์ค๋ช :
- ์ด ์ฝ๋ ๋ธ๋ก์์๋
langchain_openaiํจํค์ง์OpenAIEmbeddings๋ฅผ ์ฌ์ฉํ์ฌ ์ฃผ์ด์ง ์ง๋ฌธ๊ณผ ๋ฌธ์๋ฅผ ์๋ฒ ๋ฉํฉ๋๋ค. - ์๋ฒ ๋ฉ(embedding)์ด๋ ํ ์คํธ๋ฅผ ๊ณ ์ฐจ์ ๋ฒกํฐ๋ก ๋ณํํ๋ ๊ณผ์ ์ ๋๋ค. ์ด ๋ฒกํฐ๋ค์ ์๋ฏธ์ ์ผ๋ก ์ ์ฌํ ํ ์คํธ๋ค์ด ์๋ก ๊ฐ๊น์ด ์์นํ๋๋ก ํ์ฌ, ํ ์คํธ์ ์๋ฏธ๋ฅผ ์์น์ ์ผ๋ก ํํํ ์ ์๊ฒ ํฉ๋๋ค.
embed_query๋ฉ์๋๋ ์ฃผ์ด์ง ํ ์คํธ(์ง๋ฌธ์ด๋ ๋ฌธ์)๋ฅผ ์๋ฒ ๋ฉํ์ฌ ๋ฒกํฐ ํํ์ผ๋ก ๋ณํํฉ๋๋ค.- ๊ฒฐ๊ณผ์ ์ผ๋ก
query_result์document_result๋ ๊ฐ๊ฐ ์ง๋ฌธ๊ณผ ๋ฌธ์์ ์๋ฒ ๋ฉ ๋ฒกํฐ๊ฐ ๋ฉ๋๋ค.(print๋ก ๊ธธ์ด ์ถ๋ ฅ ์ ๋์ผ ๊ธธ์ด์ ์๋ฐฐ๋ฉ ๋ฒกํฐ๊ฐ ์์ฑ๋๋ ๊ฒ์ ํ์ธํ ์ ์์)

3. ์ฝ์ฌ์ธ ์ ์ฌ๋ ๊ณ์ฐ
1
2
3
4
5
6
7
8
9
10
11
import numpy as np
def cosine_similarity(vec1, vec2):
dot_product = np.dot(vec1, vec2)
norm_vec1 = np.linalg.norm(vec1)
norm_vec2 = np.linalg.norm(vec2)
return dot_product / (norm_vec1 * norm_vec2)
similarity = cosine_similarity(query_result, document_result)
print("Cosine Similarity:", similarity)
์ค๋ช :
- ์ด ๋ธ๋ก์ ๋ ์๋ฒ ๋ฉ ๋ฒกํฐ ๊ฐ์ ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ ๊ณ์ฐํฉ๋๋ค.
- ์ฝ์ฌ์ธ ์ ์ฌ๋๋ ๋ ๋ฒกํฐ ์ฌ์ด์ ๊ฐ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ์ฌ์ฑ์ ์ธก์ ํ๋ฉฐ, 1์ ๊ฐ๊น์ธ์๋ก ๋ ๋ฒกํฐ๊ฐ ์ ์ฌํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
np.dot(vec1, vec2)๋ ๋ ๋ฒกํฐ์ ๋ด์ ์ ๊ณ์ฐํ๊ณ ,np.linalg.norm(vec1)์np.linalg.norm(vec2)๋ ๊ฐ๊ฐ์ ๋ฒกํฐ์ ํฌ๊ธฐ(๋ ธ๋ฆ)๋ฅผ ๊ณ์ฐํฉ๋๋ค.- ๋ง์ง๋ง์ผ๋ก
dot_product / (norm_vec1 * norm_vec2)๋ ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ ๋ฐํํฉ๋๋ค.

- ์ ๊ฐ ์ฌ์ฉํ GPT ๋ชจ๋ธ์ ์๋ฐฐ๋ฉ ๊ธฐ๋ฐ์ Query์, Document์ ์ ์ฌ๋๋ ๊ทธ๋ ๊ฒ ๋์ง ์์ง๋ง, ์ด๋์ ๋์ ์ ์ฌ์ฑ์ ๋๊ณ ์๊ธดํ๋ค์!
4. ๋ฌธ์ ๋ก๋
1
2
3
4
5
6
7
8
9
10
11
12
13
from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader(
web_paths=("<https://lilianweng.github.io/posts/2023-06-23-agent/>",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header")
)
),
)
blog_docs = loader.load()
์ค๋ช :
- ์ด ๋ธ๋ก์์๋
langchain_community์WebBaseLoader๋ฅผ ์ฌ์ฉํ์ฌ ์ง์ ๋ ์น ํ์ด์ง์์ ๋ฌธ์๋ฅผ ๋ก๋ํฉ๋๋ค. WebBaseLoader๋ ์น ํ์ด์ง์ ํน์ HTML ์์๋ฅผ ์ถ์ถํ์ฌ ํ ์คํธ๋ก ๋ณํํ๋ ์ญํ ์ ํฉ๋๋ค. ์ฌ๊ธฐ์๋bs4.SoupStrainer๋ฅผ ์ฌ์ฉํ์ฌclass_๋ก ์ง์ ๋ ์์๋ค๋ง ์ถ์ถํฉ๋๋ค.loader.load()๋ ๋ก๋๋ ๋ฌธ์๋ฅผ ๋ฐํํฉ๋๋ค.
5. ํ ์คํธ ๋ถํ ๊ธฐ
1
2
3
4
5
6
7
8
9
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=300,
chunk_overlap=50
)
splits = text_splitter.split_documents(blog_docs)
์ค๋ช :
- ์ด ๋ถ๋ถ์ ๊ธด ํ
์คํธ๋ฅผ ์์ ๋ฉ์ด๋ฆฌ๋ก ๋ถํ ํ๋ ๊ณผ์ ์
๋๋ค.
RecursiveCharacterTextSplitter๋ ํ ์คํธ๋ฅผ ํน์ ํฌ๊ธฐ๋ก ๋๋๋, ๋ฉ์ด๋ฆฌ ๊ฐ์ ๊ฒน์น๋ ๋ถ๋ถ๋ ํฌํจ๋๋๋ก ํฉ๋๋ค. chunk_size=300์ ๊ฐ ๋ฉ์ด๋ฆฌ์ ์ต๋ ํฌ๊ธฐ๋ฅผ ์ค์ ํ๋ฉฐ,chunk_overlap=50์ ๋ฉ์ด๋ฆฌ ๊ฐ์ ๊ฒน์น๋ ๋ถ๋ถ์ ํฌ๊ธฐ๋ฅผ ์ค์ ํฉ๋๋ค.- ์ต์ข
์ ์ผ๋ก
split_documents๋ฉ์๋๋ฅผ ํตํด ๋ฌธ์๋ฅผ ๋ถํ ํฉ๋๋ค.
6. ๋ฒกํฐ ์คํ ์ด
1
2
3
4
5
6
7
8
9
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
vectorstore = Chroma.from_documents(documents=splits,
embedding=OpenAIEmbeddings())
# ๊ฒ์๊ธฐ ์์ฑ
retriever = vectorstore.as_retriever()
์ค๋ช :
- ์ด ์ฝ๋์์๋ ๋ถํ ๋ ๋ฌธ์ ๋ฉ์ด๋ฆฌ๋ค์ ๋ฒกํฐ ์คํ ์ด์ ์ธ๋ฑ์ฑํฉ๋๋ค.
Chroma๋ ํ ์คํธ์ ์๋ฒ ๋ฉ์ ์ ์ฅํ๊ณ ๊ฒ์ํ ์ ์๋ ๋ฒกํฐ ์คํ ์ด๋ฅผ ์์ฑํ๋ ์ญํ ์ ํฉ๋๋ค. Chroma.from_documents๋ ๋ฌธ์์ ์๋ฒ ๋ฉ์ ์์ฑํ์ฌ ๋ฒกํฐ ์คํ ์ด์ ์ ์ฅํฉ๋๋ค.- ๋ง์ง๋ง์ผ๋ก
vectorstore.as_retriever()๋ฅผ ํตํด ์ ์ฅ๋ ์๋ฒ ๋ฉ์์ ํ ์คํธ ๊ฒ์์ด ๊ฐ๋ฅํ ๊ฒ์๊ธฐ๋ฅผ ์์ฑํฉ๋๋ค.
Part 3 (๊ฒ์)
- ์ด ๋น๋์ค๋ LangChain์ Lance๊ฐ ์งํํ๋ โRAG From Scratchโ ์๋ฆฌ์ฆ์ ์ธ ๋ฒ์งธ ์์์ผ๋ก, ์ด๋ฒ ์ฃผ์ ๋ ์ ๋ณด ๊ฒ์(Retrieval)์ ๋๋ค.
- ์์ธํ์ ๊ธฐ๋ณธ ๊ฐ๋
- ๋ฌธ์๋ฅผ ์์ธํํ๋ ๊ณผ์ ์ ๋ฌธ์๋ฅผ ์์ ์กฐ๊ฐ(Chunks)์ผ๋ก ๋๋๊ณ , ์ด๋ฅผ ์๋ฒ ๋ฉ(Embedding)ํ์ฌ ๋ฒกํฐํํ๋ ๊ฒ์ ๋๋ค.
- ์ด ๋ฒกํฐํ๋ ์๋ฒ ๋ฉ์ ๋ฌธ์์ ์๋ฏธ์ ๋ด์ฉ์ ๋ฐ๋ผ ๊ณ ์ฐจ์ ๊ณต๊ฐ์ ํ ์ ์ผ๋ก ํํ๋ฉ๋๋ค. ์ด ์ ์ ์์น๋ ๋ฌธ์์ ์๋ฏธ๋ฅผ ๋ฐ์ํ๋ฉฐ, ์ง๋ฌธ๋ ๋์ผํ ๋ฐฉ์์ผ๋ก ์๋ฒ ๋ฉ๋์ด ํด๋น ๊ณต๊ฐ์์ ๋ฌธ์์์ ์ ์ฌ์ฑ์ ๊ฒ์ํ๊ฒ ๋ฉ๋๋ค.

์ฝ๋ ์์ฐ
- ๋ฌธ์๊ฐ ์๋ฒ ๋ฉ๋ ๊ณ ์ฐจ์ ๊ณต๊ฐ์์ ์ง๋ฌธ๊ณผ ์ ์ฌํ ๋ฌธ์๋ฅผ ๊ฒ์ํ๋ ๊ณผ์ ์ ๋ก์ปฌ ๋ค์ด๋ฒํ๋ ๊ฒ์(Local Neighborhood Search)์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค.
- ์ด ๊ฒ์ ๊ณผ์ ์์ ์ง๋ฌธ๊ณผ ๊ฐ๊น์ด ์์น์ ์๋ ๋ฌธ์๋ค์ ์ฐพ์๋ด๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ด๋ จ ๋ฌธ์๋ฅผ ๋ฐํํฉ๋๋ค.
- ๋ฌธ์ ๊ฒ์ ์, ๋ฐํํ ๋ฌธ์์ ์๋ฅผ ๊ฒฐ์ ํ๋ K-๊ฐ์ ์ค์ ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, K=1๋ก ์ค์ ํ๋ฉด ์ง๋ฌธ๊ณผ ๊ฐ์ฅ ๊ฐ๊น์ด ํ ๊ฐ์ ๋ฌธ์๋ง ๋ฐํํฉ๋๋ค.
1 2 3 4 5 6 7 8 9 10 11 12 13
# Index from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma # ์์์ ๋ณธ ๊ฒ์ฒ๋ผ vectorstore ์ ์ธ vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings()) retriever = vectorstore.as_retriever(search_kwargs={"k": 1}) docs = retriever.get_relevant_documents("What is Task Decomposition?") len(docs) # 1์ด ๋ฐํ๋จ

Part 4 (์์ฑ)
- ํด๋น ์์์์๋ ๋ฌธ์ ๊ฒ์์ ํตํด ์ป์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก
LLM(Large Language Model)์ ํ์ฉํ์ฌ ๋ต๋ณ์ ์์ฑํ๋ ๊ณผ์ ์ ์ค์ ์ ๋ก๋๋ค.

(๋ณต์ต) ๋ฌธ์ ์ฝ์ ๊ณผ LLM ์ปจํ ์คํธ ์๋์ฐ
- ๋ฌธ์ ๊ฒ์ ํ, ๋ฌธ์๋ฅผ ์์ ๋จ์๋ก ๋๋ ๋ค, ์ด๋ฅผ ๋ฒกํฐ๋ก ๋ณํํ์ฌ ๋ฒกํฐ ์คํ ์ด์ ์ ์ฅํฉ๋๋ค.
- ์ง๋ฌธ๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ฒกํฐ๋ก ๋ณํํ์ฌ KNN(K-Nearest Neighbors)๊ณผ ๊ฐ์ ๊ธฐ๋ฒ์ผ๋ก ๊ฒ์๋ ๋ฌธ์์ ๋น๊ตํฉ๋๋ค.
- ๊ฒ์๋ ๋ฌธ์๋ LLM์ ์ปจํ ์คํธ ์๋์ฐ์ ์ฝ์ ๋์ด ๋ต๋ณ ์์ฑ์ ์ฌ์ฉ๋ฉ๋๋ค.
์์์ ๋ณด์ฌ๋๋ฆฐ ๊ทธ๋ฆผ์ ์ข ๋ ์์ธํ๊ฒ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค:

- Documents โ Embedding โ Vectorstore
- Documents: ์ด ๋ถ๋ถ์ ์ฐ๋ฆฌ๊ฐ ์ฒ๋ฆฌํ๊ณ ์ ํ๋ ์๋ณธ ๋ฌธ์์ ๋๋ค. ๋ค์ํ ํ ์คํธ ๋ฐ์ดํฐ๊ฐ ํฌํจ๋์ด ์์ ์ ์์ต๋๋ค.
- Embedding: ๋ฌธ์๋ฅผ ์ง์ ์ ์ผ๋ก ๊ฒ์ํ๊ธฐ ์ด๋ ค์ฐ๋ฏ๋ก, ์ด ๋ฌธ์๋ค์ ์๋ฒ ๋ฉ(embedding)ํ์ฌ ๊ณ ์ฐจ์ ๋ฒกํฐ๋ก ๋ณํํฉ๋๋ค. ์๋ฒ ๋ฉ๋ ๋ฒกํฐ๋ ๋ฌธ์์ ์๋ฏธ์ ํน์ง์ ํฌํจํ๊ณ ์์ผ๋ฉฐ, ์ด๋ฅผ ๋ฐํ์ผ๋ก ์ ์ฌ๋๋ฅผ ๊ณ์ฐํ ์ ์์ต๋๋ค.
- Vectorstore: ๋ฒกํฐ๋ก ๋ณํ๋ ๋ฌธ์๋ค์ ๋ฒกํฐ ์คํ ์ด(vectorstore)์ ์ ์ฅ๋ฉ๋๋ค. ๋ฒกํฐ ์คํ ์ด๋ ๋์ค์ ์ง๋ฌธ๊ณผ ์ ์ฌํ ๋ฌธ์๋ฅผ ํจ์จ์ ์ผ๋ก ๊ฒ์ํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค. (Ex. KNN, HNSW ๊ธฐ๋ฒ ๋ฑ์ ๊ธฐ๋ฐ์ผ๋ก ๊ฒ์์ ์ํํฉ๋๋ค.)
HNSW(Hierarchical Navigable Small World): ๊ณ ์ฐจ์ ๋ฒกํฐ ๋ฐ์ดํฐ์ ๊ทผ์ฌ ์ต๊ทผ์ ์ด์(Approximate Nearest Neighbor) ๊ฒ์์ ์ํ ํจ์จ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ ๋๋ค.
- Question โ Embedding โ Vectorstore โ Relevant Documents
- Question: ์ฌ์ฉ์๊ฐ ์ง๋ฌธ์ ๋์ง๋ฉด, ์ด ์ง๋ฌธ๋ ๋ฌธ์์ ๋์ผํ ๋ฐฉ์์ผ๋ก ์๋ฒ ๋ฉ๋ฉ๋๋ค. ์ฆ, ์ง๋ฌธ๋ ๋ฒกํฐ๋ก ๋ณํ๋ฉ๋๋ค.
- Vectorstore โ Relevant Documents: ๋ณํ๋ ์ง๋ฌธ ๋ฒกํฐ๋ ๋ฒกํฐ ์คํ ์ด์์ ๋ฌธ์ ๋ฒกํฐ๋ค๊ณผ ๋น๊ต๋์ด ๊ฐ์ฅ ์ ์ฌํ ๋ฌธ์๋ค์ด ๊ฒ์๋ฉ๋๋ค.
- ์ด ๋ฌธ์๋ค์ด Relevant Documents๋ก ํ์๋ฉ๋๋ค. ์ด๋ ์ง๋ฌธ๊ณผ ์๋ฏธ์ ์ผ๋ก ๊ฐ์ฅ ๊ฐ๊น์ด ๋ฌธ์๋ค์ด๋ฉฐ, LLM์ ์ปจํ ์คํธ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
- Dict ์์ฑ
- ๊ฒ์๋ ๋ฌธ์์ ์ง๋ฌธ์ Dict๋ผ๋ ์๋ฃ ๊ตฌ์กฐ๋ก ๋ณํ๋ฉ๋๋ค.
- ์ด Dict๋ ๋ ๊ฐ์ง ํ๋๋ฅผ ํฌํจํ๊ณ ์๋๋ฐ:
- {Context}: ๊ฒ์๋ ๋ฌธ์๊ฐ ๋ค์ด๊ฐ๋๋ค.
- {Question}: ์ฌ์ฉ์์ ์ง๋ฌธ์ด ๋ค์ด๊ฐ๋๋ค.
- ์ด Dict๋ Prompt Template์ ๋ค์ด๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ํ๋ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
- Prompt Template
- ํ๋กฌํํธ ํ
ํ๋ฆฟ(Prompt Template)์ LLM(Large Language Model)์ ์ด์ฉํ์ฌ ํ
์คํธ๋ฅผ ์์ฑํ ๋ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
- LLM์ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์๋ค์ด๋ ๋ฐฉ์์ด ๋งค์ฐ ์ ์ฐํ์ง๋ง, ์ฌ์ฉ์๊ฐ ์ํ๋ ๋๋ก ๊ฒฐ๊ณผ๋ฅผ ๋์ถํ๋ ค๋ฉด ์ผ๊ด๋๊ณ ๊ตฌ์กฐํ๋ ๋ฐฉ์์ผ๋ก ๋ชจ๋ธ์๊ฒ ์ ๋ณด๋ฅผ ์ ๊ณตํ ํ์๊ฐ ์์ต๋๋ค. ํ๋กฌํํธ ํ ํ๋ฆฟ์ ๋ฐ๋ก ์ด ๊ณผ์ ์ ๋๊ธฐ ์ํ ๋๊ตฌ์ ๋๋ค.
- ํ๋กฌํํธ ํ
ํ๋ฆฟ์ ํ
์คํธ ์์ฑ ๊ณผ์ ์ ์ฝ๊ฒ ๋ฐ๋ณต ๊ฐ๋ฅํ๊ฒ ํ๊ณ , ๊ฐ๊ธฐ ๋ค๋ฅธ ์
๋ ฅ์ ๋ฐ๋ผ ๋ณํํ ์ ์๋ ์ผ์ข
์ ํ
ํ๋ฆฟ์ ์ ๊ณตํฉ๋๋ค.
- ๋ฌธ์์ ์ง๋ฌธ์ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณ์ ์์ฑํ๋ ์์ ์ ํ ๋, ๋ชจ๋ธ์ ๋์ผํ ๊ตฌ์กฐ๋ก ์ ๋ณด(๋ฌธ์์ ์ง๋ฌธ)๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ํ๋กฌํํธ ํ
ํ๋ฆฟ์ ๋ฌธ์๋ฅผ {context}์, ์ง๋ฌธ์ {question}์ ์ฝ์
ํจ์ผ๋ก์จ ์ด ๊ตฌ์กฐ๋ฅผ ์ ์งํด ์ค๋๋ค.
1 2 3 4 5 6 7 8
# ์์ ํ๋กฌํํธ ํ ํ๋ Answer the question based only on the following context: {context} Question: {question} - ์ด์ , ์ค์ ๋ฐ์ดํฐ๋ฅผ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ์ฑ์ ๋ฃ๋ ์๊ฐ, Prompt Value๋ผ๋ ์ต์ข ํ๋กฌํํธ๊ฐ ๋ง๋ค์ด์ง๋๋ค. ์ด ํ๋กฌํํธ๋ ํ ์คํธ๋ก ๋ณํ๋์ด LLM์ ์ ๋ฌํ ์ค๋น๊ฐ ๋ ์ํ์ ๋๋ค.
- ์๋ฅผ ๋ค์ด, ํ
ํ๋ฆฟ์ ๊ฒ์๋ ๋ฌธ์์ ์ง๋ฌธ์ด ์ฝ์
๋๋ฉด ๋ค์๊ณผ ๊ฐ์ ํํ๋ก ๋ณํ๋ฉ๋๋ค.
1 2 3 4 5 6
Answer the question based only on the following context: "Document 1 content here. Document 2 content here..." Question: "What is Task Decomposition?"
- ํ๋กฌํํธ ํ
ํ๋ฆฟ(Prompt Template)์ LLM(Large Language Model)์ ์ด์ฉํ์ฌ ํ
์คํธ๋ฅผ ์์ฑํ ๋ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
Q. ์๋ ๊ทธ๋ฅ dict๋ก ์ฃผ๋ฉด ๋์ง ์๋? ์ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ์ฌ์ฉํ์ง?
A. ์ด ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ ์ฅ์ ์ด ์์ต๋๋ค:
- ์๋ํ: ์ฌ๋ฌ ๊ฐ์ ์ง๋ฌธ๊ณผ ์ปจํ ์คํธ์ ๋ํด ๋์ผํ ํ์์ ํ๋กฌํํธ๋ฅผ ์๋์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค.
- ์ฌ์ฌ์ฉ์ฑ: ๊ฐ์ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ๋ค์ํ ์ ๋ ฅ ๋ฐ์ดํฐ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ์ผ๊ด์ฑ: ์ผ์ ํ ํ์์ผ๋ก LLM์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ์ฌ, ๋ ์ผ๊ด๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
- LLM (Large Language Model)
- Prompt Value๊ฐ LLM์ ์ ๋ฌ๋ฉ๋๋ค. LLM์ ์ปจํ ์คํธ ์๋์ฐ(Context Window)๋ฅผ ์ฌ์ฉํด ์ ๋ฌ๋ ๋ฌธ์์ ์ง๋ฌธ์ ๋ฐํ์ผ๋ก ๋ต๋ณ์ ์์ฑํฉ๋๋ค.
- Parser
- LLM์์ ์์ฑ๋ ๋ต๋ณ์ Parser๋ฅผ ํตํด ์ฒ๋ฆฌ๋ฉ๋๋ค. Parser๋ LLM์์ ์์ฑ๋ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ์ ์ ํ ํ์ฑํ์ฌ ์ต์ข ์ ์ผ๋ก Answer๋ก ๋ฐํํฉ๋๋ค.
์ฝ๋ ์์ฐ
- ๋ฌธ์๋ฅผ ๋ก๋ํ๊ณ , ์ด๋ฅผ ๋ถํ ํ ํ, ์๋ฒ ๋ฉ์ ์ ์ฉํ์ฌ ๋ฒกํฐ๋ก ๋ณํํ๊ณ ๋ฒกํฐ ์คํ ์ด์ ์ ์ฅํ๋ ์์ ์ ๋ณด์ฌ์ค๋๋ค.
- ์์ฑ ๋ถ๋ถ์์๋ ๊ฐ๋จํ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ๋ง๋ค์ด, ๋ฌธ๋งฅ(context)๊ณผ ์ง๋ฌธ(question)์ ๋ฐํ์ผ๋ก ๋ต๋ณ์ ์์ฑํ๋ ๋ฐฉ์์ ์ค๋ช ํฉ๋๋ค.
- LangChain์์ ์ ๊ณตํ๋
LangChain Expression Language๋ฅผ ํ์ฉํ์ฌ ํ๋กฌํํธ, LLM, ํ์(parser), ๊ทธ๋ฆฌ๊ณ ๋ฆฌํธ๋ฆฌ๋ฒ(retriever)๋ฅผ ์ฝ๊ฒ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค.
(1) ํ๋กฌํํธ ํ ํ๋ฆฟ์ด LLM์ ๋ค์ด๊ฐ๋ ๊ณผ์
- ์๋ ์ฝ๋๋ฅผ ํตํด ํ๋กฌํํธ ํ
ํ๋ฆฟ์ LLM์ ์ ๋ฌ๋๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ก ๋ต๋ณ์ด ์์ฑํ ์ ์์ต๋๋ค.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate # ํ๋กฌํํธ ํ ํ๋ฆฟ ์ ์ template = """Answer the question based only on the following context: {context} Question: {question} """ # ํ ํ๋ฆฟ์ ChatPromptTemplate ๊ฐ์ฒด๋ก ๋ณํ prompt = ChatPromptTemplate.from_template(template) # LLM ์์ฑ llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) # ์ฒด์ธ(chain) ์์ฑ: ํ๋กฌํํธ์ LLM ์ฐ๊ฒฐ chain = prompt | llm # ์คํ: ์ปจํ ์คํธ์ ์ง๋ฌธ์ ํ ํ๋ฆฟ์ ์ฝ์ ํ์ฌ ๋ต๋ณ ์์ฑ # ์์์ ์ ์ธํด๋ docs์ Question์ Invokeํ๋ ํํ chain.invoke({"context": docs, "question": "What is Task Decomposition?"})- ํ๋กฌํํธ ํ ํ๋ฆฟ ์ ์: ๋จผ์ ํ ํ๋ฆฟ ๋ฌธ์์ด์ ์ ์ํฉ๋๋ค. ์ฌ๊ธฐ์ {context}๋ ๊ฒ์๋ ๋ฌธ์๊ฐ ์ฝ์ ๋๋ ์๋ฆฌ์ด๊ณ , {question}์ ์ง๋ฌธ์ด ์ฝ์ ๋๋ ์๋ฆฌ์ ๋๋ค. ์ด ํ ํ๋ฆฟ์ ๋ต๋ณ์ ์์ฑํ๋ ๋ฐ ํ์ํ ๋ชจ๋ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ํ ํ๋ฆฟ์ ๊ฐ์ฒด๋ก ๋ณํ: ChatPromptTemplate.from_template() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋ฌธ์์ด ํํ์ ํ ํ๋ฆฟ์ ๊ฐ์ฒด๋ก ๋ณํํฉ๋๋ค. ์ด ๊ฐ์ฒด๋ ๋์ค์ ๋ฐ์ดํฐ๊ฐ ์ฝ์ ๋ ์ ์๋ ์ค๋น๋ ํ ํ๋ฆฟ์ ๋๋ค.
- LLM ์ ์: ChatOpenAI ํด๋์ค์์ gpt-3.5-turbo๋ผ๋ ๋ชจ๋ธ์ ์ ํํ์ฌ LLM์ ์ ์ํฉ๋๋ค. ์ฌ๊ธฐ์ temperature=0์ ๋ชจ๋ธ์ด ๋์ฑ ์ผ๊ด๋(์ฆ, ๋ ๋๋คํ) ๋ต๋ณ์ ์์ฑํ๋๋ก ์ค์ ํ ๊ฒ์ ๋๋ค.
- ํ๋กฌํํธ์ LLM ์ฐ๊ฒฐ(Chain): ํ๋กฌํํธ ํ ํ๋ฆฟ๊ณผ LLM์ ์ฐ๊ฒฐํ์ฌ ์ฒด์ธ์ ๋ง๋ญ๋๋ค. ์ด ์ฒด์ธ์ ํ๋กฌํํธ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ๊ณ , LLM์ ํธ์ถํด ๋ต๋ณ์ ์์ฑํ๋ ์ ์ฒด ํ๋ก์ธ์ค๋ฅผ ํ๋์ ํ๋ฆ์ผ๋ก ๋ฌถ์ต๋๋ค.
- ์คํ: chain.invoke() ๋ฉ์๋๋ฅผ ํตํด {context}์๋ ๋ฌธ์(docs)๊ฐ, {question}์๋ ์ง๋ฌธ์ด ์ฝ์ ๋ ํ๋กฌํํธ๊ฐ LLM์ ์ ๋ฌ๋ฉ๋๋ค. ๋ชจ๋ธ์ ์ด ํ๋กฌํํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณ์ ์์ฑํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค.
(2) LangChain๊ณผ ํ๋กฌํํธ ํ ํ๋ฆฟ
- LangChain์์๋ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ๋ณด๋ค ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ ๋ค์ํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์๋ฅผ ๋ค์ด, ํ
ํ๋ฆฟ๊ณผ LLM, ๊ทธ๋ฆฌ๊ณ ํ์๋ฅผ ์ฐ๊ฒฐํ์ฌ ํ๋์ ํ์ดํ๋ผ์ธ์ ๋ง๋ค๊ณ , ์ด๋ฅผ ํตํด ๊ฒ์ ๊ธฐ๋ฐ ๋ต๋ณ ์์ฑ(RAG)์ ์ํํ ์ ์์ต๋๋ค.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
from langchain import hub from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough prompt_hub_rag = hub.pull("rlm/rag-prompt") # RAG ์ฒด์ธ ์์ฑ rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt_hub_rag # ํธ์ถํ template ์ฌ์ฉ | llm | StrOutputParser() ) # ์ง๋ฌธ์ ๋ํ ๋ต๋ณ ์์ฑ rag_chain.invoke("What is Task Decomposition?") - ์ ๋๋ก ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๊ณ , ์คํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค:


