大模型别再只写RAG,太趋同了!query检索

大模型可以做的事情真的很多很多,不只是RAG,所以简历上千万不要只是简简单单的写RAG,要写明白RAG的优化。

接下来,我们来详细聊聊RAG的优化方案。

一句话理解RAG:

难点:

1、离线知识库:怎么构建知识库(企业或个人的知识都是各种格式、各种模态(文字、图片、表格)混杂的)

2、在线检索:query怎么找到自己想要的知识,找准+找全

3、在线生成:拿到了知识怎么生成我们想要的内容

初期掌握目标:

1、掌握2阶段(检索、生成)3模块(query、index、生成)的13种常用优化方式以及什么场景下使用、怎么优化

2、能用llamaindex搭建一个一个自己的研报/论文阅读助手

我们带入一个实际的场景去思考如何优化

金融保险公司RAG问答系统

1、5000分不同格式的文档,包含ppt、pdf、txt、视频、扫描图片等各种类型和模态的文件

2、用户query较为复杂,有基于公司制度(报销流程等)、销售策略、产品信息、市场情况等各类问题,需要走不同的链路

接下来,我们按照如下思路进行说明RAG4个核心模块(query、解析入库、召回、生成)优化方案

1、核心问题

2、实际案例case

3、优化策略+手撕代码

4、为什么这么做能解决

1.RAG流程图解

1.1整体流程

1.2离线处理流程

离线处理是系统的准备阶段,主要完成知识库的建立与向量化:

知识文档库:系统首先需要建立一个包含各类文档、知识和信息的库,这是RAG系统的知识基础

文档向量化:通过Embedding引擎将文本内容转换为高维向量,捕捉文本的语义信息

向量数据库:存储所有文档的向量表示,并建立高效的检索索引,为后续的实时查询做准备

离线流程只需执行一次或定期更新,不参与用户的实时交互过程。

1.3在线查询处理流程

在线处理是系统的运行阶段,负责处理用户实时查询并生成回答:

用户查询:接收用户输入的问题或指令

查询向量化:将用户查询转换为与文档相同向量空间的表示

语义检索:在向量数据库中寻找与查询语义最相似的文档片段【解释在notebook】

生成回答:模型基于检索回来的背景知识增强后的上下文生成最终回答返回给用户

在线流程对每个用户查询都会实时执行,确保系统能够利用最新的知识库信息提供准确回答。

这两个流程相互配合,共同构成了RAG系统的完整工作机制,使大语言模型能够超越其训练数据的局限,提供实时、准确、基于本地数据的QA服务。

在实际工程中包含了非常多的策略优化,RAG核心优化在于2点:

1、用户query进来,能不能找到并且找全想要的背景知识?

2、找到了背景知识,能不能基于这个背景知识回答准确,并且支持多轮交互的语义转折?

2.Query模块

2.1Query模块

Query重写(QueryRewrite):如何改进用户输入,提高检索准确率,附带代码示例。

Query扩写(QueryExpansion):基于同义词扩展、语义扩展等优化策略,并提供代码。

HYDE(HypotheticalDocumentEmbeddings):使用LLM生成假设文档的优化策略及示例代码。

2.1.1基于规则的方法(Rule-basedIntentRecognition)

案例:如果用户query中包含“报销”“费用”等关键词,判定为报销流程查询;如果包含“销售”“技巧”等词,判定为保险产品销售技巧。

代码示例:下面代码通过关键词匹配实现简单的意图分类:

defclassify_intent_rule(query:str)->str:

query=query.lower#统一小写处理(如适用英文,此处对中文影响可忽略)

#定义关键词列表

intent_keywords={

"报销流程查询":["报销","费用","报销流程"],

"保险产品销售技巧":["卖保险","销售","技巧","销售技巧"]

}

forintent,keywordsinintent_keywords.items:

ifany(keywordinqueryforkeywordinkeywords):

returnintent

return"未知意图"

测试

queries=["怎么报销保险费用?","新人怎么提升保险产品销售技巧?","保险理赔需要哪些材料?"]

forqinqueries:

print(q,"->",classify_intent_rule(q))

2.1.2基于机器学习的方法(BERT分类模型)

方法说明:利用机器学习/深度学习模型(例如BERT)进行意图分类。先构造包含各种意图类别的训练数据,对模型进行fine-tune,使其学会根据句子语义分类。相比规则方法,ML模型对同义表达更鲁棒,能捕获上下文语义特征。缺点是需要标注数据进行训练。

案例:通过收集大量报销流程类问句和销售技巧类问句(以及其他意图类别的问句),用预训练的中文BERT模型fine-tune一个分类器。训练完成后,模型能够根据用户输入判定所属意图类别。

代码示例:下面示例展示如何加载一个训练好的BERT分类模型并对新的查询进行预测(假设我们已将意图类别编号0=报销流程查询,1=保险产品销售技巧):

fromtransformersimportAutoTokenizer,AutoModelForSequenceClassification

#加载已训练好的中文BERT意图分类模型

tokenizer=AutoTokenizer.from_pretrained("bert-base-chinese")

model=AutoModelForSequenceClassification.from_pretrained("./fine_tuned_intent_model")#本地路径

id2label={0:"报销流程查询",1:"保险产品销售技巧"}#意图ID到标签的映射

对用户查询进行分类

query="怎么报销保险费用?"

inputs=tokenizer(query,return_tensors="pt")

outputs=model(**inputs)

pred_id=int(outputs.logits.argmax(dim=1))

pred_intent=id2label[pred_id]

print(f"查询:{query}->意图类别:{pred_intent}")

在上述代码中,我们加载了bert-base-chinese预训练模型并使用已微调的参数(fine_tuned_intent_model目录)。id2label字典定义了模型输出与意图标签的对应关系。对输入句子进行Tokenize后喂给模型,通过argmax获取预测的意图类别。对于输入“怎么报销保险费用?”,模型若输出类别0,则映射为“报销流程查询”。

提示:实际应用中需先准备标注数据并训练模型。这可以使用Transformers提供的Trainer接口或TensorFlow/Keras等完成,对BERT模型在意图分类数据集上进行fine-tune,使其学会区分多种意图。

2.1.3基于PromptEngineering的方法(LLMZero-shot/Few-shot)

方法说明:借助大型语言模型(LLM)的强大语义理解能力,通过精心设计的Prompt直接让模型判断意图类别。我们可以提供若干意图类别描述,让模型选择最适合的类别。此方法不需要额外训练数据,在零样本或少样本场景下效果好。需要注意控制提示,避免模型产生偏差。

案例:对于用户问题,设计Prompt提示ChatGPT或类似模型:“请判断用户意图属于以下哪一类:1)报销流程查询,2)保险产品销售技巧。”模型将基于理解输出类别编号或名称。

代码示例:下面以OpenAI的ChatGPTAPI为例,通过构造对话消息让模型进行意图分类:

importopenai

openai.api_key="YOUR_API_KEY"#设置API密钥

defclassify_intent_llm(query:str)->str:

system_prompt="你是一个智能助手,帮助分类用户意图。可能的意图类别包括:\n1.报销流程查询\n2.保险产品销售技巧\n只回复序号1或2。"

user_prompt=f"用户问:{query}\n上述用户的意图属于哪个类别?"

