GenAI Course · Module 8 · Session 30

Conversational Chains & Tracing

⏱ 13 min read🎯 Intermediate🖼 2 diagrams🧪 2 live demos🐍 Python code

Real apps work with many documents and long conversations. A model’s context window is limited, so we need patterns to combine documents, ways to remember what the user said earlier, and a way to see what is happening inside every chain. That is this session.

In this session

  • Four document chains: Stuff, Map-Reduce, Refine and Map-Rerank
  • Managing conversation memory: full history, windows, summaries and trimming
  • The modern way to add memory: LangGraph persistence (tested code)
  • Tracing and debugging with LangSmith

01Four ways to work with many documents

Suppose you must summarise 50 customer-call transcripts, or answer a question from 20 retrieved chunks. Here are the classic patterns:

ChainLLM callsParallel?Best for
Stuff1–Small sets that fit in context
Map-ReduceN + 1YesSummarising many long documents
RefineNNoBuilding a detailed answer that needs context
Map-RerankNYesFinding the single best answer source
🗂️

Map-Reduce is like a class project. Each student summarises one chapter (map), then the group leader combines them into one report (reduce). Refine is one student reading chapter by chapter and updating the same notes.

Older LangChain versions had ready-made load_summarize_chain and load_qa_chain helpers for these. Today it is simpler to write them yourself with LCEL:

map_reduce.py
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

map_chain = ChatPromptTemplate.from_template("Summarise this support call in one line:\n{chunk}") | llm | StrOutputParser()
reduce_chain = ChatPromptTemplate.from_template("Combine these summaries into the top issues:\n{summaries}") | llm | StrOutputParser()

partials = map_chain.batch([{"chunk": c} for c in call_transcripts])     # map, in parallel
print(reduce_chain.invoke({"summaries": "\n".join(partials)}))         # reduce
['Card blocked after ATM failure.', 'Refund delayed by 10 days.', 'App login OTP not received.'] Top issues: card blocks, refund delays, OTP failures.

Output from a test run with a fake model on langchain-core.

02Managing conversation memory

LLMs are stateless: each call knows nothing about earlier calls. “Memory” simply means sending the relevant past messages again with each new question. The art is choosing which messages to send.

For long-term facts (name, preferences across sessions), store them separately and fetch when needed.

The modern way: LangGraph persistence

In current LangChain, the old memory classes (like ConversationBufferMemory and RunnableWithMessageHistory) are deprecated. The recommended approach is a small LangGraph app with a checkpointer that saves each conversation thread. We will learn LangGraph properly in Session 37.

chat_memory.py
# pip install langgraph langchain-openai
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import SystemMessage, trim_messages

def chatbot(state: MessagesState):
    history = trim_messages(state["messages"], max_tokens=2000, token_counter=llm, strategy="last")
    reply = llm.invoke([SystemMessage("You are a bank support assistant.")] + history)
    return {"messages": [reply]}

graph = StateGraph(MessagesState)
graph.add_node("chatbot", chatbot)
graph.add_edge(START, "chatbot")
graph.add_edge("chatbot", END)
app = graph.compile(checkpointer=MemorySaver())       # use a database checkpointer in production

cfg = {"configurable": {"thread_id": "customer-42"}}
app.invoke({"messages": [("user", "Hi, my name is Priya.")]}, cfg)
out = app.invoke({"messages": [("user", "What is my name?")]}, cfg)
print(out["messages"][-1].content, "|", len(out["messages"]), "messages in thread")
Your name is Priya. | 4 messages in thread
Privacy: memory stores what customers say. Decide what you are allowed to keep, mask sensitive data like account numbers, and set an expiry for stored threads.

03Tracing with LangSmith

When a chain gives a bad answer, you need to see which step went wrong. LangSmith records every run as a tree: each prompt, model call and parser, with inputs, outputs, tokens, cost and time.

A trace shows where the time, tokens and errors are.

Turning it on needs only environment variables; your chain code does not change:

.env
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=your-key-here
LANGSMITH_PROJECT=bank-support-bot

What to use traces for:

Open-source alternatives include Langfuse, Arize Phoenix and OpenTelemetry-based tools.

Go deeper: memory with checkpointers and middleware is covered in the LangChain notes.

04Summary

05Quick quiz

1. You must summarise 200 long transcripts quickly. Best chain?

2. Which chain makes only one LLM call?

3. How does a chatbot “remember” your name?

4. A downside of window memory (last k turns)?

5. What does LangSmith tracing show you?

06References

Created by Ajeetkumar