You have a prompt that changes every time based on your client's data? Extracting structured information from an LLM's output is a nightmare of manual parsing? We, at Meteora Web, have been there. After 8 years building platforms for real businesses, we learned that without chains, prompt templates, and output parsers every LLM integration becomes an unmanageable mess.
This guide is for developers who have already seen a model generate text. Now we want that text to become usable data: JSON, enums, lists, dates. And we want the prompt to be reusable without copy-pasting every time.
Let's start with a concrete problem: you need to call an LLM and get an array of objects with name, price, and category. If you parse with regex, when the model changes wording your code breaks. That's why output parsers exist.
Why Prompt Templates and Output Parsers Are Not Optional
An LLM only sees strings. But your business logic works with objects, arrays, enums. Without a parser, every response is raw text: writing fragile regex, handling exceptions, hoping the model respects the format. It's inefficient and brittle. We see it every day in projects that come to us: code full of split(",") and json.loads without error handling.
Prompt templates save you from building strings with manual concatenation: variables, f-strings, and other tricks work, but become unreadable when the prompt exceeds 10 lines. A template separates the fixed structure from the variable part, and supports formats like f-string or jinja2.
The Basics: From Raw Prompt to Structured Template
LangChain offers PromptTemplate and ChatPromptTemplate. We almost always use the latter because in real projects chat models (gpt-4, claude, gemini) respond better to structured messages (system, user, assistant).
Simple PromptTemplate
from langchain.prompts import PromptTemplate
template = """Translate the following sentence into French:
Sentence: {sentence}
Translation:"""
prompt = PromptTemplate(input_variables=["sentence"], template=template)
print(prompt.format(sentence="Hello world"))
Output: the string ready to be sent. Simple, but it has a limitation: it doesn't handle separate system messages.
ChatPromptTemplate for Real Conversations
When using chat models, the prompt is a list of messages. Here's how it's done:
from langchain.prompts import ChatPromptTemplate
from langchain.schema import SystemMessage, HumanMessage
template = ChatPromptTemplate.from_messages([
("system", "You are an expert Italian cuisine assistant."),
("human", "Give me a recipe for {dish}.")
])
messages = template.format_messages(dish="carbonara")
print(messages)
The messages are objects that LangChain will pass directly to the model.
Partial Templates
Often you have fixed variables across multiple calls (e.g., output language). Instead of passing it every time, use partial variables:
partial = template.partial(language="English")
# now you can call format without specifying language
print(partial.format(product="coffee"))
Output Parser: Turning Text into Data
The parser does the dirty work for you. LangChain has predefined ones, but you can create your own by subclassing BaseOutputParser.
PydanticOutputParser
Our favorite: declare a Pydantic model, and the parser generates instructions for the model and then validates the output JSON.
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(description="Name of the product")
price: float = Field(description="Price in USD")
category: str = Field(description="Category of the product")
parser = PydanticOutputParser(pydantic_object=Product)
# Get the format instructions for the prompt
instructions = parser.get_format_instructions()
print(instructions)
The instructions tell the model to return a JSON with those fields. Then, when you get the response:
llm_response = '{"name": "Arabica Coffee", "price": 12.50, "category": "Beverages"}'
product = parser.parse(llm_response)
print(product.name) # Arabica Coffee
If the JSON is malformed or fields are missing, parse throws an error. Handle it with a try-except.
CommaSeparatedListOutputParser
Useful for simple lists:
from langchain.output_parsers import CommaSeparatedListOutputParser
parser = CommaSeparatedListOutputParser()
print(parser.parse("apples, pears, bananas")) # ['apples', 'pears', 'bananas']
OutputFixingParser
When the model gets the format wrong, this parser attempts to fix it by calling a second LLM. We only use it in contexts where extra cost is acceptable (e.g., batch jobs at night).
Chains: Connecting Template, Model, Parser
A chain is a link between prompt, LLM, and parser. LangChain has LLMChain that does everything at once, but for greater control we use LCEL (LangChain Expression Language).
Classic LLMChain
from langchain.chains import LLMChain
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
chain = LLMChain(
prompt=template,
llm=llm,
output_parser=parser
)
result = chain.run(product="coffee")
print(result) # already parsed as Product object
Chain with LCEL (Modern)
LCEL is more powerful and composable. We, at Meteora Web, have migrated all projects to LCEL:
from langchain.schema import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
chain = template | llm | parser
# equivalent: prompt -> LLM -> parser
result = chain.invoke({"dish": "risotto"})
With LCEL you can easily add intermediate steps, logging, fallbacks. It's the future.
Common Mistakes (and How We Solve Them)
1. The model ignores format instructions. Common with smaller models. Solution: lower temperature to 0.1, or use few-shot examples in the prompt.
2. Output truncated mid- Some output parsers expect a complete JSON. If the model stops, parsing fails. We use RetryOutputParser or add a stop token to the prompt.
3. Missing variables in template. PromptTemplate throws an error if a variable is missing. Check with prompt.input_variables.
Complete Example: Extracting Products from Textual Catalog
Let's put it all together. Imagine a client (like Hibrido Clothing) has a catalog in free text and wants to extract it into JSON.
from langchain.prompts import ChatPromptTemplate
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough
class Product(BaseModel):
name: str = Field(description="Product name")
price: float = Field(description="Price in euros")
stock: int = Field(description="Quantity available")
parser = PydanticOutputParser(pydantic_object=Product)
prompt = ChatPromptTemplate.from_messages([
("system", "You are an assistant that extracts structured data from product descriptions."),
("human", "Description: {text}\n{format_instructions}")
])
# Insert format instructions as a variable
chain = (
{"text": RunnablePassthrough(), "format_instructions": lambda _: parser.get_format_instructions()}
| prompt
| ChatOpenAI(temperature=0, model="gpt-4")
| parser
)
product_text = "Organic cotton t-shirt, 29.90€, only 15 pieces left."
product = chain.invoke(product_text)
print(product.dict())
Executing yields {"name": "Organic cotton t-shirt", "price": 29.9, "stock": 15}. Data ready for your ERP.
In Summary — What to Do Now
- Identify a spot in your application where you extract data from LLM output using manual parsing (regex, split). Replace it with a LangChain output parser.
- Rewrite your prompts using
ChatPromptTemplatewith variables, never manually concatenated strings again. - Use LCEL to compose even simple chains: it's more maintainable and allows adding logging or fallbacks later.
- Test the parser with real examples: if the model produces valid JSON but with extra fields, Pydantic ignores them. If a required field is missing, catch the exception and retry with a stricter prompt.
- Don't forget error handling: a parser failure shouldn't bring down the whole system. Log and fallback.
We've seen companies save hours of manual data entry thanks to these three components. And if you want to dive deeper into how AI agents orchestrate multiple chains, read our guide on AI Agents. For faster AI-assisted coding, check out Cursor AI guide.
Sponsored Protocol