๐Ÿ“š ๋ช…๋ นํ–‰ RAG ์ฑ„ํŒ… ๊ตฌํ˜„ยถ

1. ์‹ฑ๊ธ€ํ„ด LLM ๋Œ€ํ™”ยถ

์žฅ๊ณ  ์™ธ์ ์œผ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ ๋งŒ์œผ๋กœ OpenAI API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‹ฑ๊ธ€ํ„ด LLM ๋Œ€ํ™”๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

์žฅ๊ณ  ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฒฝ๋กœ์—์„œ๋„ ํŠน์ • ์žฅ๊ณ  ํ”„๋กœ์ ํŠธ ๋‚ด ์ž์›(๋ชจ๋ธ, ํ…œํ”Œ๋ฆฟ, ์บ์‹œ ๋“ฑ)๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. OpenAI API Key ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐ’ ๋กœ๋”ฉ์„ ์œ„ํ•ด ์žฅ๊ณ  ํ”„๋กœ์ ํŠธ๋ฅผ ๋กœ๋”ฉํ•˜์—ฌ settings.OPENAI_API_KEY ๊ฐ’์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

LLM ๋ชจ๋ธ์€ gpt-4o-mini ๋ชจ๋ธ์„ ์‚ฌ์šฉํ–ˆ์œผ๋ฉฐ, ๋‹ค๋ฅธ OpenAI API ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์—์„œ model ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ chat-cli.py ๊ฒฝ๋กœ์— ์œ„ ์ฝ”๋“œ๋ฅผ ์ €์žฅํ•˜์‹œ๊ณ  ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”. [Human] ํ”„๋กฌํ”„ํŠธ๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์‹œ๋ฉด, OpenAI LLM์„ ํ†ตํ•ด ์‘๋‹ต์ด ์ƒ์„ฑ๋˜๊ณ  ์˜์–ด/ํ•œ๊ธ€๋กœ ๋ฒˆ์—ญ๋œ ๋ฉ”์‹œ์ง€๋„ ๊ฐ™์ด ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

../../_images/single.png

2. ์žฅ๊ณ  ๋ช…๋ น์œผ๋กœ ๊ธ€์ž์ˆ˜ ์‘๋‹ต ์ฑ„ํŒ… CLI ๊ตฌํ˜„ยถ

์ด๋ฒˆ์—๋Š” ์žฅ๊ณ  ๋ช…๋ น์„ ํ†ตํ•ด ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ์˜ ๊ธ€์ž์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฑ„ํŒ… CLI๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„ ํŒŒ์ด์ฌ ํŒŒ์ผ์ด ์•„๋‹Œ ์žฅ๊ณ  ๋ช…๋ น์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด, ์žฅ๊ณ  ํ”„๋กœ์ ํŠธ ๋‚ด ๋‹ค์–‘ํ•œ ์ž์›๋“ค์„ ๋ณ„๋„ ์„ค์ •์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ์žฅ๊ณ  BaseCommand๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ๋ช…๋ น ์˜ต์…˜์„ ์†์‰ฝ๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์žฅ๊ณ  ๋ช…๋ น์€ ํ•ญ์ƒ ์•ฑ/managment/commands/ ๊ฒฝ๋กœ์— ์ €์žฅํ•ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. chat-rag-cli.py ํŒŒ์ผ๋กœ ์ €์žฅํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— python manage.py chat-rag-cli ๋ช…๋ น์„ ํ†ตํ•ด ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

../../_images/count-ch.png

3. ๋ฒˆ์—ญ ์ฑ„ํŒ… CLI ๊ตฌํ˜„ยถ

LLM ์‘๋‹ต์„ ์ƒ์„ฑํ•˜๋Š” make_ai_message ํ•จ์ˆ˜๋Š” ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด chat/llm.py ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , model, temperature, max_tokens ๋“ฑ ๋ชจ๋ธ ์„ค์ • ์ธ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋” ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

chat-rag-cli.py ํŒŒ์ผ์—์„œ๋Š” ๊ธ€์ž์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” len(user_input) ๋Œ€์‹  ai_message = make_ai_message(system_prompt, user_input) ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ LLM ์‘๋‹ต์„ ์ƒ์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

python manage.py chat-rag-cli ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด, ์ฑ„ํŒ…์ด ์ง„ํ–‰๋˜๋ฉฐ ๋ฒˆ์—ญ๋œ ๋ฉ”์‹œ์ง€๋ฅผ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

../../_images/translator.png

๊ทธ๋Ÿฐ๋ฐ, ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ €์žฅํ•˜์ง€ ์•Š์•„ ๋Œ€ํ™”๊ฐ€ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ถ„๋ช… ์ œ๊ฐ€ ์ด๋ฆ„์„ ์ด์•ผ๊ธฐํ•˜๊ณ  ์ด๋ฆ„์„ ๋ฌผ์–ด๋ณด๋Š” ๋ฐ ์ด๋ฆ„์„ ๋ชจ๋ฅธ๋‹ค๊ณ  ํ•˜๋„ค์š”. ๐Ÿ˜ญ

4. ๋ฉ€ํ‹ฐํ„ด LLM ๋Œ€ํ™”ยถ

OpenAI LLM์„ ๋น„๋กฏํ•œ ๋ชจ๋“  LLM์€ ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ €์žฅํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ €์žฅํ•˜๊ณ , ๋งค ๋Œ€ํ™”๋งˆ๋‹ค ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ „๋‹ฌํ•˜์—ฌ LLM ์‘๋‹ต์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

make_ai_message ํ•จ์ˆ˜๋ฅผ ํ™•์žฅํ•˜์—ฌ LLM ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๊ณ , ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ €์žฅํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. make_ai_message ํ•จ์ˆ˜ ์ด๋ฆ„์€ ๋ณด๋‹ค ๋ช…ํ™•ํ•˜๊ฒŒ make_reply๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

