Open in Colab Open in LangChain Academy

메시지 필터링 및 트리밍

복습

이제 몇 가지 사항에 대해 더 깊이 이해하게 되었습니다:

  • 그래프 상태 스키마를 커스터마이징하는 방법
  • 커스텀 상태 리듀서를 정의하는 방법
  • 다중 그래프 상태 스키마를 사용하는 방법

목표

이제 LangGraph에서 모델과 함께 이러한 개념들을 사용할 수 있습니다!

다음 몇 개 세션에서는 장기 메모리를 가진 챗봇을 구축해 나갈 것입니다.

챗봇이 메시지를 사용할 것이므로, 먼저 그래프 상태에서 메시지를 다루는 고급 방법에 대해 조금 더 이야기해보겠습니다.

%%capture --no-stderr
%pip install --quiet -U langchain_core langgraph langchain_openai
from dotenv import load_dotenv
 
load_dotenv("../.env", override=True)
True
import os
import getpass
 
 
def _set_env(var: str):
    if not os.environ.get(var):
        os.environ[var] = getpass.getpass(f"{var}: ")
 
 
_set_env("OPENAI_API_KEY")

추적(tracing)을 위해 LangSmith를 사용하겠습니다.

langchain-academy 프로젝트에 로깅할 것입니다.

_set_env("LANGSMITH_API_KEY")
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_PROJECT"] = "langchain-academy"

상태로서의 메시지

먼저 몇 가지 메시지를 정의해보겠습니다.

from langchain_core.messages import AnyMessage, AIMessage, HumanMessage
 
messages: list[AnyMessage] = [
    AIMessage("그러니까 해양 포유류를 연구하고 있다고 하셨나요?", name="Bot")
]
messages.append(
    HumanMessage(
        "네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?",
        name="Lance",
    )
)
 
for m in messages:
    m.pretty_print()
================================== Ai Message ==================================
Name: Bot

그러니까 해양 포유류를 연구하고 있다고 하셨나요?
================================ Human Message =================================
Name: Lance

네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?

이들을 채팅 모델에 전달할 수 있다는 것을 기억하세요.

from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o")
llm.invoke(messages)
AIMessage(content='해양 포유류에 대해 더 깊이 이해하려면 다양한 주제에 걸쳐 공부하는 것이 좋습니다. 여기 몇 가지 중요한 주제를 소개할게요:\n\n1. **고래와 돌고래의 생리학 및 생태학**: 고래, 돌고래, 그리고 작은 고래류의 신체 구조와 생리학적 특성, 그리고 그들의 생태적 역할에 대해 더 많이 알아보세요.\n\n2. **바다표범과 해마**: 이들 포유류는 북극과 남극을 포함한 다양한 해양 환경에 서식합니다. 그들의 생활사, 번식, 먹이 습관 등에 대해 배우세요.\n\n3. **수달과 같은 반수생 포유류**: 일부 포유류는 완전히 해양 생활을 하진 않지만, 수생 환경에서 상당한 시간을 보냅니다. 수달의 경우 물속에서의 사냥 및 생존 전략에 대해 알아보세요.\n\n4. **환경 영향**: 오염, 기후 변화, 해양 쓰레기 등이 해양 포유류에 끼치는 영향을 연구하세요. 이는 보전 노력과 정책 수립에 중요한 요소입니다.\n\n5. **고래의 사회적 구조와 의사소통**: 고래는 복잡한 사회 구조를 가질 수 있으며, 특정한 의사소통 방식과 문화를 가지고 있습니다.\n\n6. **보존과 법률**: 해양 포유류의 보전을 위한 국제적 협정과 법률에 대해 알아보세요. 예를 들어, 국제 포경 위원회(IWC)의 역할도 포함됩니다.\n\n7. **연구 방법론**: 해양 포유류 연구에 사용되는 과학적 방법론 및 기술에 대해 학습하세요. 예를 들어, 음향 추적 기술, 위성 마커, 사진 식별 기술 등이 있습니다.\n\n각각의 주제는 매우 광범위하고 중요한 부분들이니, 관심 있는 부분을 깊이 있게 탐구하시길 추천합니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 431, 'prompt_tokens': 53, 'total_tokens': 484, '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-2024-08-06', 'system_fingerprint': 'fp_f33640a400', 'id': 'chatcmpl-CLQbcZCC0cR1S8pHM9718dQ4iUR7d', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--76eddfa1-a161-4461-a915-57ca188b74f8-0', usage_metadata={'input_tokens': 53, 'output_tokens': 431, 'total_tokens': 484, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})

