{"id":180589,"date":"2026-07-11T18:42:05","date_gmt":"2026-07-11T18:42:05","guid":{"rendered":"https:\/\/ktromedia.com\/?p=180589"},"modified":"2026-07-11T18:42:05","modified_gmt":"2026-07-11T18:42:05","slug":"llm-orchestration-frameworks-compared-langchain-vs-llamaindex-vs-raw-api-calls","status":"publish","type":"post","link":"https:\/\/ktromedia.com\/?p=180589","title":{"rendered":"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls"},"content":{"rendered":"<div id=\"\">\n<p>In this article, you will learn how LangChain, LlamaIndex, and raw API calls each solve a different layer of the LLM application stack, and how to choose among them based on what your project actually requires.<\/p>\n<p>Topics we will cover include:<\/p>\n<ul>\n<li>What each option is designed to do, stated plainly without marketing spin.<\/li>\n<li>How the three approaches compare on performance, token overhead, debugging clarity, and code volume.<\/li>\n<li>A practical decision framework for picking the right level of abstraction before you build \u2014 and before that choice becomes expensive to undo.<\/li>\n<\/ul>\n<p>Let\u2019s not waste any more time.<\/p>\n<p><\/p>\n<h2>Introduction<\/h2>\n<p>You have a working prompt. The model is giving good answers. Then the next requirement lands. Maybe it is memory; the model needs to remember what was said three messages ago. Maybe it is retrieval \u2014 the model needs to answer questions about documents it was not trained on. Maybe it is tool use; the model needs to check a database, run a calculation, or call an external API before it can respond. Suddenly, a single <code>client.chat.completions.create()<\/code> call is not enough, and you are standing at the first real architectural decision in your LLM project.<\/p>\n<p>Three paths exist from that moment: reach for LangChain, reach for LlamaIndex, or build a thin layer on top of the raw SDK yourself. Getting this choice wrong does not break the prototype. It breaks the production system six months later, when you are debugging stack traces 40 frames deep, paying 2.7x what you should be on token costs, or spending a sprint migrating away from breaking API changes.<\/p>\n<p><a href=\"https:\/\/www.getmaxim.ai\/articles\/best-llm-cost-tracking-tools-in-2026\/\" target=\"_blank\">LLM API spend doubled from \\$3.5 billion to \\$8.4 billion between late 2024 and mid-2025<\/a>. These are real production budgets. The framework layer \u2014 the code that sits between your application and the model \u2014 directly determines how much of that spend is doing useful work versus paying for abstraction you did not need.<\/p>\n<p>This article gives you an honest comparison: what each option actually is, where it genuinely wins, where it costs you, and a decision framework you can use tomorrow.<\/p>\n<h2>The Landscape in Plain English<\/h2>\n<p>Before comparing trade-offs, it helps to understand what each option actually is \u2014 not what its marketing says, but what problem it was built to solve.<\/p>\n<ol>\n<li><strong>LangChain<\/strong> started in October 2022 as a general-purpose framework for chaining LLM operations together. Its core idea was that building real applications required composing multiple steps \u2014 prompt templates, model calls, output parsers, memory, tools \u2014 and there should be a standard way to do that. It has grown into the largest LLM framework by adoption: 119K GitHub stars, 500+ integrations, and a sprawling ecosystem. The LangChain team now builds LangGraph, a separate package for stateful, graph-based agent workflows, as the recommended way to build production agents within the ecosystem.<\/li>\n<li><strong>LlamaIndex<\/strong> (launched as GPT Index in November 2022) was built to solve a different problem: getting LLMs to reason over your own data. Its design is organized around data ingestion, chunking, embedding, indexing, and retrieval. Where LangChain is about orchestrating what happens between steps, LlamaIndex is about making the retrieval step itself as accurate and efficient as possible. It sits at 44K GitHub stars with 300+ data connectors through LlamaHub, covering sources like Notion, Google Drive, Slack, PDFs, and databases.<\/li>\n<li><strong>Raw API calls<\/strong> means using the OpenAI Python SDK, the Anthropic SDK, or any model provider\u2019s client directly \u2014 no orchestration layer, no abstractions beyond what the provider ships. You write the prompt, call the model, and handle the response yourself. This is not the primitive fallback it is sometimes presented as; it is the approach production teams are increasingly migrating back to for workloads where the framework\u2019s complexity stopped paying for itself.<\/li>\n<\/ol>\n<p>The critical thing to understand before reading any comparison is that these three options are not competing on the same dimension. LangChain is an orchestration toolkit. LlamaIndex is a retrieval toolkit. Raw API calls are a stance on how much abstraction you need. Many production systems use two of them together. The question is always: <em>given what I am actually building, which layer of abstraction earns its cost?<\/em><\/p>\n<h2>LangChain: The Orchestration Layer<\/h2>\n<p>LangChain\u2019s strength is assembling complexity. If your application involves multiple steps, multiple tools, conditional routing, memory across turns, or agents that reason before acting, LangChain provides the building blocks for all of it, with connectors to 500+ services and a community large enough that someone has already solved most of the edge cases you will encounter.<\/p>\n<p>LangGraph, built by the same team and stable at v1.0 since October 2025, is where the serious agent work lives now. It models agent workflows as directed graphs, where nodes are Python functions, edges are state transitions, and a central typed state object flows through the entire execution. It has <a href=\"https:\/\/www.morphllm.com\/comparisons\/langchain-vs-llamaindex\" target=\"_blank\">built-in persistence via checkpointers to SQLite, PostgreSQL, or Redis<\/a>, which means agents can pause mid-workflow, persist their state, and resume hours later. That is genuinely hard to build yourself and is one of LangChain\u2019s clearest justifications in a production context.<\/p>\n<p>The honest trade-offs are worth naming directly. <a href=\"https:\/\/aimultiple.com\/rag-frameworks\" target=\"_blank\">LangChain adds ~10ms framework overhead per step, and LangGraph adds ~14ms<\/a>. For most human-facing applications that make LLM calls taking 1\u20133 seconds each, this is irrelevant. For high-throughput pipelines processing thousands of requests per minute, it compounds. <a href=\"https:\/\/ravoid.com\/blog\/langchain-exit-raw-sdk-migration-2026\" target=\"_blank\">Stack traces from LangChain production errors routinely span 15 to 40 frames of internal framework code<\/a>; finding the actual source of a bug is slower than in a system you wrote yourself. And for simple use cases, <a href=\"https:\/\/checkthat.ai\/brands\/langchain\/pricing\" target=\"_blank\">one documented comparison found LangChain incurring 2.7x higher costs than a native implementation for a basic RAG pipeline<\/a> \u2014 the abstraction overhead consumed tokens that did not need to be consumed.<\/p>\n<p>LangChain v1.0 (October 2025) committed to API stability after a turbulent v0.1 through v0.3 period that forced multiple breaking migrations. That history is worth knowing. For new projects, the stability concern is largely resolved. For teams running v0.x code in production, the migration cost to v1.0 is real.<\/p>\n<p>Here is a working LangChain LCEL chain \u2014 the modern way to compose LangChain operations.<\/p>\n<p>Prerequisites:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f3b263947723\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\npip install langchain langchain-openai python-dotenv<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">install <\/span><span class=\"crayon-e\">langchain <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>How to run: Save as <strong>langchain_chain.py<\/strong>, add <strong>OPENAI_API_KEY<\/strong> to your <strong>.env<\/strong>, run <strong>python langchain_chain.py<\/strong><\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f48910269957\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\n# langchain_chain.py&#13;<br \/>\n# A LangChain LCEL chain: prompt template \u2192 model \u2192 output parser&#13;<br \/>\n# Prerequisites: pip install langchain langchain-openai python-dotenv&#13;<br \/>\n# How to run: python langchain_chain.py&#13;<br \/>\n&#13;<br \/>\nimport os&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom langchain_core.prompts import ChatPromptTemplate&#13;<br \/>\nfrom langchain_core.output_parsers import StrOutputParser&#13;<br \/>\nfrom langchain_openai import ChatOpenAI&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 MODEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# ChatOpenAI wraps OpenAI&#8217;s chat models. Swap the model string to switch&#13;<br \/>\n# to gpt-4o-mini (cheaper) or claude-3-5-sonnet (via langchain-anthropic) &#8211;&#13;<br \/>\n# the chain code below stays identical either way. This model portability&#13;<br \/>\n# is one of LangChain&#8217;s genuine advantages over raw API calls.&#13;<br \/>\nllm = ChatOpenAI(&#13;<br \/>\n    model=&#8221;gpt-4o&#8221;,&#13;<br \/>\n    temperature=0.2,&#13;<br \/>\n    api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 PROMPT TEMPLATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# ChatPromptTemplate defines the message structure with named variables.&#13;<br \/>\n# {topic} gets filled in at runtime &#8212; templates are reusable and versionable.&#13;<br \/>\nprompt = ChatPromptTemplate.from_messages([&#13;<br \/>\n    (&#8220;system&#8221;, &#8220;You are a concise technical explainer. Keep answers under 100 words.&#8221;),&#13;<br \/>\n    (&#8220;human&#8221;, &#8220;Explain {topic} in simple terms.&#8221;)&#13;<br \/>\n])&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 OUTPUT PARSER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# StrOutputParser extracts the text content from the model&#8217;s AIMessage response.&#13;<br \/>\n# Without it you get back an AIMessage object rather than a plain string.&#13;<br \/>\nparser = StrOutputParser()&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 CHAIN (LCEL) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# The pipe operator (|) builds a sequential chain: prompt \u2192 llm \u2192 parser.&#13;<br \/>\n# LCEL (LangChain Expression Language) makes the composition readable and&#13;<br \/>\n# supports streaming, batching, and async execution with the same interface.&#13;<br \/>\nchain = prompt | llm | parser&#13;<br \/>\n&#13;<br \/>\nif __name__ == &#8220;__main__&#8221;:&#13;<br \/>\n    # invoke() runs the full chain synchronously&#13;<br \/>\n    result = chain.invoke({&#8220;topic&#8221;: &#8220;vector embeddings&#8221;})&#13;<br \/>\n    print(result)&#13;<br \/>\n&#13;<br \/>\n    # stream() yields tokens as they arrive &#8212; no code changes needed for streaming&#13;<br \/>\n    print(&#8220;\\n&#8212; Streaming response &#8212;&#8220;)&#13;<br \/>\n    for chunk in chain.stream({&#8220;topic&#8221;: &#8220;RAG pipelines&#8221;}):&#13;<br \/>\n        print(chunk, end=&#8221;&#8221;, flush=True)&#13;<br \/>\n    print()<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># langchain_chain.py<\/span><\/p>\n<p><span class=\"crayon-p\"># A LangChain LCEL chain: prompt template \u2192 model \u2192 output parser<\/span><\/p>\n<p><span class=\"crayon-p\"># Prerequisites: pip install langchain langchain-openai python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\"># How to run: python langchain_chain.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">prompts <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatPromptTemplate<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">output_parsers <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">StrOutputParser<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">langchain_openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 MODEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># ChatOpenAI wraps OpenAI&#8217;s chat models. Swap the model string to switch<\/span><\/p>\n<p><span class=\"crayon-p\"># to gpt-4o-mini (cheaper) or claude-3-5-sonnet (via langchain-anthropic) &#8212;<\/span><\/p>\n<p><span class=\"crayon-p\"># the chain code below stays identical either way. This model portability<\/span><\/p>\n<p><span class=\"crayon-p\"># is one of LangChain&#8217;s genuine advantages over raw API calls.<\/span><\/p>\n<p><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0.2<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 PROMPT TEMPLATE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># ChatPromptTemplate defines the message structure with named variables.<\/span><\/p>\n<p><span class=\"crayon-p\"># {topic} gets filled in at runtime &#8212; templates are reusable and versionable.<\/span><\/p>\n<p><span class=\"crayon-v\">prompt<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">ChatPromptTemplate<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">from_messages<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;system&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;You are a concise technical explainer. Keep answers under 100 words.&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;human&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;Explain {topic} in simple terms.&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 OUTPUT PARSER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># StrOutputParser extracts the text content from the model&#8217;s AIMessage response.<\/span><\/p>\n<p><span class=\"crayon-p\"># Without it you get back an AIMessage object rather than a plain string.<\/span><\/p>\n<p><span class=\"crayon-v\">parser<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">StrOutputParser<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 CHAIN (LCEL) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># The pipe operator (|) builds a sequential chain: prompt \u2192 llm \u2192 parser.<\/span><\/p>\n<p><span class=\"crayon-p\"># LCEL (LangChain Expression Language) makes the composition readable and<\/span><\/p>\n<p><span class=\"crayon-p\"># supports streaming, batching, and async execution with the same interface.<\/span><\/p>\n<p><span class=\"crayon-v\">chain<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">prompt<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">|<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">|<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">parser<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">__name__<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;__main__&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># invoke() runs the full chain synchronously<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">chain<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">invoke<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;topic&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;vector embeddings&#8221;<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># stream() yields tokens as they arrive &#8212; no code changes needed for streaming<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;\\n&#8212; Streaming response &#8212;&#8220;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">chunk <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">chain<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">stream<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;topic&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;RAG pipelines&#8221;<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">chunk<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">end<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">flush<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p><strong>What this does:<\/strong> Three objects \u2014 <code>prompt<\/code>, <code>llm<\/code>, <code>parser<\/code> \u2014 are connected with the <code>|<\/code> operator. LangChain\u2019s LCEL executes them in order: the template fills in <code>{topic}<\/code>, passes a formatted message to the model, and the parser extracts a plain string from the response. The same chain supports <code>.invoke()<\/code>, <code>.stream()<\/code>, <code>.batch()<\/code>, and <code>.ainvoke()<\/code> without any changes to the chain definition itself. That interface consistency is the clearest argument for LangChain on projects that need multiple execution patterns.<\/p>\n<p>Here is the same foundation extended to a tool-using agent with LangGraph.<\/p>\n<p>Prerequisites:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f4e743253684\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\npip install langchain langchain-openai langgraph langchain-community python-dotenv<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">install <\/span><span class=\"crayon-e\">langchain <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">langgraph <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">community <\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>How to run: Save as <strong>langchain_agent.py<\/strong> and run <strong>python langchain_agent.py<\/strong><\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f52689296235\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\n# langchain_agent.py&#13;<br \/>\n# A LangGraph ReAct agent with two tools: web search and a calculator&#13;<br \/>\n# Prerequisites: pip install langchain langchain-openai langgraph langchain-community python-dotenv&#13;<br \/>\n# How to run: python langchain_agent.py&#13;<br \/>\n&#13;<br \/>\nimport os&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom langchain_openai import ChatOpenAI&#13;<br \/>\nfrom langchain.tools import tool&#13;<br \/>\nfrom langchain_community.tools import DuckDuckGoSearchRun&#13;<br \/>\nfrom langchain_core.messages import HumanMessage&#13;<br \/>\nfrom langgraph.prebuilt import create_react_agent&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\nllm = ChatOpenAI(model=&#8221;gpt-4o&#8221;, temperature=0, api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;))&#13;<br \/>\n&#13;<br \/>\n# Web search &#8212; no API key required&#13;<br \/>\nsearch = DuckDuckGoSearchRun()&#13;<br \/>\n&#13;<br \/>\n@tool&#13;<br \/>\ndef calculate(expression: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Evaluate a safe mathematical expression. Use for arithmetic or percentage calculations.&#13;<br \/>\n    Input: a Python math expression string (e.g., &#8216;1500 * 0.08&#8217;).&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    try:&#13;<br \/>\n        result = eval(expression, {&#8220;__builtins__&#8221;: {}}, {})&#13;<br \/>\n        return f&#8221;Result: {result}&#8221;&#13;<br \/>\n    except Exception as e:&#13;<br \/>\n        return f&#8221;Error: {str(e)}&#8221;&#13;<br \/>\n&#13;<br \/>\ntools = [search, calculate]&#13;<br \/>\n&#13;<br \/>\n# create_react_agent wires together the LLM, tools, and a built-in ReAct loop.&#13;<br \/>\n# The agent thinks, calls a tool, reads the result, and continues until done.&#13;<br \/>\nagent = create_react_agent(llm, tools)&#13;<br \/>\n&#13;<br \/>\nif __name__ == &#8220;__main__&#8221;:&#13;<br \/>\n    result = agent.invoke({&#13;<br \/>\n        &#8220;messages&#8221;: [HumanMessage(content=&#8221;What is 15% of 2400?&#8221;)]&#13;<br \/>\n    })&#13;<br \/>\n    print(result[&#8220;messages&#8221;][-1].content)<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># langchain_agent.py<\/span><\/p>\n<p><span class=\"crayon-p\"># A LangGraph ReAct agent with two tools: web search and a calculator<\/span><\/p>\n<p><span class=\"crayon-p\"># Prerequisites: pip install langchain langchain-openai langgraph langchain-community python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\"># How to run: python langchain_agent.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">langchain_openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">tools <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">tool<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_community<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">tools <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">DuckDuckGoSearchRun<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">messages <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">HumanMessage<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langgraph<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">prebuilt <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">create_react_agent<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Web search &#8212; no API key required<\/span><\/p>\n<p><span class=\"crayon-v\">search<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">DuckDuckGoSearchRun<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-sy\">@<\/span><span class=\"crayon-e\">tool<\/span><\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">calculate<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">expression<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Evaluate a safe mathematical expression. Use for arithmetic or percentage calculations.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Input: a Python math expression string (e.g., &#8216;1500 * 0.08&#8217;).<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">try<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">eval<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">expression<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;__builtins__&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Result: {result}&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">except <\/span><span class=\"crayon-e\">Exception <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">e<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Error: {str(e)}&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">tools<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">search<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">calculate<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># create_react_agent wires together the LLM, tools, and a built-in ReAct loop.<\/span><\/p>\n<p><span class=\"crayon-p\"># The agent thinks, calls a tool, reads the result, and continues until done.<\/span><\/p>\n<p><span class=\"crayon-v\">agent<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">create_react_agent<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">tools<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">__name__<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;__main__&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">agent<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">invoke<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;messages&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-e\">HumanMessage<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">content<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;What is 15% of 2400?&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;messages&#8221;<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">content<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p><strong>What this does:<\/strong> <strong>create_react_agent<\/strong> abstracts the full reasoning loop. The model decides whether to use a tool, LangGraph executes the selected tool, feeds the result back into the message history, and repeats until the model has a final answer. What would take 50+ lines in a raw implementation is four lines here. That abstraction is appropriate when you need it. The question the next section addresses is: <em>when do you not?<\/em><\/p>\n<h2>LlamaIndex: The Retrieval Layer<\/h2>\n<p>LlamaIndex was designed from the ground up for one job: helping LLMs reason over external data. That focus is both its biggest strength and the clearest signal for when to use it. If your application\u2019s central challenge is \u201chow do I get the model to answer accurately from my documents,\u201d LlamaIndex is the right starting point.<\/p>\n<p>The performance numbers reflect that specialization. <a href=\"https:\/\/wifitalents.com\/llamaindex-statistics\/\" target=\"_blank\">LlamaIndex indexes documents 2.5x faster than LangChain and hits sub-200ms query latency for 10,000 documents<\/a>. Its framework overhead of <a href=\"https:\/\/aimultiple.com\/rag-frameworks\" target=\"_blank\">~6ms compares favorably to LangChain\u2019s ~10ms and LangGraph\u2019s ~14ms<\/a>. At the token level, <a href=\"https:\/\/www.morphllm.com\/comparisons\/langchain-vs-llamaindex\" target=\"_blank\">LlamaIndex uses ~1.6K tokens per query versus LangChain\u2019s ~2.4K<\/a> \u2014 a 33% difference that adds up quickly at scale.<\/p>\n<p>The architectural reason for those differences is that LlamaIndex treats retrieval as a first-class primitive, not a composable component. Its five core abstractions \u2014 data connectors, node parsers, indices, query engines, and workflows \u2014 are designed to work together out of the box. Hierarchical chunking preserves parent-child relationships between document sections. Auto-merging retrieval recombines related chunks at query time. Sub-question decomposition breaks complex queries into simpler ones and merges the results. You get all of this with less code: <a href=\"https:\/\/www.morphllm.com\/comparisons\/langchain-vs-llamaindex\" target=\"_blank\">LangChain requires 30\u201340% more code than LlamaIndex for equivalent RAG pipelines<\/a>.<\/p>\n<p>Where LlamaIndex is weaker is on the agent side. Its Workflows system handles async, event-driven pipelines well, but stateful multi-turn agents with built-in persistence require more manual implementation than LangGraph. LangGraph\u2019s checkpointing \u2014 where an agent pauses, persists its full state, and resumes later \u2014 is something LlamaIndex Workflows can achieve but does not provide out of the box. For document Q&amp;A and knowledge retrieval, this rarely matters. For long-running agentic workflows with human-in-the-loop requirements, it matters a great deal.<\/p>\n<p>Here is a complete LlamaIndex RAG pipeline, from document ingestion to query.<\/p>\n<p>Prerequisites:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f58186363867\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\npip install llama-index llama-index-llms-openai llama-index-embeddings-openai python-dotenv<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">install <\/span><span class=\"crayon-v\">llama<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">index <\/span><span class=\"crayon-v\">llama<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">llms<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">llama<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">embeddings<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>How to run: Save as <strong>llamaindex_rag.py<\/strong> and run <strong>python llamaindex_rag.py<\/strong><\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f5d747344026\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\n# llamaindex_rag.py&#13;<br \/>\n# Complete LlamaIndex RAG pipeline: ingest documents \u2192 index \u2192 query&#13;<br \/>\n# Prerequisites: pip install llama-index llama-index-llms-openai&#13;<br \/>\n#                llama-index-embeddings-openai python-dotenv&#13;<br \/>\n# How to run: python llamaindex_rag.py&#13;<br \/>\n&#13;<br \/>\nimport os&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom llama_index.core import VectorStoreIndex, Document, Settings&#13;<br \/>\nfrom llama_index.llms.openai import OpenAI as LlamaOpenAI&#13;<br \/>\nfrom llama_index.embeddings.openai import OpenAIEmbedding&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 GLOBAL SETTINGS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# LlamaIndex v0.10+ uses a global Settings object instead of ServiceContext.&#13;<br \/>\n# Configure your LLM and embedding model once here &#8212; all pipeline components&#13;<br \/>\n# pick them up automatically. Swap models here to change the whole pipeline.&#13;<br \/>\nSettings.llm = LlamaOpenAI(&#13;<br \/>\n    model=&#8221;gpt-4o&#8221;,&#13;<br \/>\n    temperature=0,&#13;<br \/>\n    api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n)&#13;<br \/>\nSettings.embed_model = OpenAIEmbedding(&#13;<br \/>\n    model=&#8221;text-embedding-3-small&#8221;,  # Fast and cost-effective for most RAG tasks&#13;<br \/>\n    api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 DOCUMENTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# In production, replace with: SimpleDirectoryReader(&#8220;.\/docs&#8221;).load_data()&#13;<br \/>\n# LlamaHub provides 300+ connectors for Notion, Google Drive, PDFs, databases.&#13;<br \/>\n# Documents created inline here to keep the example fully self-contained.&#13;<br \/>\ndocuments = [&#13;<br \/>\n    Document(&#13;<br \/>\n        text=(&#13;<br \/>\n            &#8220;LlamaIndex is a data framework for LLM applications. &#8220;&#13;<br \/>\n            &#8220;It specializes in document ingestion, chunking, embedding, and retrieval. &#8220;&#13;<br \/>\n            &#8220;Core abstractions: data connectors, node parsers, indices, query engines, &#8220;&#13;<br \/>\n            &#8220;and workflows. LlamaHub provides 300+ pre-built data connectors.&#8221;&#13;<br \/>\n        ),&#13;<br \/>\n        metadata={&#8220;source&#8221;: &#8220;llamaindex_overview&#8221;}&#13;<br \/>\n    ),&#13;<br \/>\n    Document(&#13;<br \/>\n        text=(&#13;<br \/>\n            &#8220;LangChain is a general-purpose LLM orchestration framework. &#8220;&#13;<br \/>\n            &#8220;It excels at chaining operations, multi-step agents, tool use, and memory. &#8220;&#13;<br \/>\n            &#8220;LangGraph &#8212; the recommended way to build stateful agents in the LangChain &#8220;&#13;<br \/>\n            &#8220;ecosystem &#8212; stabilized at v1.0 in October 2025.&#8221;&#13;<br \/>\n        ),&#13;<br \/>\n        metadata={&#8220;source&#8221;: &#8220;langchain_overview&#8221;}&#13;<br \/>\n    ),&#13;<br \/>\n    Document(&#13;<br \/>\n        text=(&#13;<br \/>\n            &#8220;Raw API calls use the OpenAI or Anthropic SDK directly with no framework. &#8220;&#13;<br \/>\n            &#8220;This approach has the lowest latency and highest transparency. &#8220;&#13;<br \/>\n            &#8220;Best for simple, one-off tasks where framework abstraction adds no value. &#8220;&#13;<br \/>\n            &#8220;As complexity grows, a thin internal wrapper is usually preferable to &#8220;&#13;<br \/>\n            &#8220;adopting a full orchestration framework.&#8221;&#13;<br \/>\n        ),&#13;<br \/>\n        metadata={&#8220;source&#8221;: &#8220;raw_api_overview&#8221;}&#13;<br \/>\n    ),&#13;<br \/>\n]&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 INDEX \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# from_documents() handles the full pipeline: chunk \u2192 embed \u2192 store.&#13;<br \/>\n# By default, vectors are stored in memory. For production, pass a vector store:&#13;<br \/>\n# index = VectorStoreIndex.from_documents(docs, storage_context=storage_context)&#13;<br \/>\n# where storage_context points to Pinecone, Weaviate, Chroma, etc.&#13;<br \/>\nindex = VectorStoreIndex.from_documents(documents)&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 QUERY ENGINE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# as_query_engine() creates a retrieval + generation pipeline in one call.&#13;<br \/>\n# similarity_top_k=2 retrieves the 2 most relevant chunks per query.&#13;<br \/>\n# response_mode=&#8221;compact&#8221; merges retrieved chunks before passing to the LLM &#8211;&#13;<br \/>\n# reduces token usage compared to &#8220;default&#8221; mode, which sends each chunk separately.&#13;<br \/>\nquery_engine = index.as_query_engine(&#13;<br \/>\n    similarity_top_k=2,&#13;<br \/>\n    response_mode=&#8221;compact&#8221;&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\nif __name__ == &#8220;__main__&#8221;:&#13;<br \/>\n    questions = [&#13;<br \/>\n        &#8220;What is LlamaIndex best suited for?&#8221;,&#13;<br \/>\n        &#8220;How does LangChain differ from LlamaIndex?&#8221;,&#13;<br \/>\n        &#8220;When should I use raw API calls instead of a framework?&#8221;,&#13;<br \/>\n    ]&#13;<br \/>\n&#13;<br \/>\n    for q in questions:&#13;<br \/>\n        print(f&#8221;Q: {q}&#8221;)&#13;<br \/>\n        response = query_engine.query(q)&#13;<br \/>\n        print(f&#8221;A: {response}\\n&#8221;)<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<p>66<\/p>\n<p>67<\/p>\n<p>68<\/p>\n<p>69<\/p>\n<p>70<\/p>\n<p>71<\/p>\n<p>72<\/p>\n<p>73<\/p>\n<p>74<\/p>\n<p>75<\/p>\n<p>76<\/p>\n<p>77<\/p>\n<p>78<\/p>\n<p>79<\/p>\n<p>80<\/p>\n<p>81<\/p>\n<p>82<\/p>\n<p>83<\/p>\n<p>84<\/p>\n<p>85<\/p>\n<p>86<\/p>\n<p>87<\/p>\n<p>88<\/p>\n<p>89<\/p>\n<p>90<\/p>\n<p>91<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># llamaindex_rag.py<\/span><\/p>\n<p><span class=\"crayon-p\"># Complete LlamaIndex RAG pipeline: ingest documents \u2192 index \u2192 query<\/span><\/p>\n<p><span class=\"crayon-p\"># Prerequisites: pip install llama-index llama-index-llms-openai<\/span><\/p>\n<p><span class=\"crayon-p\">#\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0llama-index-embeddings-openai python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\"># How to run: python llamaindex_rag.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">llama_index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">core <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-v\">VectorStoreIndex<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">Document<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">Settings<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">llama_index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">llms<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">OpenAI <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">LlamaOpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">llama_index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">embeddings<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">OpenAIEmbedding<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 GLOBAL SETTINGS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># LlamaIndex v0.10+ uses a global Settings object instead of ServiceContext.<\/span><\/p>\n<p><span class=\"crayon-p\"># Configure your LLM and embedding model once here &#8212; all pipeline components<\/span><\/p>\n<p><span class=\"crayon-p\"># pick them up automatically. Swap models here to change the whole pipeline.<\/span><\/p>\n<p><span class=\"crayon-v\">Settings<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">LlamaOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">Settings<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">embed_model<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">OpenAIEmbedding<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;text-embedding-3-small&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\">\u00a0\u00a0<\/span><span class=\"crayon-p\"># Fast and cost-effective for most RAG tasks<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 DOCUMENTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># In production, replace with: SimpleDirectoryReader(&#8220;.\/docs&#8221;).load_data()<\/span><\/p>\n<p><span class=\"crayon-p\"># LlamaHub provides 300+ connectors for Notion, Google Drive, PDFs, databases.<\/span><\/p>\n<p><span class=\"crayon-p\"># Documents created inline here to keep the example fully self-contained.<\/span><\/p>\n<p><span class=\"crayon-v\">documents<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">Document<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;LlamaIndex is a data framework for LLM applications. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;It specializes in document ingestion, chunking, embedding, and retrieval. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Core abstractions: data connectors, node parsers, indices, query engines, &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;and workflows. LlamaHub provides 300+ pre-built data connectors.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">metadata<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;source&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;llamaindex_overview&#8221;<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">Document<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;LangChain is a general-purpose LLM orchestration framework. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;It excels at chaining operations, multi-step agents, tool use, and memory. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;LangGraph &#8212; the recommended way to build stateful agents in the LangChain &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;ecosystem &#8212; stabilized at v1.0 in October 2025.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">metadata<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;source&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;langchain_overview&#8221;<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">Document<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Raw API calls use the OpenAI or Anthropic SDK directly with no framework. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;This approach has the lowest latency and highest transparency. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Best for simple, one-off tasks where framework abstraction adds no value. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;As complexity grows, a thin internal wrapper is usually preferable to &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;adopting a full orchestration framework.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">metadata<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;source&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;raw_api_overview&#8221;<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 INDEX \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># from_documents() handles the full pipeline: chunk \u2192 embed \u2192 store.<\/span><\/p>\n<p><span class=\"crayon-p\"># By default, vectors are stored in memory. For production, pass a vector store:<\/span><\/p>\n<p><span class=\"crayon-p\"># index = VectorStoreIndex.from_documents(docs, storage_context=storage_context)<\/span><\/p>\n<p><span class=\"crayon-p\"># where storage_context points to Pinecone, Weaviate, Chroma, etc.<\/span><\/p>\n<p><span class=\"crayon-v\">index<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">VectorStoreIndex<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">from_documents<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">documents<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 QUERY ENGINE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># as_query_engine() creates a retrieval + generation pipeline in one call.<\/span><\/p>\n<p><span class=\"crayon-p\"># similarity_top_k=2 retrieves the 2 most relevant chunks per query.<\/span><\/p>\n<p><span class=\"crayon-p\"># response_mode=&#8221;compact&#8221; merges retrieved chunks before passing to the LLM &#8212;<\/span><\/p>\n<p><span class=\"crayon-p\"># reduces token usage compared to &#8220;default&#8221; mode, which sends each chunk separately.<\/span><\/p>\n<p><span class=\"crayon-v\">query_engine<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">as_query_engine<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">similarity_top_k<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">2<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">response_mode<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;compact&#8221;<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">__name__<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;__main__&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">questions<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;What is LlamaIndex best suited for?&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;How does LangChain differ from LlamaIndex?&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;When should I use raw API calls instead of a framework?&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">q<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">questions<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Q: {q}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">response<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">query_engine<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">q<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;A: {response}\\n&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p><strong>What this does:<\/strong> <code>Settings.llm<\/code> and <code>Settings.embed_model<\/code> configure the entire pipeline once. <code>VectorStoreIndex.from_documents()<\/code> handles chunking, embedding, and indexing in a single call \u2014 a process that takes 30\u201340% more code in LangChain. <code>as_query_engine()<\/code> then creates a retrieval + generation pipeline with two lines. The <code>similarity_top_k<\/code> and <code>response_mode<\/code> parameters give you control over the retrieval behavior without requiring you to assemble the retrieval components yourself. That is the LlamaIndex value proposition in concrete form: less assembly, more retrieval quality.<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.webp\" target=\"_blank\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.webp\" alt=\"A two-column architecture diagram comparing LlamaIndex and LangChain RAG pipelines side by side\" width=\"800\" height=\"706\"\/><\/a><\/p>\n<p class=\"wp-caption-text\">A two-column architecture diagram comparing LlamaIndex and LangChain RAG pipelines side by side (<a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.webp\" target=\"_blank\">click to enlarge<\/a>)<\/p>\n<\/div>\n<h2>Raw API Calls: The Minimal Path<\/h2>\n<p>The default assumption in most LLM developer communities is that you start with raw API calls and graduate to a framework as your project grows. The pattern worth examining in 2026 is the reverse: teams that started with LangChain and are quietly rewriting to raw SDKs.<\/p>\n<p>The OpenAI Agents SDK, released in March 2025 with 26,900 GitHub stars and 10.3 million monthly downloads, provides tool use, multi-agent handoffs, built-in tracing, and guardrails in a minimal package. Its overhead per tool call is 2\u20135ms versus LangChain\u2019s 10\u201330ms. Teams migrating from LangChain to raw SDKs typically see a 40\u201360% reduction in code volume and a 70\u201390% reduction in monthly framework maintenance burden.<\/p>\n<p>The argument for the raw path is not that frameworks are bad. It is that the value of an abstraction layer depends entirely on whether it is hiding complexity you actually face. In 2022, building prompt chains and handling tool calls reliably required framework support because vendor APIs were inconsistent. By 2026, OpenAI and Anthropic have absorbed tool calling, streaming, function schemas, and multi-turn memory into their native SDKs. The framework\u2019s abstractions no longer hide meaningful differences. They hide clarity.<\/p>\n<p><a href=\"https:\/\/www.aibuilderclub.com\/blog\/langchain-vs-crewai-vs-raw-api\" target=\"_blank\">Raw API is consistently the fastest option<\/a>, with no framework overhead and no extra LLM calls for orchestration. Frameworks add 100\u2013500ms of Python overhead per agent step. For latency-sensitive workloads \u2014 real-time customer support, voice agents, and high-throughput pipelines \u2014 that overhead is real and worth avoiding.<\/p>\n<p>Here is a complete tool-using agent built on the raw OpenAI SDK in under 80 lines.<\/p>\n<p>Prerequisites:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f63147020755\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\npip install openai python-dotenv<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">install <\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>How to run: Save as <strong>raw_api_agent.py<\/strong> and run <strong>python raw_api_agent.py<\/strong><\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f67198043774\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\n# raw_api_agent.py&#13;<br \/>\n# A complete tool-using agent on the raw OpenAI SDK &#8212; no framework.&#13;<br \/>\n# This is ~75 lines including comments. Compare it to the LangChain equivalent.&#13;<br \/>\n# Prerequisites: pip install openai python-dotenv&#13;<br \/>\n# How to run: python raw_api_agent.py&#13;<br \/>\n&#13;<br \/>\nimport os&#13;<br \/>\nimport json&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\nfrom openai import OpenAI&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\nclient = OpenAI(api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;))&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 TOOL DEFINITIONS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# The model reads these descriptions to decide when and how to call each tool.&#13;<br \/>\n# Clear, specific descriptions are more important here than in any framework &#8211;&#13;<br \/>\n# there is no wrapper to fill in gaps.&#13;<br \/>\nTOOLS = [&#13;<br \/>\n    {&#13;<br \/>\n        &#8220;type&#8221;: &#8220;function&#8221;,&#13;<br \/>\n        &#8220;function&#8221;: {&#13;<br \/>\n            &#8220;name&#8221;: &#8220;calculate&#8221;,&#13;<br \/>\n            &#8220;description&#8221;: (&#13;<br \/>\n                &#8220;Evaluate a mathematical expression. Use for arithmetic, &#8220;&#13;<br \/>\n                &#8220;percentages, or numerical computation. &#8220;&#13;<br \/>\n                &#8220;Input: a Python math expression as a string.&#8221;&#13;<br \/>\n            ),&#13;<br \/>\n            &#8220;parameters&#8221;: {&#13;<br \/>\n                &#8220;type&#8221;: &#8220;object&#8221;,&#13;<br \/>\n                &#8220;properties&#8221;: {&#13;<br \/>\n                    &#8220;expression&#8221;: {&#13;<br \/>\n                        &#8220;type&#8221;: &#8220;string&#8221;,&#13;<br \/>\n                        &#8220;description&#8221;: &#8220;A Python math expression, e.g. &#8216;1500 * 0.08&#8242;&#8221;&#13;<br \/>\n                    }&#13;<br \/>\n                },&#13;<br \/>\n                &#8220;required&#8221;: [&#8220;expression&#8221;]&#13;<br \/>\n            }&#13;<br \/>\n        }&#13;<br \/>\n    },&#13;<br \/>\n    {&#13;<br \/>\n        &#8220;type&#8221;: &#8220;function&#8221;,&#13;<br \/>\n        &#8220;function&#8221;: {&#13;<br \/>\n            &#8220;name&#8221;: &#8220;get_word_count&#8221;,&#13;<br \/>\n            &#8220;description&#8221;: &#8220;Count the number of words in a given string of text.&#8221;,&#13;<br \/>\n            &#8220;parameters&#8221;: {&#13;<br \/>\n                &#8220;type&#8221;: &#8220;object&#8221;,&#13;<br \/>\n                &#8220;properties&#8221;: {&#13;<br \/>\n                    &#8220;text&#8221;: {&#8220;type&#8221;: &#8220;string&#8221;, &#8220;description&#8221;: &#8220;The text to count.&#8221;}&#13;<br \/>\n                },&#13;<br \/>\n                &#8220;required&#8221;: [&#8220;text&#8221;]&#13;<br \/>\n            }&#13;<br \/>\n        }&#13;<br \/>\n    }&#13;<br \/>\n]&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 TOOL IMPLEMENTATIONS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\ndef calculate(expression: str) -&gt; str:&#13;<br \/>\n    try:&#13;<br \/>\n        result = eval(expression, {&#8220;__builtins__&#8221;: {}}, {})&#13;<br \/>\n        return str(result)&#13;<br \/>\n    except Exception as e:&#13;<br \/>\n        return f&#8221;Error: {e}&#8221;&#13;<br \/>\n&#13;<br \/>\ndef get_word_count(text: str) -&gt; str:&#13;<br \/>\n    return str(len(text.split()))&#13;<br \/>\n&#13;<br \/>\n# Maps tool name \u2192 Python function for dynamic dispatch in the loop below&#13;<br \/>\nTOOL_DISPATCH = {&#8220;calculate&#8221;: calculate, &#8220;get_word_count&#8221;: get_word_count}&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500 AGENT LOOP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\ndef run_agent(user_message: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    A complete ReAct-style agent loop using raw OpenAI tool calls.&#13;<br \/>\n    The model decides whether to call a tool or return a final answer.&#13;<br \/>\n    The loop continues until the model stops requesting tool calls.&#13;<br \/>\n    Every step is visible &#8212; no framework wrapping, no hidden logic.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    messages = [&#13;<br \/>\n        {&#8220;role&#8221;: &#8220;system&#8221;, &#8220;content&#8221;: &#8220;You are a helpful assistant.&#8221;},&#13;<br \/>\n        {&#8220;role&#8221;: &#8220;user&#8221;,   &#8220;content&#8221;: user_message},&#13;<br \/>\n    ]&#13;<br \/>\n&#13;<br \/>\n    while True:&#13;<br \/>\n        response = client.chat.completions.create(&#13;<br \/>\n            model=&#8221;gpt-4o&#8221;,&#13;<br \/>\n            messages=messages,&#13;<br \/>\n            tools=TOOLS,&#13;<br \/>\n            tool_choice=&#8221;auto&#8221;,  # Model decides: call a tool or respond directly&#13;<br \/>\n            temperature=0,&#13;<br \/>\n        )&#13;<br \/>\n&#13;<br \/>\n        message = response.choices[0].message&#13;<br \/>\n        messages.append(message)  # Always add the assistant message to history&#13;<br \/>\n&#13;<br \/>\n        # No tool calls = the model has its final answer&#13;<br \/>\n        if not message.tool_calls:&#13;<br \/>\n            return message.content&#13;<br \/>\n&#13;<br \/>\n        # Execute each tool call the model requested&#13;<br \/>\n        for tool_call in message.tool_calls:&#13;<br \/>\n            name = tool_call.function.name&#13;<br \/>\n            args = json.loads(tool_call.function.arguments)&#13;<br \/>\n            fn   = TOOL_DISPATCH.get(name)&#13;<br \/>\n            result = fn(**args) if fn else f&#8221;Unknown tool: {name}&#8221;&#13;<br \/>\n&#13;<br \/>\n            # Tool result goes back into the message history.&#13;<br \/>\n            # The model reads this on the next iteration to decide what to do next.&#13;<br \/>\n            messages.append({&#13;<br \/>\n                &#8220;role&#8221;:        &#8220;tool&#8221;,&#13;<br \/>\n                &#8220;tool_call_id&#8221;: tool_call.id,&#13;<br \/>\n                &#8220;content&#8221;:     result,&#13;<br \/>\n            })&#13;<br \/>\n        # Loop &#8212; the model now processes the tool results&#13;<br \/>\n&#13;<br \/>\nif __name__ == &#8220;__main__&#8221;:&#13;<br \/>\n    queries = [&#13;<br \/>\n        &#8220;What is 18% of 3500?&#8221;,&#13;<br \/>\n        &#8220;How many words are in: The quick brown fox jumps over the lazy dog?&#8221;,&#13;<br \/>\n        &#8220;Split 240 items into groups of 16. How many groups?&#8221;,&#13;<br \/>\n    ]&#13;<br \/>\n    for q in queries:&#13;<br \/>\n        print(f&#8221;Q: {q}\\nA: {run_agent(q)}\\n&#8221;)<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<p>66<\/p>\n<p>67<\/p>\n<p>68<\/p>\n<p>69<\/p>\n<p>70<\/p>\n<p>71<\/p>\n<p>72<\/p>\n<p>73<\/p>\n<p>74<\/p>\n<p>75<\/p>\n<p>76<\/p>\n<p>77<\/p>\n<p>78<\/p>\n<p>79<\/p>\n<p>80<\/p>\n<p>81<\/p>\n<p>82<\/p>\n<p>83<\/p>\n<p>84<\/p>\n<p>85<\/p>\n<p>86<\/p>\n<p>87<\/p>\n<p>88<\/p>\n<p>89<\/p>\n<p>90<\/p>\n<p>91<\/p>\n<p>92<\/p>\n<p>93<\/p>\n<p>94<\/p>\n<p>95<\/p>\n<p>96<\/p>\n<p>97<\/p>\n<p>98<\/p>\n<p>99<\/p>\n<p>100<\/p>\n<p>101<\/p>\n<p>102<\/p>\n<p>103<\/p>\n<p>104<\/p>\n<p>105<\/p>\n<p>106<\/p>\n<p>107<\/p>\n<p>108<\/p>\n<p>109<\/p>\n<p>110<\/p>\n<p>111<\/p>\n<p>112<\/p>\n<p>113<\/p>\n<p>114<\/p>\n<p>115<\/p>\n<p>116<\/p>\n<p>117<\/p>\n<p>118<\/p>\n<p>119<\/p>\n<p>120<\/p>\n<p>121<\/p>\n<p>122<\/p>\n<p>123<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># raw_api_agent.py<\/span><\/p>\n<p><span class=\"crayon-p\"># A complete tool-using agent on the raw OpenAI SDK &#8212; no framework.<\/span><\/p>\n<p><span class=\"crayon-p\"># This is ~75 lines including comments. Compare it to the LangChain equivalent.<\/span><\/p>\n<p><span class=\"crayon-p\"># Prerequisites: pip install openai python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\"># How to run: python raw_api_agent.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">json<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">OpenAI<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">client<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">OpenAI<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 TOOL DEFINITIONS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># The model reads these descriptions to decide when and how to call each tool.<\/span><\/p>\n<p><span class=\"crayon-p\"># Clear, specific descriptions are more important here than in any framework &#8212;<\/span><\/p>\n<p><span class=\"crayon-p\"># there is no wrapper to fill in gaps.<\/span><\/p>\n<p><span class=\"crayon-v\">TOOLS<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;type&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;function&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;function&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;name&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;calculate&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;description&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Evaluate a mathematical expression. Use for arithmetic, &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;percentages, or numerical computation. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Input: a Python math expression as a string.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;parameters&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;type&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;object&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;properties&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;expression&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;type&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;string&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;description&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;A Python math expression, e.g. &#8216;1500 * 0.08&#8242;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;required&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;expression&#8221;<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;type&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;function&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;function&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;name&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;get_word_count&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;description&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;Count the number of words in a given string of text.&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;parameters&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;type&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;object&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;properties&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;text&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;type&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;string&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;description&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;The text to count.&#8221;<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;required&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;text&#8221;<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 TOOL IMPLEMENTATIONS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">calculate<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">expression<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">try<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">eval<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">expression<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;__builtins__&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">str<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">except <\/span><span class=\"crayon-e\">Exception <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">e<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Error: {e}&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">get_word_count<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">str<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">len<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">split<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Maps tool name \u2192 Python function for dynamic dispatch in the loop below<\/span><\/p>\n<p><span class=\"crayon-v\">TOOL_DISPATCH<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;calculate&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">calculate<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;get_word_count&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">get_word_count<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500 AGENT LOOP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">run_agent<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">user_message<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0A complete ReAct-style agent loop using raw OpenAI tool calls.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0The model decides whether to call a tool or return a final answer.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0The loop continues until the model stops requesting tool calls.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Every step is visible &#8212; no framework wrapping, no hidden logic.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">messages<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;role&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;system&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;content&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;You are a helpful assistant.&#8221;<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;role&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;user&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\">\u00a0\u00a0 <\/span><span class=\"crayon-s\">&#8220;content&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">user_message<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">while<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-t\">True<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">response<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">client<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chat<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">completions<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">create<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">messages<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">messages<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">tools<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">TOOLS<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">tool_choice<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;auto&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\">\u00a0\u00a0<\/span><span class=\"crayon-p\"># Model decides: call a tool or respond directly<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">response<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">choices<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">message<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">messages<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">append<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\">\u00a0\u00a0<\/span><span class=\"crayon-p\"># Always add the assistant message to history<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># No tool calls = the model has its final answer<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">not<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">tool_calls<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-i\">content<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Execute each tool call the model requested<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">tool_call <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">tool_calls<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">name<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">tool_call<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-t\">function<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">name<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">args<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">json<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">loads<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">tool_call<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-t\">function<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">arguments<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">fn<\/span><span class=\"crayon-h\">\u00a0\u00a0 <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">TOOL_DISPATCH<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">get<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">name<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">fn<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-o\">*<\/span><span class=\"crayon-o\">*<\/span><span class=\"crayon-v\">args<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">fn <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Unknown tool: {name}&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Tool result goes back into the message history.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># The model reads this on the next iteration to decide what to do next.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">messages<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">append<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;role&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;tool&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;tool_call_id&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">tool_call<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">id<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;content&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0 <\/span><span class=\"crayon-v\">result<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Loop &#8212; the model now processes the tool results<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">__name__<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;__main__&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">queries<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;What is 18% of 3500?&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;How many words are in: The quick brown fox jumps over the lazy dog?&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Split 240 items into groups of 16. How many groups?&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">q<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">queries<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Q: {q}\\nA: {run_agent(q)}\\n&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p><strong>What this does:<\/strong> The agent loop is fully transparent. There is no framework between you and the model\u2019s response. The <strong>while True<\/strong> loop runs until <code>message.tool_calls<\/code> is empty, which happens when the model decides it has enough information to answer directly. Every message \u2014 system, user, assistant, and tool result \u2014 is in a plain Python list you can inspect, log, or modify at any point. That transparency is the raw path\u2019s core advantage: when something breaks, you know exactly where to look.<\/p>\n<h2>Head-to-Head Comparison<\/h2>\n<p>The same task was evaluated across three dimensions. All measurements reflect current benchmarks from independent analysis cited throughout this article.<\/p>\n<p><strong>Framework Overhead and Performance<\/strong><\/p>\n<table style=\"width: 100%;border-collapse: collapse;font-family: Arial, sans-serif;font-size: 14px;color: #333\">\n<thead>\n<tr>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Metric<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Raw API<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>LlamaIndex<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>LangChain (LCEL)<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>LangGraph<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Framework overhead<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~0ms<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~6ms<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~10ms<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~14ms<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Token overhead (per query)<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">0<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~1.6K<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~2.4K<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~2.0K<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Per tool call latency<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">2\u20135ms<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">N\/A<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">10\u201330ms<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">10\u201330ms<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Stack trace depth on error<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">2\u20135 frames<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">5\u201310 frames<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">15\u201340 frames<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">15\u201340 frames<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Debug transparency<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">High<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Medium<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Low<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Low<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Code Volume: Same RAG Task, Three Ways<\/strong><br \/>This is the most concrete way to feel the trade-off. All three implementations below answer the same question from the same context document:<\/p>\n<table style=\"width: 100%;border-collapse: collapse;font-family: Arial, sans-serif;font-size: 14px;color: #333\">\n<thead>\n<tr>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Implementation<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Lines of code<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Framework install size<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Debugging clarity<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Raw OpenAI SDK<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~20 lines<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\"><strong>openai<\/strong> only<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Full visibility<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">LlamaIndex<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~15 lines<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\"><strong>llama-index + plugins<\/strong><\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Medium<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">LangChain LCEL<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">~18 lines<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\"><strong>langchain + langchain-openai<\/strong><\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Low\u2013medium<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For a basic one-document Q&amp;A, the difference is marginal. Where LlamaIndex\u2019s code advantage compounds is when you add chunking strategies, multiple documents, re-ranking, metadata filtering, and hybrid search \u2014 each of which requires more assembly in LangChain than in LlamaIndex.<\/p>\n<p><strong>When Each One Breaks<\/strong><br \/>Knowing when each approach fails is as useful as knowing when it succeeds.<\/p>\n<table style=\"width: 100%;border-collapse: collapse;font-family: Arial, sans-serif;font-size: 14px;color: #333\">\n<thead>\n<tr>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Failure mode<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>Raw API<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>LlamaIndex<\/strong><\/th>\n<th style=\"padding: 12px;border: 1px solid #ddd;text-align: left;background-color: #add3ed\"><strong>LangChain<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Retrieval accuracy degrades<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">You built it, you fix it<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Tune chunking\/index strategy<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Tune each pipeline component separately<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Agent loops indefinitely<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Add <strong>max_iterations<\/strong> manually<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Workflow timeout<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\"><strong>max_iterations<\/strong> parameter<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Prompt changes break output<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Immediate, obvious<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Immediate, obvious<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">May propagate through chain silently<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Model API changes<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Update SDK<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Update llama-index package<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Update langchain-openai + retest<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Debugging a production error<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Direct, small stack<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Moderate<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Deep stack traces, hard to isolate<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Scaling to high throughput<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Optimal<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Good<\/td>\n<td style=\"padding: 12px;border: 1px solid #ddd\">Framework overhead compounds<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Full Working Example<\/h2>\n<p>The same document Q&amp;A task implemented three ways. Same input document, same question, different path through the stack. Read these side by side and the trade-offs become concrete.<\/p>\n<p>Prerequisites:<\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f6d279902760\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\npip install openai langchain langchain-openai llama-index \\&#13;<br \/>\n            llama-index-llms-openai llama-index-embeddings-openai python-dotenv<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">install <\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">langchain <\/span><span class=\"crayon-v\">langchain<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">llama<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">index<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">\\<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">llama<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">llms<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">llama<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">embeddings<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-v\">python<\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-v\">dotenv<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>How to run: Save as <strong>three_ways.py<\/strong> and run <strong>python three_ways.py<\/strong><\/p>\n<div id=\"urvanov-syntax-highlighter-6a5273c748f72497519606\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-mac print-yes notranslate\" data-settings=\" minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" readonly=\"readonly\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\n# three_ways.py&#13;<br \/>\n# The same document Q&amp;A task implemented three ways:&#13;<br \/>\n# Raw OpenAI SDK, LlamaIndex, and LangChain LCEL.&#13;<br \/>\n# Same input. Same output. Different path through the stack.&#13;<br \/>\n# Prerequisites: pip install openai langchain langchain-openai llama-index&#13;<br \/>\n#                llama-index-llms-openai llama-index-embeddings-openai python-dotenv&#13;<br \/>\n# How to run: python three_ways.py&#13;<br \/>\n&#13;<br \/>\nimport os&#13;<br \/>\nimport time&#13;<br \/>\nfrom dotenv import load_dotenv&#13;<br \/>\n&#13;<br \/>\nload_dotenv()&#13;<br \/>\n&#13;<br \/>\nQUESTION = &#8220;What is retrieval-augmented generation and why does it matter?&#8221;&#13;<br \/>\n&#13;<br \/>\nCONTEXT_DOC = &#8220;&#8221;&#8221;&#13;<br \/>\nRetrieval-Augmented Generation (RAG) is a technique that improves LLM responses&#13;<br \/>\nby fetching relevant context from an external knowledge base before generating&#13;<br \/>\nan answer. Instead of relying solely on training data, RAG retrieves the most&#13;<br \/>\nrelevant document chunks and includes them in the prompt. This reduces&#13;<br \/>\nhallucinations, keeps answers grounded in your actual data, and allows the model&#13;<br \/>\nto answer questions about information it was never trained on.&#13;<br \/>\n&#8220;&#8221;&#8221;&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# APPROACH 1: RAW OPENAI SDK&#13;<br \/>\n# When to use: simple, one-off calls where full visibility matters most&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\ndef raw_api_answer(question: str, context: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;Answer a question using context, via raw OpenAI SDK &#8212; no framework.&#8221;&#8221;&#8221;&#13;<br \/>\n    from openai import OpenAI&#13;<br \/>\n    client = OpenAI(api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;))&#13;<br \/>\n&#13;<br \/>\n    # Everything is explicit: the system prompt, the context injection,&#13;<br \/>\n    # the message structure. Nothing is hidden in a framework abstraction.&#13;<br \/>\n    response = client.chat.completions.create(&#13;<br \/>\n        model=&#8221;gpt-4o&#8221;,&#13;<br \/>\n        temperature=0,&#13;<br \/>\n        messages=[&#13;<br \/>\n            {&#13;<br \/>\n                &#8220;role&#8221;: &#8220;system&#8221;,&#13;<br \/>\n                &#8220;content&#8221;: (&#13;<br \/>\n                    &#8220;Answer questions using only the provided context. &#8220;&#13;<br \/>\n                    &#8220;If the answer is not in the context, say so clearly.&#8221;&#13;<br \/>\n                )&#13;<br \/>\n            },&#13;<br \/>\n            {&#13;<br \/>\n                &#8220;role&#8221;: &#8220;user&#8221;,&#13;<br \/>\n                &#8220;content&#8221;: f&#8221;Context:\\n{context}\\n\\nQuestion: {question}&#8221;&#13;<br \/>\n            }&#13;<br \/>\n        ]&#13;<br \/>\n    )&#13;<br \/>\n    return response.choices[0].message.content&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# APPROACH 2: LLAMAINDEX&#13;<br \/>\n# When to use: document-heavy retrieval where you want optimized RAG out of the box&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\ndef llamaindex_answer(question: str, context: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;Answer a question using LlamaIndex &#8212; purpose-built retrieval pipeline.&#8221;&#8221;&#8221;&#13;<br \/>\n    from llama_index.core import VectorStoreIndex, Document, Settings&#13;<br \/>\n    from llama_index.llms.openai import OpenAI as LlamaOpenAI&#13;<br \/>\n    from llama_index.embeddings.openai import OpenAIEmbedding&#13;<br \/>\n&#13;<br \/>\n    # Configure once &#8212; all pipeline components pick it up&#13;<br \/>\n    Settings.llm = LlamaOpenAI(&#13;<br \/>\n        model=&#8221;gpt-4o&#8221;, temperature=0,&#13;<br \/>\n        api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n    )&#13;<br \/>\n    Settings.embed_model = OpenAIEmbedding(&#13;<br \/>\n        model=&#8221;text-embedding-3-small&#8221;,&#13;<br \/>\n        api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    # from_documents() = chunk + embed + index in one call&#13;<br \/>\n    # For multiple documents, pass a list: from_documents([doc1, doc2, doc3])&#13;<br \/>\n    index = VectorStoreIndex.from_documents([Document(text=context)])&#13;<br \/>\n&#13;<br \/>\n    # as_query_engine() = retriever + generator, wired together automatically&#13;<br \/>\n    query_engine = index.as_query_engine(similarity_top_k=1)&#13;<br \/>\n    return str(query_engine.query(question))&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# APPROACH 3: LANGCHAIN LCEL&#13;<br \/>\n# When to use: workflows that will grow to include agents, memory, or routing&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\ndef langchain_answer(question: str, context: str) -&gt; str:&#13;<br \/>\n    &#8220;&#8221;&#8221;Answer a question using a LangChain LCEL chain.&#8221;&#8221;&#8221;&#13;<br \/>\n    from langchain_core.prompts import ChatPromptTemplate&#13;<br \/>\n    from langchain_core.output_parsers import StrOutputParser&#13;<br \/>\n    from langchain_openai import ChatOpenAI&#13;<br \/>\n&#13;<br \/>\n    llm = ChatOpenAI(&#13;<br \/>\n        model=&#8221;gpt-4o&#8221;, temperature=0,&#13;<br \/>\n        api_key=os.getenv(&#8220;OPENAI_API_KEY&#8221;)&#13;<br \/>\n    )&#13;<br \/>\n&#13;<br \/>\n    prompt = ChatPromptTemplate.from_messages([&#13;<br \/>\n        (&#8220;system&#8221;,&#13;<br \/>\n         &#8220;Answer using only the provided context. &#8220;&#13;<br \/>\n         &#8220;If the answer is not in the context, say so.\\n\\nContext:\\n{context}&#8221;),&#13;<br \/>\n        (&#8220;human&#8221;, &#8220;{question}&#8221;)&#13;<br \/>\n    ])&#13;<br \/>\n&#13;<br \/>\n    # The same chain supports .stream(), .batch(), .ainvoke() &#8212; no code changes needed&#13;<br \/>\n    chain = prompt | llm | StrOutputParser()&#13;<br \/>\n    return chain.invoke({&#8220;context&#8221;: context, &#8220;question&#8221;: question})&#13;<br \/>\n&#13;<br \/>\n&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\n# RUN ALL THREE AND COMPARE&#13;<br \/>\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&#13;<br \/>\nif __name__ == &#8220;__main__&#8221;:&#13;<br \/>\n    approaches = [&#13;<br \/>\n        (&#8220;Raw OpenAI SDK&#8221;, raw_api_answer),&#13;<br \/>\n        (&#8220;LlamaIndex&#8221;,     llamaindex_answer),&#13;<br \/>\n        (&#8220;LangChain LCEL&#8221;, langchain_answer),&#13;<br \/>\n    ]&#13;<br \/>\n&#13;<br \/>\n    for name, fn in approaches:&#13;<br \/>\n        print(f&#8221;\\n{&#8216;=&#8217;*60}&#8221;)&#13;<br \/>\n        print(f&#8221;Approach: {name}&#8221;)&#13;<br \/>\n        print(f&#8221;{&#8216;=&#8217;*60}&#8221;)&#13;<br \/>\n        start = time.perf_counter()&#13;<br \/>\n        answer = fn(QUESTION, CONTEXT_DOC)&#13;<br \/>\n        elapsed = time.perf_counter() &#8211; start&#13;<br \/>\n        print(f&#8221;Answer: {answer}&#8221;)&#13;<br \/>\n        print(f&#8221;Time (excluding LLM): visible in wall clock&#8221;)<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<p>37<\/p>\n<p>38<\/p>\n<p>39<\/p>\n<p>40<\/p>\n<p>41<\/p>\n<p>42<\/p>\n<p>43<\/p>\n<p>44<\/p>\n<p>45<\/p>\n<p>46<\/p>\n<p>47<\/p>\n<p>48<\/p>\n<p>49<\/p>\n<p>50<\/p>\n<p>51<\/p>\n<p>52<\/p>\n<p>53<\/p>\n<p>54<\/p>\n<p>55<\/p>\n<p>56<\/p>\n<p>57<\/p>\n<p>58<\/p>\n<p>59<\/p>\n<p>60<\/p>\n<p>61<\/p>\n<p>62<\/p>\n<p>63<\/p>\n<p>64<\/p>\n<p>65<\/p>\n<p>66<\/p>\n<p>67<\/p>\n<p>68<\/p>\n<p>69<\/p>\n<p>70<\/p>\n<p>71<\/p>\n<p>72<\/p>\n<p>73<\/p>\n<p>74<\/p>\n<p>75<\/p>\n<p>76<\/p>\n<p>77<\/p>\n<p>78<\/p>\n<p>79<\/p>\n<p>80<\/p>\n<p>81<\/p>\n<p>82<\/p>\n<p>83<\/p>\n<p>84<\/p>\n<p>85<\/p>\n<p>86<\/p>\n<p>87<\/p>\n<p>88<\/p>\n<p>89<\/p>\n<p>90<\/p>\n<p>91<\/p>\n<p>92<\/p>\n<p>93<\/p>\n<p>94<\/p>\n<p>95<\/p>\n<p>96<\/p>\n<p>97<\/p>\n<p>98<\/p>\n<p>99<\/p>\n<p>100<\/p>\n<p>101<\/p>\n<p>102<\/p>\n<p>103<\/p>\n<p>104<\/p>\n<p>105<\/p>\n<p>106<\/p>\n<p>107<\/p>\n<p>108<\/p>\n<p>109<\/p>\n<p>110<\/p>\n<p>111<\/p>\n<p>112<\/p>\n<p>113<\/p>\n<p>114<\/p>\n<p>115<\/p>\n<p>116<\/p>\n<p>117<\/p>\n<p>118<\/p>\n<p>119<\/p>\n<p>120<\/p>\n<p>121<\/p>\n<p>122<\/p>\n<p>123<\/p>\n<p>124<\/p>\n<p>125<\/p>\n<p>126<\/p>\n<p>127<\/p>\n<p>128<\/p>\n<p>129<\/p>\n<p>130<\/p>\n<p>131<\/p>\n<p>132<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># three_ways.py<\/span><\/p>\n<p><span class=\"crayon-p\"># The same document Q&amp;A task implemented three ways:<\/span><\/p>\n<p><span class=\"crayon-p\"># Raw OpenAI SDK, LlamaIndex, and LangChain LCEL.<\/span><\/p>\n<p><span class=\"crayon-p\"># Same input. Same output. Different path through the stack.<\/span><\/p>\n<p><span class=\"crayon-p\"># Prerequisites: pip install openai langchain langchain-openai llama-index<\/span><\/p>\n<p><span class=\"crayon-p\">#\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0llama-index-llms-openai llama-index-embeddings-openai python-dotenv<\/span><\/p>\n<p><span class=\"crayon-p\"># How to run: python three_ways.py<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">os<\/span><\/p>\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">time<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">dotenv <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">load_dotenv<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">load_dotenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">QUESTION<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;What is retrieval-augmented generation and why does it matter?&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">CONTEXT_DOC<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">Retrieval-Augmented Generation (RAG) is a technique that improves LLM responses<\/span><\/p>\n<p><span class=\"crayon-s\">by fetching relevant context from an external knowledge base before generating<\/span><\/p>\n<p><span class=\"crayon-s\">an answer. Instead of relying solely on training data, RAG retrieves the most<\/span><\/p>\n<p><span class=\"crayon-s\">relevant document chunks and includes them in the prompt. This reduces<\/span><\/p>\n<p><span class=\"crayon-s\">hallucinations, keeps answers grounded in your actual data, and allows the model<\/span><\/p>\n<p><span class=\"crayon-s\">to answer questions about information it was never trained on.<\/span><\/p>\n<p><span class=\"crayon-s\">&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># APPROACH 1: RAW OPENAI SDK<\/span><\/p>\n<p><span class=\"crayon-p\"># When to use: simple, one-off calls where full visibility matters most<\/span><\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">raw_api_answer<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Answer a question using context, via raw OpenAI SDK &#8212; no framework.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">OpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">client<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">OpenAI<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Everything is explicit: the system prompt, the context injection,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># the message structure. Nothing is hidden in a framework abstraction.<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">response<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">client<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">chat<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">completions<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">create<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">messages<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;role&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;system&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;content&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Answer questions using only the provided context. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;If the answer is not in the context, say so clearly.&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">{<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;role&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;user&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;content&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Context:\\n{context}\\n\\nQuestion: {question}&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">}<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">response<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">choices<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">message<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-i\">content<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># APPROACH 2: LLAMAINDEX<\/span><\/p>\n<p><span class=\"crayon-p\"># When to use: document-heavy retrieval where you want optimized RAG out of the box<\/span><\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">llamaindex_answer<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Answer a question using LlamaIndex &#8212; purpose-built retrieval pipeline.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">llama_index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">core <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-v\">VectorStoreIndex<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">Document<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">Settings<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">llama_index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">llms<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">OpenAI <\/span><span class=\"crayon-st\">as<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">LlamaOpenAI<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">llama_index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">embeddings<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-i\">OpenAIEmbedding<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Configure once &#8212; all pipeline components pick it up<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">Settings<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">LlamaOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">Settings<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">embed_model<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">OpenAIEmbedding<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;text-embedding-3-small&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># from_documents() = chunk + embed + index in one call<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># For multiple documents, pass a list: from_documents([doc1, doc2, doc3])<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">VectorStoreIndex<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">from_documents<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-e\">Document<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># as_query_engine() = retriever + generator, wired together automatically<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">query_engine<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">index<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">as_query_engine<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">similarity_top_k<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">1<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">str<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">query_engine<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># APPROACH 3: LANGCHAIN LCEL<\/span><\/p>\n<p><span class=\"crayon-p\"># When to use: workflows that will grow to include agents, memory, or routing<\/span><\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">langchain_answer<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">-&gt;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">str<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;Answer a question using a LangChain LCEL chain.&#8221;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">prompts <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatPromptTemplate<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">langchain_core<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">output_parsers <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">StrOutputParser<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">langchain_openai <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ChatOpenAI<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">model<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;gpt-4o&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">temperature<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">api_key<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">os<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">getenv<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;OPENAI_API_KEY&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">prompt<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">ChatPromptTemplate<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">from_messages<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;system&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/span><span class=\"crayon-s\">&#8220;Answer using only the provided context. &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/span><span class=\"crayon-s\">&#8220;If the answer is not in the context, say so.\\n\\nContext:\\n{context}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;human&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;{question}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># The same chain supports .stream(), .batch(), .ainvoke() &#8212; no code changes needed<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">chain<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">prompt<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">|<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">llm<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">|<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">StrOutputParser<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">chain<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">invoke<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">{<\/span><span class=\"crayon-s\">&#8220;context&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">context<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;question&#8221;<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">question<\/span><span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-p\"># RUN ALL THREE AND COMPARE<\/span><\/p>\n<p><span class=\"crayon-p\"># \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/span><\/p>\n<p><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">__name__<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">==<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;__main__&#8221;<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">approaches<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;Raw OpenAI SDK&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">raw_api_answer<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;LlamaIndex&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0 <\/span><span class=\"crayon-v\">llamaindex_answer<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;LangChain LCEL&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">langchain_answer<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">name<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">fn <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">approaches<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;\\n{&#8216;=&#8217;*60}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Approach: {name}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;{&#8216;=&#8217;*60}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">start<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">time<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">perf_counter<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">answer<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">fn<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">QUESTION<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">CONTEXT_DOC<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">elapsed<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">time<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">perf_counter<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">start<\/span><\/p>\n<p><span class=\"crayon-e\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Answer: {answer}&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-e\">print<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;Time (excluding LLM): visible in wall clock&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p><strong>What this does:<\/strong> All three functions receive the same <code>QUESTION<\/code> and <code>CONTEXT_DOC<\/code> and return a string answer. The raw API version manually constructs the message list and extracts the response. The LlamaIndex version uses <code>from_documents()<\/code> and <code>as_query_engine()<\/code> to handle the pipeline. The LangChain version assembles a prompt, model, and parser with the <code>|<\/code> operator. At this scale \u2014 one document, one question \u2014 the differences are minimal. Feed this function 500 documents and a complex query, and the gap between LlamaIndex\u2019s purpose-built retrieval and the other two approaches opens up significantly.<\/p>\n<h2>Wrapping Up<\/h2>\n<p>The framework decision is not about which option has the most GitHub stars or the most features. It is about matching the abstraction level of your tool to the actual complexity of your problem.<\/p>\n<p>For simple, one-shot tasks, raw API calls are faster to write, faster to run, and easier to debug than any framework. For document retrieval at any meaningful scale, LlamaIndex earns its dependency through better chunking, faster indexing, and less code. For stateful agents with memory, tools, and multi-step reasoning, LangGraph\u2019s persistence and graph-based control flow are genuinely hard to replicate cleanly with a hand-rolled loop.<\/p>\n<p>The pattern that <a href=\"https:\/\/callsphere.ai\/blog\/llm-orchestration-langchain-llamaindex-comparison\" target=\"_blank\">most production teams converge on by mid-2026<\/a> is not a single framework but a layered stack: raw SDK for the simple calls, LlamaIndex for the retrieval layer, LangGraph for the agent loop, and LangSmith for tracing across everything. None of those choices locks you out of the others. They compose.<\/p>\n<p>The practical rule is this: start with the minimal option that handles your current requirements, and add a framework when you hit a problem the framework was built to solve \u2014 not before. A retrieval problem you encounter is a reason to add LlamaIndex. A state management problem you encounter is a reason to add LangGraph. Adding either before you feel the pain they address means adding maintenance overhead for a future problem that may not arrive in the shape you expected.<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this article, you will learn how LangChain, LlamaIndex, and raw API calls each solve a different layer of the LLM application stack, and how to choose among them based on what your project actually requires. Topics we will cover include: What each option is designed to do, stated plainly without marketing spin. How the<\/p>\n","protected":false},"author":1,"featured_media":180590,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[],"class_list":{"0":"post-180589","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-ai"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls - Ktromedia<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ktromedia.com\/?p=180589\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls - Ktromedia\" \/>\n<meta property=\"og:description\" content=\"In this article, you will learn how LangChain, LlamaIndex, and raw API calls each solve a different layer of the LLM application stack, and how to choose among them based on what your project actually requires. Topics we will cover include: What each option is designed to do, stated plainly without marketing spin. How the\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ktromedia.com\/?p=180589\" \/>\n<meta property=\"og:site_name\" content=\"Ktromedia\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/KTROMedia\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-11T18:42:05+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"680\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"KTRO TEAM\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"KTRO TEAM\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"33 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ktromedia.com\/?p=180589#article\",\"isPartOf\":{\"@id\":\"https:\/\/ktromedia.com\/?p=180589\"},\"author\":{\"name\":\"KTRO TEAM\",\"@id\":\"https:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b\"},\"headline\":\"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls\",\"datePublished\":\"2026-07-11T18:42:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ktromedia.com\/?p=180589\"},\"wordCount\":6609,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ktromedia.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/ktromedia.com\/?p=180589#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png\",\"articleSection\":[\"\u4eba\u5de5\u667a\u80fd\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ktromedia.com\/?p=180589#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ktromedia.com\/?p=180589\",\"url\":\"https:\/\/ktromedia.com\/?p=180589\",\"name\":\"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls - Ktromedia\",\"isPartOf\":{\"@id\":\"https:\/\/ktromedia.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ktromedia.com\/?p=180589#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ktromedia.com\/?p=180589#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png\",\"datePublished\":\"2026-07-11T18:42:05+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ktromedia.com\/?p=180589#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ktromedia.com\/?p=180589\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ktromedia.com\/?p=180589#primaryimage\",\"url\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png\",\"contentUrl\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png\",\"width\":1024,\"height\":680},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ktromedia.com\/?p=180589#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ktromedia.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ktromedia.com\/#website\",\"url\":\"https:\/\/ktromedia.com\/\",\"name\":\"Ktromedia\",\"description\":\"KTRO MEDIA Crypto News\",\"publisher\":{\"@id\":\"https:\/\/ktromedia.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ktromedia.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ktromedia.com\/#organization\",\"name\":\"Ktromedia\",\"url\":\"https:\/\/ktromedia.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ktromedia.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png\",\"contentUrl\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png\",\"width\":250,\"height\":250,\"caption\":\"Ktromedia\"},\"image\":{\"@id\":\"https:\/\/ktromedia.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/KTROMedia\/\",\"https:\/\/www.linkedin.com\/company\/ktro-media\/\",\"https:\/\/t.me\/ktrogroup\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b\",\"name\":\"KTRO TEAM\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ktromedia.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png\",\"contentUrl\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png\",\"caption\":\"KTRO TEAM\"},\"description\":\"KTRO MEDIA \u662f\u4e00\u5bb6\u5168\u7403\u6027\u7684\u534e\u6587WEB3\u5a92\u4f53\u516c\u53f8\u3002\u6211\u4eec\u81f4\u529b\u4e8e\u4e3a\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u9886\u57df\u63d0\u4f9b\u6700\u65b0\u7684\u65b0\u95fb\u3001\u89c1\u89e3\u548c\u8d8b\u52bf\u5206\u6790\u3002\u6211\u4eec\u7684\u5b97\u65e8\u662f\u4e3a\u5168\u7403\u7528\u6237\u63d0\u4f9b\u9ad8\u8d28\u91cf\u3001\u5168\u9762\u7684\u8d44\u8baf\u670d\u52a1\uff0c\u8ba9\u4ed6\u4eec\u66f4\u597d\u5730\u4e86\u89e3\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6700\u65b0\u52a8\u6001\u3002\u6211\u4eec\u4e5f\u5e0c\u671b\u80fd\u5e2e\u5230\u66f4\u591a\u4f18\u79c0\u7684WEB3\u4ea7\u54c1\u627e\u5230\u66f4\u591a\u66f4\u597d\u7684\u8d44\u6e90\u597d\u8ba9\u8fd9\u9886\u57df\u53d8\u5f97\u66f4\u6210\u719f\u3002 \u6211\u4eec\u7684\u62a5\u9053\u8303\u56f4\u6db5\u76d6\u4e86\u533a\u5757\u94fe\u3001\u52a0\u5bc6\u8d27\u5e01\u3001\u667a\u80fd\u5408\u7ea6\u3001DeFi\u3001NFT \u548c Web3 \u751f\u6001\u7cfb\u7edf\u7b49\u9886\u57df\u3002\u6211\u4eec\u7684\u62a5\u9053\u4e0d\u4ec5\u6765\u81ea\u884c\u4e1a\u5185\u7684\u4e13\u5bb6\uff0c\u5148\u950b\u8005\u4e5f\u5305\u62ec\u4e86\u6211\u4eec\u81ea\u5df1\u7684\u5206\u6790\u548c\u89c2\u70b9\u3002\u6211\u4eec\u5728\u5404\u4e2a\u56fd\u5bb6\u548c\u5730\u533a\u90fd\u8bbe\u6709\u56e2\u961f\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u672c\u5730\u5316\u7684\u62a5\u9053\u548c\u5206\u6790\u3002 \u9664\u4e86\u65b0\u95fb\u62a5\u9053\uff0c\u6211\u4eec\u8fd8\u63d0\u4f9b\u5e02\u573a\u7814\u7a76\u548c\u54a8\u8be2\u670d\u52a1\u3002\u6211\u4eec\u7684\u4e13\u4e1a\u56e2\u961f\u53ef\u4ee5\u4e3a\u60a8\u63d0\u4f9b\u6709\u5173\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6df1\u5165\u5206\u6790\u548c\u5e02\u573a\u8d8b\u52bf\uff0c\u5e2e\u52a9\u60a8\u505a\u51fa\u66f4\u660e\u667a\u7684\u6295\u8d44\u51b3\u7b56\u3002 \u6211\u4eec\u7684\u4f7f\u547d\u662f\u6210\u4e3a\u5168\u7403\u534e\u6587\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u6700\u53d7\u4fe1\u8d56\u7684\u4fe1\u606f\u6765\u6e90\u4e4b\u4e00\u3002\u6211\u4eec\u5c06\u7ee7\u7eed\u4e0d\u65ad\u52aa\u529b\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u6700\u65b0\u3001\u6700\u5168\u9762\u3001\u6700\u53ef\u9760\u7684\u4fe1\u606f\u670d\u52a1\u3002\",\"sameAs\":[\"https:\/\/ktromedia.com\"],\"url\":\"https:\/\/ktromedia.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls - Ktromedia","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ktromedia.com\/?p=180589","og_locale":"en_US","og_type":"article","og_title":"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls - Ktromedia","og_description":"In this article, you will learn how LangChain, LlamaIndex, and raw API calls each solve a different layer of the LLM application stack, and how to choose among them based on what your project actually requires. Topics we will cover include: What each option is designed to do, stated plainly without marketing spin. How the","og_url":"https:\/\/ktromedia.com\/?p=180589","og_site_name":"Ktromedia","article_publisher":"https:\/\/www.facebook.com\/KTROMedia\/","article_published_time":"2026-07-11T18:42:05+00:00","og_image":[{"width":1024,"height":680,"url":"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png","type":"image\/png"}],"author":"KTRO TEAM","twitter_card":"summary_large_image","twitter_misc":{"Written by":"KTRO TEAM","Est. reading time":"33 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ktromedia.com\/?p=180589#article","isPartOf":{"@id":"https:\/\/ktromedia.com\/?p=180589"},"author":{"name":"KTRO TEAM","@id":"https:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b"},"headline":"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls","datePublished":"2026-07-11T18:42:05+00:00","mainEntityOfPage":{"@id":"https:\/\/ktromedia.com\/?p=180589"},"wordCount":6609,"commentCount":0,"publisher":{"@id":"https:\/\/ktromedia.com\/#organization"},"image":{"@id":"https:\/\/ktromedia.com\/?p=180589#primaryimage"},"thumbnailUrl":"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png","articleSection":["\u4eba\u5de5\u667a\u80fd"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ktromedia.com\/?p=180589#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ktromedia.com\/?p=180589","url":"https:\/\/ktromedia.com\/?p=180589","name":"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls - Ktromedia","isPartOf":{"@id":"https:\/\/ktromedia.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ktromedia.com\/?p=180589#primaryimage"},"image":{"@id":"https:\/\/ktromedia.com\/?p=180589#primaryimage"},"thumbnailUrl":"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png","datePublished":"2026-07-11T18:42:05+00:00","breadcrumb":{"@id":"https:\/\/ktromedia.com\/?p=180589#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ktromedia.com\/?p=180589"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ktromedia.com\/?p=180589#primaryimage","url":"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png","contentUrl":"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/07\/1783795323_LLM-Orchestration-Frameworks-Compared-LangChain-vs-LlamaIndex-vs-Raw-API.png","width":1024,"height":680},{"@type":"BreadcrumbList","@id":"https:\/\/ktromedia.com\/?p=180589#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ktromedia.com\/"},{"@type":"ListItem","position":2,"name":"LLM Orchestration Frameworks Compared: LangChain vs. LlamaIndex vs. Raw API Calls"}]},{"@type":"WebSite","@id":"https:\/\/ktromedia.com\/#website","url":"https:\/\/ktromedia.com\/","name":"Ktromedia","description":"KTRO MEDIA Crypto News","publisher":{"@id":"https:\/\/ktromedia.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ktromedia.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ktromedia.com\/#organization","name":"Ktromedia","url":"https:\/\/ktromedia.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ktromedia.com\/#\/schema\/logo\/image\/","url":"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png","contentUrl":"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png","width":250,"height":250,"caption":"Ktromedia"},"image":{"@id":"https:\/\/ktromedia.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/KTROMedia\/","https:\/\/www.linkedin.com\/company\/ktro-media\/","https:\/\/t.me\/ktrogroup"]},{"@type":"Person","@id":"https:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b","name":"KTRO TEAM","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ktromedia.com\/#\/schema\/person\/image\/","url":"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png","contentUrl":"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png","caption":"KTRO TEAM"},"description":"KTRO MEDIA \u662f\u4e00\u5bb6\u5168\u7403\u6027\u7684\u534e\u6587WEB3\u5a92\u4f53\u516c\u53f8\u3002\u6211\u4eec\u81f4\u529b\u4e8e\u4e3a\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u9886\u57df\u63d0\u4f9b\u6700\u65b0\u7684\u65b0\u95fb\u3001\u89c1\u89e3\u548c\u8d8b\u52bf\u5206\u6790\u3002\u6211\u4eec\u7684\u5b97\u65e8\u662f\u4e3a\u5168\u7403\u7528\u6237\u63d0\u4f9b\u9ad8\u8d28\u91cf\u3001\u5168\u9762\u7684\u8d44\u8baf\u670d\u52a1\uff0c\u8ba9\u4ed6\u4eec\u66f4\u597d\u5730\u4e86\u89e3\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6700\u65b0\u52a8\u6001\u3002\u6211\u4eec\u4e5f\u5e0c\u671b\u80fd\u5e2e\u5230\u66f4\u591a\u4f18\u79c0\u7684WEB3\u4ea7\u54c1\u627e\u5230\u66f4\u591a\u66f4\u597d\u7684\u8d44\u6e90\u597d\u8ba9\u8fd9\u9886\u57df\u53d8\u5f97\u66f4\u6210\u719f\u3002 \u6211\u4eec\u7684\u62a5\u9053\u8303\u56f4\u6db5\u76d6\u4e86\u533a\u5757\u94fe\u3001\u52a0\u5bc6\u8d27\u5e01\u3001\u667a\u80fd\u5408\u7ea6\u3001DeFi\u3001NFT \u548c Web3 \u751f\u6001\u7cfb\u7edf\u7b49\u9886\u57df\u3002\u6211\u4eec\u7684\u62a5\u9053\u4e0d\u4ec5\u6765\u81ea\u884c\u4e1a\u5185\u7684\u4e13\u5bb6\uff0c\u5148\u950b\u8005\u4e5f\u5305\u62ec\u4e86\u6211\u4eec\u81ea\u5df1\u7684\u5206\u6790\u548c\u89c2\u70b9\u3002\u6211\u4eec\u5728\u5404\u4e2a\u56fd\u5bb6\u548c\u5730\u533a\u90fd\u8bbe\u6709\u56e2\u961f\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u672c\u5730\u5316\u7684\u62a5\u9053\u548c\u5206\u6790\u3002 \u9664\u4e86\u65b0\u95fb\u62a5\u9053\uff0c\u6211\u4eec\u8fd8\u63d0\u4f9b\u5e02\u573a\u7814\u7a76\u548c\u54a8\u8be2\u670d\u52a1\u3002\u6211\u4eec\u7684\u4e13\u4e1a\u56e2\u961f\u53ef\u4ee5\u4e3a\u60a8\u63d0\u4f9b\u6709\u5173\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6df1\u5165\u5206\u6790\u548c\u5e02\u573a\u8d8b\u52bf\uff0c\u5e2e\u52a9\u60a8\u505a\u51fa\u66f4\u660e\u667a\u7684\u6295\u8d44\u51b3\u7b56\u3002 \u6211\u4eec\u7684\u4f7f\u547d\u662f\u6210\u4e3a\u5168\u7403\u534e\u6587\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u6700\u53d7\u4fe1\u8d56\u7684\u4fe1\u606f\u6765\u6e90\u4e4b\u4e00\u3002\u6211\u4eec\u5c06\u7ee7\u7eed\u4e0d\u65ad\u52aa\u529b\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u6700\u65b0\u3001\u6700\u5168\u9762\u3001\u6700\u53ef\u9760\u7684\u4fe1\u606f\u670d\u52a1\u3002","sameAs":["https:\/\/ktromedia.com"],"url":"https:\/\/ktromedia.com\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts\/180589","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=180589"}],"version-history":[{"count":1,"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts\/180589\/revisions"}],"predecessor-version":[{"id":180591,"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts\/180589\/revisions\/180591"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/media\/180590"}],"wp:attachment":[{"href":"https:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=180589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=180589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=180589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}