LLM应用实战:当KBQA集成LLM(二)

简介: 本文主要是针对KBQA方案基于LLM实现存在的问题进行优化,主要涉及到图谱存储至Es,且支持Es的向量检索,还有解决了一部分基于属性值倒查实体的场景,且效果相对提升。

1. 背景

又两周过去了,本qiang~依然奋斗在上周提到的项目KBQA集成LLM,感兴趣的可通过传送门查阅先前的文章《LLM应用实战:当KBQA集成LLM》。

本次又有什么更新呢?主要是针对上次提到的缺点进行优化改进。主要包含如下方面:

1. 数据落库

上次文章提到,KBQA服务会将图谱的概念、属性、实体、属性值全部加载到内存,所有的查询均在内存中进行,随之而来的问题就是如果图谱的体量很大呢,那内存不爆了么…

2. 支持基于属性值查实体

上篇文章不支持属性值查找实体,比如”最会照顾宝宝的是什么龙”,”什么龙是大龙和大龙生活,小龙和小龙生活”。本次已经此问题优化。

此篇文章是对这两周工作的一个整体总结,其中包含部分工程层面的优化。

2. 整体框架

KBQA2_架构.png

整体框架和上篇大致相同,不同之处在于:

1. 对齐模块:先前是基于SIM筛选候选实体,本次基于ES进行候选实体召回

2. 解析模块:先前是基于hugegraph和内存中的实体信息进行解析,本次优化为基于hugegraph和elasticsearch

3. 核心功能

3.1 数据库选型

由于需要支撑语义相似度检索,因此数据库选型为Milvus与Elasticsearch。

二者之间的比对如下:

 

 

Milvus

Elastic

 

 

 

扩展性层面

存储和计算分离

查询和插入分类

组件级别支持

服务器层面支持

多副本

动态分段 vs 静态分片

动态分段

静态分片

云原生

十亿级规模向量支持

 

 

 

功能性层面

权限控制

磁盘索引支撑

混合搜索

分区/命名空间/逻辑组

索引类型

11个(FLAT, IVF_FLAT, HNSW)等

1个(HNSW)

多内存索引支持

 

 

 

专门构建层面

为向量而设计

可调一致性

流批向量数据支持

二进制向量支持

多语言SDK

python, java, go, c++, node.js, ruby

python, java, go, c++, node.js, ruby, Rust, C#, PHP, Perl

数据库回滚

但由于Milvus针对国产化环境如华为Atlas适配不佳,而Es支持国产化环境,因此考虑到环境通用性,选择Es,且其文本搜索能力较强。

3.2 表结构设计

由于知识图谱的概念、属性一般量级较少,而实体数随着原始数据的丰富程度客场可短。因此将实体及其属性值在Es中进行存储。

针对KBQA集成LLM的场景,有两块内容会涉及语义搜索召回。

1. 对齐prompt中的候选实体

2. 解析模块中存在需要基于属性值查询实体的情况。

3. 涉及到数值类型的查询,如大于xx,最大,最小之类。

综合考虑,将Es的index结构设计如下:

属性

含义

类型

备注

name

实体名

keyword

 

concepts

所属概念

keyword

一个实体可能存在多个概念

property

属性

keyword

属性名称

value

属性值

text

ik分词器进行分词

numbers

数值属性值

double_range

会存在一个区间范围

embeddings

向量

elastiknn_dense_float_vector

1. 非数值属性对应value的向量

2. 使用elastiknn插件

3.3 安装部署

项目使用的Es版本是8.12.2,原因是elastiknn插件和Ik插件针对该版本均支持,且8.12.2版本是当前阶段的次新版本。

3.3.1 基于docker的ES部署

# 拉取镜像(最好先设置国内镜像加入)

docker pull elasticsearch:8.12.2

 

# es容器启动,存在SSL鉴权

docker run -d --name es01 --net host  -p 9200:9200 -it -e "ES_JAVA_OPTS=-Xms1024m -Xmx1024m" elasticsearch:8.13.2

 

# 容器中拉取需要鉴权的信息到本地

