超音数学习笔记

 SuperSonic主要流程

  1. 原始文本先经过Schema Mapper生成SchemaMapInfo;

  2. 经过Schema Parser生成SemanticQuery和SemanticParseInfo(包含S2SQL);其中规则/LLM解析均生成S2SQL;

  3. S2SQL经过SemanticCorrector对S2SQL进行修正,生成修正S2SQL;

  4. 修正后的S2SQL再经过Semantic层转换为执行SQL,查询物理数据模型获取结果数据;

一、Mapper功能

1.作用

SchemaMapper用于从输入文本中,解析所有可能存在的SchemaElement;并将这些SchemaElement数据通过SchemaMapInfo将传递到下一步的Parser中,作为后续Parser解析识别使用;

2.分类

以访问次数为例:

基于Embedding的语义识别(EmbeddingMapper):

浏览量与访问次数语义近似

前后缀识别(HanlpDictMapper):

前缀

后缀

模糊识别(FuzzyNameMapper):

3.SchemaMapper使用的识别器

com.tencent.supersonic.chat.api.component.SchemaMapper=\
    com.tencent.supersonic.chat.mapper.EmbeddingMapper,\
    com.tencent.supersonic.chat.mapper.HanlpDictMapper,\
    com.tencent.supersonic.chat.mapper.FuzzyNameMapper,\
    com.tencent.supersonic.chat.mapper.QueryFilterMapper,\
    com.tencent.supersonic.chat.mapper.EntityMapper
  • EmbeddingMapper:支持语义层面映射,获取到输入文本中更多的语义相似的词匹配。
  • HanlpDictMapper:借用Hanlp词典能力,从纯文本角度按前后缀方式识别文本中的相似词。
  • FuzzyNameMapper:通过模糊搜索识别更多的词,以解决HanlpDictMapper只支持前后缀搜索的的不足。
  • QueryFilterMapper:用于过滤查询条件,去掉无效或冗余的查询条件。
  • EntityMapper:处理实体数据的映射,识别文本中的实体名称。

4.工作流程

输入数据:用户输入的查询文本

输出数据:SchemaElement数据

SchemaElement主要分为几个类型:model(模型)、metric(指标)、dimension(维度)、value(维度值)、entity(实体名)

输入文本的接收与预处理
  • 接收文本:接收用户输入的查询文本。
  • 分词处理:使用HanLP工具进行分词处理,将输入文本分割为一个个词语。
②词典数据利用
  • 词典数据格式:词典主要由word、nature、frequency,三类组成,其中nature构造如下; 

 同一个word,如果frequency不同,可以选取frequency较大的作为识别的内容;

  • 实时词典构造
    • 数据更新
      • 定期(每1分钟)对比model、metric、dimension、value(仅在界面配置的维度值数据)、entity元数据,如果有变化则更新词典。
      • 监听model、metric、dimension、value、entity元数据变更事件,实时更新词典。
    • 具体实现
com.tencent.supersonic.knowledge.listener.ApplicationStartedListener    //初始化
com.tencent.supersonic.knowledge.dictionary.builder.ModelWordBuilder
com.tencent.supersonic.knowledge.dictionary.builder.MetricWordBuilder
com.tencent.supersonic.knowledge.dictionary.builder.DimensionWordBuilder
com.tencent.supersonic.knowledge.dictionary.builder.ValueWordBuilder
com.tencent.supersonic.knowledge.dictionary.builder.EntityWordBuilder
  • 离线词典构造
    •  一般生产环境下,维度值数据量大且不像维度名、指标名等可以实时获取变更;因此,对于维度值的构造,采用离线方式;
    • 定期或主动触发离线构造任务,通过查询语义层获取维度值,并按词典格式写入到文件中。
    • 文件存储方式可支持本地文件、HDFS等。
