[๊ฐ•์˜๋…ธํŠธ] RAG From Scratch : Query Routing & Structuring

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

[๊ฐ•์˜๋…ธํŠธ] RAG From Scratch : Query Routing & Structuring

์›๋ณธ ๊ฒŒ์‹œ๊ธ€: https://velog.io/@euisuk-chung/RAG-From-Scratch-10-11

  • ํ•ด๋‹น ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋Š” RAG From Scratch : Coursework ๊ฐ•์˜ ํŒŒํŠธ 10 - 11 ๋‚ด์šฉ์„ ๋‹ค๋ฃจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๋น„๋””์˜ค ์š”์•ฝ ๊ฐ•์˜ ๋งํฌ ์Šฌ๋ผ์ด๋“œ
Part 10 (๋ผ์šฐํŒ…) ์ฟผ๋ฆฌ๋ฅผ ๊ด€๋ จ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ์œ ๋„ํ•˜๊ธฐ ์œ„ํ•œ ๋…ผ๋ฆฌ์  ๋ฐ ์˜๋ฏธ์  ์ฟผ๋ฆฌ ๋ผ์šฐํŒ…์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๐Ÿ“Œ ๊ฐ•์˜ ๐Ÿ“– ์Šฌ๋ผ์ด๋“œ
Part 11 (์ฟผ๋ฆฌ ๊ตฌ์กฐํ™”) ์ž์—ฐ์–ด ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํ˜ธ์ž‘์šฉ์„ ํšจ์œจํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๐Ÿ“Œ ๊ฐ•์˜ ๐Ÿ“– ์ฐธ๊ณ ์ž๋ฃŒ

Part 10 (๋ผ์šฐํŒ…)

  • ์œ„ ๊ทธ๋ฆผ์˜ ๋‹จ๊ณ„์— ๋”ฐ๋ผ ๊ฐ ๊ณผ์ •์˜ ์—ญํ• ๊ณผ ๊ฐœ๋…์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:

    1. Question (์งˆ๋ฌธ) : ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ์Šคํ…œ์— ์ž…๋ ฅํ•˜๋Š” ์ž์—ฐ์–ด ํ˜•ํƒœ์˜ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ „์ฒด ํ”„๋กœ์„ธ์Šค์˜ ์‹œ์ž‘์ ์ด ๋ฉ๋‹ˆ๋‹ค.
    2. Query Translation (์ฟผ๋ฆฌ ๋ฒˆ์—ญ) : ์‚ฌ์šฉ์ž์˜ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ์‹œ์Šคํ…œ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ž์—ฐ์–ด ์ฒ˜๋ฆฌ ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
    3. Routing (๋ผ์šฐํŒ…, ์ด๋ฒˆ ์ฑ•ํ„ฐ๐Ÿ“Œ) : ๋ณ€ํ™˜๋œ ์ฟผ๋ฆฌ๋ฅผ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ๋‚˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ์•ˆ๋‚ดํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์งˆ๋ฌธ์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ ์ตœ์ ์˜ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    4. Query Construction (์ฟผ๋ฆฌ ๊ตฌ์„ฑ) : ๋ผ์šฐํŒ…๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๊ฒ€์ƒ‰ ์—”์ง„์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ์˜ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    5. Indexing (์ธ๋ฑ์‹ฑ) : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋ฌธ์„œ ์ปฌ๋ ‰์…˜์—์„œ ํšจ์œจ์ ์ธ ๊ฒ€์ƒ‰์„ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์กฐํ™”ํ•˜๊ณ  ์กฐ์งํ™”ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ฃผ๋กœ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋‹จ๊ณ„์—์„œ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
    6. Retrieval (๊ฒ€์ƒ‰) : ๊ตฌ์„ฑ๋œ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ๋ฑ์‹ฑ๋œ ๋ฐ์ดํ„ฐ์—์„œ ๊ด€๋ จ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ ์งˆ๋ฌธ๊ณผ ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ ๋†’์€ ์ •๋ณด๋ฅผ ์ฐพ์•„๋ƒ…๋‹ˆ๋‹ค.
    7. Generation (์ƒ์„ฑ) : ๊ฒ€์ƒ‰๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์ฃผ๋กœ ์ž์—ฐ์–ด ์ƒ์„ฑ ๊ธฐ์ˆ ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    8. Answer (๋‹ต๋ณ€) : ์ตœ์ข…์ ์œผ๋กœ ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์›๋ž˜ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ, ์ž์—ฐ์–ด ํ˜•ํƒœ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.
  • ์ด ๊ณผ์ •์€ ์งˆ๋ฌธ ์‘๋‹ต ์‹œ์Šคํ…œ์˜ ์ „ํ˜•์ ์ธ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ๊ฐ ๋‹จ๊ณ„๋Š” ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์— ๋Œ€ํ•ด ์ •ํ™•ํ•˜๊ณ  ๊ด€๋ จ์„ฑ ๋†’์€ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์œ ๊ธฐ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฒˆ ๊ฐ•์˜๋Š” 3. Routing(๋ผ์šฐํŒ…)์ด๋ผ๋Š” ๊ฐœ๋…์„ ์„ค๋ช…ํ•˜๊ณ , ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ๋ผ์šฐํŒ… ๋ฐฉ๋ฒ•์ธ โ‘  ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ…(Logical Routing)๊ณผ โ‘ก ์˜๋ฏธ์  ๋ผ์šฐํŒ…(Semantic Routing)์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

