langchain / / 2024. 11. 3. 19:29

[langgraph] ReAct AgentExecutor in LangGraph

udemy 강좌 내용[LangGraph- Develop LLM powered AI agents with LangGraph]의 일부를 정리한 내용입니다. 예제의 일부 내용을 수정하였습니다. 문제가 되면 삭제하겠습니다.

https://www.udemy.com/course/langgraph/learn/lecture/43638176#overview

예제

여기서는 아래와 같이 서울의 날씨를 물어보고 거기에 3배수 하는 Agent를 만들어보자.

if __name__ == "__main__":
    res = app.invoke(
        input={"input": "현재 서울 기온은 몇도야? 그리고 거기에 곱하기 3을 해줘"}
    )

그래프의 모양은 아래와 같다.

환경구성

1. 패키지 설치

관련 패키지를 설치하자.

pip install langchain langgraph langchain-openai langchainhub black isort python-dotenv

black은 python용 코드 포맷터이고 isort는 import 패키지를 알파벳으로 정렬해주는 기능을 한다.

2. env 생성

OPENAI_API_KEY=sk-xxxx
TAVILY_API_KEY=tvly-xxxxx

OpenAI와 Tavily 키를 .env에 추가하자.

TAVILY 키가 없으면 새로 발급받자.
https://app.tavily.com/home

ReAct Runnable

위의 input에서 필요한 기능은 2가지이다. 현재 서울 날씨를 가져오고, 곱하기 3을 하는 함수가 필요하다. 아래와 같이 두 개의 tool을 정의하자.

[react.py]

from dotenv import load_dotenv
from langchain import hub
from langchain.agents import create_react_agent
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.prompts import PromptTemplate
from langchain_core.tools import tool
from langchain_openai.chat_models import ChatOpenAI

load_dotenv()


react_prompt: PromptTemplate = hub.pull("hwchase17/react")


@tool
def triple(num: float) -> float:
    """
    :param num: a number to triple
    :return: the number tripled ->  multiplied by 3
    """
    return 3 * float(num)


tools = [TavilySearchResults(max_results=1), triple]

llm = ChatOpenAI(model="gpt-3.5-turbo-1106")

react_agent_runnable = create_react_agent(llm, tools, react_prompt)

hwchase17/react

https://smith.langchain.com/hub/hwchase17/react

우선 hwchase17/react는 langchainhub에서 react 프롬프트를 import 한다.

tool 이름, tool description을 받고 사용자의 입력을 받는다.

tool을 실행할 필요가 있거나, 답변이 준비가 되었으면 동작하고 그렇지 않는다면 아무것도 하지 않는다.
그리고 react loop에서 무엇을 실행했는지 history 역할을 하는 agent_scratchpad를 연결할 필요가 있다. 이것이 agent의 reasoning engine이 하는 역할이다.

우선 tool을 만들자. 3배수를 하는 triple이라는 이름으로 함수를 만든다.

@tool
def triple(num: float) -> float:
    """
    :param num: a number to triple
    :return: the number tripled ->  multiplied by 3
    """
    return 3 * float(num)

tools = [TavilySearchResults(max_results=1), triple]

llm = ChatOpenAI(model="gpt-3.5-turbo-1106")

react_agent_runnable = create_react_agent(llm, tools, react_prompt)

인수와 리턴 타입을 나타내는 docstring을 적어야 한다. 이것은 모호함을 없애고 LLM으로 하여금 reasoning engine으로 동작하도록 하는 역할을 한다. 인수는 float으로 받고 float으로 리턴한다.

tools 변수를 만들어 2개의 tool을 가진 list로 만든다. 하나는 Tavily를 사용하여 검색을 하는 tool이고 다른 하나는 3배수를 하는 tool이다.

다음은 ChatOpenAI를 생성한다. 모델은 gpt-3.5-turbo를 선택하였다.

마지막으로 해야 할 일은 react_agent_runnable을 만들어 create_react_agent를 사용할 것이다.

create_react_agent는 ReAct 프롬프트를 사용하는 agent이다.

AgentState

[state.py]

import operator
from typing import Annotated, TypedDict, Union

