Skip to content

01: Conversation History

The previous chatbot can answer once.

Now the user asks:

What did I just say I like?

If we only send that sentence to the model, it has no idea where "just" points. A model is not an automatic memory store. On each call, it sees only the messages we pass in.

This chapter adds one thing: keep the current conversation history and pass it back on every turn.

Code First

from __future__ import annotations

from pathlib import Path

from agent_patterns_lab.runtime import Message, MockLLM, Tracer


def main() -> None:
    tracer = Tracer()
    model = MockLLM(
        [
            "Got it: you like tea, local food, and easy walking.",
            "I still remember your preferences: tea, local food, and easy walking.",
        ]
    )

    messages: list[Message] = []

    def chat(user_text: str) -> str:
        messages.append(Message(role="user", content=user_text))
        answer = model.complete(messages, tracer=tracer)
        messages.append(Message(role="assistant", content=answer))
        return answer

    print(chat("Remember this: I like tea, local food, and easy walking."))
    print(chat("What preferences did I just give you?"))

    trace_path = tracer.export_jsonl(Path(".traces") / "01_conversation_history.jsonl")
    print(f"[trace] {trace_path}")


if __name__ == "__main__":
    main()

Run:

uv run python examples/01_conversation_history.py

Expected output:

Got it: you like tea, local food, and easy walking.
I still remember your preferences: tea, local food, and easy walking.
[trace] .traces/01_conversation_history.jsonl

What Changed

The important part is not a clever prompt. It is these three lines:

messages.append(Message(role="user", content=user_text))
answer = model.complete(messages, tracer=tracer)
messages.append(Message(role="assistant", content=answer))

Each turn:

  1. Append the user's new message.
  2. Send the full messages list to the model.
  3. Append the assistant's answer.

The next call no longer sees a lonely "do you remember?" It sees the current conversation.

flowchart TD
  U1["User: I like tea and local food"] --> H["messages"]
  H --> M1["model.complete"]
  M1 --> A1["Assistant: got it"]
  A1 --> H
  U2["User: what did I just say I like?"] --> H
  H --> M2["model.complete"]
  M2 --> A2["Assistant: tea, local food, easy walking"]

What It Fixes

Conversation history fixes references inside the current session.

The travel assistant can now handle requests like:

  • "Replan it using the preferences I just gave you."
  • "Make the afternoon route less tiring."
  • "Keep the 300 RMB budget from before."

The memory here is plain: Python resends previous messages. The model did not permanently learn anything.

What It Does Not Fix

Conversation history is not full memory.

Misunderstanding Reality
"History means long-term memory" No. Restart the program or start a new session, and it is gone unless you persist it.
"More history is always better" Not always. Long context costs more and can distract the model.
"The model will maintain clean preferences" Not guaranteed. It sees text; it may not maintain a structured profile.
"It now knows live facts" No. It only sees what you pass in.

This layer fixes one problem: the current conversation should not lose its own thread.

What To Remember

If the user's next turn depends on the previous turn, pass the history back in.

But the answer is still free-form text. If a UI needs morning / afternoon / evening / packing, prose becomes a problem.

Next: fix the shape in 02: Structured Output.