大模型学习比较-优化提示词改善答疑机器人回答质量

答疑机器人

大模型如何工作

工作流程

  1. 输入文本分词化
    Token:分词,具有独立语义的词语,每个Token分配一个ID。
  2. Token向量化。
  3. 推理
  4. 循环输出Token==》输出文本

影响大模型内容生成的随机性参数

  1. temperature:温度从低到高(0.1 -> 0.7 -> 1.2),概率分布从陡峭趋于平滑,输出也会从相对固定 到逐渐多样化
  2. top_p:范围在0~1。从候选 Token 集合中选出符合条件的"小集合"。具体方法是:按概率从高到低排序,选取累计概率达到设定阈值的 Token 组成新的候选集合,从而缩小选择范围。
  • 值越大 :候选范围越广,内容更多样化,适合创意写作、诗歌生成等场景。
  • 值越小 :候选范围越窄,输出更稳定,适合新闻初稿、代码生成等需要明确答案的场景。
  1. top_k:范围在>=1。从概率排名前k的Token中随机选择一个进行输出。一般来说,top_k越大,生成内容越多样化;top_k越小,内容则更固定。
  2. seed:每次模型调用时传入相同的seed值,并保持其他参数不变,模型会尽最大可能返回相同结果。

RAG应用-扩展答疑机器人的知识范围

RAG的工作原理

RAG:Retrieval Argumented Generation

建立索引

文档解析==》分段(切片)==》向量化==》存储索引(向量数据库)

python 复制代码
# 加载文档并解析
documents = SimpleDirectoryReader('./docs').load_data()
# 建立索引(包含切片)
index = VectorStoreIndex.from_documents(
    documents,
    # 指定embedding 模型
    embed_model=DashScopeEmbedding(
        model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2
    ))

检索+生成

  • 检索:通过Embedding模型比对文本,也可以做rerank、句子窗口检索方法。
  • 生成:利用大模型的总结能力,根据问题和文本段生成回答。

多轮对话

如果将完整历史对话与问题都输入到检索系统,由于字数较多,检索系统可能无法处理(embedding模型在长文本上效果差于短文本)。业界常用的解决方法是:

  1. 通过大模型,基于历史对话信息,将用户的问题改写为一个新的query,新的query将包含历史对话的关键信息。
  2. 使用新的query,按照原先流程进行检索与生成的过程。
python 复制代码
chat_engine = CondenseQuestionChatEngine.from_defaults(
    # 查询引擎
    query_engine=query_engine,
    # 提示词模板
    condense_question_prompt=custom_prompt,
    # 历史对话记录
    chat_history=custom_chat_history,
    llm=OpenAILike(
        model="qwen-plus-0919",
        api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        is_chat_model=True
        ),
    verbose=True
streaming_response = chat_engine.stream_chat("核心职责是什么")
)

提示词工程-优化提示词改善答疑机器人回答质量

提示词在很大程度上决定了大模型的回答质量,接下来你可以参考一些提示词框架构建提示词。

提示词框架

python 复制代码
from chatbot import rag
# 加载索引
index = rag.load_index()
query_engine = rag.create_query_engine(index=index)
# 问答
streaming_response = query_engine.query(question)
streaming_response.print_response_stream()

基本要素

提示词中需要明确以下几个要素:任务目标、上下文、角色、受众、样例、输出格式。这些要素构成了一个提示词框架,能帮助你构建一个完整、有效的提示词。

要素 含义
任务目标(Object) 明确要求大模型完成什么任务,让大模型专注具体目标
上下文(Context) 任务的背景信息,比如操作流程、任务场景等,明确大模型理解讨论的范围
角色(Role) 大模型扮演的角色,或者强调大模型应该使用的语气、写作风格等,明确大模型回应的预期情感
受众(Audience) 明确大模型针对的特定受众,约束大模型的应答风格
样例(Sample) 让大模型参考的具体案例,大模型会从中抽象出实现方案、需要注意的具体格式等信息
输出格式(Output Format) 明确指定输出的格式、输出类型、枚举值的范围。通常也会明确指出不需要输出的内容和不期望的信息,可以结合样例来进一步明确输出的格式和输出方法

提示词自动优化工具:bailian.console.aliyun.com/?tab=app#/c...

提示词模板

直接让用户根据框架书写提示词并非最佳选择。

