Integrating with LangChain
|
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. |
Examples on this page target LangChain / langchain-core / LangGraph 1.4 / 1.6 / 1.2,
langchain-text-splitters 1.1, langchain-ollama, langchain-postgres 0.0.18, and LangChain4j 1.20.1 against
Ollama 0.34 (nomic-embed-text, 768 dimensions, and llama3.1:8b). The full, standalone LangChain reference — chat models, prompts, LCEL, structured output, tools, agents, LangGraph, LangSmith and LangChain4j — is the
LangChain sub-section of the AI section; this page collects, under one set of stable anchors, only the document-loading, vector-store, retrieval,
RAG-chain and agent-memory surface that the rest of this
Vector Databases & RAG section already relies on and links to by anchor. Every concept below was already
shown, working against the DocsAssistant schema, on an earlier page in this section — this page is the
reference those pages point back to, not a new tutorial.
Embeddings
LangChain’s Embeddings interface abstracts over an embedding provider behind two methods: embed_documents
for a batch of texts and embed_query for a single query string, so retrieval code never depends on which
model produced the vectors.
from langchain_ollama import OllamaEmbeddings
embeddings = OllamaEmbeddings(model="nomic-embed-text")
doc_vectors = embeddings.embed_documents([
"search_document: HNSW builds a multi-layer proximity graph over the vectors.",
"search_document: pgvector adds a vector column type and distance operators to PostgreSQL.",
])
query_vector = embeddings.embed_query("search_query: what does ef_search control?")
See LangChain: Ollama embeddings. A
hosted or self-hosted HuggingFace model plugs into the same interface through
langchain-huggingface’s `HuggingFaceEmbeddings (local sentence-transformers models) or
HuggingFaceEndpointEmbeddings (a remote Inference Endpoint):
from langchain_huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
See LangChain: HuggingFace
embeddings. generating-embeddings-in-practice.adoc walks through OllamaEmbeddings end to end, including
task prefixes and caching; see Generating Embeddings in Practice.
Vector stores
Every LangChain vector store implements the same VectorStore base class, so swapping the underlying engine
is a one-line change to the constructor. The methods used throughout this section are:
| Method | Purpose |
|---|---|
|
Embeds and writes a batch of |
|
Returns the |
|
Same as above, but also returns each result’s raw distance or similarity score. |
|
Diversity-aware retrieval: over-fetches |
|
A store-specific keyword argument (a dict, a SQL-like expression string, or a store’s own filter object) restricting the search to matching metadata, as shown on Metadata Filtering. |
|
Removes documents by id or by a metadata filter. |
Against the DocsAssistant scenario, langchain-postgres’s `PGVector store is the one used throughout this
section. It owns its own schema under the collection name docsassistant — separate tables from the
hand-written documents / chunks / chunk_embeddings schema on
PostgreSQL & pgvector, because each framework manages its
own storage layout rather than reusing another framework’s tables:
from langchain_postgres import PGVector
from langchain_ollama import OllamaEmbeddings
vector_store = PGVector(
embeddings=OllamaEmbeddings(model="nomic-embed-text"),
collection_name="docsassistant",
connection="postgresql+psycopg://docs:docs@localhost:5432/docsassistant",
use_jsonb=True,
)
vector_store.add_documents(chunks, ids=[c.metadata["chunk_id"] for c in chunks])
hits = vector_store.similarity_search_with_score(
"search_query: what does ef_search control?", k=5, filter={"component": {"$eq": "vector-rag"}},
)
See LangChain: PGVector. Three more stores are used elsewhere in this section as lighter-weight or local alternatives:
-
SQLiteVec, wrapping the sqlite-vecvec0table pattern from Vector Search in SQLite; see LangChain: SQLiteVec. -
FAISS, a disposable, single-process store backed by the library covered on FAISS; see LangChain: FAISS vector store. -
InMemoryVectorStore, an ephemeral, dependency-free store useful for tests and small demos that keeps every vector in a Python dict and scores it with brute-force cosine similarity — no server, no persistence:from langchain_core.vectorstores import InMemoryVectorStore from langchain_ollama import OllamaEmbeddings vector_store = InMemoryVectorStore(OllamaEmbeddings(model="nomic-embed-text")) vector_store.add_documents(chunks)
For every other engine this site documents, LangChain ships an equivalent store: Qdrant
(langchain-qdrant’s `QdrantVectorStore, see
Qdrant: RAG Integration Patterns), Redis
(langchain-redis’s `RedisVectorStore, see
Redis: Vector Search), Elasticsearch
(langchain-elasticsearch’s `ElasticsearchStore, see
Elasticsearch: Vector & Semantic Search),
MongoDB (langchain-mongodb’s `MongoDBAtlasVectorSearch, see
MongoDB: Special Indexes & Search), and Neo4j
(langchain-neo4j’s `Neo4jVector, see
Neo4j: Vector Search & GenAI). See
LangChain: vector store integrations for the
full list and each store’s own configuration.
Splitters
A document loader reads source files into LangChain Document objects (page content plus a metadata dict).
For the DocsAssistant sample corpus — docs/vector-rag/hnsw.adoc, docs/vector-rag/pgvector.adoc and
docs/spring-ai/rag.adoc — langchain-community’s `DirectoryLoader globs the AsciiDoc files and reads each
one with TextLoader, which records the file path under the source metadata key; the loop then maps it to the
section’s source_path and component keys:
from langchain_community.document_loaders import DirectoryLoader, TextLoader
loader = DirectoryLoader(
"docs", glob="**/*.adoc", loader_cls=TextLoader, loader_kwargs={"encoding": "utf-8"},
)
documents = loader.load()
for doc in documents:
doc.metadata["source_path"] = doc.metadata["source"]
doc.metadata["component"] = "spring-ai" if "/spring-ai/" in doc.metadata["source"] else "vector-rag"
See
LangChain
reference: DirectoryLoader,
LangChain reference:
TextLoader and LangChain: Document
loaders for the PDF, HTML and other format-specific loaders shown on
Document Ingestion & Chunking.
langchain-text-splitters then turns the loaded documents into the chunks a vector store indexes. The package used
throughout Document Ingestion & Chunking covers
RecursiveCharacterTextSplitter (recursive character/token windows with overlap) and
MarkdownHeaderTextSplitter (structure-aware splitting on heading levels), both shown there against the
DocsAssistant sample corpus:
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(documents)
See LangChain: Recursive character text splitter and LangChain: Markdown header text splitter. Every chunking strategy this section covers — fixed windows, sentence-aware, recursive, structure-aware, semantic, parent-child, late chunking and contextual retrieval — is explained in depth, with the trade-offs between them, on Document Ingestion & Chunking: Chunking strategies.
Retrievers
Every VectorStore exposes a Runnable retriever through as_retriever, so it composes with the rest of a
LangChain pipeline (prompts, chains, agent tools) without the caller touching the store’s own API:
retriever = vector_store.as_retriever(
search_type="mmr",
search_kwargs={"k": 5, "fetch_k": 20, "lambda_mult": 0.5, "filter": {"component": {"$eq": "vector-rag"}}},
)
docs = retriever.invoke("what does ef_search control?")
search_type selects "similarity" (default), "similarity_score_threshold" (drop results below a minimum
score), or "mmr" (the diversity-aware search from Vector stores); search_kwargs forwards the
matching keyword arguments — k, score_threshold, filter, fetch_k and lambda_mult — to the
underlying store method. See LangChain: Retrieval.
Beyond this basic retriever, langchain_classic.retrievers packages the retrieval techniques covered on
Hybrid Search & Re-ranking (EnsembleRetriever,
ContextualCompressionRetriever) and
Retrieval Strategies (MultiQueryRetriever,
ParentDocumentRetriever, SelfQueryRetriever); see
LangChain:
langchain_classic.retrievers.
RAG chain
RAG Fundamentals shows the full pipeline this anchor refers
to: a retriever built with as_retriever, a ChatPromptTemplate that formats the retrieved documents and the
question, and a chat model, composed with LangChain Expression Language’s | operator into one Runnable:
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template(
"Answer using only the context below.\n\nContext:\n{context}\n\nQuestion: {question}"
)
model = ChatOllama(model="llama3.1:8b", temperature=0.1, num_ctx=8192)
def format_docs(docs):
return "\n\n".join(f"[{d.metadata['title']} § {d.metadata['section']}]\n{d.page_content}" for d in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
answer = rag_chain.invoke("what does ef_search control?")
See LangChain: Retrieval for this composition
pattern, and Prompt Augmentation &
Generation for the context-formatting and prompt-template choices behind format_docs and prompt.
Agentic RAG
Agentic RAG covers the shift from a fixed chain to an LLM that
decides whether and what to retrieve. LangChain 1.x’s create_agent builds a tool-calling agent from a chat
model and a list of tools — including a retriever exposed as a tool — in one call:
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
@tool
def search_docs(query: str, component: str | None = None, k: int = 5) -> list[dict]:
"""Search the DocsAssistant documentation for passages relevant to the query."""
filter_ = {"component": {"$eq": component}} if component else None
hits = vector_store.similarity_search_with_score(query, k=k, filter=filter_)
return [{"source_path": d.metadata["source_path"], "section": d.metadata["section"],
"content": d.page_content, "score": score} for d, score in hits]
agent = create_agent(model=ChatOllama(model="llama3.1:8b", temperature=0.1), tools=[search_docs])
result = agent.invoke({"messages": [{"role": "user", "content": "What does ef_search control?"}]})
See LangChain: Agents for `create_agent’s full configuration (system prompts, structured output, middleware). For multi-step agentic RAG — retrieve, grade the retrieved documents, then rewrite the query or generate the answer — LangChain 1.x agents run on a LangGraph graph underneath; building that graph directly gives explicit control over the retrieve/grade/ rewrite/generate nodes that `create_agent’s default loop does not expose. See LangChain: Agentic RAG (LangGraph).
Memory for such an agent comes from LangGraph: a checkpointer persists each thread’s conversation state
(short-term memory, keyed by thread_id), and a store holds long-term memories that are searched by
embedding similarity across threads. langgraph-checkpoint-postgres provides both against the same
DocsAssistant PostgreSQL database, with the store’s semantic index using the same nomic-embed-text
embeddings:
from langchain.agents import create_agent
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langgraph.checkpoint.postgres import PostgresSaver
from langgraph.store.postgres import PostgresStore
DB_URI = "postgresql://docs:docs@localhost:5432/docsassistant"
embeddings = OllamaEmbeddings(model="nomic-embed-text")
with (
PostgresSaver.from_conn_string(DB_URI) as checkpointer,
PostgresStore.from_conn_string(
DB_URI, index={"dims": 768, "embed": embeddings, "fields": ["text"]},
) as store,
):
checkpointer.setup() # creates the checkpoint tables on first run
store.setup() # creates the store tables and its pgvector index
store.put(("docsassistant", "user-42"), "pref-1", {"text": "Prefers answers with SQL examples."})
memories = store.search(("docsassistant", "user-42"), query="how should answers be formatted?", limit=3)
agent = create_agent(
model=ChatOllama(model="llama3.1:8b", temperature=0.1),
tools=[search_docs],
checkpointer=checkpointer,
store=store,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "And which operator computes cosine distance?"}]},
{"configurable": {"thread_id": "conversation-1"}},
)
Install with pip install langgraph-checkpoint-postgres "psycopg[binary]". See
LangChain: Short-term memory for the
Postgres checkpointer, LangChain: Long-term
memory for the store and its semantic index, and
Agent
Memory & Semantic Cache: LangGraph checkpointer and store for how this maps onto the section’s hand-written
message_embeddings memory.
LangChain4j
LangChain4j is the JVM counterpart used throughout this section wherever a page shows a Java client alongside
its Python and Spring AI ones (for example
Generating Embeddings in Practice's
OllamaEmbeddingModel and Document Ingestion &
Chunking's EmbeddingStoreIngestor). Its central interfaces mirror LangChain’s Python ones under different
names:
| Interface | Role |
|---|---|
|
The vector-store abstraction, equivalent to LangChain’s |
|
Wires a document loader, a splitter, an |
|
Wraps an |
|
Declares an interface-backed chat service and augments every call with retrieved content before it reaches the model, equivalent to composing a retriever into a LangChain RAG chain. |
PgVectorEmbeddingStore is the EmbeddingStore implementation used against the DocsAssistant database; like
the Python PGVector store, it owns its own table (docs_lc4j_embeddings), separate from the hand-written
schema and from Spring AI’s docs_vector_store:
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.rag.DefaultRetrievalAugmentor;
import dev.langchain4j.service.AiServices;
EmbeddingModel embeddingModel = OllamaEmbeddingModel.builder()
.baseUrl("http://localhost:11434")
.modelName("nomic-embed-text")
.build();
EmbeddingStore<TextSegment> embeddingStore = PgVectorEmbeddingStore.builder()
.host("localhost").port(5432).database("docsassistant")
.user("docs").password("docs")
.table("docs_lc4j_embeddings")
.dimension(768)
.useIndex(true) // creates an IVFFlat index (vector_cosine_ops) ...
.indexListSize(100) // ... whose lists count is required whenever useIndex is true
.build();
EmbeddingStoreIngestor.builder()
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build()
.ingest(documents);
EmbeddingStoreContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(5)
.build();
interface DocsAssistant {
String answer(String question);
}
DocsAssistant assistant = AiServices.builder(DocsAssistant.class)
.chatModel(OllamaChatModel.builder().baseUrl("http://localhost:11434").modelName("llama3.1:8b").build())
.retrievalAugmentor(DefaultRetrievalAugmentor.builder().contentRetriever(contentRetriever).build())
.build();
useIndex(true) makes the store create an IVFFlat index on its table, and indexListSize (the IVFFlat
lists parameter) must then be set to a value greater than zero — otherwise table initialization throws an
IllegalArgumentException. See
LangChain4j: PGVector embedding store.
The Maven coordinates for the pieces above (the pgvector module is published on LangChain4j’s -beta
track, so its version carries the -beta30 suffix):
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.20.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>1.20.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-pgvector</artifactId>
<version>1.20.1-beta30</version>
</dependency>
LangChain4j groups RAG implementations into three levels of the same taxonomy used on
RAG Fundamentals: Easy RAG
(EmbeddingStoreIngestor with default settings, for a first working pipeline), Naive RAG (an explicit
EmbeddingStoreContentRetriever composed by hand, as above), and Advanced/Modular RAG (a
RetrievalAugmentor with a configurable QueryTransformer, QueryRouter and ContentAggregator, covered in
depth on Retrieval Strategies: LangChain4j).
See LangChain4j: Retrieval-Augmented Generation and
LangChain4j: embedding (vector) stores.
Spring AI equivalents
Every concept above has a Spring AI counterpart under its own fixed anchor on
Integrating with Spring AI: embeddings
(EmbeddingModel, #spring-ai-embeddings), vector stores (VectorStore and PgVectorStore,
#spring-ai-vector-store-api), metadata filtering (#spring-ai-filter-expressions), ingestion
(#spring-ai-etl), the RAG chain (QuestionAnswerAdvisor, #spring-ai-rag-advisors), modular RAG
(#spring-ai-modular-rag), and agent memory (ChatMemory, #spring-ai-chat-memory) alongside tool-calling
(@Tool, #spring-ai-tools). Where LangChain composes a Runnable pipeline or wraps a service in
AiServices, Spring AI composes an advisor chain around a ChatClient; the two frameworks solve the same
retrieval and augmentation problems with different composition styles, and every concept page in this section
shows both side by side.
Related pages
-
Integrating with Spring AI — the Spring AI equivalents to every anchor above.
-
RAG Integration Patterns — wiring Qdrant into a RAG pipeline via LangChain.
-
Extensions and the Platform — LangChain4j as a Quarkus extension.
-
RAG Fundamentals — the RAG pipeline these vector-store and retriever building blocks assemble into.
-
Agentic RAG — the agentic-RAG loop built on
create_agentand LangGraph. -
Exposing RAG through MCP — exposing the same retrieval to any agent through an MCP server, and consuming it back with
langchain-mcp-adapters.