IOAdapter=com.tencent.supersonic.knowledge.dictionary.HadoopFileIOAdapter
③探测过程
public List<T> detect(QueryContext queryContext, List<Term> terms, Set<Long> detectModelIds) {
    // QueryContext queryContext:包含查询的上下文信息,例如用户的查询文本。
    // List<Term> terms:要检测的术语列表。
    // Set<Long> detectModelIds:用于检测的模型ID集合。
    Map<Integer, Integer> regOffsetToLength = getRegOffsetToLength(terms);
    // 该行代码通过调用 getRegOffsetToLength 方法获取术语的偏移量和长度的映射。
    String text = queryContext.getRequest().getQueryText();
    // 从查询上下文中提取用户的查询文本。
    Set<T> results = new HashSet<>();
    Set<String> detectSegments = new HashSet<>();
    // 用于存储检测到的结果和检测段。
    
    for (Integer startIndex = 0; startIndex <= text.length() - 1; ) { //遍历查询文本的每个起始索引。
        for (Integer index = startIndex; index <= text.length(); ) { //从当前起始索引开始,逐步向后移动,检测文本段。
            int offset = mapperHelper.getStepOffset(terms, startIndex);
            index = mapperHelper.getStepIndex(regOffsetToLength, index);
            // 通过 mapperHelper 获取当前的偏移量和更新索引。
            if (index <= text.length()) {
                String detectSegment = text.substring(startIndex, index);
                detectSegments.add(detectSegment);
                // 提取当前的检测段,并将其添加到 detectSegments 集合中。
                detectByStep(queryContext, results, detectModelIds, startIndex, index, offset);
                // 调用 detectByStep 方法进行具体的检测操作。
            }
        }
        startIndex = mapperHelper.getStepIndex(regOffsetToLength, startIndex);
        //更新起始索引,以便进行下一轮检测。
    }
    
    detectByBatch(queryContext, results, detectModelIds, detectSegments); //在所有段落检测完成后,进行批量检测。
    return new ArrayList<>(results);    //将结果集合转换为列表并返回。
}

语义识别(EmbeddingMapper)

  • 分词结果:通过HanLP获取的分词结果。
  • 向量查询:将所有分词结果导入向量数据库,进行批量查询,获取最相似的数据。
  • 模型过滤:按model进行分类。
  • 相似度过滤:按配置的相似度阈值进行过滤。
  • 结果写入:将识别结果写入到QueryContext中的mapInfo中。

上下文识别 (HanlpDictMapper)

  • 分词结果:通过HanLP获取的分词结果。
  • Trie树查询:通过Trie树进行前后缀匹配。
  • 编辑距离计算:计算分词结果与词典中词的编辑距离,按配置的相似度阈值进行过滤。
  • 结果写入:将识别结果写入到QueryContext中的mapInfo中。

 模糊识别(FuzzyNameMapper)

  • 分词结果:通过HanLP获取的分词结果。
  • 数据库模糊搜索:从数据库中进行模糊搜索。
  • 编辑距离计算:计算分词结果与数据库中词的编辑距离,按配置的相似度阈值进行过滤。
  • 结果写入:将识别结果写入到QueryContext中的mapInfo中。
④结果去重
  • 结果汇总:各个Mapper识别到的SchemaElement按model进行分类,统一添加至QueryContext中的mapInfo中。
  • 去重处理:在添加时进行对数据去重处理,避免重复识别。
⑤结果输出
  • 最终生成的SchemaElement通过QueryContext传递给后续的Parser进行进一步解析和处理。

二、Parser功能

1.作用

SemanticParser接收用户输入文本Mapper映射结果,并抽取语义信息SemanticParseInfo封装到SemanticQuery,用于后续SQL生成和前端信息展示。

2.分类

Parser主要分为SQLPlugin两大类:

  • SQL类型的Parser用于将用户输入的文本以及Mapper阶段探测识别生成的SchemaMapInfo转化为S2SQL。
  • Plugin类型的Parser主要用来根据用户文本对Plugin做召回。

根据解析方式的不同,SQL类型的Parser又可分为LLM两种类型

  • 规则类型的Parser根据常见的数据查询场景内置了一些规则,并基于这些内置的规则和SchemaMapInfo来生成解析结果。
  • LLM Parser则直接将用户输入的文本以及相关SchemaElement交给LLM来生成解析结果。

Plugin类型的Parser则主要通过Embedding或者FunctionCall的方式来召回一些插件,插件可以是一个网页,也可以是一个HTTP服务。

3.SqlParser

LLMSqlParser