python 复制代码
# 构建提示词模板
prompt_template_string = (
    "你是公司的客服小蜜,你需要简明扼要的回答用户的问题"
    "【注意事项】:\n"
    "1. 依据上下文信息来回答用户问题。\n"
    "2. 你只需要回答用户的问题,不要输出其他信息\n"
    "以下是参考信息。"
    "---------------------\n"
    "{context_str}\n"
    "---------------------\n"
    "问题:{query_str}\n。"
    "回答:"
)

# 更新提示词模板
rag.update_prompt_template(query_engine,prompt_template_string)

构建有效提示词的技巧

  1. 清晰表达需求,并使用分隔符。
  2. 限定角色和受众。
    角色:大模型扮演什么角色。
    受众:用户扮演什么角色。
  3. 提供少量样本示例。
  4. 给模型"思考"的时间。 思维连(COT)是让大模型进行思考的一种方式。但是紧靠"思考"无法完成更复杂的工作,大模型也从思维连到多智能体发展。

使用大模型做意图识别

让大模型进行意图识别也有以下两种方法:

  • 使用提示词:设计特定的提示词,引导大模型生成符合预期的回答。无需要修改模型参数,而是依靠构造的输入来激发模型内部已有的知识。
  • 微调模型:使用特定的标注数据进一步训练模型,使其更好的对意图进行分类。

推理大模型

python 复制代码
from openai import OpenAI

# 初始化客户端
client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)   

自动化评测

RAG自动化评测体系

从以下几个维度评估:

  1. 召回质量:【检索】正确且相关
  2. 答案忠实度:【生成】基于检索的上下文
  3. 答案相关性:【生成】
  4. 上下文利用率/效率:是否有效利用上下文。

评测框架:Ragas、Trulens、DeepEval

使用Ragas评估

需要准备question、ground_truth。

  1. Answer Correctness的计算过程。
  • 语义相似度:计算answer和ground_truth的文本向量相似度。
  • 事实相似度:Ragas衡量事实准确度的方法,维护TP、FP、FN列表。
  1. 召回效果:参考信息的准确度。
  • 需要准备:question、context、ground_truth
  • Context precision:召回的context中,与ground_truth相关的context是否排名靠前。
    • context中,有多少context与ground_truth相关,以及context的排名情况。
  • Context recall:context与ground_truth的一致性,侧重事实准确度。
    • ground_truth 中有多少比例的观点可以得到 contexts 的支持。

如何根据Ragas指标进行优化

  1. 上下文是RAG的生命线。
  • Lost in the Middle:在海量的无关信息里,大模型对关键信息"视而不见"。
  • 知识浓度:相关信息密度高、噪音少、与问题直接关联。
  1. Context recall。 如果Context recall较低,可以考虑:
  • 检查知识库;
  • 更换Embedding模型
  • query改写(提示词工程)
  1. Context precision。 如果Context precision较低,可以考虑:
  • 检索阶段增加rerank,提升相关文本段的排名。
  1. Answer Correctness。 如果Answer Correctness较低,而前两个指标较高,则考虑:
  • 优化提示词;
  • 调整temperature等超参;
  • 更换性能强大的大模型;
  • 微调

打造卓越的评测体系

  • 业务专家参与
  • 从用户视角出发
  • 持续运营
  • 维护评测集。

建议在实际应用时,邀请 RAG 应用对应的领域专家(人工参与)一起构建能反映真实场景问题分布的测试集,并且持续更新测试集。

优化RAG提升准确度

初步优化检索结果

让大模型获得更多的参考信息

调整代码,检索引擎召回的切片数增加。

==》召回的文档切片存在无关信息,且有效信息未被完全召回。

给大模型结构更清晰的参考信息

Markdown格式是一个很好的选择。(需要重建索引) ==》回答准确度能够提高。

RAG的工作流程

文档解析与切片、向量存储、检索与召回参考信息、生成答案。

RAG应用各个环节与改进策略

文档准备阶段

  1. 识别意图空间和知识空间。
  2. 构建一套可以持续收集用户意图的机制,从而完善知识库,邀请专家参与评测,形成"数据采集-知识更新-专家验证"的闭环流程。