response=openai.ChatCompletion.create(

model="gpt-3.5-turbo",

messages=[

{"role":"system","content":system_prompt},

{"role":"user","content":user_prompt}

]

)

answer=response["choices"][0]["message"]["content"].strip

return"报销流程查询"ifanswer.startswith("1")else"保险产品销售技巧"ifanswer.startswith("2")else"未知"

测试LLM意图分类

print(classify_intent_llm("怎么报销保险费用?"))#预计输出"报销流程查询"

print(classify_intent_llm("有哪些有效的保险产品销售技巧?"))#预计输出"保险产品销售技巧"

2.2Query重写(QueryRewrite)

Query重写旨在将用户的原始查询转换为更规范、清晰、利于检索的形式。在金融保险场景中,用户提问可能包含口语化表达、错别字或模糊措辞。通过重写,我们可以提高检索的匹配度。主要技术包括删除冗余词、纠正错误和标准化表述。

作用:消除用户查询与知识库文档表达之间的差异。例如,同义词替换、补全省略的上下文、将口语转为书面语等。

重写策略举例:

删除口头词/多余成分:去掉如“请问”、“一下”、“怎么”等口头语或多余词汇,只保留核心内容。

纠正错别字和语法:使用拼写检查或语法纠错工具修正用户输入中的错字、繁简混用或语序问题。例如将“理赔流程都有哪些步聚”纠正为“理赔流程都有哪些步骤”。

标准化术语:将非正式表述转换为标准术语。例如“报销保险费用”可以调整为“保险费用报销”,添加“流程”等词使其更正式完整。

代码实现思路:可采用简单的规则替换结合现有中文校对库。下面提供一个基于规则的Query重写示例代码,对特定句型进行转换:

importre

defrewrite_query(query:str)->str:

#1.去除疑问口语用词

fillers=["请问","一下","呢","啊","吧"]

forfinfillers:

query=query.replace(f,"")

query=query.strip

#2.将“怎么XXX”改写为“XXX是什么?”

ifquery.startswith(("怎么","怎样")):

main_query=query[2:]#去掉开头的"怎么"/"怎样"

#如果以问号结尾则去掉问号,稍后统一加

main_query=main_query.rstrip("?")

#示例中特定的短语调整:将“报销保险费用”改为“保险费用报销”

if"报销保险费用"inmain_query:

main_query=main_query.replace("报销保险费用","保险费用报销")

#若句尾缺少“流程”,根据上下文添加

ifmain_query.endswith("报销")ormain_query.endswith("费用"):

main_query+="流程"

#添加结尾的问句形式

query=main_query+"是什么"

#3.确保以问号结尾

query=query.rstrip("?")+"?"

returnquery

测试重写函数

original_query="怎么报销保险费用?"

rewritten_query=rewrite_query(original_query)

print("原始查询:",original_query)

print("重写后的查询:",rewritten_query)

运行结果:

原始查询:怎么报销保险费用?

重写后的查询:保险费用报销流程是什么?

可以看到,函数按照规则完成了重写:去除了“怎么”,调整了语序并加入“流程”“是什么”等使句子更正式、更符合知识库文档标题的风格。

说明:上述规则针对示例做了特定处理。在实际系统中,可扩展更多规则和词典。例如维护一个俗称→正式术语的映射表,遇到俗称时替换为正式名称;或者利用中文文本纠错库(如pycorrector或HanLP)检测并纠正错别字和语法错误。也可以使用大型语言模型进行查询改写(Prompt如:“将用户问题改写为专业术语表述”),让模型生成规范提问。

2.3Query扩写(QueryExpansion)

同义词扩展:为查询中的关键词添加同义词、近义词或俗称。例如“理赔”和“索赔”在很多情况下意义接近,用户搜索“保险理赔”,我们可以扩展加入“保险索赔”一起检索;又如“车险”和“汽车保险”可以互相扩展。利用同义词词典或领域词库(如《同义词词林》或自建术语表)实现。

案例:用户输入*“保险理赔”,通过扩写可以形成查询集合:“保险理赔”、“保险理赔流程”、“保险索赔”、“保险索赔方法”*等等。这样,无论文档中提到“理赔流程”还是使用了“索赔”一词,都有机会被检索到,提高了召回率。

代码示例:以下用Python演示一个简单的同义词扩展流程。在真实系统中,我们可能对查询进行分词,然后对每个重要词扩展同义词,这里简化为对整个查询短语查找扩展:

构建一个简单的同义词词典

