langgraph / / 2024. 12. 3. 07:56

[langgraph] 처음부터 ReAct 에이전트를 만드는 방법

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 참고 자료

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유