langchain-ai / langchain

🦜🔗 Build context-aware reasoning applications
https://python.langchain.com
MIT License
88.45k stars 13.89k forks source link

DOC: <Issue related to /v0.2/docs/tutorials/qa_chat_history/> #22400

Open amersheikh opened 1 month ago

amersheikh commented 1 month ago

URL

https://python.langchain.com/v0.2/docs/tutorials/qa_chat_history/

Checklist

Issue with current documentation:

How do you limit the number of previous conversations for the checkpoint memory ? As it is shown in the documentation, The checkpoint just grows and grows which eventually exceeds the LLM input token limit.

https://python.langchain.com/v0.2/docs/tutorials/qa_chat_history/

Idea or request for content:

Please add a section on how to limit the the number of previopus conversations that go into a checkpoint.

https://python.langchain.com/v0.2/docs/tutorials/qa_chat_history/

wulifu2hao commented 4 weeks ago

I suspect it's not just an issue with documentation. The react agent is created by using from langgraph.prebuilt import create_react_agent and its implementation in langgraph keeps the message in AgentState and currently there is no implementation to provide some kind of buffer window memory to manage these messages like ConversationBufferWindowMemory

amersheikh commented 4 weeks ago

The workaround is to instantiate a create_react_agent with tools. Then place the agent in a LCEL chain with a prompt and then to wrap all of it with RunnableWithMessageHistory. See:- https://python.langchain.com/v0.2/docs/tutorials/chatbot/ The message list can be limited quite easily.

wulifu2hao commented 4 weeks ago

Thanks @amersheikh it does work as a workaround

# create agent executor without checkpointer
agent_executor = chat_agent_executor.create_tool_calling_executor(
    llm, tools
)

# manage message history limit
from langchain_core.runnables import RunnablePassthrough
def filter_messages(messages, k=3):
    return messages[-k:]

chain = (
    RunnablePassthrough.assign(messages=lambda x: filter_messages(x["messages"]))
    | agent_executor
)

# manage message history using a store
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]
with_message_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="messages",
)

config = {"configurable": {"session_id": "abc20"}}  
# 1st run: tell AI my name
response = with_message_history.invoke(
    {
        "messages": [HumanMessage(content="hi, I'm Bob")],
    },
    config=config,
)
print(response)

# 2nd run: give AI some non-sense information
response = with_message_history.invoke(
    {
        "messages": [HumanMessage(content="what is 1+1?")],
    },
    config=config,
)
print(response)

# 3rd run: ask AI what's my name, it should have forgeten
response = with_message_history.invoke(
    {
        "messages": [HumanMessage(content="what's my name?")],
    },
    config=config,
)
print(response)

However I'm not sure if it is a idiomatic way of using the StateGraph. It seems to be a desirable feature for it to provide a way of limiting message history