输入参数:主要包含查询文本、识别的value取值linking、识别的modelName、以及相关的中文字段名;

输出参数:主要是大模型解析后的S2SQL;

LLMSqlParser通过把Mapper阶段识别到的相关SchemaElement提供给LLM作为先验知识的方式来生成S2SQL,主要实现原理如下:

  1. 首先进行前置判断,由于LLM Parser需要请求大模型,耗时较长,因此会首先通过SatisfactionChecker来判断是否已有得分较高且满足阈值(参考SemanticParseInfo中的score字段)的Query,若已有,则进行跳过。

  2. 其次进行数据模型的选择,由于Mapper阶段从文本中可能识别得到归属于多个数据模型的SchemaElement,因此在请求大模型之前,需要先根据规则(这个规则可以通过SPI的方式自行定义,如选择包含SchemaElement最多的数据模型)选择得到最有可能的模型。

  3. 开始组装模型请求结构体,根据步骤2得到的数据模型,获取这个数据模型三个部分的数据:

    a. Mapper阶段识别到的指标和维度

    b. 根据使用次数统计排序得到的TOP-N个指标和维度

    c. Mapper阶段识别到的维度值(考虑到数据隐私,可通过系统设置中的开关来决定是否要把值传递给模型)

  4. 请求大模型,得到大模型解析到的S2SQL,若得到多条S2SQL,则进行去重并为每条S2SQL生成一个Query。

RuleSqlParser

RuleSqlParser通过规则的方式来生成S2SQL,通过这种方式来生成S2SQL的好处也显而易见,那就是快速而又成本低,主要实现原理如下:

1.如下图,系统中首先内置了指标标签两种类型的共8种查询模式,以及规定了每种查询模式要求文本必须包含的元素。

 2.当用户文本经过Mapper识别成SchemaMapInfo之后,RuleSqlParser则可以根据生成的SchemaMapInfo来判断是否满足某个查询模式的要求,若满足,则生成一个Query,放入候选Query列表。

其它辅助Parser

其它辅助Parser主要用于对RuleSqlParser做辅助增强。 

ContextInheritParser是一个基于规则实现上下文的Parser,当RuleSqlParser所得到的SchemaMapInfo不足以构成上述任何一个查询模式的时候,ContextInheritParser就开始发挥作用了,它把当前会话里上一次成功问询的的SchemaMapInfo拿出来,并和本次的SchemaMapInfo做一次合并,然后再去挨个判断一下是否满足上述哪个查询模式,若满足,则同样生成一个Query,并放入候选Query列表。 

TimeRangeParser主要用来解析文本中涉及到的时间,通过正则xk-time这个开源时间解析工具来达到时间解析的目的。 

AggregateTypeParser则是通过正则的方式来识别文本中涉及到的聚合算子意图词。

AgentCheckParser用于校验,判断当前生成的候选Query是否满足当前所选Agent的限定范围。

QueryTypeParser

QueryTypeParser主要做了两件事情。第一件事情是为每个规则类型的Query根据SemanticParseInfo中的结构体信息生成S2SQL,便于后续解析执行,第二件事情是根据S2SQL中的字段来为Query划分模式。划分规则如下: 

ID模式: 主要用于ID查询实体的场景,如果S2SQL中含有对主键(在创建数据源的时候指定的主键并配置了实体别名)的过滤就为ID模式

标签模式: 主要用于标签圈选实体的场景,如果where子句中的字段都为标签,则为标签模式 

指标模式: 主要用于指标计算场景,可根据维度来进行过滤和下钻,若不满足以上两种模式,且S2SQL中包含指标,则为指标模式

4.PluginParser

Plugin注册

用户在页面上录入一个Plugin之后,会通过监听机制,注册到向量数据库。由于向量库的文本ID不可重复,因此注册的时候需要根据系统内置的计算方式,为Plugin的每个预设问题分配一个唯一ID,从而方便在进行召回的时候,根据预设问题的ID反向计算得到Plugin的ID。

PluginParser

