Embeddings and Vector Stores
Purpose and Scope
Embeddings and vector stores are the retrieval foundation for many LangChain applications. An embedding model converts text into a numeric vector that preserves semantic meaning, while a vector store persists those vectors and retrieves nearby items for a natural-language query. In a typical retrieval-augmented generation workflow, documents are loaded, optionally parsed or split, embedded, stored, and later searched so an agent or chain can answer with relevant context instead of relying only on the model prompt.
LangChain treats these pieces as replaceable interfaces rather than as one hard-coded database or model. That design lets an application start with an in-memory or local store during development, then move to a production vector database without rewriting the surrounding retrieval logic. The same idea applies to embedding providers: the app depends on an embedding interface that can be implemented by OpenAI, Cohere, Hugging Face, or other integrations, while downstream retrieval code receives vectors and documents through consistent operations.
The repository evidence for this page centers on storage and document preparation. The core store module defines the generic key-value storage contract used by LangChain infrastructure, including embedding-cache-like use cases where keys map to stored values such as vector arrays. The JavaScript language parser module shows how classic document-loading paths preserve compatibility by dynamically forwarding deprecated parser imports to the community package, which matters because high-quality retrieval depends on predictable document ingestion before embedding. Sources: libs/core/langchain_core/stores.py, libs/langchain/langchain_classic/document_loaders/parsers/language/javascript.py
Relevant Source Files
- libs/core/langchain_core/stores.py - Defines
BaseStore, the generic batched key-value interface with sync and async methods. Its module docstring states that these stores primarily support caching, and the class contract is directly relevant to embedding caches and storage-backed retrieval infrastructure. - libs/langchain/langchain_classic/document_loaders/parsers/language/javascript.py - Provides a compatibility import path for
JavaScriptSegmenterby delegating deprecated imports throughcreate_importer. It is part of the document-ingestion side of retrieval workflows, where source code can be segmented before later embedding and indexing.
Core Primitives
An embedding model is responsible for vectorization: it takes raw text and returns fixed-length numeric arrays. Official LangChain docs describe two common operations: embedding a list of documents and embedding a single query. The distinction is important because some providers optimize document and query embeddings differently, even when the application treats both as vectors. Once text is embedded, similarity metrics such as cosine similarity, Euclidean distance, or dot product can be used to compare semantic closeness.
A vector store is the retrieval-facing persistence layer for embedded data. It stores documents and their vectors, supports adding documents, allows deletion by identifier or filter where supported, and exposes similarity search for semantic lookup. Most vector stores are initialized with an embedding model so they can turn incoming documents and user queries into vectors internally. The practical effect is that application code can call a high-level operation such as similarity search while the store handles embedding, indexing, filtering, and provider-specific query execution.
A key-value store is a lower-level storage primitive that often supports the embedding workflow indirectly. BaseStore[K, V] is generic over key and value types, so it can represent mappings such as string keys to message objects, string keys to serialized documents, or string keys to numeric vectors. The class deliberately exposes batch methods instead of single-key helpers because storage backends often perform better when they can reduce network round trips or write multiple records in one operation. Sources: libs/core/langchain_core/stores.py
Document loaders and parsers are also part of the retrieval pipeline, even though they do not compute vectors themselves. The classic JavaScript parser module exports JavaScriptSegmenter through a deprecated lookup that points at langchain_community.document_loaders.parsers.language.javascript. This compatibility layer helps older imports continue to resolve while centralizing deprecation behavior. In retrieval systems that index source code, segmentation quality affects the units that are embedded and later returned by a vector search. Sources: libs/langchain/langchain_classic/document_loaders/parsers/language/javascript.py
System-to-Code Mapping
| Concept | Repository surface | Why it matters |
|---|---|---|
| Batched key-value storage | BaseStore.mget, BaseStore.mset, BaseStore.mdelete, BaseStore.yield_keys | Supports efficient storage and cache-like access patterns for retrieval infrastructure. |
| Async storage fallback | BaseStore.amget, BaseStore.amset, BaseStore.amdelete, BaseStore.ayield_keys | Lets async applications use the same interface; defaults run sync methods in an executor unless implementations override them. |
| Generic key and value types | BaseStore[K, V] | Allows stores to hold different payloads, including values related to embeddings or cached computation. |
| Document parsing compatibility | JavaScriptSegmenter deprecated lookup | Keeps classic language-parser imports working while delegating implementation to the community package. |
The BaseStore API is intentionally small. Implementations must provide mget, mset, and mdelete, and they also provide key iteration through yield_keys. Async counterparts are present for applications built on event loops, and the default async implementation delegates to synchronous methods via run_in_executor. That means a custom store can become usable in both sync and async retrieval applications with a small required surface, while still allowing native async stores to override the defaults for better performance. Sources: libs/core/langchain_core/stores.py
Retrieval Workflow
A common retrieval workflow starts by loading source material and normalizing it into document-sized chunks. For code repositories, a language-aware parser can segment files so embeddings represent coherent units rather than arbitrary byte ranges. The supplied JavaScript parser module belongs to this ingestion layer: it does not embed text, but it controls how an import path resolves for a segmenter that can participate before vectorization. Keeping that import path stable reduces migration friction for applications that still rely on classic document loader names.
After documents are prepared, an embedding model converts each chunk into a vector. Those vectors can be inserted into a vector store along with page content and metadata such as source path, timestamp, tenant, or document type. Metadata matters because semantic similarity alone is often not enough: a support agent may need only public documentation, a code assistant may need only files from one repository, and a compliance application may need date or access filters applied before or during search.
At query time, the user question is embedded with the query embedding operation, then compared against stored document vectors. The vector store returns the nearest documents, usually limited by a result count and optionally constrained by metadata filters. Those retrieved documents can then be passed into a model prompt, agent state, or runnable chain. The storage interfaces in LangChain are designed so caching and persistence concerns can be separated from the higher-level retrieval orchestration that decides what to do with the returned context.
Compact API Reference
BaseStore is the concrete repository-backed API surface visible in the supplied source. It is an abstract generic class, so application developers subclass it or use a provided implementation rather than instantiating it directly. Required synchronous methods operate on batches: mget(keys: Sequence[K]) -> list[V | None], mset(key_value_pairs: Sequence[tuple[K, V]]) -> None, and mdelete(keys: Sequence[K]) -> None. The return shape of mget preserves input order and returns None when an individual key is not present. Sources: libs/core/langchain_core/stores.py
The async companion methods are amget, amset, amdelete, and ayield_keys. The source comments explain that the default implementations use the synchronous methods, so a simple store can implement the sync contract first. If a backend natively supports asynchronous I/O, it should override the async methods to avoid unnecessary executor work. yield_keys(prefix: str | None = None) supports key discovery with optional prefix filtering, which is useful for cache maintenance, namespace cleanup, and inspection tooling. Sources: libs/core/langchain_core/stores.py
from collections.abc import Iterator, Sequence
from langchain_core.stores import BaseStore
class EmbeddingCache(BaseStore[str, list[float]]):
def __init__(self) -> None:
self.store: dict[str, list[float]] = {}
def mget(self, keys: Sequence[str]) -> list[list[float] | None]:
return [self.store.get(key) for key in keys]
def mset(self, pairs: Sequence[tuple[str, list[float]]]) -> None:
for key, value in pairs:
self.store[key] = value
def mdelete(self, keys: Sequence[str]) -> None:
for key in keys:
self.store.pop(key, None)
def yield_keys(self, prefix: str | None = None) -> Iterator[str]:
for key in self.store:
if prefix is None or key.startswith(prefix):
yield keyImplementation Details and Next Steps
When choosing an embedding provider or vector store, keep the interface boundaries clear. Embedding models answer the question, how should this text be represented numerically. Vector stores answer, how should those vectors be indexed, filtered, and searched. Key-value stores answer, how should reusable intermediate values be cached or persisted. Document loaders and parsers answer, what text units should be embedded in the first place. Mixing those responsibilities makes retrieval code harder to test and harder to swap across providers.
For production systems, decide early how IDs, metadata, and cache keys are generated. Stable IDs make deletion and re-indexing possible, metadata makes retrieval safer and more precise, and batched store methods keep indexing efficient. If you are building a custom backend, implement the BaseStore batch contract first and add native async overrides when your storage driver supports them. Then connect that storage layer to the broader retrieval flow: loaders and splitters prepare content, embeddings vectorize it, vector stores retrieve it, and agents or chains consume the returned context.
Related pages: documents-and-loaders, text-splitters, retrievers, indexing-api, providers-overview