LangChain Mastery

Course Content

LangChain Mastery

7 sections · 109 lessons

What are best practices for structuring LangChain code?


What you need to know

A layout that holds up

Text
app/  config.py        settings from environment (models, k, URLs, flags)  models.py        init_chat_model / embeddings factories — the only place providers appear  prompts/         prompt templates as files, versioned in git  retrieval/       loaders, splitters, index job, retriever factories  tools/           @tool functions, one module per external system  chains/          LCEL pipelines, one factory per use case  agents/          create_agent factories and middleware  api.py           FastAPI routes — thin: validate, call, returntests/  unit/            fake models, no network  evals/           LangSmith datasets and evaluators

The rules that matter

  • Factories with injected dependencies. build_support_agent(model, tools, checkpointer) instead of creating clients at import time. Tests pass a fake model; staging passes a cheap one.
  • One place for providers. init_chat_model("openai:gpt-5.4-mini") or init_chat_model(settings.chat_model) in models.py. Switching providers then touches one file.
  • Prompts are assets. Keep them in files (or a prompt registry such as LangSmith's prompt hub) with a comment on why each rule exists.
  • Tools are small and typed. Type hints or a Pydantic args_schema, a docstring that says when to use the tool and when not to, and no hidden global state.
  • Chain or agent on purpose. A fixed sequence (retrieve → answer) is an LCEL chain: faster, cheaper, predictable. Use create_agent only when the model must decide which tools to call and how many times.
  • Cross-cutting concerns as middleware or config. Retries, fallbacks, PII redaction, call limits and human approval go in middleware; tracing goes in config and environment — not copied into each tool.

Legacy vs current

Legacy patternCurrent pattern
LLMChain, SequentialChain, custom Chain subclassesLCEL runnables joined with the pipe operator (prompt, model, parser)
initialize_agent, AgentExecutorcreate_agent (runs on LangGraph)
ConversationBufferMemoryCheckpointer + thread_id, trimming or summarisation middleware
Global langchain.debug = TrueLangSmith tracing, langchain_core.globals.set_debug for local work

A real-life example

A travel-booking startup's first agent was one 900-line main.py: the model client created at import, prompts as f-strings, and a search_flights tool that read an API key from a global. Testing needed real API keys, and switching providers for a cost experiment took two days.

The refactor followed the layout above. models.py reads CHAT_MODEL from config; tools/flights.py exposes search_flights and book_flight with Pydantic schemas; agents/booking.py builds the agent with HumanInTheLoopMiddleware so bookings over ₹50,000 need confirmation. The provider experiment then became a config change, and 140 unit tests run in 6 seconds with fake models.

Follow-up questions to expect

  • "Where does business logic go?" — In plain Python functions and tools, not inside prompts. The model decides what to call; your code decides how it is done.
  • "How do you share code between chains?" — Small runnables (formatters, retrievers, validators) that compose; avoid deep class hierarchies.
  • "How do you deploy it?" — Any Python web stack (FastAPI is common); LangGraph's own server is an option for long-running agents that need persistence and streaming.