RagFlow Full Text Query
rag\nlp
模块提供了 NLP 相关的功能,如分词、查询构造器等。前面我们已经介绍了 NLP 中的 RagTokenizer 分词器的实现。这一节我们来讲解与检索相关的功能实现。
1. NLP 模块
在具体讲解检索实现之前,我们先来看一下 NLP 这个模块的整体结构。
|
|
1.1 __init__.py
- NLP工具集合
核心功能:
- 编码检测与转换:自动检测文本编码格式,支持多种字符集
- 文档结构分析:
- 问题项目符号识别(如"第一问"、“第二条"等)
- 章节标题层次结构识别
- 目录页面自动移除
- 文本分块策略:
naive_merge
: 朴素合并策略,按token数量分块hierarchical_merge
: 层次化合并,基于文档结构分块naive_merge_with_images
: 支持图像的文本分块
- 多语言支持:中英文识别和处理
- 图像处理:图像拼接和文档位置信息处理
1.2 rag_tokenizer.py
- 智能分词器
核心功能:
- 中文分词:基于词典和统计的混合分词算法
- 英文处理:词干提取和词形还原
- 多语言融合:中英文混合文本的智能切分
- 细粒度分词:
fine_grained_tokenize
提供更精细的分词结果 - 词典管理:
- 支持用户自定义词典
- 词频统计和词性标注
- 动态词典加载
1.3 search.py
- 搜索引擎核心
核心功能:
- 检索策略:
- 向量检索 + 全文检索融合
- 重排序算法支持
- 分页和结果聚合
- 搜索结果处理:
- 高亮显示匹配内容
- 文档聚合统计
- 相似度得分计算
- 标签系统:
- 文档标签提取和匹配
- 查询标签分析
- 标签权重计算
- SQL查询:支持直接SQL检索
1.4 query.py
- 查询处理引擎
核心功能:
- 查询解析:
- 去除无关疑问词(“什么”、“如何"等)
- 中英文混合查询处理
- 查询词权重计算
- 混合检索:
- 文本匹配 + 向量相似度计算
- 多字段查询支持(标题、内容、关键词等)
- 同义词扩展查询
- 相似度计算:
hybrid_similarity
: 文本+向量混合相似度token_similarity
: 基于词汇的相似度
- 引用插入:自动为生成答案插入文档引用标记
1.5 synonym.py
- 同义词管理
核心功能:
- 同义词扩展:基于预构建词典的同义词查找
- 英文同义词:集成WordNet进行英文同义词扩展
- 动态更新:支持Redis实时同义词更新
- 查询增强:为搜索查询自动添加同义词,提高召回率
1.6 term_weight.py
- 查询权重计算
核心功能:
- TF-IDF计算:基于词频和逆文档频率的权重计算
- 命名实体识别:识别公司名、地名、学校名等实体类型
- 停用词过滤:移除无意义的常用词
- 词汇合并:将相关的短词合并为完整术语
- 权重标准化:对计算出的权重进行归一化处理
1.7 surname.py
- 姓氏识别
核心功能:
- 中文姓氏库:包含常见单姓和复姓
- 姓氏判断:
isit()
函数判断给定文本是否为中文姓氏 - 命名实体支持:为人名识别提供基础支持
从导入关系可以看到模块之间的依赖关系:
- search 依赖:
- query
- query 依赖:
- rag_tokenizer
- term_weight
- synonym
- term_weight 依赖:
- rag_tokenizer
从语义和依赖关系可以看到,ragflow 检索分成了两个模块:
- query
- search
这一节我们来学习 query 部分。
2. surname
surname 有一个包含常见姓名的词典,并提供了一个 isit 查询函数。
|
|
3. synonym
synonym 主要用于同义词扩展。英文同义词扩展使用 wordnet。
|
|
3.1 wordnet
wordnet
是 NLTK(自然语言工具包,Natural Language Toolkit)中最重要的词汇资源之一,它本身是一个由普林斯顿大学维护的 WordNet 词汇数据库的 Python 接口。
数据结构:同义词集(Synset)
- Synset(同义词集) 是 WordNet 的核心单元,每个 Synset 表示一组意义相同或相近的词。
- 例如:
wordnet.synsets("dog")
会返回多个 Synset,因为 “dog” 既可以指动物、也可以指俚语“卑鄙的人”等。
|
|
以 frump.n.01
为例:
-
frump
- 词元(lemma name),即同义词集中最典型的一个单词。
- 这里就是单词 frump(意为“邋遢的人”)。
-
.n
-
词性(part of speech, POS) 缩写:
n
→ noun(名词)v
→ verb(动词)a
→ adjective(形容词)s
→ adjective satellite(形容词卫星,修饰性形容词,依附于某些形容词)r
→ adverb(副词)
-
-
.01
- 义项编号(sense number),表示这是该词在该词性下的第几个含义。
01
就是第一个含义,02
就是第二个,以此类推。- 例如
dog.n.01
表示“家犬”,而dog.n.02
则可能表示“讨厌的人”。
词义和定义
每个 Synset 提供:
- 定义(
definition()
) - 例句(
examples()
) - 词元(lemma),即该同义词集中的所有单词(
lemmas()
)
|
|
语义关系(WordNet 的亮点)
WordNet 不只是同义词库,还存储了丰富的语义网络:
- 同义词(synonyms):在一个 synset 内
- 反义词(antonyms):通过
lemma.antonyms()
- 上位词(hypernyms):更一般的概念 例:dog → animal
- 下位词(hyponyms):更具体的概念 例:dog → retriever
- 部分整体关系(meronyms / holonyms) 例:wheel 是 car 的部分(meronym)
|
|
词形变换
可以把单词还原到 原型形式(lemma form):
|
|
相似度计算
WordNet 允许基于 层级结构计算词语的相似度:
- 路径相似度(path_similarity)
- Wu-Palmer 相似度(wup_similarity)
|
|
4. term_weight
term_weight 用于:
- TF-IDF计算:基于词频和逆文档频率的权重计算
- 命名实体识别:识别公司名、地名、学校名等实体类型
- 停用词过滤:移除无意义的常用词
- 词汇合并:将相关的短词合并为完整术语
- 权重标准化:对计算出的权重进行归一化处理
|
|
3.1 权重计算
weights()
函数的目的是计算 query term weighting(查询词加权),类似 BM25 / TF-IDF 的思想,结合了 NLP 特征(NER、词性、词频等),目的是衡量每个 token 对查询或检索的重要性。
- 输入一组 token(tks),
- 给每个 token 计算一个 权重(数值),
- 这些权重归一化(归一化后总和=1),作为词的重要性分布。
这和搜索引擎里 查询词加权 是一样的思想:哪些词更关键、应该更大程度影响匹配。
(1) idf(s, N)
|
|
- 原理:典型 Inverse Document Frequency(逆文档频率)。
- 语义:如果某词在很多文档出现(频率高),它的信息量低 → 权重应该小;如果很少出现,区分度强 → 权重应该大。
- 这里加了 10 平滑,避免值太小/为负。
(2) freq(t)
- 基于 token 的出现频率(可能是全局词表里的 term frequency 或 tokenizer 提供的频率)。
- 高频词 → 信息量小 → 最后权重会降低。
- 如果没查到频率,还做了 fine-grained tokenization(进一步拆词),用子词的频率估计。
这保证了:稀有词、长词(低频) → 权重提升。
(3) df(t)
- 类似文档频率(document frequency)。
- 如果词在很多文档里都出现过 → 权重低。
- 如果是英文短词(like “the”, “is”)→ 默认权重给到很低(300,表示非常常见)。
- 如果是长词但不在词典里,会拆成子词,取子词的 DF。
这就是 逆文档频率的近似。
(4) ner(t)
-
用命名实体识别标签(NER)来调整权重。
-
例如:
- 人名(
firstnm
)权重低(1), - 公司、地名、股票、学校(
corp/loca/sch/stock
)权重高(3), - 数字(
[0-9,.]{2,}
)→ 2。
- 人名(
语义:实体通常是关键信息,尤其是机构、地点,比停用词或功能词更重要。
(5) postag(t)
- 基于词性标注(POS tag)。
- 代词、副词 → 权重降低(0.3)。
- 名词、专有名词 → 权重升高(2-3)。
- 数字也提升到 2。
语义:符合 IR 经验:名词(尤其实体名词)比虚词更重要。
权重计算公式
|
|
-
分两部分:
idf1
: 基于词频 (freq) 的逆频率idf2
: 基于文档频率 (df) 的逆频率- 混合:
0.3 * idf1 + 0.7 * idf2
(文档频率更重要)
-
再乘上 NER 与 POS 权重
- 保证实体/名词更突出,虚词/高频词更弱。
-
归一化
- 把所有权重加起来再归一化。
- 这样保证返回结果是个概率分布,方便后续计算(比如句子相似度时做加权)。
总结
weights()
是一个 混合启发式的 term weighting 方案:
- 低频稀有词 → 权重高(信息量大)。
- 高频常见词/停用词 → 权重低(信息量小)。
- 命名实体 → 权重高(通常是关键信息)。
- 名词/数字 → 权重高;虚词 → 权重低。
- 归一化为概率分布 → 方便做向量组合或相似度计算。
它结合了 IR(TF-IDF / BM25 思想) 和 NLP(NER+POS 特征),使得 query 或文本表示更符合语义检索的需要。
5. query
query.py
实现查询处理引擎,核心功能:
- 查询解析:
- 去除无关疑问词(“什么”、“如何"等)
- 中英文混合查询处理
- 查询词权重计算
- 混合检索:
- 文本匹配 + 向量相似度计算
- 多字段查询支持(标题、内容、关键词等)
- 同义词扩展查询
- 相似度计算:
hybrid_similarity
: 文本+向量混合相似度token_similarity
: 基于词汇的相似度
- 引用插入:自动为生成答案插入文档引用标记
|
|
5.1 question
question
函数的目标是:
- 接收一个用户的查询文本(
txt
),可能是中文、英文,或中英文混合。 - 输出一个 搜索引擎能理解的查询表达式(
MatchTextExpr
)和关键词列表。
核心思路是:把自然语言查询拆分成关键 token,计算权重,加上同义词扩展,再构建搜索表达式。
(1)中英文分开处理
|
|
- add_space_between_eng_zh:确保中英文混排的词能被 tokenizer 正确拆分。
- strQ2B:全角字符 → 半角,统一字符宽度。
- tradi2simp:繁体 → 简体,统一中文字符。
- rmWWW:去掉无意义的疑问词/虚词,如 “请问”“吗”“what”等。
目的:把原始查询文本变成干净、标准化的 token 流。
(2)判断中文或英文
|
|
- 英文主要用 空格分词 + 同义词扩展 + n-gram 短语组合
- 中文需要 细粒度分词 + 权重计算 + 同义词扩展
原理:英文本身是空格分隔,中文需要 tokenizer 切分;而且中文同义词、权重对匹配影响更大。
(3)英文查询构建
|
|
- tokenize:把英文句子拆成单词。
- weights:给每个单词一个权重(重要性)。
- syn.lookup:查找同义词扩展,例如
"car"
→"automobile"
。
|
|
- 构造查询表达式
(tk^weight OR syns)
- 每个 token 都有自己的权重,用 ^weight 来告诉搜索引擎这个词重要性。
目的:增强英文查询的召回能力,让同义词也能匹配。
(4)中文查询构建
中文逻辑更复杂:
-
分词 + 细粒度切分
1
tt = self.tw.split(txt)[:256]
- 使用
term_weight.Dealer.split
得到重要词,避免无意义词干扰。
- 使用
-
计算 token 权重
1
twts = self.tw.weights([tt])
- 权重 = 词频 + 文档频率 + NER + 词性等。
- 目的:更重要的词在查询中权重更高。
-
同义词扩展
1
tk_syns = self.syn.lookup(tk)
- 中文同义词也被加入查询。
-
组合短语、模糊匹配
1 2
if sm: tk = f'{tk} OR "%s" OR ("%s"~2)^0.5' % (" ".join(sm), " ".join(sm))
"%s"~2
:允许两个词之间有一个词间隔(近似匹配)。- 避免精确匹配太严格导致漏匹配。
目的:中文分词的模糊匹配 + 同义词 + 权重组合,让搜索既准确又召回高。
(5)最终组合查询
|
|
- 把每个 token 或 token 组合 OR 起来,构成完整查询表达式。
- minimum_should_match:至少匹配多少 token 才算命中,提高精确度。
- query_fields:定义查询的索引字段和权重。
核心思想:搜索表达式 = token OR 同义词 OR 短语模糊匹配 + 权重
总结
层级 | 原理 | 作用 |
---|---|---|
预处理 | 中文简繁转换、全角半角、空格分隔 | 清洗文本,标准化 |
分词 | 中文/英文分开处理 | 得到搜索的最小单元 token |
权重 | 词频、文档频率、NER、词性 | 重要词更优先匹配 |
同义词扩展 | 英文 WordNet,中文词典 | 提升召回,覆盖同义表达 |
短语 & 模糊匹配 | "word1 word2"~2 |
保留上下文顺序信息,允许近似匹配 |
组合查询 | OR 拼接,minimum_should_match | 搜索引擎可理解的表达式,提高召回和精度 |
总体来说,就是 把自然语言查询转化为搜索引擎友好的加权 token 查询,并且考虑同义词、短语和模糊匹配。