LlamaIndex 系列【31】检索增强策略:命名实体识别(NER)

文章目录

  • [1. 是什么?](#1. 是什么?)
  • [2. 案例演示](#2. 案例演示)
    • [2.1 集成 spaCy](#2.1 集成 spaCy)
    • [2.2 抽取演示](#2.2 抽取演示)
    • [2.3 抽取入库](#2.3 抽取入库)
      • [2.3.1 模拟语料](#2.3.1 模拟语料)
      • [2.3.2 模型抽取 & 写入节点元数据](#2.3.2 模型抽取 & 写入节点元数据)
    • [2.4 构建检索器](#2.4 构建检索器)
    • [2.5 执行查询](#2.5 执行查询)
    • [2.6 使用 NER 标准组件](#2.6 使用 NER 标准组件)

1. 是什么?

命名实体识别 (Named Entity Recognition)用于识别查询语句内特定类型的信息并进行分类,以此实现更精准的检索与筛选策略。

通俗解释 :NER 就是从用户的问题文本里,自动把地点、人名、时间、角色、机构这类专有名词抽取出来并打上标签。在 RAG 检索场景里,抽取出的实体可以作为元数据,过滤向量库,缩小检索范围,让检索结果更贴合用户查询。

执行流程:

  1. 用户输入查询 Query
  2. GLiNER 模型(NER 开源模型)对 Query 做零样本命名实体识别,提取自定义实体
  3. 根据抽取到的实体,构造元数据查询条件
  4. 在文档知识库中,使用元数据过滤,筛选出候选文档子集(缩小检索范围)

NER 本身不是检索器,它的价值是给每个节点打上实体标签。

2. 案例演示

最小实现演示,所以效果一般...

python 复制代码
from pathlib import Path
import tempfile

from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.vector_stores.types import (
    FilterCondition, FilterOperator, MetadataFilter, MetadataFilters,
)
from llama_index.embeddings.openai_like import OpenAILikeEmbedding

2.1 集成 spaCy

spaCy 是工业级、面向生产环境的 Python 开源 NLP 库,2015 年发布,主打高性能、开箱即用的一体化 NLP 流水线。

核心能力(内置流水线组件):

  1. 分词 Tokenization:语言学驱动分词,支持标点、缩写、复杂文本拆分,比基础空格分词更智能
  2. 词性标注 POS Tagging:标记词语词性(名词、动词、形容词等)
  3. 词形还原 Lemmatization :把单词还原为词根原形(如 running → run)
  4. 依存句法分析 Dependency Parsing:识别词与词之间语法依赖关系,可可视化句法树
  5. 命名实体识别 NER:提取实体,如人名、地点、机构、日期、产品,支持自定义实体训练
  6. 句子分割:自动切分句子边界
  7. 词向量与文本相似度 :预训练词向量,计算 Token/Span/Doc之间语义相似度
  8. 文本分类、实体链接 :v3 版本原生支持 HuggingFace Transformer(BERT 等)接入,支持多任务学习

一次性安装准备:

python 复制代码
    pip install spacy
    python -m spacy download zh_core_web_sm     # 中文小模型,约 40MB

输出提示:

python 复制代码
Requirement already satisfied: catalogue<2.1.0,>=2.0.3 in E:\ProgramData\miniconda3\envs\LlamaIndexProject\Lib\site-packages (from srsly<3.0.0,>=2.3.0->spacy-pkuseg<2.0.0,>=1.0.0->zh-core-web-sm==3.8.0) (2.0.10)
Installing collected packages: spacy-pkuseg, zh-core-web-sm
Successfully installed spacy-pkuseg-1.0.1 zh-core-web-sm-3.8.0
✔ Download and installation successful
You can now load the package via spacy.load('zh_core_web_sm')

查看模型装了哪些组件、认识哪些实体类型:

python 复制代码
import spacy

nlp = spacy.load("zh_core_web_sm")

print("=" * 70)
print("模型流水线组件:", nlp.pipe_names)          # 例如 ['tok2vec','ner',...]

ner = nlp.get_pipe("ner")
print(f"模型认识的实体标签({len(ner.labels)}类): {sorted(ner.labels)}")

输出示例:

python 复制代码
模型流水线组件: ['tok2vec', 'tagger', 'parser', 'attribute_ruler', 'ner']
模型认识的实体标签(18类): ['CARDINAL', 'DATE', 'EVENT', 'FAC', 'GPE', 'LANGUAGE', 'LAW', 'LOC', 'MONEY', 'NORP', 'ORDINAL', 'ORG', 'PERCENT', 'PERSON', 'PRODUCT', 'QUANTITY', 'TIME', 'WORK_OF_ART']

中文模型常见标签:

python 复制代码
PERSON=人名  ORG=机构名  GPE=行政区划/地名  LOC=地点  ...

2.2 抽取演示

准备文本进行对象抽取:

python 复制代码
def extract_entities(text: str) -> list:
    """纯模型抽取:跑流水线 → doc.ents 按类型白名单过滤 → 去重排序。"""
    entities = {ent.text for ent in nlp(text).ents if ent.label_ in KEEP_LABELS}
    return sorted(entities)

SAMPLES = [
    "华为在深圳发布了新款昇腾芯片。",
    "明珠手机是华星科技发布的旗舰手机。",
]
for node in nodes:
    ents = extract_entities(node.get_content())
    node.metadata["entities"] = "、".join(ents)
    node.metadata["primary_entity"] = ents[0] if ents else "未知"
    print(f"\n节点 [{node.metadata.get('file_name')}] 入库实体: {ents}")

输出示例(效果一般):

python 复制代码
文本: 华为在深圳发布了新款昇腾芯片。
    实体: 华为     类型: GPE(地名)
    实体: 深圳     类型: GPE(地名)
    实体: 昇腾     类型: PERSON(人名)
文本: 明珠手机是华星科技发布的旗舰手机。
    实体: 华星     类型: LOC(地点)

2.3 抽取入库

2.3.1 模拟语料

python 复制代码
PHONE_DOC = (
    "明珠手机是华星科技于2023年发布的旗舰折叠屏手机,搭载自研天璇芯片,"
    "配备6.8英寸柔性折叠屏与5000mAh电池,影像系统由星辰光学调校。"
)
PAD_DOC = (
    "雷克平板是华星科技旗下雷克品牌2024年发布的大屏平板,同样搭载天璇芯片,"
    "配备12.4英寸护眼屏幕与8000mAh电池,主打移动办公与手写笔记。"
)
tmp_dir = Path(tempfile.mkdtemp())
(tmp_dir / "a").mkdir()
(tmp_dir / "a" / "phone.txt").write_text(PHONE_DOC, encoding="utf-8")
(tmp_dir / "b").mkdir()
(tmp_dir / "b" / "pad.txt").write_text(PAD_DOC, encoding="utf-8")

2.3.2 模型抽取 & 写入节点元数据

加载数据,对每个节点进行对象抽取,并添加元数据:

python 复制代码
documents = SimpleDirectoryReader(
    str(tmp_dir),
    recursive=True,   # 坑:默认只扫顶层目录、不进子目录------语料在 a/ b/ 子目录里,
                      # 不加 recursive=True 会报 "No files found"
).load_data()
nodes = SentenceSplitter(chunk_size=512).get_nodes_from_documents(documents)

for node in nodes:
    # 直接遍历 doc.ents,保留类型标签:kept = [(实体文本, 类型), ...]
    kept = [(ent.text, ent.label_) for ent in nlp(node.get_content()).ents
            if ent.label_ in KEEP_LABELS]
    # metadata 里存两个字段(键名 = 第 5 节过滤器用的字段名,别改):
    #   entities       ------ 全部实体(展示用)
    #   primary_entity ------ 第一个实体(过滤字段,类型信息另在打印里看)
    node.metadata["entities"] = "、".join(t for t, _ in kept)
    node.metadata["primary_entity"] = kept[0][0] if kept else "未知"

    print(f"\n节点 [{node.metadata.get('file_name')}]")
    if kept:
        for t, l in kept:   # 逐个打印 实体 + 类型(和第 2 节同样的格式)
            print(f"    实体: {t:<6} 类型: {l}({LABEL_CN.get(l, '')})")
    else:
        print("    (模型未抽到实体)")
    print(f"    → 入库主实体: {node.metadata['primary_entity']}")

每个节点入库的元数据:

python 复制代码
节点 [phone.txt]
    实体: 华星     类型: GPE(地名)
    → 入库主实体: 华星

节点 [pad.txt]
    实体: 雷克     类型: LOC(地点)
    实体: 华星科技旗下雷克品牌 类型: ORG(机构名)
    → 入库主实体: 雷克

2.4 构建检索器

从节点创建索引存储,并添加一个元数据过滤器:

python 复制代码
index = VectorStoreIndex(nodes=nodes)

filters = MetadataFilters(
    filters=[
        MetadataFilter(
            key="primary_entity",
            value="雷克",
            operator=FilterOperator.EQ,   # 精确等于;匹配多个实体时用 IN
        )
    ]
)
retriever = index.as_retriever(similarity_top_k=4, filters=filters)

只在 primary_entity == "雷克" 的块里检索(其他块根本不进候选)

2.5 执行查询

python 复制代码
QUERY = "雷克平板哪里产的?"
print("\n" + "=" * 70)
print(f"查询:{QUERY}   [过滤条件: primary_entity == 雷克]")
for i, ns in enumerate(retriever.retrieve(QUERY), 1):
    ent = ns.node.metadata.get("primary_entity", "?")
    print(f"  ({i}) 实体={ent} | {ns.node.get_content()[:30]}...")

输出示例:

python 复制代码
  (1) 实体=雷克 | 雷克平板是华星科技旗下雷克品牌2024年发布的大屏平板,同样...

2.6 使用 NER 标准组件

LlamaIndex 官方把 spaCy NER 封装成标准组件的版本,可以换成官方组件的写法:

python 复制代码
# pip install llama-index-extractors-entity spacy
from llama_index.extractors.entity import EntityExtractor
from llama_index.core.ingestion import IngestionPipeline

pipeline = IngestionPipeline(transformations=[
    SentenceSplitter(chunk_size=512),          # 切分
    EntityExtractor(model="zh_core_web_sm",    # 官方 NER 组件(内部就是 spaCy)
                    label_entities=True),      # 实体带类型:"华星科技:ORG"
])
nodes = pipeline.run(documents=documents)      # 切分+抽实体自动串起来,写入 metadata
相关推荐
小盆女神节奶粉7 分钟前
让 Agent 在沙箱里写代码跑代码,产物进 OSS
langchain·agent
wang_yb7 分钟前
Scikit-Learn实战:5步搞定PCA降维
ai·databook
武子康23 分钟前
两台 A6000,LingBot 应该先验哪条路径?
人工智能·后端·agent
Anastasiozzzz29 分钟前
深度拆解 Jev 模型:扒开“系统一模型”的底层工作原理
ai·语言模型
codigger29 分钟前
Redis 正式接入 AI:当"最懂速度的数据库"开始解决"记忆问题"
redis·分布式·后端·ai·向量检索
七夜zippoe30 分钟前
Agent 编排引擎设计:任务 DAG、条件分支与循环控制
ai·agent·循环控制·条件分支·任务dag
涛思数据(TDengine)36 分钟前
栖息地 AI 超恒气候系统用 TDengine 支撑全屋环境品质实时监测与历史追溯
人工智能·ai·时序数据库·tdengine·工业ai
ndsc_d38 分钟前
2026年有哪些好用的AI UI设计工具?主流工具功能和适用场景对比
前端·人工智能·ui·ai·设计师·ai ui·ai ui工具
ofoxcoding1 小时前
借助 CLAUDE.md 约束 Sonnet 5.5 多文件重构行为的提示词实践
大数据·elasticsearch·ai·重构
c萱1 小时前
AI产品经理——04RAG 检索增强生成
ai·aigc·产品经理·ai编程·ai-native