PluginParser是一个插件召回的抽象类,它定义了召回插件的基本流程和构造Query等一些公共方法。主要流程如下:

  1. 首先判断前置条件,Embedding和FunctionCall的前置条件各不相同,因此定义为抽象模板方法。

  2. 进行Plugin召回,Embedding和FunctionCall的召回方式也各不相同,因此也定义为抽象模板方法。

  3. 若有满足条件的Plugin成功被召回,则构造为一个Query

EmbeddingRecallParser

EmbeddingRecallParser是PluginParser的其中一种实现,主要定义了EmbeddingRecall所需要的前置条件和具体召回实现。

a. 前置条件:系统中当前助理下可被召回的Plugin不为空。

b. 具体召回实现:

  1. 把用户输入的文本去请求向量数据库。

  2. 从向量数据库召回多个(召回数量可配置)跟用户输入文本相似的Plugin预设问题,并按相似度进行排序

  3. 按上面Plugin注册提到的ID计算方式,根据预设问题的ID反向计算得到插件的ID。

  4. 对召回的Plugin进行校验,由于每个Plugin在页面配置的时候设定了必要的参数,因此需要把SchemaMapInfo中识别到的Schema Element和Plugin的参数进行比对,以判断当前识别到的Element能否构成Plugin的参数。假如在Plugin定义中,name这个维度的值是这个召回所必要的参数,那么就需要判断SchemaMapInfo是否包含name这个维度的值,如Alice。

  5. 选中一个满足参数条件的且相似度最高的Plugin。

FunctionCallParser

FunctionCallParser是PluginParser的另一种实现,它和EmbeddingRecallParser相比略有不同,主要不同点在于FunctionCall的方式耗时较长,成本较高且无需注册,因此前置条件和召回方式都略有不同。

a. 前置条件: 系统中当前助理下可被召回的Plugin不为空且暂时还没有满足SatisfactionChecker条件的Query。

b. 具体召回实现:

  1. 获取到当前助理下所有可被用于召回的插件,并进行参数校验

  2. 若校验之后只剩一个插件,则直接返回这个插件,否则把多个Plugin拼装FunctionCall结构体,请求FunctionCall

  3. 得到返回结果,由于FunctionCall只会返回唯一一个结果,因此直接选中返回

 三、Corrector功能

1.作用

经过规则/LLM解析生成的S2SQL(尤其是LLM解析),常常出现不符合语义、信息展示不全、日期缺失等问题;Semantic Corrector主要功能是对S2SQL进行修正,使查询结果更符合用户要求。

2.分类 

SuperSonic中的Corrector有以下几类,SchemaCorrector、SelectCorrector、WhereCorrector、GroupByCorrector、HavingCorrector;

3.SchemaCorrector

    由于大模型生成的S2SQL,在字段名、维度值、以及聚合函数等上生产错误;该Corrector主要作用是修正在Schema相关侧的S2SQL的错误;

a.字段名修正

    例如,S2SQL中,访问者 = 'alice' 和访问日期 >= '2023-11-04' ;传给大模型的filedNameList中,不存在字段名"访问者"、"访问日期";因此,需要将S2SQL中的字段"访问者"修正为"用户名",字段"访问日期"修正为"数据日期";

b.维度值修正

    例如,访问者 = 'alice' 中的alice,模型有时候会生成错误的维度值如"Alice"等;S2QL的维度值,将导致无法查询到准确的数据;因此,需要将S2SQL中的维度值进行修正;

c.修正策略

    目前的修正策略主要是利用hanlp来进行维度值修正,通过文本相似度来修正字段名;目前正在实现通过语义来修正字段名和维度值;

4.SelectCorrector

该corrector主要作用是将一些S2SQL Where条件中的字段一起展示出来,提升用户体验;如"按部门统计,访问次数大于10次部门有哪些",大模型生成的S2SQL:

SELECT 部门 FROM 超音数 GROUP BY 部门 HAVING SUM(访问次数) > 10

    该查询只会将字段"部门"查出,通常用户需要知道超过10次的"部门"以及对应"访问次数"具体是多少;主要实现原理是通过jsqlparser框架将group by、order by涉及的字段,加入到select中;

5.WhereCorrector

