VOOZH about

URL: https://dev.to/yakhilesh/105-langchain-orchestrating-ai-applications-2b2o

⇱ 105. LangChain: Orchestrating AI Applications - DEV Community


You have spent four posts building agents from scratch. Raw API calls. Custom tool loops. Manual memory management. Now see it in ten lines.

chain = prompt | llm | parser

LangChain wraps the patterns you built by hand into composable, reusable components. The criticism: it abstracts too much and debugging is hard. The counter: you now know what is under the hood, so the abstractions are navigable. You built the engine. Now drive the car.


Setup

# pip install langchain langchain-core langchain-community
# pip install langchain-anthropic langchain-openai
# pip install langchain-chroma sentence-transformers faiss-cpu

import os
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_core.documents import Document
import warnings
warnings.filterwarnings("ignore")

llm = ChatAnthropic(
 model = "claude-3-5-haiku-20241022",
 api_key = os.environ.get("ANTHROPIC_API_KEY"),
 max_tokens = 500,
 temperature= 0.7,
)

parser = StrOutputParser()
response = llm.invoke("What is LangChain in one sentence?")
print(f"LLM response: {response.content}")

LCEL: The Pipe Operator

basic_chain = (
 ChatPromptTemplate.from_template("Explain {concept} to a beginner in 2 sentences.")
 | llm
 | parser
)

result = basic_chain.invoke({"concept": "vector embeddings"})
print(f"Chain output: {result}")

from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
from typing import List

class CodeReview(BaseModel):
 has_bugs: bool = Field(description="Whether code has bugs")
 quality: int = Field(description="Quality score 1-10")
 issues: List[str] = Field(description="Specific issues found")
 suggestions: List[str] = Field(description="Improvement suggestions")

pydantic_parser = PydanticOutputParser(pydantic_object=CodeReview)