docker cp es01:/usr/share/elasticsearch/config/certs/http_ca.crt .

chmode 777 http_ca.crt

 

# 密码第一次启动的日志中有,需要保存下来

export ELASTIC_PASSWORD=xxxxxx

 

# 验证es是否启动成功

curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD https://localhost:9200

3.3.2 elastiknn插件集成

elastiknn插件是为了优化ES自身的向量检索性能,安装此插件后,ES的向量检索性能会提升数倍,如果再增加SSD固态硬盘,性能会进一步提升数倍

#下载插件包

wget https://github.com/alexklibisz/elastiknn/releases/download/8.12.2.1/elastiknn-8.12.2.1.zip

 

# 导入容器中指定目录

docker cp  elastiknn-8.12.2.1.zip es01:/usr/share/elasticsearch/

 

# 进入容器,默认目录即为/usr/share/elasticsearch/

docker exec -it es01 bash

 

# 安装插件

elasticsearch-plugin install file:elastiknn-8.12.2.1.zip

 

# 退出,重启容器

docker restart es01

 

# 验证

# 创建mapping

curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD -XPOST https://localhost:9200/test/_mapping -H 'Content-Type:application/json' -d '

{

"properties": {

"embeddings": {

"type": "elastiknn_dense_float_vector",

"elastiknn": {

"model": "lsh",

"similarity": "cosine",

"dims": 768,

"L": 99,

"k": 3

}

}

}

}'

 

# 验证mapping是否生效

curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD -XGET https://localhost:9200/test/_mapping?pretty

 

采坑总结:

1. elastiknn插件导入始终无法安装,且报错。

解决:

(1) 一定要注意,安装es插件需要指定路径,且增加”file:” 的前缀,不加此前缀,那就等着报错吧

(2) 拷贝到容器内部,一定要注意,不要将elastiknn-8.12.2.1.zip拷贝至/usr/share/elasticsearch/plugins目录,否则安装也报错。

3.3.3 ik分词器插件集成

#下载插件包

wget https://github.com/infinilabs/analysis-ik/releases/download/v8.12.2/elasticsearch-analysis-ik-8.12.2.zip

 

# 导入容器中指定目录

docker cp elasticsearch-analysis-ik-8.12.2.zip es01:/usr/share/elasticsearch/

 

# 进入容器,默认目录即为/usr/share/elasticsearch/

docker exec -it es01 bash

 

# 安装插件

elasticsearch-plugin install file:elasticsearch-analysis-ik-8.12.2.zip

 

# 退出,重启容器

docker restart es01

 

# 验证是否生效

curl --cacert http_ca.crt -u elastic:$ELASTIC_PASSWORD -XPOST https://localhost:9200/_analyze?pretty -H 'Content-Type:application/json' -d '{"text":"三角龙或者霸王龙","analyzer": "ik_smart"}'

# 返回结果中不包含”或者”,因为”或者”在默认的停用词表中。

采坑总结:

1. ik分词器插件导入始终无法安装,且报错。

解决:一定要注意,安装es插件需要指定路径,且增加”file:” 的前缀,不加此前缀,那就等着报错吧

2. ik分词器添加自定义专有名词以及停用词不生效(浪费了1天的时间来排查)

解决:

(1) 一定要注意,8.12.2版本的ik分词器如果想要配置自定义专有名词或停用词,配置的完整目录是/usr/share/elasticsearch/config/analysis-ik,而不是/usr/share/elasticsearch/plugins/analysis-ik,这点需要注意下。

在config/analysis-ik中配置IKAnalyzer.cfg.xml,修改内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>

<comment>IK Analyzer 扩展配置</comment>

<!--用户可以在这里配置自己的扩展字典 -->

<entry key="ext_dict">extra_main.dic</entry>

 <!--用户可以在这里配置自己的扩展停止词字典-->

<entry key="ext_stopwords">extra_stopword.dic</entry>

<!--用户可以在这里配置远程扩展字典 -->

<!-- <entry key="remote_ext_dict">words_location</entry> -->

<!--用户可以在这里配置远程扩展停止词字典-->

