Skip to course content
Free LangChain course

LangChain for LLM Applications and RAG

Unit 01.03: Reading a chain as data flow

A chain is a sequence of type transformations. Reading it that way makes most chain bugs obvious.

Name the type at every boundary

A dict goes in, becomes a prompt value, becomes a message, becomes a string. Each | is a boundary where a type changes.

The code prints the type and value at each stage.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.language_models.fake_chat_models import FakeListChatModel

prompt = ChatPromptTemplate.from_messages([("human", "Summarise: {text}")])
model = FakeListChatModel(responses=["A short summary."])
chain = prompt | model | StrOutputParser()

# The pipe operator composes runnables. Each step's output is the next input.
stages = {
    "input":            {"text": "a long document"},
    "after prompt":     prompt.invoke({"text": "a long document"}),
    "after model":      model.invoke(prompt.invoke({"text": "a long document"})),
}
for name, value in stages.items():
    print(f"{name:14} {type(value).__name__:22} {str(value)[:52]}")

print(f"\nfinal          str                    {chain.invoke({'text': 'a long document'})}")

# Reading a chain means naming the type at each boundary. A chain that fails
# usually fails at a boundary -- a parser handed a message instead of a string,
# or a prompt handed a string instead of a dict.

The types are the useful part: dict to ChatPromptValue to AIMessage to str. A chain that fails almost always fails at one of these boundaries - a parser handed a message instead of a string, or a prompt handed a string instead of a dict.

That is also why the error messages can be confusing. The failure is reported inside the framework's internals, several frames from the line you wrote, and the actual problem is a type mismatch you can see in three print statements.

The mistake this prevents

The mistake is debugging a chain by changing the prompt. If the failure is a type mismatch at a boundary, no wording changes it. Print the intermediate values first and find out which boundary is wrong.

Takeaway

Read a chain as types at boundaries. Printing the value at each stage localises most failures faster than reading the traceback.