AI ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค Pinecone: RAG ์์คํ ๋ฐ ๊ณ ์ฑ๋ฅ ๋ฒกํฐ ๊ฒ์์ ์ํ ๋ฐ์ดํฐ ํ๋ซํผ
Pinecone์ AI ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋, ๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ(LLM) ๊ธฐ๋ฐ์ RAG (๊ฒ์ ์ฆ๊ฐ ์์ฑ) ์์คํ ๋ฐ ์ถ์ฒ ์์คํ ์ ๋น ๋ฅด๊ณ ์ ํํ๊ฒ ๊ตฌ์ถํ ์ ์๋ ์์ ๊ด๋ฆฌํ(Fully Managed) ๊ณ ์ฑ๋ฅ ๋ฐ์ดํฐ ํ๋ซํผ ์๋ฃจ์ ์ ๋๋ค.
AI ๊ธฐ์ ์ด ๋ฐ์ ํ๋ฉด์ ๋ฐฉ๋ํ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์๋ฏธ ๊ธฐ๋ฐ ๊ฒ์(Semantic Search)์ ๊ตฌํํ๋ ๊ฒ์ด ํต์ฌ ๊ณผ์ ๊ฐ ๋์์ต๋๋ค. Pinecone์ ์ด๋ฌํ AI ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋ฒกํฐ ๊ฒ์์ ์ํด ์ต์ ํ๋ ๋ฒกํฐ ๋ฐ์ดํฐ ํ๋ซํผ์ผ๋ก, ๊ฐ๋ฐ์์ ๊ธฐ์ ์ด ๋น ๋ฅด๊ณ ์ ํํ ๊ฒ์ ์์ง, ์ถ์ฒ ์์คํ , ์์ฐ์ด ์ฒ๋ฆฌ(NLP) ์๋น์ค๋ฅผ ์ฝ๊ฒ ๊ตฌ์ถํ ์ ์๋๋ก ์ง์ํฉ๋๋ค.
์ค๋์ AI๋ฅผ ํ์ฉํ๊ธฐ ์ํด ํ์์ ์ธ ๊ธฐ๋ณธ ์๋ฃ, ํนํ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ RAG ํ์ดํ๋ผ์ธ์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์ธํ ์์๋ณด๊ฒ ์ต๋๋ค.
AI ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค Pinecone์ด๋ ๋ฌด์์ด๋ฉฐ, LLM RAG์ ํ์์ ์ธ ์ด์ ?
Pinecone์ ํ ์คํธ, ์ด๋ฏธ์ง, ์ค๋์ค ๋ฑ์ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ๊ณ ์ฐจ์ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ก ๋ณํํ์ฌ ์ ์ฅํ๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํจ์จ์ ์ธ ์ ์ฌ๋ ๊ฒ์ ๋ฐ AI ์ถ์ฒ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํด๋ผ์ฐ๋ ๊ธฐ๋ฐ ๊ด๋ฆฌํ ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๋ค. ๊ธฐ์กด์ SQL ๊ธฐ๋ฐ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค(RDBMS)๋ NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ํค์๋๋ ํค-๊ฐ ์กฐํ์ ๊ฐ์ ์ด ์์ง๋ง, ๋ฐ์ดํฐ์ ์๋ฏธ๋ฅผ ํ์ ํ๋ ๋ฒกํฐ ๊ฒ์ ๋ฐ ์ ์ฌ๋ ๊ณ์ฐ์๋ ๊ทผ๋ณธ์ ์ธ ํ๊ณ๊ฐ ์์ต๋๋ค.

Pinecone์ ์ผ๋ฐ์ ์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์๋, ์๋ฒ ๋ฉ ๋ฒกํฐ(Embedding Vector)๋ฅผ ๋๊ท๋ชจ๋ก ์ ์ฅํ๊ณ ๊ณ ์์ผ๋ก ๊ฒ์ํ๊ธฐ ์ํด ์ค๊ณ๋ ํนํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๋ค. ํนํ Approximate Nearest Neighbor (ANN) ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ์ฌ ์ ์ฌ๋ ๊ธฐ๋ฐ ๊ฒ์์ ๊ทน๋๋ก ์ต์ ํ๋์ด ์์ต๋๋ค. ๋ฐ๋ผ์ RAG ์์คํ ๊ตฌ์ถ, ์ด๋ฏธ์ง ๊ฒ์, ๋ฌธ์ ๊ฒ์, ์ฑ๋ด, ๊ฐ์ธํ ์ถ์ฒ ์์คํ ๋ฑ AI ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์ถ์ ์์ด ์ฑ๋ฅ๊ณผ ํ์ฅ์ฑ ๋ฉด์์ ํ์์ ์ธ ํ๋ซํผ์ผ๋ก ์ธ์ ๋ฐ๊ณ ์์ต๋๋ค.