<!-- <entry key="remote_ext_stopwords">words_location</entry> -->

</properties>

(2) 一定要注意,extra_main.dic和extra_stopword.dic的编码格式是UTF-8,如果编码格式不对的话,分词也不生效。

 

4. Es操作相关源码

4.1 es_client连接

self.es_client = Elasticsearch(config['url'],

   basic_auth=(config['user'], config['password']),

   ca_certs=config['crt_path'],

   http_compress=True,

   request_timeout=int(config['request_timeout']) if 'request_timeout' in config else 60,

   max_retries=int(config['max_retries']) if 'max_retries' in config else 5,

   retry_on_timeout=True)

4.2 构建表结构

def index(self, kg_id, force=False):

"""

构建表

"""

if force:

try:

self.es_client.indices.delete(index=kg_id, ignore_unavailable=True)

except EngineError as e:

logger.exception(f"code:{ES_DELETE_INDEX_ERROR}, message:{str(e)}")

raise e

 

if not self.es_client.indices.exists(index=kg_id):

body = {

'settings': {'index': {'number_of_shards': 2}},

'mappings': {

'dynamic': False,

'properties': {

'name': {'type': 'keyword'},

'concepts': {'type': 'keyword'},

'property': {'type': 'keyword'},

'value': {'type': 'text', 'analyzer': 'ik_max_word', 'search_analyzer': 'ik_smart'},

'numbers': {'type': 'double_range'},

'embeddings': {'type': 'elastiknn_dense_float_vector', 'elastiknn': {'dims': 768, 'model': 'lsh', 'similarity': 'cosine', 'L': 99, 'k': 3}}

}

}

}

try:

self.es_client.indices.create(index=kg_id, body=body)

except EngineError as e:

logger.exception(f"code:1008, message:{str(e)}")

raise e

try:   

self.es_client.indices.refresh(index=kg_id, ignore_unavailable=True)

except EngineError as e:

logger.exception(f"code:1008, message:{str(e)}")

raise e

说明:

1. value字段需要经过IK分词,分词方式ik_max_word,查询方式是ik_smart

2. embeddings的类型为elastiknn_dense_float_vector,其中向量维度为768,相似度计算使用cosine

4.3 候选实体查询

def get_candidate_entities(self, kg_id, query, limit=15):

"""

基于查询串查找候选实体名称

"""

body = {

'_source': {'excludes': ["embeddings"]},

'query': {

'function_score': {

'query': {

'bool': {

'must': [

{'match': {'value': query}},

{'bool': {

'filter': {

'bool': {

'should': [

{'term': {"property": "名称"}},

{'term': {"property": "别名"}},

]

}

}

}}

]

}

},

'functions': [

{

   'elastiknn_nearest_neighbors': {

   'field': 'embeddings',

   'vec': self.get_callback_ans({'query': [query]})['result'][0]['embeddings'],

   'model': 'lsh',

   'similarity': 'cosine',

   'candidates': 100

   }

}

]

}

},

'size': limit

}

return self.es_client.search(index=kg_id, body=body)['hits']['hits']

说明:

1. '_source': {'excludes': ["embeddings"]}表示输出结果中过滤embeddings字段

2. 查询以function_score方式,其中的query表示别名或名称与问题的匹配程度,functions表示打分方式,目前的打分是基于向量相似度进行打分,其中, self.get_callback_ans表示语义相似度模型将文本转换为向量。注意:最终的得分由两部分组成,一部分是文本匹配,一部分是语义相似度匹配,不过可以增加参数boost_mode进行设置。

4.4 基于属性及属性值进行查询

def search_by_property_value(self, kg_id, property, value, limit=100):

body = {

'_source': {'excludes': ["embeddings"]},

'query': {

'function_score': {

'query': {

'bool': {

'must': [

{'match': {'value': value}},

{'term': {"property": property}}

]

}

},

'functions': [

{

   'elastiknn_nearest_neighbors': {

   'field': 'embeddings',

   'vec': self.get_callback_ans({'query': [value]})['result'][0]['embeddings'],

   'model': 'lsh',

   'similarity': 'cosine',

   'candidates': 100

   }

}

],

'boost_mode': 'replace'

}

},

'size': limit

}

