回忆 RAG 关键步骤:
- 文本切割
- 嵌入处理
- 存储向量数据库
向量数据库可以分为这几种类型:
- 内存型
- 本地自托管
- 云托管
LangChain 内置了 MemoryVectorStore,这就是一个内存型向量库,用于将文档向量存储到内存中,适合本地调试、快速演示,零依赖、即插即用。
MemoryVectorStore 整体工作流:
- 把要检索的文本包装成
Document; - 调用
vectorstore.addDocuments()触发嵌入; - 用
similaritySearch(...)或similaritySearchWithScore(...)做相似度检索。
1. 实例化内存向量库
import { MemoryVectorStore } from 'langchain/vectorstores/memory'
const store = new MemoryVectorStore()
console.log(store)效果:
MemoryVectorStore { lc_serializable: false, lc_kwargs: {}, lc_namespace: [ 'langchain', 'vectorstores', 'memory' ], embeddings: undefined, memoryVectors: [], similarity: [Function: cosine]}- embeddings:嵌入工具
- memoryVectors:存储的内存向量
2. 指定嵌入工具
自定义嵌入类:NomicEmbeddings
import { MemoryVectorStore } from 'langchain/vectorstores/memory'import { NomicEmbeddings } from './utils/embed.js'
const embeddings = new NomicEmbeddings(4)
const store = new MemoryVectorStore(embeddings)
console.log(store)效果:
MemoryVectorStore { lc_serializable: false, lc_kwargs: {}, lc_namespace: [ 'langchain', 'vectorstores', 'memory' ], embeddings: NomicEmbeddings { caller: AsyncCaller { maxConcurrency: Infinity, maxRetries: 6, onFailedAttempt: [Function: defaultFailedAttemptHandler], queue: [PQueue] }, model: 'nomic-embed-text', apiUrl: 'http://localhost:11434/api/embeddings', concurrency: 4 }, memoryVectors: [], similarity: [Function: cosine]}3. 添加文档
添加文档可以调用 vectorstore 实例的 addDocuments 方法,该方法接收一个数组,数组里面每一项是 Document 实例对象,之后会自动调用 embeddings.embedDocuments 生成向量并存入内存。
await vectorstore.addDocuments([ new Document({ pageContent: 'Vue 是一个渐进式前端框架,易于上手。', metadata: { id: 'a1', tag: 'frontend' }, }), new Document({ pageContent: 'React 通过 JSX 描述 UI,强调组件化与状态管理。', metadata: { id: 'b2', tag: 'frontend' }, }), new Document({ pageContent: 'LangChain 提供 RAG 组件与向量检索工具。', metadata: { id: 'c3', tag: 'ai' }, }),])具体示例:
import { MemoryVectorStore } from 'langchain/vectorstores/memory'import { NomicEmbeddings } from './utils/embed.js'import { TextLoader } from 'langchain/document_loaders/fs/text'import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'
const loader = new TextLoader('data/kong.txt')
const docs = await loader.load()
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 64, chunkOverlap: 0,})
const splittedDocs = await splitter.splitDocuments(docs)
const embeddings = new NomicEmbeddings(4)
const store = new MemoryVectorStore(embeddings)
await store.addDocuments(splittedDocs)
console.log(store.memoryVectors)4. 检索操作
首先创建一个检索器:
const retriever = vectorstore.asRetriever(2)vectorstore.asRetriever(2) 是一个简写,等价于 vectorstore.asRetriever({ k: 2 }),表示创建一个检索器,每次查询返回前 2 条最相似的 Document。返回值是一个 VectorStoreRetriever
快速上手示例:
import { MemoryVectorStore } from 'langchain/vectorstores/memory'import { NomicEmbeddings } from './utils/embed.js'import { TextLoader } from 'langchain/document_loaders/fs/text'import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'
const loader = new TextLoader('data/kong.txt')
const docs = await loader.load()
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 64, chunkOverlap: 0,})
const splittedDocs = await splitter.splitDocuments(docs)
const embeddings = new NomicEmbeddings(4)
const store = new MemoryVectorStore(embeddings)
await store.addDocuments(splittedDocs)
// 创建一个检索器,现在仓库已经有值了const retriever = store.asRetriever(1)
const res = await retriever.invoke('茴香豆是做什么用的')
console.log(res)store.asRetriever(1)store.asRetriever({ k: 1,})配置对象支持如下的参数:
const retriever = vectorstore.asRetriever({ k: 4, searchType: 'mmr', searchKwargs: { fetchK: 20, lambda: 0.5 }, filter: (doc) => doc.metadata?.source?.endsWith('data/kong.txt'), tags: ['demo', 'kong'], metadata: { lesson: 'RAG-intro' }, verbose: true,})-
k:每次查询返回的文档条数;不填默认
4。asRetriever(2)的数字简写等价于{ k: 2 }。 -
searchType:检索策略:
"similarity"(默认):按向量相似度排序返回前 k 条;"mmr":最大边际相关性(Maximal Marginal Relevance),在相关性与多样性之间折中。
-
searchKwargs:MMR 的细化参数,仅当
searchType: "mmr"时生效fetchK:先抓取的候选集合规模(越大,MMR 可选空间越大)lambda:0~1 的权衡系数;0更偏向多样性、1更偏向相关性
-
filter:用于预筛候选文档,返回
true的文档才参与检索。 -
callbacks:检索过程中的回调钩子(开始/结束/错误等),与 LangChain 的可观测性机制对接。
-
tags:给该检索器打标签,便于在日志/跟踪里区分。
-
metadata:附加的上下文信息,会随运行一起记录,便于审计与调试。
-
verbose:是否开启详细日志,默认
false。
另外配置项也支持数字简写,方法签名如下:
asRetriever(kOrFields?, filter?, callbacks?, tags?, metadata?, verbose?)不过想跳过中间的参数再传后面的,就用 undefined 占位。例如:
const r3 = vectorstore.asRetriever(3, undefined, undefined, ['demo', 'kong'])另外还有 similaritySearch 以及 similaritySearchWithScore 方法,用法也和上面类似。
平时在使用的时候更推荐使用 asRetriever 方法先创建一个检索器,后期进行检索的时候只需要传入检索文本即可。