1. ํ ๋ฌ ํ๊ณ
ํ ๋ฌ์ด ์ด๋ป๊ฒ ํ๋ฌ๊ฐ๋์ง ๋ชจ๋ฅด๊ฒ ๋ค.
๋น ๋ฐ์ดํฐ ์ผ๋ฅผ ๊ฐ๋ค์์ ๊ทธ๋ฐ์ง
์์ฆ Langchain - RAG ์ด ๋ถ๋ถ์ด ๋์ฑ ํฅ๋ฏธ๊ฐ ์๊ฒผ๋ค. ๋จ์ํ ๊ธฐ์ ์ ์ผ๋ก ํฅ๋ฏธ๋กญ๋ค๋ ๊ฑธ ๋์ด์ ์์ฆ ํ์ฌ๋ค์ด ์ด ๋ถ๋ถ์ ๋ง์ด ๊ฐ๋ฐํ๋ ค๊ณ ํ๋ค๋ ์ ์์ ๊ทธ๋ฆฌ๊ณ ๋ด๊ฐ ๋ญ๊ฐ ๊ธฐ์ฌํ ์ ์์ ๊ฑฐ ๊ฐ์์ ์ด ์ชฝ ๋ถ์ผ๋ก ์ผ์ ์ฐพ๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
ํ์ค์ ์ผ๋ก ์ ์ ์ ๋ง์ด ๋ฝ๋ ๋ถ์ผ๋ ์๋์๊ธฐ์ ๊ณ ๋ฏผํ๋ ์์ค์, ์์นจ์ ๊ฐ์ฌ๋์ด ์ด๋ป๊ฒ ์ง๋ด๋๊ณ ๋ฌผ์ด๋ด์ฃผ์ จ๊ณ ์ง๋ก์ ๋ํ ์๋ด์ ์ ๊น์ด๋๋ง ํ ์ ์์๋ค. ๊ผญ ์ด ์ชฝ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด๋ณด๊ณ ์ถ๋ค.
2. ํํ๋ก์ ํธ
์์์ผ ๋ฐํ!
์์ฒญ๋ ๋ฐ์ดํฐ ์ฌ์ด์์ ํ๋์ด๋ค๊ฐ ์คํธ๋ฆผ๋ฆฟ๊น์ง ๊ตฌํํ๋๋ฐ ๋น์ผ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๋ต๋ณํ๋ค๋ ๊ฑธ ๊นจ๋ฌ์๋ค.. ๊ทธ๋ฆฌ๊ณ ์ด๊ฑธ ๊ณ ์ณค๋ ํ ํ์์ด ๊ณ ์์ ํ๋ค... ๊ทธ ๋ ธ๋ ฅ๊ณผ ์ง์ค๋ ฅ์ ์กด๊ฒฝํ๋ค ์ ๋ง..
์ฌ์ค ๋๋ ๊ทธใ ก ๊ณผ์ ์ ์์์ ๋ฐฐ์ธ ์ ์์์ผ๋ฉด ์ข์๊ฒ ์ง๋ง, ์๊ฐ์ด ๋๋ฌด ์์๋ค.
๋ค์๋ฒ์ ์ฝ๋๋ฅผ ๋ค์ ๋ฌผ์ด๋ณด๊ณ ๋ด ์์ผ๋ก ๋ค์ ์ฌํ์ ํด๋ด์ผ๊ฒ ๋ค.
์ด๋ฒ์๋ ๋๋ ๋ฐํ์ ์ฐธ์ฌํ๋ค. ํฌ๊ฒ ์ด๋ ค์ด ๊ฑด ์๋์๋๋ฐ ์ค๋๋ง์ ๋ฐํ๋ผ ๋ง์ด ๋จ๋ ธ๋ค.
๋ด๊ฐ ๋งก์ ๋ถ๋ถ์ ์ค๋ช ํ๊ณ ์ ๋ฌํ๋ค๋ ๊ฒ๋ง์ผ๋ก ์ด ํํ๋ก์ ํธ๋ฅผ ์ ์ดํดํ๊ณ ์๋ค๋ ์ ์์ ๋ด ์ค์ค๋ก ์๋ฏธ์๋ค๊ณ ์๊ฐํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ ์ฒ๋ฆฌ๋ถ๋ถ๊ณผ ํ์ธํ๋ ๋ถ๋ถ์ ์ ๋ฌธ๊ฐ(?)์ธ ์ฌ๋ฒ์ด๊ฐ ๋ฐํ๋ฅผ ํ๋ค. ๋ ๋ฉ์ง ๋๊ธฐ๋ค. ๋ง์ด ๋ฐฐ์๊ฐ๋๋ค.


