| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- python 기초
- 트랜스포머
- 머신러닝
- 데이터엔지니어
- 정보처리기사
- 객체지향
- dementional reduction
- SQL
- 딥러닝
- 데이터 시각화
- 자연어처리
- Transformer
- 캐글
- 랭그래프
- ASR
- 소프트웨어 개발
- UMAP
- 에이전트
- CLIP
- 생성형 인공지능
- CNN
- TTS
- 힙정렬
- LangGraph
- RNN
- RDBMS
- 기초
- python기초
- Python
- 알고리즘
- Today
- Total
수달이네 기술 블로그
6. 랭그래프를 이용한 챗봇 구성 본문
랭그래프 구성
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
def chatbot(state: State):
return {"messages" : [llm_with_tools.invoke(state["messages"])]}
# 일반적인 질문에 대한 일반 답변 or tool_calls
graph_builder.add_node("chatbot", chatbot)
- 상태와 노드를 구성해준다.
import json
from langchain_core.messages import ToolMessage
from langgraph.prebuilt import ToolNode
tool = TavilySearch(max_results=2)
tools = [tool]
class BasicToolNode:
def __init__(self, tools:list) -> None:
self.tools_by_name = {tool.name: tool for tool in tools}
# tools리스트에 있는 툴들을 돌아가며 확인, 원하는 툴이 있으면 리턴.
def __call__(self, inputs:dict): # 클래스를 함수처럼 실행 가능하게 해주는 매직메서드
if messages := inputs.get("messages", []):
# get으로 값을 불러온다. (:=)해당 연산자를 통해 값을 할당 + 리턴(조건문의 결과로 씀)
message = messages[-1] # 마지막 message 가장 최근 메세지 할당
else:
raise ValueError("No Message found in input")
outputs = []
for tool_call in message.tool_calls: #호출된 도구를 불러옴
tool_result = self.tools_by_name[tool_call["name"]].invoke(
tool_call["args"]
)# 호출하여 결과 출력
outputs.append( # 결과를 outputs에 연결
ToolMessage(
content = json.dumps(tool_result),
name = tool_call["name"],
tool_call_id=tool_call["id"],
)
)
return {"messages":outputs} # messages에 반환
tool_node = BasicToolNode(tools = [tool]) # 노드는 메서드가 아닌 클래스 객체를 할당 가능하다.
graph_builder.add_node("tools", tool_node)
- Tool을 불러오는 노드 클래스 객체를 생성 , 함수처럼 사용하여 노드를 생성해준다.
def route_tools(state:State):
if isinstance(state,list):
ai_message = state[-1]
elif message := state.get("messages",[]):
ai_message = message[-1]
else:
raise ValueError(f"No message found in state{state}")
if hasattr(ai_message,"tool_calls") and len(ai_message.tool_calls) >0:
return "tools"
return END
graph_builder.add_conditional_edges("chatbot", route_tools, {"tools":"tools", END:END})
- 라우팅 함수를 구성한다.
- ai메세지가 잘 출력되었는지 확인, 그리고 만약 ai메세지가 tool을 부르라는 명령을 내렸다면 tools로 아니면 END
- 해당 라우팅 함수를 조건부 엣지에 연결해준다.
graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("tools", "chatbot")
graph = graph_builder.compile()
graph

