---
title: "14 — Agent Frameworks: LangChain, LlamaIndex, and the Build-It-Yourself Path"
uid: agent-frameworks
tags: ["frameworks", "ragflow", "llamaindex", "haystack", "agent-sdk", "roadmap:ai-engineer", "langchain"]
excerpt: "LangChain isn't doing anything you couldn't wire yourself — it's an opinionated assembly of the same primitives. The real question: how much assembly do you want to hand off?"
date: 2026-08-13T03:28:36+0000
source: https://www.aveshina.my.id/en/blog/agent-frameworks
---

"Just use LangChain" was my reflex for any agent or RAG project, and it worked until I couldn't tell what the framework was actually doing for me. The frame that fixed that: **frameworks are an opinionated assembly of the same primitives — chunking, embeddings, retrieval, generation, the agent loop — and the alternative, wiring SDKs directly, is more reachable than it sounds.** [1][2] The framework isn't doing something I couldn't do myself; it's doing many small things I'd rather not rewrite, with opinions baked in about how they fit together. Knowing that changes the decision from "which framework" to "how much of the assembly do I want to hand off."

The framing that finally landed is a build-versus-assemble spectrum. At one end, a framework like LangChain or LlamaIndex gives me a complete, opinionated pipeline out of the box. At the other end, I wire the individual SDKs together myself — a text splitter here, an embedding API there, a vector database SDK, a generator call. The middle ground is a lighter framework or a manual implementation of just the pieces I need. Where I land depends on how standard my problem is and how much control I want.

```figure
<svg viewBox="0 0 740 260" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A spectrum from monolithic framework to hand-wired SDKs. Left: a single big box labelled Framework (LangChain / LlamaIndex). Middle: a marker labelled 'manual implementation'. Right: separate SDK chips — text splitter, embeddings, vector DB, generator — connected by hand-drawn lines.">
  <defs>
    <marker id="fwarrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- spectrum line -->
    <line x1="40" y1="130" x2="700" y2="130" stroke="#94a3b8" stroke-width="1.5"/>
    <text x="40" y="40" font-size="11" font-weight="700" fill="#1e1b4b">Opinionated framework</text>
    <text x="700" y="40" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="end">Hand-wired SDKs</text>

    <!-- left: monolithic -->
    <rect x="40" y="60" width="180" height="140" rx="10" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="130" y="84" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">LangChain / LlamaIndex</text>
    <g fill="#c7d2fe" stroke="#6366f1">
      <rect x="56" y="100" width="148" height="20" rx="3"/>
      <rect x="56" y="124" width="148" height="20" rx="3"/>
      <rect x="56" y="148" width="148" height="20" rx="3"/>
      <rect x="56" y="172" width="148" height="20" rx="3"/>
    </g>
    <text x="130" y="220" font-size="9.5" font-style="italic" fill="#1e1b4b" text-anchor="middle">primitives bundled + opinions</text>

    <!-- middle marker -->
    <rect x="320" y="80" width="100" height="100" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5" stroke-dasharray="5 3"/>
    <text x="370" y="124" font-size="10.5" font-weight="700" fill="#422006" text-anchor="middle">manual</text>
    <text x="370" y="140" font-size="10.5" font-weight="700" fill="#422006" text-anchor="middle">implementation</text>
    <text x="370" y="156" font-size="9.5" fill="#422006" text-anchor="middle">(light framework</text>
    <text x="370" y="168" font-size="9.5" fill="#422006" text-anchor="middle">or hand-built)</text>

    <!-- right: SDKs -->
    <g font-size="9.5" fill="#052e16" text-anchor="middle">
      <rect x="500" y="64" width="92" height="28" rx="5" fill="#dcfce7" stroke="#16a34a"/><text x="546" y="82">text splitter</text>
      <rect x="608" y="64" width="92" height="28" rx="5" fill="#dcfce7" stroke="#16a34a"/><text x="654" y="82">embeddings</text>
      <rect x="500" y="118" width="92" height="28" rx="5" fill="#dcfce7" stroke="#16a34a"/><text x="546" y="136">vector DB SDK</text>
      <rect x="608" y="118" width="92" height="28" rx="5" fill="#dcfce7" stroke="#16a34a"/><text x="654" y="136">generator call</text>
    </g>
    <line x1="546" y1="92" x2="546" y2="118" stroke="#15803d" stroke-width="1" stroke-dasharray="3 2"/>
    <line x1="654" y1="92" x2="654" y2="118" stroke="#15803d" stroke-width="1" stroke-dasharray="3 2"/>
    <line x1="592" y1="132" x2="608" y2="132" stroke="#15803d" stroke-width="1" stroke-dasharray="3 2"/>
    <text x="600" y="180" font-size="9.5" font-style="italic" fill="#052e16" text-anchor="middle">you wire the pieces</text>

    <!-- arrows -->
    <line x1="222" y1="130" x2="318" y2="130" stroke="#64748b" stroke-width="1.3" marker-end="url(#fwarrow)"/>
    <line x1="422" y1="130" x2="498" y2="130" stroke="#64748b" stroke-width="1.3" marker-end="url(#fwarrow)"/>
    <text x="370" y="240" font-size="10" font-style="italic" fill="#475569" text-anchor="middle">the same primitives, assembled with more or less help</text>
  </g>
</svg>
```