from langchain_core.agents import AgentAction, AgentFinish


class AgentState(TypedDict):
    input: str
    agent_outcome: Union[AgentAction, AgentFinish, None]
    intermediate_steps: Annotated[list[tuple[AgentAction, str]], operator.add]

State는 노드가 실행전에 input으로 받고 실행 후 update하는 역할을 한다.

state를 변경하는 방법은 두 가지가 있다. 하나는 set을 사용하는 하는 것이다. 즉, 기존 값을 덮어쓰는 것이다. 다른 하나는 add를 사용하는 것이다. 기존 값에 추가를 하는 것이다.

agent executor를 구현하는 데 기억해야 할 3가지 속성이 있다. 하나는 사용자의 입력(input)이다. 두번째는 agent가 무엇을 해야 할지에 대한 현재의 상태(agent_outcome)이다. action 혹은 finish가 될 수 있다. 세 번째는 history이다. 즉, intermediate_steps이다.

Nodes

from dotenv import load_dotenv
from langgraph.prebuilt.tool_executor import ToolExecutor

from react import react_agent_runnable, tools
from state import AgentState

load_dotenv()


def run_agent_reasoning_engine(state: AgentState):
    agent_outcome = react_agent_runnable.invoke(state)
    return {"agent_outcome": agent_outcome}


tool_executor = ToolExecutor(tools)


def execute_tools(state: AgentState):
    agent_action = state["agent_outcome"]
    output = tool_executor.invoke(agent_action)
    return {"intermediate_steps": [(agent_action, str(output))]}

노드의 첫 번째 iteration에서 사용자의 입력을 받고 agent의 outcome은 None이 된다. 그리고 intemediate_steps는 빈 list가 된다.

이 노드에서 하는 일은 사용자의 입력을 받고 ReAct 프롬프트로 LLM으로 보내고 LLM의 응답을 받아 AgentFinish 혹은 AgentAction으로 파싱하여 상태를 업데이트 한다.

우선 state를 받아 react_agent_runnable를 실행하는 run_agent_reasoning_engine를 만들자. 실행한 결과가 agent_output에 담긴다. 물론 기존 상태를 덮어쓰는 것이 아니라 추가되는 것이다.

ReAct 알고리즘에서 reasoning 역할을 하는 노드이고 이제는 Act 역할을 하는 노드를 구현할 것이다.

우선 tools를 초기화 한다. Tavily tool과 triple tool이다. ToolExecutor은 함수를 실행할 수 있는 역할을 한다.

Act 노드는 execute_tools라는 이름의 함수이고 state를 인수로 받는다. Act 노드는 Agent Reason 노드 후에 오기 때문에 agent_outcome을 받는다.

Graph

이제 마지막으로 LangGraph로 agentExecutor를 구현해보자. node, edge, conditional edge를 생성할 것이다.

from dotenv import load_dotenv

load_dotenv()

from langchain_core.agents import AgentFinish
from langgraph.graph import END, StateGraph

from nodes import execute_tools, run_agent_reasoning_engine
from state import AgentState

AGENT_REASON = "agent_reason"
ACT = "act"


def should_continue(state: AgentState) -> str:
    if isinstance(state["agent_outcome"], AgentFinish):
        return END
    else:
        return ACT


flow = StateGraph(AgentState)

flow.add_node(AGENT_REASON, run_agent_reasoning_engine)
flow.set_entry_point(AGENT_REASON)
flow.add_node(ACT, execute_tools)


flow.add_conditional_edges(
    AGENT_REASON,
    should_continue,
)

flow.add_edge(ACT, AGENT_REASON)

app = flow.compile()
# app.get_graph().draw_mermaid_png(
#     output_file_path="./graph.png",
#     draw_method=MermaidDrawMethod.PYPPETEER,
# )

if __name__ == "__main__":
    print("Hello, world!")
    res = app.invoke(
        input={"input": "현재 서울 기온은 몇도야? 그리고 거기에 곱하기 3을 해줘"}
    )
    print(res)

StateGraph는 node간의 상태를 읽고 쓰는데 사용되는 Graph이다. State는 우리가 정의한 어떤 것이 될 수도 있다.

