企业实战:主体识别

目录

  • [1. 节点作用与实现思路](#1. 节点作用与实现思路)
  • [2. 步骤分解](#2. 步骤分解)
  • [3. 准备Embedding模型和工具](#3. 准备Embedding模型和工具)
    • [3.1 什么是 "生成词向量"?](#3.1 什么是 “生成词向量”?)
    • [3.2 "稀疏向量 + 稠密向量"](#3.2 “稀疏向量 + 稠密向量”)
    • [3.3 安装Python依赖库](#3.3 安装Python依赖库)
    • [3.4 Embedding下载模型](#3.4 Embedding下载模型)
    • [3.5 工具代码导入](#3.5 工具代码导入)

1. 节点作用与实现思路

作为文档结构化解析与差异化分类的核心关键节点 ,依托大语言模型深度语义理解能力,精准萃取文档核心主体、业务实体与专属概念,快速判定文档所属品类与内容属性。

通过全局主体标识绑定,实现多源文档精准区分、内容归类、数据去重与精细化管控,搭建实体与文本切片的强关联映射体系,为后续实体级检索、语义对齐、定向过滤、结构化问答筑牢底层支撑,大幅提升知识库检索精准度与数据治理能力。

实现思路

  1. 关键上下文精准裁剪:优先截取文档高价值头部切片,涵盖标题、概述等核心摘要信息,精简输入上下文,在控制推理成本的同时,保障大模型主体识别的准确率。
  2. 大模型语义萃取识别:结合定制化业务提示词,依托 LLM 深层语义解析能力,智能提取文档核心主体、专属名词与业务标识,完成文档类型自动判别与内容切面划分。
  3. 全链路容错兜底设计:针对大模型输出不稳定、识别异常、返回空值等场景,配置异常捕获与默认兜底策略,保障导入流程稳定运行,避免单点故障中断全链路任务。
  4. 实体向量化预处理:将识别后的标准主体实体统一完成向量编码,对接向量库实现跨表述语义匹配,打通别名关联、语义联动能力,实现模糊检索与精准召回。

2. 步骤分解

  1. 导入与配置: 引入必要的库(LangChain, Milvus, etc.)及配置参数。
  2. 核心辅助函数: 包含字符串安全转义等辅助逻辑。
  3. 主流程定义: LangGraph 节点的入口函数,串联各个步骤。
  4. 步骤 1: 获取输入 : 校验 State 中的 file_titlechunks
  5. 步骤 2: 构建上下文: 截取前 K 个切片作为 LLM 的识别素材。
  6. 步骤 3: 调用 LLM: 使用大模型识别商品名称,包含错误重试与兜底。
  7. 步骤 4: 回填数据: 将识别结果更新回 State 和 Chunks 元数据。
  8. 步骤 5: 生成向量: 调用 Embedding 模型生成 Dense/Sparse 向量。
  9. 步骤 6: 保存结果: 将数据写入 Milvus 向量库,并处理幂等性。
  10. 单元测试: 独立运行的测试代码,验证核心流程。

3. 准备Embedding模型和工具

3.1 什么是 "生成词向量"?

词向量(Word Vector/Embedding)就是把文字(比如 "苏泊尔 5000W 大功率电磁炉")转换成计算机能理解的数字列表(向量) 的过程。

打个比方

  • 人类理解文字:"苹果手机"= 品牌(苹果)+ 品类(手机);
  • 计算机理解文字:没法直接懂 "苹果手机",但能懂 [0.23, -0.56, 1.89, ...] 这样的数字列表;
  • 词向量的作用:把文字的语义信息(含义、特征、关联度)编码成数字,让计算机能 "计算文字相似度""分类文字""检索相似内容"。

举个简单例子

文字 对应的词向量(简化版,实际是几百 / 几千维)
苹果手机 0.23, -0.56, 1.89, 0.78
华为手机 0.21, -0.58, 1.91, 0.76
苹果笔记本 0.22, -0.55, 0.87, 0.79

计算机通过对比这些数字列表的相似度,就能判断:

  • "苹果手机" 和 "华为手机" 更像(数字差异小);
  • "苹果手机" 和 "苹果笔记本" 相似度低(数字差异大)。

3.2 "稀疏向量 + 稠密向量"

代码是基于 BGE-M3 模型生成两种词向量(这是当前主流的多模态嵌入方案)拆解:

类型 特点 用途
稠密向量(Dense Vector) 长度固定(比如 768 维 / 1024 维),每个位置都是连续数值(如 0.23、-0.56) 捕捉文字的语义信息(比如 "苹果手机" 的核心含义),适合相似度计算
稀疏向量(Sparse Vector) 长度极长(比如几十万维),但只有少数位置有非 0 值,其余都是 0 捕捉文字的关键词 / 字面特征(比如 "苹果""5000W""电磁炉"),适合精准检索

BGE-M3 模型同时输出这两种向量,结合使用能兼顾 "语义理解" 和 "精准匹配"。

3.3 安装Python依赖库

在使用模型之前,需要安装相关的 Python 依赖库。

cmd 复制代码
# ===================== 环境安装命令(适配BGE-M3+Milvus,GPU/CPU版区分)=====================
# 【GPU版】安装CUDA 12.4版PyTorch(含torchvision/torchaudio,NVIDIA显卡GPU加速必备)
# 适配:有NVIDIA独显且驱动≥551.61,后续BGE-M3可开启FP16半精度推理
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

# 【备用-CPU版】无NVIDIA显卡(AMD/Intel集显)请用此命令,直接安装CPU版PyTorch
# 注释掉上方GPU版命令,取消注释下方即可
uv add torch torchvision torchaudio

# 安装Milvus和BGE-M3核心依赖(所有环境必装,无GPU/CPU区分)
# pymilvus[model]:Milvus Python客户端(带模型相关依赖,适配向量入库/检索)
# FlagEmbedding:BGE-M3向量生成模型的核心依赖(不可替代)
# transformers:FlagEmbedding底层依赖,Hugging Face模型运行库
uv add  pymilvus[model] FlagEmbedding transformers

#⚠️:安装FlagEmbedding的时候会自动安装一个cpu版本的torch替换掉之前的gpu版本的torch,
# 要解决这个问题需要做以下几个步骤
# 步骤1 先删除已经安装的FlagEmbedding(先在pyproject.toml中确定一下自己安装的版本)
uv remove FlagEmbeddin

# 步骤2 将以下内容配置在pyproject.toml中
dependencies = [
     其他之前安装过的配置,
    "flagembedding>=v1.3.5",
    "torch>=2.10.0",
    "torchvision>=0.25.0",
    "torchaudio>=2.10.0",
]

[tool.uv.sources]
# 强制从 NVIDIA 源安装
torch = { index = "pytorch-cuda" }
torchvision = { index = "pytorch-cuda" }
torchaudio = { index = "pytorch-cuda" }

[[tool.uv.index]]
name = "pytorch-cuda"
url = "https://download.pytorch.org/whl/cu128"
explicit = true

# 步骤3 删除锁文件并重新锁定
rm uv.lock
uv lock

# 步骤4:重新同步环境
uv sync --reinstall

# 步骤5:验证
uv run python -c "import torch; print('GPU:', torch.cuda.is_available())"

CUDA 每个版本都有最低算力要求 ,CUDA 12.4 要求显卡的CUDA 算力≥3.5 (几乎 2016 年之后的 NVIDIA 独显都满足,老款如 GTX 750 Ti 也达标),主流显卡(RTX30/40 系、GTX16/20 系)全兼容,几乎不用担心里程碑。

直接打开 NVIDIA 官方算力表,搜索自己的显卡型号,看对应的Compute Capability(算力) 数值:

NVIDIA 显卡 CUDA 算力官方查询地址

  • 桌面显卡看GeForce 栏,笔记本显卡看GeForce Notebook栏;
  • 示例:RTX 3060 算力 8.6、GTX 1650 算力 7.5、RTX 4090 算力 8.9,都远大于 3.5,完美适配 CUDA 12.4。

3.4 Embedding下载模型

如果访问 HuggingFace 较慢,可以使用阿里云(阿里巴巴通义实验室(原达摩院))的 ModelScope 社区下载。

https://www.modelscope.cn/models/BAAI/bge-m3

1. 安装 modelscope 库

python 复制代码
uv add modelscope

2. 运行 Python 脚本下载: 创建一个临时的 Python 脚本(例如 download_bge.py)并运行:

python 复制代码
from modelscope.hub.snapshot_download import snapshot_download

# 下载模型到当前目录下的 models/bge-m3 文件夹
model_dir = snapshot_download('BAAI/bge-m3', cache_dir='D:/ai_models/modelscope_cache/models')
print(f"模型已下载到: {model_dir}")

3. .env配置

ini 复制代码
#embedding配置
# BGE-M3模型本地缓存/部署路径(本地加载模型时使用,指向ModelScope下载的模型目录)
BGE_M3_PATH=D:\ai_models\modelscope_cache\models\BAAI\bge-m3
# BGE-M3模型官方标识(ModelScope/HuggingFace通用,拉取模型时使用)
BGE_M3=BAAI/bge-m3
# BGE-M3运行设备,cuda:0表示使用第1块GPU,cpu表示使用CPU,cuda:N表示第N+1块GPU
BGE_DEVICE=cuda:0 
# BGE-M3是否开启FP16半精度推理,1=开启(GPU加速更高效),0=关闭(兼容低版本GPU/CPU)
BGE_FP16=1

4. 配置参数读取

文件:app.config.embedding_config.py

py 复制代码
# 导入核心依赖:数据类、环境变量读取、路径处理
from dataclasses import dataclass
import os
from dotenv import load_dotenv

# 提前加载.env配置文件(保持和原代码一致,只需执行一次)
load_dotenv()

# 定义Embedding配置(适配BGE-M3的所有配置,类名embedding_config)
@dataclass
class EmbeddingConfig:
    bge_m3_path: str  # 本地模型路径
    bge_m3: str       # 模型仓库标识
    bge_device: str   # 运行设备(cuda:0/cpu)
    bge_fp16: bool    # 是否开启半精度(1=True/0=False)

# 实例化配置对象,和原代码lm_config风格保持一致
embedding_config = EmbeddingConfig(
    bge_m3_path=os.getenv("BGE_M3_PATH"),
    bge_m3=os.getenv("BGE_M3"),
    bge_device=os.getenv("BGE_DEVICE"),
    # 特殊处理:将.env中的1/0转为布尔值,兼容常见的数字/字符串格式
    bge_fp16=os.getenv("BGE_FP16") in ("1", "True", "true", 1)
)

3.5 工具代码导入

文件:app.lm.embedding_utils.py

python 复制代码
from pymilvus.model.hybrid import BGEM3EmbeddingFunction
from app.core.logger import logger
from app.conf.embedding_config import embedding_config

# 模型单例对象,避免重复初始化
_bge_m3_ef = None

def get_bge_m3_ef():
    """
    获取BGE-M3模型单例对象,自动加载环境变量配置
    :return: 初始化完成的BGEM3EmbeddingFunction实例
    """
    global _bge_m3_ef
    # 单例模式:已初始化则直接返回,避免重复加载模型
    if _bge_m3_ef is not None:
        logger.debug("BGE-M3模型单例已存在,直接返回实例")
        return _bge_m3_ef

    # 从环境变量加载配置,无配置则使用默认值
    # 本地有可以使用本地地址! 没有使用 "BAAI/bge-m3" 会自动下载! 如果云端部署也可以使用url地址!
    model_name = embedding_config.bge_m3_path or "BAAI/bge-m3"
    device = embedding_config.bge_device or "cpu"
    use_fp16 = embedding_config.bge_fp16 or False

    # 打印模型初始化配置,便于问题排查
    logger.info(
        "开始初始化BGE-M3模型",
        extra={
            "model_name": model_name,
            "device": device,
            "use_fp16": use_fp16,
            "normalize_embeddings": True
        }
    )

    try:
        # 初始化BGE-M3模型,开启原生L2归一化(适配Milvus IP内积检索)
        # pymilvus.model.hybrid.BGEM3EmbeddingFunction ,在工程上最大的好处是: 
        # 和 Milvus 检索链路天然对齐 ,上线更稳更省事。
        _bge_m3_ef = BGEM3EmbeddingFunction(
            model_name=model_name,
            device=device,
            use_fp16=use_fp16,
            normalize_embeddings=True  # 模型原生对稠密+稀疏向量做L2归一化
        )
        logger.success("BGE-M3模型初始化成功,已开启原生L2归一化")
        # "它把所有向量拉伸到统一长度(模长为1),让我们能在数据库中放心使用最快的内积(IP)检索,既提速又不丢精度。"
        return _bge_m3_ef
    except Exception as e:
        logger.error(f"BGE-M3模型初始化失败:{str(e)}", exc_info=True)
        raise  # 向上抛出异常,由调用方处理


def generate_embeddings(texts):
    """
    为文本列表生成稠密+稀疏混合向量嵌入(模型原生L2归一化)
    :param texts: 要生成嵌入的文本列表,单文本也需封装为列表
    :return: 字典格式的向量结果,key为dense/sparse,对应嵌套列表/字典列表
    :raise: 向量生成过程中的异常,由调用方捕获处理
    """
    # 入参合法性校验
    if not isinstance(texts, list) or len(texts) == 0:
        logger.warning("生成向量入参不合法,texts必须为非空列表")
        raise ValueError("参数texts必须是包含文本的非空列表")

    logger.info(f"开始为{len(texts)}条文本生成混合向量嵌入")
    try:
        # 加载BGE-M3模型单例
        model = get_bge_m3_ef()
        # 模型编码生成向量,返回dense(稠密向量)+sparse(CSR格式稀疏向量)
        embeddings = model.encode_documents(texts)
        logger.debug(f"模型编码完成,开始解析稀疏向量格式,共{len(texts)}条")

        # 初始化稀疏向量处理结果,解析为字典格式(适配序列化/存储)
        processed_sparse = []
      	# 把模型输出的 CSR 稀疏矩阵 ,按"每条文本一行"拆成 {特征索引: 权重} 字典
        # - indices :非零元素的"列号(特征ID)"
		# - data :对应列号的权重值
		# - indptr :每一行在 indices/data 里的起止位置指针 
        # 数据示例:
        # indices = [3, 8, 20, 1, 9]
		# data    = [0.7, 0.2, 0.1, 0.6, 0.4]
        # indptr  = [0, 3, 5]
        # 获取对应的数据
        # - 第0条文本用 0:3 => indices=[3,8,20] , data=[0.7,0.2,0.1]
		# - 第1条文本用 3:5 => indices=[1,9] , data=[0.6,0.4]
        for i in range(len(texts)):
            # 提取第i个文本的稀疏向量索引:np.int64 → Python int(满足字典key可哈希要求)
            sparse_indices = embeddings["sparse"].indices[
                embeddings["sparse"].indptr[i]:embeddings["sparse"].indptr[i + 1]
            ].tolist()
            # 提取第i个文本的稀疏向量权重:np.float32 → Python float(适配JSON序列化/接口返回)
            sparse_data = embeddings["sparse"].data[
                embeddings["sparse"].indptr[i]:embeddings["sparse"].indptr[i + 1]
            ].tolist()
            # 构造{特征索引: 归一化权重}的稀疏向量字典
            sparse_dict = {k: v for k, v in zip(sparse_indices, sparse_data)}
            processed_sparse.append(sparse_dict)

        # 构造最终返回结果,稠密向量转列表(解决numpy数组不可序列化问题)
        result = {
            "dense": [emb.tolist() for emb in embeddings["dense"]],  # 嵌套列表,与输入文本一一对应
            "sparse": processed_sparse  # 字典列表,模型已做L2归一化
        }
        logger.success(f"{len(texts)}条文本向量生成完成,格式已适配工业级使用")
        return result

    except Exception as e:
        logger.error(f"文本向量生成失败:{str(e)}", exc_info=True)
        raise  # 不吞异常,向上传递让调用方做重试/降级处理


"""
核心设计亮点&适配说明:
1. normalize_embeddings=True 的价值:
- 检索更稳定 :不同文本长短、词频差异不会把分数拉偏。
- IP 可近似 cosine :向量都归一化后, Inner Product 和余弦相似度等价,Milvus 用 IP 检索就很合适。
- dense/sparse 都统一标尺 :混合检索时两路分数更容易做融合,不容易一边压死另一边。
- 减少异常高分 :防止"模长大"的向量仅靠长度拿高分。
2. 彻底解决NumPy类型做key问题:sparse_indices加.tolist(),将np.int64转为Python原生int,满足字典key的可哈希要求,无报错风险;
3. 稀疏值适配序列化:sparse_data加.tolist(),将np.float32转为Python原生float,支持JSON写入/接口返回/Milvus入库等所有场景;
4. 单例模式优化:模型仅初始化一次,避免重复加载耗时耗资源,提升批量处理效率;
5. 格式匹配业务调用:返回dense嵌套列表、sparse字典列表,与vector_result["dense"][0]/sparse_vector["sparse"][0]取值逻辑完美契合;
6. 分级日志覆盖:从模型初始化、向量生成到异常报错,全流程日志记录,便于生产环境问题排查;
7. 入参合法性校验:防止空列表/非列表入参导致的内部报错,提升工具类健壮性。
"""
相关推荐
AI英德西牛仔3 小时前
豆包导出 pdf 颜色不一样怎么办,选用 AI 导出鸭优化文档导出,结合行业白皮书数据解析色彩失真成因
人工智能·ai·chatgpt·pdf·deepseek·ai导出鸭
薰珞婷紫小亭子6 小时前
如何解决IEEE期刊LaTeX 模版编译pdf出现大空白的问题
经验分享·pdf
小马过河R7 小时前
Graph Engineering 深度解析:模型越强,越需要给它画好“地图”
人工智能·langchain·graph·ai工程化·harness·驾驭工程
jyOverQ7 小时前
LangGraph 记忆管理详解:短期记忆、长期记忆与 Runtime Context
python·langchain
做前端的娜娜子8 小时前
文本切片与召回(Chunk、Overlap 到混合检索)学习笔记
langchain·openai·掘金·金石计划
Hello-FPGA9 小时前
AI 如何自动通过 PDF 原理图生成管脚映射
人工智能·fpga开发·pdf
赵广陆9 小时前
企业实战:Milvues向量数据库实践
数据库·pycharm·langchain
阿部多瑞 ABU10 小时前
告别手工核图:基于 .NET + MuPDFCore 的 CAD 等轴测 PDF 材料表提取与新旧版本对比实战
后端·算法·ui·pdf·c#