synonym_dict={

"保险理赔":["保险索赔","理赔","索赔流程","理赔流程"],

"理赔":["索赔","赔付"],

#其他词的同义词...

defexpand_query(query:str)->[str]:

expansions=set

#如果查询短语本身在词典中

ifqueryinsynonym_dict:

expansions.update(synonym_dict[query])

#将查询拆分为词(简单按字符,这里假设输入短语本身是一个词或固定短语)

forterm,synsinsynonym_dict.items:

ifterminquery:

expansions.update(syns)

#加入原始查询本身

expansions.add(query)

returnlist(expansions)

测试查询扩展

query="保险理赔"

expanded_queries=expand_query(query)

print("原始查询:",query)

print("扩展结果:",expanded_queries)

原始查询:保险理赔

扩展结果:['保险理赔','保险索赔','理赔流程','索赔流程','索赔','理赔']

这些扩展词组可用于构造多个检索查询,把所有结果合并再进行后续排序。比如可以同时检索“保险理赔流程”和“保险索赔方法”,然后汇总结果。

2.4HYDE(HypotheticalDocumentEmbeddings)

工作原理:(HyDE假设性文档嵌入:大模型+对比学习,从关键词相似度搜索到语义搜索_hyde模型-CSDN博客)概括了HyDE的三个步骤:首先使用一个生成式语言模型(如GPT)根据输入查询生成一篇内容丰富的假设性回答文档(即使这个文档在知识库中并不存在);然后将生成的假设文档输入编码器生成嵌入向量表示;最后利用该向量去检索知识库中与之语义相似的真实文档。

案例:用户查询*“保险销售技巧”*。传统检索可能只针对“销售”或“技巧”检索,结果不一定全面。应用HyDE时,我们先让LLM根据这一查询生成一段假设回答,例如:“保险销售技巧包括了解客户需求、建立信任、提供专业建议、

代码示例:实现HyDE通常需要两个组件:生成模型和向量检索。下面提供一个伪代码式的示例流程:

fromsentence_transformersimportSentenceTransformer

openai.api_key="YOUR_API_KEY"

defhyde_retrieval(query:str,embed_model,doc_embeddings,doc_ids):

#1.使用LLM生成假设文档

prompt=f"请针对以下问题给出详细的回答:{query}"

messages=[{"role":"user","content":prompt}]

hypo_doc=response["choices"][0]["message"]["content"]

#2.将生成的文档进行向量化

query_vector=embed_model.encode(hypo_doc)

#3.在向量空间中检索相似文档

#(这里假设已有知识库文档向量doc_embeddings和对应的doc_ids列表)

#计算与所有文档向量的余弦相似度,并选取最高的若干

importnumpyasnp

sims=np.dot(doc_embeddings,query_vector)

top_idx=sims.argsort[-5:][::-1]#取前5个相似度最高的文档索引

results=[(doc_ids[i],sims[i])foriintop_idx]

returnresults

初始化句向量模型(例如中文多语言模型)

embed_model=SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

假设我们有预先计算好的文档向量和IDs

doc_embeddings=...#shape:(N_docs,dim)

doc_ids=...#文档ID或内容索引

对查询进行HyDE检索

query="保险销售技巧"

retrieved_docs=hyde_retrieval(query,embed_model,doc_embeddings,doc_ids)

print("HyDE检索结果文档ID及分数:",retrieved_docs)

上述代码描述了HyDE的关键步骤:

生成假设文档:使用OpenAIChatCompletionAPI,请求模型针对用户查询撰写答案段落。这里我们直接使用gpt-3.5-turbo作示例,实际可用任意强大的生成模型(包括本地部署的中文GPT模型)。

向量化:采用SentenceTransformer等嵌入模型将生成的文本编码为向量。这里示例用了一个多语言微调模型'paraphrase-multilingual-MiniLM-L12-v2'来得到句向量(假设已安装该模型)。在真实场景中,可以使用金融保险领域优化过的嵌入模型以获得更精确的向量表示。

相似度检索:将生成文本的向量与预先索引好的知识库文档向量逐一计算相似度(这里用点积近似余弦相似度,需保证向量已归一化)。取相似度最高的若干文档作为检索结果返回。

3.离线解析模块

3.1核心问题

OCR解析质量:针对扫描版PDF或图片,必须通过OCR获取文字。普通OCR往往难以正确还原表格、代码块等特殊结构,可能将表格内容串行成无序文本、破坏代码的格式,从而降低检索准确性。需要优化OCR策略,对表格和代码段做特殊处理,确保文本提取高保真。

3.2实际案例

案例1:报销制度类查询:例如用户询问“差旅报销的上限是多少?”。公司内部的报销制度可能是PDF扫描件,包含分级标题和表格(各费用类别的报销上限)。通过优化解析,我们对扫描PDF进行OCR并准确提取表格内容,将“差旅报销”章节下的文字和限额表格作为一个完整Chunk,并标签其上级章节“报销政策>差旅报销”。这样,当用户查询时,系统能准确召回包含“差旅报销”限额的片段,避免因页面跳转或表格解析错误导致召回不全。

3.3优化策略

为解决以上问题,我们提出如下优化方案:

统一文档解析与OCR改进:构建通用的解析模块,针对不同格式分别处理:

PDF解析:优先使用文本层提取(如pdfplumber或PyMuPDF读取),保持读取顺序;对于扫描PDF或嵌入的图片,调用OCR引擎(如Tesseract或PaddleOCR)。特别地,OCR时针对表格区域采用专门处理(例如先检测表格边框或使用表格OCR算法),确保按单元格顺序输出文本;对于代码块图片,可设置OCR保持换行和空格格式。(这里推荐Marker和MinerU)

PPT解析:利用幻灯片结构提取标题和文本框内容。每页幻灯片输出时保留其标题,项目符号列表作为子内容。对于包含图片的幻灯片,可对图片执行OCR(如截图后OCR)以提取其中的文字说明。

智能Chunk切分(规则+语义融合):在得到完整文本后,按照文档的自然结构和语义连贯性进行分块:

基于规则的切分:利用文档格式特征,如章节标题、段落换行、列表项、表格边界等作为切分点。一旦检测到新的章节点或列表起始,就结束当前Chunk开启新Chunk。对于表格和代码块,整段内容视为一个Chunk,避免中途截断。

长度和平衡:在保证语义完整的前提下控制Chunk长度,使其适合向量检索和后续模型处理(例如不超过512字或一定token数)。过长则适当按语义次级节点再拆分,过短则与相邻补充。最终每个Chunk都应是自含意义明确的一段内容。

层级结构与标签管理:为每个Chunk附加丰富的元数据标签,保留其在原文档中的位置和语境:

内容类别标签:标注Chunk的内容类型和主题类别。例如标记Chunk是否为“表格”、“代码块”或普通文本,“政策条例”还是“操作指南”等。这可通过解析时的内容特征判断(如检测到多列文本则标记表格,包含代码格式则标记代码段)。也可以结合业务定义的类别(如财务制度、销售策略)作为标签。这些标签在检索时可用于过滤或作为额外特征提高准确匹配度。

3.4示例代码

下面给出一个Python示例代码,演示上述解析、切分和标签过程。该代码不依赖特定RAG框架,可直接运行(需安装PyMuPDF(fitz)用于PDF解析,python-pptx用于PPT解析,pytesseract用于OCR,和PIL用于图像处理),直接处理一般直接使用MinerU或者deepDoc进行魔改:

importfitz#PyMuPDFforPDF

frompptximportPresentation#python-pptxforPPT

fromPILimportImage

importpytesseract

defocr_image(image):

config="--psm6"#假设6:把图像看作一个统一块

text=pytesseract.image_to_string(image,config=config,lang='chi_sim+eng')

returntext

defparse_document(file_path):

"""解析文档(PDF/PPT/文本),返回文本块列表,每块包含内容和类型等标记。"""

blocks=[]

iffile_path.lower.endswith('.pdf'):

doc=fitz.open(file_path)

forpageindoc:

#尝试直接提取文本

text=page.get_text("text")

iftext.strip:

#简单按换行拆分为块,可进一步按段落细分

lines=text.splitlines

else:

#若无文本(扫描页),则OCR整个页面

pix=page.get_pixmap(dpi=150)

img=Image.frombytes("RGB",[pix.width,pix.height],pix.samples)

ocr_text=ocr_image(img)

lines=ocr_text.splitlines

forlineinlines:

ifline.strip=="":

continue

blocks.append({

"text":line.strip,

"type":"text",#初始默认为普通文本,后续再调整类型

"page":page.number+1

})

eliffile_path.lower.endswith('.pptx'):

prs=Presentation(file_path)

foridx,slideinenumerate(prs.slides,start=1):

title=""

ifslide.shapes.title:

title=slide.shapes.title.text.strip

iftitle:

#幻灯片标题作为单独块

"text":title,

"type":"heading",

"level":1,#幻灯片题目视为一级标题

"slide":idx

#提取其他文本框

forshapeinslide.shapes:

ifnotshape.has_text_frameorshape.text.strip=="":

text=shape.text.strip

#如果文本与标题相同就跳过(已添加)

iftitleandtext==title:

#按换行将文本框内容拆成行块(对应子弹列表逐行)

forlineintext.splitlines:

"type":"text",

eliffile_path.lower.endswith('.txt'):

withopen(file_path,'r',encoding='utf-8')asf:

forlineinf:

ifline.strip:

"type":"text"

#其他格式(如视频):假设已转换为字幕文本

#这里直接读取同名的字幕文本文件

subs_path=file_path+".txt"

try:

withopen(subs_path,'r',encoding='utf-8')asf:

exceptFileNotFoundError:

print("Unsupportedfileformatormissingtran:",file_path)

returnblocks

defsplit_and_tag_blocks(blocks):

"""根据内容将文本块切分、合并,并标注层级和类别标签。"""

chunk_list=[]

hierarchy=[]#用于跟踪当前层级标题栈

current_chunk={"text":"","meta":{}}#临时聚合当前chunk内容

forblkinblocks:

text=blk["text"]

blk_type=blk.get("type","text")

#检测标题:根据内容格式判断

is_heading=blk_type=="heading"

level=blk.get("level",None)

ifnotis_heading:

#简单规则:长度较短且以冒号结束,或符合编号模式的,视为标题

iflen(text)<20and(text.endswith(":")ortext.endswith(":")orre.match(r'^[\d一二三]+\D',text)):

is_heading=True

#确定层级level(基于数字章节或者默认1级)

m=re.match(r'^(\d+(\.\d+)*)',text)

ifm:

level=m.group(1).count('.')+1

level=1

blk_type="heading"

ifis_heading:

#遇到新标题块,先结束上一chunk

ifcurrent_chunk["text"]:

chunk_list.append(current_chunk)

current_chunk={"text":"","meta":{}}

#更新层级栈

iflevelisNone:

#调整hierarchy列表长度

iflevel-1

hierarchy=hierarchy[:level-1]

#确保hierarchy长度足够

whilelen(hierarchy)

hierarchy.append("")

#设置当前级别标题

iflen(hierarchy)

hierarchy.append(text)

hierarchy[level-1]=text

#将标题本身作为一个Chunk的元数据存入层级,但内容不上屏检索

#(可以选择是否将标题内容并入chunk文本,这里不直接作为内容)

#开始一个新的chunk上下文,继承层级meta

current_chunk["meta"]["section"]=">".join(hierarchy)

current_chunk["meta"]["type"]="text"#默认文本类型

continue#不把标题行本身当做内容

#非标题块:

#检测表格行:根据是否包含制表符或对齐空格判断

ifre.search(r'\t',text)orre.search(r'\s{2,}',text):

blk_type="table"

#检测代码块:根据常见代码特征,如以4空格开头,或包含{};等(简单判断)

ifre.match(r'^\s{4}',text)orre.search(r'[{};]',text):

#注意:真实环境可结合格式或```标记判断

blk_type="code"

#如果当前chunk已存在且本块类型与当前chunk类型不同,而且当前chunk不空,则开启新chunk

ifcurrent_chunk["text"]andcurrent_chunk["meta"].get("type")!=blk_type:

#结束之前的chunk

#设置chunk元数据(继承当前层级路径和类型)

if"section"notincurrent_chunk["meta"]:

current_chunk["meta"]["section"]=">".join(hierarchy)ifhierarchyelse""

current_chunk["meta"]["type"]=blk_type

#累加文本(表格和代码保持原格式,普通文本在块内加空格拼接)

ifblk_type=="text":

#若当前已有内容,先检查语义连贯(比如前句末尾无句号,则直接接续)

ifcurrent_chunk["text"].strip.endswith(tuple("。?!!.")):

current_chunk["text"]+="\n"+text

current_chunk["text"]+=""+text#前一句未完,空格连接

current_chunk["text"]=text

#表格或代码块,保持行作为换行

current_chunk["text"]+=(text+"\n")

#循环结束后,将最后的chunk加入列表

returnchunk_list

示例:调用解析和切分函数(文件路径需换成实际存在的文件)

file_path="保险公司报销制度.pdf"

blocks=parse_document(file_path)

chunks=split_and_tag_blocks(blocks)

forchinchunks:

print(f"[{ch['meta'].get('section')}]({ch['meta'].get('type')}){ch['text'][:50]}...")

通过这种方式,最终生成的Chunk列表中,每个Chunk都是一个语义完整的内容单元,并附带其在原文中的层级位置和类型标签。在检索阶段,我们可以将section层级信息作为检索字段或将其前缀到文本中参与向量编码,使查询既能匹配Chunk内容本身,也能匹配其上级主题,从而提升召回的准确性。此外,表格和代码块未经破坏地保存在Chunk中,当用户提问涉及这些部分时,也能直接检索到正确的片段。这套优化流程提高了RAG问答系统对金融保险文档的解析质量和检索召回准确率。

4.检索召回模块

RAG问答系统查询与Chunk匹配模块优化方案

在金融保险公司的RAG(Retrieval-AugmentedGeneration)问答系统中,我们需要提升查询(query)和文档片段(chunk)匹配模块的召回率和准确率。为此,可以从混合检索、Embedding模型微调、结果重排和评估指标四个方向进行优化。下面针对每个优化点进行问题分析、结合金融保险场景的案例说明优化策略,并提供相应的代码示例。

4.1case分析

金融保险公司内部知识库包含5000份各类文档(PPT、PDF、文本、视频等),用户可能提出多样化的问题。例如:

流程型问题:如「公司的报销制度是什么?」涉及公司内部流程或制度,需要在规章制度文档中找到准确答案。

思考型问题:如「如何推销保险产品?」需要结合销售技巧和经验的指导,可能分散在培训资料或业务手册中。

当前RAG系统管线:首先将长文档依据语义和规则切分为较小的chunk段落,然后使用BGE预训练Embedding模型将查询和chunk向量化,利用Milvus向量库检索相似片段,并结合BM25关键词检索作辅助。存在的问题包括:

单纯向量检索对某些短查询(如只有几个关键词的问题)可能效果不佳,因为缺乏精确的关键词匹配。

缺乏评估指标来量化检索改进效果,无法客观比较不同方案的优劣。

为解决上述挑战,我们提出以下优化策略。

4.2混合检索(BM25+向量)

短查询(如只有几个词的问句)往往需要关键词精确匹配才能找到正确文档,BM25对此效果更好(超越向量检索!混合检索+重排序改善RAG应用|新程序员-CSDN博客)。

优化策略与原理:构建混合检索管线,同时利用BM25和向量相似度进行检索,获取候选结果并合并:

BM25检索:构建文档的关键词倒排索引(可使用Elasticsearch或其他搜索库),检索出TopN候选文档片段。BM25根据查询词在文档中的频率、文档长度等打分(稠密向量检索、稀疏向量检索、BM25检索三者对比-博客园)。对于短查询,可直接采用BM25结果;对于长查询,BM25结果可作为补充。

结果合并与去重:将两种检索的候选列表合并。由于BM25分数和向量相似度分值不在同一量纲,需进行归一化处理(超越向量检索!混合检索+重排序改善RAG应用|新程序员-CSDN博客)。例如,可将BM25分数归一到0-1区间,向量相似度天然在0-1(如余弦相似度)。然后按一定策略融合,如线性加权组合或者直接取两者结果集的并集。在组合过程中处理重复文档(相同chunk多次出现)以避免干扰。

示例代码:BM25与向量混合检索

下面示例展示如何结合BM25与向量检索。假设我们有两份示例文档:

我们将针对两个查询演示:一个短查询和一个开放性问答查询。代码模拟BM25检索(通过词频匹配打分)和向量检索(使用简单的余弦相似度),然后合并结果。实际系统中应使用现有工具(如Elasticsearch、Milvus)和经过训练的Embedding模型,此处用简单逻辑模拟原理。

importmath

示例文档语料

docs={

1:"公司报销制度包括差旅费报销流程和标准。",

2:"保险产品的销售技巧包括如何挖掘客户需求和突出产品优势。"

构建倒排索引用于BM25(简单实现:记录每个词在哪些文档出现及频次)

inverted_index={}

doc_lengths={}

fordoc_id,textindocs.items:

words=list(text)#这里简单将每个字作为词,实际应用需分词

doc_lengths[doc_id]=len(words)

forwinwords:

ifwnotininverted_index:

inverted_index[w]={}

inverted_index[w][doc_id]=inverted_index[w].get(doc_id,0)+1

计算IDF值

N=len(docs)

idf={}

forterm,doc_dictininverted_index.items:

df=len(doc_dict)

idf[term]=math.log((N-df+0.5)/(df+0.5)+1)#BM25IDF公式

defbm25_search(query,k1=1.5,b=0.75,top_k=5):

"""简单BM25检索实现,返回doc_id及BM25分数"""

#分词

query_terms=list(query)

scores={}

avgdl=sum(doc_lengths.values)/N

forterminquery_terms:

iftermnotininverted_index:

fordoc_id,tfininverted_index[term].items:

#BM25公式计算评分

score=idf.get(term,0)*(tf*(k1+1))/(tf+k1*(1-b+b*(doc_lengths[doc_id]/avgdl)))

scores[doc_id]=scores.get(doc_id,0)+score

#返回按分数排序的TopK文档

returnsorted(scores.items,key=lambdax:x[1],reverse=True)[:top_k]

模拟向量检索:计算简单的TF-IDF向量并用余弦相似度

这里为了模拟,同样使用字频作为向量表示

defvector_search(query,top_k=5):

#计算查询向量(TF-IDF)

query_vec=[]

vocab=list(inverted_index.keys)

forterminvocab:

tf=query_terms.count(term)

query_vec.append(tf*idf.get(term,0))

query_vec=np.array(query_vec)

#计算每个文档的向量并求余弦相似度

doc_terms=list(text)

doc_vec=[]

tf=doc_terms.count(term)

doc_vec.append(tf*idf.get(term,0))

doc_vec=np.array(doc_vec)

#计算余弦相似度

ifnp.linalg.norm(doc_vec)==0ornp.linalg.norm(query_vec)==0:

cos_sim=0.0

cos_sim=np.dot(query_vec,doc_vec)/(np.linalg.norm(query_vec)*np.linalg.norm(doc_vec))

scores[doc_id]=cos_sim

定义查询

queries={

"Q1":"报销制度",#短查询,期待BM25擅长

"Q2":"如何推销保险产品"#开放式查询,语义同义词“推销”≈“销售”,期待向量检索擅长

forqid,queryinqueries.items:

bm25_results=bm25_search(query)

vec_results=vector_search(query)

print(f"\n查询:{query}")

print("BM25结果:",bm25_results)

print("向量检索结果:",vec_results)

combined_scores={}

fordoc_id,scoreinbm25_results:

combined_scores[doc_id]=combined_scores.get(doc_id,0)+0.6*(score/(bm25_results[0][1]ifbm25_resultselse1))

fordoc_id,scoreinvec_results:

combined_scores[doc_id]=combined_scores.get(doc_id,0)+0.4*score#假设向量相似度已是0-1

final_ranked=sorted(combined_scores.items,key=lambdax:x[1],reverse=True)

print("合并后Top结果:",final_ranked)

4.3Embedding模型微调

具体问题包括:

优化策略与原理:对Embedding模型进行有监督微调或继续预训练:

数据预处理:清洗数据,去除噪音和冗余,确保文本质量。对中文需分词或使用适配中文的Tokenizer。

选择微调方法:

继续预训练(UnsupervisedDomainAdaptation):将BGE模型在大量领域文本上继续训练(如通过MaskedLanguageModel任务或者对比学习),让模型嵌入空间更贴合领域分布(Fine-TuneEmbedding:TheSecrettoImproveResponseRates|iWeaverAI)。

训练过程:使用上述方法在领域数据上微调BGE模型若干轮。过程中可引入验证集,评估模型在检索任务上的表现,选择最佳模型(比如根据验证集的NDCG@10或MRR指标挑选效果最好的checkpoint(GetbetterRAGbyfine-tuningembeddingmodels-Redis))。

效果评估:对比微调前后的模型检索性能。通常微调能显著提高领域内检索的召回率。例如,有报告显示对嵌入模型进行领域微调后,检索召回率直接提升了33%(Fine-TuneEmbedding:TheSecrettoImproveResponseRates|iWeaverAI)。

示例代码:微调Embedding模型

下面给出使用HuggingFaceTransformers/Sentence-Transformers框架微调Embedding模型的代码示例。该过程包括:加载预训练模型(如BAAI/bge-base-zh),准备训练的问答对数据集,将数据转换为模型训练需要的格式(InputExample),定义损失函数(使用MultipleNegativesRankingLoss适合只有正例的情况),然后使用Trainer或Sentence-Transformers自带的SentenceTransformer进行训练和评估。

fromsentence_transformersimportSentenceTransformer,InputExample,losses,models

fromtorch.utils.dataimportDataLoader

假设我们使用一个预训练的中文嵌入模型,例如BAAI发布的BGE模型

model=SentenceTransformer('BAAI/bge-base-zh')#加载预训练BGE中文基座模型

准备训练数据:ListofInputExample(question,positive_passage)

train_examples=[

InputExample(texts=["什么是保单的现金价值?","保单的现金价值是指保单在退保或某些情况下可领取的金额。"]),

InputExample(texts=["保险合同的冷静期有多长?","一般人寿保险合同都有10天的冷静期,在此期间可以无条件退保。"]),

#...更多问答对...

如果有负例,可以在texts加入第三项;只有正例时MultipleNegativesRankingLoss会自动负采样

train_dataloader=DataLoader(train_examples,batch_size=16,shuffle=True)

定义训练损失为多负样本排名损失(适用于只有正向对的情况)

train_loss=losses.MultipleNegativesRankingLoss(model)

(可选)定义评估方法:使用SentenceTransformer提供的InformationRetrievalEvaluator

fromsentence_transformersimportevaluation

evaluator=evaluation.InformationRetrievalEvaluator(query_embeddings,corpus_embeddings,relevant_docs)

配置训练参数并训练

num_epochs=1

warmup_steps=100

model.fit(

train_objectives=[(train_dataloader,train_loss)],

epochs=num_epochs,

warmup_steps=warmup_steps,

show_progress_bar=True

微调完成后保存模型

model.save("finetuned-bge-insurance")

代码说明:

SentenceTransformer('BAAI/bge-base-zh'):加载预训练的BGE中文模型作为基线(需要确保本地或网络可用该模型)。

MultipleNegativesRankingLoss:如果我们只有正例对,这个损失函数会将同一batch内其它样本作为负例进行对比学习(GetbetterRAGbyfine-tuningembeddingmodels-Redis)。这样模型会拉近正确问答对的向量距离,拉开无关对的距离。

model.fit(...):开始训练Embedding模型。可以加入评估器InformationRetrievalEvaluator定期评估模型在验证集上的MRR/NDCG等指标(GetbetterRAGbyfine-tuningembeddingmodels-Redis)。参数如epochs和warmup_steps应根据数据量和观察的收敛情况调整。

4.4检索结果重排(Rerank)

优化策略与原理:

候选集获取:首先利用混合检索得到较宽松的候选列表,例如Top50段落。

融合:重排序可以应用于混合检索的合并结果(如本场景),也可以在单一检索模式后使用。例如仅用BM25检索也能在其结果后加一层语义重排来提升效果(超越向量检索!混合检索+重排序改善RAG应用|新程序员-CSDN博客)。因此,重排策略通用性强,能显著改善最终顶层结果的准确性。

示例代码:Cross-Encoder重排候选结果

fromsentence_transformersimportCrossEncoder

query="最近公司的车险理赔流程是什么?"

candidates=[

"公司2020年的车险理赔流程包括报案、查勘定损、提交资料、理赔审核和赔付。",

"最新的车险理赔流程(2023年更新)为:在线报案->现场查勘->材料上传->理算审核->赔款支付。",

"车险理赔是指车辆发生保险事故后,被保险人向保险公司申请赔偿的过程。"

初始化Cross-Encoder重排模型(此示例用英文MiniLM模型,占位)

reranker=CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')#实际应使用中文微调模型

pair_inputs=[(query,doc)fordocincandidates]

scores=reranker.predict(pair_inputs)

将候选及得分配对,按分数排序

ranked_results=sorted(zip(candidates,scores),key=lambdax:x[1],reverse=True)

fordoc,scoreinranked_results:

print(f"Score:{score:.4f}|Passage:{doc[:30]}...")

说明:

4.5:评估指标与效果评估

核心问题分析:为了验证以上优化是否提升了系统性能,我们需要引入科学的评估指标来量化召回率和准确率的改进。信息检索领域常用的指标有:

优化策略与原理:在优化过程中和上线前后,需持续评估指标:

计算指标:编写脚本计算每种指标。在Python中,可使用现有评测库如pytrec_eval来方便计算Precision、Recall、NDCG等(EvaluationMetricsforSearchandRecommendationSystems|Weaviate)(EvaluationMetricsforSearchandRecommendationSystems|Weaviate)。也可自行根据公式实现。

对比实验:分别运行基线和各优化方案,记录指标值。例如,对比“仅向量”vs“混合检索”vs“混合+微调”vs“混合+微调+重排”,观察指标变化。

分析结果:如果某些查询的指标未提升,深入分析原因(例如某查询意图分类错误导致没用对策略)。不断迭代调整。

通过量化指标,我们可以直观证明每个优化点的价值。例如混合检索可能主要提升Recall@K(召回率)而Cross-Encoder重排显著提升MRR和NDCG(排序质量)。在验证达到预期提升后,再将新方案应用到生产。

示例代码:计算MRR、NDCG、P@K指标

@假设有两个查询的测试集

ground_truth={

retrieved={

defcalculate_mrr(gt,res):

"""计算平均倒数排名(MRR)"""

total_reciprocal_rank=0.0

num_queries=len(gt)

forq,relevant_docsingt.items:

rank=0

foridx,doc_idinenumerate(res.get(q,[]),start=1):

ifdoc_idinrelevant_docs:

rank=idx

break

ifrank!=0:

total_reciprocal_rank+=1.0/rank

returntotal_reciprocal_rank/num_queries

defcalculate_precision_at_k(gt,res,K=3):

"""计算每个查询的Precision@K,并返回平均Precision@K"""

precisions=[]

retrieved_k=res.get(q,[])[:K]

ifnotretrieved_k:

rel_count=sum(1fordoc_idinretrieved_kifdoc_idinrelevant_docs)

precisions.append(rel_count/K)

print(f"{q}的Precision@{K}:{rel_count}/{K}={rel_count/K:.2f}")

#返回平均Precision@K

returnsum(precisions)/len(precisions)ifprecisionselse0.0

mrr_value=calculate_mrr(ground_truth,retrieved)

precision3=calculate_precision_at_k(ground_truth,retrieved,K=3)

print(f"MRR={mrr_value:.3f}")

print(f"平均Precision@3={precision3:.3f}")

计算NDCG@K

defcalculate_ndcg_at_k(gt,res,K=3):

total_ndcg=0.0

num_q=len(gt)

#计算DCG@K

dcg=0.0

foridx,doc_idinenumerate(res.get(q,[])[:K],start=1):

ifidx==1:

dcg+=rel

dcg+=rel/math.log2(idx)#折损:log2(rank)

#计算IDCG@K(理想情况下的DCG)

ideal_rels=sorted([1.0]*len(relevant_docs)+[0.0]*K,reverse=True)[:K]

idcg=0.0

foridx,relinenumerate(ideal_rels,start=1):

idcg+=rel

idcg+=rel/math.log2(idx)

ndcg=dcg/idcgifidcg>0else0.0

total_ndcg+=ndcg

print(f"{q}的NDCG@{K}:{ndcg:.3f}")

returntotal_ndcg/num_q

avg_ndcg3=calculate_ndcg_at_k(ground_truth,retrieved,K=3)

print(f"平均NDCG@3={avg_ndcg3:.3f}")

根据这个示例数据,程序会输出每个查询的Precision@3和NDCG@3,以及总体的平均值。例如可能输出:

Q1的Precision@3:2/3=0.67

Q2的Precision@3:1/3=0.33

MRR=0.583

Q1的NDCG@3:0.789

Q2的NDCG@3:0.500

平均NDCG@3=0.644

4.6召回总结

综合上述优化方案:

Embedding模型微调:在金融保险语料上细调Embedding,使模型更懂领域语言,提高语义匹配效果(Fine-TuneEmbedding:TheSecrettoImproveResponseRates|iWeaverAI)。

评估指标:用MRR、NDCG、P@K等指标验证召回和排序效果的提升,指导迭代优化。

每个优化点相辅相成:混合检索和模型微调侧重提高召回率,重排序侧重提升准确率,最终使RAG问答系统能更准确地找到并提供答案。在实际落地时,应根据指标反馈不断调整策略,例如调整BM25与向量结果权重、增加训练数据微调Embedding、引入更强大的重排模型等,以持续优化用户提问的解答效果。通过完整的方案和持续评估,我们有信心显著改善金融保险场景下RAG问答系统的性能,提升用户满意度。

5.生成阶段优化

5.1核心问题

多模态知识的利用困难:金融保险领域的知识库包含PDF手册、PPT演示、文本说明、视频讲解等多种形式。如果不对这些不同格式的数据进行预处理和结构化,检索时可能遗漏关键信息,导致答案不全面。

5.2实际案例(Case)

假设一位用户向保险问答系统咨询:

用户:我有一款寿险产品ABC,它的保障范围是什么?

接着用户继续问:

用户:这个怎么申请?(这里“这个”指代寿险产品ABC)

优化策略

为解决上述问题,我们可以从以下几个方面优化生成阶段:

整合多模态知识,提高答案全面性:预先对PDF、PPT、文本、视频等资料进行解析和结构化处理,存入统一的向量数据库以便检索。比如:

PDF/PPT:提取文字内容,保留章节标题、表格数据等结构信息,将长文档按段落或页面切分成知识片段(chunks),并为每个片段添加文档名称、页码/幻灯片编号等元数据。

代码示例(GPT-4适用)

初始化向量数据库,已预先加载PDF文本片段、PPT文本片段、视频字幕片段等多模态知识

vector_store=VectorStore(data=load_multimodal_chunks)

conversation_history=[]#用于保存多轮对话的问答历史

defis_follow_up(question):

"""简单判定问题是否为跟进问答(根据代词/省略等)"""

follow_keywords=["这个","那种","这样","怎么","如何","吗"]#简化判断逻辑

returnany(kwinquestionforkwinfollow_keywords)

defanswer_question(user_question):

#如果是跟进问题且有历史,则将上一次用户提问或主题融入当前问题

ifconversation_historyandis_follow_up(user_question):

last_topic=conversation_history[-1]["topic"]#上一轮对话主题

#将用户问题重写,加入主题关键字

rewritten_q=f"{last_topic},{user_question}"

rewritten_q=user_question

docs=vector_store.search(rewritten_q,top_k=3)

context="".join([f"[{i}]{doc.content}\n"fori,docinenumerate(docs,start=1)])

#构造提示,要求GPT-4根据检索片段回答,并标注引用编号

prompt=(

f"{context}\n"

f"用户问题:{user_question}\n"

"回答:"

#调用GPT-4模型生成答案

answer=openai.ChatCompletion.create(model="gpt-4",messages=[{"role":"user","content":prompt}])

answer_text=answer["choices"][0]["message"]["content"]

#记录本轮主题(可选:从user_question或检索结果中抽取主题关键词)

topic=extract_topic(user_question,docs)

conversation_history.append({"question":user_question,"answer":answer_text,"topic":topic})

returnanswer_text

模拟对话:

q1="ABC寿险的保障范围是什么?"

q2="这个怎么申请?"#跟进问答,指代上文提到的“ABC寿险”

THE END
1.保险图片新闻大全金投保险网金投网详解车险购买技巧 你知道多少 11图 保险图片新闻 住院报销的保险有哪些 你们知道多少 8图 保险图片新闻 12图 河南南阳发生地震 地震保险公司赔吗 保险图片新闻 1 2018年养老保险大变化 这里来详解 9图 保险图片新闻 点击加载更多 轻应用 期货App 期货开户 行情中心 金投网App 日历 快讯https://m.cngold.org/insurance/lm3690/
2.2025最新实力见证,享受特别待遇:官方抢先?专属手机赌博软件输二十万 转转乐棋牌官网 nba消息战报及排名 试机号3d今天查询开机号 h5飞禽走兽 cq9跳腾龙公司网址 五星体育频道节目单 斗地主免费版斗地主 大赢家-足球比分即时比分 99安卓游戏网站 银河正app排行榜前十名 18送38彩金 影汇备用网站 博亚洲在线 赢家交易版手机下载 盘让球盘基础知识http://m.aqpjss.cn/JVB/detail/ifSjWxQtd.html
3.行业标杆?.?:图片报:在对阵狼堡的比赛后加拿大彩票公司官网,北京快八开奖结果查询今天,多宝体育手机版官方网站,大胜发棋牌游戏,冰球摆脱步骤加拿大28下载苹果,大富翁6下载安卓版下载,华体育hth官网登录下载,南宫游戏官网入口,大圣电竞奖金怎么提现南方双彩网3d专家预测,大奖唯一官网,博牛彩票官网,大发彩票首页注册,十款大信誉可靠平台嘀嘀彩票开户网址,大圣娱乐http://m.donniwan.cn/NTM/detail/tOpUBTLdPY.html
4.华商网华商网主要提供在线陕西新闻资讯和互动娱乐平台服务。旨在为陕西网民提供丰富的陕西新闻资讯和多元的网络生活社区。作为陕西地区乃至西部地区影响力领先的互联网品牌,华商网为三秦大地超过1500万用户提供全面及时的陕西新闻资讯、互动娱乐服务,内容涵盖了国内外突发新闻、http://hsw.cn/
5.业界翘楚?.?:图片报:沙欣痛批马伦比赛中博乐体育官方下载,大乐透浙江风采网走势图,可的捕鱼电玩,大丰收捕鱼游戏,大哥四肖八码是什么网站的创世彩票注册开户,大富翁准确打一数字,四码期期必中法,凯发体育官网APP,凤凰平台上93O79 判官团队大吉大利棋牌手机游戏,台湾娱乐,喜乐棋牌,大家网论坛免费资料,利博手机版登录入口大发彩票网址788356入口,十大外围公司排名http://m.crealliance.cn/ISR/detail/PaKyNyr.html
6.商业网腾讯自研大模型亮相 航空物流枢纽发展指数排名出炉 市场需求催热 公募债基规模连续7个月上升 践行创新驱动深圳、上海位列前五 汾阳市对2024年度城乡居民基本医疗保险参保缴费工作进行安排部署 第十届台湾青年深圳、上海位列前五 “星闪概念股”创耀科技涨停 公司星闪芯片不会用于手机终端 甘肃夏河泥石流灾害http://www.mbahome.com/
7.车险排名前十大公司图片车友交流懂车帝提供车险排名前十大公司图片的车友交流详细内容,懂车帝是一个汽车资讯平台,懂车更懂你。我们提供最新汽车报价,汽车图片,汽车价格大全,行情、评测、导购等内容,看车选车买车就上懂车帝。https://www.dongchedi.com/tag/ugc/15103358
8.上证指数(000001)股票行情新股申购新股日历资金流向AH股比价主力排名板块资金个股研报行业研报盈利预测千股千评年报季报龙虎榜单限售17:53:26|【中欧双方团队就电动车谈判开始接触】前几天,商务部部长王文涛与欧盟委员会贸易和经济安全17:40:28|4月13日,上大股份(301522)发布公告,本次解除限售股份的数量为372万股,占公司总股本的1.0011https://quote.eastmoney.com/zs000001.html
9.车天下热门好去处 别人的城市是你眼中的风景 上海 三亚 杭州 成都 北京 广州Copyright ? 2025 北京车天下资讯有限公司 版权所有 京ICP备09074410号-3 京公网安备:11010602103798号 http://chetx.com/
10.APP注册?入口登录博友彩app官方,半岛真人体育官网,千亿棋牌官网最新版qy988,全球赌场排名前十名,全民49彩票正规吗双赢棋牌sg飞艇网址,大地网投不见了,华信app下载安卓,吧手机版下载,哆哆海宝即胜体育注册,博天下彩票安卓版,大发赌场注册登录下载,博球体育,可以赢钱的网站游戏牛牛大富翁图纸打印版,北安彩票登录注册,博雅乐山棋牌官网apphttp://m.bynr-es.cn/GIT/detail/CLv.html
11.APP平台电子用心打造盛世ll下载网址,竞彩足球比分赔付率表,果博东方公司在那里,彩经一肖图片,kaiyun登录入口app球探网比分拼十牛牛,芝加哥娱乐城,云海游戏app,彩5官网app苹果,靠比软件app官方下载澳门电子游戏app排行榜前十名欧冠排名万博z 爆大奖视频 3d往期开奖1000期 永利皇宫提现一直审核中 西甲回放 全明星赛2016 大掌柜棋牌http://m.httnyci.cn/FWA/detail/wpk.html
12.十里八街姚蕊最帅三个图片「僕のためにわざわざ薄味でつくったの」另外,该笔债权还由北京新东飞祥义信息咨询有限公司100%股权、瑞顺鸿业100%股权、精达地产52.5%股权和然而,这是前所未有的一步,这是向第三次世界大战爆发迈出的一大步,而美国人将利用一个即将离任的以汽修店为幌 雇人当车手制造单方事故骗保与此同时,一个以犯罪嫌疑人李某为首的车险骗保团伙也被警方https://www.gaolangs.com/android/nvshenghenanshengbuyiqichachacha/
13.超凡赛事,尊享独特优势:下载网址专属最新三d开奖结果查询今天,充值优惠大的彩票平台,博看网官网,万利网彩票平台,彩运网公司招聘广东体彩手机投注,澳彩资料库官方网站王牌神料,体育电子网赌软件下载,大奖娱乐为什么打不开?,澳门最新网站大全澳门最快开奖历史查看香港,乐橙足彩下载官网,伸手党实况足球论坛,58买球中国,海南头尾奖是什么意思玛丽水果机手机版http://m.xhtx59.cn/QWK/detail/atVScOEvA.html
14.斗球直播app下载安卓版2025-04-05 10:50【7分钟前】?【 斗球直播app官方版下载 】支持:32/64bi系统类型:(官方)官方网站IOS/Android通用版/手机APP(2025APP下载)《斗球直播app官方版下载》直播吧3月7日讯 《队报》报道,虽然丰塞卡被禁赛9个月,但预计他可以带里昂踢欧联杯比赛。里昂主帅http://changzhi.guzhibiao118.com/
15.荣耀特权?.?:电讯报:曼城出售球员赚取高加拿大彩票max怎么中,凯发k8注册入口,千亿体肓app,利豪棋牌网址,全球赌场排名榜前十名免费领60元红包,大吉彩票娱乐平台,华体会手机登陆网站,在线注册网站,双人大富翁手机版游戏加拿大28开奖号码,四码必中期期准,四冲六冲下载,在哪找百家乐在线官网,在哪找万博体育足猜彩下载网址?在线购买福彩快乐8,充10块提现10块http://m.jingchuanget.cn/ITX/detail/hEWhTUlk.html
16.m.dtpulxp.cn/nianshuan/988723.html大屁股干妈乱伦 谢小蒽在线视频 强迫屈辱玩弄粗暴h 319.6MB 546好评 yuojizz黑吊 补课老师…好紧 好湿好深h 公司团建迷玩3p少妇国产裸模大尺度掰穴图片 亚洲gay里gay气男同性恋视频网站观看 xxxnxkino 辣文合集乱 北条麻妃的十部巅峰之作 91无码毛婷被摄影师http://m.dtpulxp.cn/nianshuan/988723.html
17.致富有道?.?:给你机会不中用!利物浦2连平大从棋牌大世界官方版在app买彩票算赌博吗,大发888体育押注官网,发彩网彩票发彩网娱乐,北京跑车下载,劲爆体育频道在线直播北北彩票平台注册,大公鸡七星彩官方下载手机版,即时波胆,博易宝彩票娱乐平台,大红鹰平台网站多少四季彩注册链接,口袋b计划官方版下载,四川快三,华纳公司客服上分,大发彩神8爭霸最新版使用方法大发http://m.jiaoyuzhushou.cn/KBB/detail/ILaXC.html
18.hth网页版在线登录入口官方版《hth网页版在线登录入口》消息称苹果iOS 19 UI大变 图标菜单全部重新设计高志丹表示,要加大科技创新,扩大开放交流,巩固好奥运强国建设的成果,全面做好米兰冬奥会、洛杉矶奥运会的准备工作,在巩固传统优势项目的同时,在田径、“三大球”等基础和重点项目上力争取得实质性的突破。http://wap.zabaox.com/UhR/detail/nRwcna/
19.报告大厅首选市场调研报告门户推荐品牌|2024年年报海南地区A股销售费用排名前三大上市公司 2024年年报铁路公路行业A股营业利润排名前五大白菜产品报价预警:河南省新野县黄心大白菜价格4周上涨40%(20 蜜桃产品报价预警:江苏省惠山区白凤桃价格https://www.chinabgao.com/
20.赢取超值大奖:直播体育APP聚宝棋牌官网,91新棋牌游戏,魅影快三直播游戏,3d开机号吗,足球解说员排名前十名nba直播的免费软件,青岛有赌博游戏厅吗,鸿利代理,途游如何和好友玩,永利广场饭店彩票注册送18元app在线,988棋牌最老版,8848官网,网络真人斗地主,168赛车开奖网进口皇冠v6,贴金麻将,紫金阁二手房,竞彩足球计算器旧版,2015011期3d开奖http://m.212sq.cn/VCD/detail/ggbkiMibx.html
21.豆瓣怎么切换账号?豆瓣切换账号教程天天短讯使用豆瓣的时候,有的小伙伴有多个账号,想重新选择账号切换登录,但不知道怎么弄,下面小编就来为大家介绍一下方法教程。 豆瓣怎么切换账号?豆瓣切换账号教程 1、首先我们打开豆瓣,点击右下角我的; 2、然后点击左上角的设置图标; 3、最后点击退出登录,重新切换账号登录即可。 http://m.chynews.cn/tech/tech/2023/0313/61714.html
22.?鱼跃龙门勇闯通关金誉app下载,遵义麻将,金利来是什么档次的包包,金和顺娱乐,金沙欧布图片金贝有借款平台是真的吗安全吗,酷炫财神棋牌官网,金金网新春网络寄语登录平台2024,银牛棋牌苹果版,雅虎日本二手拍卖随乐游云游戏平台,重大回答最大赌博软件的问题,金钱兔pg最高爆多少达y.进入官网.中国,金龙影视文化传媒有限公司电话多少,金都休闲http://mgomb.yujiangapp.com/
23.开元棋脾595cc手机版开元棋脾595cc安卓版百度TOP1?开元棋脾595cc官网-APP下载?已认证: 地址:http://www.funsdiy.com?(2025全站)「体育、足球、电竞、电子、真人、棋牌、娱乐、直播、注册、登录、入口、官方、官网、全站、平台、买球、世界杯、欧洲杯、欧国联焦点战、nba、cba、五大联赛下http://m.lq.funsdiy.com/
24.品牌沉淀,抢注必享:?♂?平台登录站点万彩彩票,棋牌开元,9832万众堂资料大全,科内利亚诺欧冠赛程,大赢家足球比分901516226注册注册,广东红中麻将一元一分,可以养鱼捕鱼游戏,cq9传奇电子吧历史背景,够力彩票下载久久发彩票cp,狗亚公司,真娃斗地主,2018网上电子游艺网站,洛阳棋牌最可靠赚钱软件,可以离线玩的捕鱼游戏下载,易投官网,众彩娱乐怎么注册,竞彩网比分http://m.okooyvf.cn/GKV/detail/JyDSGPrZEwIf.html
25.best365官方网站登录入口1.?「科普」best365官方网站登录入口官网-APP下载支持:winall/win7/win10/win11系统类型:best365官方网站登录入口下载(2025全站)最新版本IOS/安卓官方入口V7.8.9.78(安全平台)登录入口《best365官方网站登录入口》app安卓是专为平安车险工作人员设计的一款理财应用软件,利用这款软http://m.share.shemegui.com/htm/detail/sspeahy.html
26.最新优惠,尊享独特优势:下载网址专属平台qq群赌球骗局,十拿九稳了3d预测,港澳周公神算网,桂林办民族共同体大讲堂,盛易棋牌186娱乐app,足彩买升赔还是降赔,云南快乐十分遗漏数据查询,恒大EMSneo网页版,领域棋牌最好玩的在线游戏g3娱乐排名,福彩3d最强高手论坛,彩经网官方网17500,星河中文版官方下载,打鱼微信24小时上下分欠债500万无力偿还会坐牢吗,哪里可以http://m.nano-star.cn/UUX/detail/gvEApl.html