chat-rag-cli ๋ช…๋ น์—์„œ๋Š” LLM ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋Œ€ํ™” ๊ธฐ๋ก์„ ๊ด€๋ฆฌํ•˜๊ณ , make_reply ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ LLM ์‘๋‹ต์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์‹คํ–‰ํ•ด๋ณด์‹œ๋ฉด, ๋Œ€ํ™” ๊ธฐ๋ก์„ LLM์ด ์•Œ๊ณ  ์žˆ๊ธฐ์— ์ด๋ฆ„์„ ๋ฌผ์–ด๋ณด๋Š” ๋Œ€ํ™”๊ฐ€ ์ด์–ด์ง์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

../../_images/multi.png

ํŒŒ์ด์ฌ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์•„๋‹Œ ์žฅ๊ณ  ๋ชจ๋ธ์„ ํ†ตํ•ด์„œ ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ €์žฅ/๊ด€๋ฆฌํ•˜์‹ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์Œ ๐Ÿ’ฌ ์ฑ„ํŒ…๋ฐฉ/๋ฉ”์‹œ์ง€ ๋ชจ๋ธ ๋ฐ ๊ธฐ๋ณธ ํŽ˜์ด์ง€ ๊ตฌ์„ฑ ๋ฌธ์„œ์—์„œ ์ด์–ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.

5. RAG ๋Œ€ํ™”ยถ

LLM์€ ๊ฒ€์ƒ‰์—”์ง„์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋‹จ์ง€ ์•Œ๊ณ  ์žˆ๋Š” ์ง€์‹์— ๊ธฐ๋ฐ˜ํ•ด์„œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ๋ฟ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ LLM์ด ๋ชจ๋ฅด๋Š” ์ง€์‹์— ๋Œ€ํ•ด์„œ๋Š” ํ™˜๊ฐ (Hallucination)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค. ์ถฉ๋ถ„ํ•œ ์ง€์‹์ด ์žˆ๋Š” ์ƒํ™ฉ์—์„œ๋Š” ํ™˜๊ฐ์ด ๋ฐœ์ƒํ•  ํ™•๋ฅ ์ด ๋‚ฎ์•„์ง‘๋‹ˆ๋‹ค.

ํŒ

RAG ๊ฐœ๋…์— ๋Œ€ํ•ด์„œ๋Š” RAG #01. RAG ๋ฐ‘๋ฐ”๋‹ฅ๋ถ€ํ„ฐ ์›น ์ฑ„ํŒ…๊นŒ์ง€ ํŠœํ† ๋ฆฌ์–ผ๊ณผ RAG #02. ์‹ค์ „: ์„ธ๋ฒ• RAG๋ฅผ ์œ„ํ•œ pgvector ์ž„๋ฒ ๋”ฉ ํŠœํ† ๋ฆฌ์–ผ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

../../_images/llm-rag.png

๊ด€๋ จ ์ง€์‹๊ณผ ํ•จ๊ป˜ ์งˆ๋ฌธํ•˜๋ฉด, LLM์ด ๋ชจ๋ฅด๋Š” ์ง€์‹(๋ฒ•๋ น, ํšŒ์‚ฌ ์ •๋ณด ๋“ฑ)์„ ๋ณด์ถฉํ•ด์„œ ์ •ํ™•ํ•œ ๋‹ต๋ณ€์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ยถ

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

sqlite-vec/pgvector ๊ธฐ๋ฐ˜์œผ๋กœ ์žฅ๊ณ  ๋ชจ๋ธ์„ ํ†ตํ•ด ๋ฒกํ„ฐ ์Šคํ† ์–ด๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ†ตํ•ฉ์„ฑ - ๋ณ„๋„ ์ธํ”„๋ผ ์ถ”๊ฐ€์—†์ด ๋น ๋ฅด๊ฒŒ ๋ฒกํ„ฐ ์Šคํ† ์–ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๋ฌธ์„œ๋งŒ ๋ณ„๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  2. ํ™•์žฅ์„ฑ - ์ƒํ™ฉ์— ๋”ฐ๋ผ (๋กœ์ปฌํ™˜๊ฒฝ, ์†Œ๊ทœ๋ชจ, ๋Œ€๊ทœ๋ชจ ์šด์˜ํ™˜๊ฒฝ) SQLite, PostgreSQL ๋“ฑ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  3. ๊ด€๋ฆฌ ์šฉ์ด์„ฑ - ์žฅ๊ณ  ์–ด๋“œ๋ฏผ์„ ํ†ตํ•ด ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  4. ์ผ๊ด€๋œ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ - ๋‹ค๋ฅธ ๋ชจ๋ธ๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด ๊ฐœ๋ฐœ ์ผ๊ด€์„ฑ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

  5. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ง€์› - ์žฅ๊ณ ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹œ์Šคํ…œ์„ ํ†ตํ•ด ๋ฒกํ„ฐ ์Šคํ† ์–ด ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ง€์‹์„ ์ฐพ๊ณ  ํ”„๋กฌํ”„ํŠธ์— ์ ์šฉํ•˜์—ฌ RAG ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

user_input = "์žฌํ™” ์ˆ˜์ถœํ•˜๋Š” ๊ฒฝ์šฐ ์˜์„ธ์œจ ์ฒจ๋ถ€ ์„œ๋ฅ˜๋กœ ์ˆ˜์ถœ์‹ค์ ๋ช…์„ธ์„œ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•"

doc_list = TaxLawDocument.objects.similarity_search(user_input)
์ง€์‹ = str(doc_list)
user_input = f"""<context>{์ง€์‹}</context>\n\n์งˆ๋ฌธ : {user_input}"""
../../_images/rag.png