# LangChain Series #3: Prompts Explained — Prompt Templates, Chat Prompts, Dynamic Prompting…

> Source: <https://pub.towardsai.net/langchain-series-3-prompts-explained-prompt-templates-chat-prompts-dynamic-prompting-fd1411b3e986?source=rss----98111c9905da---4>
> Published: 2026-06-12 17:01:04+00:00

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.