1. ๋ผ์šฐํŒ… ๊ฐœ๋…

  • ๋ผ์šฐํŒ…(Routing)์€ ์งˆ๋ฌธ์„ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ์ „๋‹ฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

    • RAG(์งˆ๋ฌธ-๋‹ต๋ณ€ ์ƒ์„ฑ ๋ชจ๋ธ)์—์„œ ๋ผ์šฐํŒ…์€ ํŠน์ • ์งˆ๋ฌธ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ํ”„๋กฌํ”„ํŠธ์— ์—ฐ๊ฒฐํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅด๊ฒŒ ์ •์˜ํ•ด๋ณธ๋‹ค๋ฉด, ๋ผ์šฐํŒ…์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์˜ํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    • ์ •์˜: ์ž…๋ ฅ๋œ ์ฟผ๋ฆฌ๋‚˜ ์ž‘์—…์„ ์ ์ ˆํ•œ ๋ชจ๋ธ์ด๋‚˜ ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ๋กœ ์•ˆ๋‚ดํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.
    • ๋ชฉ์ : ์ฃผ์–ด์ง„ ์ž…๋ ฅ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ LLM์ด๋‚˜ ์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
    • ์ž‘๋™ ๋ฐฉ์‹: ์ž…๋ ฅ์˜ ํŠน์„ฑ์„ ๋ถ„์„ํ•˜์—ฌ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๊ทœ์น™์ด๋‚˜ ํ•™์Šต๋œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ†ตํ•ด ์ตœ์ ์˜ ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    • ์ ์šฉ: ๋‹ค์–‘ํ•œ LLM์„ ํšจ์œจ์ ์œผ๋กœ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๋„๋ฉ”์ธ์— ํŠนํ™”๋œ ๋ชจ๋ธ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์‹œ๋‚˜๋ฆฌ์˜ค: ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ”๋”ฉ ๊ด€๋ จ ์งˆ๋ฌธ์„ ํ•  ๋•Œ, ์‹œ์Šคํ…œ์€ ๊ทธ ์งˆ๋ฌธ์„ ์ ์ ˆํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด ๋ฌธ์„œ(์˜ˆ: Python, JS, Golang)๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ๋ผ์šฐํŒ… ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ…(Logical Routing)๊ณผ ์˜๋ฏธ์  ๋ผ์šฐํŒ…(Semantic Routing)์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‘ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋˜๊ณ  ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค:

2.1. ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ… (Logical Routing) ์„ค๋ช…

๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ… ๊ฐœ๋…

  • ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ…์€ ์‹œ์Šคํ…œ์ด ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ค‘ ์–ด๋–ค ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ• ์ง€ ๋ฏธ๋ฆฌ ์„ค์ •๋œ ๊ทœ์น™์— ๋”ฐ๋ผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜๋ฉด LLM(Large Language Model)์€ ๊ทธ ์งˆ๋ฌธ์ด ์–ด๋А ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋ฌธ์„œ ์†Œ์Šค์™€ ๊ฐ€์žฅ ๊ด€๋ จ์ด ์žˆ๋Š”์ง€ ๊ฒฐ์ •ํ•˜๊ณ , ํ•ด๋‹น ์†Œ์Šค๋กœ ๋ผ์šฐํŒ…ํ•˜์—ฌ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํŠน์ง•

  • ์ฃผ๋กœ โ€œ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅโ€์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์šฐํŒ…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์‹œ์Šคํ…œ์€ ์งˆ๋ฌธ์„ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๊ทœ์น™์— ๋”ฐ๋ผ ๋ถ„๋ฅ˜ํ•˜๊ณ  ๊ทธ์— ๋งž๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  • ์งˆ๋ฌธ์ด ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ ๊ฐ€๋Šฅํ•œ ์ฃผ์ œ๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๊ด€๋ จ์ด ์žˆ์„ ๋•Œ ๋งค์šฐ ์ ํ•ฉํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