MessagesState를 사용하여 간단한 그래프에서 채팅 모델을 실행할 수 있습니다.

from IPython.display import Image, display
from langgraph.graph import MessagesState
from langgraph.graph import StateGraph, START, END
 
 
# Node
def chat_model_node(state: MessagesState):
    return {"messages": llm.invoke(state["messages"])}
 
 
# Build graph
builder = StateGraph(MessagesState)
builder.add_node("chat_model", chat_model_node)
builder.add_edge(START, "chat_model")
builder.add_edge("chat_model", END)
graph = builder.compile()
 
# View
# display(Image(graph.get_graph().draw_mermaid_png()))

output = graph.invoke({"messages": messages})
for m in output["messages"]:
    m.pretty_print()
================================== Ai Message ==================================
Name: Bot

그러니까 해양 포유류를 연구하고 있다고 하셨나요?
================================ Human Message =================================
Name: Lance

네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?
================================== Ai Message ==================================

고래 외에도 다양한 해양 포유류가 있습니다. 연구를 시작하시기 좋은 몇 가지 그룹을 소개해 드릴게요:

1. **돌고래와 흰긴수염고래과**: 돌고래는 고래와 가깝지만 더 작은 편이며, 사회적이고 지능이 높은 것으로 알려져 있습니다. 흰긴수염고래과에는 범고래와 같은 포식자도 포함됩니다.

2. **기각류 (바다사자, 물개, 바다코끼리 등)**: 물개류와 바다사자류는 물속과 육지를 모두 잘 이용하며, 바다코끼리는 그 중 가장 큰 종입니다.

3. **해우류 (매너티와 듀공)**: 이들은 크게 매너티와 듀공으로 나뉩니다. 초식성 동물로 천천히 움직이며, 주로 해초를 먹습니다.

4. **수달류**: 주로 민물에서 사는 수달도 있지만, 해달은 해양 환경에 적응하여 살아가는 포유류입니다.

각 그룹은 각자의 행동, 생리학, 서식지 등이 독특하므로 연구를 통해 많은 것을 배울 수 있습니다. 해양 포유류가 인간 활동과 환경 변화에 어떻게 영향을 받는지도 중요한 연구 주제입니다. 관심 있는 분야나 종을 좁힌 후, 특정 종에 대해 더 깊이 파악해 나가시면 좋을 것 같습니다.

리듀서

메시지를 다룰 때 실질적인 과제는 장기 실행 대화를 관리하는 것입니다.

주의하지 않으면 장기 실행 대화는 모델에 계속 증가하는 메시지 목록을 전달하기 때문에 높은 토큰 사용량과 지연 시간을 초래합니다.

이를 해결할 수 있는 몇 가지 방법이 있습니다.

먼저 RemoveMessageadd_messages 리듀서를 사용하여 본 트릭을 떠올려보세요.

from langchain_core.messages import RemoveMessage
 
 
# Nodes
def filter_messages(state: MessagesState):
    # 가장 최근의 2개 메시지를 제외한 모든 메시지를 삭제하세요
    delete_messages = [RemoveMessage(id=m.id) for m in state["messages"][:-2]]
    return {"messages": delete_messages}
 
 
def chat_model_node(state: MessagesState):
    return {"messages": [llm.invoke(state["messages"])]}
 
 
# Build graph
builder = StateGraph(MessagesState)
builder.add_node("filter", filter_messages)
builder.add_node("chat_model", chat_model_node)
builder.add_edge(START, "filter")
builder.add_edge("filter", "chat_model")
builder.add_edge("chat_model", END)
graph = builder.compile()
 
# View
# display(Image(graph.get_graph().draw_mermaid_png()))

