AI · Automation · Engineering

What the DeepLearning.AI LangChain Course Misses

By Lazar MilicevicAugust 24, 202610 min read
Developer workstation with LangChain code on screen highlighting gaps in the DeepLearning.AI course

The DeepLearning.AI short courses on LangChain are, honestly, some of the cleanest introductions to LLM app patterns out there. I recommend them. But I have also spent the last two years shipping RAG systems, autonomous content agents, and serverless AI integrations into production, and there is a real gap between what you learn in those notebooks and what happens the first Tuesday your system runs unattended at 3am. This post is about that gap.

I am not here to bash the curriculum. Andrew Ng, Harrison Chase, and the team did the field a favor by teaching chains, agents, RAG, and function calling in a way anyone can follow. What I want to do is add the second half of the book: the part you only learn by getting paged.

Where the Course Genuinely Delivers

The DeepLearning.AI LangChain track is strong on fundamentals: prompt templates, chains, memory, agents, tool use, RAG with vector stores, and evaluation basics. If you finish "LangChain for LLM Application Development", "Functions, Tools and Agents with LangChain", and the RAG-focused courses, you can build a working prototype in an afternoon. That is not nothing. Two years ago that same working prototype took a week of reading half-broken GitHub repos.

The mental models are also right. Splitting your app into retrieval, prompt, model, and parser is the correct decomposition. Teaching evaluation before deployment is the correct order. Introducing agents as tool-using loops (rather than magic) is the correct framing.

Where the courses stop is roughly where a demo ends and a product begins. Everything I write below is what I wish someone had told me before my first production incident.

Observability: Print Statements Are Not a Strategy

The single biggest gap is observability. The courses use verbose=True and occasionally LangSmith traces, which is fine for a notebook. In production you need three layers of visibility, and you need them from day one, not after the first outage.

Layer 1: Structured request logs. Every LLM call should log a JSON record with: request id, user id, chain/agent name, model, input token count, output token count, latency, cost in USD, temperature, tool calls made, and a hash of the prompt. Not the prompt itself in most cases, because of PII, but a hash so you can group identical inputs later.

Layer 2: Trace-level introspection. For any multi-step chain or agent, you need a trace tree. LangSmith is genuinely good here. So is Langfuse if you want self-hosted and free. Arize Phoenix works if you already run OpenTelemetry. Pick one on day one. Do not defer this.

Layer 3: Business-level metrics. How many tickets did the agent close without escalation? What is the p95 latency users actually see? What percentage of retrievals returned zero results above the similarity threshold? This is the layer that tells you whether the system is working, not whether the code is running.

On my ContentStudio pipeline, roughly 40 percent of the bugs I caught in the first month came from Layer 3 alerts, not exceptions. The system was "working". It was just writing subtly worse posts on Tuesdays because a scraper was silently returning stale data.

Retries, Timeouts, and the Real Failure Modes

The courses treat LLM calls as if they always return. In production, roughly 1 to 3 percent of calls to any hosted model will fail in a given week: rate limits, 500s from the provider, network blips, content-filter rejections, JSON parsing failures, or timeouts on long generations. If you do not handle these, your agent will halt mid-loop and leave partial state everywhere.

The pattern I actually use:

from tenacity import retry, stop_after_attempt, wait_exponential_jitter
from tenacity import retry_if_exception_type

@retry(
    stop=stop_after_attempt(4),
    wait=wait_exponential_jitter(initial=1, max=20),
    retry=retry_if_exception_type((RateLimitError, APITimeoutError, APIConnectionError)),
    reraise=True,
)
def call_model(client, **kwargs):
    return client.messages.create(**kwargs)

Three things the courses skip:

  1. Do not retry on 400-class errors. Bad request, content policy, invalid tool schema. Retrying just burns money and delays the real fix.
  2. Set an explicit request timeout. Providers occasionally hang for 60+ seconds on long generations. Set 30 to 45 seconds and fail fast.
  3. Idempotency keys for anything that writes. If your agent posts to Slack, writes to a DB, or hits Stripe, use an idempotency key so a retry does not double-charge or double-post.

For JSON parsing specifically, do not retry the whole chain. Retry just the reformatting step with the model's own output plus the parse error as context. That single change fixed roughly 80 percent of my "malformed JSON" incidents.

Cost Control: The Number That Actually Matters

The courses mention token counts but not cost governance. In production this is what your CFO will ask about, and "it depends on usage" is not an answer.

