问题与目标
上一节只在列表中计算相似度。真实知识库需要保存向量和元数据,并支持过滤、增量写入、更新、删除与重启恢复。LangChain 中 VectorStore 管理数据,Retriever 面向上层提供“给定查询返回 Document”的检索接口。
本篇使用内存向量库跑通职责边界,再给出持久化索引必须保存的版本信息。内存实现适合单元测试,不适合多进程共享和长期数据保存。

文档更新先产生新版本 Chunk,经验证后再切换索引;Retriever 面向查询隐藏具体存储实现,但不替代数据生命周期管理。
核心概念
- VectorStore:负责
add/search/delete等存储操作,能力取决于具体后端。 - Retriever:只暴露检索语义,可包装向量库、关键词检索或组合策略。
- 文档 ID:标识业务文档;Chunk ID:标识某版本中的具体片段。
- 元数据过滤:应在候选检索前执行权限限制,而不是生成答案后删除文本。
- 持久化:不仅保存向量,还要保存模型、切分、清洗和文档版本。
更新的稳妥方式通常是为新文档版本生成全新 Chunk,写入成功并验收后,再删除旧版本。直接覆盖容易留下半新半旧状态。
可运行实现
python -m pip install "langchain-core>=1,<2"
import hashlib
import math
from langchain_core.documents import Document
from langchain_core.embeddings import Embeddings
from langchain_core.vectorstores import InMemoryVectorStore
class HashEmbeddings(Embeddings):
def _one(self, text: str) -> list[float]:
vector = [0.0] * 64
for char in text:
index = int(hashlib.sha256(char.encode()).hexdigest()[:8], 16) % 64
vector[index] += 1
norm = math.sqrt(sum(x * x for x in vector)) or 1
return [x / norm for x in vector]
def embed_documents(self, texts: list[str]) -> list[list[float]]:
return [self._one(text) for text in texts]
def embed_query(self, text: str) -> list[float]:
return self._one(text)
store = InMemoryVectorStore(HashEmbeddings())
docs = [
Document(page_content="关键告警必须立即通知负责人", metadata={
"source": "duty.md", "version": "v1", "scope": "team-a"
}),
Document(page_content="恢复后观察十分钟并记录处理人", metadata={
"source": "recovery.md", "version": "v1", "scope": "team-a"
}),
]
ids = ["duty:v1:000", "recovery:v1:000"]
store.add_documents(docs, ids=ids)
retriever = store.as_retriever(search_kwargs={"k": 2})
for doc in retriever.invoke("关键告警通知谁"):
print(doc.metadata, doc.page_content)
store.delete(ids=["duty:v1:000"])
示例后端不演示过滤。选择生产后端前应针对 scope="team-a" 实测服务端过滤、删除一致性和重启恢复,不能只看接口名称。
持久化清单至少包含:
{"index_version":"kb-2026-08-27","embedding_model":"...","dimension":1024,
"splitter":"recursive-v2","document_version":"v3"}
常见问题与排查
重复执行入库脚本后结果越来越多
用稳定 Chunk ID 或内容哈希去重,把写入设计成幂等操作。入库完成后核对文档数、Chunk 数与唯一 ID 数。
删除文档却还能检索到
检查是否只删了业务表、未删向量索引,或多个索引别名仍指向旧版本。用 Chunk ID 做删除后的反向查询验收。
先检索再在 Python 中过滤权限
未授权内容已经进入应用内存,且可能挤掉授权候选。尽量使用向量库原生过滤,并在返回后再次防御性校验。
更换 Embedding 后继续使用旧集合
新建索引版本、完整重嵌入、运行固定评估,再原子切换别名。不要在一个集合中混合两个向量空间。
小结
VectorStore 解决数据生命周期,Retriever 隔离上层检索接口。真正可维护的索引必须有稳定 ID、版本、权限过滤、幂等更新和删除验收;“能够 Top-k 查询”只是起点。
License: CC BY-NC 4.0
Updated 2 hours ago
Was this article helpful? Give it a like.
0 comments


