langgraph의 공식문서를 번역해 놓은 자료입니다. 이해하기 쉽게 예제는 변경하였습니다. 또한 필요한 경우 부연 설명을 추가하였습니다. 문제가 되면 삭제하겠습니다.
https://langchain-ai.github.io/langgraph/tutorials/introduction/#part-4-human-in-the-loop
Part 4: Human-in-the-loop
에이전트는 신뢰할 수 없을 때가 있으며, 작업을 성공적으로 수행하기 위해 사람의 개입이 필요할 수 있다. 마찬가지로, 일부 작업에 대해서는 실행하기 전에 사람의 승인이 필요할 수 있다. 이것은 모든 것이 의도대로 진행되는지 확인하기 위함이다.
LangGraph는 여러 방식으로 사람이 참여하는 워크플로우를 지원한다. 이번 섹션에서는 LangGraph의 interrupt_before
기능을 사용하여 항상 tool node에서 중단되도록 할 것이다.
먼저, 기존 코드를 바탕으로 시작한다. 아래 코드는 3부에서 가져온 것이다.
from typing import Annotated
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from typing_extensions import TypedDict
memory = MemorySaver()
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatOpenAI(model="gpt-3.5-turbo")
llm_with_tools = llm.bind_tools(tools)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)
graph_builder.add_conditional_edges(
"chatbot",
tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
이제 tools node에서 중단되도록 지정하여 그래프를 컴파일한다.
graph = graph_builder.compile(
checkpointer=memory,
# 새로운 기능
interrupt_before=["tools"],
# tools after도 사용할 수 있다.
# interrupt_after=["tools"]
)
user_input = "지금 LangGraph를 공부하고 있어. LangGraph에 대해 찾아줄 수 있어?"
config = {"configurable": {"thread_id": "1"}}
events = graph.stream(
{"messages": [("user", user_input)]}, config, stream_mode="values"
)
for event in events:
if "messages" in event:
event["messages"][-1].pretty_print()
실행결과
================================ Human Message =================================
지금 LangGraph를 공부하고 있어. LangGraph에 대해 찾아줄 수 있어?
================================== Ai Message ==================================
Tool Calls:
tavily_search_results_json (call_KvsI0vNYyeu1XUlpbDOLJNtV)
Call ID: call_KvsI0vNYyeu1XUlpbDOLJNtV
Args:
query: LangGraph
위의 interrupt_before를 사용하지 않으면 HumanMessage -> AIMessage -> ToolMessage -> AIMessage가 실행이 되지만 tools에 interrupt_before를 추가함으로써 HumanMessage -> AIMessage까지만 실행이 된다. (Tool 이전에 일시중지)
graph state가 잘 동작하는지 확인해보자.
snapshot = graph.get_state(config)
print(snapshot.next)
('tools',)
이번에는 이전과 달리 "next" 노드가 'tools'로 설정되어 있다. 여기서 중단되었다. 이제 도구 호출을 확인해보자.
existing_message = snapshot.values["messages"][-1]
print(existing_message.tool_calls)
[{'name': 'tavily_search_results_json',
'args': {'query': 'LangGraph framework for building language model applications'},
'id': 'toolu_01R4ZFcb5hohpiVZwr88Bxhc',
'type': 'tool_call'}]
이 시점에서 할 수 있는 가장 간단한 일은 그래프가 계속 실행되도록 하는 것이다. 아래에서 그렇게 해보자.
다음으로, 그래프를 계속 진행하자. None
을 전달하면 상태에 새로운 내용을 추가하지 않고 그래프가 중단된 지점에서 계속 진행된다.
# `None` will append nothing new to the current state, letting it resume as if it had never been interrupted
events = graph.stream(None, config, stream_mode="values")
for event in events:
if "messages" in event:
event["messages"][-1].pretty_print()
================================== Ai Message ==================================
[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and comprehensive information, I'll use the Tavily search engine to look this up. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_01R4ZFcb5hohpiVZwr88Bxhc', 'input': {'query': 'LangGraph framework for building language model applications'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
tavily_search_results_json (toolu_01R4ZFcb5hohpiVZwr88Bxhc)
Call ID: toolu_01R4ZFcb5hohpiVZwr88Bxhc
Args:
query: LangGraph framework for building language model applications
================================= Tool Message =================================
Name: tavily_search_results_json
[{"url": "https://www.datacamp.com/tutorial/langgraph-tutorial", "content": "LangGraph is a library within the LangChain ecosystem that simplifies the development of complex, multi-agent large language model (LLM) applications. Learn how to use LangGraph to create stateful, flexible, and scalable systems with nodes, edges, and state management."}, {"url": "https://langchain-ai.github.io/langgraph/", "content": "LangGraph is a low-level framework that allows you to create stateful, multi-actor applications with LLMs, using cycles, controllability, and persistence. Learn how to use LangGraph with LangChain, LangSmith, and Anthropic tools to build agent and multi-agent workflows."}]
================================== Ai Message ==================================
LangGraph는 LangChain 생태계 내의 라이브러리로, 복잡한 다중 에이전트 대규모 언어 모델 (LLM) 애플리케이션의 개발을 간소화합니다. LangGraph를 사용하여 노드, 엣지 및 상태 관리를 통해 상태 유지 가능하고 유연하며 확장 가능한 시스템을 만드는 방법에 대해 학습할 수 있습니다.
더 자세한 내용을 원하시면 아래 링크를 참고하시기 바랍니다:
1. [DataCamp Tutorial on LangGraph](https://www.datacamp.com/tutorial/langgraph-tutorial)
2. [LangGraph Official Website](https://langchain-ai.github.io/langgraph/)
이 호출의 LangSmith 트레이스를 검토하여 위 호출에서 수행된 작업을 정확히 확인하자. 상태가 첫 번째 단계에서 로드되어 챗봇이 중단된 지점에서 계속 진행할 수 있도록 되어 있다.
인터럽트를 사용하여 챗봇에 사람이 개입 가능한 실행을 추가하여 필요할 때 사람의 감독과 개입을 허용했다. 이는 AI 시스템으로 만들 수 있는 잠재적인 사용자 인터페이스를 열어준다. 이미 체크포인터를 추가했기 때문에 그래프는 무기한으로 일시 중지하고 아무 일도 없었던 것처럼 언제든지 재개할 수 있다.
다음으로, 사용자 정의 상태 업데이트를 사용하여 봇의 행동을 커스트마이징하는 방법을 알아보자.