LangChain 自定义检索器报没有 embeddings 属性
自定义 LangChain 检索器报没有 embeddings 属性,检查 VectorStore 与 Retriever 的对象层次;用 BaseRetriever 包装,并区分 invoke 和实现方法。
给已有检索器加上重排或结果过滤,链调用时却出现:
AttributeError: 'VectorStoreRetriever' object has no attribute 'embeddings'
若 traceback 停在 VectorStoreRetriever._get_ls_params(),先看包装类的继承和 vectorstore 字段。这里未必是 embedding 模型没有加载;也可能把一个检索器放进了本来要求向量存储对象的字段。
以下接口按 langchain-core 0.3.79 核对;另对照 1.0.0 的检索器接口说明旧方法迁移,不代表所有第三方适配器都使用相同实现。
vectorstore 字段应该装什么
VectorStoreRetriever 继承自 BaseRetriever,其 vectorstore 字段应是 VectorStore。例如已有向量库对象 store,通过 store.as_retriever() 得到检索器。
下面的自定义结构会把对象层次混在一起:
from langchain_core.vectorstores import VectorStoreRetriever
class WrappedRetriever(VectorStoreRetriever):
vectorstore: VectorStoreRetriever
外层继承了向量检索器的行为,内层字段却改成另一个检索器。父类读取 self.vectorstore.embeddings 时,拿到的是内层检索器,而非拥有该属性的向量存储对象。错误可能在执行自定义检索方法之前出现,所以仅把方法名加上下划线并不一定解决它。
若没有自定义包装类,检查传给标准 VectorStoreRetriever 的对象是否确实符合当前版本的 VectorStore 契约。适配器版本或错误构造也可能导致类似属性错误,不能只凭异常名称认定是这一种根因。
包装已有检索器,用 BaseRetriever 组合
仅添加过滤、截取或重排时,外层可直接继承 BaseRetriever,另设字段保存内部检索器。下面用截取前几条说明结构,不实现重排模型:
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_core.retrievers import BaseRetriever
from pydantic import Field
class LimitedRetriever(BaseRetriever):
inner: BaseRetriever
limit: int = Field(default=3, ge=1)
def _get_relevant_documents(
self,
query: str,
*,
run_manager: CallbackManagerForRetrieverRun,
) -> list[Document]:
docs = self.inner.invoke(
query,
config={"callbacks": run_manager.get_child()},
)
return docs[:self.limit]
inner 应替换为应用已经配置好的检索器。例如 wrapped = LimitedRetriever(inner=store.as_retriever(), limit=3),调用入口是 wrapped.invoke("实际查询")。返回值仍应是 Document 列表;检索结果不足三条时切片自然返回现有条目,不直接访问 docs[2]。
例子没有共享查询缓存,也不改变文档和索引。实际加入重排时,还需处理空列表、分数方向、文档与分数对应关系;不能把随手截取的例子当成重排算法。
invoke 与下划线方法各有用途
调用方用 invoke(query),异步调用用 ainvoke(query);自定义类实现 _get_relevant_documents(),需要原生异步逻辑时再实现 _aget_relevant_documents()。不要把下划线方法直接当业务入口,否则可能跳过回调和运行管理。
0.3.79 仍保留 get_relevant_documents(),并标注自 0.1.46 弃用、预定 1.0 移除;1.0.0 的 BaseRetriever 已没有这个公开方法。看到弃用警告与看到 AttributeError 是两种状态。只把旧调用改成 invoke,也不会自动纠正前面错误的对象继承。
回查时先让内部检索器独立返回预期文档,再从原有链入口调用包装器,核对过滤数量和文档内容。异步链还要检查内部调用是否阻塞,以及原生异步实现的回调是否传递完整;以上同步示例不承诺并发性能。


