LangGraph 공식문서를 번역한 내용입니다. 필요한 경우 부연 설명을 추가하였고 이해하기 쉽게 예제를 일부 변경하였습니다. 문제가 되면 삭제하겠습니다.
https://langchain-ai.github.io/langgraph/how-tos/react-agent-from-scratch/
기본 제공되는 ReAct 에이전트(create_react_agent)를 사용하는 것은 시작하기에 훌륭하지만, 때때로 더 많은 제어와 사용자화가 필요할 수 있다. 그런 경우에는 사용자 정의 ReAct 에이전트를 만들 수 있다. 이 가이드는 LangGraph를 사용하여 ReAct 에이전트를 처음부터 구현하는 방법을 보여준다.
준비
우선, 필요한 패키지를 설치하자.
pip install langgraph langchain-openai
ReAct 에이전트 생성
필요한 패키지를 설치하고 환경 변수를 설정했으므로, 이제 ReAct 에이전트를 만들어보자.
그래프 상태 정의
이번 예제에서는 가장 기본적인 ReAct 상태를 정의할 것이다. 이 상태는 단지 메시지 목록만 포함한다.
여러분의 특정 사용 사례에 맞게 필요한 다른 상태 키를 추가하셔도 된다.
from typing import (
Annotated,
Sequence,
TypedDict,
)
from dotenv import load_dotenv
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
load_dotenv()
class AgentState(TypedDict):
"""The state of the agent."""
messages: Annotated[Sequence[BaseMessage], add_messages]
모델과 도구 정의
다음으로 예제에서 사용할 도구와 모델을 정의하자.
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
model = ChatOpenAI(model="gpt-4o-mini")
@tool
def get_weather(location: str):
"""Call to get the weather from a specific location."""
if location == "서울":
return "서울은 항상 맑아요~ 😈."
else:
return f"{location}의 날씨가 어떤지 모르겠어요 ㅠ"
tools = [get_weather]
model = model.bind_tools(tools)
노드와 엣지 정의
다음으로 노드와 엣지를 정의해 보자. 기본적인 ReAct 에이전트에는 모델을 호출하는 노드와 도구를 사용하는 노드 두 개만 있다. 하지만 이 기본 구조를 여러분의 사용 사례에 맞게 수정할 수 있다. 여기서 정의한 도구 노드는 미리 구축된 ToolNode의 단순화된 버전으로, 몇 가지 추가 기능을 가지고 있다.
예를 들어, 구조화된 출력을 추가하는 노드나 외부 작업을 실행하는 노드(이메일 보내기, 캘린더 이벤트 추가 등)를 추가하고 싶을 수도 있다. 또는 call_model 노드가 작동하는 방식과 should_continue가 도구를 호출할지 결정하는 방식만 변경하고 싶을 수도 있다. 가능성은 무궁무진하며, LangGraph는 여러분의 특정 사용 사례에 맞게 이 기본 구조를 쉽게 맞춤화할 수 있도록 해준다.
import json
from langchain_core.messages import ToolMessage, SystemMessage
from langchain_core.runnables import RunnableConfig
tools_by_name = {tool.name: tool for tool in tools}
def tool_node(state: AgentState):
outputs = []
for tool_call in state["messages"][-1].tool_calls:
tool_result = tools_by_name[tool_call["name"]].invoke(tool_call["args"])
outputs.append(
ToolMessage(
content=json.dumps(tool_result),
name=tool_call["name"],
tool_call_id=tool_call["id"],
)
)
return {"messages": outputs}
def call_model(
state: AgentState,
config: RunnableConfig,
):
# 이건 create_react_agent를 state_modifier로 커스터마이징하는 것과 비슷하지만 훨씬 유연하다
system_prompt = SystemMessage(
"You are a helpful AI assistant, please respond to the users query to the best of your ability!"
)
response = model.invoke([system_prompt] + state["messages"], config)
return {"messages": [response]}
def should_continue(state: AgentState):
messages = state["messages"]
last_message = messages[-1]
if not last_message.tool_calls:
return "end"
else:
return "continue"
그래프 정의
모든 노드와 엣지를 정의했으므로, 이제 그래프를 정의하고 컴파일할 수 있다. 추가한 노드나 엣지에 따라 이를 여러분의 특정 사용 사례에 맞게 수정해야 할 수 있다.
from langgraph.graph import StateGraph, END
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges(
"agent",
should_continue,
{
"continue": "tools",
"end": END,
},
)
workflow.add_edge("tools", "agent")
graph = workflow.compile()
from IPython.display import Image, display
try:
display(
Image(
graph.get_graph().draw_mermaid_png(
output_file_path="how-to-create-prebuilt-react-agent-from-scratch.png"
)
)
)
except Exception:
pass
ReAct 에이전트 사용
이제 ReAct 에이전트를 생성하고 테스트를 해보자.
def print_stream(stream):
for s in stream:
message = s["messages"][-1]
if isinstance(message, tuple):
print(message)
else:
message.pretty_print()
================================ Human Message =================================
서울 날씨 어때?
================================== Ai Message ==================================
Tool Calls:
get_weather (call_7gPlMcrXJEYvwTWt6V5RJw0w)
Call ID: call_7gPlMcrXJEYvwTWt6V5RJw0w
Args:
location: 서울
================================= Tool Message =================================
Name: get_weather
"\uc11c\uc6b8\uc740 \ud56d\uc0c1 \ub9d1\uc544\uc694~ \ud83d\ude08."
================================== Ai Message ==================================
서울은 현재 맑은 날씨입니다. 😊
그래프는 get_weather 도구를 올바르게 호출하고 도구에서 정보를 받은 후 사용자에게 응답한다.
LangGraph 참고 자료
- Controllability
- Persistence
- Memory
- Human-in-the-loop
- Streaming
- Tool calling
- Subgraphs
- State Management
- Other
- Prebuilt ReAct Agent