作者:Thought Agent 社区在对话系统的设计和实现中,传统的基于 Rasa-like 框架的方法往往需要依赖于多个模块的紧密协作,例如我们在之前的文章中提到的基于大模型(LLM)构建的任务型对话 Agent,Thought Agent,其由自然语言理解(NLU)、对话管理(DM)和对话策略(DP)等模块共同协作组成。这种模块化的设计虽然在理论上具有灵活性,但在实践中却带来了诸多挑战,尤其是在系统集成、错误传播、维护更新以及开发门槛等方面。
为了克服这些挑战,构建一个端到端(E2E)的模型显得尤为关键。E2E 的模型通过将对话的各个阶段集成到一个统一的框架中,极大地简化了系统架构,提高了处理效率,并减少了错误传递的可能性。此外,由于其简化的架构,也更易于维护和更新,从而降低了开发和维护的成本。
在我们看来,端到端的对话 Agent 不仅在技术上更具优势,而且在实际应用中也展现了其独特的价值和潜力,例如能够快速构建帮助用户查询信息、调度技能的 Agent。
本文将指导读者如何采用蒙特卡洛方法(Monte Carlo)模拟用户行为并结合 LLM 的方法来构建训练数据集;使用 LLaMA Factory 对多种 LLM 进行高效微调构建任务型对话 Agent。该方案允许用户快速创建出能够精准调用外部工具的 Agent。
挑战
任务型对话系统的核心需求包括意图识别、槽位填充、状态管理和策略决策。我们识别了以下几个关键挑战:
构造数据集
为了应对上述挑战,首先我们需要构造能够覆盖大部分场景的对话数据集,我们面临的核心难点是如何模拟真实世界中用户的多样化行为和对话系统的有效响应。但是对于任务型对话 Agent 来说,用户和 Agent 之间的对话域是有限的,因为 Agent 只需要处理业务范围内的用户意图,超出处理范围的内容,只需要返回一些固定的兜底话术即可。
因此我们可以采用了状态图对对话的过程进行建模,使用蒙特卡洛方法对真实的对话过程进行模拟,接着使用 LLM 的生成能力来创建符合状态、角色定义的对话内容从而达到构建数据集的目的。
基于图的对话流程图的建模
我们使用有向图(Directed Graph)的数据结构来对通用的任务型对话流程进行建模,这比传统的有限状态机更加灵活和通用。在构建对话流程图时,我们首先定义了一组节点,每个节点代表了对话中的一个关键状态。例如,一个理想的对话过程至少包含以下节点:
在定义了节点之后,我们使用边将可以进行状态转移的节点连接起来,从而构建一个有向图用来表征对话过程中所有可能的转移关系,如下图。在这个图中,主要的变量是用户对话内容,Agent 的回复内容是随着用户的对话意图和槽位状态发生的变化而变化。对于每个原子对话来说,我们认为在用户提供了清晰的意图以及提供了全部的必填槽位信息之后,这个原子对话就算结束了,即可以触发 Function Calling 的指令。
图 1. 对话流程转移图示例(可能没有覆盖全部场景)
初始状态随机生成
在对话系统的开始阶段,用户的首次提问可能包含从零到全部所需槽位的不同信息量。为了模拟这种多样性,我们可以使用蒙特卡洛方法来随机决定哪些槽位在用户的首次提问中被提及。具体来说,对于一个意图中的所有槽位,我们可以生成一个由 0 或 1 组成的随机数组,其中 0 表示该槽位不能再首次提问中提及,而 1 表示需要被提及。
例如,考虑一个酒店预订任务,可能的关键信息包括「入住日期」、「退房日期」 和 「房间类型」。利用上述的方法,我们可以为每个槽位生成一个对应的随机值,从而决定用户的首次提问中需要包含哪些信息。这不仅增加了对话样本的多样性,也使得训练数据集更加贴近真实世界的对话情况。
随机游走模拟用户行为
初始状态生成了之后,我们需要生成生成多样化的对话路径,这里采用蒙特卡洛方法使得当前的对话状态在建立好的对话转移状态图中随机游走。在每个状态完成之后,将随机选择下一个状态,各状态的转移概率可以根据经验进行定义,从而模拟用户可能采取的不同行动。例如,用户在首问中没有提供全部的必填槽位, Agent 将发起槽位的追问,对于 Agent 的追问,用户可能认真的回答槽位信息,也有可能发起闲聊,还有可能改变了主意,问了一个新的问题,不同的转移路径我们可以设置不同的概率,例如上面的转移路径我们根据经验分为设置概率为 [0.8, 0.1, 0.1]。
通过这种随机游走的方式,可以生成不同的对话状态路径,每条路径都代表了一种可能的用户行为和 Agent 响应。这些路径为我们提供了丰富的训练数据,帮助对话系统学习如何处理各种情况。
对上下文理解能力的增强
在实际对话中,用户通常不会在每个回合都重复提供所有相关信息。相反,他们会根据上下文,利用代词、省略或简化的表述来替代之前已经提及过的内容。为了让对话系统能够正确理解这种上下文依赖的表达方式,我们需要在训练数据中模拟这种用户行为模式。
具体来说,我们将对话分为多个阶段,每个阶段对应不同的任务意图。在后续阶段生成语料时,我们会考虑之前阶段已经提供的槽位信息。如果用户的新问题与之前的问题存在槽位重叠,且该槽位已在先前回合中提供过,那么在生成新问题时,我们将有意识地省略这部分信息,只保留用户需要补充的新信息。
例如,假设用户之前已经询问过「成都市内哪家火锅好吃」,这句话中包含了用户想要了解的位置和餐厅类型两个槽位信息。在后续对话中,如果用户想询问这些餐馆的价格区间,可能会使用「它们的价格大概是多少?」这样的省略式表述,而非重复提供完整的问句。通过模拟这种情况,我们可以增强模型对于上下文依赖的理解能力。
基于 LLM 的对话内容生成
LLM 在这一过程中扮演了至关重要的角色。我们利用 LLM 的强大生成能力来模拟用户的提问和系统的追问,生成接近真实对话的数据。例如,以推荐餐厅这个意图为例, 用于生成首问的 Prompt 可以这样写,
你是一个用户,你现在想要「根据自己的位置、兴趣和预算,让智能客服推荐当地的餐厅」,请向智能客服寻求帮助
你的问题需要满足以下几个条件
1. 在问题中需要提到具体的用户当前的区域或希望探索的区域。
2. 在问题中一定不要提到具体的用户感兴趣的餐厅类型,中餐,日料,西餐等。
3. 在问题中一定不要提到具体的用户的最大预算。
请生成一句满足当前的场景和设定的问题
LLM 广阔的知识面为我们提供了丰富的语言资源,支持我们模拟各种场景的对话。此外,LLM 还能够根据上下文生成连贯且逻辑性强的回复,进一步提高了数据集的质量。
为了增强任务型对话 Agent 对领域信息的理解以及提高对话的多样性, RAG 技术将被用于为对话内容注入领域相关的知识。特别是在处理涉及特定领域业务的时候,领域知识在这一过程中至关重要。为了在实现领域信息的注入,以办理业务这个意图为例,可以采取以下实施步骤:
通过这种方法,任务型对话代理可以更好地理解和响应用户需求,提供更精准和个性化的服务。
易扩展的意图配置
对于任务型 Agent 来说,对话的目标是一致,即收集足够的信息帮助用户执行任务。我们可以通过一个 YAML 文件来对任务的详细内容和槽位信息进行描述,用户意图增加和减少都可以通过编辑一系列 YAML 配置文件来实现,而无需对有状态转移图或生成流程进行复杂的更改。这种设计提高了本文方案的可扩展性。例如想生成一个根据地点,餐厅类型,最大预算推荐餐厅任务相关的数据集,只需要编写如下配置文件即可:
name: recommend_restaurant
description: 根据自己的位置、兴趣和预算,让智能客服推荐当地的餐厅
parameters:
- name: destination
description: 用户当前的区域或希望探索的区域。
type: text
required: True
- name: cuisine_type
description: 用户感兴趣的餐厅类型,中餐,日料,西餐等
type: text
required: True
- name: budget
description: 用户的最大预算
type: float
required: False
目标为任务型对话 Agent 的 LLM 微调
我们选择 LLaMA Factory 作为我们的微调工具,这是一个开源的高效微调框架,专为 LLMs 设计,能够适应各种下游任务,并且兼容大部分主流模型,同时提供一个图形界面 LLaMA Board 帮助用户更友好的执行和管理微调任务。
对于本文的任务,对 LLM 按照任务型 Agent 方向进行微调,因为需要学习的知识较少,并且不会对模型整体的回答能力进行大的变化,这里我们采用 LoRA 微调技术对模型进行 SFT。在基础模型的选择上,没有经过指令微调的 Base 模型和有过指令微调后的 Chat 模型(e.g., Qwen 1.5 和 ChatGLM3)都会被纳入选择范围。
图 2. 面向 LLM 微调的 LLaMA Board 系统
LLaMA Factory 支持 Alpaca 和 ShareGPT 两种数据集的格式,这里我们将上面使用蒙卡 + LLM 生成的数据集处理为 ShareGPT 格式。下面就是个 ShareGPT 格式包含功能调用内容的数据例子,其中 conversations 中是对话历史,tools 是当前对话中所有可用的工具。
为了保证不同的意图和槽位能被相对准确的识别到,根据实践经验我们认为训练数据量需要满足意图数 x 槽位数 x 500 的规模。这里我们构造了 5 个任务,分别是根据实时汇率转换货币金额,了解某个地区的习俗和文化特点,根据用户的位置或兴趣,推荐附近的博物馆,根据自己的位置、兴趣和预算,推荐当地的餐厅以及查询去某个目的地的交通方式。每个意图包含 2 至 3 个槽位,共生成了 6000 条左右的数据,覆盖了 5 个任务大部分用户状态变化路径,接下来将对微调训练的部分进行详细的介绍。
实验配置及结果
本文选择在 A6000 显卡上进行微调,微调精度选择 fp16,LoRA Rank 设置为 4,训练 3 个 Epoch。在不使用 flash attention 加速时整个训练过程耗时 1.5h 左右,对于 6B - 7B 的模型,显存占用在 20 - 22G 左右。实验对象包括目前市面上主流的开源模型,ChatGLM3 6B,Qwen 1.5 7B,Yi 6B 系列的 Chat 模型和 Base 模型。在额外构造了 100 个验证对话集对模型进行评估之后,这里将微调前的 Qwen 1.5 Chat 模型作为基线对比了不同模型微调后的表现,
表 1. 不同基模型的微调结果对比
从对比结果可以发现,经过我们微调后的 Qwen 1.5 Chat 模型在整体性能上表现最佳,相对于 Baseline(微调前的 Qwen 1.5 Chat 模型),除了微调不成功的 ChatGLM3 之外,性能上都有比较显著的提升。Qwen 1.5 的意图召回率略高于 Qwen 1.5 Chat 的原因是 Qwen 1.5 出现幻觉的概率较高,在给出 Tools 之后,没有明显调用工具的提问也较容易触发工具调用。
对比一下 Qwen 1.5 Chat 微调前和微调后的表现,下图 3 为 Qwen 1.5 Chat 微调前的对话表现,图 4 为 Qwen 1.5 Chat 经过微调后的对话表现。可以明显发现微调后的模型对于槽位的识别方面提升很大,并且还附加了槽位追问的能力。
图 3. Qwen 1.5 Chat 微调前的对话表现
图 4. Qwen 1.5 Chat 微调后的对话表现
此外,Agent 对于用户在对话中省略主语时也能正确的识别槽位信息,如下图所示,用户在说「5000 人民币可以在当地还多少钱呢」的时候,模型能够智能的将「当地」和上文中提到的「京都」联系起来,在上下文结合和理解上的表现上比传统的 Rasa-like 对话 Agent 表现的更加智能和灵活,达到了我们的预期水平。
图 5. 本文微调的 Agent 模型超越普通 Rasa-like 对话 Agent 的能力
对比经过指令微调的 Chat 模型和没有指令微调过的 Base 模型我们还发现,经过指令微调的 Chat 模型得到的微调反馈最佳,特别是经过 Agent 相关指令微调的 Chat 模型,这主要是这类模型已经使用了大量包括 Function Calling 的语料进行了训练,我们在此基础上进行微调实际上属于同方向的增量学习,需要 Agent 额外的学习成本更小。
结论
本文提出了一种利用蒙卡方法和 LLM 生成训练数据集,并将其与 LLaMA Factory 框架相结合,高效微调多种语言模型,构建任务型对话 Agent 的新颖方案。该方案不仅保留了大语言模型强大的理解和生成能力,而且显著提高了微调后模型在意图识别、槽位填充等关键任务上的性能表现。
与传统的模块化对话系统相比,本文方法构建的端到端 Agent 架构更加简洁高效,易于部署和维护。实验结果表明,经过微调的语言模型不仅能够准确识别用户意图和关键信息,还能根据上下文理解用户的省略表达,并在必要时主动追问槽位信息,相较于微调前的模型,展现出更强的理解和交互能力,相较于传统的模型,展现出了更多的智能性。
尽管如此,该方案仍然存在一定的不足,首先在数据集的构建方面,靠人脑整理的行为状态图很难考虑到所有可能的用户路径,建模的过程十分耗时。在 Agent 微调方面,准确率还有待进一步提高,因为是 E2E 的系统,整体的可控性和可解释性相对较差。
未来我们的工作重点将包括:
ChatGLM3 在 LoRA 微调之后幻觉情况特别严重,无法称之为可用,因此 ChatGLM3 系列的模型暂无法计算具体的性能指标。可能是因为 ChatGLM3 的 Prompt 格式比较特殊, LLaMA Factory 官方没有对 ChatGLM3 进行特殊优化。同时,在 ChatGLM3 的 Repo 中发现官方删除了 ChatGLM3 使用 P-tuning 微调 Agent 的代码,给出的理由是效果较差。关于 ChatGLM3 的 Agent 微调至此作罢,接下来会持续跟踪后续进展。
本文分享自 ThoughtWorks洞见 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!