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.
