微信扫码
与创始人交个朋友
我要投稿
一、RAG基本流程
为了让大模型能回答关于公司规章制度的问题,我们需要构建一个 RAG 应用,RAG应用的工作流程包括:
解析:加载公司规章制度文档(如pdf、docx等),并解析为文本形式;
分段:对解析后的文档进行分段,因为大模型的输入长度是有限的;
建立索引:使用嵌入模型(embedding 模型)对切片后的文档建立索引,并以向量数据库形式存储,便于后续检索;
发起提问:用户输入的问题与向量数据库的知识进行匹配,并通过大模型生成结合知识库与用户问题的回复。
通过LlamaIndex,只需要几行代码就可以完成上述功能。
# 导入依赖
from llama_index.embeddings.dashscope import DashScopeEmbedding,DashScopeTextEmbeddingModels
from llama_index.core import SimpleDirectoryReader,VectorStoreIndex
print("正在解析文件...")
# LlamaIndex提供了SimpleDirectoryReader方法,可以直接将指定文件夹中的文件加载为document对象,对应着解析过程
documents = SimpleDirectoryReader('./docs').load_data()
print("正在创建索引...")
# from_documents方法包含切片与建立索引步骤
index = VectorStoreIndex.from_documents(
documents,
# 指定embedding 模型
embed_model=DashScopeEmbedding(
# 你也可以使用阿里云提供的其它embedding模型:https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hw
model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2
))
print("正在创建提问引擎...")
query_engine = index.as_query_engine(
# 设置为流式输出
streaming=True,
# 此处使用qwen-plus模型,你也可以使用阿里云提供的其它qwen的文本生成模型:https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u
llm=DashScope(model_name="qwen-plus")
)
print("正在生成回复...")
streaming_response = query_engine.query('我们公司项目管理应该用什么工具')
print("回答是:")
streaming_response.print_response_stream()
这段代码输出如下。已经可以让大模型进行普通的回答了。
正在解析文件...正在创建索引...正在创建提问引擎...正在生成回复...回答是:公司项目管理可以使用Jira或Trello这样的项目管理软件。这些工具能够帮助团队更好地规划和跟踪项目的进度,确保任务按时完成。同时,它们还支持任务分配、时间跟踪和团队协作等功能,有助于提高工作效率。
你可能会发现,创建索引消耗的时间比较长。如果能够将索引保存到本地,并在需要使用的时候直接加载,而不是重新建立索引,那就可以大幅减少从问题到给出回复的耗时。为此,可以借助LlamaIndex提供的保存与加载索引文件的方法来实现这一点,这里就不再赘述。
二、优化提示词改善回答质量
接下来,我们希望在问到上面类似的问题时,大模型都可以自动给出下载地址。因此可以考虑改进下程序,在收到用户问题后,自动补充一些关于回答内容的要求:
print("正在生成回复...")streaming_response = query_engine.query('我们公司项目管理应该用什么工具?如果是工具咨询类问题,请务必给出下载地址链接。')print("回答是:")streaming_response.print_response_stream()
在上面代码调整中在问题后面增加了 "如果是工具查询类问题,请务必给出下载地址链接。"的提示词,来获得需要的更好指定的效果。
对于项目管理,推荐使用Jira或Trello。这两个工具都能帮助团队有效地管理项目进度,确保任务按时完成。Jira更适合复杂项目管理和软件开发团队,而Trello则以其简洁直观的界面受到广泛欢迎,适合各种规模的项目。- Jira: 可以访问 [Atlassian官网](https://www.atlassian.com/software/jira) 下载。- Trello: 可以访问 [Trello官网](https://trello.com/) 下载。
也可以进一步优化原来的提示词模板如下。
from llama_index.core import PromptTemplate
prompt_template_string = (
"你是公司AI助理,你需要简明扼要的回答大家的问题"
"注意事项:\n"
"1. 根据上下文信息而非先验知识来回答问题。\n"
"2. 如果是工具咨询类问题,请务必给出下载地址链接。\n"
"以下是参考信息。"
"---------------------\n"
"{context_str}\n"
"---------------------\n"
"问题:{query_str}\n。"
"回答:"
)
prompt_template = PromptTemplate(prompt_template_string)
query_engine = index.as_query_engine(streaming=True, llm=DashScope(model_name="qwen-plus"))
query_engine.update_prompts({"response_synthesizer:text_qa_template": prompt_template})
def ask_v3(question, query_engine):
streaming_response = query_engine.query(question + "如果是工具查询类问题,请务必给出下载地址链接。")
streaming_response.print_response_stream()
ask_v3('我们公司项目管理应该用什么工具', query_engine=query_engine)
从上面可以看出,提示词工程 PE 是第一个我们需要去优化的重要工作。现在网上有很多的提示词工程优化方法,许多 MaaS 平台也提供了提示词自动优化工具来帮助完善提示词。这里是一个开源的提示词工程指南 https://www.promptingguide.ai/zh,可以作为参考。
三、意图分类,不同问题路由到对应处理方式
随着越来越多的同事使用你开发的答疑机器人,他们开始发现答疑机器人不只是可以用来查资料,还可以用来帮助他们审查文档、翻译文档。比如你可以让机器人帮你检查一段文档是否存在问题:
rag.ask('请帮我检查下这段文档:地球和月亮之间存在着玩有引力,所以月亮会绕着地球。', query_engine=query_engine)
得到的一个回复是:
这段文档中有一个小错误,正确的表述应该是:“地球和月亮之间存在着万有引力,所以月亮会绕着地球转。”
修改建议:
- 将“玩有引力”改为“万有引力”。
- 将句尾的“。”改为“转。”以使句子更加通顺。
为了解决上述问题,可以让这类问题直接输入给大模型,而不必经过 RAG 链路来生成答案。同时,考虑到让同事只需要使用全公司统一的一个问答机器人,而无需在多个机器人之间切换,也可以借助大模型来识别问题的类型,然后根据不同的问题类型,在背后调用不同的大模型应用来生成答案。
通过大模型提示词可以先有一个简单的POC实现:
prompt = '''
【角色背景】
你是一个问题分类路由器,负责判断用户问题的类型,并将其归入下列三类之一:
1. 公司内部文档查询
2. 内容翻译
3. 文档审查
【任务要求】
你的任务是根据用户的输入内容,判断其意图并仅选择一个最贴切的分类。请仅输出分类名称,不需要多余的解释。判断依据如下:
- 如果问题涉及公司政策、流程、内部工具或职位描述与职责等内容,选择“公司内部文档查询”。
- 如果问题涉及任意一门非中文的语言,且输入中没有任何出现任何外语或出现“翻译”等字眼,选择“内容翻译”。
- 如果问题涉及检查或总结外部文档或链接内容,选择“文档审查”。
- 用户的前后输入与问题分类并没有任何关系,请单独为每次对话考虑分类类别。
【Few-shot 示例】
示例1:用户输入:“公司内部有哪些常用的项目管理工具?”
分类:公司内部文档查询
示例2:用户输入:“How can we finish the assignment on time?”
分类:内容翻译
示例3:用户输入:“https://help.aliyun.com/zh/model-studio/user-guide/long-context-qwen-long”
分类:文档审查
示例4:用户输入:“技术内容工程师需要设计和开发⾼质量的教育教材和课程吗?”
分类:文档审查
示例5:用户输入:“技术内容工程师核心职责是什么?”
分类:公司内部文档查询
【用户输入】
以下是用户的输入,请判断分类:
'''
print(get_question_type('https://www.promptingguide.ai/zh/techniques/fewshot'))
print('\n')
print(get_question_type('That is a big one I dont know why'))
print('\n')
print(get_question_type('作为技术内容工程师有什么需要注意的吗?'))
执行上面代码输出如下。可见已经初步实现了分类路由。通过明确的输出格式和 few-shot 示例,大模型可以更准确地识别问题类型并输出符合预期的结果格式。这种优化让分类任务更加标准化,为接下来添加路由模块应用到问答机器人中打下了基础。
文档审查
内容翻译
公司内部文档查询
有了路由模块后,就可以让问答机器人先识别问题的类型,在背后使用不同提示词和工作流程来回答问题。
def ask_v4(question):
question_type = get_question_type(question)
print(f'问题:{question}\n类型:{question_type}')
if question_type == '文档审查':
return llm.invoke('你是文档纠错专家,负责找出文档中或网页内容的明显错误,并且言简意赅的回复。如果没有明显问题,请直接回复没有问题\n' + question)
elif question_type == '公司内部文档查询':
return rag.ask(question, query_engine=query_engine)
elif question_type == '内容翻译':
return llm.invoke(f'你是一名翻译专家,你要识别不同语言的文本,并翻译为中文。\n{question}')
else:
return "未能识别问题类型,请重新输入。"
# 示例测试
query_engine = rag.create_query_engine(index=rag.load_index())
print(ask_v4('https://www.promptingguide.ai/zh/techniques/fewshot'))
print('')
print(ask_v4('请帮我检查下这段文档:技术内容工程师有需要进行内容优化与更新与跨部门合作吗?'))
print('')
print(ask_v4('技术内容工程师有需要进行内容优化与更新与跨部门合作吗?'))
print('')
print(ask_v4('A true master always carries the heart of a student.'))
最后结果如下:
问题:https://www.promptingguide.ai/zh/techniques/fewshot
类型:文档审查
没有问题。
问题:请帮我检查下这段文档:技术内容工程师有需要进行内容优化与更新与跨部门合作吗?
类型:文档审查
没有问题。
问题:技术内容工程师有需要进行内容优化与更新与跨部门合作吗?
类型:公司内部文档查询
技术内容工程师确实需要进行内容优化与更新,这包括根据学习者的反馈和评价来识别并调整内容中的潜在问题,以及定期更新材料以反映新的研究成果、技术进步和市场变化。此外,他们也需要与教学设计师、教育心理学家、技术团队及市场营销人员等多个部门紧密合作,确保内容的技术实施过程顺利进行,并有效传达给目标受众。这种跨部门的合作有助于共同创造出既具教育价值又具市场竞争力的产品。None
问题:A true master always carries the heart of a student.
类型:内容翻译
这是一句英文,翻译成中文是:“真正的高手总是怀有一颗学生的心。”
可以看到,目前已经可以更准确回答问题了。
四、RAG 链路环节优化,提升问答准确度
这一步骤是最繁杂,也是 RAG 应用最核心的部分。需要更深入的了解理解 RAG 的原理和技术细节,以及常见的一些文档及其格式内容的处理建议,在实践中不断优化,才会取得比较好的效果。
先来看大模型怎样回答这么一个问题:“张伟是哪个部门的?”。
query_engine = rag.create_query_engine(rag.load_index())
def ask(question, query_engine):
rag.update_prompt_template(query_engine=query_engine)
print('提问:' + question)
response = query_engine.query(question)
print('回答:', end='')
response.print_response_stream()
print('\n\n检索召回的文档切片如下:')
for source_node in response.source_nodes:
print(source_node)
return response
response = ask('张伟是哪个部门的', query_engine)
此时由于 RAG 检索策略默认只能检索前 2 个文档切片,所以出现了回答不了的情况。
提问:张伟是哪个部门的
回答:根据提供的参考信息,没有找到名为张伟的员工信息。如果您能提供更多详细信息,例如部门或其他联系方式,我可以帮助您进一步查找。
检索召回的参考信息如下:
Node ID: c98bf1c0-c88d-43ee-b3ef-56079b335404
Text: 核,提供⾏政管理与协调⽀持,优化⾏政⼯作流程。⾏政部 秦⻜ 蔡静 G705 034 ⾏政 ⾏政专员 13800000034
维护公司档案与信息系统,负责公司通知及公告的发布,
Score:0.171
Node ID: daa30e2b-cbb1-4a5a-b2ae-e7f5cae4e3b9
Text: ⽀持。绩效管理部 韩杉 李⻜ I902 041 ⼈⼒资源 绩效专员 13800000041
建⽴并维护员⼯绩效档案,定期组织绩效评价会议,协调各部⻔反馈,制定考核流程与标准,确保绩效
Score:0.169
这个时候可以尝试调整检索策略,一次检索出5个文档切片。
from llama_index.llms.dashscope import DashScope
index = rag.load_index()
query_engine = index.as_query_engine(
streaming=True,
llm=DashScope(model_name="qwen-plus"),
# 一次检索出 5 个文档切片,默认为 2
similarity_top_k=5
)
response = ask('张伟是哪个部门的', query_engine)
这个时候可以看到回答正确了。
提问:张伟是哪个部门的
回答:张伟是IT部的IT专员。
检索召回的文档切片如下:
Node ID: c98bf1c0-c88d-43ee-b3ef-56079b335404
Text: 核,提供⾏政管理与协调⽀持,优化⾏政⼯作流程。⾏政部 秦⻜ 蔡静 G705 034 ⾏政 ⾏政专员 13800000034
维护公司档案与信息系统,负责公司通知及公告的发布,
Score:0.171
Node ID: daa30e2b-cbb1-4a5a-b2ae-e7f5cae4e3b9
Text: ⽀持。绩效管理部 韩杉 李⻜ I902 041 ⼈⼒资源 绩效专员 13800000041
建⽴并维护员⼯绩效档案,定期组织绩效评价会议,协调各部⻔反馈,制定考核流程与标准,确保绩效
Score:0.169
Node ID: 445d424e-d896-4bde-b4de-85f7a4e78ee4
Text: 效管理部 南 ⻜ 0 ⼒资源 效专员 0
责制定绩效考核体系,组织绩效评估的实施与反馈,撰写评估报告,分析绩效数据以提出优化建议,提供决策
Score:0.154
Node ID: 5d98660f-2745-400a-bf45-eca616189317
Text: 组织公司活动的前期准备与后期评估,确保公司各项⼯作的顺利进⾏。IT部 张伟 ⻢云 H802 036 IT⽀撑 IT专员
13800000036 zhangwei036@educompany.com 进⾏公司⽹络及硬件设备的配置
Score:0.140
Node ID: 74f4a375-a55c-4971-97ab-d6045c6b241e
Text: ⽀持,确保⼈⼒资源部⻔顺畅运作。⾏政部 黎晗 蔡静 G704 033 ⾏政 ⾏政专员 13800000033
负责采购办公设备与耗材,登记与管理公司固定资产,协助实施绩效考
Score:0.131
可以看到,调整了召回数量后,答疑机器人能够回答「张伟是哪个部门?」这个问题了。
截止目前,已经完成了一些改进,让答疑机器人的问答准确度更高了。但在实际生产环境中,可能会遇到的问题远不止于此。系统全面地了解 RAG 工作流程,有助于进一步提升回答效果。RAG工作原理如下图所示:
RAG 应用中每个环节都有改进空间。
4.1 文档解析与切片阶段
大模型在回答问题时拿到的文档切片如果缺少关键信息,会回答不准确;如果拿到的文档切片非关联信息过多(噪声),也会影响回答质量。信息过少或过多,同样也会影响检索效果。因此,在建立索引的前期,对文档进行解析与切片阶段时,需要想办法最终的切片信息要完整,但也不要包含太多干扰信息。
在文档解析与切片阶段,从参考信息完整度的角度,可以把可能出现的问题分为两大类:参考信息缺失、干扰信息过多。
问题大类 | 细分类型 | 改进策略 |
---|---|---|
参考信息缺失 | 没有相关知识文档 | 补充文档 |
有相关的知识文档,但和用户问题表述方式不接近 | 改进文档,补充用户视角描述信息 | |
文档类型不统一,部分格式的文档不支持解析 | 开发对应文档格式的解析器,或转换文档格式 | |
已支持解析的文档格式里,存在一些特殊内容(比如嵌入了表格、图片、视频等) | 改进文档解析器 | |
文档切片长度过短,有效信息被截断 | 扩大切片长度,或结合具体业务开发为更合适的切片策略 | |
…… | …… | |
干扰信息过多 | 文档中有很多主题接近的内容(比如工作手册文档中,需求分析、开发、发布部署每个阶段都有注意事项和操作指导等) | 扩写文档标题及子标题,如[注意事项]扩写成[需求分析>注意事项]; 建立文档元数据(打标)等 |
文档切片长度过大,引入过多干扰项 | 减少切片长度,或结合具体业务开发为更合适的切片策略 | |
…… | …… |
上面列出了各种场景,限于篇幅不一一展开。重点说下常见的 PDF 文件转换成更亲近大模型的 Markdown 文档格式的一个场景。
在阿里云百炼中,我们可以借助百炼提供的 DashScopeParse 来完成 PDF、Word 等格式的文件解析。DashScopeParse 背后使用了阿里云的[文档智能](https://www.aliyun.com/product/ai/docmind)服务,能够从 PDF、Word 等格式的文件中识别文档中的图片、提取出结构化的文本信息进行处理。
from llama_index.readers.dashscope.utils import ResultType
from llama_index.readers.dashscope.base import DashScopeParse
import os
import re
import json
import nest_asyncio
nest_asyncio.apply()
# 使用环境变量
os.environ['DASHSCOPE_API_KEY'] = os.getenv('DASHSCOPE_API_KEY')
# 文件通过DashScopeParse接口解析为程序与大模型易于处理的markdown文本
def file_to_md(file, category_id):
parse = DashScopeParse(
result_type=ResultType.DASHSCOPE_DOCMIND,
category_id=category_id
)
documents = parse.load_data(file_path=file)
# 初始化一个空字符串来存储Markdown内容
markdown_content = ""
for doc in documents:
doc_json = json.loads(json.loads(doc.text))
for item in doc_json["layouts"]:
if item["text"] in item["markdownContent"]:
markdown_content += item["markdownContent"]
else:
# DashScopeParse处理时,会将文档图片内的文本信息也解析到初始markdown文本中(类似OCR),这对于本文示例文件中的命令行截图、文本截图是足够的,示例无需深层次解析图片。
# 实际场景中的知识库文档,如果涉及不规则、复杂信息的图片并且需要深层次理解图片内容,您可以调用视觉模型进一步理解图片含义。
# (DashScopeParse返回的数据结构中,针对图片数据,markdownContent字段是图片url,text字段是解析出的文本)
# if ".jpg" in item["markdownContent"] or ".jpeg" in item["markdownContent"] or ".png" in item["markdownContent"]:
# image_url = re.findall(r'\!\[.*?\]\((https?://.*?)\)', item["markdownContent"])[0]
# print(image_url)
# markdown_content = markdown_content + parse_image_to_text(image_url)+"\n"
# else:
# markdown_content = markdown_content + item["text"]+"\n"
markdown_content = markdown_content + item["text"]+"\n"
return markdown_content
### 调用示例
# 1、可选配置。
# 百炼平台上,可以对不同项目配置不同的业务空间,默认情况下是使用默认业务空间。
# 如果需要使用非默认空间,可以前往[百炼控制台-业务空间管理](https://bailian.console.aliyun.com/?admin=1#/efm/business_management),配置业务空间并获取Workspace ID。
# 完成后,取消注释并修改这段代码为实际值:
# os.environ['DASHSCOPE_WORKSPACE_ID'] = "<Your Workspace id, Default workspace is empty.>"
# 2、可选配置。
# 文件通过DashScopeParse进行解析时,需要配置上传的数据目录id。可以前往[百炼控制台-数据管理](https://bailian.console.aliyun.com/#/data-center),配置类目并获取ID
category_id="default" # 建议修改为自定义的类目ID,以便分类管理文件
md_content = file_to_md(['./docs/内容公司各部门职责与关键角色联系信息汇总.pdf'], category_id)
print(md_content)
由于pdf、docx等多种文件格式来源的多样性,文件解析到markdown过程中可能存在一些格式上的小问题,比如 PDF 里的跨页表格行可能被解析为多行。可以使用大模型对生成的markdown文本进行润色,修正目录层级、缺失信息等。
from dashscope import Generation
def md_polisher(data):
messages = [
{'role': 'user', 'content': '下面这段文本是由pdf转为markdown的,格式和内容可能存在一些问题,需要你帮我优化下:\n1、目录层级,如果目录层级顺序不对请以markdown形式补全或修改;\n2、内容错误,如果存在上下文不一致的情况,请你修改下;\n3、如果有表格,注意上下行不一致的情况;\n4、输出文本整体应该与输入没有较大差异,不要自己制造内容,我是需要对原文进行润色;\n4、输出格式要求:markdown文本,你的所有回答都应该放在一个markdown文件里面。\n特别注意:只输出转换后的 markdown 内容本身,不输出任何其他信息。\n需要处理的内容是:' + data}
]
response = Generation.call(
model="qwen-plus",
messages=messages,
result_format='message',
stream=True,
incremental_output=True
)
result = ""
for chunk in response:
print(chunk.output.choices[0].message.content, end='')
result += chunk.output.choices[0].message.content
return(result)
md_polisher(md_content)
出来的结果就很清晰了:
# 内容公司各部门职责与关键角色联系方式
## 公司定位
我们的公司致力于推动教育的数字化转型,专注于开发富有创意和互动性的在线学习平台与工具。我们结合最新的教育理论与技术创新,旨在提升学习者的参与度和学习效果。通过提供个性化学习体验,我们希望满足不同年龄段和背景的学习需求,助力教育公平和终身学习。我们的使命是使知识获取更加便捷、高效,让每个人都能充分发挥潜力。
## 各部门介绍
- **教研部**: 教育课程研究设计
- **课程开发部**: 技术内容需求开发
- **教材编写部**: 教材、练习题等内容汇编与修订
- **评估部**: 内容质量质检
- **市场部**: 市场活动营销
- **人力资源部**: 人力资源管理
- **IT部**: IT技术支撑
- **绩效管理部**: 人员绩效考核设计
## 各部门关键角色联系人
| 部门 | 员工姓名 | 员工主管 | 工位| 工号 | 岗位 | 职位 | 电话| 邮箱| 工作职责 |
|------------|----------|----------|-------|------|------------|----------------|---------------|-----------------------------|--------------------------------------------------------------------------------------------|
| 教研部 | 张伟 | 李琳 | A101| 001| 内容设计 | 教研专员 | 13800000001| zhangwei@educompany.com | 负责教育课程的研究与开发,分析教学效果,整理教案,协助课程优化,以及参与教育项目的评估和反馈。|
| 教研部 | 王芳 | 李琳 | A102| 002| 内容设计 | 教研专员 | 13800000002| wangfang@educompany.com | 负责制定学科教学方案,策划教学活动,编写教案,收集学生反馈,参与课程改进会议,提供专业意见。|
| 教研部 | 刘杰 | 李琳 | A103| 003| 内容设计 | 教研专员 | 13800000003| liujie@educompany.com | 组织教研活动,分析教学成果,开展学术研究,协助撰写与研究报告,输出有效教学策略,提升课程品质。|
| 课程开发部 | 李丽 | 王强 | B201| 007| 内容开发 | 课程开发专员 | 13800000007| lili@educompany.com | 设计并开发教育课程,撰写课程大纲,组织课程试点,分析市场需求,调整课程内容,以适应学习者的需求和反馈。|
...
| 行政部 | 秦飞 | 蔡静 | G705| 034| 行政 | 行政专员 | 13800000034| qinf@educompany.com | 维护公司档案与信息系统,负责公司通知及公告的发布,组织公司活动的前期准备与后期评估,确保公司各项工作的顺利进行。|
| IT部 | 张伟 | 马云 | H802| 036| IT支撑 | IT专员 | 13800000036| zhangwei036@educompany.com| 进行公司网络及硬件设备的配置与维护,监控系统运行状态,及时处理技术问题与故障,提供技术支持及工具使用培训。|
| IT部 | 谢宇 | 马云 | H803| 037| IT支撑 | IT专员 | 13800000037| xieyu@educompany.com| 支持公司软件系统的开发与更新,参与IT项目的计划与实施,撰写技术文档与使用说明,确保信息技术的安全性与有效性。|
| 绩效管理部 | 朱南 | 李飞 | I901| 040| 人力资源 | 绩效专员 | 13800000040| zun@educompany.com| 负责制定绩效考核体系,组织绩效评估的实施与反馈,撰写评估报告,分析绩效数据以提出优化建议,提供决策支持。|
| 绩效管理部 | 韩杉 | 李飞 | I902| 041| 人力资源 | 绩效专员 | 13800000041| hanshan@educompany.com| 建立并维护员工绩效档案,定期组织绩效评价会议,协调各部门反馈,制定考核流程与标准,确保绩效考核的有效执行与公正性。|
通过上面的步骤,已经成功地将 PDF 转成了 Markdown,并且修正了其中的格式问题。与此同时,即使文档中存在图片,图片中的信息也能被提取出来,以便构建更有利于检索效果的知识库。
4.2 切片向量化与存储阶段
文档切片后,我们还需要对其建立索引,以便后续检索。一个常见的方案是使用嵌入(Embedding)模型将切片向量化,并存储到向量数据库中。在这一阶段,需要选择合适的 Embedding 模型以及向量数据库,这对于提升检索效果至关重要。
Embedding 模型可以将文本转换为高维向量,用于表示文本语义,相似的文本会映射到相近的向量上,检索时可以根据问题的向量找到相似度高的文档切片。因此,往往也需要选择合适的 Embedding 模型。通常越新的 Embedding 模型表现越好。
文档切片转为向量后,需要持久化存储下来,用于后续检索。目前构建的答疑机器人中,使用的是 LlamaIndex 内置的内存向量存储。实际生产环境中,选择一个合适的向量数据库是更好的选择,如ADB-PG、Milvus等。
4.3 检索召回阶段
检索阶段会遇到的主要问题就是,很难从众多文档切片中,找出和用户问题最相关、且包含正确答案信息的片段。从切入时机来看,可以将解法分为两大类:
在执行检索前,很多用户问题描述是不完整、甚至有歧义的,需要想办法还原用户真实意图,以便提升检索效果。
在执行检索后,可能会发现会存在一些无关的信息,需要想办法减少无关信息,避免干扰下一步的答案生成。
时机 | 改进策略 | 示例 |
---|---|---|
检索前 | 问题改写(通过重新表达,让问题更匹配数据库中的信息) | “附近有好吃的餐厅吗?=>“请推荐我附近的几家评价较高的餐厅” |
问题扩写(通过增加更多信息,让检索结果更全面) | “张伟是哪个部门的?”=>“张伟是那个部门的?他的联系方式、职责范围、工作目标是什么?” | |
提取标签(提取标签,用于后续标签过滤+向量相似度检索) | “内容工程师有哪些工作注意事项”=>
| |
反问用户 | “工作职责是什么”=>大模型反问“请问你想了解哪个岗位的工作职责” | |
思考并规划多次检索 | “张伟不在,可以找谁” =>大模型思考规划: =>task1:张伟的职责是什么,task2:${task1_result}职责的人有谁 =>按顺序执行多次检索 | |
…… | …… | |
检索后 | 重排序 ReRank+过滤 | chunk1, chunk2,...,chunk10 =>chunk2,chunk4,chunk5... |
滑动窗口检索 | ||
…… | …… |
这里也就重点说下 Rerank,其他的不一一展开。
我们可以调整之前代码,先从向量数据库中检索召回 20 条文档切片,再借助阿里云百炼提供的[文本排序模型](https://help.aliyun.com/zh/model-studio/getting-started/models#eafbfdceb7n03)进行重排序,并且筛选出其中最相关的 3 条参考信息。运行代码后可以看到,同样是 3 条参考信息,这次大模型能够准确回答问题了。
from llama_index.postprocessor.dashscope_rerank import DashScopeRerank
from llama_index.core.postprocessor import SimilarityPostprocessor
query_engine = index.as_query_engine(
llm=DashScope(model_name="qwen-plus"),
# 先设置一个较大的召回切片数量
similarity_top_k=20,
streaming=True,
node_postprocessors=[
# 在rerank 模型中选择你最终想召回的切片个数,重排模型选择通义实验室的gte-rerank模型
3, model="gte-rerank"), =
# 设置一个相似度阈值,低于该阈值的切片会被过滤掉
0.2) =
]
)
response = ask("张伟是哪个部门的", query_engine=query_engine)
提问:张伟是哪个部门的
回答:公司中有三位名叫张伟的员工,分别隶属于不同的部门:
教研部的张伟,岗位为内容设计教研专员。
课程开发部的张伟,岗位为内容开发课程开发专员。
IT部的张伟,岗位为IT支撑IT专员。
请问您需要了解哪一位张伟的信息?
检索召回的参考信息如下:
Node ID: 9ebe53cb-6dc2-4384-8ef1-b95d2fba0d91
Text: 组织公司活动的前期准备与后期评估,确保公司各项⼯作的顺利进⾏。IT部 张伟 ⻢云 H802 036 IT⽀撑 IT专员
13800000036 zhangwei036@educompany.com 进⾏公司⽹络及硬件设备的配置
Score:0.638
Node ID: 487d986f-d9bd-46d6-b231-4c61a41a56d0
Text: 内容公司各部⻔职责与关键⻆⾊联系⽅式 公司定位
我们结合最新的教育理论与技术创新,
通过提供个性化学习体验, 我们希望满⾜不同年龄段和背景的学习需求,
各部⻔介绍 教研部:教育课程研究设计
教材编写部:教材、练习题等内容汇编与修订 评估部:内容质量质检 市场部:市场活动营销 ⼈⼒资源部:⼈⼒资源管理
绩效管理部:⼈员绩效考核设计 各部⻔关键⻆⾊联系⼈ 部⻔ 员⼯姓名 员⼯主管 ⼯位 ⼯号 岗位 职位 电话 邮箱
教...
Score:0.574
...
Text: 点,分析市场需求,调整课程内容,以适应学习者的需求和反馈。课程开发部 张伟 王强 B202 008 内容开发 课程开发专员
13800000008 zhangwei01@educompany.com 精细化课程设计,确保课程符合国家教育标
Score:0.551
如此进入到最后的生成答案阶段。
4.4 生成答案阶段
到了最后,大模型最终生成的答案可能还是不及预期。这时遇到的问题可能会有:
没有检索到相关信息,大模型捏造答案。
检索到了相关信息,但是大模型没有按照要求生成答案。
检索到了相关信息,大模型也给出了答案,但是你希望 AI 给出更全面的答案。
为了解决这些问题,可以从以下角度着手分析与解决:
选择合适的大模型:1)如果只是简单的信息查询总结,小参数量的模型足以满足需求,比如 [qwen-turbo](https://help.aliyun.com/zh/model-studio/getting-started/models#ff492e2c10lub)。 2) 如果希望答疑机器人能完成较为复杂的逻辑推理,建议选择参数量更大、推理能力更强的大模型,比如 [qwen-plus](https://help.aliyun.com/zh/model-studio/getting-started/models#bb0ffee88bwnk) 甚至是 [qwen-max](https://help.aliyun.com/zh/model-studio/getting-started/models#cf6cc4aa2aokf)。 3) 如果构建的 RAG 应用面向一些非通用领域,如法律领域,建议使用面向特定领域训练的模型,如[通义法睿](https://help.aliyun.com/zh/model-studio/getting-started/models#f0436273ef1xm)。
充分优化提示词模板:比如 1) 明确要求:可以在提示词对于捏造答案(幻觉)的情况,通过提示词要求大模型:「如果所提供的信息不足以回答问题,请明确告知“根据现有信息,我无法回答这个问题。”切勿编造答案。」 2) 使用分隔符:检索召回的文档切片如果随意混杂在提示词里,人也很难看清整个提示词的结构,大模型也会受到干扰。建议将提示词和检索切片明确的分开,以便大模型能够正确地理解你的意图。 3) 动态提示词模板:不同问题的回答范式可能是不同的,可以借助大模型识别问题类型,然后映射使用不同的提示词模板。比如有些问题,希望大模型先输出整体框架,然后再输出细节;有些问题你可能希望大模型言简意赅的给出结论。
调整大模型的参数:比如 1) 如果希望大模型输出在相同的问题下,输出的内容尽可能相同,可以在每次模型调用时传入相同的seed值。2) 如果希望大模型在回答用户问题时不要总是用重复的句子,可以适当调高 presence_penalty 值。3) 可以查阅[通义千问 API Reference]([presence_penalty](https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api)),来了解更多参数的使用说明。
微调调优大模型:如果上述方法都做了充分的尝试,仍然不及预期,或者希望有更进一步的效果提升,也可以尝试面向该场景调优一个模型,但到这步也会更加复杂且成本高,一般需要引入专家辅助完成。
五、从“解释问题”到“解决问题”,进一步扩展 RAG 应用
到目前为止,已经得到了一个能把问答解释清楚的AI 助理机器人了。但它还有是局限性,只能“问答答疑”,而无法完成更多的需求,比如帮助你直接智能请假、帮助你自动发送邮件等。使对提示词进行精心设计、对RAG的整体流程进行优化,它依然无法做到,因为这需要与外界进行交互,而答疑机器人只能回答问题。
此时就需要引入“智能体 Agent”了。通过创建智能体(Agent)应用可以解决这些问题,它以大模型为基础,同时可以配备工具,从而达到与外界交互的目的,就像给大脑配备了四肢,实现从“解释问题”到“解决问题”的能力进一步跨越。这部分留待后续文章再展开。
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-11-22
如何使用 RAG 提高 LLM 成绩
2024-11-21
提升RAG性能的全攻略:优化检索增强生成系统的策略大揭秘 | 深度好文
2024-11-20
FastGraphRAG 如何做到高达 20%优化检索增强生成(RAG)性能优化
2024-11-20
为裸奔的大模型穿上"防护服":企业AI安全护栏设计指南
2024-11-20
RAG-Fusion技术在产品咨询中的实践与分析
2024-11-19
构建高性能RAG:文本分割核心技术详解
2024-11-19
【RAG竞赛获奖方案】CCF第七届AIOps国际挑战赛季军方案分享EasyRAG:一个面向AIOps的简洁RAG框架
2024-11-19
企业RAG构建中,如何用“行级别权限管控”避免数据泄露
2024-07-18
2024-05-05
2024-07-09
2024-07-09
2024-05-19
2024-06-20
2024-07-07
2024-07-07
2024-07-08
2024-07-09
2024-11-06
2024-11-06
2024-11-05
2024-11-04
2024-10-27
2024-10-25
2024-10-21
2024-10-21