๊ณผ์ •

  1. ์‚ฌ์šฉ์ž๊ฐ€ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

  2. LLM์ด ๊ทธ ์งˆ๋ฌธ์„ ๋ถ„์„ํ•˜์—ฌ ๋ฏธ๋ฆฌ ์„ค์ •๋œ ๋ฐ์ดํ„ฐ ์†Œ์Šค ๋ชฉ๋ก(์˜ˆ: Python ๋ฌธ์„œ, JS ๋ฌธ์„œ ๋“ฑ) ์ค‘์—์„œ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์†Œ์Šค๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

  3. ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์„ค๋ช…

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
28
from typing import Literal
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

# Data model
class RouteQuery(BaseModel):
    """Route a user query to the most relevant datasource."""
    datasource: Literal["python_docs", "js_docs", "golang_docs"] = Field(
        ..., description="Given a user question choose which datasource would be most relevant for answering their question",
    )

# LLM with function call 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm = llm.with_structured_output(RouteQuery)

# Prompt ์ •์˜
system = """
You are an expert at routing a user question to the appropriate data source.

Based on the programming language the question is referring to, route it to the relevant data source.
"""

prompt = ChatPromptTemplate.from_messages(
    [("system", system), ("human", "{question}")])

# Define router 
router = prompt | structured_llm
  • RouteQuery๋Š” ์‚ฌ์šฉ์ž ์งˆ๋ฌธ์„ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ๋ผ์šฐํŒ…ํ•˜๊ธฐ ์œ„ํ•ด ๋ฏธ๋ฆฌ ์ •์˜๋œ datasource ์˜ต์…˜์„ ํฌํ•จํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
    • Field(...)์—์„œ โ€ฆ์€ Python์˜ pydantic ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํŠน๋ณ„ํ•œ ํ‘œํ˜„์œผ๋กœ, ํ•„๋“œ๋ฅผ ํ•„์ˆ˜ ํ•„๋“œ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
    • ์œ„ ์ฝ”๋“œ์—์„œ datasource ํ•„๋“œ๋Š” ํ•„์ˆ˜๋กœ ์ œ๊ณต๋˜์–ด์•ผ ํ•˜๋ฉฐ, ๊ฐ’์ด ์—†๋‹ค๋ฉด ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • structured_llm์€ LLM์ด ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ ํ˜•์‹์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •๋œ LLM์ž…๋‹ˆ๋‹ค.
  • prompt๋Š” ์‹œ์Šคํ…œ์ด ์งˆ๋ฌธ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•˜๋Š” ํ…œํ”Œ๋ฆฟ์ž…๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
# ์งˆ๋ฌธ ์˜ˆ์‹œ
question = """
Why doesn't the following code work:

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages(["human", "speak in {language}"])
prompt.invoke("french")

"""

# LLM์ด ์งˆ๋ฌธ์„ ๋ถ„์„ํ•˜์—ฌ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ฐ˜ํ™˜
result = router.invoke({"question": question})
  • result์—๋Š” RouteQuery(datasource=โ€™python_docsโ€™)๊ฐ’์ด ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž๋Š” Python ์ฝ”๋“œ์™€ ๊ด€๋ จ๋œ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. (question)
    • LLM์€ ๋ฏธ๋ฆฌ ์„ค์ •๋œ ์ง€์นจ์— ๋”ฐ๋ผ ์งˆ๋ฌธ์„ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. (router.invoke({โ€œquestionโ€: question}))
    • LLM์€ ์งˆ๋ฌธ์˜ ๋งฅ๋ฝ์ด Python๊ณผ ๊ด€๋ จ๋˜์–ด ์žˆ์Œ์„ ๊ฐ์ง€ํ•˜๊ณ , (router = prompt structured_llm)
    • RouteQuery(datasource=โ€™python_docsโ€™)๋ผ๋Š” ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ์„ ์ƒ์„ฑํ•˜์—ฌ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
# ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๋”ฐ๋ผ ์ถ”๊ฐ€์ ์ธ ์ฒ˜๋ฆฌ ์ˆ˜ํ–‰
def choose_route(result):
    if "python_docs" in result.datasource.lower():
        return "chain for python_docs"
    elif "js_docs" in result.datasource.lower():
        return "chain for js_docs"
    else:
        return "chain for golang_docs"

# ์ตœ์ข…์ ์œผ๋กœ ์„ ํƒ๋œ ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ๋ผ์šฐํŒ…
full_chain = router | RunnableLambda(choose_route)

full_chain.invoke({"question": question})  # 'chain for python_docs'
  • choose_route() ํ•จ์ˆ˜๋Š” ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด ์˜ˆ์‹œ์—์„œ ์‚ฌ์šฉ์ž๋Š” ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜๊ณ , ์‹œ์Šคํ…œ์€ ์ด๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Python๊ณผ ๊ด€๋ จ๋œ ์งˆ๋ฌธ์ด๋ผ๋ฉด python_docs๋กœ ๋ผ์šฐํŒ…๋ฉ๋‹ˆ๋‹ค.

