Building Your First Chain
A chain in LangChain is a sequence of components connected together, where the output of one becomes the input of the next. In this lesson, you'll build a real chain from scratch: a research summarizer that takes a topic, generates a structured analysis, and formats it as a markdown report.
The LCEL Pipe Operator
LangChain Expression Language (LCEL) uses the | operator to compose components. This is equivalent to function composition: f | g | h means "pass input through f, then g, then h."
Every LCEL component implements the Runnable interface with these key methods:
.invoke(input)— run synchronously with a single input.batch(inputs)— run on multiple inputs in parallel.stream(input)— stream output tokens as they arrive
Building a Research Summarizer Chain
# agent/chains/research_summarizer.py
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# Step 1: Define the model
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
# Step 2: Create a prompt template
research_prompt = ChatPromptTemplate.from_messages([
("system", """You are a research assistant. When given a topic, provide:
1. A 2-sentence definition
2. Three key concepts (as bullet points)
3. Two practical applications
4. One open challenge in the field
Format your response in clean markdown."""),
("human", "Research topic: {topic}")
])
# Step 3: Create an output parser
output_parser = StrOutputParser()
# Step 4: Chain them together with LCEL
research_chain = research_prompt | llm | output_parser
# Usage
if __name__ == "__main__":
result = research_chain.invoke({"topic": "transformer attention mechanisms"})
print(result)
Adding Multiple Prompts (Sequential Chains)
Real pipelines often need multiple LLM calls. Here's a two-step chain: first research, then critique:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# First chain: generate research
research_prompt = ChatPromptTemplate.from_template(
"Provide a 3-paragraph overview of: {topic}"
)
research_chain = research_prompt | llm | StrOutputParser()
# Second chain: critique the research
critique_prompt = ChatPromptTemplate.from_messages([
("system", "You are a critical editor. Identify gaps and improvements."),
("human", "Original topic: {topic}\n\nDraft content:\n{research}\n\nProvide 3 specific improvements.")
])
critique_chain = critique_prompt | llm | StrOutputParser()
# Combine: pass topic to research, then both topic and research to critique
full_pipeline = (
RunnablePassthrough.assign(research=research_chain)
| critique_chain
)
result = full_pipeline.invoke({"topic": "vector databases"})
print(result)
Streaming Output
For interactive applications, stream tokens as they generate instead of waiting for the full response:
# Stream tokens in real-time
for chunk in research_chain.stream({"topic": "reinforcement learning"}):
print(chunk, end="", flush=True)
print() # Final newline
Adding Structured Output Parsing
Instead of free-form text, parse the LLM output into a typed Python object:
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel, Field
from typing import List
class ResearchResult(BaseModel):
topic: str = Field(description="The researched topic")
summary: str = Field(description="A 2-sentence summary")
key_concepts: List[str] = Field(description="3-5 key concepts")
applications: List[str] = Field(description="2-3 practical applications")
# Tell the model to output JSON matching the schema
json_parser = JsonOutputParser(pydantic_object=ResearchResult)
structured_prompt = ChatPromptTemplate.from_messages([
("system", "You are a research assistant. Respond with valid JSON only."),
("human", "Research topic: {topic}\n\n{format_instructions}")
]).partial(format_instructions=json_parser.get_format_instructions())
structured_chain = structured_prompt | llm | json_parser
result: ResearchResult = structured_chain.invoke({"topic": "RAG systems"})
print(f"Topic: {result['topic']}")
print(f"Key concepts: {result['key_concepts']}")
Inspecting Chain Execution
LangChain provides callbacks for debugging:
from langchain_core.callbacks import StdOutCallbackHandler
# Print all LLM inputs/outputs during execution
result = research_chain.invoke(
{"topic": "AI safety"},
config={"callbacks": [StdOutCallbackHandler()]}
)
You now have a working chain that takes a topic, runs it through a structured prompt, and returns formatted research output. In the next lesson, you'll attach real tools to turn this chain into a true agent.