์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
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 |
- SQL
- DFS
- LIS
- ํ์ด์ฌ
- skala1๊ธฐ
- ์์ํ์
- ๋ฐ์ดํฐ๋ฒ ์ด์ค
- skala
- ๋๋น์ฐ์ ํ์
- SK
- ๋ณํฉ์ ๋ ฌ
- ๊ทธ๋ํํ์
- ๋์ ๊ณํ๋ฒ
- ๊น์ด์ฐ์ ํ์
- ๊ทธ๋ํ
- ๋์ ํฉ
- ํฐ์คํ ๋ฆฌ์ฑ๋ฆฐ์ง
- db
- ๋ฐฑ์ค
- ๊ตฌํ
- ํ๋ก๊ทธ๋๋จธ์ค
- ์๊ณ ๋ฆฌ์ฆ
- ๋ค์ด๋๋ฏนํ๋ก๊ทธ๋๋ฐ
- BFS
- ์ ๋ ฌ
- DP
- ๊ทธ๋ฆฌ๋
- ์ค๋ธ์
- ๋จธ์ง์ํธ
- ์ํ
- Today
- Total
๐๐ญ๐ฐ๐ธ ๐ฃ๐ถ๐ต ๐ด๐ต๐ฆ๐ข๐ฅ๐บ
[LLM] LangChain๊ณผ RAG์ ํ์ฉํ ๊ฐ๋จํ LLM ๊ธฐ๋ฐ ์ฑ๋ด ๊ตฌํํ๊ธฐ ๋ณธ๋ฌธ
[LLM] LangChain๊ณผ RAG์ ํ์ฉํ ๊ฐ๋จํ LLM ๊ธฐ๋ฐ ์ฑ๋ด ๊ตฌํํ๊ธฐ
.23 2025. 3. 26. 00:55LangChain?
LangChain์ ๋ํ ์ธ์ด ๋ชจ๋ธ(LLM)์ ํจ์จ์ ์ผ๋ก ํ์ฉํ ์ ์๋๋ก ์ง์ํ๋ ํ๋ ์์ํฌ๋ก, AI ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ๋ณด๋ค ์ฒด๊ณ์ ์ด๊ณ ์ ์ฐํ๊ฒ ์ค๊ณํ ์ ์๋๋ก ๋๋๋ค. LangChain์ ๋ค์ํ LLM๊ณผ ์ฐ๋ํ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด AI ๋ชจ๋ธ์ ์ฝ๊ฒ ํธ์ถํ๊ณ , ๋ํํ ์๋ต์ ์์ฑํ๊ฑฐ๋ ๋ฐ์ดํฐ ๋ถ์ ๋ฐ ์๋ํ ํ๋ก์ธ์ค๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ํ์ฉํ ์ ์๋ค.
- ChatGPT
LangChain์ ๋ค์ํ LLM ๋ชจ๋ธ์ ์์ฝ๊ฒ ํธ์ถํ๊ณ ์ฌ์ฉํ ์ ์๊ฒ ์ง์ํ๋ค. ํนํ OpenAI API๋ฅผ ์ง์ ํ์ฉํ์ฌ ํ๋กฌํํธ๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ์๋ณด๋ค LangChain์ ํ์ฉํ๋ฉด LLM์ ํ์ฉํ๋ ์ธก๋ฉด์์ ํ์ฅ์ฑ๋ ์ข๊ณ ์ฝ๋ ํ์ฉ๋ ํจ์ฌ ๊ฐํธํ๋ค.

์ค์ ์ฝ๋๋ฅผ ๋๊ณ ๋น๊ตํด๋ ์๋์ ๊ฐ์ด ๋ ์ง๊ด์ ์ผ๋ก ์ดํดํ๊ธฐ ์ฌ์ด ํํ์ด๊ณ , LLM ๋ชจ๋ธ์ด ๊ฐ์ ธ์ค๋ ์๋ต ์ญ์ ๋ณ๋ค๋ฅธ ํ์ฑ์ด ํ์์๊ธฐ ๋๋ฌธ์ ์๋ต ํ์ฉ์ด ํจ์ฌ ์์ ๋กญ๋ค.


