SnackRAG๋ ์ํ ๋ฐ ์ฒจ๊ฐ๋ฌผ ์ ๋ณด๋ฅผ OpenAI GPT-4o๋ฅผ ์ด์ฉํด ์์ฐ์ด๋ก ์ง๋ฌธํ๊ณ , LangChain ๊ธฐ๋ฐ RAG(Retrieval-Augmented Generation) ๊ตฌ์กฐ๋ฅผ ํตํด ์ค์๊ฐ์ผ๋ก ๋ต๋ณํ๋ FastAPI + Chainlit ํ๋ก์ ํธ์ ๋๋ค.
- LLM: OpenAI GPT-4o via LangChain
- Vector DB: PostgreSQL + PGVector
- Embedding:
text-embedding-3-small - Backend: FastAPI + SQLModel
- Frontend: Chainlit (React ๊ธฐ๋ฐ LLM ์ฑํ UI)
- Streaming: SSE (Server-Sent Events)
# Docker ์คํ
docker-compose up --build
# Chainlit ์คํ (ํฌํธ 8502)
chainlit run chainlit/main.py --port 8502| Endpoint | ์ค๋ช |
|---|---|
/api/v1/snacks/test/rag |
์ฟผ๋ฆฌ ๊ธฐ๋ฐ GPT-4 ์๋ต ์์ฑ |
/api/v1/snacks/sse |
SSE ๊ธฐ๋ฐ ์คํธ๋ฆฌ๋ฐ ์๋ต |
/api/v1/snacks/test/rag/context-only |
GPT ํธ์ถ ์์ด ์ ์ฌ ๋ฌธ์๋ง ๊ฒ์ |
- "MSG๊ฐ ๋ค์ด๊ฐ ๊ณผ์๋ฅผ ์๋ ค์ค"
- "์์ด๋ค์๊ฒ ์์ ํ ๊ณผ์๋ฅผ ์ถ์ฒํด์ค"
- "์ด ์ฒจ๊ฐ๋ฌผ์ ์ด๋ค ์ฉ๋์ธ๊ฐ์?"
app/- ๋ฐฑ์๋ ์ ์ฒด ๊ตฌ์ฑapp/repository/- PGVector ๋ฐ DB ์ก์ธ์คapp/services/- RAG ๋ก์ง, GPT ํธ์ถchainlit/- ํ๋ก ํธ์๋ UIsql/- ๋ฐ์ดํฐ ์ด๊ธฐํ ๋ฐ ์๋ฒ ๋ฉ ๋์
SnackRAG๋ Retrieval-Augmented Generation ๊ตฌ์กฐ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํฉ๋๋ค:
[User Query]
โ
[Retriever] PGVector๋ก ์ ์ฌ ๋ฌธ์ ๊ฒ์
โ
[Context Builder] ๊ด๋ จ ์ ๋ณด ๋ฌธ์์ด ๊ตฌ์ฑ
โ
[Generator] LangChain ๊ธฐ๋ฐ GPT-4o ํธ์ถ
โ
[Streaming Response] Chainlit ๋๋ FastAPI๋ฅผ ํตํด SSE ์ ์ก
- ๋ฌธ์ ์๋ฒ ๋ฉ:
OpenAIEmbeddings(text-embedding-3-small) - LLM ์๋ต:
ChatOpenAI(model="gpt-4o") - ๋ฒกํฐ ๊ฒ์: cosine ์ ์ฌ๋ ๊ธฐ๋ฐ
- ๋ฌธ๋งฅ ์ฃผ์
: ํ๋กฌํํธ ํ
ํ๋ฆฟ ๋ด
{context}ํ์ฉ
| ํ ์ด๋ธ | ์ค๋ช |
|---|---|
snack |
๊ณผ์ ๊ธฐ๋ณธ ์ ๋ณด |
snack_additive |
์ฒจ๊ฐ๋ฌผ ์ ๋ณด ๋ฐ ๋ฑ๊ธ |
snack_item |
๊ณผ์๋ณ ์์ ์ฑ๋ถ |
map_snack_item_additive |
๊ณผ์-์ฒจ๊ฐ๋ฌผ ๊ด๊ณ N:M ๋งคํ |
pg_embedding |
๋ฒกํฐํ๋ ๋ฌธ์์ ๋ฉํ๋ฐ์ดํฐ ์ ์ฅ์ |
pytest tests/๋จ์ ํ ์คํธ ์์:
- RAG ์๋ต์ด ์ ์์ ์ผ๋ก ์์ฑ๋๋์ง ํ์ธ
- ์๋ต์ด ๋ฌธ์์ด์ด๊ณ , ํ์ ํค์๋๋ฅผ ํฌํจํ๋์ง ๊ฒ์ฆ
- โ LangSmith ์ถ์ ํ์ฑํ
- โ ๋ฌธ์ relevance ํ๊ฐ ๋ชจ๋ธ ๊ณ ๋ํ (re-ranking)
- โ ์ฌ์ฉ์ ํ๋กํ์ผ ๊ธฐ๋ฐ ์ถ์ฒ ์์คํ ์ ๋ชฉ
- โ ํ๋กฌํํธ ๋ค์ํ ๋ฐ ์์ ์ฑ ์ฒดํฌ ๋ชจ๋ธ ์ถ๊ฐ
์ด ํ๋ก์ ํธ๋ Retrieval-Augmented Generation(RAG) ๊ตฌ์กฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ ๋จ๊ณ๋ฅผ ๋ฐ๋ฆ ๋๋ค:
Chainlit ๋๋ FastAPI API๋ฅผ ํตํด ์ฌ์ฉ์ ์์ฐ์ด ์ง๋ฌธ(query)์ ์ ๋ ฅ๋ฐ์ต๋๋ค.
app/repository/pgvector_repository.py์์ PGVector๋ฅผ ์ด์ฉํด ์ฌ์ฉ์ ์ง๋ฌธ์ ์๋ฒ ๋ฉํ๊ณ ,
cosine similarity ๊ธฐ๋ฐ์ผ๋ก ๊ด๋ จ ๋ฌธ์๋ฅผ ๊ฒ์ํฉ๋๋ค.
- ์ฌ์ฉ ์๋ฒ ๋ฉ ๋ชจ๋ธ:
text-embedding-3-small - ๋ฒกํฐ ์ ์ฅ์: PostgreSQL + PGVector
๊ฒ์๋ ๋ฌธ์ ๋ฆฌ์คํธ๋ app/services/rag_service_test.py์์ LangChain์ Document ํ์์ผ๋ก ์ฒ๋ฆฌ๋๊ณ ,
ํ๋กฌํํธ ๋ด {context} ์๋ฆฌ์ ์ฝ์
๋ฉ๋๋ค.
context = "\n\n".join([
f"๋ฌธ์ {i + 1}:\n{doc.page_content}\n๋ฉํ๋ฐ์ดํฐ: {doc.metadata}"
])LangChain์ ChatOpenAI(model="gpt-4o")๋ฅผ ์ฌ์ฉํด ์์ฑ๋ ํ๋กฌํํธ๋ฅผ GPT์๊ฒ ์ ๋ฌํฉ๋๋ค.
์๋ต์ ์ค์๊ฐ์ผ๋ก ํ ํฐ ๋จ์๋ก ์คํธ๋ฆฌ๋ฐ๋ฉ๋๋ค (StreamingResponse).
/snacks/sse ๋๋ /snacks/test/rag/context ์๋ํฌ์ธํธ๋ฅผ ํตํด ์ฌ์ฉ์์๊ฒ ์ค์๊ฐ ์ ์ก๋ฉ๋๋ค.
Chainlit์์๋ httpx.AsyncClient๋ฅผ ํตํด SSE๋ฅผ ์์ ํ๊ณ ์ถ๋ ฅํฉ๋๋ค.
rag_service_test.py: RAG์ ํต์ฌ ๊ตฌํ ์์น. ๋ฌธ์ ๊ฒ์ โ ๋ฌธ๋งฅ ๊ตฌ์ฑ โ GPT ์๋ต ์์ฑ.pgvector_service.py: LLM ๊ธฐ๋ฐ ๋ฌธ์ relevance ์ฌ์ ์ (LLM-based re-ranking).rag_service.py: ๊ฒ์๋ ๋ฌธ์ + DB ์์ธ์ ๋ณด๋ฅผ ํจ๊ป ์๋ต (ํผํฉํ ์๋ต์ฉ).generized_metadate_by_ID.py: LLM์ ํตํด ๋ฒกํฐ ๋ฌธ์์ ๋ฉํ๋ฐ์ดํฐ ์์ฑ ์๋ํ.
routes/snacks.py:/snacks/test/rag,/snacks/sse๋ฑ API ์๋ํฌ์ธํธ ์ ์.dto/models.py: RAG ์๋ต ํ์ ์ ์ (SnackContextPayload,SnackRagDocument๋ฑ).deps.py: DB ์ธ์ ๋ฐ ์์กด์ฑ ์ฃผ์ ์ ์.
agent_tools.py: GPT-4o ๋ชจ๋ธ, OpenAI ์๋ฒ ๋ฉ ๋ชจ๋ธ, PGVector ๋ฒกํฐ์คํ ์ด ์ ์.prompt.py: GPT ์ ๋ ฅ์ฉ ํ๋กฌํํธ ํ ํ๋ฆฟ ์ ์ (SEARCH_SELECT_INSTRUCTIONS,RAG_PROMPT).db.py/config.py: DB ์ฐ๊ฒฐ ์ค์ ๋ฐ ํ๊ฒฝ ๋ณ์ ๋ก๋ฉ ๋ด๋น.
์ด ํ๋ก์ ํธ๋ ๋จ์ ๋ฌธ์ ๊ธฐ๋ฐ ๊ฒ์์ ๋์ด์, ๊ตฌ์กฐํ๋ ๊ณผ์/์ฒจ๊ฐ๋ฌผ ๋ฐ์ดํฐ๋ฅผ ๋ฒกํฐํํ์ฌ ์ ์ฅํ๊ณ , LangChain + GPT-4o๋ก ์๋ฏธ ์๋ ์ง์์๋ต์ ์ ๊ณตํ๋ ์ ํ + ์์ฑํ ํตํฉํ RAG ์์คํ ์ ๋๋ค.