## LangChain: the general-purpose orchestrator

**LangChain** is the broadest of the frameworks — a development toolkit for building applications powered by language models, focused on creating _chains_, sequences of operations where the model interacts with databases, APIs, and other models to perform complex tasks [1]. A chain might be "retrieve documents, stuff them into a prompt, call the model, parse the structured output," and LangChain provides the abstractions to compose those steps. It also extends naturally into agents: the same composition primitives become the agent loop when the model is allowed to decide which step runs next.

The value is the breadth — there's an integration for almost everything — and the cost is the abstraction weight. LangChain's chains hide a lot of machinery, which speeds up the happy path and slows down debugging when something goes wrong inside a chain. For standard RAG and agent patterns it's excellent; for unusual requirements the abstractions can fight back.

## LlamaIndex: data-first RAG

**LlamaIndex** is the data-framework counterpart, built specifically to connect LLMs to structured and unstructured data sources [2]. Where LangChain started from the orchestration side, LlamaIndex started from the _indexing_ side — its strength is ingesting documents, databases, and APIs, indexing them for retrieval, and exposing them to an LLM through clean query interfaces. For a feature whose center of gravity is "I have a lot of data and I need an LLM to query it," LlamaIndex's data connectors and index abstractions are often a better fit than LangChain's generic chains.

In practice the two overlap heavily and both can do RAG and agents. The choice often comes down to which abstraction feels closer to the problem: chains and tools (LangChain) or documents and indices (LlamaIndex).

## Haystack, RAGFlow, and the focused alternatives

Not every problem needs a general framework. **Haystack** is an open-source Python framework focused on search and question-answering pipelines — connect data sources, pick a model, set up pipelines that retrieve passages, run the model, and rank results, with backends like Elasticsearch, OpenSearch, FAISS, and Pinecone [3]. **RAGFlow** is a newer framework dedicated specifically to streamlining RAG pipeline creation, evaluation, and deployment, with modular components for data loaders, retrievers, and generators [4]. These focused tools are often a better fit than a general framework when the problem is squarely within their scope, because they encode fewer opinions about things outside it.

## Using SDKs directly: the build-it-yourself path

Here's the part I underappreciated: I don't have to use a framework at all. The roadmap makes a point of this, and it's worth taking seriously [5]. If I understand the RAG stages — chunk, embed, retrieve, generate — I can assemble them myself from the underlying SDKs:

- **Chunk** with a text-splitter package (e.g. @langchain/textsplitters) — usable independently of the full LangChain framework.
- **Embed** through any embedding API via its SDK (OpenAI, Cohere, a local model).
- **Store and retrieve** via the vector database's own SDK (Supabase, Pinecone, Qdrant).
- **Generate** via the model API.

The payoff of building it yourself is transparency — every step is code I wrote and can debug — and the freedom to pick the best tool for each stage instead of what the framework bundles. The cost is that I'm responsible for the wiring, the error handling, and the retries that a framework would have handled. For a simple RAG pipeline this is genuinely tractable; for a complex multi-agent system it's a lot of glue code.

## Agent SDKs: the model vendors' offerings

A separate category is the agent SDKs from the model vendors themselves. **OpenAI's AgentKit / Agents SDK** provides tools and abstractions for managing agent state, behavior, and tool connections [6]. **Anthropic's Claude Agent SDK** offers pre-built components for planning, tool usage, memory management, and human interaction built around Claude [7]. **Google's Agent Development Kit (ADK)** streamlines agent development with orchestration, tool integration, and evaluation [8]. And **Vertex AI** is Google Cloud's broader platform for building, training, and deploying ML models — including agent-building tools on managed infrastructure [9].

These vendor SDKs differ from the framework-agnostic ones (LangChain, LlamaIndex) in that they're shaped around a specific model family's strengths. The tradeoff is tighter integration with that vendor's features (prompt caching, extended thinking, specific tool formats) against portability across vendors. I reach for them when I've committed to a vendor and want the best-supported path; I reach for the agnostic frameworks when portability matters more.

