Learn LangChain - Output Parsers & Structured Output
Episode 6 of 23

Learn LangChain - Output Parsers & Structured Output

Dissecting output parsers in LangChain: StrOutputParser, JsonOutputParser, PydanticOutputParser, and with_structured_output to force the model to return a structured format, complete with streaming parsing techniques for partial output.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 5 you assembled your first chain with LCEL: chain = prompt | model | parser. That chain works because every component is a Runnable, and the output parser is one of the important members of that chain. You also got a taste of RunnableParallel and RunnablePassthrough for manipulating the data flow between components.

This episode focuses on one element that's often underestimated but determines application quality: the output parser. Language models return free text, while your applications frequently need structured data. We'll dissect four strategies: StrOutputParser for plain text, JsonOutputParser for JSON, PydanticOutputParser for validated objects, and with_structured_output, which hands the formatting task directly to the model. Finally, we'll learn how to parse streams of streamed chunks.

Why Output Parsers Matter

Without a parser, every model response is a free-form string you have to parse yourself — and parsing free text is fragile. Output parsers add a contract layer: the chain always returns the same type, so the calling code doesn't have to guess.

PythonWithout a parser, output is a raw string
from langchain_openai import ChatOpenAI
 
model = ChatOpenAI(model="gpt-4o-mini")
hasil = model.invoke("Sebutkan tiga bahasa pemrograman populer")
print(hasil.content)

The end of the chain is where each parser makes its main difference. There are four families we'll use over and over: StrOutputParser converts AIMessage to str, JsonOutputParser converts a JSON string to dict, PydanticOutputParser converts JSON to a BaseModel instance, and with_structured_output makes the model produce JSON matching the requested schema.

StrOutputParser

The simplest and most widely used parser. It takes the content from the model output — usually an AIMessage — and returns it as a pure string.

PythonChain with StrOutputParser
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
prompt = ChatPromptTemplate.from_messages([
    ("system", "Jawab dalam bahasa Indonesia, maksimal dua kalimat."),
    ("human", "{pertanyaan}"),
])
model = ChatOpenAI(model="gpt-4o-mini")
 
chain = prompt | model | StrOutputParser()
jawaban = chain.invoke({"pertanyaan": "Apa itu vector database?"})
print(jawaban)

Notice that chain.invoke() now returns a str, not an AIMessage. This simplifies the calling code: other functions can consume jawaban directly without knowing anything about message structure. StrOutputParser can also be placed at the end of more complex chains, for example after a RunnableParallel whose results you want combined into a single text.

JsonOutputParser

When you need structured data like key-value pairs, JsonOutputParser is a practical choice. Its advantage: it knows how to handle the partial JSON that comes out during streaming, because this parser runs per-character logic using an internal parser tolerant of incomplete tokens.

PythonJsonOutputParser for structured data
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
 
parser = JsonOutputParser()
prompt = PromptTemplate(
    template="Jawab dalam JSON. {format_instructions}\nPertanyaan: {pertanyaan}",
    input_variables=["pertanyaan"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
model = ChatOpenAI(model="gpt-4o-mini")
 
chain = prompt | model | parser
data = chain.invoke({"pertanyaan": "Data ibu kota Indonesia"})
print(data["ibu_kota"])

get_format_instructions() injects the format instructions into the prompt so the model knows exactly the expected JSON structure. In the example above, the result is a Python dict so you can directly access data["ibu_kota"] without regex or string slicing.

Info

JsonOutputParser is not a schema validator. It only ensures the output is parseable JSON. If you need strict validation of specific fields, move up to Pydantic.

PydanticOutputParser

For a stricter contract, use PydanticOutputParser. It combines JSON parsing with Pydantic validation: the output schema is derived from a BaseModel definition, and wrong-typed fields immediately raise a ValidationError.

PythonStructured output with Pydantic
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field
 
class Ringkasan(BaseModel):
    judul: str = Field(description="Judul artikel")
    poin_utama: list[str] = Field(description="Poin-poin penting")
 
parser = PydanticOutputParser(pydantic_object=Ringkasan)
prompt = PromptTemplate(
    template="Ringkas teks berikut dalam JSON. {format_instructions}\nTeks: {teks}",
    input_variables=["teks"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
model = ChatOpenAI(model="gpt-4o-mini")
 
chain = prompt | model | parser
hasil = chain.invoke({"teks": "LangChain adalah framework untuk membangun aplikasi LLM..."})
print(hasil.judul)
print(hasil.poin_utama)

The main advantage here is type safety: hasil.judul is guaranteed to be a str and hasil.poin_utama a list[str]. Typo errors in the JSON output are caught at validation time, not in the middle of your application logic.

with_structured_output

The most modern approach: instead of parsing after the response arrives, we ask the model to return a specific format from the start. with_structured_output maps the schema to the provider's built-in tool calling or response format, so the result is more consistent than relying on text instructions in the prompt.

Pythonwith_structured_output with a dataclass
from dataclasses import dataclass
from langchain_openai import ChatOpenAI
 
@dataclass
class Klasifikasi:
    label: str
    skor: float
 
model = ChatOpenAI(model="gpt-4o-mini")
model_terstruktur = model.with_structured_output(Klasifikasi)
 
hasil = model_terstruktur.invoke(
    "Teks ini tentang database vector: skor sentimen dan label temanya"
)
print(hasil.label, hasil.skor)

with_structured_output accepts Pydantic classes, dataclasses, or TypedDict. When given a dataclass, LangChain performs automatic coercion to the Python types you define. The result isn't raw JSON, but an object ready to be used in your domain code.

Success

For production chains that need deterministic output, prioritize with_structured_output or PydanticOutputParser. Manually parsing free text only adds points of failure.

Streaming Parsing

In streaming mode, output arrives in parts. Text-based parsers like StrOutputParser can be used directly; for JSON, JsonOutputParser produces partial objects that are still valid when parsed partially. This matters for a responsive user experience.

PythonParsing streamed chunks
chain = prompt | model | StrOutputParser()
 
async for potongan in chain.astream({"pertanyaan": "Jelaskan RAG secara singkat"}):
    print(potongan, end="", flush=True)

Because StrOutputParser just passes text through, each potongan above is a string chunk that can be rendered directly. For JsonOutputParser, chain.astream() produces progressively more complete partial dictionaries as the data flows — just render the fields already available. For instruction-based parsers like PydanticOutputParser, full streaming isn't supported across all versions, so plain invoke or stream_events is safer for monitoring stages without sacrificing validation.

Conclusion

This episode turned model output from free text into structured contracts: StrOutputParser for strings, JsonOutputParser for dicts, PydanticOutputParser for validated objects, and with_structured_output for direct coercion to Python types. You also learned how to handle streamed chunks without breaking the pipeline.

Key takeaways:

  • StrOutputParser converts AIMessage to str — the most common endpoint in a chain.
  • JsonOutputParser is great for quick JSON and tolerant of partial output during streaming.
  • PydanticOutputParser provides strict validation via the BaseModel schema and automatic format instructions.
  • with_structured_output hands JSON generation to the model via tool calling or response format, the most consistent for production.
  • Streaming parsing works smoothly for text- and JSON-based parsers; instruction-based parsers are safer with invoke.

In episode 7 we deal with memory: Memory & Conversation State — storing conversation history, using thread_id, and leveraging LangGraph checkpoints so your agents remember who they're talking to. See you there!

Learn LangChain - Output Parsers & Structured Output | Learn LangChain