# Message list with a preamble
messages: list[AnyMessage] = [AIMessage("안녕하세요.", name="Bot", id="1")]
messages.append(HumanMessage("안녕하세요.", name="Lance", id="2"))
messages.append(
    AIMessage("그러니까 해양 포유류를 연구하고 있다고 하셨나요?", name="Bot", id="3")
)
messages.append(
    HumanMessage(
        "네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?",
        name="Lance",
        id="4",
    )
)
 
# Invoke
output = graph.invoke({"messages": messages})
for m in output["messages"]:
    m.pretty_print()
================================== Ai Message ==================================
Name: Bot

그러니까 해양 포유류를 연구하고 있다고 하셨나요?
================================ Human Message =================================
Name: Lance

네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?
================================== Ai Message ==================================

고래와 함께 해양 포유류에 대해 연구하려면 다양한 종과 그들의 생태에 대해 배울 수 있습니다. 다음은 몇 가지 주요 해양 포유류 그룹과 연구할 수 있는 주제들입니다.

1. **돌고래**: 돌고래의 사회적 행동, 의사소통 방식, 지능 수준 등을 연구할 수 있습니다. 돌고래의 음향 신호와 무리 생활의 사회적 구조도 중요한 연구 주제입니다.

2. **물범 및 바다사자**: 이들은 기각류( Pinnipedia)에 속하며, 하위 그룹으로 물범(Phocidae), 바다사자(Otariidae) 및 바다코끼리(Odobenidae)가 포함됩니다. 이들의 육상 번식습성, 수영과 잠수 능력, 먹이 습성을 연구할 수 있습니다.

3. **바다소 및 듀공**: 이들은 초식성 해양 포유류로, 서식지인 해초밭의 상태를 통해 환경 변화를 감지할 수 있습니다. 이들의 보전 상태와 인간 활동의 영향을 연구하는 것도 중요합니다.

4. **바다수달**: 이들은 해양 생태계에서 중요한 역할을 하며, 특히 해양 먹이 사슬과 해초밭의 건강에 큰 영향을 미칩니다. 수달의 먹이 활동과 생태적 중요성을 연구할 수 있습니다.

5. **북극곰**: 온도 상승에 민감한 종으로, 기후 변화가 이들이 어떻게 사냥하고 생존하는지에 미치는 영향을 연구할 수 있습니다.

각 종의 생리학, 번식, 이동 패턴, 보호 및 보존 상태, 환경 및 인간 활동의 영향을 이해하는 것이 중요합니다. 최신 연구 및 발견을 지속적으로 업데이트하고 해양 포유류 보전을 위한 노력을 기울이는 것이 연구자에게 필수적입니다.

메시지 필터링

그래프 상태를 수정할 필요가 없거나 원하지 않는다면, 채팅 모델에 전달하는 메시지를 필터링하기만 하면 됩니다.

예를 들어, 모델에 필터링된 목록을 전달하면 됩니다: llm.invoke(messages[-1:]).

# Node
def chat_model_node(state: MessagesState):
    return {"messages": [llm.invoke(state["messages"][-1:])]}
 
 
# Build graph
builder = StateGraph(MessagesState)
builder.add_node("chat_model", chat_model_node)
builder.add_edge(START, "chat_model")
builder.add_edge("chat_model", END)
graph = builder.compile()
 
# View
# display(Image(graph.get_graph().draw_mermaid_png()))

기존 메시지 목록을 가져와서 위의 LLM 응답을 추가하고, 후속 질문을 추가해보겠습니다.

messages.append(output["messages"][-1])
messages.append(HumanMessage("일각고래에 대해 더 알려줘!", name="Lance"))
for m in messages:
    m.pretty_print()
================================== Ai Message ==================================
Name: Bot

안녕하세요.
================================ Human Message =================================
Name: Lance

안녕하세요.
================================== Ai Message ==================================
Name: Bot

그러니까 해양 포유류를 연구하고 있다고 하셨나요?
================================ Human Message =================================
Name: Lance

네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?
================================== Ai Message ==================================

고래와 함께 해양 포유류에 대해 연구하려면 다양한 종과 그들의 생태에 대해 배울 수 있습니다. 다음은 몇 가지 주요 해양 포유류 그룹과 연구할 수 있는 주제들입니다.

