LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
43 small wins to finish your pathNext lesson

State beyond messages

An agent's state is more than its messages. Add keys of your own with state_schema, and tools can read and update them as the conversation goes on.

The shop wants to count how many orders a customer looked up in one conversation. That count belongs to the thread, like the messages, so it goes in the state: the dictionary the checkpointer saves after each step.

Exampleagent.py
from langchain.agents import AgentState


class DeskState(AgentState):
    lookups: int

AgentState is the default state, with its messages key. Subclassing it adds keys, and state_schema=DeskState gives the agent the bigger shape.

A tool that updates the state

Exampletools.py
from langchain.messages import ToolMessage
from langchain.tools import ToolRuntime, tool
from langgraph.types import Command

ORDERS = {"A17": "shipped on 3 March", "C40": "waiting for stock"}


@tool
def lookup_order(order_id: str, runtime: ToolRuntime) -> Command:
    """Look up an order's shipping status by its id, such as A17."""
    count = runtime.state.get("lookups", 0) + 1
    text = f"{order_id} {ORDERS.get(order_id, 'is not an order we have')}."
    reply = ToolMessage(text, tool_call_id=runtime.tool_call_id)
    return Command(update={"lookups": count, "messages": [reply]})

runtime.state is the current state. To change it, the tool returns a Command with an update: the new count, and the tool message it would otherwise have returned. The tool message has to be there, tagged with the call's id, because every tool call needs its result.

Exampleagent.py, continued
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from shop_model import ShopModel

agent = create_agent(ShopModel(), tools=[lookup_order], state_schema=DeskState,
                     checkpointer=InMemorySaver())
thread = {"configurable": {"thread_id": "ravi-1"}}
Example
agent.invoke({"messages": [{"role": "user", "content": "Where is A17?"}]}, thread)
result = agent.invoke({"messages": [{"role": "user", "content": "And C40?"}]}, thread)

print(result["lookups"])
print(result["messages"][-1].text)

The count is 2 after two lookups in two calls. It came back with the conversation because it lives in the same state, saved by the same checkpointer.

Setting a key when you invoke

Example
result = agent.invoke({"messages": [{"role": "user", "content": "Where is A17?"}], "lookups": 10},
                      {"configurable": {"thread_id": "fresh"}})
print(result["lookups"])

Keys other than messages can be passed in with the input. This thread started at 10, and one lookup made it 11. Lesson 9's context is fixed for one call and never saved; state is saved and can change as the conversation goes on.

Try it yourself
  • Ask about A17 and C40 in one message and read the error when both calls update lookups in the same step.
  • Add a key last_order: str to DeskState and set it in the tool's update.
  • Print agent.get_state(thread).values.keys().

Every expert started right here.