文档解析与切片阶段

  1. 解析:百炼DashScopeParse解析PDF、Word文档。
  2. 切片:切片方式影响召回质量。
  • 文档切片缺少关键信息:回答不准确。
  • 文档切片非关联信息过多(噪声):影响回答质量。

Token切片、句子切片、句子窗口切片、语义切片、Markdown切片

类别 细分类型 改进策略 场景化示例
文档解析 文档类型不统一,部分格式的文档不支持解析 比如前面用到的 SimpleDirectoryLoader 并不支持 Keynote 格式的文件 开发对应格式的解析器,或转换文档格式 例如,某公司使用了大量的 Keynote 文件存储员工信息,但现有的解析器不支持 Keynote 格式。可以开发 Keynote 解析器或将文件转换为支持的格式(如 PDF)。
文档解析 已支持解析的文档格式里,存在一些特殊内容 比如文档里嵌入了表格、图片、视频等 改进文档解析器 例如,某文档中包含了大量的表格和图片,现有解析器无法正确提取表格中的信息。可以改进解析器,使其能够处理表格和图片。
文档解析 ... ... ...
文档切片 文档中有很多主题接近的内容 比如工作手册文档中,需求分析、开发、发布等每个阶段都有注意事项、操作指导 扩写文档标题及子标题 「注意事项」=>「需求分析>注意事项」 建立文档元数据(打标) 例如,某文档中包含多个阶段的注意事项,用户提问"需求分析的注意事项是什么?"时,系统返回了所有阶段的注意事项。可以通过扩展标题和打标来区分不同阶段的内容。
文档切片 文档切片长度过大,引入过多干扰项 减少切片长度,或结合具体业务开发为更合适的切片策略 例如,某文档的切片长度过大,包含了多个不相关的主题,导致检索时返回了无关信息。可以减少切片长度,确保每个切片只包含一个主题。
文档切片 文档切片长度过短,有效信息被截断 扩大切片长度,或结合具体业务开发为更合适的切片策略 例如,某文档中每个切片只有一句话,导致检索时无法获取完整的上下文信息。可以增加切片长度,确保每个切片包含完整的上下文。
文档切片 ... ... ...

切片向量化与存储阶段

文档切片后,需要建立索引,以便后续检索。一种常见方法是使用Embedding模型将切片向量化。

  1. 选择合适Embedding模型。
    百炼的text-embedding-v2、text-embedding-v3。
  2. 选择合适的向量数据库:内存向量数据库、本地向量数据库、云服务向量数据库(Milvus)

检索召回阶段

主要问题:找出与问题最相关、包含正确答案的切片。

  1. 用户问题不完整、有歧义。==》想办法还原用户意图。
  2. 检索存在无关信息。==》减少无关信息,避免影响答案生成。

解决:

  • 检索前:问题改写、问题扩充、提取标签、反问用户、思考并规划多次检索。
  • 检索后:重排序+过滤、句子窗口检索。

重点:

  1. 问题改写:
    1)使用大模型扩充用户问题;
    2)将单一查询改为多步骤查询。
    StepDecomposeQueryTransform:复杂问题分解为多步骤; MultiStepQueryEngine:多步骤查询引擎。
    3)假想文档(Hypothetical Document Embeddings)
  2. 重排序:从向量数据库检索召回3条相关文档片段,但是这3条不一定是事实相关的。百炼提供文本排序模型,对文档做重排序,筛选最相关的3条。
  3. 提取标签:建立索引时,可以将标签与文档切片一起存储,这种"标签过滤+向量检索"的组合方式,能大幅提升检索准确性。