1. **돌고래**: 돌고래의 사회적 행동, 의사소통 방식, 지능 수준 등을 연구할 수 있습니다. 돌고래의 음향 신호와 무리 생활의 사회적 구조도 중요한 연구 주제입니다.

2. **물범 및 바다사자**: 이들은 기각류( Pinnipedia)에 속하며, 하위 그룹으로 물범(Phocidae), 바다사자(Otariidae) 및 바다코끼리(Odobenidae)가 포함됩니다. 이들의 육상 번식습성, 수영과 잠수 능력, 먹이 습성을 연구할 수 있습니다.

3. **바다소 및 듀공**: 이들은 초식성 해양 포유류로, 서식지인 해초밭의 상태를 통해 환경 변화를 감지할 수 있습니다. 이들의 보전 상태와 인간 활동의 영향을 연구하는 것도 중요합니다.

4. **바다수달**: 이들은 해양 생태계에서 중요한 역할을 하며, 특히 해양 먹이 사슬과 해초밭의 건강에 큰 영향을 미칩니다. 수달의 먹이 활동과 생태적 중요성을 연구할 수 있습니다.

5. **북극곰**: 온도 상승에 민감한 종으로, 기후 변화가 이들이 어떻게 사냥하고 생존하는지에 미치는 영향을 연구할 수 있습니다.

각 종의 생리학, 번식, 이동 패턴, 보호 및 보존 상태, 환경 및 인간 활동의 영향을 이해하는 것이 중요합니다. 최신 연구 및 발견을 지속적으로 업데이트하고 해양 포유류 보전을 위한 노력을 기울이는 것이 연구자에게 필수적입니다.
================================ Human Message =================================
Name: Lance

일각고래에 대해 더 알려줘!
# Invoke, using message filtering
output = graph.invoke({"messages": messages})
for m in output["messages"]:
    m.pretty_print()
================================== Ai Message ==================================
Name: Bot

안녕하세요.
================================ Human Message =================================
Name: Lance

안녕하세요.
================================== Ai Message ==================================
Name: Bot

그러니까 해양 포유류를 연구하고 있다고 하셨나요?
================================ Human Message =================================
Name: Lance

네, 고래에 대해서는 알고 있어요. 그런데 다른 것들은 무엇을 배워야 할까요?
================================== Ai Message ==================================

고래와 함께 해양 포유류에 대해 연구하려면 다양한 종과 그들의 생태에 대해 배울 수 있습니다. 다음은 몇 가지 주요 해양 포유류 그룹과 연구할 수 있는 주제들입니다.

1. **돌고래**: 돌고래의 사회적 행동, 의사소통 방식, 지능 수준 등을 연구할 수 있습니다. 돌고래의 음향 신호와 무리 생활의 사회적 구조도 중요한 연구 주제입니다.

2. **물범 및 바다사자**: 이들은 기각류( Pinnipedia)에 속하며, 하위 그룹으로 물범(Phocidae), 바다사자(Otariidae) 및 바다코끼리(Odobenidae)가 포함됩니다. 이들의 육상 번식습성, 수영과 잠수 능력, 먹이 습성을 연구할 수 있습니다.

3. **바다소 및 듀공**: 이들은 초식성 해양 포유류로, 서식지인 해초밭의 상태를 통해 환경 변화를 감지할 수 있습니다. 이들의 보전 상태와 인간 활동의 영향을 연구하는 것도 중요합니다.

4. **바다수달**: 이들은 해양 생태계에서 중요한 역할을 하며, 특히 해양 먹이 사슬과 해초밭의 건강에 큰 영향을 미칩니다. 수달의 먹이 활동과 생태적 중요성을 연구할 수 있습니다.

5. **북극곰**: 온도 상승에 민감한 종으로, 기후 변화가 이들이 어떻게 사냥하고 생존하는지에 미치는 영향을 연구할 수 있습니다.

각 종의 생리학, 번식, 이동 패턴, 보호 및 보존 상태, 환경 및 인간 활동의 영향을 이해하는 것이 중요합니다. 최신 연구 및 발견을 지속적으로 업데이트하고 해양 포유류 보전을 위한 노력을 기울이는 것이 연구자에게 필수적입니다.
================================ Human Message =================================
Name: Lance