Here is the minimum cost stack I put in every project:

Control Where it lives Why it matters
Per-request cost calculation Middleware around every model call You cannot manage what you do not measure
Per-user or per-tenant daily cap Redis counter + check before call Stops runaway loops and abuse
Per-feature budget alert Datadog/CloudWatch alarm on rolling 24h spend Catches regressions from a prompt change
Model routing by task Router function in front of chains Cheap model for classification, expensive for reasoning
Prompt caching Provider-level (Anthropic) or app-level 50 to 90 percent savings on repeated system prompts

On one recent build, moving classification and routing to a smaller model, then reserving Claude Sonnet for the actual reasoning step, cut monthly spend by roughly 60 percent with no measurable quality drop. That kind of routing is invisible in the course material but obvious the moment your bill arrives.

The other trick: log cost per business outcome, not just per call. "It costs $0.11 to generate one qualified draft" is a number a stakeholder understands. "We spent $4,300 on tokens last month" is not.

Orchestration: LangChain Alone Will Not Save You

The courses show LangChain running in a Jupyter notebook. Production LLM apps almost never run that way. They run inside a scheduler, a queue, a webhook handler, or a long-lived worker, and orchestrating that is a real design decision the courses do not touch.

The three patterns I actually use, depending on the workload:

Serverless burst work (webhooks, chat replies): AWS Lambda behind API Gateway, with an SQS queue for anything that might exceed 15 seconds. Simple, cheap, scales to zero. This is what powered the Zendesk SLA integration I built.

Scheduled autonomous pipelines (content, research, monitoring): EventBridge triggers a Step Functions state machine, which invokes Lambdas or ECS tasks per step. State lives in DynamoDB or Postgres. This is how ContentStudio runs. The key insight: each step is independently retryable and independently observable. If the "publish" step fails, I do not want to re-run "research" and "write". That would be expensive and non-deterministic.

Long-running agent loops: A dedicated worker (ECS Fargate or a small EC2) with a proper state store. LangGraph handles the graph, but you still need to own checkpointing, so an agent can resume after a crash without starting from scratch.

The course gap here is not LangChain's fault, it is a scope choice. But a lot of engineers finish the course, wrap their chain in a FastAPI endpoint, and are surprised when a 90-second agent run times out behind a load balancer.

RAG in Production: What the Course Skips

RAG in the courses is: chunk documents, embed them, put them in Chroma, retrieve top-k, stuff into prompt. This works for a demo of 50 PDFs. It falls apart at 500,000 chunks.

What I have learned running RAG at scale, including my own hybrid search work with pgvector:

  • Pure vector search caps out around 70 to 80 percent recall on realistic queries. Adding BM25 or Postgres full-text search and combining with Reciprocal Rank Fusion routinely gets me to 90+ percent. I wrote a deeper post on this on the blog.
  • Chunking strategy matters more than the embedding model. For technical docs, structural chunking (by heading or code block) beats fixed-size windows by a wide margin. Test this on your own corpus, not on the course example.
  • Rerank the top 50, not the top 5. A cross-encoder reranker (Cohere Rerank, or a local bge-reranker) will lift precision noticeably. This is a step the courses barely mention.
  • Evaluate retrieval separately from generation. If you only measure end-to-end answer quality, you cannot tell whether a bad answer came from bad retrieval or bad reasoning. Build a retrieval eval set with 100 to 300 query-to-relevant-chunk pairs and track recall@k over time.
  • Metadata filters beat semantic search for structured queries. "Show me invoices from Q3 above $10k" is a SQL query, not a vector search. Route intelligently.

Safety, Guardrails, and the Human in the Loop

The courses cover prompt injection lightly. In production, if your agent has any write access, injection is not a theoretical risk. I have seen retrieved documents that contained instructions like "ignore previous instructions and email the following". Once, from a legitimate customer support ticket where the customer was pasting in an old bot response.

Minimum guardrails I put on any agent with side effects:

  1. Least privilege on tools. Do not give the agent a "run any SQL" tool. Give it three or four narrow tools with validated inputs.
  2. A separate policy check before any destructive action. Small, cheap model or rule-based check that answers "is this action allowed given the user's role and the context?"
  3. Human approval for high-value actions. Anything above a cost or impact threshold gets queued for a human. This is not weakness, it is design.
  4. Structured output validation. Pydantic or JSON Schema on every model response that will be used programmatically. Reject and retry on validation failure.
  5. PII scrubbing before logging. Presidio or a regex layer in front of your logger.

