AI夏令营 #Datawhale #夏令营
基于星火大模型的群聊对话分角色要素提取挑战赛
举办方:科大讯飞股份有限公司
一、赛事背景
在当今数字化时代,企业积累了丰富的对话数据,这些数据不仅是客户与企业之间交流的记录,更是隐藏着宝贵信息的宝库。在这个背景下,群聊对话分角色要素提取成为了企业营销和服务的一项重要策略。
群聊对话分角色要素提取的理念是基于企业对话数据的深度分析和挖掘。通过对群聊对话数据进行分析,企业可以更好地理解客户的需求、兴趣和行为模式,从而精准地把握客户的需求和心理,提供更加个性化和优质的服务。这不仅有助于企业更好地满足客户的需求,提升客户满意度,还可以为企业带来更多的商业价值和竞争优势。
群聊对话分角色要素提取的研究,将企业对话数据转化为可用的信息和智能的洞察,为企业营销和服务提供了新的思路和方法。通过挖掘对话数据中隐藏的客户行为特征和趋势,企业可以更加精准地进行客户定位、推广营销和产品服务,实现营销效果的最大化和客户价值的最大化。这将为企业带来更广阔的发展空间和更持续的竞争优势。
二、赛事任务
从给定的<客服>与<客户>的群聊对话中, 提取出指定的字段信息,待提取的全部字段见下数据说明。
三、评审规则
1.平台说明
参赛选手需基于讯飞星火大模型V3.5完成任务。允许使用大模型微调的方式进行信息抽取, 但微调的基座模型仅限星火大模型。
关于星火V3.5资源,组委会将为报名参赛选手统一发放API资源福利,选手用个人参赛账号登录讯飞开放平台:https://www.xfyun.cn/ ,前往控制台中查看使用。关于微调训练资源,选手用参赛账户登陆大模型训练平台( https://training.xfyun.cn/overview ),可领取本次比赛的训练资源福利。
2.数据说明
赛题方提供了184条真实场景的群聊对话数据以及人工标注后的字段提取结果,其中训练数据129条,测试数据 55条。按照各类字段提取的难易程度,共设置了1、2、3三种难度分数。待提取的字段以及提取正确时的得分规则如下:
序号 字段名称 是否单值 是否可为空 难度分数 答案是否唯一 备注
1 基本信息-姓名 是 是 1 是
2 基本信息-手机号码 是 是 1 是
3 基本信息-邮箱 是 是 1 是
4 基本信息-地区 是 是 1 是
5 基本信息-详细地址 是 是 1 是
6 基本信息-性别 是 是 1 是
7 基本信息-年龄 是 是 1 是
8 基本信息-生日 是 是 1 是
9 咨询类型 否 是 2 是
10 意向产品 否 是 3 是
11 购买异议点 否 是 3 是
12 客户预算-预算是否充足 是 是 2 是
13 客户预算-总体预算金额 是 是 2 是
14 客户预算-预算明细 是 是 3 否
15 竞品信息 是 是 2 是
16 客户是否有意向 是 是 1 是
17 客户是否有卡点 是 是 1 是
18 客户购买阶段 是 是 2 是
19 下一步跟进计划-参与人 否 是 2 是
20 下一步跟进计划-时间点 是 是 2 是
21 下一步跟进计划-具体事项 是 是 3 否
备注:
1)可为空的字段,当判定无相应信息、无法做出判断等情况,统一取值为空字符串
2)对于非单值字段,请使用list来表示
3.评估指标
测试集的每条数据同样包含共21个字段, 按照各字段难易程度划分总计满分36分。每个提取正确性的判定标准如下:
1)对于答案唯一字段,将使用完全匹配的方式计算提取是否正确,提取正确得到相应分数,否则为0分
2)对于答案不唯一字段,将综合考虑提取完整性、语义相似度等维度判定提取的匹配分数,最终该字段得分为 “匹配分数 * 该字段难度分数”
每条测试数据的最终得分为各字段累计得分。最终测试集上的分数为所有测试数据的平均得分。
4.评测及排行
1)本赛题均提供下载数据,选手在本地进行算法调试,在比赛页面提交结果。
2)排行按照得分从高到低排序,排行榜将选择团队的历史最优成绩进行排名。
四、作品提交要求
1、文件格式:按照 json格式提交
2、文件大小:无要求
3、提交次数限制:每支队伍每天最多3次
4、文件详细说明:编码为UTF-8,具体格式参考提交示例
5、关于大模型的使用说明&限制。
• 如果使用大模型进行信息抽取, 本次仅限使用星火大模型。
• 为了排除人工校验、修正等作弊方式,本次比赛除了提交答案之外,排行榜前3名选手需要提供完整的源代码进行审核,要求抽取的结果必须可以准确复现。
• 注:排行榜前3名有审核不通过现象时,依次按得分顺延。满分36分,原则上最终入围决赛三甲得分不得低于20分。
• 允许使用大模型微调的方式进行信息抽取, 微调的基座模型仅限星火大模型。
五、赛程规则
本赛题实行一轮赛制
【赛程周期】
6月9日-7月30日
1、6月9日10:00发布训练集、开发集、测试集(即开启比赛榜单)
2、比赛作品提交截止日期为7月30日17:00,公布名次日期为8月16日10:00
【现场答辩】
1、最终前三名团队将受邀参加科大讯飞AI开发者大赛总决赛并于现场进行答辩
2、答辩以(10mins陈述+5mins问答)的形式进行
3、根据作品成绩和答辩成绩综合评分(作品成绩占比70%,现场答辩分数占比30%)
引用https://datawhaler.feishu.cn/wiki/OALOwvCYuiS5jIkakU5c7nElnYg的文章:
赛事背景
在数字化时代,企业积累了大量对话数据,这些数据不仅是交流记录,还隐藏着宝贵的信息。群聊对话分角色要素提取是企业营销和服务的重要策略,通过分析这些数据,企业可以更好地理解客户需求,提供个性化服务,提升客户满意度和商业价值。
赛事任务
从给定的<客服>与<客户>的群聊对话中,提取出指定的字段信息,具体待提取的字段信息见下文。
笔者思路
Q:题解析当中能否提一提这个赛题是一个经典NLP任务,以往的处理方案和用大模型处理各自的优势点是什么?
A:其实看数据的话也不经典,知识数据的实体抽取任务以前的SOTA方案是使用UIE。(https://www.cnblogs.com/ting1/p/17637546.html)往往能拿到高分,但是和大模型的本质思路类似。还有这个赛题的数据量之大,可能只有大模型一条路了。在大模型出来之前百度在抽取任务国际领先的,现在大模型出来对抽取任务有降维打击的味道了。
从赛题本身要求看,只支持api与微调,可以说是半开放命题。所以只选用了大模型方法解决这个赛题咯。
评审规则
- 平台说明
参赛选手需基于讯飞星火大模型Spark Max完成任务,可使用大模型微调。报名参赛选手将获得API和微调训练资源福利,详情请登录讯飞开放平台和大模型训练平台查看。 - 数据说明
赛题提供了184条真实场景的群聊对话数据,包括训练数据129条,测试数据55条。字段提取的难易程度分为1、2、3三种,具体得分规则如下:
序号
字段名称
是否单值
是否可为空
难度分数
答案是否唯一
1
基本信息-姓名
是
是
1
是
2
基本信息-手机号码
是
是
1
是
3
基本信息-邮箱
是
是
1
是
4
基本信息-地区
是
是
1
是
5
基本信息-详细地址
是
是
1
是
6
基本信息-性别
是
是
1
是
7
基本信息-年龄
是
是
1
是
8
基本信息-生日
是
是
1
是
9
咨询类型
否
是
2
是
10
意向产品
否
是
3
是
11
购买异议点
否
是
3
是
12
客户预算-预算是否充足
是
是
2
是
13
客户预算-总体预算金额
是
是
2
是
14
客户预算-预算明细
是
是
3
否
15
竞品信息
是
是
2
是
16
客户是否有意向
是
是
1
是
17
客户是否有卡点
是
是
1
是
18
客户购买阶段
是
是
2
是
19
下一步跟进计划-参与人
否
是
2
是
20
下一步跟进计划-时间点
是
是
2
是
21
下一步跟进计划-具体事项
是
是
3
否
备注 - 可为空的字段,当判定无相应信息、无法做出判断等情况,统一取值为空字符串。
- 对于非单值字段,请使用列表(list)来表示。
赛题数据集
- 训练数据集:train.json,格式大致如下:
[
{
"chat_text": "王勇7:哈喽吴娜8,企微那边关于外部联系人上回说的沟通对接有什么反馈嘛\n王勇7:价格有没有调整\n吴娜8:目前应该是没有 具体的方案还没出来 我估计对方是突然不需要了就不搞了吧\n王勇7:[图片]\n王勇7:会话存档又有新动作了\n吴娜8:企微好友这块还没明确\n吴娜8:[图片]\n吴娜8:不知道到底啥情况hhh\n王勇7:他们腾讯内部针对代理商售卖折扣做了调整,可能只是渠道这一类\n王勇7:现在他们有很多腾讯代理商也是企微服务商,做底走单子\n王勇7:[图片]\n王勇7:看样子这条路也堵住了\n王勇7:太难了\n吴娜8:真不容易hhh\n王勇7:哎对了吴娜8,咨询你个事\n王勇7:今天辉煌有限公司这个公司咨询我们工具\n吴娜8:也是元保的 子公司\n王勇7:我记得这个公司是之前元保对接下过会话存档,他是和元保啥关系\n王勇7:哦哦\n王勇7:原来如此\n王勇7:okok\n王勇7:懂了\n吴娜8:就是一个子公司hhh\n王勇7:付款还是元保付嘛\n王勇7:还是他们自己也可以走对接\n吴娜8:都是同一批人吧 最终还是采购部采购 \n吴娜8:是不是换人了\n王勇7:对 换人了\n王勇7:那我找元保的采购老师问问吧\n吴娜8:嗯嗯 采购部新招了好几个人 \n吴娜8:其实都是一个部门的 分工有调整\n吴娜8:或者业务先咨询 后面转采购也是有可能的\n王勇7:get 还得是你呀\n王勇7:最近我们公司有一批腾讯龙年的企鹅公仔\n王勇7:给你寄一个吧\n王勇7:2024年的新款\n吴娜8:之前元保采购就两个人 我和领导 \n吴娜8:我走了之后变成了 六七个人 哈哈哈哈哈\n王勇7:[图片]\n王勇7:[图片]\n王勇7:这是一条引用/回复消息:\n"吴娜8:\n我走了之后变成了 六七个人 哈哈哈哈哈"\n------\n这说明你一个人顶六七个人,牛蛙牛蛙\n吴娜8:一把辛酸泪hhh 不用给我寄啦哈哈别客气\n王勇7:[表情]\n王勇7:行吧行吧\n吴娜8:[表情]\n王勇7:后面企微有新动静都帮你关注,第一时间跟你说\n吴娜8:好嘞~\n王勇7:哦了",
"infos": [
{
"基本信息-姓名": "吴娜8",
"基本信息-手机号码": "",
"基本信息-邮箱": "",
"基本信息-地区": "",
"基本信息-详细地址": "",
"基本信息-性别": "",
"基本信息-年龄": "",
"基本信息-生日": "",
"咨询类型": [
"答疑"
],
"意向产品": [],
"购买异议点": [
"客户内部问题"
],
"客户预算-预算是否充足": "",
"客户预算-总体预算金额": "",
"客户预算-预算明细": "",
"竞品信息": "",
"客户是否有意向": "无意向",
"客户是否有卡点": "无卡点",
"客户购买阶段": "项目搁置",
"下一步跟进计划-参与人": [],
"下一步跟进计划-时间点": "",
"下一步跟进计划-具体事项": ""
}
]
},
...
]
我们需要关注的字段解释如下:- chat_text:群聊消息记录
- infos:提取的字段信息
- 测试数据集
[
{
"chat_text": "王勇4:钱老师,客户那边确定了时间了嘛?\n钱洋9:确定了呀 我发咱们群里了\n王勇4:是不是还有其他群啊?我在的这个群,没收到信息呢\n王勇4:[图片]\n王勇4:现在确定的具体时间是怎样的呢,我好内部协调下时间安排,谢啦\n钱洋9:哦哦sorry[捂脸]下周一(12.4号)下午两点交流哈\n钱洋9:时间没问题吧\n王勇4:收到,我去内部协调时间哈\n钱洋9:多谢\n王勇4:[握手]\n王勇4:钱老师,下周一我们售前是线上支持,届时需要您帮忙创建一个线上会议链接哈。我要他们通过个微登录进去。\n钱洋9:啊能尽量现场支持一下吗\n王勇4:我去沟通下哈,请问客户侧此次参加的是哪个部门呢\n钱洋9:sorry刚在客户,多谢哈,线上支持这个效果可能达不到预期,因为这块确实咱们巨石蓝海的同事更专业, 而且客户这边层级还是蛮高的是客户CTO和营销系统总监\n王勇4:好滴\n王勇4:我内部协调下哈\n钱洋9:感谢\n王勇4:已经确定好了哈,售前会去现场\n钱洋9:感谢感谢 可以拉进群里到时候好碰头[抱拳]\n王勇4:嗯嗯,好滴\n钱洋9:王勇4 麻烦问下吉祥汽车你们有在接触吗\n钱洋9:上次听他们IT说也有scrm需求不知道你们是不是已经对接了\n王勇4:吉利是我们合作的\n王勇4:但是吉祥没有呢\n钱洋9:OK\n王勇4:早上好啊,极石客户那边上次提到和业务部门进行需求确定,有给答复嘛?\n王勇4:风华有限公司,上次咱们聊到的吉祥汽车,我们内部暂时没有人在对接的哈\n钱洋9:好的 客户还没反馈哈 估计要下周了\n王勇4:好滴\n钱洋9:[会话记录消息]\n钱洋9:洛轲那边年前需求就能确定 跟王勇4老师同步下[呲牙]\n王勇4:好嘞\n王勇4:那就等他们的最新消息了\n钱洋9:嗯啊",
"infos": []
},
...
] - 提交示例
暂时无法在飞书文档外展示此内容
- Baseline 详解
基于星火大模型的群聊对话分角色要素提取挑战-baseline
baseline基于讯飞星火大模型V3.5模型对群聊对话数据进行字段的抽取
2.1 环境配置
⚠️注意:spark_ai_python要求python版本一定要3.8以上
!pip install --upgrade -q spark_ai_python tqdm
2.2 数据处理
这里面我们可以简单读取一下训练集和测试集的数据本身。了解具体需要处理什么?
import json
def read_json(json_file_path):
"""读取json文件"""
with open(json_file_path, 'r') as f:
data = json.load(f)
return data
def write_json(json_file_path, data):
"""写入json文件"""
with open(json_file_path, 'w') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
读取数据
train_data = read_json("dataset/train.json")
test_data = read_json("dataset/test_data.json")
查看对话数据
print(train_data[100]['chat_text'])
可以看到下面的对话数据很像我们的聊天截图,这样的聊天数据其实在日常生活中很常见。那么这个任务也很有必要,就是从这些杂乱的记录里抽取出我们需要的数据。
[图片]
2.3 prompt工程
这里的prompt编写规则可以进行这样的理解:
任务目标——抽取数据定义——抽取内容引入——抽取规则强调
以这样的prompt模版配合强大的星火3.5模型,可以完成我们的抽取任务。
prompt 设计
PROMPT_EXTRACT = """
你将获得一段群聊对话记录。你的任务是根据给定的表单格式从对话记录中提取结构化信息。在提取信息时,请确保它与类型信息完全匹配,不要添加任何没有出现在下面模式中的属性。
表单格式如下:
info: Array<Dict(
"基本信息-姓名": string | "", // 客户的姓名。
"基本信息-手机号码": string | "", // 客户的手机号码。
"基本信息-邮箱": string | "", // 客户的电子邮箱地址。
"基本信息-地区": string | "", // 客户所在的地区或城市。
"基本信息-详细地址": string | "", // 客户的详细地址。
"基本信息-性别": string | "", // 客户的性别。
"基本信息-年龄": string | "", // 客户的年龄。
"基本信息-生日": string | "", // 客户的生日。
"咨询类型": string[] | [], // 客户的咨询类型,如询价、答疑等。
"意向产品": string[] | [], // 客户感兴趣的产品。
"购买异议点": string[] | [], // 客户在购买过程中提出的异议或问题。
"客户预算-预算是否充足": string | "", // 客户的预算是否充足。示例:充足, 不充足
"客户预算-总体预算金额": string | "", // 客户的总体预算金额。
"客户预算-预算明细": string | "", // 客户预算的具体明细。
"竞品信息": string | "", // 竞争对手的信息。
"客户是否有意向": string | "", // 客户是否有购买意向。示例:有意向, 无意向
"客户是否有卡点": string | "", // 客户在购买过程中是否遇到阻碍或卡点。示例:有卡点, 无卡点
"客户购买阶段": string | "", // 客户当前的购买阶段,如合同中、方案交流等。
"下一步跟进计划-参与人": string[] | [], // 下一步跟进计划中涉及的人员(客服人员)。
"下一步跟进计划-时间点": string | "", // 下一步跟进的时间点。
"下一步跟进计划-具体事项": string | "" // 下一步需要进行的具体事项。
)>
请分析以下群聊对话记录,并根据上述格式提取信息:
对话记录:
{content}
请将提取的信息以JSON格式输出。
不要添加任何澄清信息。
输出必须遵循上面的模式。
不要添加任何没有出现在模式中的附加字段。
不要随意删除字段。
输出:
[{{
"基本信息-姓名": "姓名",
"基本信息-手机号码": "手机号码",
"基本信息-邮箱": "邮箱",
"基本信息-地区": "地区",
"基本信息-详细地址": "详细地址",
"基本信息-性别": "性别",
"基本信息-年龄": "年龄",
"基本信息-生日": "生日",
"咨询类型": ["咨询类型"],
"意向产品": ["意向产品"],
"购买异议点": ["购买异议点"],
"客户预算-预算是否充足": "充足或不充足",
"客户预算-总体预算金额": "总体预算金额",
"客户预算-预算明细": "预算明细",
"竞品信息": "竞品信息",
"客户是否有意向": "有意向或无意向",
"客户是否有卡点": "有卡点或无卡点",
"客户购买阶段": "购买阶段",
"下一步跟进计划-参与人": ["跟进计划参与人"],
"下一步跟进计划-时间点": "跟进计划时间点",
"下一步跟进计划-具体事项": "跟进计划具体事项"
}}, ...]
"""
2.4 数据抽取
使用prompt进行调试发现以下几个问题:
1. 大模型总是不能直接输出python直接可读取的json格式,如:
[
{
"基本信息-姓名": "张三",
"基本信息-手机号码": "12345678901",
"基本信息-邮箱": "[email protected]",
"基本信息-地区": "北京市",
"基本信息-详细地址": "朝阳区某街道",
"基本信息-性别": "男",
"基本信息-年龄": "30",
"基本信息-生日": "1990-01-01",
"咨询类型": ["询价"],
"意向产品": ["产品A"],
"购买异议点": ["价格高"],
"客户预算-预算是否充足": "充足",
"客户预算-总体预算金额": "10000",
"客户预算-预算明细": "详细预算内容",
"竞品信息": "竞争对手B",
"客户是否有意向": "有意向",
"客户是否有卡点": "无卡点",
"客户购买阶段": "合同中",
"下一步跟进计划-参与人": ["客服A"],
"下一步跟进计划-时间点": "2024-07-01",
"下一步跟进计划-具体事项": "沟通具体事项"
}
]
故使用函数convert_all_json_in_text_to_dict对json数据进行提取
def convert_all_json_in_text_to_dict(text):
"""提取LLM输出文本中的json字符串"""
dicts, stack = [], []
for i in range(len(text)):
if text[i] == '{':
stack.append(i)
elif text[i] == '}':
begin = stack.pop()
if not stack:
dicts.append(json.loads(text[begin:i+1]))
return dicts
2. 大模型偶尔会出现缺少字段的情况,故使用check_and_complete_json_format函数对大模型抽取的结果进行字段格式的检查以及缺少的字段进行补全。
import json
class JsonFormatError(Exception):
def init(self, message):
self.message = message
super().init(self.message)
def check_and_complete_json_format(data):
required_keys = {
"基本信息-姓名": str,
"基本信息-手机号码": str,
"基本信息-邮箱": str,
"基本信息-地区": str,
"基本信息-详细地址": str,
"基本信息-性别": str,
"基本信息-年龄": str,
"基本信息-生日": str,
"咨询类型": list,
"意向产品": list,
"购买异议点": list,
"客户预算-预算是否充足": str,
"客户预算-总体预算金额": str,
"客户预算-预算明细": str,
"竞品信息": str,
"客户是否有意向": str,
"客户是否有卡点": str,
"客户购买阶段": str,
"下一步跟进计划-参与人": list,
"下一步跟进计划-时间点": str,
"下一步跟进计划-具体事项": str
}
if not isinstance(data, list):
raise JsonFormatError("Data is not a list")
for item in data:
if not isinstance(item, dict):
raise JsonFormatError("Item is not a dictionary")
for key, value_type in required_keys.items():
if key not in item:
item[key] = [] if value_type == list else ""
if not isinstance(item[key], value_type):
raise JsonFormatError(f"Key '{key}' is not of type {value_type.__name__}")
if value_type == list and not all(isinstance(i, str) for i in item[key]):
raise JsonFormatError(f"Key '{key}' does not contain all strings in the list")
return data
完整代码
from sparkai.llm.llm import ChatSparkLLM, ChunkPrintHandler
from sparkai.core.messages import ChatMessage
import json
from tqdm import tqdm
星火认知大模型Spark3.5 Max的URL值,其他版本大模型URL值请前往文档(https://www.xfyun.cn/doc/spark/Web.html)查看
SPARKAI_URL = 'wss://spark-api.xf-yun.com/v3.5/chat'
星火认知大模型调用秘钥信息,请前往讯飞开放平台控制台(https://console.xfyun.cn/services/bm35)查看
SPARKAI_APP_ID = ''
SPARKAI_API_SECRET = ''
SPARKAI_API_KEY = ''
星火认知大模型Spark3.5 Max的domain值,其他版本大模型domain值请前往文档(https://www.xfyun.cn/doc/spark/Web.html)查看
SPARKAI_DOMAIN = 'generalv3.5'
prompt 设计
PROMPT_EXTRACT = """
你将获得一段群聊对话记录。你的任务是根据给定的表单格式从对话记录中提取结构化信息。在提取信息时,请确保它与类型信息完全匹配,不要添加任何没有出现在下面模式中的属性。
表单格式如下:
info: Array<Dict(
"基本信息-姓名": string | "", // 客户的姓名。
"基本信息-手机号码": string | "", // 客户的手机号码。
"基本信息-邮箱": string | "", // 客户的电子邮箱地址。
"基本信息-地区": string | "", // 客户所在的地区或城市。
"基本信息-详细地址": string | "", // 客户的详细地址。
"基本信息-性别": string | "", // 客户的性别。
"基本信息-年龄": string | "", // 客户的年龄。
"基本信息-生日": string | "", // 客户的生日。
"咨询类型": string[] | [], // 客户的咨询类型,如询价、答疑等。
"意向产品": string[] | [], // 客户感兴趣的产品。
"购买异议点": string[] | [], // 客户在购买过程中提出的异议或问题。
"客户预算-预算是否充足": string | "", // 客户的预算是否充足。示例:充足, 不充足
"客户预算-总体预算金额": string | "", // 客户的总体预算金额。
"客户预算-预算明细": string | "", // 客户预算的具体明细。
"竞品信息": string | "", // 竞争对手的信息。
"客户是否有意向": string | "", // 客户是否有购买意向。示例:有意向, 无意向
"客户是否有卡点": string | "", // 客户在购买过程中是否遇到阻碍或卡点。示例:有卡点, 无卡点
"客户购买阶段": string | "", // 客户当前的购买阶段,如合同中、方案交流等。
"下一步跟进计划-参与人": string[] | [], // 下一步跟进计划中涉及的人员(客服人员)。
"下一步跟进计划-时间点": string | "", // 下一步跟进的时间点。
"下一步跟进计划-具体事项": string | "" // 下一步需要进行的具体事项。
)>
请分析以下群聊对话记录,并根据上述格式提取信息:
对话记录:
{content}
请将提取的信息以JSON格式输出。
不要添加任何澄清信息。
输出必须遵循上面的模式。
不要添加任何没有出现在模式中的附加字段。
不要随意删除字段。
输出:
[{{
"基本信息-姓名": "姓名",
"基本信息-手机号码": "手机号码",
"基本信息-邮箱": "邮箱",
"基本信息-地区": "地区",
"基本信息-详细地址": "详细地址",
"基本信息-性别": "性别",
"基本信息-年龄": "年龄",
"基本信息-生日": "生日",
"咨询类型": ["咨询类型"],
"意向产品": ["意向产品"],
"购买异议点": ["购买异议点"],
"客户预算-预算是否充足": "充足或不充足",
"客户预算-总体预算金额": "总体预算金额",
"客户预算-预算明细": "预算明细",
"竞品信息": "竞品信息",
"客户是否有意向": "有意向或无意向",
"客户是否有卡点": "有卡点或无卡点",
"客户购买阶段": "购买阶段",
"下一步跟进计划-参与人": ["跟进计划参与人"],
"下一步跟进计划-时间点": "跟进计划时间点",
"下一步跟进计划-具体事项": "跟进计划具体事项"
}}, ...]
"""
def read_json(json_file_path):
"""读取json文件"""
with open(json_file_path, 'r') as f:
data = json.load(f)
return data
def write_json(json_file_path, data):
"""写入json文件"""
with open(json_file_path, 'w') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
def get_completions(text):
messages = [ChatMessage(
role="user",
content=text
)]
spark = ChatSparkLLM(
spark_api_url=SPARKAI_URL,
spark_app_id=SPARKAI_APP_ID,
spark_api_key=SPARKAI_API_KEY,
spark_api_secret=SPARKAI_API_SECRET,
spark_llm_domain=SPARKAI_DOMAIN,
streaming=False,
)
handler = ChunkPrintHandler()
a = spark.generate([messages], callbacks=[handler])
return a.generations[0][0].text
def convert_all_json_in_text_to_dict(text):
"""提取LLM输出文本中的json字符串"""
dicts, stack = [], []
for i in range(len(text)):
if text[i] == '{':
stack.append(i)
elif text[i] == '}':
begin = stack.pop()
if not stack:
dicts.append(json.loads(text[begin:i+1]))
return dicts
class JsonFormatError(Exception):
def init(self, message):
self.message = message
super().init(self.message)
def check_and_complete_json_format(data):
required_keys = {
"基本信息-姓名": str,
"基本信息-手机号码": str,
"基本信息-邮箱": str,
"基本信息-地区": str,
"基本信息-详细地址": str,
"基本信息-性别": str,
"基本信息-年龄": str,
"基本信息-生日": str,
"咨询类型": list,
"意向产品": list,
"购买异议点": list,
"客户预算-预算是否充足": str,
"客户预算-总体预算金额": str,
"客户预算-预算明细": str,
"竞品信息": str,
"客户是否有意向": str,
"客户是否有卡点": str,
"客户购买阶段": str,
"下一步跟进计划-参与人": list,
"下一步跟进计划-时间点": str,
"下一步跟进计划-具体事项": str
}
if not isinstance(data, list):
raise JsonFormatError("Data is not a list")
for item in data:
if not isinstance(item, dict):
raise JsonFormatError("Item is not a dictionary")
for key, value_type in required_keys.items():
if key not in item:
item[key] = [] if value_type == list else ""
if not isinstance(item[key], value_type):
raise JsonFormatError(f"Key '{key}' is not of type {value_type.__name__}")
if value_type == list and not all(isinstance(i, str) for i in item[key]):
raise JsonFormatError(f"Key '{key}' does not contain all strings in the list")
if name == "main":
retry_count = 5 # 重试次数
result = []
error_data = []
# 读取数据
train_data = read_json("dataset/train.json")
test_data = read_json("dataset/test_data.json")
for index, data in tqdm(enumerate(test_data)):
index += 1
is_success = False
for i in range(retry_count):
try:
res = get_completions(PROMPT_EXTRACT.format(content=data["chat_text"]))
infos = convert_all_json_in_text_to_dict(res)
infos = check_and_complete_json_format(infos)
result.append({
"infos": infos,
"index": index
})
is_success = True
break
except Exception as e:
print("index:", index, ", error:", e)
continue
if not is_success:
data["index"] = index
error_data.append(data)
write_json("output.json", result)
本教程主要由 @测试员001 、@刘伟鸿 写作贡献,经过多位助教内测和建议修改,期待你的点赞和评论~
引用https://datawhaler.feishu.cn/wiki/MiiLwNkN1iIgQQksPVRcqbOvnkf文章:
- 提分整体方向
当大家看到这个任务的时候说明有很强的意愿去拿下大模型这个赛题的高分。那么也请关注一些问题,首先你如何理解这个任务呢?这个任务是一个抽取任务,但是也需要大家明确主要做信息抽取。那么抽取想拿高分就需要精度,通过大语言模型怎么保证每个抽取信息点都被关注到?还有抽取的内容是否有效呢?
我相信带着问题,更容易找到你想要的答案。下面的思路很多,可以互相组合搭配。 - 提分思路
2.1 数据处理
我们观察过整体数据,有的很长,长到3万字左右;有的很短,短到300字左右,而平均数据每条也有6000字。有没有想过怎么让数据展现的信息更有效?
你可以从以下几个方面去努力 - 试着用大语言模型做数据增强,根据抽取字段做数据提取。
- 通过字符的处理,将对话的无关信息删除。不要让“[图片]”这种信息干扰你。
数据处理优质方案:群内大佬分享
[图片]
2.2 prompt工程
prompt是一个做大语言模型应用都需要掌握的东西,目前baseline的抽取方式已经有一定的效果。你可以从下面这篇文章中找到一些更好的方法进行输出约束,让结果更满意。
https://mp.weixin.qq.com/s/J8J_8ht7NSgbpJV5HNyhgA
当然,除了prompt本身的优化,你可以试试COT等方法增强抽取的效果。
datawhale也有很多优秀的课程,我们推荐:
https://github.com/datawhalechina/llm-cookbook/tree/main/content/必修一-Prompt Engineering For Developers
2.3 agent思路
当你处理复杂问题的时候,往往单独使用大语言模型无法直接完成你想要的内容,这里你可以试试做一个自己的agent。
什么是AI Agent:
Agent(智能体) = 一个设置了一些目标或任务,可以迭代运行的大型语言模型。这与大型语言模型(LLM)在像ChatGPT这样的工具中“通常”的使用方式不同。在ChatGPT中,你提出一个问题并获得一个答案作为回应。而Agent拥有复杂的工作流程,模型本质上可以自我对话,而无需人类驱动每一部分的交互。
-Logan Kilpatrick, OpenAI 开发者关系负责人
ChatGPT接收单一查询的输入并返回输出,它一次不能完成超过一个任务。而AI Agent则可以自驱的定义工作流程并规划任务进行解决。比如,如果你有一个天气插件,当用户问“NYC(纽约缩写)的温度是多少?”,模型就会知道它无法回答这个问题,并查看用户安装的可用插件。假设它发送请求,API返回了一个错误信息,说“NYC不是一个有效的地点,请使用详细的城市名称,不要使用缩写”,模型实际上可以读取这个错误并发送新的请求来修复它。在这次人工智能的浪潮中AI Agent的火花诞生于 GPT插件商城以及AutoGPT。这分别提到Agent的工具调用能力和规划能力,在 LLM 支持的自主Agent系统中,LLM 充当Agents的大脑,并辅以几个关键组成部分:
• 规划
○ 子目标和分解:Agents将大型任务分解为更小的、可管理的子目标,从而能够有效处理复杂的任务。
○ 反思和完善:Agents可以对过去的行为进行自我批评和自我反思,从错误中吸取教训,并针对未来的步骤进行完善,从而提高最终结果的质量。
• 记忆
○ 短期记忆:我认为所有的上下文学习(参见提示工程)都是利用模型的短期记忆来学习。
○ 长期记忆:这为Agents提供了长时间保留和回忆(无限)信息的能力,通常是通过利用外部向量存储和快速检索来实现。
• 工具使用
○ Agents学习调用外部 API 来获取模型权重中缺失的额外信息(通常在预训练后很难更改),包括当前信息、代码执行能力、对专有信息源的访问等。
[图片]
图 1. LLM 驱动的自主Agents系统概述参考LLM Powered Autonomous AgentsLilian Weng(OpenAI研究主管)
这里我们推荐:
https://github.com/datawhalechina/hugging-multi-agent/tree/main
2.4 数据集扩充
回顾数据集,我们的训练集为130条,但如果只是130条做微调一个问题是数据量太少了,我们没法完全去微调我们的数据。那这里就需要使用大模型对task3处理的数据再做扩充,这样会保证微调效果更好。
2.5 微调改进
微调方面也需要做更新改进,在task03中我们使用了讯飞平台的微调任务。但只是通过这种方法得到的微调效果可能并不理想,怎么在这里下点功夫呢?
-
很简单的一点是对赛题抽取数据的任务分类,用大模型总结成不同的类别。接着针对类别单独做抽取微调。效果会好很多。
-
设计总结模型,首先训练出来一个总结的大模型,通过总结大模型稳定的抽取出关键信息减少其他信息干扰,然后再送入抽取的微调模型。
2.6 function call
function call可以设计出专门用来抽取的工具,相比于直接调用大模型,function call的优势在于可以稳定控制输出,得到需要的输出结构。正巧星火3.5大模型也支持这个功能,建议大家尝试。
这里有吴恩达的抽取任务新课,大家学有余力可以看看:
《Function-Calling and Data Extraction with LLMs》
https://www.bilibili.com/video/BV1pw4m1e7U3/?spm_id_from=333.999.0.0&vd_source=1a432a45372ea0a0d1ec88a20d9cef2c
2.7 验证工具
当每天只有三次提交机会的时候,很多同学觉得很有限。还有你不知道那些字段抽取还有提升空间,那些字段效果已经很好不用再做优化?如何精准命中?
这里你可以尝试自己根据
测试集的每条数据同样包含共21个字段, 按照各字段难易程度划分总计满分36分。每个提取正确性的判定标准如下:
1)对于答案唯一字段,将使用完全匹配的方式计算提取是否正确,提取正确得到相应分数,否则为0分
2)对于答案不唯一字段,将综合考虑提取完整性、语义相似度等维度判定提取的匹配分数,最终该字段得分为 “匹配分数 * 该字段难度分数”
每条测试数据的最终得分为各字段累计得分。最终测试集上的分数为所有测试数据的平均得分。打分标准,进行打分函数的设计。来自我评价。同时通过训练集,对你预测的结果做评价,看看你在训练集每个字段的综合得得分率,找到抽取的薄弱环节进行优化。
-
知识补充
-
动手学大语言模型应用开发 https://github.com/datawhalechina/llm-universe/tree/main
-
多智能体开发 https://github.com/datawhalechina/hugging-multi-agent/tree/main
-
聪明办法学python(深度学习方向)https://github.com/datawhalechina/learn-python-the-smart-way
-
结语
在本节中介绍的很多方法都需要大家慢慢积累,逐个实践。上分之路是痛苦的,是充满挑战的。但是每次能看到自己的成绩在不断刷新,也会明显感受到自己的进步。不管你是小白还是大神,在看完本节内容都会有所启发。不过一个人的能力有限,这里还是建议大家组队学习。在datawhale的夏令营找到自己的学习搭子,不断进步不断超越自己,相信这肯定是一段难忘的经历!加油!
本教程主要由 @测试员001 、@刘伟鸿 写作贡献,经过多位助教内测和建议修改,期待你的点赞和评论