일각고래에 대해 더 알려줘!
================================== Ai Message ==================================

일각고래, 학명으로는 Monodon monoceros,는 북극해와 주변 바다에서 주로 발견되는 고래류입니다. 이들은 이마에서 앞으로 뻗어 있는 긴 나선형 뿔로 유명한데, 사실 이 "뿔"은 왼쪽 상악의 이가 매우 길게 자라난 것입니다. 수컷에게서 주로 발견되며, 이는 경쟁과 짝짓기에 중요한 역할을 하는 것으로 추정됩니다.

일각고래의 주요 특징과 행동은 다음과 같습니다:

1. **긴 뿔**: 보통 수컷에게서 약 1.5~3미터 길이로 자라며, 이는 탐지기와 같은 역할을 하기도 한다고 합니다. 일부 암컷도 뿔을 가질 수 있지만, 그 길이는 훨씬 짧습니다.

2. **생태와 서식지**: 일각고래는 보통 대서양 북부와 러시아 주변의 차가운 북극해에서 발견됩니다. 여름에는 얼음이 덜 있는 지역으로 이동하고, 겨울에는 얼음 아래로 잠수해 생활합니다.

3. **식습관**: 주로 물고기, 오징어, 새우 등을 먹습니다. 먹이를 찾아 얼음 아래로 수백 미터까지 잠수할 수 있습니다.

4. **사회적 행동**: 일각고래는 사회적 동물로, 작은 무리를 지어 다니는 경향이 있습니다. 이들은 특유의 소리로 서로 의사소통하며, 이는 탐색과 사냥에 유용하게 사용됩니다.

5. **보전 상태**: 현재 일각고래는 특정한 위협에 직면해 있지 않지만, 기후 변화로 인해 서식지가 변화하고, 사냥과 관련된 위험이 존재합니다. 따라서 적절한 보전 노력이 필요합니다.

이들은 독특한 외모와 북극 생태계에 필수적인 존재로, 과학자들에게도 많은 흥미와 연구의 대상이 되고 있습니다.

상태에는 모든 메시지가 있습니다.

하지만 LangSmith 추적을 보면 모델 호출이 마지막 메시지만 사용하는 것을 확인할 수 있습니다:

https://smith.langchain.com/public/9a3078a9-2408-4f8e-ab02-dfbfbc760839/r

메시지 트리밍(Trim messages)

또 다른 접근 방식은 설정된 토큰 수를 기반으로 trim messages하는 것입니다.

이는 메시지 기록을 지정된 토큰 수로 제한합니다.

필터링은 에이전트 간의 메시지 중 사후 부분 집합만 반환하는 반면, 트리밍은 채팅 모델이 응답하는 데 사용할 수 있는 토큰 수를 제한합니다.

아래의 trim_messages를 참조하세요.

from langchain_core.messages import trim_messages
 
 
# Node
def chat_model_node(state: MessagesState):
    messages = trim_messages(
        state["messages"],
        max_tokens=100,
        strategy="last",
        token_counter=ChatOpenAI(model="gpt-4o"),
        allow_partial=False,
    )
    return {"messages": [llm.invoke(messages)]}
 
 
# Build graph
builder = StateGraph(MessagesState)
builder.add_node("chat_model", chat_model_node)
builder.add_edge(START, "chat_model")
builder.add_edge("chat_model", END)
graph = builder.compile()
 
# View
# display(Image(graph.get_graph().draw_mermaid_png()))

messages.append(output["messages"][-1])
messages.append(HumanMessage("오카스가 어디에 사는지 말해줘!", name="Lance"))
# Example of trimming messages
trim_messages(
    messages,
    max_tokens=100,
    strategy="last",
    token_counter=ChatOpenAI(model="gpt-4o"),
    allow_partial=False,
)
[HumanMessage(content='오카스가 어디에 사는지 말해줘!', additional_kwargs={}, response_metadata={}, name='Lance')]
# Invoke, using message trimming in the chat_model_node
messages_out_trim = graph.invoke({"messages": messages})

LangSmith 추적을 살펴 모델 호출을 확인해 보겠습니다:

https://smith.langchain.com/public/84ceedc2-7b5a-45e7-a793-4347eba8a9e3/r