langgraph / / 2024. 11. 30. 22:26

[langgraph] 그래프의 과거 상태를 보고 수정하는 방법

LangGraph 공식문서를 번역한 내용입니다. 필요한 경우 부연 설명을 추가하였고 이해하기 쉽게 예제를 일부 변경하였습니다. 문제가 되면 삭제하겠습니다.

https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/time-travel/

그래프에서 체크포인트를 사용하면 언제든지 에이전트의 상태를 가져오거나 업데이트할 수 있다. 이것으로 몇 가지 작업을 가능하다.

  • 중단 시 상태를 사용자에게 표시하여 액션을 수락하게 할 수 있다.
  • 그래프를 되감아 문제를 재현하거나 방지할 수 있다.
  • 상태를 수정하여 에이전트를 더 큰 시스템에 통합하거나 사용자가 에이전트의 행동을 더 잘 제어할 수 있게 할 수 있다.

이 기능을 위해 사용되는 주요 메서드는 다음과 같다.

  • get_state: target config에서 값을 가져온다.
  • update_state: 주어진 값을 target 상태(state)에 적용한다.

참고: 이를 위해서는 체크포인터를 전달해야 한다.

아래는 간단한 예시이다.

준비

우선, 필요한 패키지를 설치하자.

pip install langgraph langchain_openai

에이전트 만들기

툴 호출을 수행하는 상대적으로 간단한 ReAct 스타일의 에이전트를 구축할 것이다. 데모 목적을 위해 OpenAI 모델과 가짜 툴을 사용할 것이다.

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.graph import MessagesState, START
from langgraph.prebuilt import ToolNode
from langgraph.graph import END, StateGraph
from langgraph.checkpoint.memory import MemorySaver

load_dotenv()


@tool
def play_song_on_spotify(song: str):
    """Play a song on Spotify"""
    return f"Successfully played {song} on Spotify!"


@tool
def play_song_on_apple(song: str):
    """Play a song on Apple Music"""
    return f"Successfully played {song} on Apple Music!"


tools = [play_song_on_apple, play_song_on_spotify]
tool_node = ToolNode(tools)


model = ChatOpenAI(model="gpt-4o-mini")
model = model.bind_tools(tools, parallel_tool_calls=False)


def should_continue(state):
    messages = state["messages"]
    last_message = messages[-1]
    if not last_message.tool_calls:
        return "end"
    else:
        return "continue"


def call_model(state):
    messages = state["messages"]
    response = model.invoke(messages)
    return {"messages": [response]}


workflow = StateGraph(MessagesState)

workflow.add_node("agent", call_model)
workflow.add_node("action", tool_node)

workflow.add_edge(START, "agent")

workflow.add_conditional_edges(
    "agent",
    should_continue,
    {
        "continue": "action",
        "end": END,
    },
)

workflow.add_edge("action", "agent")

memory = MemorySaver()

app = workflow.compile(checkpointer=memory)

에이전트와 상호작업

이제 에이전트와 상호작용할 수 있다. 에이전트에게 테일러 스위프트의 가장 인기 있는 노래를 재생하라고 요청해 보자.

from langchain_core.messages import HumanMessage

config = {"configurable": {"thread_id": "1"}}
input_message = HumanMessage(content="성시경의 가장 인기 있는 노래를 재생해줄래?")
for event in app.stream({"messages": [input_message]}, config, stream_mode="values"):
    event["messages"][-1].pretty_print()
================================ Human Message =================================

성시경의 가장 인기 있는 노래를 재생해줄래?
================================== Ai Message ==================================
Tool Calls:
  play_song_on_apple (call_wKCXhzvQw5mmtWFB18NNAJYA)
 Call ID: call_wKCXhzvQw5mmtWFB18NNAJYA
  Args:
    song: 성시경 - 두 사람
================================= Tool Message =================================
Name: play_song_on_apple

Successfully played 성시경 - 두 사람 on Apple Music!
================================== Ai Message ==================================

성시경의 가장 인기 있는 노래 중 하나인 "두 사람"을 Apple Music에서 재생했습니다! 즐기세요!

이력 확인

이 스레드의 시작부터 끝까지의 히스토리를 살펴보자.

