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

答疑机器人

大模型如何工作

工作流程

  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. 快速验证效果
相关推荐
GetcharZp6 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
Pitayafruit11 小时前
Spring AI 进阶之路03:集成RAG构建高效知识库
spring boot·后端·llm
GitLqr13 小时前
AI洞察 | 智元、阿里在机器人领域的重磅开源
meta·机器人·llm
聚客AI15 小时前
🔷告别天价算力!2025性价比最高的LLM私有化训练路径
人工智能·llm·掘金·日新计划
用户849137175471616 小时前
joyagent智能体学习(第1期):项目概览与架构解析
人工智能·llm·agent
逍岚子16 小时前
以官网计算器为例:手把手教你用 TypeScript SDK 开发 MCP Server
llm·agent·mcp
hojyn19 小时前
LLM应用评估终极指南
llm
逍岚子19 小时前
新闻搜索 MCP Server 开发秘籍:Python - SDK 携手 SerpApi,融入 Trae 不再难
llm·agent·mcp
亚里随笔1 天前
稳定且高效:GSPO如何革新大型语言模型的强化学习训练?
人工智能·机器学习·语言模型·自然语言处理·llm·rlhf
SuperherRo1 天前
Web攻防-大模型应用&LLM安全&提示词注入&不安全输出&代码注入&直接间接&数据投毒
大模型·llm·提示词注入·不安全输出·直接·间接