时机 改进策略 示例
检索前 问题改写 「附近有好吃的餐厅吗?」=> 「请推荐我附近的几家评价较高的餐厅」
检索前 问题扩写 通过增加更多信息,让检索结果更全面 「张伟是哪个部门的?」=> 「张伟是哪个部门的?他的联系方式、职责范围、工作目标是什么?」
检索前 基于用户画像扩展上下文 结合用户信息、行为等数据扩写问题 内容工程师提问「工作注意事项」=> 「内容工程师有哪些工作注意事项」 项目经理提问「工作注意事项」=> 「项目经理有哪些工作注意事项」
检索前 提取标签 提取标签,用于后续标签过滤+向量相似度检索 「内容工程师有哪些工作注意事项」=> * 标签过滤:{"岗位": "内容工程师"} * 向量检索:「内容工程师有哪些工作注意事项」
检索前 反问用户 「工作职责是什么」=> 大模型反问:「请问你想了解哪个岗位的工作职责」 实现反问的提示词可以参考: 10分钟构建能主动提问的智能导购
检索前 思考并规划多次检索 「张伟不在,可以找谁」 => 大模型思考规划: => task_1:张伟的职责是什么, task_2:${task_1_result}职责的人有谁 => 按顺序执行多次检索
检索前 ... ...
检索后 重排序 ReRank + 过滤 多数向量数据库会考虑效率,牺牲一定精确度,召回的切片中可能有一些实际相关性不够高 chunk1、chunk2...、chunk10 => chunk 2、chunk4、chunk5
检索后 滑动窗口检索 在检索到一个切片后,补充前后相邻的若干个切片。这样做的原因是:相邻切片之间往往存在语义联系,仅看单个切片可能会丢失重要信息。 滑动窗口检索确保了不会因为过度切分而丢失文本间的语义连接。 常见的实现是句子滑动窗口,你可以用下方的简化形式来理解: 假设原始文本为:ABCDEFG(每个字母代表一个句子) 当检索到切片:D 补充相邻切片后:BCDEF(前后各取2个切片) 这里的BC和EF是D的上下文。比如: * BC可能包含解释D的背景信息 * EF可能包含D的后续发展或结果 * 这些上下文信息能帮助你更准确地理解D的完整含义 通过召回这些相关的上下文切片,你可以提高检索结果的准确性和完整性。
检索后 ... ...

生成答案阶段

  1. 选择合适的大模型。
  2. 优化提示词模板:
  • 要求不编造答案。
  • 添加分隔标记。
  • 先识别问题类型,后映射到不同的提示词模板,即根据问题类型调整模板。
  1. 调整大模型超参:seed、presense_penalty(重复惩罚)、temperature、top_p、max_tokens

插件-Agent

智能体不仅能够与外界交互,还能处理复杂任务,几个核心模块:

  • 工具模块:定义和管理工具
  • 记忆模块:
    • 长期记忆:帮助智能体学习。
    • 短期记忆:临时存储当前任务信息。
  • 计划能力
    • 决策、规划
  • 行动能力
    • 与工具模块紧密结合,执行任务
python 复制代码
from dashscope import Assistants, Messages, Runs, Threads

ChatAssistant = Assistants.create(
    # 在此指定模型名称
    model="qwen-plus",
    # 在此指定Agent名称
    name='公司小蜜',
    # 在此指定Agent的描述信息
    description='一个智能助手,能够查询员工信息,帮助员工发送请假申请,或者查询公司规章制度。',
    # 用于提示大模型所具有的工具函数能力,也可以规范输出格式
    instructions='''你是公司小蜜,你的功能有以下三个:
    1. 查询员工信息。例如:查询员工张三的HR是谁;
    2. 发送请假申请。例如:当员工提出要请假时,你可以在系统里帮他完成请假申请的发送;
    3. 查询公司规章制度。例如:我们公司项目管理的工具是什么?
    请准确判断需要调用哪个工具,并礼貌回答用户的提问。
    ''',
    # 将工具函数传入
    tools=[
        {
            # 定义工具函数类型,一般设置为function即可
            'type': 'function',
            'function': {
                # 定义工具函数名称,通过map方法映射到query_employee_info函数
                'name': '查询员工信息',
                # 定义工具函数的描述信息,Agent主要根据description来判断是否需要调用该工具函数
                'description': '当需要查询员工信息时非常有用,比如查询员工张三的HR是谁,查询教育部门总人数等。',
                # 定义工具函数的参数
                'parameters': {
                    'type': 'object',
                    'properties': {
                        # 将用户的提问作为输入参数
                        'query': {
                            'type': 'str',
                            # 对输入参数的描述
                            'description': '用户的提问。'
                        },
                    },
                    # 在此声明该工具函数需要哪些必填参数
                    'required': ['query']},
            }
        }
    ]
)

# 提前定义好工具函数query_employee_info
function_mapper = {
    "查询员工信息": query_employee_info
}