## Manual implementation: maximum control

The far end of the spectrum is **manual implementation** — building the agent logic from scratch, without relying on a framework's abstractions [10]. This is the most work but gives maximum control and customization. OpenAI's "practical guide to building agents" and Anthropic's docs on custom subagents both acknowledge this path exists and is legitimate for sophisticated use cases. The honest assessment: manual implementation earns its place when the framework's abstractions are actively getting in the way — when I need a control flow or a tool-calling pattern the framework doesn't support cleanly. For most features, it's more work than justified.

## How I use this

My rule is to match the framework choice to the problem's shape and my need for control. For a standard RAG feature, I'll use LlamaIndex or LangChain to move fast, because the patterns are well-trodden and the abstractions fit. For a feature with unusual retrieval or agent requirements, I'll wire the SDKs directly so I can debug every stage and pick the best tool per step — the transparency is worth the glue code. For vendor-specific features where I've committed to a provider, I'll consider that vendor's agent SDK for the integration quality. And I treat the choice as reversible: starting with a framework to learn the shape of the problem, then dropping to direct SDKs once I understand which abstractions were helping and which were hurting, is a perfectly reasonable path. The framework is a productivity tool, not a commitment — and knowing the primitives underneath is what lets me move between them.

## References

[1] LangChain, 2024. [Online]. Available: [https://www.langchain.com/](https://www.langchain.com/)

[2] LlamaIndex, "Documentation," 2024. [Online]. Available: [https://docs.llamaindex.ai/en/stable/](https://docs.llamaindex.ai/en/stable/)

[3] deepset, "Haystack," 2024. [Online]. Available: [https://haystack.deepset.ai/](https://haystack.deepset.ai/)

[4] RAGFlow, 2024. [Online]. Available: [https://ragflow.io/](https://ragflow.io/)

[5] npm, "@langchain/textsplitters," 2024. [Online]. Available: [https://www.npmjs.com/package/@langchain/textsplitters](https://www.npmjs.com/package/@langchain/textsplitters)

[6] OpenAI, "Introducing AgentKit," 2024. [Online]. Available: [https://openai.com/index/introducing-agentkit/](https://openai.com/index/introducing-agentkit/)

[7] Anthropic, "Agent SDK overview," 2024. [Online]. Available: [https://platform.claude.com/docs/en/agent-sdk/overview](https://platform.claude.com/docs/en/agent-sdk/overview)

[8] Google, "Agent Development Kit," 2025. [Online]. Available: [https://google.github.io/adk-docs/](https://google.github.io/adk-docs/)

[9] Google Cloud, "Vertex AI," 2024. [Online]. Available: [https://cloud.google.com/generative-ai-studio](https://cloud.google.com/generative-ai-studio)

[10] OpenAI, "A practical guide to building agents," 2024. [Online]. Available: [https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf](https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf)

```quiz
Q: Frameworks like LangChain and LlamaIndex are best understood as…
- a new kind of AI model
- an opinionated assembly of the same primitives — chunk, embed, retrieve, generate, the agent loop
correct: 1
explain: Frameworks don't do something you couldn't do yourself. They bundle the standard steps with opinions about how they fit together, saving you from rewriting glue code.

Q: LlamaIndex's distinguishing strength versus LangChain is…
- it only works with closed models
- it started from the data side — ingesting, indexing, and exposing documents to LLMs
correct: 1
explain: LlamaIndex's center of gravity is data connectors and indices. LangChain's is chains and orchestration. Both can do RAG; their abstractions differ.

Q: "Using SDKs directly" instead of a framework is…
- impossible without rewriting the model
- assembling the stages yourself from individual SDKs (text splitter, embedding API, vector DB, generator) — more transparent, more glue code
correct: 1
explain: Each stage has an SDK you can call directly. You trade framework convenience for transparency and per-stage tool choice.

Q: A vendor-specific agent SDK (OpenAI AgentKit, Claude Agent SDK, Google ADK) trades…
- portability across vendors for tighter integration with one vendor's features
- cost for quality
correct: 0
explain: Vendor SDKs are shaped around one model family's strengths (caching, tool formats, reasoning modes). You gain integration quality and lose cross-vendor portability.

Q: When is manual implementation of an agent from scratch justified?
- always; frameworks are bad
- when the framework's abstractions are actively getting in the way of a control flow or tool pattern you need
correct: 1
explain: Manual implementation is the most work. It earns its place only when frameworks obstruct a specific requirement. For most features, a framework or direct SDKs are simpler.
```