review_chain = (
 ChatPromptTemplate.from_messages([
 ("system", f"Review Python code. {pydantic_parser.get_format_instructions()}"),
 ("human", "Review:\n```

python\n{code}\n

```")
 ])
 | llm
 | pydantic_parser
)

buggy = "def divide(a, b):\n return a / b\n\nprint(divide(10, 0))"
review = review_chain.invoke({"code": buggy})
print(f"\nCode Review:")
print(f" Has bugs: {review.has_bugs}")
print(f" Quality: {review.quality}/10")
print(f" Issues: {review.issues}")

Memory and History

from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

store = {}

def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
 if session_id not in store:
 store[session_id] = InMemoryChatMessageHistory()
 return store[session_id]

chat_prompt = ChatPromptTemplate.from_messages([
 ("system", "You are a helpful tutor."),
 ("placeholder", "{chat_history}"),
 ("human", "{input}"),
])

chain_with_history = RunnableWithMessageHistory(
 chat_prompt | llm | parser,
 get_session_history,
 input_messages_key = "input",
 history_messages_key = "chat_history",
)

config = {"configurable": {"session_id": "student_001"}}

turns = [
 "My name is Priya. I am learning about neural networks.",
 "Can you explain what backpropagation does?",
 "What is my name and what am I studying?",
]

print("\nMulti-turn conversation with memory:")
for turn in turns:
 response = chain_with_history.invoke({"input": turn}, config=config)
 print(f" User: {turn}")
 print(f" Agent: {response[:100]}...")
 print()

print(f"History: {len(store['student_001'].messages)} messages stored")

RAG with LangChain

documents = [
 Document(page_content="The transformer uses self-attention mechanisms.",
 metadata={"source": "transformer_paper"}),
 Document(page_content="BERT is pretrained using masked language modeling.",
 metadata={"source": "bert_paper"}),
 Document(page_content="GPT uses autoregressive next-token prediction.",
 metadata={"source": "gpt_paper"}),
 Document(page_content="LoRA adds low-rank matrices to frozen pretrained weights.",
 metadata={"source": "lora_paper"}),
 Document(page_content="Fine-tuning adapts pretrained models to downstream tasks.",
 metadata={"source": "fine_tuning_guide"}),
]

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=30)
split_docs = splitter.split_documents(documents)

embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
vectorstore = FAISS.from_documents(split_docs, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

def format_docs(docs):
 return "\n\n".join([
 f"[{i+1}] Source: {d.metadata['source']}\n{d.page_content}"
 for i, d in enumerate(docs)
 ])

rag_prompt = ChatPromptTemplate.from_template("""
Answer ONLY from the context below. Cite sources as [1], [2].
If not in context: "I cannot find this in the documents."

Context:
{context}

Question: {question}
""")

rag_chain = (
 {"context": retriever | format_docs, "question": RunnablePassthrough()}
 | rag_prompt
 | llm
 | parser
)

queries = [
 "How does BERT get pretrained?",
 "What is LoRA?",
 "Who is the president of France?",
]

print("\nRAG Chain Demo:")
for query in queries:
 answer = rag_chain.invoke(query)
 print(f" Q: {query}")
 print(f" A: {answer[:120]}...")
 print()

Agents in LangChain

from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
import math

@tool
def calculator(expression: str) -> str:
 """Evaluate a math expression. Supports +,-,*,/,sqrt,pi,e."""
 try:
 result = eval(expression, {"__builtins__": {}},
 {"sqrt": math.sqrt, "pi": math.pi, "e": math.e})
 return str(round(float(result), 6))
 except Exception as e:
 return f"Error: {e}"

@tool
def word_count(text: str) -> str:
 """Count words and characters in text."""
 return f"Words: {len(text.split())}, Characters: {len(text)}"

@tool
def reverse_string(text: str) -> str:
 """Reverse a string."""
 return text[::-1]

agent_prompt = ChatPromptTemplate.from_messages([
 ("system", "You are a helpful assistant. Use tools when needed."),
 ("placeholder", "{chat_history}"),
 ("human", "{input}"),
 ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, [calculator, word_count, reverse_string], agent_prompt)
executor = AgentExecutor(agent=agent, tools=[calculator, word_count, reverse_string],
 verbose=False, max_iterations=5)

test_queries = [
 "What is the square root of 1764?",
 "Count the words in 'To be or not to be that is the question'",
 "Reverse the string 'LangChain'",
]

print("\nAgent with Tools:")
for query in test_queries:
 result = executor.invoke({"input": query, "chat_history": []})
 print(f" Q: {query}")
 print(f" A: {result['output']}")
 print()

When to Use LangChain vs From Scratch

comparison = {
 "Simple single LLM call": ("From scratch", "Overkill to add LangChain"),
 "RAG with multiple retrievers": ("LangChain", "Pre-built components save time"),
 "Multi-provider LLM switching": ("LangChain", "Unified interface is valuable"),
 "Custom agent with full control": ("From scratch", "Full visibility, easier debug"),
 "Rapid prototyping": ("LangChain", "Much faster to build"),
 "Production with LangSmith tracing":("LangChain", "Observability built in"),
}

print("LangChain vs From Scratch:")
print(f"{'Scenario':<42}{'Recommendation':<15}{'Reason'}")
print("=" * 85)
for scenario, (rec, reason) in comparison.items():
 print(f"{scenario:<42}{rec:<15}{reason}")

LangSmith: Debugging LangChain

print("\nLangSmith: Essential for Production LangChain")
print()
print("Setup (3 lines):")
print(" export LANGSMITH_API_KEY='your_key'")
print(" export LANGSMITH_TRACING='true'")
print(" export LANGCHAIN_PROJECT='my_project'")
print()
print("That is it. Every run is traced automatically.")
print()
print("What you see in LangSmith:")
features = [
 "Full trace of every LLM call with tokens and cost",
 "Timing breakdown per chain step",
 "Side-by-side diff of prompt changes",
 "User feedback collection",
 "Dataset creation from production traces",
 "A/B testing of different prompts",
]
for f in features:
 print(f"{f}")
print()
print("Without LangSmith, debugging LangChain in production is painful.")
print("It is not optional for serious applications.")

Reference Links

print("\nLangChain Reference Links:")
print()

refs = {
 "Official Docs": [
 ("LangChain Python docs", "python.langchain.com"),
 ("LCEL reference", "python.langchain.com/docs/expression_language"),
 ("LangGraph (stateful agents)", "langchain-ai.github.io/langgraph"),
 ("LangSmith docs", "docs.smith.langchain.com"),
 ("All integrations", "python.langchain.com/docs/integrations"),
 ],
 "Cheat Sheets": [
 ("LCEL cheatsheet", "python.langchain.com/docs/expression_language/interface"),
 ("Output parsers guide", "python.langchain.com/docs/modules/model_io/output_parsers"),
 ("v0.3 migration guide", "python.langchain.com/docs/versions/migrating_chains"),
 ("Memory types reference", "python.langchain.com/docs/modules/memory"),
 ],
 "Tutorials": [
 ("DeepLearning.AI LangChain course", "learn.deeplearning.ai/langchain"),
 ("LangChain RAG tutorial", "python.langchain.com/docs/use_cases/question_answering"),
 ("Build a chatbot tutorial", "python.langchain.com/docs/use_cases/chatbots"),
 ("LangChain cookbook", "github.com/langchain-ai/langchain/tree/master/cookbook"),
 ("LangGraph agent tutorial", "langchain-ai.github.io/langgraph/tutorials/introduction"),
 ],
 "Community": [
 ("LangChain GitHub (star it)", "github.com/langchain-ai/langchain"),
 ("Discord", "discord.gg/langchain"),
 ("Awesome LangChain resources", "github.com/kyrolabs/awesome-langchain"),
 ],
}

for category, links in refs.items():
 print(f"{category}:")
 for name, url in links:
 print(f"{name:<45}{url}")
 print()

Try This

Create langchain_practice.py.

Part 1: build three LCEL chains using the pipe operator: a translation chain, a code review chain that outputs structured JSON, and a topic classification chain.

Part 2: build a RAG chain with FAISS and all-MiniLM-L6-v2. Load 20 documents. Test 10 queries. Compare retrieval quality versus the manual cosine similarity approach from post 85.

Part 3: build a LangChain agent with four tools. Give it three multi-step tasks. Enable verbose=True and compare the reasoning trace to the raw agent you built in post 101.

Part 4: add conversation history with RunnableWithMessageHistory. Build a 5-turn conversation. Inspect the stored history. Start a new session and confirm it has no memory of the previous one.


What's Next

LangChain provides the scaffolding. LangGraph adds state machines: model agent behavior as a directed graph where nodes are actions and edges are conditional transitions. Complex agents become debuggable, resumable, and production-ready. Next post.