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)
()
스냅샷이 업데이트되어 이제 다음 단계가 없다는 것을 알 수 있다.