2.2. ์˜๋ฏธ์  ๋ผ์šฐํŒ… (Semantic Routing) ์„ค๋ช…

์˜๋ฏธ์  ๋ผ์šฐํŒ… ๊ฐœ๋…

  • ์˜๋ฏธ์  ๋ผ์šฐํŒ…์€ ์งˆ๋ฌธ๊ณผ ์—ฌ๋Ÿฌ ํ”„๋กฌํ”„ํŠธ ๊ฐ„์˜ ์˜๋ฏธ์  ์œ ์‚ฌ์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ํ•ฉํ•œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • ์—ฌ๊ธฐ์„œ ์งˆ๋ฌธ๊ณผ ํ”„๋กฌํ”„ํŠธ๋Š” ์ž„๋ฒ ๋”ฉ(๋ฒกํ„ฐํ™”)๋œ ํ›„ ์œ ์‚ฌ๋„๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ, ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

ํŠน์ง•

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

๊ณผ์ •

  1. ์‚ฌ์šฉ์ž๊ฐ€ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜๋ฉด ์‹œ์Šคํ…œ์ด ๊ทธ ์งˆ๋ฌธ์„ ์ž„๋ฒ ๋”ฉ(๋ฒกํ„ฐํ™”)ํ•ฉ๋‹ˆ๋‹ค.

  2. ์‹œ์Šคํ…œ์€ ์—ฌ๋Ÿฌ ํ”„๋กฌํ”„ํŠธ ์ค‘์—์„œ ์งˆ๋ฌธ๊ณผ ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

  3. ์„ ํƒ๋œ ํ”„๋กฌํ”„ํŠธ์— ๋”ฐ๋ผ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์„ค๋ช…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from langchain.utils.math import cosine_similarity
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# ๋‘ ๊ฐ€์ง€ ํ”„๋กฌํ”„ํŠธ ์ •์˜
physics_template = """You are a very smart physics professor. \
You are great at answering questions about physics in a concise and easy to understand manner. \
When you don't know the answer to a question you admit that you don't know.

Here is a question:
{query}"""

math_template = """You are a very good mathematician. You are great at answering math questions. \
You are so good because you are able to break down hard problems into their component parts, \
answer the component parts, and then put them together to answer the broader question.
  • ๋ฌผ๋ฆฌํ•™ ํ”„๋กฌํ”„ํŠธ์™€ ์ˆ˜ํ•™ ํ”„๋กฌํ”„ํŠธ ๋‘ ๊ฐ€์ง€๊ฐ€ ์ฃผ์–ด์ง€๋ฉฐ, ์‹œ์Šคํ…œ์ด ์งˆ๋ฌธ์„ ๋ถ„์„ํ•œ ํ›„ ์ ํ•ฉํ•œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
Here is a question:
{query}"""

# ํ”„๋กฌํ”„ํŠธ ์ž„๋ฒ ๋”ฉ
embeddings = OpenAIEmbeddings()
prompt_templates = [physics_template, math_template]
prompt_embeddings = embeddings.embed_documents(prompt_templates)
  • OpenAIEmbeddings()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ๊ณผ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž„๋ฒ ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
  • cosine_similarity() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ๊ณผ ํ”„๋กฌํ”„ํŠธ ๊ฐ„์˜ ์œ ์‚ฌ๋„๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ์งˆ๋ฌธ์„ ์ ํ•ฉํ•œ ํ”„๋กฌํ”„ํŠธ๋กœ ๋ผ์šฐํŒ…
def prompt_router(input):
    query_embedding = embeddings.embed_query(input["query"])
    similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
    most_similar = prompt_templates[similarity.argmax()]
    print("Using MATH" if most_similar == math_template else "Using PHYSICS")
    return PromptTemplate.from_template(most_similar)

chain = (
    {"query": RunnablePassthrough()}
    | RunnableLambda(prompt_router)
    | ChatOpenAI()
    | StrOutputParser()
)

print(chain.invoke("What's a black hole"))
  • ์‹œ์Šคํ…œ์€ ์„ ํƒ๋œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ์งˆ๋ฌธ์ด โ€œ๋ธ”๋ž™ํ™€์€ ๋ฌด์—‡์ธ๊ฐ€?โ€๋ผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฌผ๋ฆฌํ•™ ๊ด€๋ จ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์„ ํƒ๋˜๊ณ  ๋‹ต๋ณ€์„ ์ œ๊ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
Using PHYSICS
   
A black hole is a region in space where the gravitational pull is so strong that nothing, not even light, can escape from it. This occurs because a significant amount of mass has been compressed into a very small area. 

The boundary around a black hole is called the event horizon, and once something crosses this boundary, it cannot escape. Black holes can be formed from the remnants of massive stars that have ended their life cycles in a supernova explosion and collapsed under their own gravity. 

