LangGraph 챗봇에서 내장된 도구 노드 사용하기
챗봇이 자체 '기억'으로 답변할 수 없는 질문을 처리하기 위해 웹 검색 도구를 통합하여 기능을 향상시켜 보겠습니다. 이를 통해 챗봇은 더 관련성 높은 정보를 찾아 더 나은 응답을 제공할 수 있습니다.
학습 목표
- 챗봇에 도구를 통합하여 기능 확장하기
- Tavily 검색 도구를 사용하여 질문에 답변하기
- LangGraph의 내장된 도구 노드 사용법 학습하기
1. 환경 설정
라이브러리 설치
다음 명령어를 실행하여 필요한 라이브러리를 설치합니다.
pip install -U python-dotenv notebook langgraph langchain_openai tavily-python langchain_community
환경변수 설정
환경변수 파일 .env를 생성하여 다음의 내용을 설정합니다.
OPENAI_API_KEY=본인의_OpenAI_API키
OPENAI_MODEL=gpt-4o-mini
TAVILY_API_KEY=본인의_tavily_api_key
환경변수를 로드하기 위해 Python의 python-dotenv 라이브러리를 사용합니다.
2. 코드 설명
라이브러리 임포트
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from typing_extensions import TypedDict
from typing import Annotated, List
from dotenv import load_dotenv
import os
- ChatOpenAI: OpenAI의 Chat Completion API를 호출하기 위한 클래스입니다.
- StateGraph, START, END: LangGraph의 상태 기반 그래프 구조를 구성하는 데 사용됩니다.
- add_messages: LangGraph의 메시지 관리 기능을 자동화하는 역할을 수행합니다.
- TypedDict, Annotated, List: 상태 정의를 위한 타입 힌트 도구입니다.
- load_dotenv: 환경변수를 로드합니다.
환경변수 로딩
load_dotenv()
openai_model = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
도구 정의
Tavily 검색 도구를 설정합니다.
from langchain_community.tools.tavily_search import TavilySearchResults
tool = TavilySearchResults(max_results=2)
tool.invoke("LangGraph에서 '노드'란 무엇인가요?")
- TavilySearchResults: 사용자가 입력한 질의에 대해 최대 2개의 결과를 가져오는 검색 도구를 만들었습니다.
상태(State) 정의
class State(TypedDict):
messages: Annotated[list, add_messages]
- 이 코드는 LangGraph에서 챗봇의 상태(State)를 정의하는 부분입니다.
- 상태는 LangGraph의 핵심 개념입니다. 챗봇이 메시지를 주고받으며 관리할 데이터를 명확히 정의합니다.
- LangGraph는 상태 기반 그래프(StateGraph)를 사용하는 프레임워크로, 각 단계의 상태를 명확히 정의하고, 상태 간에 데이터를 전달하는 방식으로 작동합니다.
- messages는 메시지를 관리하는 리스트로, 자동 메시지 추가 및 관리가 가능합니다.
구성 요소 설명
- class State(TypedDict)
- Python의 TypedDict를 사용해 명시적인 타입이 있는 딕셔너리를 정의합니다.
- 이를 통해 상태(State)의 구조와 타입을 명확히 지정하여 코드의 가독성과 안정성을 높입니다.
- messages
- 상태에 저장되는 주요 데이터입니다. 여기서는 대화 중 주고받은 메시지를 담고 있는 리스트입니다.
- LangGraph는 대화의 문맥을 유지하기 위해 메시지의 리스트를 관리합니다.
- Annotated[List, add_messages]
- Annotated 타입 힌트를 사용하여 메시지 리스트가 특정한 규칙(add_messages)을 따르도록 합니다.
- 여기서 add_messages는 LangGraph에서 제공하는 특별한 주석(annotation)으로, 메시지 리스트를 다룰 때 자동으로 관리(추가)를 돕는 기능을 수행합니다.
이 상태 정의를 통해 챗봇은:
- 대화의 각 단계를 명확히 관리할 수 있습니다.
- 사용자의 메시지와 모델의 응답 메시지를 체계적으로 기록하고 관리합니다.
- 명시적인 타입과 자동 관리 기능을 활용하여 코드의 오류를 최소화하고 유지보수를 쉽게 합니다.
이러한 방식을 통해, LangGraph는 상태 기반의 복잡한 챗봇 애플리케이션에서도 효율적이고 명확한 데이터 관리와 흐름 제어를 가능하게 합니다.
LLM 모델 설정 및 도구 바인딩
llm = ChatOpenAI(model=openai_model)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)
- llm: 사용되는 LLM 모델로, 여기서는 OpenAI의 gpt-4o-mini 모델을 사용합니다.
- llm_with_tools: LLM에 정의한 도구를 바인딩하여 도구 사용이 가능하게 합니다.
챗봇 노드 정의
챗봇이 도구를 이용하여 사용자 메시지에 응답하도록 설정합니다.
def chatbot(state: State):
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
- chatbot(state: State): 챗봇 노드 함수로, 상태에서 받은 메시지를 기반으로 도구를 활용하여 응답을 생성하고 상태를 업데이트합니다.
도구 실행 노드 정의
ToolNode는 LangGraph에서 미리 만들어져 제공되는 도구 실행 전용 노드입니다.
from langgraph.prebuilt import ToolNode
tool_node = ToolNode(tools)
- LLM이 도구(tool)를 호출하겠다고 요청할 때, 실제 도구를 실행하는 역할을 합니다.
- 도구 호출 요청 메시지를 입력으로 받아 도구를 실행하고, 그 결과를 다시 LLM에 전달할 수 있는 형태로 반환합니다.
- 사용자가 별도의 복잡한 로직을 구현하지 않고, 간단하게 도구를 처리할 수 있도록 미리 만들어진 클래스입니다.
- ToolNode 생성시 도구를 리스트 형태로 전달합니다.
작동 원리
ToolNode의 내부 구조는 다음과 같은 순서로 동작합니다:
- 챗봇(LLM) 노드가 도구 호출을 요청한 메시지를 생성합니다.
- ToolNode가 이 메시지를 입력으로 받아 메시지에 포함된 **도구 호출(tool_calls)**을 실행합니다.
- 도구 실행 결과를 ToolMessage 형태로 변환하여 다시 챗봇(LLM) 노드가 이해할 수 있게 반환합니다.
그래프 구성 및 컴파일
LangGraph의 내장된 tools_condition을 사용하여 조건부로 도구 노드를 호출합니다.
from langgraph.prebuilt import tools_condition
tools_condition은 LangGraph에서 미리 만들어 제공하는 조건부 엣지(conditional edge)를 위한 조건 함수입니다.
- 챗봇 노드의 출력 메시지에 도구 호출 요청이 포함되어 있는지 확인합니다.
- 도구 호출 요청이 있다면, 도구 노드(ToolNode)를 실행하도록 조건을 설정해주는 기능입니다.
- 별도로 조건 로직을 구현하지 않아도, 도구 호출의 필요성을 쉽게 판단할 수 있도록 미리 만들어진 편의 함수입니다.
작동 원리
tools_condition 함수는 다음 로직을 수행합니다:
- 입력 상태에서 가장 최근 메시지를 확인합니다.
- 최근 메시지에 tool_calls가 포함되어 있는지 확인합니다.
- 만약 메시지에 하나 이상의 도구 호출 요청이 존재하면(tool_calls가 존재하면), 다음 노드를 'tools'로 설정합니다.
- 도구 호출 요청이 없다면 자동으로 종료(END) 노드로 설정하여 그래프를 종료합니다.
workflow = StateGraph(State)
workflow.add_node("chatbot", chatbot)
workflow.add_node("tools", tool_node)
workflow.add_conditional_edges("chatbot", tools_condition)
workflow.add_edge("tools", "chatbot")
workflow.set_entry_point("chatbot")
graph = workflow.compile()
- 먼저 StateGraph(State)를 통해 상태 기반 그래프를 생성합니다.
- workflow.add_node("chatbot", chatbot)은 그래프에 "chatbot"이라는 이름의 노드를 추가하며, 이 노드는 앞에서 정의한 chatbot 함수를 실행합니다.
- workflow.add_node("tools", tool_node)는 "tools"이라는 이름의 노드를 추가하며, 이 노드는 도구 실행을 관리합니다.
- workflow.add_conditional_edge("chatbot", tools_condition)은 "chatbot" 노드에서 "tools" 노드로 연결되는 조건부 엣지를 정의합니다. 이 엣지는 챗봇이 도구를 사용할 필요가 있을 때만 활성화됩니다.
- workflow.add_edge("tool", "chatbot")은 "tool" 노드에서 다시 "chatbot" 노드로 연결하는 엣지를 정의합니다. 이 엣지는 도구 실행 후 결과를 챗봇 노드로 반환하는 역할을 합니다.
- workflow.set_entry_point("chatbot")은 그래프의 시작점을 "chatbot" 노드로 설정합니다. 즉, 챗봇이 처음 실행되는 지점입니다.
- workflow.compile()은 정의한 그래프를 실제로 실행 가능한 형태로 컴파일하여 최적화 상태를 준비합니다.
다음은 그래프의 전체 흐름을 간단히 정리한 것입니다:
- 챗봇 노드가 메시지를 생성하고, 도구 호출이 필요하다고 판단할 경우 메시지에 tool_calls를 포함합니다.
- 생성된 메시지는 tools_condition으로 전달됩니다.
- 이 조건 함수가 도구 호출이 필요함을 확인하고, 다음 노드를 도구 노드(ToolNode)로 설정합니다.
- ToolNode가 실행되면서 도구 호출 요청을 처리하고 실제 도구를 실행합니다.
- 도구 실행 결과를 다시 챗봇 노드에 전달하여, LLM이 추가 응답을 생성할 수 있도록 합니다.
그래프 시각화
컴파일된 그래프를 이용해 시각화봅니다.
from IPython.display import Image, display
display(Image(graph.get_graph().draw_mermaid_png()))
3. 챗봇 실행
사용자가 입력한 메시지를 기반으로 챗봇이 응답을 생성하는 과정입니다. 도구를 사용하여 더욱 정확한 정보를 제공합니다.
user_input = "LangGraph에서 '노드'란 무엇인가요?"
state = {"messages": [HumanMessage(content=user_input)]}
response = graph.invoke(state)
print(response["messages"][-1].content)'HRDI_AI > [인공지능] AI 멀티 에이전트 서비스 개발을 위한 LangGraph' 카테고리의 다른 글
| 5. langgraph_chat_with_custom_state (3) | 2025.05.27 |
|---|---|
| 4. langgraph_chat_with_human_in_the_loop (0) | 2025.05.27 |
| 3. langgraph_chat_with_memory (0) | 2025.05.27 |
| 2-1. langgraph_chat_with_tools (0) | 2025.05.27 |
| 1. simple_langgraph_chat (0) | 2025.05.27 |