목차
langgraph 버전: 0.6.5
langchain 버전 : 0.3.27
내용 출처 : LangGrapn Docs
LangGraph
LangGraph Trusted by companies shaping the future of agents – including Klarna, Replit, Elastic, and more – LangGraph is a low-level orchestration framework for building, managing, and deploying long-running, stateful agents. Get started Install LangGr
langchain-ai.github.io
1. 개요
그동안 LangGraph의 각각의 구성요소에 대해 공부해 보았습니다.
이번 시간에는 실제로 사용할 수 있는
기본적인 Graph를 구현해 봅시다.
2. LLM + Tool 기본 구성
2.1. 구성
모델 노드 (LLM)
- 사용자 메시지를 읽음
- 필요 시 tool 호출 제안 (tool_calls 생성)
- tool 결과가 state에 들어오면 최종 답변을 생성
ToolNode
- 여러 tool(검색, 계산, RAG 등)을 담아둠
- 모델이 호출한 tool을 실행하고 결과를 state에 추가
종료 (END)
- 모델이 더 이상 tool 호출을 제안하지 않고 최종 답변을 낼 때 종료
그래프 설계도
2.2. 코드 순서
1. 툴 정의
2. LLM 정의 + 툴와 연결
3. 그래프 정의
- state 정의
- node 정의
- Graph 정의
4. Graph 실행
3. 코드
3.1. 툴 정의
3.1.1. 계산기 툴 정의
@tool
- 툴을 만들때 데코레이터를 사용한다.
- "parse_docstring=True" 를 넣어주면, docstring을 tool의 설명으로 바로 사용한다.
- docstring은 Google 스타일 docstring 구문 으로 작성해야 한다
툴의 내용은 사칙연산을 하는 계산기이다.
import math
import json
from langchain.tools import tool
@tool(parse_docstring=True)
def calculator(expression: str) -> str:
"""안전한 사칙연산 및 기본 수학함수 계산기.
Args:
expression (str): 계산할 수식. 예: "2*(3+4) + sin(0.5)"
Returns:
str: JSON 문자열. {"expression": str, "result": number}
"""
allowed_names = {
"abs": abs, "round": round,
"pow": pow, "sqrt": math.sqrt,
"sin": math.sin, "cos": math.cos, "tan": math.tan,
"log": math.log, "exp": math.exp, "pi": math.pi, "e": math.e
}
code = compile(expression, "<calc>", "eval")
for name in code.co_names:
if name not in allowed_names:
raise ValueError(f"사용 불가 식별자: {name}")
val = eval(code, {"__builtins__": {}}, allowed_names)
return json.dumps({"expression": expression, "result": val})
3.1.2. 로컬 검색 툴
간단한 검색을 하는 예시 툴이다.
툴은 만들면 리스트(list)에 모든 툴을 담아둔다
# 아주 작은 로컬 지식베이스
KB = [
{"title": "LangGraph 개요", "text": "LangGraph는 그래프 기반으로 LLM과 Tool을 오가며 에이전트를 구성하는 프레임워크다."},
{"title": "ToolNode 역할", "text": "ToolNode는 모델이 제안한 tool_calls를 실제로 실행하고 결과를 메시지로 state에 추가한다."},
{"title": "bind_tools 역할", "text": "bind_tools는 모델이 사용할 수 있는 도구 목록을 알려주어 함수 호출 형태로 제안하게 만든다."},
]
@tool(parse_docstring=True)
def local_search(query: str) -> str:
"""간단한 로컬 검색을 수행합니다.
Args:
query (str): 검색 질의 문자열.
Returns:
str: JSON 문자열. {"query": str, "results": [{"title": str, "text": str, "score": int}, ...]}
"""
qs = query.lower()
scored = []
for item in KB:
text = f"{item['title']} {item['text']}".lower()
score = sum(q in text for q in qs.split())
if score > 0:
scored.append({"title": item["title"], "text": item["text"], "score": int(score)})
scored.sort(key=lambda x: x["score"], reverse=True)
return json.dumps({"query": query, "results": scored[:3]})
TOOLS = [calculator, local_search]
실무에서 사용하는 검색도구들은 다음과 같다.
- retriever를 넣어 RAG
- 웹검색
- 데이터베이스 검색
- 파일 기반 검색
3.2. LLM 모델 정의
모델을 정의한다.
그리고, 위에서 정의한 tools를 bind_tools() 메서드로 모델과 연결한다.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
llm_with_tools = llm.bind_tools(TOOLS)
3.3. Graph 정의
3.3.1. state 정의
from langgraph.graph.message import add_messages
from typing import TypedDict, Annotated, List
class State(TypedDict):
# add_messages 어노테이션을 사용하면 메시지를 자동 머지해 줍니다.
messages: Annotated[List, add_messages]
3.3.2. node 정의
모델 노드 정의
def model_node(state: State) -> State:
"""
- 사용자/툴 메시지를 읽고
- 필요 시 tool_calls를 제안하거나
- 최종 답변을 생성합니다.
"""
resp = llm_with_tools.invoke(state["messages"])
return {"messages": [resp]}
툴 노드 정의
- ToolNode() 를 이용하면 된다.
from langgraph.prebuilt import ToolNode
tool_node = ToolNode(TOOLS)
라우터 정의
def router(state: State):
"""
모델의 마지막 응답에 tool_calls가 있으면 tools로,
없으면 end로 보냅니다.
"""
last = state["messages"][-1]
# langchain_core.messages.AIMessage 에 tool_calls 속성이 존재
if isinstance(last, AIMessage) and getattr(last, "tool_calls", None):
return "tools"
return "end"
3.3.3. 그래프 구성
StateGraph() 를 이용해 그래프를 정의한다.
노드와 엣지를 그래프에 추가해 준다.
compile() 함수로 그래프를 완성한다.
from langgraph.graph import StateGraph, START, END
graph = StateGraph(State)
graph.add_node("model", model_node)
graph.add_node("tools", tool_node)
graph.add_edge(START, "model")
graph.add_conditional_edges("model", router, {"tools": "tools", "end": END})
graph.add_edge("tools", "model")
app = graph.compile()
3.4. 그래프 실행
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
# 예시 1, 로컬 검색이 필요한 질문
out = app.invoke({"messages": [HumanMessage(content="LangGraph에서 ToolNode와 bind_tools 차이를 알려줘")]})
print("=== 예시 1 응답 ===")
print(out["messages"][-1].content)
# 예시 2, 계산이 필요한 질문
out = app.invoke({"messages": [HumanMessage(content="삼각형 내각의 합이 180도일 때, 45도와 65도의 나머지 각은 몇 도야? 식으로 계산해줘")]})
print("\n=== 예시 2 응답 ===")
print(out["messages"][-1].content)
# 예시 3, 혼합 질의
out = app.invoke({"messages": [HumanMessage(content="LangGraph ToolNode를 한 줄로 설명하고, 2*(3+4)+sin(0.5)도 계산해줘")]})
print("\n=== 예시 3 응답 ===")
print(out["messages"][-1].content)
4. 실습 코드
https://colab.research.google.com/drive/1AUUmaXrgRRYmJsmAd8Mpg_FT4htpVTgY?usp=sharing
[LangGraph] 실습 (LLM + tool).ipynb
Colab notebook
colab.research.google.com
'자연어처리 > LangGraph' 카테고리의 다른 글
[LangGraph] MCP 연동하기 (5) | 2025.08.11 |
---|---|
[LangGraph] Human-in-the-loop & Time Travel (2) | 2025.08.11 |
[LangGraph] 확장과 재사용 (Subgraph, Multi-Agent) (4) | 2025.08.11 |
[LangGraph] 실행 흐름 제어(command, send, loop, 분기, 동적 라우팅) (4) | 2025.08.10 |
[LangGraph] Persistence & Durable Execution (3) | 2025.08.10 |