They are fascinating objects that challenge our understanding of physics, particularly in the realms of general relativity and quantum mechanics.

(์ •๋ฆฌ) ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ… vs ์˜๋ฏธ์  ๋ผ์šฐํŒ… ๋น„๊ต

๊ตฌ๋ถ„ ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ… (Logical Routing) ์˜๋ฏธ์  ๋ผ์šฐํŒ… (Semantic Routing)
๋ผ์šฐํŒ… ๊ธฐ์ค€ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋ฐ์ดํ„ฐ ์†Œ์Šค ๋ชฉ๋ก์— ๊ธฐ๋ฐ˜ ์งˆ๋ฌธ๊ณผ ํ”„๋กฌํ”„ํŠธ ๊ฐ„์˜ ์˜๋ฏธ์  ์œ ์‚ฌ์„ฑ์— ๊ธฐ๋ฐ˜
๋ฐ์ดํ„ฐ ์†Œ์Šค ์„ ํƒ LLM์ด ๋ฐ์ดํ„ฐ ์†Œ์Šค ๋ชฉ๋ก์—์„œ ์ ํ•ฉํ•œ ์†Œ์Šค๋ฅผ ์„ ํƒ ์งˆ๋ฌธ์˜ ์˜๋ฏธ๋ฅผ ๋ถ„์„ํ•ด ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ํ”„๋กฌํ”„ํŠธ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ํƒ
์ฃผ์š” ํ™œ์šฉ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋‚˜ ์ž๋ฃŒ๊ฐ€ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ ๋‹ค์–‘ํ•œ ์˜๋ฏธ๋ฅผ ํฌํ•จํ•˜๋Š” ์งˆ๋ฌธ์— ๋Œ€ํ•ด ์œ ์—ฐํ•œ ๋‹ต๋ณ€ ์ œ๊ณต
์‹ค์ œ ์˜ˆ์‹œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฌธ์„œ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฌธ์„œ ๋“ฑ ํŠน์ • ์ฃผ์ œ์˜ DB ํŠน์ • ๋„๋ฉ”์ธ์— ๊ด€๋ จ๋œ ์—ฌ๋Ÿฌ ์˜๋ฏธ๊ฐ€ ํ˜ผ์žฌ๋œ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€ ์„ ํƒ
์ ํ•ฉํ•œ ์ƒํ™ฉ ์งˆ๋ฌธ์ด ์–ด๋А DB์—์„œ ์ฒ˜๋ฆฌ๋ ์ง€ ๋ฏธ๋ฆฌ ๋ช…ํ™•ํ•œ ๊ฒฝ์šฐ ์งˆ๋ฌธ์ด ๋‹ค์–‘ํ•œ ์˜๋ฏธ๋กœ ํ•ด์„๋  ์ˆ˜ ์žˆ์„ ๋•Œ ์ ํ•ฉ
  • ์œ„ ํ…Œ์ด๋ธ” ๋น„๊ต๋ฅผ ํ†ตํ•ด,
    • ๋…ผ๋ฆฌ์  ๋ผ์šฐํŒ…์€ ๋ฏธ๋ฆฌ ์„ค์ •๋œ ๊ทœ์น™์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฐ˜๋ฉด,
    • ์˜๋ฏธ์  ๋ผ์šฐํŒ…์€ ์งˆ๋ฌธ์˜ ์˜๋ฏธ์  ์œ ์‚ฌ์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ผ์šฐํŒ…์ด ์ด๋ฃจ์–ด์ง„๋‹ค๋Š” ์ฐจ์ด์ ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Part 11 (์ฟผ๋ฆฌ ๊ตฌ์กฐํ™”)

  • ์œ„ ๊ทธ๋ฆผ์˜ ๋‹จ๊ณ„์— ๋”ฐ๋ผ ๊ฐ ๊ณผ์ •์˜ ์—ญํ• ๊ณผ ๊ฐœ๋…์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:

    1. Question (์งˆ๋ฌธ) : ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ์Šคํ…œ์— ์ž…๋ ฅํ•˜๋Š” ์ž์—ฐ์–ด ํ˜•ํƒœ์˜ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ „์ฒด ํ”„๋กœ์„ธ์Šค์˜ ์‹œ์ž‘์ ์ด ๋ฉ๋‹ˆ๋‹ค.
    2. Query Translation (์ฟผ๋ฆฌ ๋ฒˆ์—ญ) : ์‚ฌ์šฉ์ž์˜ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ์‹œ์Šคํ…œ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ž์—ฐ์–ด ์ฒ˜๋ฆฌ ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
    3. Routing (๋ผ์šฐํŒ…) : ๋ณ€ํ™˜๋œ ์ฟผ๋ฆฌ๋ฅผ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ๋‚˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ์•ˆ๋‚ดํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์งˆ๋ฌธ์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ ์ตœ์ ์˜ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    4. Query Construction (์ฟผ๋ฆฌ ๊ตฌ์„ฑ, ์ด๋ฒˆ ์ฑ•ํ„ฐ๐Ÿ“Œ) : ๋ผ์šฐํŒ…๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๊ฒ€์ƒ‰ ์—”์ง„์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ์˜ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    5. Indexing (์ธ๋ฑ์‹ฑ) : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋ฌธ์„œ ์ปฌ๋ ‰์…˜์—์„œ ํšจ์œจ์ ์ธ ๊ฒ€์ƒ‰์„ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์กฐํ™”ํ•˜๊ณ  ์กฐ์งํ™”ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ฃผ๋กœ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋‹จ๊ณ„์—์„œ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
    6. Retrieval (๊ฒ€์ƒ‰) : ๊ตฌ์„ฑ๋œ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ๋ฑ์‹ฑ๋œ ๋ฐ์ดํ„ฐ์—์„œ ๊ด€๋ จ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ ์งˆ๋ฌธ๊ณผ ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ ๋†’์€ ์ •๋ณด๋ฅผ ์ฐพ์•„๋ƒ…๋‹ˆ๋‹ค.
    7. Generation (์ƒ์„ฑ) : ๊ฒ€์ƒ‰๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์ฃผ๋กœ ์ž์—ฐ์–ด ์ƒ์„ฑ ๊ธฐ์ˆ ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    8. Answer (๋‹ต๋ณ€) : ์ตœ์ข…์ ์œผ๋กœ ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์›๋ž˜ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ, ์ž์—ฐ์–ด ํ˜•ํƒœ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฒˆ ๊ฐ•์˜์—์„œ๋Š” Query Construction (or Structuring)(์ฟผ๋ฆฌ ๊ตฌ์กฐํ™”)์— ๋Œ€ํ•ด ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ๊ตฌ์กฐํ™”๋Š” ์ž์—ฐ์–ด๋กœ ๋œ ์งˆ๋ฌธ์„ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋„๋ฉ”์ธ์— ๋งž๋Š” ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋ฒกํ„ฐ ์Šคํ† ์–ด์—์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ์˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.