AI ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค Pinecone์ ์ฃผ์ ๊ธฐ๋ฅ ๋ฐ ์ฅ์ (RAG ์ต์ ํ)
| ๊ธฐ๋ฅ | ์ค๋ช |
|---|---|
| ๊ณ ์ ๋ฒกํฐ ๊ฒ์ | ์์ญ์ต ๊ฐ์ ๋ฒกํฐ๋ฅผ ๋์์ผ๋ก๋ ๋ฐ๋ฆฌ์ด ๋จ์์ ๋น ๋ฅธ ๊ฒ์ ์๋๋ฅผ ์ ๊ณตํ๋ฉฐ, ๊ฐ์ฅ ์ ์ฌํ ๋ฒกํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ํ์ํ ์ ์์ต๋๋ค. |
| ์์ ๊ด๋ฆฌํ ์๋น์ค | ์๋ฒ ๊ด๋ฆฌ, ์ธํ๋ผ ์ค์ , ์ธ๋ฑ์ฑ ์ต์ ํ ๋ฑ์ ๋ณต์กํ ์์ ์์ด, Pinecone์ด ์๋์ผ๋ก ํ์ฅ(Scaling), ๊ด๋ฆฌ, ์ต์ ํํ์ฌ ๊ฐ๋ฐ ๋ถ๋ด์ ์ต์ํํฉ๋๋ค. |
| AI & LLM ํตํฉ | Python SDK, REST API๋ฅผ ์ ๊ณตํ๋ฉฐ, LangChain ๋ฑ ๋ค์ํ ML/AI ํ๋ ์์ํฌ์ ์๋ฒฝํ๊ฒ ํธํ๋์ด LLM ๊ธฐ๋ฐ RAG ํ์ดํ๋ผ์ธ๊ณผ ์ฝ๊ฒ ์ฐ๊ฒฐ๋ฉ๋๋ค. |
| ๋ฐ์ด๋ ํ์ฅ์ฑ | ๋ฐ์ดํฐ ๋ณผ๋ฅจ์ด ์ฆ๊ฐํ๊ฑฐ๋ ํธ๋ํฝ์ด ๋ชฐ๋ ค๋ ์๋ ํ์ฅ์ด ๊ฐ๋ฅํ์ฌ, ์์ญ์ต ๊ฐ์ ๋ฒกํฐ ์ฒ๋ฆฌ๋ฅผ ์์ ์ ์ผ๋ก ์ํํ ์ ์๋ ์ํฐํ๋ผ์ด์ฆ๊ธ ํ๋ซํผ์ ๋๋ค. |
| ๋์ ์ ํ๋ | HNSW ๋ฑ ํจ์จ์ ์ธ ๋ฒกํฐ ์ธ๋ฑ์ฑ ๋ฐ ์ต์ ํ๋ ๊ฒ์ ์๊ณ ๋ฆฌ์ฆ์ ํตํด ์ ํํ ์ ์ฌ๋ ๊ธฐ๋ฐ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํ์ฌ AI ๋ต๋ณ์ ํ์ง์ ํฅ์์ํต๋๋ค. |
Pinecone AI ๋ฐ์ดํฐ ํ์ฉ ๋ถ์ผ ๋ฐ ๋ฐฉ๋ฒ
AI ์์ง๋์ด์ ๊ธฐ์ ์ Pinecone์ ํ์ฉํด ๊ณ ๋ํ๋ AI ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ํ๊ฒ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
- RAG (๊ฒ์ ์ฆ๊ฐ ์์ฑ): LLM์ ์ง์์ ์ต์ ๋๋ ์ฌ๋ด ๋ฐ์ดํฐ๋ก ํ์ฅํ์ฌ ํ๊ฐ(Hallucination) ํ์์ ์ค์ด๊ณ ๋ต๋ณ์ ์ ํ๋๋ฅผ ๋์ ๋๋ค.
- ๊ฐ์ธํ ์ถ์ฒ ์์คํ : ์ฌ์ฉ์ ํ๋ ํจํด์ด๋ ์์ดํ ์ ๋ณด๋ฅผ ๋ฒกํฐ๋ก ๋ณํํ์ฌ ๋ง์ถคํ ์ฝํ ์ธ ๋ฐ ์ํ ์ถ์ฒ์ ์ ๊ณตํฉ๋๋ค.
- ์์ฐ์ด ์ฒ๋ฆฌ (NLP) ๊ฒ์: ๋ฌธ์, FAQ, ๊ณ ๊ฐ ๋ฌธ์ ๋ฐ์ดํฐ์์ ์๋ฏธ ๊ธฐ๋ฐ ๊ฒ์๊ณผ ์ ์ฌ๋ ํ๋จ์ ์ํํ์ฌ ์ ํํ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ๋ฉํฐ๋ชจ๋ฌ ๊ฒ์: ์ด๋ฏธ์ง, ์ค๋์ค ๋ฑ์ ํน์ง ๋ฒกํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๊ฐ์ /์ฒญ๊ฐ์ ์ ์ฌํ ์ฝํ ์ธ ๋ฅผ ๊ณ ์ ๊ฒ์ํฉ๋๋ค.
- AI ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋ถ์: ๋๊ท๋ชจ ๋น์ ํ ๋ฐ์ดํฐ์ ์์ ์๋ฏธ ์๋ ํจํด๊ณผ ์ธ์ฌ์ดํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ถ์ถํฉ๋๋ค.
Pinecone์ ๋จ์ํ ๋ฒกํฐ ์ ์ฅ์๋ฅผ ๋์ด, AI ์๋น์ค ์ต์ ํ๋ฅผ ์ํ ํตํฉ ๊ด๋ฆฌํ ํ๋ซํผ์ ๋๋ค. ์ฌ์ฉ์๋ ์ธํ๋ผ ๊ด๋ฆฌ ๋ถ๋ด ์์ด AI ๋ชจ๋ธ๊ณผ ๋ฒกํฐ ๋ฐ์ดํฐ ๊ฐ ์ฐ๊ฒฐ์ ์ง์คํ ์ ์์ผ๋ฉฐ, ๋น ๋ฅด๊ณ ์ ํํ ๋ฒกํฐ ๊ฒ์ ๊ฒฐ๊ณผ๋ก ์ต์ข ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ๊ทน๋ํํ ์ ์์ต๋๋ค. ๋ํ Pinecone์ ์ํฐํ๋ผ์ด์ฆ๊ธ ํ์ฅ์ฑ๊ณผ ์์ ์ฑ์ ์ ๊ณตํ์ฌ ๋๊ท๋ชจ ์๋น์ค ํ๊ฒฝ์์๋ ์ค๋จ ์์ด ์์ ์ ์ผ๋ก ์ด์ํ ์ ์์ต๋๋ค.
AI ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๊ฑฐ๋ ๊ณ ์ฑ๋ฅ ๋ฐ์ดํฐ ๊ฒ์ ๋ฐ ์ถ์ฒ ์์คํ ์ ๊ตฌ์ถํ๋ ค๋ ๊ธฐ์ ๊ณผ ๊ฐ๋ฐ์์๊ฒ Pinecone์ ํ์์ ์ธ ๋ฒกํฐ ๋ฐ์ดํฐ ์๋ฃจ์ ์ด ๋ ๊ฒ์ ๋๋ค.
Pinecone AI ๋ฒกํฐ ๋ฐ์ดํฐ ์ฌ์ฉ ๋ฐฉ๋ฒ (๊ฐ๋ฐ์ ๊ฐ์ด๋)
ํ์๊ฐ์ ๋ฐ ํ๋ก์ ํธ ์ค์
Pinecone ๊ณต์ ์ฌ์ดํธ์ ์ ์ํด์ ํ์๊ฐ์ ์ ์๋ฃํฉ๋๋ค.
์ธ๋ฑ์ค ๋ฐ ์๋ฒ ๋ฉ ๋ชจ๋ธ ์ ํ (LLM ์ต์ ํ)
์๋ก์ด ์ธ๋ฑ์ค(Index) ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ , ์๋ฒ ๋ฉ ๋ชจ๋ธ์ ์ฑ๋ฅ๊ณผ ๋ค๊ตญ์ด ์ง์์ด ๋ฐ์ด๋ (์: multilingual-e5-large)๋ฅผ ์ ํํ์ฌ ๋ฒกํฐ ์ฐจ์(Dimension)์ ์ง์ ํฉ๋๋ค.
์ด ๋ชจ๋ธ์ ํ ์คํธ๋ฅผ 1024์ฐจ์์ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ก ๋ณํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.

