当前位置: 首页 > news >正文

【AI RAG知识库】09.【检索】【节点2】

掌柜智库项目(RAG)实战

9. 检索数据节点实现与测试

9.2 查询节点搜索向量库 (node_search_embedding)

文件:app/query_process/agent/nodes/node_search_embedding.py

步骤分解

1)获取查询上下文
从状态(state)中提取经过改写的问题(rewritten_query)以及在上一节点确认的商品名称列表(item_names)。

2)文本向量化
调用嵌入模型接口,将改写后的问题转换为向量表示。这里同时生成两种向量:

  • 稠密向量 (Dense Vector):用于捕捉语义相似度。
  • 稀疏向量 (Sparse Vector):用于捕捉关键词匹配。

3)构建混合检索请求
准备 Milvus 混合搜索参数:

  • 过滤条件:如果有确定的商品名,构造item_name in [...]过滤器,限定搜索范围。
  • 检索策略:结合稠密向量(COSINE 距离)和稀疏向量(IP 内积)进行多路召回。

4)执行向量检索
连接 Milvus 数据库的CHUNKS_COLLECTION集合,执行混合检索。

  • 加权融合:默认使用 0.8/0.2 的权重对稠密和稀疏得分进行重排序(Rerank)。
  • 结果截取:返回 Top 5 最相关的文档切片(Chunks)。

5)更新状态
将检索到的文档切片列表(embedding_chunks)存入状态,供后续节点使用。

节点代码实现
importsysimportosfromapp.utils.task_utilsimportadd_running_task,add_done_taskfromapp.lm.embedding_utilsimportgenerate_embeddingsfromapp.clients.milvus_utilsimportcreate_hybrid_search_requests,hybrid_search,get_milvus_clientfromapp.core.loggerimportloggerfromdotenvimportload_dotenv,find_dotenv load_dotenv(find_dotenv())defnode_search_embedding(state):""" 核心节点函数:基于已确认商品名+改写后的用户问题,执行Milvus向量数据库混合检索 流程:用户问题向量化 → 构造带商品名过滤的混合搜索请求 → 执行稠密+稀疏混合检索 → 返回检索结果 :param state: Dict - 会话状态字典,包含上游传递的核心信息,关键字段: { "session_id": str, # 会话唯一标识 "rewritten_query": str, # step3改写后的完整用户问题(含商品名) "item_names": list[str], # step6已确认的标准化商品名列表 "is_stream": bool/None # 是否为流式响应,可选 } :return: Dict - 检索结果字典,仅包含embedding_chunks字段,供下游节点使用: { "embedding_chunks": List[Dict] # Milvus检索结果列表,无结果则为空列表 # 每个元素为一条匹配的向量数据,含业务字段 } """logger.info("---search_milvus 开始处理---")add_running_task(state["session_id"],sys._getframe().f_code.co_name,state["is_stream"])# 1. 从会话状态中提取核心入参,为后续检索做准备query=state.get("rewritten_query")# 提取改写后的用户问题(含商品名,独立完整)item_names=state.get("item_names")# 提取已确认的标准化商品名列表(精准过滤用)logger.info(f"核心入参提取: query='{query}', item_names={item_names}")# 2. 对改写后的用户问题执行向量化,生成BGEM3稠密+稀疏向量logger.info(f"开始为文本获取嵌入值:{query[:50]}..."iflen(query)>50elsef"开始为“{query}”文本获取嵌入值...")# 调用向量化函数,入参为列表(支持批量,此处仅单条查询)# 生成与商品名匹配的语义向量,用于后续相似性检索embeddings=generate_embeddings([query])dense_vec=embeddings.get("dense")[0]sparse_vec=embeddings.get("sparse")[0]# 打印稠密/稀疏向量日志,便于调试向量生成结果logger.debug(f"向量生成成功: dense_dim={len(dense_vec)}, sparse_len={len(sparse_vec)}")# 3. 准备Milvus向量数据库连接相关配置,指定检索的集合# 从环境变量中获取Milvus中存储「文本片段向量」的集合名(表名),避免硬编码collection_name=os.environ.get("CHUNKS_COLLECTION")logger.info(f"正在连接到 Milvus 并准备集合 '{collection_name}'...")# 4. 构造Milvus混合搜索请求对象(核心步骤)# 先通过辅助函数生成商品名过滤表达式,精准过滤检索范围# 'item_name in ["苹果15", "华为P60"]'# 若无商品名,直接返回None(不做过滤)ifnotitem_names:logger.warning("item_names 为空,跳过检索,返回空结果")return{"embedding_chunks":[]}# 对每个商品名添加双引号,拼接为Milvus支持的in语法格式quoted=", ".join(f'"{v}"'forvinitem_names)# 构造最终过滤表达式expr=f"item_name in [{quoted}]"logger.info(f"创建搜索请求过滤表达式:{expr}")# 构造稠密+稀疏混合搜索请求,整合向量、过滤条件、搜索参数reqs=create_hybrid_search_requests(dense_vector=dense_vec,# 取用户问题的稠密向量(单条,故取索引0)sparse_vector=sparse_vec,# 取用户问题的稀疏向量(单条,故取索引0)expr=expr,# 商品名过滤表达式,缩小检索范围(仅检索指定商品名的向量)limit=10# 底层检索返回数量(后续会再过滤为5,预留更多结果做重排序))# 5. 执行Milvus稠密+稀疏混合向量检索(核心调用)logger.info("开始执行 Milvus 混合检索...")client=get_milvus_client()res=hybrid_search(client=client,collection_name=collection_name,# 检索的目标集合名(文本片段向量集合)reqs=reqs,# 构造好的混合搜索请求对象(稠密+稀疏)ranker_weights=(0.8,0.2),# 稠/稀疏向量评分权重配比,各占50%(可按业务调优)norm_score=True,# 开启评分归一化,将距离值转为0-1区间的相似度评分limit=5,# 最终返回的TOP5相似度最高结果output_fields=["chunk_id","content","item_name"]# 指定返回的业务字段)# 打印节点处理成功日志,输出原始检索结果,便于调试hit_count=len(res[0])ifresandlen(res)>0else0logger.info(f"节点 search_embedding 处理成功,检索到{hit_count}条相关片段")ifhit_count>0:logger.debug(f"Top1 检索结果示例:{res[0][0]}")# 标记当前任务完成,更新任务状态add_done_task(state["session_id"],sys._getframe().f_code.co_name,state.get("is_stream"))# 6. 构造并返回结果:若检索结果非空,取res[0](适配Milvus批量搜索格式),否则返回空列表# res[0]为当前单条查询的检索结果,包含TOP5匹配的向量数据及业务字段return{"embedding_chunks":res[0]ifreselse[]}
主流程测试
if__name__=="__main__":# 模拟测试数据test_state={"session_id":"test_search_embedding_001","rewritten_query":"HAK 180 烫金机使用说明",# 模拟改写后的查询"item_names":["HAK 180 烫金机"],# 模拟已确认的商品名"is_stream":False}print("\n>>> 开始测试 node_search_embedding 节点...")try:# 执行节点函数result=node_search_embedding(test_state)logger.info(f"检索结果汇总:{result}")# 验证结果chunks=result.get("embedding_chunks",[])print(f"\n>>> 测试完成!检索到{len(chunks)}条结果")ifchunks:print("\n>>> Top 1 结果详情:")top1=chunks[0]# 打印关键字段(注意:entity字段可能包含具体业务数据)print(f"ID:{top1.get('id')}")print(f"Distance:{top1.get('distance')}")entity=top1.get('entity',{})print(f"Item Name:{entity.get('item_name')}")print(f"Content Preview:{entity.get('content','')[:100]}...")else:print("\n>>> 警告:未检索到任何结果,请检查 Milvus 数据或 item_names 是否匹配")exceptExceptionase:logger.error(f"测试运行失败:{e}",exc_info=True)
http://www.jsqmd.com/news/1148331/