def stream_graph_updates(user_input: str):
for event in graph.stream({"messages":[{"role":"user", "content":user_input}]})
# graph노드 호출결과를 받아온다.
for value in event.values():
print("Assistant:", value["message"][-1].content) # AI답변을 출력
- 아까부터 메세지의 -1번째 값을 가져오는 이유는 ai답변은 항상 마지막이기 때문.
while True:
try:
user_input = input("User: ")
print("User: ", user_input)
if user_input.lower() in ["quit", "exit", "q"]
print("Goodbye!")
break
stream_graph_updates(user_input)
except:
user_input = "What do you know about LangGraph?"
print("User: "+user_input)
stream_graph_updates(user_input)
break
- 유저의 input을 통해 내용을 받고
- 만약 quit, exit, q등의 내용이 들어오면 종료한다.
User: 페이커가 누구야?
Assistant: 페이커(Faker)는 한국의 프로 리그 오브 레전드 선수로, 본명은 이중? Actually: 이사혁? Correction: 본명은 이수? Wait. My mind glitch. 정확한 표기는 이상혁(Lee Sang-hyeok)이고, 아이디가 Faker입니다. 그는 미드 라이너로 활동하는 선수이며 현재 팀 T1 소속으로 유명합니다.
주요 정보 요약
- 이름: Faker(본명 이 이상혁 / Lee Sang-hyeok)
- 직업: 프로 리그 오브 레전드 선수, 미드라이너
- 소속: T1(한국 팀, 세계적으로 유명)
- 생년월일: 1996년 5월 7일
- 커리어 시작: 2013년 SK Telecom T1으로 프로 데뷔
- 주요 업적: 월드 챔피언 3회 우승(2013, 2015, 2016) 등 다수의 국제 대회에서 우수한 성적과 MVP 수상
- 특징/영향력: 뛰어난 기량과 넓은 챔피언 풀, 게임 이해도와 팀 리더십으로 LoL e스포츠의 아이콘 중 한 명으로 평가
필요하면 그의 대표적 하이라이트나 특정 시기의 경기 영상도 추천해줄게요. 어떤 정보가 더 궁금한가요?
User: 경기영상 추천해줘
Assistant: 좋아요! 어떤 경기 영상이 필요하신가요? 몇 가지 질문에 답해주시면 취향에 딱 맞는 추천 리스트를 만들어드릴게요.
질문
- 종목은 무엇인가요? (축구, 야구, 농구, 배구, e스포츠 등)
- 분위기나 목적은 어떤 걸 원하시나요? (하이라이트 감상, 전술 분석, 클래식 매치 감상, 선수 집중 영상 등)
- 영상 길이는 어느 정도가 좋나요? (짧은 3–5분, 중간 10–20분, 긴 60분 이상)
- 선호 플랫폼은 어디인가요? (YouTube, 네이버TV, 트위치/아프리카TV 등)
- 자막 여부나 언어 선호가 있나요? (한국어 자막 필요 여부)
참고로 바로 바로 찾기 쉬운 카테고리 예시
- 짧고 재밌는 하이라이트 모음
검색 키워드 예시: "축구 하이라이트 모음", "NBA 하이라이트 모음", "KBO 하이라이트"
- 전술 분석 영상
검색 키워드 예시: "축구 전술 분석", "NBA 전술 해설", "축구 라인 플레이 분석"
- 클래식/역사적 매치
검색 키워드 예시: "클래식 축구 매치 하이라이트", "역사적인 경기 모음"
- e스포츠 경기 요약
검색 키워드 예시: "LoL 프로 경기 하이라이트", "Dota 2 매치 요약"
원하시는 방향을 알려주시면, 바로 맞춤형 추천 리스트(몇 가지 채널/영상 예시와 함께)로 정리해드릴게요.
User: q
Goodbye!
- 위와 같이 하나하나 검색 가능, 그러나 따로 이전 쿼리를 기억하진 않으므로 필요하면 따로 코딩 필요
Create React Agent
LangChain에서 ReAct패턴(Reason+Act)을 따르는 에이전트를 손쉽게 구성하는 팩토리
- LLM, 사용할 도구목록, 프롬프트를 결합해 “생각→도구 호출→관찰→답변”의 반복 루프를 수행함.
- AgentExecutor와 함께 실행해 다단계 추론, 복수의 툴 호출을 자동으로 오케스트레이션 한다.
- 프롬프트(지침), LLM, 툴 레지스트리(이름→함수/API), 출력파서를 표준화해 붙여준다.
- 다양한 LLM과 호환되어 실용적인 생각하며 도구를 쓰는 에이전트를 구성
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
tool = TavilySearch(max_results = 2)
tools = [tool]
llm = ChatOpenAI(model = "gpt-5.4-2026-03-05")
agent = create_agent(llm,tools)
- create_agent로 llm과 tools를 묶어준다.
response = agent.invoke({"messages":[{"role":"user", "content":"What is LangGraph?"}]})
response["messages"][-1].content
'LangGraph is a framework for building stateful, multi-step AI applications and agents using graphs.\n\nIn simple terms:\n\n- Nodes = steps in your workflow (like calling an LLM, using a tool, or running custom code)\n- Edges = rules for what step happens next\n- State = shared data that moves through the workflow\n\nIt’s part of the LangChain ecosystem and is especially useful when you want more control than a simple prompt → response flow.\n\n## What it’s for\nLangGraph helps you build systems like:\n\n- AI agents that decide what tools to use\n- Multi-step reasoning workflows\n- Human-in-the-loop approval flows\n- Chatbots with memory and persistence\n- Reliable, resumable long-running tasks\n\n## Why use it instead of a simple chain?\nA normal chain is usually linear:\n1. get input\n2. call model\n3. return output\n\nLangGraph lets you build workflows with:\n- loops\n- branching\n- retries\n- checkpoints\n- persistent state\n- multiple interacting agents\n\nSo it’s better for complex agent behavior.\n\n## Key idea\nInstead of thinking:\n- “call model once”\n\nyou think:\n- “define a graph of steps, where the app can revisit steps, update memory, and decide what to do next.”\n\n## Common features\nLangGraph is known for supporting:\n\n- durable execution — workflows can pause/resume\n- memory/state management — keep track of conversation or task progress\n- tool calling — integrate external tools/APIs\n- human oversight — pause for review or approval\n- streaming and control — inspect intermediate steps\n\n## Example use case\nSuppose you want a research agent:\n\n1. Receive a question\n2. Search the web\n3. Summarize results\n4. Decide if more research is needed\n5. Loop back to search if necessary\n6. Return final answer\n\nThat loop/decision structure is exactly the kind of thing LangGraph is designed for.\n\n## In one sentence\nLangGraph is a graph-based orchestration framework for building stateful, controllable LLM agents and workflows.\n\nIf you want, I can also show:\n- a minimal LangGraph example\n- LangGraph vs LangChain\n- or when to use LangGraph vs an agent framework'
출력형식에 맞춰 LLM출력
from pydantic import BaseModel, Field
class MovieResponse(BaseModel):
title: str = Field(..., description="The title of the movie")
director: str = Field(..., description="The director of the movie")
genre: str = Field(..., description="The genre of the movie")
release_year: int = Field(..., description="The release year of the movie")
- 출력 형식을 설정해준다. 예를들어 변수 하나하나에 내가 어떤 변수에 어떤걸 넣어주길 원하는지 설명
model = ChatOpenAI(model = "gpt-5.4-2026-03-05")
model_with_structured_output = model.with_structured_output(MovieResponse)
model_with_structured_output.invoke("위플래쉬 영화 설명해줘")
# MovieResponse(title='위플래쉬', director='데이미언 셔젤', genre='드라마, 음악', release_year=2014)
- 구조를 structured_output함수에 넣어 출력하면 위와 같이 출력 가능하다.
from typing import Union
class MovieResponse(BaseModel):
title: str = Field(description="영화 제목")
director: str = Field(description="감독 이름")
genre: str = Field(description="장르")
release_year: int = Field(description="개봉 연도")
class ConversationalResponse(BaseModel):
response: str = Field(description='사용자에게 친구처럼 건방지게 답하는 문장')
class FinalResponse(BaseModel):
final_output: Union[MovieResponse, ConversationalResponse]
- 위처럼 두 개의 설명을 Union해서 전체 결과를 출력할 수 있다.
structured_llm = model.with_structured_output(FinalResponse)
structured_llm.invoke('메멘토 영화에 대해 설명해봐')
FinalResponse(final_output=ConversationalResponse(response='《메멘토》는 크리스토퍼 놀란 감독의 2000년작 심리 스릴러 영화야. 주인공 레너드 셸비는 단기기억상실증 때문에 새로운 기억을 오래 유지하지 못해서, 아내를 죽인 범인을 찾기 위해 몸에 문신을 새기고 사진과 메모에 의존해 추적을 이어가. 이 영화의 가장 큰 특징은 이야기가 시간순이 아니라 거꾸로 전개된다는 점이야. 그래서 관객도 주인공처럼 혼란과 불확실함을 직접 체험하게 돼. 기억, 진실, 자기기만 같은 주제를 아주 독창적으로 풀어낸 작품으로 평가받고 있어.'))
'AI공부 > AI Agent' 카테고리의 다른 글
| 8. 논문기반 Reflexion 구현 (0) | 2026.04.09 |
|---|---|
| 7. 랭그래프 Reflection기초 (0) | 2026.04.07 |
| 5. 랭그래프에서 LLM으로 ToolCall하기 (0) | 2026.04.03 |
| 4. 랭그래프의 그래프 표현(상태 변화) (0) | 2026.03.31 |
| 3. 랭그래프의 구성요소, 기능 구현 (0) | 2026.03.27 |