API ์ง์ ํ์ฉ ๋ฐฉ์์ result๋ response๋ก๋ถํฐ ๊ตฌ๊ตฌ์ ์ (์ง๊ธ์ deprecated๋ ํ์) ํ์ฑํด์์ผ ํ๋ ๋ฐ๋ฉด,
LangChain์ result๋ invoke ํ๋ฒ์ด๋ฉด ๋๋๋ค. ์ถ๊ฐ์ ์ผ๋ก, ๋ณ์ ํ์ฉ ์ธก๋ฉด์์๋ ์์ ๋กญ๊ธฐ ๋๋ฌธ์ ๋ณธ ์ฑ๋ด ๊ตฌํ์๋ LangChain์ ์ต๋ํ ํ์ฉํ๊ณ ์ ํ๋ค.
์ฑ๋ด ์ค๊ณ ๋ชฉ์
์ฒญ์๋ ๋์ ๊ธ์ต ๊ต์ก์ฉ ์ฑ๋ด์ ์ค๊ณํ๊ธฐ ์ํด ๊ต์ก์๋ฃ๊ฐ ๋ ์ ์๋ ์ต์ ๋ด์ค/๊ต์ก์๋ฃ ๋ฑ์ ๋ฒกํฐDB(chroma db ์ฌ์ฉ)์ ์ฐ์ ์ ์ฅํด๋๊ณ , ์ฑ๋ด์์ ์ง์๋ฌธ์ ๋์ง๋ฉด
1. ์ฟผ๋ฆฌ์ ์ ์ฅ๋ ์ปจํ ์ธ ๋ค ๋ณ ์ ํ๊ณผ์ ์ ํฉ์ฑ์ ํ์ ํ๋ค.
2. ๊ฐ์ฅ ์ ํฉํ DB์์ ์๋ฃ๋ฅผ ๊ฐ์ ธ์ ๋ต๋ณํ๋ค.
์ ๋ฐฉ์์ผ๋ก ์ค๊ณํ์๋ค.
์๋ฃ๋ฅผ ํฌ๋กค๋งํ๋ ์ฝ๋๋ ๋ด์ค/์ฆ๊ถ๋ณด๊ณ ์/๊ต์ก์๋ฃ ๋ณ ๊ฐ๊ฐ ๋ค๋ฅธ ์ฌ๋๋ค์ด ์์ฑํ๊ธฐ ๋๋ฌธ์ ํธ์์ ๊ฐ๋ณ์ ์ธ DB๋ฅผ ๊ตฌ์ถํ๊ฒ ๋์๊ณ , ํตํฉํด์ ์๋ฃ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ณผ์ ์์ ์ฒ์์๋ ์ฟผ๋ฆฌ์ ์ฐ๊ด์ฑ์๋ ๋ชจ๋ ์๋ฃ๋ฅผ ๊ฒ์ํด์ ๊ฐ์ ธ์ค๊ฒ ํด์ผ๊ฒ ๋ค! ํ๋๋ฐ ํ ํฐ ๊ธธ์ด๊ฐ ํญ๋ฐํด๋ฒ๋ฆฌ๋ ์ด์๋ก ์ธํด.. ใ ใ
๋ถ๊ฐํผํ๊ฒ ์ ๋ ฅ์ฟผ๋ฆฌ๋ก๋ถํฐ LLM ๋ชจ๋ธ์ด ์ฐ๊ด์ฑ์ ํ์ ํ๊ฒ ์ค๊ณํด์ ์ด๋ DB๋ฅผ ์ฐพ์์ง ๋ถ๋ฅ๋ถํฐ ํ๊ณ ,
๋ถ๋ฅ๋ ๊ฒฐ๊ณผ์ DB๋ก๋ถํฐ ์๋ฃ๋ฅผ ๊ฐ์ ธ์ ๋ตํ ์ ์๋ ๋ฐฉํฅ์ฑ์ผ๋ก ์ฝ๋๋ฅผ ์ค๊ณํ๋ค.
UI ์ฐ๊ฒฐํ๊ธฐ ์ด์ ๋จ๊ณ๊น์ง ๊ตฌํ์ ๋งก์์, ๋๋ ์ฝ์๋ก ํ ์คํธ๋ฅผ ์งํํ๋ค.
์ฝ๋
๋ชจ๋ํ ํด๋ฌ๋ผ๊ณ ๋ถํ์ ๋ฐ์์ ChatBot class๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ ๋ชจ๋ ์๋ ํจ์๋ฅผ ๋๋ ค๋ฃ์๋ค.
1. query๋ก๋ถํฐ ์ฐ๊ด๋ DB์ ํ ๋ถ๋ฅ
from langchain_core.prompts import ChatPromptTemplate
def classify_query(self, query: str) -> str:
prompt = ChatPromptTemplate.from_messages([
('system',
"""
๋ค์ ์ง์๋ฅผ ์ฝ๊ณ ๊ด๋ จ๋ ์ ๋ณด ์ ํ์ ํ ๋จ์ด๋ก ์ถ๋ ฅํ์ธ์.
๊ฐ๋ฅํ ๊ฐ: "edu", "news", "report", "all", "nothing".
- edu: ๊ฒฝ์ ์ฉ์ด๋ ๊ฐ๋
๋ฑ ๋ฐฐ๊ฒฝ์ง์
- news: ์ต์ ์ฐ์
๋ํฅ
- report: ๊ธฐ์
๋ถ์, ์ฌ๋ฌด ์ ๋ณด ๋ฑ
- all/nothing: ๋ฒ์ฃผ ๊ตฌ๋ถ์ด ๋ถ๋ช
ํํ ๋
"nothing"๊ณผ "all"์ ์ต์ํ์ผ๋ก ์ฌ์ฉํ๊ณ ,
๊ฐ๋ฅํ๋ฉด edu, news, report ์ค ํ๋๋ก ๋ถ๋ฅํ์ธ์.
"""),
('user', f"์ง์: {query}\n๋ต๋ณ:")
])
chain = prompt | self.llm
classification = chain.invoke({"query": query}).content.strip().lower()
return classification
์๋ ๋ฐฉ์
๊ฐ๊ฐ ๋ชจ์ผ๋ ์๋ฃ๋ก๋ถํฐ ์ป์ ์ ์๋ ์ ๋ณด๋ค์ ์ฑ๊ฒฉ์ด ๋ค ๋ค๋ฅด๊ธฐ ๋๋ฌธ์, ์ ํ์ ๋ถ๋ฅํ๋ ํจ์๋ฅผ ์ ์ํ๋ค.
๋ฌผ๋ก OpenAI API๋ฅผ ์ต๋ํ ํ์ฉํ๊ธฐ ์ํด ์ฝ์ฌ์ธ ์ ์ฌ๋ ํ๋ณ๋ฒ์ด๋ BERT๊ฐ์ classification ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๋์ ์ง์์ ๊ฐ๋ณ์ฑ์ ์๊ฐํ์ฌ ๊ฐ์ฅ ํ์คํ LLM ๋ชจ๋ธ์๊ฒ ๋งก๊ฒผ๋ค.
์ฉ์ด ์ค๋ช / ๋ฐฐ๊ฒฝ์ง์๊ณผ ๊ฐ์ด ๊ฐ๋ ์ ์ธ ๋ถ๋ถ์ ๋ํ ์ฑ๊ฒฉ์ด ๊ฐํ๋ฉด ๊ต์ก์๋ฃ์์,
์ฐ์ / ๊ธฐ์ ๋ค์ '์ต์ ' ๋ํฅ์ ํ์ ํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ ๋ด์ค๊ธฐ์ฌ์์,
๊ทธ ์ธ ๊ธฐ์ ๋ค์ ๋๋ต์ ์ธ ํ์ฌ ์ ๋ณด๋ฅผ ํ์ ํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ ์ฆ๊ถ๋ณด๊ณ ์์์ ์๋ฃ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํ์๋ค.
๊ฒฝ์ ๊ด๋ จ์ด๋ ๋ฑ ํ๋๋ก ๋ตํ๊ธฐ ๋ชจํธํ ์ฟผ๋ฆฌ๊ฐ ๋ค์ด์ฌ ๊ฒฝ์ฐ all, ๊ทธ ์ธ ์ฑ๊ฒฉ์ ์ฟผ๋ฆฌ๊ฐ ๋ค์ด์ฌ ๊ฒฝ์ฐ
1. ์ผ๋จ ๊ฒฝ์ ๊ด๋ จ ์ฉ์ด์ธ์ง ์๊ฐํด๋ณด๊ณ
2. ์ ์๋๋ค ์ถ์ผ๋ฉด ๋๋ต์ ํ์ง ์๊ฒ
ํ๋ ๊ท์น์ ๋ง๋ค๊ธฐ ์ํด nothing๋ ํจ๊ป ์ค๊ณํด์คฌ๋ค. ๊ทธ๋ฆฌ๊ณ ์ต๋ํ ๋๊ฐ์ label๋ก ๋ถ๋ฅํ๋ ์ผ์ด ์๋๋ก ํ๋กฌํํธ์ ๋ช ์ํ๋ค.
'SK', '์ผ์ ' ์ฒ๋ผ ๊ธฐ์ ๋ช ๋ง ์น๋ฉด ์ฆ๊ถ๋ณด๊ณ ์์์ ์ ๋ณด๋ฅผ ์ฐพ์์ฃผ๊ณ ,
'SK ์ต๊ทผ ํธ์ฌ', 'ํ์จ ๋ณ๋ ์ถ์ด' ์ด๋ฐ '์ต์ /ํํฉ'๊ณผ ๊ฐ์ ํ๋ฆ์ ์ธ ํค์๋๋ฅผ ๊ฐ์กฐํ ์ฟผ๋ฆฌ๊ฐ ๋ค์ด์ค๋ฉด ๋ด์ค๊ธฐ์ฌ๋ก ์ ๋ถ๋ฅํด์ค๋ค.
์๋ ์๋ฆฌ
prompt = ChatPromptTemplate.from_messages([
('system',
"""
~~~~
"""),
('user', f"์ง์: {query}\n๋ต๋ณ:")
])
ChatPromptTemplate.from_messages : ์ผ๋ฐ์ ์ผ๋ก LangChain์ ํ์ฉํ ๋ํํ prompt๋ฅผ ์์ฑํ ๋ ๋ง์ด ์ฐ๋ ํจ์ ์ค ํ๋์ด๋ค. ๋ฐฐ์ดํ์ผ๋ก message๋ฅผ ์ ๋ ฅํ ์ ์๋๋ฐ, ๊ฐ ๋ฉ์์ง๋ ์ญํ (role)๊ณผ ๋ด์ฉ(content)์ผ๋ก ๊ตฌ์ฑ๋๋ค. ๊ตฌ์ฑ๋ง ์งํจ๋ค๋ฉด ์์ ๋ฉ์์ง ์์ฑ ๋ฐฉ์๋ ๋น๊ต์ ๋ค์ํ ํธ์ด๋ค.
์์ ์์์ฒ๋ผ ํํ๋ค์ ๋ฐฐ์ด ํํ๋ก "system", "user" ๋ฉ์์ง๋ฅผ ๋ฐ๋ก๋ฐ๋ก ๋๋ ์์ฑํด์ฃผ๋ ๋ฐฉ๋ฒ๋ ์๊ณ ,
from langchain.schema.messages import SystemMessage, HumanMessage, AIMessage
prompt_messages = [
SystemMessage(content=
f"""
์์คํ
๋ฉ์์ง~~~
"""),
HumanMessage(content=
f"""
์ธ๊ฐ๋ฉ์์ง~~~~
""")
]
SystemMessage, HumanMessage๋ฅผ ๋ถ๋ฌ์ ๊ฐ ์ญํ ๋ณ ๋ฉ์์ง๋ฅผ ์ ์ํ๋ ๋ฐฉ๋ฒ๋ ์กด์ฌํ๋ค. ๋ชฉ์ ์ ๋ง๊ฒ, ํธํ๋๋ก ์ฌ์ฉํ๋ฉด ๋๋ค.
๋ํ f-string์ ํ์ฉํ์ฌ ํ๋กฌํํธ ์์ฒด์ ๋ณ์ ์ ์ธ์ ํด์ฃผ๊ณ , ๋์ค์ invoke๋ฅผ ํตํด chain์ ์คํํ ๋ ๋ณ์์ ๋ํ ๊ฐ์ ๋ฐ์ธ๋ฉ์ด ๊ฐ๋ฅํ๋ฐ, ์ด๋ฅผ ํตํด ํ๋กฌํํธ๋ฅผ ์์ ๋กญ๊ฒ ๋ณํํ๋ฉฐ ํ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ํนํ ์ฑ๋ด๊ฐ์ ๋์ ์์คํ ์ ์ค๊ณํ ๋ ์ ์ฉํ๊ฒ ์ธ ์ ์๋ค.
chain = prompt | self.llm
classification = chain.invoke({"query": query}).content.strip().lower()
์ดํ chain์ด๋ผ๋ ๋ณ์์ ํ๋กฌํํธ์ ๋ฏธ๋ฆฌ ์ ์ธํ๋ llm ๋ชจ๋ธ(gpt-4o ์ฌ์ฉ)์ ๋นํธ์ฐ์ฐ์ '|'๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ ์ฒด์ด๋ ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ LangChain ๊ตฌ์กฐ๋ฅผ ํ ๋นํด์ค๋ค.
์ฌ๊ธฐ์ ๋ฑ์ฅํ๋ ์ฒด์ด๋ ์ฐ์ฐ์ '|'๋ LCEL(LangChain Expression Language)์์ ๋ฑ์ฅํ๋ ๊ฐ๋ ์ผ๋ก, ์๋ก ๋ค๋ฅธ ๊ตฌ์ฑ์์์ธ ํ๋กฌํํธ์ LLM ๋ชจ๋ธ๊ณผ Parser(์ฌ๊ธฐ์ ์ฌ์ฉํ์ง ์์์ง๋ง ์์ฃผ ์ฌ์ฉํจ), ์๋๋ฉด ๋๋ค๋ฅธ LLM ๋ชจ๋ธ ๋ฑ๋ฑ์ ์ฎ์ด ๊ตฌ์ฑ ์์๋ค์ ์ฐ๊ฒฐํ๋ ์ญํ ์ ํ๋ค.
์ผ๋ฐ์ ์ผ๋ก ํ๋กฌํํธ | ๋ชจ๋ธ | ํ์ ์ ๊ฐ์ด ์ ์ธํ๋๋ฐ, ์ ํด์ง ๊ท์น์ ์๋์ง๋ง ๋ฐ์ดํฐ์ ์ ๋ฌ ํ๋ฆ์ ํํํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ณดํต ์์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ค๊ณํ๋ค.
chain1 = prompt1 | llm | JsonOutputParser()
chain2 =(
chain1 | prompt2 | llm | StrOutputParser()
)
๋ฐ๋์ ์ ํ์์ ์ง์ผ์ผ๋๋ค๋ ๊ฒ์ด ์๋ ๋ฟ, ์์ ๊ฐ์ด ๋ ๋ณตํฉ์ ์ธ ๋ฐฉ์์ผ๋ก ์ ์ํ์ฌ ๋ค์ํ ์ธ๊ณต์ง๋ฅ ๋ชจ๋ธ๋ค์ ํ์ฅ ์ฌ์ฉํ ์ ์๋ค!
prompt ๋ด์ {query} ๋ผ๋ ๋ณ์๊ฐ ์์๋๋ฐ, invokeํ ๋ ์์ dictionaryํ ์ธ์๋ก ๋๊ฒจ์ฃผ๊ธฐ๋ง ํ๋ฉด ๊ฐ๋จํ๊ฒ ๋ค์ํ ์ฟผ๋ฆฌ์ ๋ํด ๋ถ๋ฅ๊ฐ ๊ฐ๋ฅํ๋ค.
2. ๋ถ๋ฅ๋ ์ง์๋ฌธ๊ณผ DB ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ต๋ณ ์์ฑ
def generate_answer(self, query: str, classification: str, context: str) -> str:
# ๋ถ๋ฅ๊ฐ nothing์ผ ๊ฒฝ์ฐ: ๊ฒฝ์ ์ฌ๋ถ ํ๋จ
if classification == "nothing":
prompt = ChatPromptTemplate.from_messages([
SystemMessage(
"""
๋๋ ์ฃผ์ ๊ต์ก ๋ฐ ์ข
๋ชฉ ์ถ์ฒ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์ฒญ์๋
๋ง์ถค ๊ฒฝ์ ๊ต์ก ์ฑ๋ด์ด์ผ.
์ฌ์ฉ์์ ์ง์๊ฐ ๊ฒฝ์ ๊ด๋ จ์ด๋ฉด ์ฝ๊ฒ ์ค๋ช
ํด์ฃผ๊ณ , ์๋๋ผ๋ฉด '๊ฒฝ์ ์ ๊ด๋ จ๋ ์ง๋ฌธ์ ์
๋ ฅํด์ฃผ์ธ์!' ๋ผ๊ณ ๋ต๋ณํด.
๋งํฌ๋ ์กด๋๋ง์ ์ ์งํด์ค.
"""
),
HumanMessage(f"์ง์: {query}\n๋ต๋ณ:")
])
else:
prompt = ChatPromptTemplate.from_messages([
SystemMessage(
f"""
๋๋ ์ฃผ์ ๊ต์ก ๋ฐ ์ถ์ฒ ์ข
๋ชฉ์ ๊ดํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์ฒญ์๋
๋ง์ถค ๊ฒฝ์ ๊ต์ก ์ฑ๋ด์ด์ผ.
์๋๋ ์ฐธ๊ณ ํ ์ ์๋ ๋ฌธ์ ์ ๋ณด์ผ:\n\n{context}
์ ์ ๋ณด๋ฅผ ์ฐธ๊ณ ํ์ฌ ์ฌ์ฉ์์ ์ง์์ ๋ํด ์ฝ๊ฒ ์ค๋ช
ํด์ค.
ํนํ ๋ด์ค ๊ธฐ๋ฐ์ด๋ผ๋ฉด ์ต๊ทผ ๋ํฅ์ ํฌํจํด์ค.
ํ์ํ๋ค๋ฉด ์ถ์ฒ ์ข
๋ชฉ๋ ํจ๊ป ์๋ ค์ค.
๋งํฌ๋ ์กด๋๋ง์ ์ ์งํด์ค.
"""
),
HumanMessage(f"์ง์: {query}\n๋ต๋ณ:")
])
chain = prompt | self.llm
return chain.invoke({"query": query, "combined_info": context}).content
์๋ ๋ฐฉ์
์ฟผ๋ฆฌ ๋ถ๋ฅ ๊ฒฐ๊ณผ๊ฐ nothing์ผ ๊ฒฝ์ฐ, ๊ฒฝ์ ๊ด๋ จ์ผ๋ก ๋ตํ ์ ์๋์ง ๊ฒํ ํ๊ณ ์์์ ์ปทํ ์ ์๊ฒ ๋จผ์ ๋ถ๊ธฐํ๋ค.
๊ทธ ์ธ์ ๊ฒฐ๊ณผ์ ๋ํด์๋, prompt์ ๋ฒกํฐDB์์ ์ฐพ์ ๊ฒฐ๊ณผ๋ฅผ {context} ๋ณ์์ ๋ด์ ์ฐธ๊ณ ์๋ฃ๋ก ํ์ฉํ์ฌ RAG(๊ฒ์ ์ฆ๊ฐ ์์ฑ)๋ฅผ ๊ฐ๋ฅํ๊ฒ ์ค๊ณํ๋ค.
์ฌ์ค ํด๋น ์ฝ๋๋ฅผ ์์ฑํ ๋ LangChain์ ์ข ์ต์ํด์ง๊ณ ์ถ์ด์ ์ด์ ์ ์ฟผ๋ฆฌ ๋ถ๋ฅ๊ธฐ์์๋ ์์คํ ๊ณผ ์ ์ ๋ฉ์์ง๋ฅผ ์์ฑํ ๋ ํํ๋ก ์ ์ํ๊ณ , ์ฌ๊ธฐ์๋ SystemMessage์ HumanMessage๋ก ์์ฑํ๋ค.
๋ญ ์จ๋ ๋๋ค ๋๊ฐ์ด ์๋ํ๋๊น ํธํ๊ฒ ์ฐ๋ฉด ๋๋ค.
์ด์ ๊ณผ ๋๊ฐ์ด chain์ผ๋ก ์ฎ์ด๋ด invokeํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ฝ๋ ์๋ ์๋ฆฌ๋ ํจ์คํ๋ค.
์คํ ๊ฒฐ๊ณผ