API ํค ๋ฐ ํ๊ฒฝ ์ฃผ์ ํ์ธ
๊ฐ์ ํ ์ ๊ณต๋๋ API ํค์ ์์ฑ๋ Pinecone ํ๊ฒฝ ์ฃผ์(Host URL)๋ฅผ ํ์ธํ๊ณ ์์ ํ๊ฒ ๋ณด๊ดํฉ๋๋ค. ์ด ์ ๋ณด๋ ๋ฐ์ดํฐ ์ ๋ก๋ ๋ฐ ๋ฒกํฐ ๊ฒ์ ์์ฒญ ์ ์ฌ์ฉ๋ฉ๋๋ค.

ํ์ Python ํจํค์ง ์ค์น
ํ์ด์ฌ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์๋ ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ Pinecone ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋์ ํ์ํ ํต์ฌ ๋๊ตฌ๋ค์ ์ค์นํฉ๋๋ค.
pip install pinecone sentence-transformers requests beautifulsoup4 tqdm
- Pinecone ํด๋ผ์ด์ธํธ: ๋ฒกํฐ ๋ฐ์ดํฐ ์ ๋ก๋(Upsert) ๋ฐ ์ ์ฌ๋ ๊ฒ์(Query) ์ํ
- Sentence Transformers: ๊ณ ํ์ง ํ ์คํธ ์๋ฒ ๋ฉ ์์ฑ (ํ ์คํธ๋ฅผ ๋ฒกํฐ๋ก ๋ณํ)
- BeautifulSoup4 / Requests: RAG ๋ฐ์ดํฐ ์์ค(์: ๋ธ๋ก๊ทธ, ๋ฌธ์) ์คํฌ๋ํ ๋ฐ ์์ง
- tqdm: ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ ์งํ๋ฅ ํ์
๋ฐ์ดํฐ ์์ง ๋ฐ ๋ฒกํฐํ ์ฝ๋ ์ ์ฉ
์๋ ์ํ ํ์ด์ฌ ์ฝ๋๋ฅผ ์์ ํ์ฌ Pinecone ์ธ๋ฑ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ก๋ํ๋ ์์ ์ ์ํํฉ๋๋ค. API ํค, Pinecone ์ฃผ์, ๋ฐ์ดํฐ ์์ค ์ฃผ์ ๋ฑ์ ํ๊ฒฝ์ ๋ง๊ฒ ์ค์ ํด์ผ ํฉ๋๋ค.
์ด ์ฝ๋๋ ์น์์ ๋ธ๋ก๊ทธ ์ฝํ ์ธ ๋ฅผ ์ถ์ถํ์ฌ AI์ฉ ๋ฒกํฐ ๋ฐ์ดํฐ๋ก ๋ณํํ๊ณ Pinecone์ ๋ฐฐ์น ์ ๋ก๋ํ๋ RAG ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ ์์ ์ ๋๋ค.
์ํ Python ์ฝ๋ (Ingestion Script)
import requests
from bs4 import BeautifulSoup
from sentence_transformers import SentenceTransformer
from tqdm.auto import tqdm
import re
# =================================================================
# ์ค์ : API KEY, HOST, ์๋ฒ ๋ฉ ๋ชจ๋ธ ์ ๋ณด ์ค์
# =================================================================
API_KEY = "YOUR_PINECONE_API_KEY"
PINECONE_HOST = "https://your-pinecone-instance.svc..pinecone.io"
EMBEDDING_MODEL_NAME = 'intfloat/multilingual-e5-large' # ๊ณ ์ฑ๋ฅ ๋ค๊ตญ์ด ์๋ฒ ๋ฉ ๋ชจ๋ธ
BLOG_DOMAIN = "https:๋ธ๋ก๊ทธ ์ฃผ์"
EMBEDDING_DIMENSION = 1024 # ๋ชจ๋ธ์ด ์์ฑํ๋ ๋ฒกํฐ ์ฐจ์ (multilingual-e5-large ๊ธฐ์ค)
# ์๋ฒ ๋ฉ ๋ชจ๋ธ ์ด๊ธฐํ
model = SentenceTransformer(EMBEDDING_MODEL_NAME)
# =================================================================
# ์ฌ์ดํธ๋งต์์ ํฌ์คํธ URL ์์ง ํจ์
# =================================================================
import xml.etree.ElementTree as ET
def parse_post_urls_from_xml_number(url):
urls = set()
try:
response = requests.get(url, timeout=15)
response.raise_for_status()
content = response.content.decode('utf-8')
content = re.sub(r'xmlns="[^"]+"', '', content)
root = ET.fromstring(content)
for element in root.findall('.//loc'):
loc_url = element.text
if loc_url:
# /์ซ์ ํ์์ URL๋ง ํํฐ๋ง (๊ฒ์๊ธ ์๋ณ)
if re.search(r'/\d+$', loc_url) and not any(keyword in loc_url for keyword in ['/category', '/pages', '/tag', '/guestbook']):
urls.add(loc_url.split('?')[0])
return urls
except Exception as e:
print(f" ์ฌ์ดํธ๋งต ํ์ฑ ์ค๋ฅ ({url}): {e}")
return set()
def get_post_urls_from_sitemap(blog_domain):
sitemap_url = f"{blog_domain}/sitemap.xml"
print(f"[{sitemap_url}]์์ RAG ๋ฐ์ดํฐ ์์ค URL์ ์์งํฉ๋๋ค...")
post_urls = parse_post_urls_from_xml_number(sitemap_url)
print(f" ์ฌ์ดํธ๋งต ํ์ฑ์์ ์ด {len(post_urls)}๊ฐ์ ๊ฒ์๊ธ URL์ ์ฐพ์์ต๋๋ค.")
return list(post_urls)
# =================================================================
# HTML์์ ์ฝํ
์ธ ํ์ฑ (๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ) ํจ์
# =================================================================
def scrape_blog_content(url):
title = "์ ๋ชฉ ์์"
content = ""
canonical_url = url
try:
response = requests.get(url, timeout=15)
response.raise_for_status()
html = response.text
soup = BeautifulSoup(html, 'html.parser')
# canonical URL ์ถ์ถ
canonical_tag = soup.find('link', rel='canonical')
if canonical_tag and 'href' in canonical_tag.attrs:
canonical_url = canonical_tag['href'].split('?')[0]
# ์ฝํ
์ธ ๊ฐ ํฌํจ๋ ํต์ฌ div ์ถ์ถ (๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ)
main_div = soup.find('div', class_='tt_article_useless_p_margin contents_style')
if main_div:
# ์ ๋ชฉ ์ถ์ถ ๋ฐ ์ ๊ฑฐ
h1_tag = main_div.find('h1')
h2_tag = main_div.find('h2')
if h1_tag:
title = h1_tag.get_text(strip=True)
h1_tag.decompose()
elif h2_tag:
title = h2_tag.get_text(strip=True)
h2_tag.decompose()
# ์ ๋ฆฌ๋ ํ
์คํธ ์ถ์ถ
content = main_div.get_text(separator='\n', strip=True)
else:
print(f"โ ์ฝํ
์ธ div ์์: {url}")
return title, content, canonical_url
except Exception as e:
print(f" HTML ํ์ฑ ์ค๋ฅ ({url}): {e}")
return "์ค๋ฅ ๋ฐ์", "", url
# =================================================================
# Pinecone REST API๋ฅผ ์ด์ฉํ ๋ฒกํฐ ๋ฐฐ์น ์
๋ก๋ (Upsert) ํจ์
# =================================================================
import json
import requests
def upsert_batch_via_rest(vectors_batch):
"""๋ฒกํฐ ๋ฐฐ์น๋ฅผ Pinecone ์ธ๋ฑ์ค์ ์
๋ก๋ํฉ๋๋ค."""
url = f"{PINECONE_HOST}/vectors/upsert"
# ๋ค์์คํ์ด์ค๋ ํ์์ ๋ฐ๋ผ ์ค์ ๊ฐ๋ฅ (RAG ๋ฑ์์ ๋ฐ์ดํฐ ๋ถ๋ฆฌ ์ฉ๋)
payload = {"vectors": vectors_batch, "namespace": ""}
headers = {"Api-Key": API_KEY, "Content-Type": "application/json"}
try:
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"\n Pinecone Upsert ์ค๋ฅ ๋ฐ์: {e}")
return None
# =================================================================
# ๋ฉ์ธ ํจ์ (์ ์ฒด ์คํ ๋ก์ง)
# =================================================================
def main():
post_urls = get_post_urls_from_sitemap(BLOG_DOMAIN)
if not post_urls:
print(" ์์งํ ํฌ์คํธ URL์ด ์์ต๋๋ค. ์คํฌ๋ฆฝํธ๋ฅผ ์ข
๋ฃํฉ๋๋ค.")
return
upserts = []
for url in tqdm(post_urls, desc="ํฌ์คํธ ์ฒ๋ฆฌ ๋ฐ ์๋ฒ ๋ฉ ์์ฑ ์ค"):
title, content, canonical_url = scrape_blog_content(url)
if content and title != "์ค๋ฅ ๋ฐ์":
# ๋ฒกํฐ ID๋ URL์ ๋ง์ง๋ง ์ซ์๋ก ์ค์ (๊ณ ์ ์๋ณ์)
vector_id = canonical_url.split('/')[-1]
try:
# Sentence Transformer๋ฅผ ์ฌ์ฉํ์ฌ ํ
์คํธ๋ฅผ ๋ฒกํฐ๋ก ์ธ์ฝ๋ฉ
embedding = model.encode(content).tolist()
except Exception as e:
print(f"์๋ฒ ๋ฉ ์์ฑ ์ค๋ฅ (๋ฒกํฐํ ์คํจ): {e}")
continue
# Pinecone ์
๋ก๋์ฉ ๋ฐ์ดํฐ ํ์
upserts.append({
'id': vector_id,
'values': embedding,
'metadata': { # ๋ฉํ๋ฐ์ดํฐ๋ ๊ฒ์ ๊ฒฐ๊ณผ์ ํจ๊ป ๋ฐํ๋จ (RAG์์ ํ์)
'text': content,
'title': title,
'url': canonical_url
}
})
if upserts:
print(f"\n์ด {len(upserts)}๊ฐ์ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ฅผ Pinecone์ ์
๋ก๋ํฉ๋๋ค...")
# 100๊ฐ ๋จ์๋ก ๋ฐฐ์น ์
๋ก๋ํ์ฌ ํจ์จ์ฑ ๋์ด๊ธฐ
for i in tqdm(range(0, len(upserts), 100), desc="Pinecone ๋ฒกํฐ ๋ฐ์ดํฐ ์
๋ก๋ ์ค"):
batch = upserts[i:i + 100]
upsert_batch_via_rest(batch)
print("\n AI ๋ฒกํฐ ๋ฐ์ดํฐ ์
๋ก๋ ์๋ฃ! ์ด์ ์ ์ฌ๋ ๊ฒ์์ ์ํํ ์ ์์ต๋๋ค.")
else:
print("์
๋ก๋ํ ์ ํจํ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.")
if __name__ == "__main__":
main()
๋ฐ์ดํฐ ์ ๋ก๋ ์คํฌ๋ฆฝํธ ์คํ
ํฐ๋ฏธ๋์์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ฉด ๋ธ๋ก๊ทธ ๋ฐ์ดํฐ๊ฐ Pinecone ์ธ๋ฑ์ค์ ๋ฒกํฐ ๋ฐ์ดํฐ๋ก ๋ณํ๋์ด ์ ์ฅ๋๋ ์์ ์ด ์์๋ฉ๋๋ค. ์ด ๊ณผ์ ์ ํตํด RAG ์์คํ ์ ์ง์ ๊ธฐ๋ฐ์ด ๊ตฌ์ถ๋ฉ๋๋ค.

python ingest_data.py
AI ๋ฐ์ดํฐ ๋ฒ ์ด์ค ์์ฑ ๋จ๊ณ๋ณ ์์ฝ
AI ๋ฐ์ดํฐ๋ฒ ์ด์ค(Pinecone)๋ฅผ ํ์ฉํ ๋ฒกํฐ ๋ฐ์ดํฐ ์ ๋ก๋ ๊ณผ์ ์ ๋จ๊ณ๋ณ๋ก ์์ฝํ๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
- ์๋ฒ ๋ฉ ์์ฑ
- ํ ์คํธ, ์ด๋ฏธ์ง, ์๋ฆฌ ๋ฐ์ดํฐ๋ฅผ 1024์ฐจ์ ๋ฒกํฐ๋ก ๋ณํ
- ์๋ฏธ ๊ธฐ๋ฐ ๊ฒ์์ด ๊ฐ๋ฅํ Embedding Vector ์์ฑ
- ๋ฐ์ดํฐ ์์ง ๋ฐ ์ ์ฒ๋ฆฌ
- ๋ธ๋ก๊ทธ ์ฌ์ดํธ๋งต(sitemap.xml)์์ /์ซ์ URL ์ถ์ถ
- HTML ์ฝํ ์ธ ์์ ์ ๋ชฉ, ๋ณธ๋ฌธ, ๊ตฌ์กฐํ ๋ฐ์ดํฐ ์ ๋ฆฌ
- ๋ฒกํฐ ์
๋ก๋
- Pinecone REST API๋ฅผ ์ฌ์ฉํด 100๊ฐ ๋จ์๋ก ๋ฐฐ์น ์ ๋ก๋
- ์ ๋ก๋ ์ ๋ฒกํฐ์ ๋ฉํ๋ฐ์ดํฐ(์ ๋ชฉ, URL) ํฌํจ
ํฌ์คํธ ์ฒ๋ฆฌ ๋ฐ ์๋ฒ ๋ฉ ์์ฑ
์๋ฒ ๋ฉ ๋ฒกํฐ(Embedding Vector)๋ ํ ์คํธ, ์ด๋ฏธ์ง, ์๋ฆฌ ๊ฐ์ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ์ปดํจํฐ๊ฐ ์ดํดํ๊ณ ์ฒ๋ฆฌํ ์ ์๋ ์ซ์ ๋ฆฌ์คํธ๋ก ๋ณํํ ๊ฒ์ ๋๋ค. ์ฝ๊ฒ ๋งํ๋ฉด ๋จ์ด๋ ๋ฌธ์ฅ์ ์๋ฏธ๋ฅผ ๋ํ๋ด๋ ์ขํ๋ผ๊ณ ๋ณผ ์ ์์ต๋๋ค.
- ์์ง๋ URL์ ํ๋์ฉ ์ ์ํ์ฌ HTML์ ์คํฌ๋ฉํฉ๋๋ค.
- ์คํฌ๋ฉํ ์ฝํ ์ธ ์์ ์ ๋ชฉ, ๋ณธ๋ฌธ ๋ฑ์ ์ถ์ถํ์ฌ ํ ์คํธ๋ฅผ ์ ๋ฆฌํฉ๋๋ค.
- ์ ๋ฆฌ๋ ํ ์คํธ๋ฅผ ํ ํฐ ์ฒญํฌ๋ก ๋๋๋๋ค.
- ๊ฐ ์ฒญํฌ๋ฅผ multilingual-e5-large ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ 1024์ฐจ์์ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ก ๋ณํํฉ๋๋ค.
Pinecone์ ๋จ์ํ ๋ฒกํฐ ์ ์ฅ์๋ฅผ ๋์ด, AI ์ ํ๋ฆฌ์ผ์ด์ ์ ํจ์จ์ ์ด๊ณ ์์ ์ ์ผ๋ก ์ด์ํ ์ ์๊ฒ ํด์ฃผ๋ ๊ฐ๋ ฅํ ํ๋ซํผ์ ๋๋ค. ํ ์คํธ, ์ด๋ฏธ์ง, ์๋ฆฌ ๋ฑ ๋ค์ํ ํํ์ ๋ฐ์ดํฐ๋ฅผ 1024์ฐจ์ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ก ๋ณํํ๊ณ , ์๋ฏธ ๊ธฐ๋ฐ ๊ฒ์๊ณผ ์ ์ฌ๋ ๊ณ์ฐ์ ํตํด ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๊ฐ๋ฐ์๋ ์๋ฒ ๊ด๋ฆฌ๋ ์ธํ๋ผ ๊ณ ๋ฏผ ์์ด ๋ฐ์ดํฐ์ AI ๋ชจ๋ธ์ ์ฐ๊ฒฐ์ ์ง์คํ ์ ์์ผ๋ฉฐ, Pinecone์ ์๋ ํ์ฅ์ฑ๊ณผ ๋์ ์์ ์ฑ ๋๋ถ์ ๋๊ท๋ชจ ์๋น์ค ํ๊ฒฝ์์๋ ๋ฌธ์ ์์ด ์ด์์ด ๊ฐ๋ฅํฉ๋๋ค.
์ฆ, Pinecone์ ํ์ฉํ๋ฉด AI ๊ธฐ๋ฐ ์ถ์ฒ ์์คํ , ๋ฌธ์ ๊ฒ์, ์ฑ๋ด, ์ด๋ฏธ์ง ๊ฒ์ ๋ฑ ๋ค์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก ๊ตฌ์ถํ ์ ์์ผ๋ฉฐ, ์ฌ์ฉ์ ๊ฒฝํ๊ณผ ์๋น์ค ํ์ง์ ๋์์ ๋์ผ ์ ์์ต๋๋ค.
AI ํ๋ก์ ํธ๋ฅผ ์์ํ๋ ค๋ ๊ธฐ์ ๊ณผ ๊ฐ๋ฐ์๋ผ๋ฉด, Pinecone์ ๋ฐ๋์ ๊ณ ๋ คํด์ผ ํ ํ์ ์๋ฃจ์ ์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค.
์์ฃผ ๋ฌป๋ ์ง๋ฌธ (FAQ)
Q1. Pinecone์ด๋ ๋ฌด์์ด๋ฉฐ, ์ผ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ค๋ฅธ ์ ์ ๋ฌด์์ธ๊ฐ์?
Pinecone์ ๋ฐ์ดํฐ๋ฅผ ๊ณ ์ฐจ์ ๋ฒกํฐ๋ก ๋ณํํด ์ ์ฅํ๊ณ ๊ฒ์ํ ์ ์๋ ๊ด๋ฆฌํ AI ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๋ค. ์ผ๋ฐ SQL์ด๋ NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ํค-๊ฐ ์กฐํ์ ๊ฐ์ ์ด ์์ง๋ง, ์๋ฏธ ๊ธฐ๋ฐ ๊ฒ์์ด๋ ์ ์ฌ๋ ๊ณ์ฐ์๋ ํ๊ณ๊ฐ ์์ต๋๋ค. Pinecone์ ์ด๋ฏธ์ง ๊ฒ์, ๋ฌธ์ ๊ฒ์, ์ถ์ฒ ์์คํ , ์ฑ๋ด ๋ฑ AI ์ ํ๋ฆฌ์ผ์ด์ ์ ์ต์ ํ๋ ์ ์ฌ๋ ๊ธฐ๋ฐ ๊ฒ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
Q2. Pinecone์ ํ์ฉํด AI ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ค๋ฉด ์ด๋ค ๊ณผ์ ์ด ํ์ํ๊ฐ์?
Pinecone์ ํ์ฉํ๋ ค๋ฉด ํฌ๊ฒ ๋ค์๊ณผ ๊ฐ์ ๋จ๊ณ๊ฐ ํ์ํฉ๋๋ค. โ ํ์๊ฐ์ ํ ํ๋ก์ ํธ ์์ฑ ๋ฐ ๋ชจ๋ธ ์ ํ(์: multilingual-e5-large), โก API ํค ํ์ธ ๋ฐ ํ๊ฒฝ ์ค์ , โข ๋ธ๋ก๊ทธ ๋๋ ๋ฐ์ดํฐ ์์ค์์ URL ์์ง ๋ฐ HTML ์ฝํ ์ธ ์คํฌ๋ํ, โฃ ํ ์คํธ, ์ด๋ฏธ์ง ๋ฑ์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ก ๋ณํ, โค Pinecone REST API๋ฅผ ํตํด ๋ฒกํฐ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ๋ก๋ํฉ๋๋ค. ์ด๋ฅผ ํตํด AI ๊ธฐ๋ฐ ์๋ฏธ ๊ฒ์๊ณผ ์ถ์ฒ ์์คํ ๊ตฌ์ถ์ด ๊ฐ๋ฅํฉ๋๋ค.
Q3. Pinecone์ ์ฃผ์ ์ฅ์ ๊ณผ ๊ธฐ๋ฅ์ ๋ฌด์์ธ๊ฐ์?
Pinecone์ ์ฃผ์ ์ฅ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. โ ์ค์๊ฐ ๋ฒกํฐ ๊ฒ์: ๋๊ท๋ชจ ๋ฐ์ดํฐ์์๋ ๋น ๋ฅธ ๊ฒ์ ์๋ ์ ๊ณต, โก ์์ ๊ด๋ฆฌํ ์๋น์ค: ์๋ฒ ๊ด๋ฆฌ ์์ด ์๋ ํ์ฅ ๋ฐ ์ต์ ํ, โข AI ์ ํ๋ฆฌ์ผ์ด์ ํตํฉ: Python SDK, REST API, ๋ค์ํ ML ํ๋ ์์ํฌ ์ง์, โฃ ๋์ ์ ํ๋: ์ ์ฌ๋ ๊ธฐ๋ฐ ์ ํํ ๊ฒฐ๊ณผ ์ ๊ณต, โค ๋ฐ์ด๋ ํ์ฅ์ฑ: ์์ญ์ต ๊ฐ ๋ฒกํฐ ์ฒ๋ฆฌ ๊ฐ๋ฅ. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ๋ฐ์ดํฐ์ AI ๋ชจ๋ธ ์ฐ๊ฒฐ์ ์ง์คํ ์ ์์ต๋๋ค.
Q4. Pinecone์์ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ ๋ฌด์์ด๋ฉฐ, ์ ํ์ํ๊ฐ์?
์๋ฒ ๋ฉ ๋ฒกํฐ๋ ํ ์คํธ, ์ด๋ฏธ์ง, ์๋ฆฌ ๋ฑ์ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ์ปดํจํฐ๊ฐ ์ดํดํ ์ ์๋ ๊ณ ์ฐจ์ ์ซ์ ๋ฆฌ์คํธ๋ก ๋ณํํ ๊ฒ์ ๋๋ค. ์ฝ๊ฒ ๋งํ๋ฉด ๋จ์ด๋ ๋ฌธ์ฅ์ ์๋ฏธ๋ฅผ ๋ํ๋ด๋ ์ขํ๋ผ๊ณ ๋ณผ ์ ์์ต๋๋ค. Pinecone์ ์ด ์๋ฒ ๋ฉ ๋ฒกํฐ๋ฅผ ํ์ฉํด ์๋ฏธ ๊ธฐ๋ฐ ๊ฒ์๊ณผ ์ ์ฌ๋ ๊ณ์ฐ์ ์ํํ๋ฏ๋ก, ์ถ์ฒ ์์คํ , ๋ฌธ์ ๊ฒ์, ์ฑ๋ด ๋ฑ AI ์๋น์ค ๊ตฌ์ถ์ ํ์์ ์ ๋๋ค.
Q5. Pinecone์ ์ฌ์ฉํ๋ฉด AI ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์์ ์ด๋ค ์ด์ ์ด ์๋์?
Pinecone์ ๋ฐ์ดํฐ ์ ์ฅ, ์๋ฒ ๋ฉ ๊ด๋ฆฌ, ๊ฒ์ ๊ธฐ๋ฅ์ ํ ๊ณณ์์ ์ ๊ณตํด ๊ฐ๋ฐ ํจ์จ์ฑ์ ๋์ ๋๋ค. ์๋ฒ ๊ด๋ฆฌ ๋ถ๋ด ์์ด AI ๋ชจ๋ธ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ฒฐํ ์ ์๊ณ , ์ค์๊ฐ ์ ์ฌ๋ ๊ฒ์๊ณผ ๋์ ์ ํ๋๋ก ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์ต๋๋ค. ๋ํ ์๋ ํ์ฅ์ฑ๊ณผ ์์ ์ฑ ๋๋ถ์ ๋๊ท๋ชจ ์๋น์ค ํ๊ฒฝ์์๋ ๋ฌธ์ ์์ด ์ด์ํ ์ ์์ต๋๋ค.
https://everydayhub.tistory.com/1161
https://everydayhub.tistory.com/1160