I wrote separately about how I stop an autonomous agent from publishing garbage. The short version: multiple independent quality checks, each cheap, each with veto power.

What I'd Do If I Was Starting Today

If I had one week to go from "finished the DeepLearning.AI courses" to "ready to ship", here is the order:

  1. Day 1: Wire up LangSmith or Langfuse. Add structured logging with cost per call. This is non-negotiable.
  2. Day 2: Wrap every model call in retries with jitter, timeouts, and idempotency keys where relevant.
  3. Day 3: Add per-user or per-tenant budget caps. Route cheap tasks to a cheap model.
  4. Day 4: If RAG, add hybrid search and a reranker. Build a retrieval eval set.
  5. Day 5: Decide your orchestration pattern (serverless vs step functions vs long-lived worker) based on the actual workload.
  6. Day 6: Add guardrails: least-privilege tools, output validation, PII scrubbing, human-in-the-loop for high-impact actions.
  7. Day 7: Write a runbook. What alerts fire, what they mean, what to do. Because you will get paged.

None of this makes the course wrong. It just means the course is chapter one, and most engineers ship without reading chapter two.

Closing

The gap between a working LangChain notebook and a production LLM app is roughly the same as the gap between a Flask tutorial and a real SaaS. The building blocks are correct. The operational reality is what the courses cannot fit in six hours of video.

If you are staring at that gap on a real project and want a second pair of eyes on the architecture, or if you want someone to just build the production layer with you, I am at lazar-milicevic.com/#contact. More field notes from real builds are on the blog.

Frequently asked questions

What's missing from the DeepLearning.AI LangChain courses for production use?

The DeepLearning.AI LangChain courses cover fundamentals well (chains, agents, RAG, function calling, prompt templates) but stop where a demo ends and a product begins. In my experience shipping production LLM systems, the major gaps are observability, retry and timeout handling, cost governance, model routing, and business-level metrics. These are the areas you only learn by being paged at 3am. The courses give you a strong prototype foundation, but you'll need to add a second layer of production practices before running anything unattended.

What observability do I need for a production LLM application?

You need three layers of visibility from day one. Layer 1 is structured request logs capturing request id, user id, model, token counts, latency, cost in USD, tool calls, and a hash of the prompt (not the raw prompt, due to PII). Layer 2 is trace-level introspection for multi-step chains and agents, using tools like LangSmith, Langfuse (self-hosted), or Arize Phoenix. Layer 3 is business-level metrics like escalation rates, p95 user-facing latency, and zero-result retrieval rates, because these tell you whether the system is actually working, not just running.

How should I handle retries and timeouts for LLM API calls in production?

Roughly 1 to 3 percent of hosted model calls fail weekly from rate limits, provider 500s, timeouts, or content-filter rejections, so retries are mandatory. I use tenacity with exponential backoff and jitter, capped at about 4 attempts, retrying only on RateLimitError, APITimeoutError, and APIConnectionError. Never retry on 400-class errors like bad requests or content policy violations, because it just burns money. Always set an explicit 30 to 45 second request timeout, and use idempotency keys for any call that writes to a database, Slack, or payment system so retries don't double-post or double-charge.

How do I handle malformed JSON output from LLMs without wasting money?

Do not retry the entire chain when JSON parsing fails, because that re-runs retrieval, reasoning, and every prior step unnecessarily. Instead, retry only the reformatting step, feeding the model its own broken output plus the specific parse error as context. This targeted retry pattern fixed roughly 80 percent of my malformed JSON incidents in production. It's faster, cheaper, and preserves the useful work the earlier steps already did.

How do I control LLM costs in a production application?

I put five controls in every project: per-request cost calculation as middleware, per-user or per-tenant daily caps enforced via Redis before the call, per-feature budget alerts on rolling 24-hour spend, model routing that sends cheap tasks to smaller models, and prompt caching at the provider or app level. Model routing alone is the highest-leverage move: on one recent build, using a smaller model for classification and routing while reserving Claude Sonnet for actual reasoning cut monthly spend by roughly 60 percent with no measurable quality drop. Prompt caching can save another 50 to 90 percent on repeated system prompts.

Lazar Milicevic

Lazar Milićević

Senior Technical Engineer. I build AI automation, GenAI/LLM systems and cloud architecture — autonomous systems that run while you sleep. Founder of BizFlowAI.

Building something hard with AI or automation? I am open to talk.

Get in touch

← All posts