Before chains, memory, or agents, three pieces do 80% of the work: the model, the prompt template, and the output parser. Master these and everything else is just composition.
1. Chat models and messages
Modern LLMs are chat models: they take a list of messages with roles โ system (instructions), user (the human), assistant (the AI). LangChain gives each a class.
from langchain.chat_models import init_chat_model
from langchain_core.messages import SystemMessage, HumanMessage
model = init_chat_model("claude-opus-5", model_provider="anthropic")
reply = model.invoke([
SystemMessage("You are a concise tutor for Anna University students."),
HumanMessage("What is a semaphore?"),
])
print(reply.content)The system message is your app's "personality and rules" โ it's where you set tone, constraints, and role.
2. Prompt templates โ stop using f-strings
Hard-coding prompts with f-strings gets messy fast. A ChatPromptTemplate is a reusable prompt with {placeholders} you fill at call time.
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "You are a tutor for {subject}. Answer in {tone} language."),
("user", "{question}"),
])
# .invoke fills the blanks and returns ready-to-send messages
messages = prompt.invoke({
"subject": "operating systems",
"tone": "simple",
"question": "What is a deadlock?",
})
print(model.invoke(messages).content)Now the same prompt serves every subject and question โ that reusability is the whole point.
3. Output parsers โ from reply object to clean data
A model returns a message object, but you usually want a plain string or structured data. Output parsers handle that last mile.
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser() # pulls out just the .content string
text = parser.invoke(model.invoke("Say hi"))
print(text) # "Hi!" โ a clean string, not a message objectNeed structured output (a dict with fixed fields)? Ask the model to fill a schema:
from pydantic import BaseModel, Field
class Flashcard(BaseModel):
question: str = Field(description="the quiz question")
answer: str = Field(description="the correct answer")
structured = model.with_structured_output(Flashcard)
card = structured.invoke("Make a flashcard about TCP three-way handshake.")
print(card.question, "->", card.answer) # a real Python objectwith_structured_output is a superpower โ it guarantees you get typed data instead of parsing free text by hand.
Putting the three together
Model + prompt + parser is a pipeline. In the next post we connect them with a single character โ the | pipe โ to form your first chain. That one operator is what makes LangChain feel elegant instead of verbose.
Related: what LangChain is ยท interview questions to test your OS/CN knowledge the tutor above quizzes you on.
โ All Articles