①添加"数据日期"字段过滤:如前面的输入"按部门统计,访问次数大于10次部门有哪些",返回的S2SQL并没有加上"数据日期"过滤,默认会全表扫描;数据日期添加的逻辑如下:

  • 判断此次查询是查询模式:指标模式or标签模式;
  • 如果是指标模式,则获取问答设置中的指标模式时间范围;如果没有配置或配置的是负数,则不进行"数据日期"添加;
  • 如果是标签模式,则获取问答设置中的标签模式时间范围,其他同b。

②解析相对时间:对于相对时间识别,大模型识别比较复杂;SuperSonic实现逻辑是:将相对时间currentDate加入到prompt中,并采用datediff来表达相对时间。

因此该Corrector的另外一个功能是,将约定的相对时间转换为数据库能执行的时间;

1、最近3天:datediff('day', 数据日期, '2022-11-06') <= 3     转换为:数据日期 <="2022-11-06" and 数据日期 >="2022-11-04"2、最近12个月:datediff('month', 数据日期, '2022-11-06') <= 123、最近3年:datediff('year', 数据日期, '2022-11-06') <= 3

 ③添加QueryFilter限定条件

    在QueryReq中有一个queryFilters参数,主要是在某些场景,需要将已知的限定条件带到后端;如:已经明确识别到了某些条件,需要将这些限定条件加入到查询中;因此,该功能主要利用jsqlparser将限定条件加入到S2SQL Where条件中;

6.GroupByCorrector

    该corrector主要作用是对大模型生成的S2SQL的group by部分进行修正;以文本-"访问次数最高的部门"举例说明,大模型生成的S2SQL: SELECT 部门 FROM 超音数 WHERE 数据日期 = '2023-11-27' ORDER BY 访问次数 DESC LIMIT 1,不符合用户查询语言,没有对部门进行group by;

1、大模型生成的S2SQLSELECT 部门 FROM 超音数 WHERE 数据日期 = '2023-11-27' ORDER BY 访问次数 DESC LIMIT 12、经过SchemaCorrectorSELECT 部门 FROM 超音数 WHERE 数据日期 = '2023-11-27' ORDER BY 访问次数 DESC LIMIT 13、经过SelectCorrectorSELECT 部门, 数据日期, 访问次数 FROM 超音数 WHERE 数据日期 = '2023-11-27' ORDER BY 访问次数 DESC LIMIT 14、经过WhereCorrectorSELECT 部门, 数据日期, 访问次数 FROM 超音数 WHERE 数据日期 = '2023-11-27' ORDER BY 访问次数 DESC LIMIT 15、经过GroupByCorrectorSELECT 部门, 数据日期, SUM(访问次数) FROM 超音数用户部门 WHERE 数据日期 = '2023-11-27' GROUP BY 部门, 数据日期 ORDER BY SUM(访问次数) DESC LIMIT 1

    可以看到经过GroupByCorrector后,我们增加了对"部门"和"数据日期"的group by操作,并且是对指标进行SUM后排序;最终结果是符合预期;

7.HavingCorrector

    该corrector主要作用是对大模型生成的S2SQL的Having部分进行修正;以文本-"访问次数大于10次的部门有哪些"举例说明,大模型生成的S2SQL:SELECT 部门 FROM 超音数 WHERE 访问次数 > 10;没有按照group by进行聚合,并且指标过滤也需要通过having才可行;

1、大模型生成的S2SQLSELECT 部门 FROM 超音数 WHERE 访问次数 > 102、经过SchemaCorrectorSELECT 部门 FROM 超音数 WHERE 访问次数 > 103、经过SelectCorrectorSELECT 部门 FROM 超音数 WHERE 访问次数 > 104、经过WhereCorrectorSELECT 部门 FROM 超音数 WHERE (访问次数 > 10) AND 数据日期 = '2023-11-20'5、经过GroupByCorrectorSELECT 部门 FROM 超音数用户部门 WHERE (SUM(访问次数) > 10) AND 数据日期 = '2023-11-20' GROUP BY 部门6、经过HavingCorrectorSELECT 部门, SUM(访问次数) FROM 超音数用户部门 WHERE 数据日期 = '2023-11-20' GROUP BY 部门 HAVING SUM(访问次数) > 10

可以看到经过HavingCorrector后,在HAVING中增加了"SUM(访问次数) > 10 "过滤,满足了用户查询语义;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值