1. ์ฟผ๋ฆฌ ๊ตฌ์กฐํ™” ๊ฐœ๋…

  • ์ฟผ๋ฆฌ ๊ตฌ์กฐํ™”๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž์—ฐ์–ด๋กœ ๋ฌป๋Š” ์งˆ๋ฌธ์„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด๋‹ค ๊ตฌ์ฒด์ ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ โ€œ2024๋…„ ์ดํ›„์— ๊ฒŒ์‹œ๋œ Lang Chain ๊ด€๋ จ ๋น„๋””์˜ค๋ฅผ ์ฐพ์•„์ฃผ์„ธ์š”โ€๋ผ๊ณ  ์งˆ๋ฌธํ•  ๊ฒฝ์šฐ, ์ด ์งˆ๋ฌธ์€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋กœ ๋ณ€ํ™˜๋˜์–ด ๋ฒกํ„ฐ ์Šคํ† ์–ด์— ์ ํ•ฉํ•œ ์ฟผ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.

2. ์ฟผ๋ฆฌ ๊ตฌ์กฐํ™”์˜ ์ฃผ์š” ํ๋ฆ„

2.1. ์ž์—ฐ์–ด ์งˆ๋ฌธ โ†’ ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ:

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ž์—ฐ์–ด๋กœ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜๋ฉด, LLM์ด ํ•ด๋‹น ์งˆ๋ฌธ์„ ๋ถ„์„ํ•˜์—ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ(์˜ˆ: ๋‚ ์งœ, ์กฐํšŒ์ˆ˜, ๋น„๋””์˜ค ๊ธธ์ด)๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฒ€์ƒ‰์— ์ ํ•ฉํ•œ ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    • ์˜ˆ์‹œ: ์œ ํŠœ๋ธŒ ๋น„๋””์˜ค ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ณ  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ ์ ์šฉ
    • ๋งํฌ : Self-reflective RAG with LangGraph: Self-RAG and CRAG

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
      from langchain_community.document_loaders import YoutubeLoader
      # https://youtu.be/pbAd8O1Lvm4
        
      docs = YoutubeLoader.from_youtube_url(
      "https://www.youtube.com/watch?v=pbAd8O1Lvm4",
      add_video_info=True
      ).load()
        
      docs[0].metadata
    
    • ์œ„ ์ฝ”๋“œ๋Š” YouTube์—์„œ ๋น„๋””์˜ค๋ฅผ ๋กœ๋“œํ•˜๊ณ  ํ•ด๋‹น ๋น„๋””์˜ค์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์˜ˆ: ์ œ๋ชฉ, ์กฐํšŒ์ˆ˜, ๊ฒŒ์‹œ์ผ)๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

