LangChain Series #3: Prompts Explained — Prompt Templates, Chat Prompts, Dynamic Prompting… LangChain introduces dynamic prompt templates and chat prompts to improve LLM output quality. The framework supports PromptTemplate for reusable dynamic prompts and ChatPromptTemplate for chat models with message history. These tools enable developers to generate customized instructions and manage conversations efficiently. No matter how powerful a model is, the quality of its output depends heavily on the instructions you provide. In LangChain, prompts are much more than simple strings. They allow developers to dynamically generate instructions, manage conversations, inject chat history, and even enforce structured outputs. A prompt is an instruction, question, or input given to a model to guide its response. For example: Explain what a Transformer model is. orSummarize the Attention Is All You Need paper. The model uses this prompt as context to generate an output. One of the first concepts to understand is the difference between static and dynamic prompting. Static prompts use hardcoded instructions. Only the user input changes while the prompt structure remains the same. Example: user input = "Attention Is All You Need" prompt = f"Summarize this topic: {user input}" Generated prompt: Summarize this topic: Attention Is All You Need Why is it called Static? Because the structure never changes. Summarize this topic: will always remain the same. Limitations of Static Prompts: While simple, static prompts have several drawbacks: This is where Prompt Templates become useful. Dynamic prompts generate instructions at runtime. Instead of hardcoding values, placeholders are used and filled dynamically. Example: Explain the research paper {paper}in a {style} stylewith a {length} explanation. Generated prompt: Explain the research paper Attention Is All You Needin a Beginner-Friendly stylewith a Long explanation. The prompt changes based on user selections. LangChain provides PromptTemplate to create reusable dynamic prompts. python from langchain core.prompts import PromptTemplatetemplate = PromptTemplate input variables= "paper", "style", "length", "style instruction" , validate template=True, template="""Please summarize the research paper titled "{paper}"Explanation Style: {style}Explanation Length: {length}Style Guidelines:{style instruction}""" Create once and use many times. PromptTemplate checks missing variables. validate template=True helps catch mistakes early. Prompt Templates work seamlessly with: Updating a single template updates your entire application. The template can be populated using: final prompt = template.invoke { "paper": paper input, "style": style input, "length": length input, "style instruction": style instructions style input } This creates a customized prompt based on user selections. Traditional LLMs accept plain text: Input ↓String ↓Output Chat Models work differently. They accept a sequence of messages. System MessageHuman MessageAI Message and return an AI response. LangChain provides three primary message types. from langchain core.messages import SystemMessage, HumanMessage, AIMessage Defines the model’s behavior. SystemMessage content="You are a concise AI assistant." Represents user input. HumanMessage content="Tell me about LangChain." Stores previous responses. AIMessage content=response.content ChatPromptTemplate helps create dynamic prompts for chat models. Example from our project: python from langchain core.prompts import ChatPromptTemplatechat template = ChatPromptTemplate 'system', 'You are a helpful {domain} expert' , 'human', 'Explain in simple terms what is {topic}' prompt = chat template.invoke { 'domain': 'machine learning', 'topic': 'diffusion models'} Instead of managing message objects manually, we can generate them dynamically. Benefits: Real chatbots need conversation history. LangChain solves this using: MessagesPlaceholder Example: python from langchain core.prompts import ChatPromptTemplate,MessagesPlaceholderchat template = ChatPromptTemplate 'system', 'You are a helpful customer support agent' , MessagesPlaceholder variable name='chat history' , 'human', '{query}' They allow dynamic insertion of: without manually rebuilding prompts every time. This becomes extremely useful in: Chatbots, Agents, Customer Support Systems So far, our models have returned plain text responses. Example: The sentiment of the review is positive. While this is easy for humans to read, it is difficult for applications to process programmatically. Imagine building: Instead of plain text, we often need structured data like: { "sentiment": "positive", "summary": "The product received favorable feedback."} This is where Structured Outputs and Output Parsers become useful. Structured Outputs allow LLMs to return data in predefined formats. Instead of generating free-form text, we define a schema that the model should follow. User Input ↓ LLM ↓Structured Response LangChain supports multiple ways to define these structures. TypedDict: Use TypedDict when:- Example: python from typing import TypedDictclass Person TypedDict : name: str age: int Why TypedDict Has No Validation Consider: person = { "name": "Bob", "age": "25"} Even though age should be an integer, TypedDict will not raise an error. This is because TypedDict provides type hints but does not validate data. Pydantic Models: Use Pydantic when:- Example: python from pydantic import BaseModelclass Student BaseModel : name: str age: int = 18 Why Use Pydantic? JSON Schema: Another popular approach is JSON Schema. Example: { "title": "student", "type": "object", "properties": { "name": "string", "age": "integer" }, "required": "name" } When Should You Use JSON Schema? What if the model cannot generate structured output natively? This is where Output Parsers come in. Output Parsers convert raw LLM responses into structured formats. LLM Output ↓Output Parser ↓Structured Data LangChain provides multiple output parsers. String Output Parser: The simplest parser. It converts model responses into plain strings. Example: python from langchain huggingface import ChatHuggingFace, HuggingFaceEndpointfrom dotenv import load dotenvfrom langchain core.prompts import PromptTemplateload dotenv llm = HuggingFaceEndpoint repo id="google/gemma-2-2b-it", task="text-generation" model = ChatHuggingFace llm=llm 1st prompt - detailed reporttemplate1 = PromptTemplate template='Write a detailed report on {topic}', input variables= 'topic' 2nd prompt - summarytemplate2 = PromptTemplate template='Write a 5 line summary on the following text. /n {text}', input variables= 'text' prompt1 = template1.invoke {'topic':'black hole'} result = model.invoke prompt1 prompt2 = template2.invoke {'text':result.content} result1 = model.invoke prompt2 print result1.content This parser is commonly used inside chains. JSON Output Parser: JSON Output Parser converts responses into JSON objects. Example: python from langchain huggingface import ChatHuggingFace, HuggingFaceEndpointfrom dotenv import load dotenvfrom langchain core.prompts import PromptTemplatefrom langchain core.output parsers import JsonOutputParserload dotenv llm = HuggingFaceEndpoint repo id="Qwen/Qwen2.5-7B-Instruct", task="text-generation" model = ChatHuggingFace llm=llm json parser = JsonOutputParser template = PromptTemplate template='Give me 5 facts about {topic} \n {format instruction}', input variables= 'topic' , partial variables={'format instruction': json parser.get format instructions } prompt = template.format model output = model.invoke prompt final result = json parser.parse model output.content chain = template | model | json parserchain result = chain.invoke {'topic': 'Origami'} print chain result The parser automatically injects formatting instructions into the prompt. Why Use JsonOutputParser? Structured Output Parser: StructuredOutputParser allows us to define required fields explicitly. Example: python from langchain huggingface import ChatHuggingFace, HuggingFaceEndpointfrom dotenv import load dotenvfrom langchain core.prompts import PromptTemplatefrom langchain classic.output parsers.structured import StructuredOutputParser, ResponseSchemaload dotenv llm = HuggingFaceEndpoint repo id="Qwen/Qwen2.5-7B-Instruct", task="text-generation" model = ChatHuggingFace llm=llm response schema = ResponseSchema name="fact1", description="The first fact about the topic" , ResponseSchema name="fact2", description="The second fact about the topic" , ResponseSchema name="fact3", description="The third fact about the topic" , ResponseSchema name="fact4", description="The fourth fact about the topic" , ResponseSchema name="fact5", description="The fifth fact about the topic" parser = StructuredOutputParser.from response schemas response schema template = PromptTemplate template='Give me 5 facts about {topic} \n {format instruction}', input variables= 'topic' , partial variables={'format instruction': parser.get format instructions } chain = template | model | parserchain result = chain.invoke {'topic': 'Origami'} print chain result Pydantic Output Parser: PydanticOutputParser combines structure and validation. Example: python from langchain huggingface import ChatHuggingFace, HuggingFaceEndpointfrom dotenv import load dotenvfrom langchain core.prompts import PromptTemplatefrom langchain core.output parsers import PydanticOutputParserfrom pydantic import BaseModel, Fieldload dotenv llm = HuggingFaceEndpoint repo id="Qwen/Qwen2.5-7B-Instruct", task="text-generation" model = ChatHuggingFace llm=llm class Person BaseModel : name: str = Field description='Name of the person' age: int = Field gt=18, description='Age of the person' city: str = Field description='Name of the city the person belongs to' parser = PydanticOutputParser pydantic object=Person template = PromptTemplate template='Generate the name, age and city of a fictional {place} person \n {format instruction}', input variables= 'place' , partial variables={'format instruction':parser.get format instructions } chain = template | model | parserfinal result = chain.invoke {'place':'sri lankan'} print final result Structured outputs make LLM responses predictable and easier to integrate into applications. LangChain provides multiple approaches: Understanding these tools is essential when building reliable AI applications that need structured, machine-readable data rather than free-form text. In the next article, we’ll dive into Chains in LangChain and learn how to combine prompts, models, and output parsers into powerful end-to-end AI pipelines using LCEL. Code Repository: https://github.com/Atul245/LangChain-for-developers https://github.com/Atul245/LangChain-for-developers If you found this article helpful, consider: ⭐ Starring the repository 🍴 Forking the repository 👤 Following me for more content on LangChain, Generative AI, and AI Engineering Connect with me on LinkedIn: https://www.linkedin.com/in/atulkumar8/ https://www.linkedin.com/in/atulkumar8/ 🚀 LangChain Series 3: Prompts Explained — Prompt Templates, Chat Prompts, Dynamic Prompting… https://pub.towardsai.net/langchain-series-3-prompts-explained-prompt-templates-chat-prompts-dynamic-prompting-fd1411b3e986 was originally published in Towards AI https://pub.towardsai.net on Medium, where people are continuing the conversation by highlighting and responding to this story.