[κ°•μ˜λ…ΈνŠΈ] RAG From Scratch : Overview

Posted by Euisuk's Dev Log on September 14, 2024

[κ°•μ˜λ…ΈνŠΈ] 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의 μ»¨ν…μŠ€νŠΈ μœˆλ„μš°μ— μ‚½μž…λ˜μ–΄ λ‹΅λ³€ 생성에 μ‚¬μš©λ©λ‹ˆλ‹€.

μœ„μ—μ„œ λ³΄μ—¬λ“œλ¦° 그림을 μ’€ 더 μžμ„Έν•˜κ²Œ μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€:

  1. Documents β†’ Embedding β†’ Vectorstore
    • Documents: 이 뢀뢄은 μš°λ¦¬κ°€ μ²˜λ¦¬ν•˜κ³ μž ν•˜λŠ” 원본 λ¬Έμ„œμž…λ‹ˆλ‹€. λ‹€μ–‘ν•œ ν…μŠ€νŠΈ 데이터가 ν¬ν•¨λ˜μ–΄ μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.
    • Embedding: λ¬Έμ„œλ₯Ό μ§μ ‘μ μœΌλ‘œ κ²€μƒ‰ν•˜κΈ° μ–΄λ €μš°λ―€λ‘œ, 이 λ¬Έμ„œλ“€μ„ μž„λ² λ”©(embedding)ν•˜μ—¬ 고차원 λ²‘ν„°λ‘œ λ³€ν™˜ν•©λ‹ˆλ‹€. μž„λ² λ”©λœ λ²‘ν„°λŠ” λ¬Έμ„œμ˜ 의미적 νŠΉμ§•μ„ ν¬ν•¨ν•˜κ³  있으며, 이λ₯Ό λ°”νƒ•μœΌλ‘œ μœ μ‚¬λ„λ₯Ό 계산할 수 μžˆμŠ΅λ‹ˆλ‹€.
    • Vectorstore: λ²‘ν„°λ‘œ λ³€ν™˜λœ λ¬Έμ„œλ“€μ€ 벑터 μŠ€ν† μ–΄(vectorstore)에 μ €μž₯λ©λ‹ˆλ‹€. 벑터 μŠ€ν† μ–΄λŠ” λ‚˜μ€‘μ— 질문과 μœ μ‚¬ν•œ λ¬Έμ„œλ₯Ό 효율적으둜 κ²€μƒ‰ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ©λ‹ˆλ‹€. (Ex. KNN, HNSW 기법 등을 기반으둜 검색을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.)
      • HNSW(Hierarchical Navigable Small World): 고차원 벑터 λ°μ΄ν„°μ˜ 근사 μ΅œκ·Όμ ‘ 이웃(Approximate Nearest Neighbor) 검색을 μœ„ν•œ 효율적인 μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€.
  2. Question β†’ Embedding β†’ Vectorstore β†’ Relevant Documents
    • Question: μ‚¬μš©μžκ°€ μ§ˆλ¬Έμ„ λ˜μ§€λ©΄, 이 μ§ˆλ¬Έλ„ λ¬Έμ„œμ™€ λ™μΌν•œ λ°©μ‹μœΌλ‘œ μž„λ² λ”©λ©λ‹ˆλ‹€. 즉, μ§ˆλ¬Έλ„ λ²‘ν„°λ‘œ λ³€ν™˜λ©λ‹ˆλ‹€.
    • Vectorstore β†’ Relevant Documents: λ³€ν™˜λœ 질문 λ²‘ν„°λŠ” 벑터 μŠ€ν† μ–΄μ—μ„œ λ¬Έμ„œ 벑터듀과 λΉ„κ΅λ˜μ–΄ κ°€μž₯ μœ μ‚¬ν•œ λ¬Έμ„œλ“€μ΄ κ²€μƒ‰λ©λ‹ˆλ‹€.
      • 이 λ¬Έμ„œλ“€μ΄ Relevant Documents둜 ν‘œμ‹œλ©λ‹ˆλ‹€. μ΄λŠ” 질문과 의미적으둜 κ°€μž₯ κ°€κΉŒμš΄ λ¬Έμ„œλ“€μ΄λ©°, LLM의 μ»¨ν…μŠ€νŠΈλ‘œ μ‚¬μš©λ©λ‹ˆλ‹€.
  3. Dict 생성
    • κ²€μƒ‰λœ λ¬Έμ„œμ™€ μ§ˆλ¬Έμ€ DictλΌλŠ” 자료 ꡬ쑰둜 λ³€ν™˜λ©λ‹ˆλ‹€.
    • 이 DictλŠ” 두 κ°€μ§€ ν•„λ“œλ₯Ό ν¬ν•¨ν•˜κ³  μžˆλŠ”λ°:
      • {Context}: κ²€μƒ‰λœ λ¬Έμ„œκ°€ λ“€μ–΄κ°‘λ‹ˆλ‹€.
      • {Question}: μ‚¬μš©μžμ˜ 질문이 λ“€μ–΄κ°‘λ‹ˆλ‹€.
    • 이 DictλŠ” Prompt Template에 λ“€μ–΄κ°ˆ 데이터λ₯Ό μ •μ˜ν•˜λŠ” μ€‘μš”ν•œ 역할을 ν•©λ‹ˆλ‹€.
  4. 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?"
               
        

Q. μ•„λ‹ˆ κ·Έλƒ₯ dict둜 μ£Όλ©΄ λ˜μ§€ μ•Šλ‚˜? μ™œ ν”„λ‘¬ν”„νŠΈ ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•˜μ§€?

A. 이 방식은 λ‹€μŒκ³Ό 같은 μž₯점이 μžˆμŠ΅λ‹ˆλ‹€:

  • μžλ™ν™”: μ—¬λŸ¬ 개의 질문과 μ»¨ν…μŠ€νŠΈμ— λŒ€ν•΄ λ™μΌν•œ ν˜•μ‹μ˜ ν”„λ‘¬ν”„νŠΈλ₯Ό μžλ™μœΌλ‘œ 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μž¬μ‚¬μš©μ„±: 같은 ν”„λ‘¬ν”„νŠΈ ν…œν”Œλ¦Ώμ„ λ‹€μ–‘ν•œ μž…λ ₯ 데이터에 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 일관성: μΌμ •ν•œ ν˜•μ‹μœΌλ‘œ LLM에 데이터λ₯Ό μ „λ‹¬ν•˜μ—¬, 더 μΌκ΄€λœ κ²°κ³Όλ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.
  1. LLM (Large Language Model)
    • Prompt Valueκ°€ LLM에 μ „λ‹¬λ©λ‹ˆλ‹€. LLM은 μ»¨ν…μŠ€νŠΈ μœˆλ„μš°(Context Window)λ₯Ό μ‚¬μš©ν•΄ μ „λ‹¬λœ λ¬Έμ„œμ™€ μ§ˆλ¬Έμ„ λ°”νƒ•μœΌλ‘œ 닡변을 μƒμ„±ν•©λ‹ˆλ‹€.
  2. 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?")
      
    
  • μ œλŒ€λ‘œ κ²°κ³Όκ°€ 좜λ ₯되고, μ‹€ν–‰λœ 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€:




-->