https://github.com/SKNETWORKS-FAMILY-AICAMP/SKN13-3rd-3Team
GitHub - SKNETWORKS-FAMILY-AICAMP/SKN13-3rd-3Team: [SK Networks Family AI Camp 13th] 3rd mini project
[SK Networks Family AI Camp 13th] 3rd mini project - SKNETWORKS-FAMILY-AICAMP/SKN13-3rd-3Team
github.com
์ฐ๋ฆฌ์ ์๋์ค๋ฌ์ด ์ฐ์ถ๋ฌผ๋ค! ์ฌ๊ธฐ๋ค๊ฐ๋ ์ฌ๋ ค๋ณธ๋ค ใ ใ ใ ใ
์๋ฌด๋๋ ์ฐ๋ฆฌํ์ด ์ ์ผ ์ํ๊ฑฐ๊ฐ๋ค(ใ
ใ
ใ
ใ
ใ
)
์ข์ ํ์๋ค ๋ง๋์ ์ผ์ฐ์ผ์ฐํ๊ณ ๊ฐ์ด ์๊ธฐํ๋ฉด์ ๋ ๋ง์ด ๊ณต๋ถ๋ฅผ ํ๊ฒ๋๊ณ ..
์ ๋ง ๋ฉ์ง ํ์๋ค๊ณผ ํจ๊ปํ๊ณ ์๋ค๋ ์ ๋ง์ผ๋ก๋ ๊ฐ์ก๋ ์๊ฐ์ด์๋ค.
์์ผ๋ก ๋ณด์ํ ์ ์ ๋ณด์ํ๊ณ ์ฅ๊ณ ๋ก ํ๋ฉด๊ตฌํ๊น์ง ์ ์ด์ด๋๊ฐ๋ณด๊ฒ ๋ค.
2.1 ๋ณด์ํ๊ณ ์ถ์ ์ - langchain์ langgraph๋ก ๋ฐ๊ฟ์ ์จ๋ณด๊ณ ์ถ๋ค.
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from typing_extensions import TypedDict
from typing import Annotated
from dotenv import load_dotenv
load_dotenv()
class ProductInfoState(TypedDict):
product: str
price: int
ingredient:str
review: str
# ๋
ธ๋ ์ ์ -> ํจ์(callable) -> ์
๋ ฅ: State, ์ถ๋ ฅ: State์ ์ ์ฅํ ์์ฑ(state)
def add_Product(state:ProductInfoState):
print("add_product:", state)
return {"product": "cream"}
def add_price(state:ProductInfoState):
print("add_price:", state)
return {"price":10000}
def add_ingredient(state:ProductInfoState):
print("add_ingredient:", state)
return {"ingredient": "water"}
def add_review(state:ProductInfoState):
print("add_review:", state)
return {"review": "perfect"}
# StateGraph๋ฅผ ๊ตฌ์ฑ.
workflow = StateGraph(UserInfoState)
# ๋
ธ๋ ์ถ๊ฐ
workflow.add_node("add_product", add_product)
workflow.add_node("add_price", add_price)
workflow.add_node("add_ingredient", add_ingredient)
workflow.add_node("add_review", add_review)
# ์ฃ์ง ์ถ๊ฐ (๋
ธ๋๊ฐ์ ์ฐ๊ฒฐ) -> ์์: START ๋
ธ๋
workflow.add_edge(START, "add_product")
workflow.add_edge("add_product", "add_ingredient")
workflow.add_edge("add_price", "add_ingredient")
workflow.add_edge("add_ingredient", "add_review")
workflow.add_edge("add_review", END)
# state graph๋ฅผ ์ปดํ์ผ - ์คํ๊ฐ๋ฅํ ์ํ๋ก ๋ง๋ ๋ค. (๊ทธ๋ํ ๊ฒ์ฆ, ์ต์ ํ)
graph = workflow.compile()
# ์๊ฐํ
from IPython.display import Image, display
display(Image(graph.get_graph().draw_mermaid_png()))
######################
# Chatbot
######################
# ์ํ(State) ํด๋์ค ์ ์
# ๋
ธ๋๋ค์ด ๊ณต์ ํ state๊ฐ๋ค์ ์ ์ํ๋ ํด๋์ค(ํ์
)
class State(TypedDict):
# state์ ์ ์ฅํ ๊ฐ(์์ฑ, state) ๋ค์ ์ ์
messages: Annotated[list, add_messages] # ๋ณ์: Annotated[๋ณ์ํ์
, ์ค๋ช
]
# add_messages(left:list, right:list) : return left + right
model = ChatOpenAI(model='gpt-4.1-mini')
#######################
# ๋
ธ๋ ์ ์
# ๋
ธ๋ == ๊ธฐ๋ฅ == ํจ์(callable): ํ๋ผ๋ฏธํฐ๋ก State ํ์
์ ์ ์ธ.
def chatbot(state:State):
# state: Dictionary - key: Stateํด๋์ค์ ์ ์ํ ๋ณ์๋ค.
print("chatbot: state type:", type(state), state)
# messages๋ฅผ llm์ query๋ก ์ ๋ฌ.
response = model.invoke(state['messages']) #invoke(str ๋๋ Message List)
# response๋ฅผ state์ ์ ์ฅ. -> dictionary๋ก ๊ตฌ์ฑ {์ ์ฅํ state์ ์ด๋ฆ: ์ ์ฅํ ๊ฐ}
return {"messages": [response]} # ๋ฆฌํด -> State์ ์ ์ฅ.
# ๊ทธ๋ํ๋ฅผ ๊ตฌ์ฑ (StateGraph)
## ๋
ธ๋์ ์ฃ์ง + state
workflow = StateGraph(State)
# graph(workflow)์ ๋
ธ๋๋ฅผ ์ถ๊ฐ. ์ด๋ฆ-ํจ์
workflow.add_node("chatbot", chatbot)
# graph์ ์ฃ์ง๋ฅผ ๊ตฌ์ฑ. ์ฃ์ง: ๋
ธ๋์ ๋
ธ๋๋ฅผ ์ฐ๊ฒฐ
## START ๋
ธ๋ - ์ฃ์ง ๊ตฌ์ฑ - (END ๋
ธ๋:์๋ต๊ฐ๋ฅ)
workflow.add_edge(START, "chatbot") # ์์๋
ธ๋(์ด๋ฆ) -> ๋์ฐฉ๋
ธ๋(์ด๋ฆ)
workflow.add_edge("chatbot", END)
# ๊ตฌ์ฑ์ ์๋ฃํ๋ฉด compile -> ๊ทธ๋ํ ์ต์ ํ๋ฅผ ํ๊ณ ์คํํ ์์๋๋ก ๋ง๋๋ ์์
.
graph = workflow.compile()
# ๊ทธ๋ํ ๊ตฌ์กฐ ์๊ฐํ -> ์คํ ํ๋ฆ์ ํ์ธ
from IPython.display import Image
Image(graph.get_graph().draw_mermaid_png())
########################
# ์คํ - graph.invoke()
########################
from langchain_core.messages import HumanMessage
query = "Langgraph์ ๋ํด์ ์๋ ค์ค."
init_state = {"messages":[HumanMessage(content=query)]}
resp = graph.invoke(init_state)
2.2 ๋ณด์ํ๊ณ ์ถ์ ์ - tool ์ฐ๊ธฐ
from dotenv import load_dotenv
from langchain_tavily import TavilySearch
load_dotenv()
avily_search = TavilySearch(
max_results=1,
include_images=True, # ๊ฒ์ํ ํ์ด์ง์ ์ด๋ฏธ์ง๋ค์ URL๋ ๋ฐํ.
time_range="month",
)
query = "๋
๋ ํ ๋ ๊ฐ๊ฒฉ์ ๋ํด ์๋ ค์ค."
resp = tavily_search.invoke(query)
print(type(resp))
resp
##### LLM MODEL์ Tool binding
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4.1-mini")
# model์ tool(๋ค)์ binding(๋ถ์ฌ์ฃผ๊ธฐ.)
tool_model = model.bind_tools(tools=[tavily_search])
type(model), type(tool_model)
query = "๋
๋ ํ ๋ ๊ฐ๊ฒฉ์ ๋ํด ์๋ ค์ค."
resp1 = tool_model.invoke(query)
print(resp2.content)
print("------------------")
resp1.tool_calls
##### Tool calls ์ด์ฉํด Tool ํธ์ถ
# tool๊ฐ์ฒด.invoke(args)
search_result = tavily_search.invoke(resp1.tool_calls[0]['args'])
type(search_result)
search_result
##### Tool call์ด ์ฌ๋ฌ๊ฐ์ธ ๊ฒฝ์ฐ
# Runnable์ ํ๋ฒ์ ์ฌ๋ฌ๋ฒ ํธ์ถํ ๋
## Runnable.batch([์ ๋ฌํ ๊ฐ1, ์ ๋ฌํ ๊ฐ2, .....]) : [๊ฒฐ๊ณผ๊ฐ1, ๊ฒฐ๊ณผ๊ฐ2, ..]
resp = model.batch(["์๋
ํ์ธ์",
"๋
๋ ํ ๋์ ๊ฐ๊ฒฉ์ ๋ํด ์๋ ค์ค.",
"์ด๋์คํ๋ฆฌ ํฌ๋ฆผ ์ฑ๋ถ์ ์๋ ค์ค."])
##### Tool ์ ์ฒ๋ฆฌ(์๋ต) ๊ฒฐ๊ณผ๋ฅผ LLM ์์ฒญ์ ์ฌ์ฉ
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate(
[
("system", "๋น์ ์ AI ์ ๋ณด์ ๊ณต์์
๋๋ค. ์ ๊ณต๋ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๋ต๋ณํด์ฃผ์ธ์."),
("human", "{user_input}"),
MessagesPlaceholder(variable_name="messages", optional=True)
]
)
input_dict = {"user_input":query, "messages":[resp2, *search_result3]} # messages: [AIMessage, ToolMessage, ToolMessage, ..]
final_chain = prompt | tool_model
final_response = final_chain.invoke(input_dict)
print(final_response.content)