cd /news/large-language-models/learning-langchain · home › topics › large-language-models › article
[ARTICLE · art-149767] src=dev.to ↗ pub= topic=large-language-models verified=true sentiment=↑ positive

Learning LangChain

A developer rebuilt their hand-rolled note-answering app, Synapse, using LangChain and documented how the framework maps onto each step they had previously written manually. The account covers message classes, prompt templates, output parsers, pipe-based chain composition with RunnableLambda and RunnableParallel, document loading and splitting, OpenAI embeddings, Chroma vector storage, and similarity versus MMR retrieval, noting that batching two requests took 2.67 seconds versus about 7.5 seconds for separate calls.

by read4 min views1 publishedOct 12, 2026

A while back I built Synapse, a small app that answers questions using my own notes. I did every part of it by hand: load the files, turn them into vectors, find the closest ones to the question, and hand those to an LLM. It worked, and I understood it, because I'd written every line myself.

So when I started LangChain, a framework that promised to do all of that for me felt a little suspicious. My honest question was: what is this doing that I haven't already done?

It turns out the answer is less "new ideas" and more "a standard shape for the ones you already have." Let me walk through it the way I learned it.

A chat model doesn't take one big string. It takes a list of messages: a system message that sets the role, then human messages from you, then AI messages from the model. LangChain gives each of those its own class. Nothing magic, it's the same thing the raw API wants.

Prompt templates were the next small step. Instead of building a string every time, you write the prompt once with a gap in it:

from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_template(
    "I've recently adopted a {pet}. Suggest three {pet} names."
)

Then an output parser takes the model's text reply and turns it into something you can actually use, like a Python list. The thing that stuck with me here is that the parser is only as good as your instructions. Vague instructions about the format gave me a messy list. Strict ones gave me a clean one.

This is the bit where it clicked for me. LangChain lets you join those pieces with a |, like a Unix pipe:

chain = chat_template | chat | list_output_parser
chain.invoke({"pet": "dog"})

A prompt goes in one end, the model sits in the middle, and a clean list comes out the other end. Each step's output is the next step's input. That's the whole idea.

It even works for ordinary Python functions. Wrap them in RunnableLambda and they snap in like any other piece:

from langchain_core.runnables import RunnableLambda

add_up = RunnableLambda(lambda x: sum(x))
square = RunnableLambda(lambda x: x**2)

(add_up | square).invoke([1, 2, 5])   # 8, then 64

You can also draw a chain. chain.get_graph().print_ascii() prints the boxes and arrows, which is surprisingly handy when a chain gets long.

Two more things I liked. RunnableParallel runs two chains side by side and gives both results to the next step. And batch is quicker than looping: two requests through batch took 2.67 seconds in my notebook, while the same two as separate calls took about 7.5.

Remember my five hand-built steps? LangChain has a piece for each.

For , there are s for different file types. I used one for PDFs and one for Word documents. Splitting was the new part, since Synapse never did any. I split a six-page document twice. First by its headings, so every chunk remembers which lecture it came from, then by size, about 500 characters with a little overlap. That left me with 20 chunks, each labelled with where it came from.

Each chunk then became 1,536 numbers using OpenAI's embedding model. I wanted to see if it really worked, so I compared three pairs of chunks. They scored 0.88, 0.80 and 0.79, so related text does end up close together. The vectors all have a length of about 1, which is why a plain dot product works as a similarity score.

Those vectors went into Chroma, which saves them to a folder on disk so I don't have to re-embed every time I open the notebook.

Retrieval comes in two flavours. A plain similarity search gives you the closest chunks. MMR gives you close chunks that are also different from each other, so you don't get five copies of the same paragraph.

And the last step is where the pipe comes back. The whole thing is one chain:

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt_template
    | chat
    | StrOutputParser()
)

chain.invoke("What software do data scientists use?")

The retriever fills in the context, the question passes straight through, the prompt tells the model to answer only from that context and name the lecture it used, and the parser hands back plain text. The answer ended with:

Resources: Programming Languages & Software Employed in Data Science - All the Tools You Need

That's the same "only use these notes" guard I wrote by hand in Synapse, plus the sources I'd been putting off adding.

LangChain didn't teach me a new idea. Everything in that pipeline is something I'd already done by hand: load, embed, find the closest, ask. What it gave me was a standard shape for each step, and the pipe to join them. Having built it the long way first is what made the short way make sense.

Next up is LangGraph.

── more in #large-language-models 4 stories · sorted by recency
── more on @langchain 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/learning-langchain] indexed:0 read:4min 2026-10-12 · —