2.2. ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ ์‚ฌ์šฉ:

  • ๋ฒกํ„ฐ ์Šคํ† ์–ด์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ํ•„ํ„ฐ๋กœ๋Š” ์กฐํšŒ์ˆ˜, ๊ฒŒ์‹œ์ผ, ๋น„๋””์˜ค ๊ธธ์ด ๋“ฑ์ด ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๋ณด๋‹ค ์„ธ๋ฐ€ํ•œ ๊ฒ€์ƒ‰์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (view_count, publish_date, length ๋“ฑ์„ ํ•„ํ„ฐ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

    • ์•„๋ž˜๋Š” ์œ„์—์„œ ํ˜ธ์ถœํ•œ ์˜์ƒ์˜ ์ •๋ณด์ž…๋‹ˆ๋‹ค.

      1
      2
      3
      4
      5
      6
      7
      8
      
      {'source': 'pbAd8O1Lvm4',
       'title': 'Self-reflective RAG with LangGraph: Self-RAG and CRAG',
       'description': 'Unknown',
       'view_count': 23406,
       'thumbnail_url': 'https://i.ytimg.com/vi/pbAd8O1Lvm4/hq720.jpg',
       'publish_date': '2024-02-07 00:00:00',
       'length': 1058,
       'author': 'LangChain'}
      

2.3. 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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    import datetime
    from typing import Literal, Optional, Tuple
    from langchain_core.pydantic_v1 import BaseModel, Field
      
    class TutorialSearch(BaseModel):
        """Search over a database of tutorial videos about a software library."""
      
        content_search: str = Field(
            ...,
            description="Similarity search query applied to video transcripts.",
        )
        title_search: str = Field(
            ...,
            description=(
                "Alternate version of the content search query to apply to video titles. "
                "Should be succinct and only include key words that could be in a video "
                "title."
            ),
        )
        min_view_count: Optional[int] = Field(
            None,
            description="Minimum view count filter, inclusive. Only use if explicitly specified.",
        )
        max_view_count: Optional[int] = Field(
            None,
            description="Maximum view count filter, exclusive. Only use if explicitly specified.",
        )
        earliest_publish_date: Optional[datetime.date] = Field(
            None,
            description="Earliest publish date filter, inclusive. Only use if explicitly specified.",
        )
        latest_publish_date: Optional[datetime.date] = Field(
            None,
            description="Latest publish date filter, exclusive. Only use if explicitly specified.",
        )
        min_length_sec: Optional[int] = Field(
            None,
            description="Minimum video length in seconds, inclusive. Only use if explicitly specified.",
        )
        max_length_sec: Optional[int] = Field(
            None,
            description="Maximum video length in seconds, exclusive. Only use if explicitly specified.",
        )
      
        def pretty_print(self) -> None:
            for field in self.__fields__:
                if getattr(self, field) is not None and getattr(self, field) != getattr(
                    self.__fields__[field], "default", None
                ):
                    print(f"{field}: {getattr(self, field)}")
    
    • LLM์€ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ด๋ฅผ JSON ํ˜•์‹์˜ ๊ตฌ์กฐํ™”๋œ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ๋กœ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

      • structured_llm = llm.with_structured_output(TutorialSearch)์— ์•ž์— ์„ค์ •ํ•ด๋‘” TutorialSearch์œผ๋กœ ์ถœ๋ ฅํ•˜๋„๋ก ์ •์˜ํ•ด๋‘ก๋‹ˆ๋‹ค. ``` from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI

    Create the Prompt Template:

    system = โ€œ"โ€You are an expert at converting user questions into database queries. You have access to a database of tutorial videos about a software library for building LLM-powered applications. Given a question, return a database query optimized to retrieve the most relevant results.โ€โ€โ€

    prompt = ChatPromptTemplate.from_messages([(โ€œsystemโ€, system), (โ€œhumanโ€, โ€œ{question}โ€)])

    Initialize the Language Model (LLM)

    llm = ChatOpenAI(model=โ€gpt-3.5-turbo-0125โ€, temperature=0) structured_llm = llm.with_structured_output(TutorialSearch)

    Prompt and LLM into a Query Analyzer

    query_analyzer = prompt | structured_llm

    ```

2.4. User Question ์ฒ˜๋ฆฌ:

  • ์•ž์—์„œ ์ •์˜ํ•œ prompt์™€ query_analyzer๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ User Question์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    • prompt = ChatPromptTemplate.from_messages([(โ€œsystemโ€, system), (โ€œhumanโ€, โ€œ{question}โ€)])
    • query_analyzer = prompt structured_llm
  • ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ question์„ ์ฒ˜๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.

    1
    
      query_analyzer.invoke({"question": "your question"}).pretty_print()
    
    1. ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ: ์‚ฌ์šฉ์ž ์งˆ๋ฌธ์„ ํ”„๋กฌํ”„ํŠธ์— ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.
    2. LLM ์ฒ˜๋ฆฌ: LLM์ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฝ๊ณ  TutorialSearch ๊ตฌ์กฐ์— ๋งž๋Š” ์ถœ๋ ฅ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    3. ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ: ๊ฒฐ๊ณผ๋ฅผ TutorialSearch ๊ฐ์ฒด๋กœ ํŒŒ์‹ฑํ•˜๊ณ  ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ: ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ์„ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค:

1
"2023๋…„ 6์›” ์ดํ›„์— ๊ฒŒ์‹œ๋œ LangChain์— ๊ด€ํ•œ ์˜์ƒ ์ค‘ 10๋ถ„ ์ดํ•˜์˜ ์˜์ƒ์„ ๋ณด์—ฌ์ค˜."
  • ์ด ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ LLM(๋Œ€ํ˜• ์–ธ์–ด ๋ชจ๋ธ)๊ณผ ์ฟผ๋ฆฌ ๋ถ„์„ ์ฒด์ธ(query analyzer)์„ ์‚ฌ์šฉํ•ด, TutorialSearch์™€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    class TutorialSearch(BaseModel):
        content_search: str = None
        title_search: str = None
        earliest_publish_date: Optional[date] = None
        latest_publish_date: Optional[date] = None
        min_length_sec: Optional[int] = None
        max_length_sec: Optional[int] = None
        min_view_count: Optional[int] = None
        max_view_count: Optional[int] = None
    
  • ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉ์ž ์งˆ๋ฌธ์„ ๋ณ€ํ™˜ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ TutorialSearch ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    1
    2
    3
    4
    5
    6
    
    TutorialSearch(
        content_search='LangChain',
        title_search='LangChain',
        earliest_publish_date=datetime.date(2023, 6, 1),
        max_length_sec=600  # 10๋ถ„์„ ์ดˆ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•œ ๊ฐ’
    )
    
  • ์ด ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ๋Š” ์ด์ œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

    • earliest_publish_date ํ•„๋“œ๋Š” 2023๋…„ 6์›” 1์ผ ์ดํ›„์— ๊ฒŒ์‹œ๋œ ์˜์ƒ๋งŒ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
    • max_length_sec ํ•„๋“œ๋Š” 10๋ถ„ ์ดํ•˜์˜ ์˜์ƒ๋งŒ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
    • content_search์™€ title_search ํ•„๋“œ๋Š” LangChain์ด๋ผ๋Š” ํ‚ค์›Œ๋“œ๊ฐ€ ํฌํ•จ๋œ ์ฝ˜ํ…์ธ ๋‚˜ ์ œ๋ชฉ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
  • ์ด ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง์„ ์ ์šฉํ•˜๋ฉด, ์กฐ๊ฑด์— ๋งž๋Š” ์˜์ƒ๋งŒ์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2.5. ์ถ”๊ฐ€ ์˜ˆ์‹œ

  1. ์ž์—ฐ์–ด ์งˆ๋ฌธ: โ€œ2023๋…„์— ๊ฒŒ์‹œ๋œ chat langchain ๋น„๋””์˜คโ€
    • ์งˆ์˜๋ฌธ:
      1
      2
      3
      
      query_analyzer.invoke(
          {"question": "videos on chat langchain published in 2023"}
      ).pretty_print()
      
    • ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ ์ถœ๋ ฅ:
      1
      2
      3
      4
      
      content_search: chat langchain
      title_search: 2023
      earliest_publish_date: 2023-01-01
      latest_publish_date: 2023-12-31
      
  2. ์ž์—ฐ์–ด ์งˆ๋ฌธ: โ€œ2024๋…„ ์ด์ „์— ๊ฒŒ์‹œ๋œ chat langchain ๋น„๋””์˜คโ€
    • ์งˆ์˜๋ฌธ:
      1
      2
      3
      
      query_analyzer.invoke(
          {"question": "videos that are focused on the topic of chat langchain that are published before 2024"}
      ).pretty_print()
      
    • ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ ์ถœ๋ ฅ:
      1
      2
      3
      
      content_search: chat langchain
      title_search: chat langchain
      latest_publish_date: 2023-12-31
      
  3. ์ž์—ฐ์–ด ์งˆ๋ฌธ: โ€œ5๋ถ„ ์ดํ•˜์˜ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ๊ด€๋ จ ๋น„๋””์˜คโ€
    • ์งˆ์˜๋ฌธ:
      1
      2
      3
      
      query_analyzer.invoke(
          {"question": "how to use multi-modal models in an agent, only videos under 5 minutes"}
      ).pretty_print()
      
    • ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ ์ถœ๋ ฅ:
      1
      2
      3
      
      content_search: multi-modal models agent
      title_search: multi-modal models agent
      max_length_sec: 300
      



-->