# Agent with memory

## 1. Checkpointer

```python
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver()
agent = create_agent(
    model="openai:gpt-5.5",
    tools=[...],
    checkpointer=checkpointer,
)
```

InMemorySaver is for dev. SqliteSaver (`langgraph-checkpoint-sqlite`) for local durable runs, PostgresSaver (`langgraph-checkpoint-postgres`) for production.

## 2. Thread ids

Every invoke or stream passes a thread id in the config. The thread is the conversation; same id resumes, new id starts fresh.

```python
config = {"configurable": {"thread_id": "user-123"}}
result = agent.invoke({"messages": [{"role": "user", "content": "Hi"}]}, config)
```

## 3. Custom state

Extend AgentState with a TypedDict for your own memory fields, and pass it as `state_schema`. Static per-run data goes through `context=` with `context_schema=`.

```python
class CustomState(AgentState):
    user_name: str
```

## 4. Write memory from tools

Tools receive a ToolRuntime and can return a Command that updates state:

```python
from langchain.tools import tool, ToolRuntime
from langgraph.types import Command
from langchain.messages import ToolMessage

@tool
def update_user_info(runtime: ToolRuntime) -> Command:
    # look up the user, then update state
    return Command(update={
        "user_name": "John Smith",
        "messages": [ToolMessage(content="updated", tool_call_id=runtime.tool_call_id)],
    })
```

## Rules

- No checkpointer means no memory, even with a thread id. Both are required.
- One thread id per conversation. Sharing a thread id across users mixes their histories.
- Prefer returning state updates from tools over stuffing everything into messages; messages are the expensive memory.