From API Call to LLM Application: Getting Started with LangChain
From a single model call to a reusable chain using prompt templates, output parsers, and LCEL
LLMs have changed how we build software applications. From intelligent chatbots to enterprise knowledge assistants, applications can now understand natural language, generate responses and interact with external systems.
However, calling an LLM through an API is only the beginning.
Today’s AI applications require prompt management, model integration, structured outputs, reusable workflows and eventually tools, memory and orchestration.
If OpenAI and Anthropic already provide APIs to interact with powerful LLMs, why do we need LangChain, memory, tools, structured outputs and orchestration? What additional value does an application framework provide?
To answer this an LLM API provides the model’s intelligence, but it does not automatically implement the complete business workflow that we need for our application. Let’s check this in detail below:
For example a user asks why did a Spark job take 5 hours to complete?
This we can send directly to OpenAI or Anthropic model using its API. The workflow looks like
user_input → OpenAI/ Anthropic API → LLM → Generated response → output_for_end_user
This workflow doesn’t need LangChain at all as this looks straightforward. The complexity increases when you want your application to maintain the context, perform actions, interact with the external systems and follow multi-step workflows.
- Retrieve actual Spark execution metrics.
- Access physical execution plans.
- Compare current and previous job runs.
- Maintain relevant conversation context.
- Execute approved operations.
- Return structured analysis.
- Handle failures and multi-step workflows.
Let’s see an example for this, Suppose an engineer asks : Analyze my Spark job, identify why it is slow, compare it with the previous successful run, and recommend optimizations?
Get Jahnavi’s stories in your inbox
Join Medium for free to get updates from this writer.
A basic API call will not be sufficient to answer this, it needs memory to store the context so that it would be easy for an application to retain the relevant context from the first interaction. To check further on slowness of a Spark job it would need to connect to your workspace/ workflow and retrieve the actual Spark execution metrics unless you provide an appropriate integration. This is where tools become useful.
The updated flow looks like :
We need structured output: A performance analysis may need to return an issue, severity, affected operator and recommended action. Structured outputs provide predictable response formats, such as JSON objects validated against a schema. This allows applications to process model responses programmatically and integrate them with dashboards, APIs and automated workflows.
Finally, we need reusable workflows and orchestration, A production application may involve several dependent operations. Managing these steps, their dependencies and their failure conditions requires application orchestration. LangChain provides reusable components and composition patterns, while LangGraph supports more complex stateful workflows involving branching, loops, persistence and human intervention.
API calls execution:
from openai import OpenAI
client = OpenAI(api_key = OPENAI_API_KEY)
def ask_question(question: str)->str:
response = client.chat.completions.create(
model = "gpt-4.1-mini",
messages = [
{
"role": "system",
"content": "You are a technical assistant. Explain concepts clearly and concisely."
},
{
"role": "user",
"content": question
}
],
temperature=0
)
return response.choices[0].message.contentresponse = ask_question ("What is the difference between Spark transformations and actions?")
print(response)LangChain workflow:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI (
model = 'gpt-4.1-mini',
temperature = 0,
api_key = OPENAI_API_KEY
)
prompt = ChatPromptTemplate.from_messages([
(
"system",
"You are a technical assistant. Explain concepts clearly and concisely."
),
("human", "{question}")
])
output_parser = StrOutputParser()
chain = prompt | llm | output_parser
answer = chain.invoke({
"question": "What is the difference between Spark transformations and actions?"
})
print(answer)





