Retrieval & RAG Bridge
|
This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
This page was written against langchain 1.4.3, langchain-core 1.6.5, langchain-classic 1.0.8 and langgraph
1.2.12 on Python 3.12, as verified on 2026-09-29. The running example is DocsAssistant, backed by the pgvector
store built in Integrating with LangChain with Ollama’s
nomic-embed-text embeddings and llama3.1:8b chat model. Ollama was not available while the snippets were
checked, so every output shown here was produced against the real packages with a scripted fake chat model and a
small in-memory stub (a keyword-based embedding class over InMemoryVectorStore) in place of pgvector; no output
comes from a live model.
This is a bridge page, deliberately short. The vector-store, splitter, retriever and RAG-chain surface of LangChain already has a canonical home in the Vector Databases & RAG section and is linked here rather than repeated. What this page adds is the LangChain-side view: how the building blocks map onto the framework’s packages, which RAG architecture to reach for, and how retrieval plugs into the agents described in Agents with create_agent.
The building blocks
Retrieval in LangChain is a pipeline of interchangeable parts. The official Retrieval page lists five: document loaders, text splitters, embedding models, vector stores and retrievers. Each is an interface with many provider implementations, so any of them can be swapped without touching the rest of the application.
Loaders, splitters, embeddings and vector stores return or consume Document objects from langchain-core;
splitters ship in the separate langchain-text-splitters package; provider integrations such as langchain-ollama
or langchain-postgres sit beside them (see Ecosystem & Packages).
The retrieval techniques that v0 kept in langchain.retrievers moved to langchain-classic in 1.x. Nothing in the
diagram is specific to agents: the last box, where retrieved text meets the model, is the design decision the rest
of this page is about.
The table sends each block to the section that documents it in depth.
| Block | Canonical page | Anchor |
|---|---|---|
Embeddings |
|
|
Vector stores |
|
|
Splitters |
|
|
Retrievers |
|
|
RAG chain |
|
|
Agentic RAG and memory |
|
|
LangChain4j equivalents |
|
Chunking choices are in Document Ingestion &
Chunking, search strategies in Retrieval Strategies and
Hybrid Search & Re-ranking, and the vector databases
themselves in PostgreSQL & pgvector,
Qdrant RAG Integration Patterns (which uses
langchain-qdrant) and Neo4j Vector Search & GenAI.
Choosing an architecture
The official page names three RAG architectures. They differ in who decides to retrieve.
| Architecture | How it is built | Trade-off |
|---|---|---|
Two-step RAG |
Retrieval always runs before generation. In LangChain 1.x, a chain (see LCEL & Runnables) or an agent whose middleware injects the retrieved text. |
Predictable latency and cost, at most one retrieval; cannot skip retrieval or retry it. |
Agentic RAG |
The model gets a retrieval tool and decides when, and how often, to call it ( |
Flexible, handles follow-ups and multi-hop questions; variable latency, and the model may skip retrieval. |
Hybrid RAG |
Adds query rewriting, retrieval grading and answer validation around retrieval, usually as a LangGraph graph (see LangGraph Workflows). |
Most control over quality; most moving parts. |
The concepts behind the agentic and hybrid variants (grading, rewriting, self-correction) are in Agentic RAG and in Workflow Patterns. The next two sections show the two agent-side patterns in LangChain 1.x. Both use the stand-in store described in the lead paragraph.
from langchain_core.documents import Document
from langchain_core.embeddings import Embeddings
from langchain_core.vectorstores import InMemoryVectorStore
VOCAB = ["hnsw", "ef_search", "recall", "latency", "cosine", "operator", "distance"]
class KeywordEmbeddings(Embeddings):
"""Stand-in for nomic-embed-text: one dimension per keyword."""
def embed_documents(self, texts):
return [[float(w in t.lower()) for w in VOCAB] for t in texts]
def embed_query(self, text):
return self.embed_documents([text])[0]
# stand-in for DocsAssistant's pgvector store
vector_store = InMemoryVectorStore(KeywordEmbeddings())
vector_store.add_documents([
Document(page_content="HNSW ef_search trades recall for latency.",
metadata={"source_path": "ann-indexes.adoc", "section": "HNSW"}),
Document(page_content="The <=> operator computes cosine distance.",
metadata={"source_path": "postgresql-pgvector.adoc", "section": "Operators"}),
])
retriever = vector_store.as_retriever(search_kwargs={"k": 1})
In production, vector_store is the pgvector store from the linked page and retriever comes from the same
as_retriever call; nothing below depends on the stub. See
LangChain — Vector store integrations.
RAG as a tool
The vector-rag page already shows an agentic-RAG agent with a hand-written @tool that calls the store. The
alternative, when the whole tool is just "run this retriever", is create_retriever_tool from
langchain_core.tools. It wraps any retriever as a tool with a single query argument, joins the returned
documents into the text the model sees, and with response_format="content_and_artifact" keeps the Document
objects on the side as the tool message’s artifact (see Tools for how artifacts work),
which is what a citation feature needs.
from langchain.agents import create_agent
from langchain_core.tools import create_retriever_tool
docs_tool = create_retriever_tool(
retriever,
name="search_docs",
description="Search the documentation site. Use it for any question about vector search.",
response_format="content_and_artifact",
)
agent = create_agent(
model=model, # ChatOllama(model="llama3.1:8b") in DocsAssistant
tools=[docs_tool],
system_prompt="Answer from search_docs results only.",
)
result = agent.invoke({"messages": [{"role": "user", "content": "What does ef_search control?"}]})
Run against the scripted fake model, which is told to call search_docs once and then answer, the tool’s schema and
the resulting message trace are:
search_docs {'query': {'description': 'query to look up in retriever', 'title': 'Query', 'type': 'string'}}
HumanMessage | What does ef_search control?
AIMessage |
ToolMessage | HNSW ef_search trades recall for latency.
AIMessage | ef_search trades recall for latency (ann-indexes.adoc).
['ann-indexes.adoc']
The last line is [d.metadata["source_path"] for d in result["messages"][2].artifact]: the model saw only the
text, while the application receives the source paths to render as citations. The tool has no filter or page
arguments; when retrieval needs metadata filters, pagination or concise-versus-detailed responses, write the tool by
hand as described in Tool Design. See
LangChain reference:
create_retriever_tool and LangChain: Agents.
Two-step RAG with a dynamic prompt
When retrieval should always happen, an agent is not needed to decide anything, but a create_agent graph is still
useful for its middleware, memory and streaming. The @dynamic_prompt middleware computes the system prompt from
the request just before each model call, so it can run the retriever on the latest user message and place the
results in the prompt. The agent then has no tools and makes exactly one model call:
from langchain.agents import create_agent
from langchain.agents.middleware import ModelRequest, dynamic_prompt
@dynamic_prompt
def with_context(request: ModelRequest) -> str:
question = request.messages[-1].text
docs = retriever.invoke(question)
context = "\n\n".join(f"[{d.metadata['source_path']}] {d.page_content}" for d in docs)
return f"Answer using only this context:\n\n{context}"
rag = create_agent(model=model, tools=[], middleware=[with_context])
out = rag.invoke({"messages": [{"role": "user", "content": "What does ef_search control?"}]})
Capturing the system message the fake model received shows what the middleware built:
Answer using only this context:
[ann-indexes.adoc] HNSW ef_search trades recall for latency.
The retrieved passages never enter the stored conversation, only the model request, so long chats do not accumulate old context. The cost is that the model cannot ask for a second, better-targeted search; that trade-off is the reason to pick the previous pattern instead. See LangChain: Custom middleware and Middleware for the full hook list.
For corpora too large to keep in one context, Deep Agents (see Multi-Agent & Deep Agents) document a variant that retrieves, offloads the chunks to a filesystem backend and delegates their analysis to subagents; see LangChain: RAG with Deep Agents.
Evaluating and going to production
Whether retrieval actually helps is measured, not assumed: Evaluating RAG Systems defines the retrieval and generation metrics, and Evaluation runs them as LangSmith experiments on DocsAssistant. Conversational RAG with follow-up rewriting, citation formatting, serving RAG over an API, and query transformation or text-to-SQL routing are the subject of RAG Systems in Production (see Conversational RAG & Memory, Citations & Grounding, Serving RAG as an API and Query Understanding & Routing); this page stops at the LangChain building blocks. Conversation state for an agent that uses either pattern above comes from Short-Term Memory & Checkpointers, and vector-backed long-term memory from Long-Term Memory & Stores.