
from pydantic import BaseModel, Field from langchain_openai import ChatOpenAI class MovieInfo(BaseModel):# BaseModel Pydantic 的基类所有自定义模型必须继承它。 电影信息 title: str Field(description电影名称) year: int Field(description上映年份) director: str Field(description导演) rating: float Field(description评分10分制) structured_llm llm.with_structured_output(MovieInfo) result structured_llm.invoke(介绍一下电影《星际穿越》)class MultiRetrieverSystem: 多路召回检索系统 支持 BM25、向量检索、混合检索、多查询检索四种模式 def init(self, documents, embedding_model, llm): self.documents documents self.embedding_model embedding_model self.llm llm self.setup_retrievers() def setup_retrievers (self): 初始化所有检索器 BM25 检索器 self.bm25 BM25Retriever.from_documents(self.documents) self.bm25.k 10 向量检索器 self.vectorstore Milvus( embedding_functionembedding_model, connection_args{uri: http://localhost:19530}, collection_namecar_info_collection ) self.vector self.vectorstore.as_retriever(search_kwargs{k: 10}) 混合检索器 self.ensemble EnsembleRetriever ( retrievers[self.bm25, self.vector], # 待融合的基础检索器列表 weights[0.4, 0.6], # BM25 权重 0.4向量检索权重 0.6 总和建议为 1向量检索权重更高 normalize_scoresTrue # 是否将不同检索器的分数归一化到 [0,1]避免因分数范围差异导致融合偏差 ) 多查询检索器 self.multi_query MultiQueryRetriever.from_llm( retrieverself.ensemble, llmself.llm, promptCUSTOM_PROMPT ) def search (self, query, modeensemble): 执行检索 mode: bm25 | vector | ensemble | multi_query retriever_map { bm25: self.bm25, vector: self.vector, ensemble: self.ensemble, multi_query: self.multi_query, } if mode not in retriever_map: raise ValueError (f不支持的检索模式: {mode}可选: {list (retriever_map.keys ())}) return retriever_map [mode].invoke (query) #实例化MultiRetrieverSystem rag_system MultiRetrieverSystem(valid_docs, embedding_model, llm) #外部调用 query 在多少速度范围内HDC才会激活 bm25_results rag_system.search(query, bm25)先对两个类进行概括第一个class MovieInfo(BaseModel)→Pydantic 数据模型用来定义数据格式第二个class MultiRetrieverSystem→业务功能类自己写的检索系统工具类第二个类在调用的时候需要先实例化再调用search方法为什么第一个没有实例化Pydantic 类依然需要实例化它不是天然实例。structured_llm llm.with_structured_output(MovieInfo)这里把这个类当作「规则模板」传给大模型工具LangChain 内部拿到这个类等 LLM 返回 JSON 后内部自动实例化 MovieInfo 对象虽外部看不到MovieInfo(xxx)这一步但实际是有实例化的步骤的。为什么MovieInfo没有__init__、没有 self1.所有 Python 类默认自带隐藏父类class A: pass # Python自动给你继承 object自带默认空的 __init__2. Pydantic 的特殊魔法BaseModel 重写了底层的__init__class MovieInfo(BaseModel):父类不再是 object而是 Pydantic 提供的BaseModelBaseModel本身继承 object但重写、拓展了大量魔法方法自带智能__init__、类型解析、字段校验不用你手写 self 赋值。BaseModel是一个高度封装的父类它已经实现好了强大的__init__自动读取类型注解title: str自动接收关键字参数、自动类型校验、自动赋值属性不需要手动写 self.title title对比# 普通原生类必须手写__init__、self class Movie: def __init__(self, title, year): self.title title self.year year # Pydantic类父类已经做好了全部初始化逻辑你只需要声明字段即可 class MovieInfo(BaseModel): title: str year: int # 调用时MovieInfo(titlexxx, year2020)父类的__init__自动完成赋值校验结论不是不需要__init__是父类BaseModel已经帮你写完了你不需要重复写。而MultiRetrieverSystem继承的是默认object没有预置初始化逻辑你自己需要手动写__init__接收外部参数documents、embedding_model。