try:

return self.es_client.search(index=kg_id, body=body)['hits']['hits']

except EngineError as e:

logger.exception(f"code:{ES_SEARCH_ERROR}, message:{str(e)}")

raise e

4.5 数值属性范围查询

主要解决的场景有:体重大于9吨的恐龙有哪些?身长小于10米的角龙类有哪些?

其中,如果提供了实体名称,则查询范围是基于这些实体进行查询比较。

def search_by_number_property(self, kg_id, property, operate, entities, limit=100):

musts = [{'term': {'property': property}}, {'range': {'numbers': operate}}]

if entities:

musts.append({'terms': {'name': entities}})

 

body = {

'_source': {'excludes': ['embeddings']},

'query': {

'bool': {

'must': musts

}

},

'size': limit

}

try:

return self.es_client.search(index=kg_id, body=body)['hits']['hits']

except EngineError as e:

logger.exception(f"code:{ES_SEARCH_ERROR}, message:{str(e)}")

raise e

4.6 数值属性最大最小查询

实现最大最小的逻辑,采用了sort机制,按照numbers进行排序,最大则顺排,最小则倒排。

def search_by_number_property_maxmin(self, kg_id, property, entities, sort_flag):

musts = [{'term': {'property': property}}]

if entities:

musts.append({'terms': {'name': entities}})

 

body = {

'_source': {'excludes': ["embeddings"]},

'query': {

'bool': {

'must': musts

}

},

'sort': {'numbers': sort_flag},

'size': 1

}

try:

return self.es_client.search(index=kg_id, body=body)['hits']['hits']

except EngineError as e:

logger.exception(f"code:{ES_SEARCH_ERROR}, message:{str(e)}")

raise e

 

5. 效果

上一版未解决的问题,在本版本优化的结果。

1. 问:头像鸭头的龙有哪些?

答:头像鸭头的有慈母龙、原角龙、鹦鹉嘴龙、姜氏巴克龙、奇异辽宁龙、多背棘沱江龙、陆家屯鹦鹉嘴龙、盖斯顿龙、小盾龙、肿头龙、弯龙

2. 问:老师说的有一个特别会照顾宝宝的恐龙是什么龙?

答:慈母龙会照顾宝宝。

3. 问:有哪些恐龙会游泳啊?

答:滑齿龙、慢龙和色雷斯龙是会游泳的恐龙。

4. 问:科学家在意大利阿尔卑斯山脉Preone山谷的乌迪内附近发现了一个会飞的史前动物化石,它是谁的化石?

答:科学家在意大利阿尔卑斯山脉Preone山谷的乌迪内附近发现的会飞的史前动物化石是沛温翼龙的化石。

6. 总结

一句话足矣~

本文主要是针对KBQA方案基于LLM实现存在的问题进行优化,主要涉及到图谱存储至Es,且支持Es的向量检索,还有解决了一部分基于属性值倒查实体的场景,且效果相对提升。

其次,提供了部分Es的操作源码,以飧读者。

 

 

附件:

1. es vs milvus: https://zilliz.com/comparison/milvus-vs-elastic

2. docker安装es:https://www.elastic.co/guide/en/elasticsearch/reference/8.12/docker.html

3. elastiknn性能分析:https://blog.csdn.net/star1210644725/article/details/134021552