์ฑ๋ด์ ๋ชฉ์ ์ ์ ํฉํ ํค์๋๋ฅผ ์ ๋ ฅํ๋ค๋ฉด, ์์ ๊ฐ์ด ์๊ฐ๋ณด๋ค ๋ ๊ด์ฐฎ์ ๋ต๋ณ์ ๊ฐ์ ธ์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ต์ ๋ด์ค๋ฅผ ํฌ๋กค๋งํ๊ธฐ ๋๋ฌธ์, ์ฑ์งํผํฐ์ ๋ฐ๋ก ์ง๋ฌธํ๋ฉด ๋์ฌ ์ ์๋ ๋ด์ฉ๊น์ง ๋ต์ผ๋ก ๊ฐ์ ธ์จ๋ค.
์์งํ ๋ฐ์ดํฐ์ ํ๋ฆฌํฐ๊ฐ ๋ต์์ ๊ฐ์ฅ ์ค์ํ ์ํฅ์ ๋ผ์น๊ธฐ ๋๋ฌธ์, ์ ์ ํ ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ ์ ์ ์ ํ๋ ๊ฒ์ด ์ ์ผ ์ค์ํ๋ค.

๋ํ, ์์ ๋ฏธ๋ฆฌ ์ฒ๋ฆฌํด์ค ์ฝ๋ ๋๋ถ์ ์ด์ํ๊ฑธ ์ง๋ฌธํด๋ ์ด๋ ๊ฒ ๋จน๊ธ๋ ์ํ๋ค.
๋ง์กฑ์ค๋ฝ๋ค. ๐
์ ์ฒด ์ฝ๋
import os
import openai
from dotenv import load_dotenv
from langchain_chroma import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain_openai import ChatOpenAI
from langchain.schema.messages import SystemMessage, HumanMessage
from langchain_core.prompts import ChatPromptTemplate
class ChatBot:
def __init__(self):
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
os.environ["OPEN_API_KEY"] = openai.api_key # ํน์ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋๋น
self.llm = ChatOpenAI(model_name="gpt-4o", temperature=0.2, max_tokens=1024)
# ๊ฐ Chroma DB ๋ก๋
self.edu_db = self.load_vector_store("chroma_edu_db")
self.news_db = self.load_vector_store("chroma_news_db")
self.report_db = self.load_vector_store("chroma_report_db")
def load_vector_store(self, persist_directory: str) -> Chroma:
"""์ง์ ๋ ๋๋ ํ ๋ฆฌ์์ Chroma ๋ฒกํฐ์คํ ์ด๋ฅผ ๋ก๋ํฉ๋๋ค."""
return Chroma(
persist_directory=persist_directory,
embedding_function=OpenAIEmbeddings()
)
def classify_query(self, query: str) -> str:
"""์ฌ์ฉ์ ์ง์๋ฅผ edu, news, report ์ค ํ๋๋ก ๋ถ๋ฅํฉ๋๋ค."""
prompt = ChatPromptTemplate.from_messages([
('system',
"""
๋ค์ ์ง์๋ฅผ ์ฝ๊ณ ๊ด๋ จ๋ ์ ๋ณด ์ ํ์ ํ ๋จ์ด๋ก ์ถ๋ ฅํ์ธ์.
๊ฐ๋ฅํ ๊ฐ: "edu", "news", "report", "all", "nothing".
- edu: ๊ฒฝ์ ์ฉ์ด๋ ๊ฐ๋
๋ฑ ๋ฐฐ๊ฒฝ์ง์
- news: ์ต์ ์ฐ์
๋ํฅ
- report: ๊ธฐ์
๋ถ์, ์ฌ๋ฌด ์ ๋ณด ๋ฑ
- all/nothing: ๋ฒ์ฃผ ๊ตฌ๋ถ์ด ๋ถ๋ช
ํํ ๋
"nothing"๊ณผ "all"์ ์ต์ํ์ผ๋ก ์ฌ์ฉํ๊ณ ,
๊ฐ๋ฅํ๋ฉด edu, news, report ์ค ํ๋๋ก ๋ถ๋ฅํ์ธ์.
"""),
('user', f"์ง์: {query}\n๋ต๋ณ:")
])
chain = prompt | self.llm
classification = chain.invoke({"query": query}).content.strip().lower()
return classification
def search_documents(self, query: str, classification: str) -> str:
"""๋ถ๋ฅ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ์ ์ ํ DB์์ ๋ฌธ์๋ฅผ ๊ฒ์ํด ๋ด์ฉ์ ๊ตฌ์ฑํฉ๋๋ค."""
combined_info = ""
if classification in ["edu", "all"]:
edu_docs = self.edu_db.similarity_search(query, k=3)
if edu_docs:
combined_info += "ใ์ ํ๋ธ ๊ฒฝ์ ๊ต์ก ์๋ฃ (์ฒญ์๋
์ฉ)ใ\n"
combined_info += "\n".join(doc.page_content for doc in edu_docs) + "\n\n"
if classification in ["news", "all"]:
news_docs = self.news_db.similarity_search(query, k=3)
if news_docs:
combined_info += "ใ๊ฒฝ์ ๊ด๋ จ ๋ด์คใ\n"
combined_info += "\n".join(doc.page_content for doc in news_docs) + "\n\n"
if classification in ["report", "all"]:
report_docs = self.report_db.similarity_search(query, k=3)
if report_docs:
combined_info += "ใ์ฆ๊ถ ๋ณด๊ณ ์ใ\n"
combined_info += "\n".join(doc.page_content for doc in report_docs) + "\n\n"
return combined_info
def generate_answer(self, query: str, classification: str, context: str) -> str:
"""๋ถ๋ฅ๋ ์ง์์ ๊ด๋ จ ๋ฌธ์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก GPT๋ก ๋ต๋ณ์ ์์ฑํฉ๋๋ค."""
# ๋ถ๋ฅ๊ฐ nothing์ผ ๊ฒฝ์ฐ: ๊ฒฝ์ ์ฌ๋ถ ํ๋จ
if classification == "nothing":
prompt = ChatPromptTemplate.from_messages([
SystemMessage(
"""
๋๋ ์ฃผ์ ๊ต์ก ๋ฐ ์ข
๋ชฉ ์ถ์ฒ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์ฒญ์๋
๋ง์ถค ๊ฒฝ์ ๊ต์ก ์ฑ๋ด์ด์ผ.
์ฌ์ฉ์์ ์ง์๊ฐ ๊ฒฝ์ ๊ด๋ จ์ด๋ฉด ์ฝ๊ฒ ์ค๋ช
ํด์ฃผ๊ณ , ์๋๋ผ๋ฉด '๊ฒฝ์ ์ ๊ด๋ จ๋ ์ง๋ฌธ์ ์
๋ ฅํด์ฃผ์ธ์!' ๋ผ๊ณ ๋ต๋ณํด.
๋งํฌ๋ ์กด๋๋ง์ ์ ์งํด์ค.
"""
),
HumanMessage(f"์ง์: {query}\n๋ต๋ณ:")
])
else:
prompt = ChatPromptTemplate.from_messages([
SystemMessage(
f"""
๋๋ ์ฃผ์ ๊ต์ก ๋ฐ ์ถ์ฒ ์ข
๋ชฉ์ ๊ดํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์ฒญ์๋
๋ง์ถค ๊ฒฝ์ ๊ต์ก ์ฑ๋ด์ด์ผ.
์๋๋ ์ฐธ๊ณ ํ ์ ์๋ ๋ฌธ์ ์ ๋ณด์ผ:\n\n{context}
์ ์ ๋ณด๋ฅผ ์ฐธ๊ณ ํ์ฌ ์ฌ์ฉ์์ ์ง์์ ๋ํด ์ฝ๊ฒ ์ค๋ช
ํด์ค.
ํนํ ๋ด์ค ๊ธฐ๋ฐ์ด๋ผ๋ฉด ์ต๊ทผ ๋ํฅ์ ํฌํจํด์ค.
ํ์ํ๋ค๋ฉด ์ถ์ฒ ์ข
๋ชฉ๋ ํจ๊ป ์๋ ค์ค.
๋งํฌ๋ ์กด๋๋ง์ ์ ์งํด์ค.
"""
),
HumanMessage(f"์ง์: {query}\n๋ต๋ณ:")
])
chain = prompt | self.llm
return chain.invoke({"query": query, "combined_info": context}).content
def run_query(self, query: str) -> str:
"""์ ์ฒด ์ง์ ์ฒ๋ฆฌ ํ๋ก์ธ์ค"""
classification = self.classify_query(query)
print(f"\n[์ง์ ๋ถ๋ฅ ๊ฒฐ๊ณผ]: {classification}")
context = self.search_documents(query, classification)
return self.generate_answer(query, classification, context)
def input_query(self, user_query: str = ""):
print("\n[์ฌ์ฉ์ ์ง์]:", user_query)
response = self.run_query(user_query)
print("\n[์ฑ๋ด ์๋ต]:\n", response)
if __name__ == "__main__":
chatbot = ChatBot()
while True:
user_input = input("\n์ง์๋ฅผ ์
๋ ฅํ์ธ์ (์ข
๋ฃํ๋ ค๋ฉด 'quit' ์
๋ ฅ): ")
if user_input.strip().lower() == 'quit':
print("์ฑ๋ด์ ์ข
๋ฃํฉ๋๋ค.")
break
chatbot.input_query(user_input)