相关文章:

  • 揭秘BiliBiliCCSubtitle:一键获取B站字幕的专业利器
  • 重塑家庭数字中心:深度解析 CasaOS 与个人云的未来形态
  • 16位ADC与MCU的高精度信号采集系统设计
  • 在资源预算表上做决策:TinyML 模型选型三维权衡框架——延时、精度、内存的量化构建方法
  • Windows系统文件Chakrathunk.dll丢失找不到问题解决
  • STM32与ADS7828的工业级ADC信号采集方案
  • CompressO:三步完成媒体文件瘦身,免费开源工具让大文件变小90%
  • @highcharts/react完整技术指南|新版Highcharts React使用、迁移、SSR、TS 优化全解
  • 从复位向量到内核入口:U-Boot SPL 到 Kernel 启动全链路逐阶段剖析
  • 多级缓存架构落地:从 L1 内存到 L3 磁盘的 RAG 检索缓存分层设计
  • 电气设备配电箱开关保护器件插座检测数据集VOC+YOLO格式4135张10类别有增强
  • 企业级CAD字体管理架构优化:3大性能提升策略实战指南
  • Python Playwright关键字驱动测试框架:从脚本到工程化实战
  • Python 数据管线调度引擎:DAG 编排代替 Cron 硬编码
  • 第十章:Gradle Wrapper
  • 中国车牌生成器:高效自动化车牌数据生成技术实现指南
  • 研究生论文降AI全流程:从初稿到盲审通过4.8元工具组合完整操作指南
  • Next.js DApp 国际化路由:多语言链上术语库与动态内容切换
  • PyTorch混合精度训练完整配置:从AMP初始化到梯度缩放故障排查
  • “轴承制造的精度革命,不只靠一台机床,而是一整套从车削到磨削的‘精密闭环’。”
  • 终极清理指南:如何用MyComputerManager彻底删除Windows此电脑中的顽固图标
  • Zotero SciHub插件技术深度解析:学术文献自动获取的高效方案
  • Layerdivider:三步实现智能图像分层的终极解决方案
  • 3分钟解锁B站缓存视频:m4s转MP4完整指南
  • 终极指南:如何通过defender-control实现Windows Defender永久禁用与精细控制
  • 原来专业高铁标识有这么多门道,到底哪家好?
  • 相机成像模型 EMVA1288 解析:从辐射亮度 L 到像素灰度值的 4 个关键参数
  • Poppins字体终极指南:如何用免费几何字体解决多语言设计难题?
  • PowerToys:重新定义Windows生产力的瑞士军刀
  • TPD2015FN智能功率开关在工业负载控制中的应用