4. es的function_score: https://www.elastic.co/guide/en/elasticsearch/reference/8.12/query-dsl-function-score-query.html

 

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。 &nbsp;
目录
相关文章
|
3月前
|
人工智能 自然语言处理 API
快速集成GPT-4o:下一代多模态AI实战指南
快速集成GPT-4o:下一代多模态AI实战指南
415 101
|
3月前
|
SQL 人工智能 监控
SLS Copilot 实践:基于 SLS 灵活构建 LLM 应用的数据基础设施
本文将分享我们在构建 SLS SQL Copilot 过程中的工程实践,展示如何基于阿里云 SLS 打造一套完整的 LLM 应用数据基础设施。
774 59
|
5月前
|
人工智能 监控 数据可视化
BISHENG下一代企业AI应用的“全能型“LLM软件
杭州奥零数据科技有限公司成立于2023年,专注于数据中台业务,维护开源项目AllData并提供商业版解决方案。AllData提供数据集成、存储、开发、治理及BI展示等一站式服务,支持AI大模型应用,助力企业高效利用数据价值。
|
6月前
|
缓存 监控 安全
通义大模型与现有企业系统集成实战《CRM案例分析与安全最佳实践》
本文档详细介绍了基于通义大模型的CRM系统集成架构设计与优化实践。涵盖混合部署架构演进(新增向量缓存、双通道同步)、性能基准测试对比、客户意图分析模块、商机预测系统等核心功能实现。同时,深入探讨了安全防护体系、三级缓存架构、请求批处理优化及故障处理机制,并展示了实时客户画像生成和动态提示词工程。通过实施,显著提升客服响应速度(425%)、商机识别准确率(37%)及客户满意度(15%)。最后,规划了技术演进路线图,从单点集成迈向自主优化阶段,推动业务效率与价值持续增长。
270 8
|
2月前
|
人工智能 自然语言处理 TensorFlow
134_边缘推理:TensorFlow Lite - 优化移动端LLM部署技术详解与实战指南
在人工智能与移动计算深度融合的今天,将大语言模型(LLM)部署到移动端和边缘设备已成为行业发展的重要趋势。TensorFlow Lite作为专为移动和嵌入式设备优化的轻量级推理框架,为开发者提供了将复杂AI模型转换为高效、低功耗边缘计算解决方案的强大工具。随着移动设备硬件性能的不断提升和模型压缩技术的快速发展,2025年的移动端LLM部署已不再是遥远的愿景,而是正在成为现实的技术实践。
|
2月前
|
存储 Prometheus 监控
136_生产监控:Prometheus集成 - 设置警报与指标选择与LLM部署监控最佳实践
在大语言模型(LLM)部署的生产环境中,有效的监控系统是确保服务稳定性、可靠性和性能的关键。随着LLM模型规模的不断扩大和应用场景的日益复杂,传统的监控手段已难以满足需求。Prometheus作为当前最流行的开源监控系统之一,凭借其强大的时序数据收集、查询和告警能力,已成为LLM部署监控的首选工具。
|
3月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
582 12
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
12_机器翻译入门:多语言LLM应用
在全球化背景下,语言障碍一直是信息交流、商业合作和文化传播的重要阻碍。2025年,随着多语言大语言模型(LLM)技术的突破,机器翻译已经从简单的单词转换发展为能够理解上下文、处理复杂句式、适应文化差异的智能系统。本文将带您入门多语言LLM在机器翻译领域的应用,重点介绍使用mT5(多语言T5)模型实现英语到中文的翻译,并探讨文化适应等高级话题。
|
2月前
|
机器学习/深度学习 自然语言处理 算法
48_动态架构模型:NAS在LLM中的应用
大型语言模型(LLM)在自然语言处理领域的突破性进展,很大程度上归功于其庞大的参数量和复杂的网络架构。然而,随着模型规模的不断增长,计算资源消耗、推理延迟和部署成本等问题日益凸显。如何在保持模型性能的同时,优化模型架构以提高效率,成为2025年大模型研究的核心方向之一。神经架构搜索(Neural Architecture Search, NAS)作为一种自动化的网络设计方法,正在为这一挑战提供创新性解决方案。本文将深入探讨NAS技术如何应用于LLM的架构优化,特别是在层数与维度调整方面的最新进展,并通过代码实现展示简单的NAS实验。
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
15_批量处理文本:LLM在数据集上的应用
在大语言模型(LLM)的实际应用中,我们很少只处理单条文本。无论是数据分析、内容生成还是模型训练,都需要面对海量文本数据的处理需求。批量处理技术是连接LLM与实际应用场景的关键桥梁,它能够显著提升处理效率、降低计算成本,并实现更复杂的数据流水线设计。