new_tool = {'type': 'function',
            'function': {
                'name': '发送请假申请',
                'description': '当需要帮助员工发送请假申请时非常有用。',
                'parameters': {
                    'type': 'object',
                    'properties': {
                        # 需要请假的时间
                        'date': {
                            'type': 'str',
                            'description': '员工想要请假的时间。'
                        },
                    },
                    'required': ['date']},
            }
           }
ChatAssistant.tools.append(new_tool)
# 提前定义好一个工具函数send_leave_application
function_mapper["发送请假申请"] = send_leave_application

多智能体

当机器人需要在一个请求中执行多个操作时,单个智能体可能无法有效完成所有子任务。

  1. planner agent
python 复制代码
planner_agent = Assistants.create(
    model="qwen-plus",
    name='流程编排机器人',
    description='你是团队的leader,你的手下有很多agent,你需要根据用户的输入,决定要以怎样的顺序去使用这些agent'
)

# 改进
planner_agent=Assistants.update(planner_agent.id,instructions="""你的团队中有以下agent。
    employee_info_agent:可以查询公司的员工信息,如果提问中关于部门、HR等信息,则调用该agent;
    leave_agent:可以帮助员工发送请假申请,如果用户提出请假,则调用该agent;
    chat_agent:如果用户的问题无需以上agent,则调用该agent。

    你需要根据用户的问题,判断要以什么顺序使用这些agent,一个agent可以被多次调用。你的返回形式是一个列表,不能返回其它信息。比如:["employee_info_agent", "leave_agent"]或者["chat_agent"],列表中的元素只能为上述的agent。""")

将任务规划结果转化为列表对象,逐步解析每个步骤。

python 复制代码
import ast
# 使用Planner Agent获取任务规划
planner_response = get_agent_response(planner_agent, "王五在哪个部门?帮我提交下周三请假的申请")
# Planner Agent返回的是一个描述调用顺序的列表形式字符串
order_stk = ast.literal_eval(planner_response)
  1. 工具函数
  2. summary agent
python 复制代码
summary_agent = Assistants.create(
    model="qwen-plus",
    name='总结机器人',
    description='一个智能助手,根据用户的问题与参考信息,全面、完整地回答用户问题',
    instructions='你是一个智能助手,根据用户的问题与参考信息,全面、完整地回答用户问题'
)

完整流程

python 复制代码
# 获取Agent的运行顺序
agent_order = get_agent_response(planner_agent,query)

order_stk = ast.literal_eval(agent_order)

cur_query = query
# 依次运行Agent
for i in range(len(order_stk)):
    cur_agent = agent_mapper[order_stk[i]]
    response = get_agent_response(cur_agent,cur_query)
    Agent_Message += f"*{order_stk[i]}*的回复为:{response}\n\n"
    ...
    # 如果当前Agent为最后一个Agent,则将其输出作为Multi Agent的输出
    # 如果当前Agent不是最后一个Agent,则将上一个Agent的输出response添加到下一轮的query中,作为参考信息

多智能体编排功能

智能体流程画布的开创者:Dify.ai

  1. 用户直观看到各个智能体的执行规则和链路。
  2. 编排多个智能体的协作
  3. 快速验证效果
相关推荐
慧都小项1 小时前
自动化UI测试工具TestComplete的AI双引擎:即时数据集 + 自愈测试
自动化测试·测试工具·llm·数据驱动测试·hipaa标准
AI大模型4 小时前
大厂LLM应用岗上岸面经:面28家拿offer,拆解“必问考点+避坑指南”
程序员·llm·agent
没用的阿星4 小时前
阿里发布Qwen3-Coder,效果比肩claude 4!
llm
阿星AI工作室4 小时前
扣子开源本地部署教程 丨Coze智能体小白喂饭级指南
llm·agent·产品
小小小小小鹿4 小时前
Ai入门-搭建一个专属的ai学习助手
llm·ai编程
r0ad6 小时前
四大主流AI Agent框架选型梳理
llm·agent
智泊AI7 小时前
GPU并行计算是什么?GPU并行计算的原理是什么?
llm
yaocheng的ai分身8 小时前
主流大模型的Cache机制对比
llm
数据智能老司机10 小时前
构建由 LLM 驱动的 Neo4j 应用程序——揭开 RAG 的神秘面纱
langchain·llm·aigc
数据智能老司机10 小时前
构建由 LLM 驱动的 Neo4j 应用程序——构建智能应用的知识图谱基础理解
langchain·llm·aigc