우선 두 가지 개념을 정의하자. agent_reason은 agent reasoning engine을 실행하는 agent reason이다. 그리고 tool 함수를 실행할 act 노드이다.

should_continue라는 이름의 함수를 정의하자. 이것은 conditional edge 역할을 하는 함수이다. state에서 agent reason을 실행한 후에 새로운 agent outcome을 가지게 된다. Agent outcome이 agent_finish 혹은 agent_action이 될 수 있다.

Agent finish라면 LLM reasoning engine이 결과를 얻었고 더 이상 loop를 실행할 필요가 없다는 것이다. 이 경우 함수는 end 키워드를 리턴할 것이다.

agent outcome이 agent_finish가 아니라면 agent action이 될 것이다. 또한 tool에 대한 모든 정보를 가지고 있고 act 노드를 실행할 것이다.

이제 실행을 해보자.

{
  "input": "현재 서울 기온은 몇도야? 그리고 거기에 곱하기 3을 해줘",
  "agent_outcome": AgentFinish(return_values={
  "output": "The current temperature in Seoul is 19.3 degrees Celsius, and when multiplied by 3, it is 57.9 degrees Celsius."
}, log="I now know the final answer\nFinal Answer: The current temperature in Seoul is 19.3 degrees Celsius, and when multiplied by 3, it is 57.9 degrees Celsius."), "intermediate_steps": [
(AgentAction(tool="tavily_search_results_json", tool_input="current temperature in Seoul", log="I should use the search engine to find the current temperature in Seoul and then use the triple function to multiply it by 3.\nAction: tavily_search_results_json\nAction Input: "current temperature in Seoul""), "[{"url": "https://www.wunderground.com/weather/kr/seoul", "content": "Get the current and forecast temperature, precipitation, air quality, and UV index for Seoul, South Korea. See radar, satellite, and historical data for the past 10 days."}]"), (AgentAction(tool="tavily_search_results_json", tool_input="https://www.wunderground.com/weather/kr/seoul", log="I need to click on the link to find the current temperature in Seoul.\nAction: tavily_search_results_json\nAction Input: "https://www.wunderground.com/weather/kr/seoul""), "[{\"url\": \"https://www.weatherapi.com/\", \"content\": "{\"location\": {\"name\": \"Seoul\", \"region\": \"\", \"country\": \"South Korea\", \"lat\": 37.5664, \"lon\": 126.9997, \"tz_id\": \"Asia/Seoul\", \"localtime_epoch\": 1730628588, \"localtime\": \"2024-11-03 19:09\"}, \"current\": {\"last_updated_epoch\": 1730628000, \"last_updated\": \"2024-11-03 19:00\", \"temp_c\": 19.3, \"temp_f\": 66.7, \"is_day\": 0, \"condition\": {\"text\": \"Partly cloudy\", \"icon\": \"//cdn.weatherapi.com/weather/64x64/night/116.png\", \"code\": 1003}, \"wind_mph\": 6.5, \"wind_kph\": 10.4, \"wind_degree\": 229, \"wind_dir\": \"SW\", \"pressure_mb\": 1020.0, \"pressure_in\": 30.12, \"precip_mm\": 0.0, \"precip_in\": 0.0, \"humidity\": 88, \"cloud\": 50, \"feelslike_c\": 19.3, \"feelslike_f\": 66.7, \"windchill_c\": 18.3, \"windchill_f\": 65.0, \"heatindex_c\": 18.3, \"heatindex_f\": 65.0, \"dewpoint_c\": 13.6, \"dewpoint_f\": 56.4, \"vis_km\": 10.0, \"vis_miles\": 6.0, \"uv\": 0.0, \"gust_mph\": 9.3, \"gust_kph\": 15.0}}"}]"), (AgentAction(tool="triple", tool_input="19.3", log="The current temperature in Seoul is 19.3 degrees Celsius. Now I can use the triple function to multiply it by 3.\nAction: triple\nAction Input: 19.3"), "57.900000000000006")
]
}

현재 서울의 날씨는 19.3도 이고 곱하기 3을 하면 57.9라는 값을 출력했다.

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