Skip to course content
Free LangChain course

LangChain for LLM Applications and RAG

Unit 03.00: Asking for a shape instead of prose

When the next step is code rather than a person, asking for a shape removes an interpretation step - and interpretation is where meaning drifts.

Format instructions plus a parser

The parser supplies the instructions telling the model what shape to produce, and then parses what comes back. Both halves come from the same object, so they cannot disagree.

The code classifies a ticket into a dict.

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

parser = JsonOutputParser()
prompt = ChatPromptTemplate.from_messages([
    ("human", "Classify the ticket. {format_instructions}\n\nTicket: {ticket}"),
]).partial(format_instructions=parser.get_format_instructions())

model = FakeListChatModel(responses=['{"category": "billing", "urgency": "low"}'])
chain = prompt | model | parser

result = chain.invoke({"ticket": "I was charged twice"})
print("parsed:", result, type(result).__name__)
print("usable downstream:", result["category"])

print("\nformat instructions sent to the model:")
print(parser.get_format_instructions()[:180], "...")

# Prose has to be interpreted by whatever comes next; a dict does not. The
# difference matters most at the boundary, where an interpretation step is
# where meaning quietly drifts.

The result is a dict and result["category"] works. Compare with a sentence saying the ticket is about billing: the next step has to find the category in the prose, and that finding step is a second place to be wrong.

.partial() binds the format instructions once at construction. Without it you would pass them on every invocation, and eventually someone would forget - producing a model with no idea what shape you wanted and a parser expecting one.

The mistake this prevents

The mistake is asking for JSON in the prompt text and parsing with json.loads. It works until the model wraps the JSON in an explanation, at which point you write a regex, and then the regex becomes the thing you maintain. Let the parser own both ends.

Takeaway

Use a parser's format instructions and its parsing together. A structured result removes the interpretation step between the model and whatever runs next.