AI & LLM APIs cookbook · Intermediate

Store and trim chat history in a database

Keep multi-turn conversations across requests and servers without sending ever-growing histories.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Store every turn in a table keyed by conversation. On each request, load the latest N turns, prepend a stored summary of older turns, and periodically refresh that summary with a small model.

Solution

import anthropic

client = anthropic.Anthropic()
KEEP_TURNS = 12


def load_context(db, conversation_id: str) -> tuple[str, list[dict]]:
    summary = db.scalar("SELECT summary FROM conversations WHERE id = %s", conversation_id) or ""
    rows = db.fetch("SELECT role, content FROM turns WHERE conversation_id = %s ORDER BY id DESC LIMIT %s",
                    conversation_id, KEEP_TURNS)
    turns = [{"role": r.role, "content": r.content} for r in reversed(rows)]
    while turns and turns[0]["role"] != "user":         # history must start with a user turn
        turns.pop(0)
    return summary, turns


def chat(db, conversation_id: str, user_text: str) -> str:
    summary, turns = load_context(db, conversation_id)
    system = "You are a helpful assistant." + (f"\n<earlier_conversation>{summary}</earlier_conversation>" if summary else "")
    turns.append({"role": "user", "content": user_text})
    response = client.messages.create(model="claude-sonnet-5-5", max_tokens=800, system=system, messages=turns)
    reply = response.content[0].text
    db.execute("INSERT INTO turns (conversation_id, role, content) VALUES (%s, 'user', %s), (%s, 'assistant', %s)",
               conversation_id, user_text, conversation_id, reply)
    return reply


def refresh_summary(db, conversation_id: str, older_text: str) -> None:
    response = client.messages.create(
        model="claude-haiku-5-5", max_tokens=300,
        messages=[{"role": "user", "content": f"Summarise the key facts and decisions:\n{older_text}"}])
    db.execute("UPDATE conversations SET summary = %s WHERE id = %s", response.content[0].text, conversation_id)

Pitfalls

  • Starting the message list with an assistant turn after trimming.
  • Summaries that drop facts the user will ask about (names, numbers); test them.
  • Keeping conversations forever with personal data; set retention.

Variations

  • Let users pin facts ("my company is Northwind") into a separate memory table.
  • Put the summary after a cached stable system prompt so caching still works.

Further reading: Context windows

Reference: https://docs.anthropic.com/en/docs/build-with-claude/context-windows

More AI & LLM APIs recipes

Was this page helpful?