app.get_state(config).values["messages"]
[
  HumanMessage(content=
  '성시경의 가장 인기 있는 노래를 재생해줄래?',
  ...
  ),
  AIMessage(content=
  '',
  additional_kwargs=
  {
    'tool_calls': [
      {
        'id': 'call_wKCXhzvQw5mmtWFB18NNAJYA',
        'function': {
          'arguments': '{"song":"성시경 - 두 사람"}',
          'name': 'play_song_on_apple'
        },
        'type': 'function'
      }
    ],
    'refusal': None
  },
  ...,
  ...,
  tool_calls=
  [
    {
      'name': 'play_song_on_apple',
      'args': {
        'song': '성시경 - 두 사람'
      },
      'id': 'call_wKCXhzvQw5mmtWFB18NNAJYA',
      'type': 'tool_call'
    }
  ],
  ...,
  ToolMessage(content=
  'Successfully played 성시경 - 두 사람 on Apple Music!',
  name=
  'play_song_on_apple',
  id=
  'de6da2fb-d1be-4ad9-b3f2-8d36f2a1abff',
  tool_call_id=
  'call_wKCXhzvQw5mmtWFB18NNAJYA'
  ),
  AIMessage(content=
  '성시경의 가장 인기 있는 노래 중 하나인 "두 사람"을 Apple Music에서 재생했습니다! 즐기세요!',
  ...,
  ...
]
all_states = []
for state in app.get_state_history(config):
    print(state)
    all_states.append(state)
    print("--")
StateSnapshot(values={'messages': [HumanMessage(content="Can you play Taylor Swift's most popular song?", additional_kwargs={}, response_metadata={}, id='2fb9991d-bea6-4d09-9368-9209b5b93454'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'function': {'arguments': '{"song":"Shake It Off by Taylor Swift"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 80, 'total_tokens': 102, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-db7c77fc-1e91-473f-b58b-0ed1513427e3-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Shake It Off by Taylor Swift'}, 'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 22, 'total_tokens': 102, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='Successfully played Shake It Off by Taylor Swift on Apple Music!', name='play_song_on_apple', id='9792ab57-0542-414b-834d-3ca981ac1b91', tool_call_id='call_ap6Aa5HCwJxWkI8SPQoZnF5Q'), AIMessage(content='I\'ve successfully played "Shake It Off" by Taylor Swift on Apple Music! Enjoy the music!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 125, 'total_tokens': 145, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-2c99cfd0-a661-4339-9f40-0e4569f91aa5-0', usage_metadata={'input_tokens': 125, 'output_tokens': 20, 'total_tokens': 145, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-44ef-6a66-8003-1cd286a9ef8a'}}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [AIMessage(content='I\'ve successfully played "Shake It Off" by Taylor Swift on Apple Music! Enjoy the music!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 125, 'total_tokens': 145, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-2c99cfd0-a661-4339-9f40-0e4569f91aa5-0', usage_metadata={'input_tokens': 125, 'output_tokens': 20, 'total_tokens': 145, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}, 'thread_id': '1', 'step': 3, 'parents': {}}, created_at='2024-11-26T22:52:14.508447+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3d1d-6c2a-8002-f26a4556624a'}}, tasks=())
--
StateSnapshot(values={'messages': [HumanMessage(content="Can you play Taylor Swift's most popular song?", additional_kwargs={}, response_metadata={}, id='2fb9991d-bea6-4d09-9368-9209b5b93454'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'function': {'arguments': '{"song":"Shake It Off by Taylor Swift"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 80, 'total_tokens': 102, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-db7c77fc-1e91-473f-b58b-0ed1513427e3-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Shake It Off by Taylor Swift'}, 'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 22, 'total_tokens': 102, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='Successfully played Shake It Off by Taylor Swift on Apple Music!', name='play_song_on_apple', id='9792ab57-0542-414b-834d-3ca981ac1b91', tool_call_id='call_ap6Aa5HCwJxWkI8SPQoZnF5Q')]}, next=('agent',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3d1d-6c2a-8002-f26a4556624a'}}, metadata={'source': 'loop', 'writes': {'action': {'messages': [ToolMessage(content='Successfully played Shake It Off by Taylor Swift on Apple Music!', name='play_song_on_apple', id='9792ab57-0542-414b-834d-3ca981ac1b91', tool_call_id='call_ap6Aa5HCwJxWkI8SPQoZnF5Q')]}}, 'thread_id': '1', 'step': 2, 'parents': {}}, created_at='2024-11-26T22:52:13.688511+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3d17-6960-8001-33e657ff478a'}}, tasks=(PregelTask(id='0abfcd46-6dd3-12ed-acd2-bff426bc5e06', name='agent', path=('__pregel_pull', 'agent'), error=None, interrupts=(), state=None, result={'messages': [AIMessage(content='I\'ve successfully played "Shake It Off" by Taylor Swift on Apple Music! Enjoy the music!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 125, 'total_tokens': 145, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-2c99cfd0-a661-4339-9f40-0e4569f91aa5-0', usage_metadata={'input_tokens': 125, 'output_tokens': 20, 'total_tokens': 145, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}),))
--
StateSnapshot(values={'messages': [HumanMessage(content="Can you play Taylor Swift's most popular song?", additional_kwargs={}, response_metadata={}, id='2fb9991d-bea6-4d09-9368-9209b5b93454'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'function': {'arguments': '{"song":"Shake It Off by Taylor Swift"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 80, 'total_tokens': 102, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-db7c77fc-1e91-473f-b58b-0ed1513427e3-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Shake It Off by Taylor Swift'}, 'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 22, 'total_tokens': 102, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=('action',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3d17-6960-8001-33e657ff478a'}}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'function': {'arguments': '{"song":"Shake It Off by Taylor Swift"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 80, 'total_tokens': 102, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-db7c77fc-1e91-473f-b58b-0ed1513427e3-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Shake It Off by Taylor Swift'}, 'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 22, 'total_tokens': 102, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}, 'thread_id': '1', 'step': 1, 'parents': {}}, created_at='2024-11-26T22:52:13.685956+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3485-6766-8000-52284417172a'}}, tasks=(PregelTask(id='b350fb36-f2f7-26dd-b412-4ed2e2ee522e', name='action', path=('__pregel_pull', 'action'), error=None, interrupts=(), state=None, result={'messages': [ToolMessage(content='Successfully played Shake It Off by Taylor Swift on Apple Music!', name='play_song_on_apple', id='9792ab57-0542-414b-834d-3ca981ac1b91', tool_call_id='call_ap6Aa5HCwJxWkI8SPQoZnF5Q')]}),))
--
StateSnapshot(values={'messages': [HumanMessage(content="Can you play Taylor Swift's most popular song?", additional_kwargs={}, response_metadata={}, id='2fb9991d-bea6-4d09-9368-9209b5b93454')]}, next=('agent',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3485-6766-8000-52284417172a'}}, metadata={'source': 'loop', 'writes': None, 'thread_id': '1', 'step': 0, 'parents': {}}, created_at='2024-11-26T22:52:12.787281+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3484-61b8-bfff-6c02cdcc21a9'}}, tasks=(PregelTask(id='f610cd09-e54f-4b4b-6faf-250e57384a0d', name='agent', path=('__pregel_pull', 'agent'), error=None, interrupts=(), state=None, result={'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'function': {'arguments': '{"song":"Shake It Off by Taylor Swift"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 80, 'total_tokens': 102, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-db7c77fc-1e91-473f-b58b-0ed1513427e3-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': 'Shake It Off by Taylor Swift'}, 'id': 'call_ap6Aa5HCwJxWkI8SPQoZnF5Q', 'type': 'tool_call'}], usage_metadata={'input_tokens': 80, 'output_tokens': 22, 'total_tokens': 102, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}),))
--
StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efac491-3484-61b8-bfff-6c02cdcc21a9'}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [HumanMessage(content="Can you play Taylor Swift's most popular song?", additional_kwargs={}, response_metadata={})]}}, 'thread_id': '1', 'step': -1, 'parents': {}}, created_at='2024-11-26T22:52:12.786728+00:00', parent_config=None, tasks=(PregelTask(id='aa0a4c6e-ef8e-f9ea-0110-a2b3b8144caf', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result={'messages': [HumanMessage(content="Can you play Taylor Swift's most popular song?", additional_kwargs={}, response_metadata={}, id='2fb9991d-bea6-4d09-9368-9209b5b93454')]}),))
--

상태 리플레이

우리는 이 상태들 중 어느 곳으로든 돌아가서 그 지점에서 에이전트를 다시 시작할 수 있다. 이제 도구 호출이 실행되기 직전으로 돌아가 보자.

to_replay = all_states[2]
print(to_replay.values)
{'messages': [HumanMessage(content='성시경의 가장 인기 있는 노래를 재생해줄래?', additional_kwargs={}, response_metadata={}, id='2c881bc3-ef95-4832-a07e-9721c86e679e'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_wKCXhzvQw5mmtWFB18NNAJYA', 'function': {'arguments': '{"song":"성시경 - 두 사람"}', 'name': 'play_song_on_apple'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 86, 'total_tokens': 108, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-33c54d07-312f-4637-9fb9-d3329dbe70d1-0', tool_calls=[{'name': 'play_song_on_apple', 'args': {'song': '성시경 - 두 사람'}, 'id': 'call_wKCXhzvQw5mmtWFB18NNAJYA', 'type': 'tool_call'}], usage_metadata={'input_tokens': 86, 'output_tokens': 22, 'total_tokens': 108, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}
print(to_replay.next)
('action',)

이 지점에서 다시 실행하려면 해당 구성을 에이전트에 다시 전달하기만 하면 된다. 이렇게 하면 에이전트가 중단된 지점인 도구 호출에서 바로 이어서 실행된다.

for event in app.stream(None, to_replay.config):
    for v in event.values():
        print(v)
{'messages': [ToolMessage(content='Successfully played 성시경 - 두 사람 on Apple Music!', name='play_song_on_apple', id='6d444021-38f8-45c7-861d-5a838b9eed1c', tool_call_id='call_izuYuhq5oqdrWxfd9n6mx0z3')]}
{'messages': [AIMessage(content='성시경의 인기 있는 노래 "두 사람"을 재생했습니다! 즐기세요!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 131, 'total_tokens': 153, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-b3d77ecd-22e8-4859-b037-d310b14fced1-0', usage_metadata={'input_tokens': 131, 'output_tokens': 22, 'total_tokens': 153, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}

과거 상태에서 분기

LangGraph의 체크포인트 기능을 사용하면 과거 상태를 재생하는 것 이상을 할 수 있다. 이전 지점에서 분기하여 에이전트가 다른 경로를 탐색하도록 하거나, 사용자가 워크플로우에서 변경 사항을 "버전 관리"할 수 있다.

이제 특정 시점에서 상태를 편집하는 방법을 보여드리겠습니다. 상태를 업데이트하여 Apple에서 노래를 재생하는 대신 Spotify에서 재생하도록 변경하자.

# 상태에서 마지막 메시지를 가져오자
# 이것은 업데이트하려는 도구 호출이 있는 메시지이다.
last_message = to_replay.values["messages"][-1]


# 호출하는 도구를 업데이트해보자
last_message.tool_calls[0]["name"] = "play_song_on_spotify"

branch_config = app.update_state(
    to_replay.config,
    {"messages": [last_message]},
)

이제 이 새로운 branch_config를 사용하여 변경된 상태에서 여기서부터 실행을 재개할 수 있다. 로그를 보면 도구가 다른 입력으로 호출된 것을 확인할 수 있다.

for event in app.stream(None, branch_config):
    for v in event.values():
        print(v)
{'messages': [ToolMessage(content='Successfully played 성시경 - 두 사람 on Spotify!', name='play_song_on_spotify', id='5c22ef71-c2e0-4c90-af32-2aaf917f60d1', tool_call_id='call_izuYuhq5oqdrWxfd9n6mx0z3')]}
{'messages': [AIMessage(content='성시경의 가장 인기 있는 노래 중 하나인 "두 사람"을 Spotify에서 재생했습니다! 즐감하세요!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 28, 'prompt_tokens': 130, 'total_tokens': 158, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-422e3c5a-40c9-4a10-890f-676d9a5d75b7-0', usage_metadata={'input_tokens': 130, 'output_tokens': 28, 'total_tokens': 158, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}

우리는 상태를 업데이트하여 도구 호출을 하지 않도록 할 수도 있다.

from langchain_core.messages import AIMessage

# 상태에서 마지막 메시지를 가져오자
# 이것은 업데이트하려는 도구 호출이 있는 메시지이다.
last_message = to_replay.values["messages"][-1]

# 마지막 메시지의 ID를 가져와서 그 ID로 새 메시지를 만들어보자.
new_message = AIMessage(
    content="조용한 시간이라 지금 음악을 재생할 수 없어!", id=last_message.id
)

branch_config = app.update_state(
    to_replay.config,
    {"messages": [new_message]},
)
branch_state = app.get_state(branch_config)
print(branch_state.values)
{'messages': [HumanMessage(content='성시경의 가장 인기 있는 노래를 재생해줄래?', additional_kwargs={}, response_metadata={}, id='96b3c962-7eb0-485b-a54b-63b03a2d8d10'), AIMessage(content='조용한 시간이라 지금 음악을 재생할 수 없어!', additional_kwargs={}, response_metadata={}, id='run-86600a8a-e914-4a46-959d-a3c5de8b9f22-0')]}
print(branch_state.next)
()

스냅샷이 업데이트되어 이제 다음 단계가 없다는 것을 알 수 있다.

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