从QQ聊天记录到AI训练数据:高效格式转换实战指南
1. 为什么需要将QQ聊天记录转为AI训练数据最近两年AI对话模型火得一塌糊涂很多开发者都想训练自己的个性化AI助手。但最大的难题就是找不到合适的训练数据——总不能直接用网上公开的语料库吧那训练出来的模型完全没有个人特色。其实我们每天都在生产大量自然对话数据比如微信、QQ聊天记录。特别是QQ它的消息管理器可以直接导出历史聊天记录为txt文件。这些真实对话包含了我们最自然的语言习惯、用词偏好和表达方式简直是训练个性化AI的绝佳原料。不过直接使用这些原始数据会遇到几个问题格式混乱包含时间戳、昵称、系统消息等干扰信息无效内容图片、表情、撤回消息等需要过滤隐私风险可能暴露真实姓名、联系方式等敏感信息结构不符AI训练需要特定的jsonl格式包含明确的角色区分我去年帮朋友训练一个模仿他说话风格的AI时就花了整整两周时间手工处理聊天记录。后来痛定思痛开发了一套自动化转换工具现在只需要3分钟就能完成原来手动2天的工作量。2. 准备工作获取原始聊天记录2.1 使用QQ消息管理器导出数据首先确保你使用的是Windows版QQMac版导出格式不太一样。操作步骤很简单打开QQ主面板点击底部消息管理器图标长得像个时钟在左侧联系人列表中找到目标对话右键点击 → 导出消息记录选择文本文件(*.txt)格式保存建议为每个重要联系人都单独导出一次存放在不同文件夹中。比如my_chats/ ├── mom/ │ └── chat_mom.txt ├── best_friend/ │ └── chat_friend.txt └── work_group/ └── chat_work.txt2.2 原始数据格式解析导出的txt文件结构大致如下消息对象:张三 2023-05-20 10:00:00 老李 周末打球去吗 2023-05-20 10:02:00 我 好啊老地方 2023-05-20 10:02:30 老李 [图片] 2023-05-20 10:03:00 老李 就这个球场关键特征前8行是元数据可以忽略每条消息以时间戳昵称开头空行分隔不同消息块图片/表情会显示为[图片][表情]系统消息如撤回了一条消息也会被记录3. 数据清洗与转换核心逻辑3.1 过滤无效内容不是所有消息都有训练价值我们需要过滤掉invalid_set { [自动回复], 撤回了一条消息, 对方已成功接收了你发送的离线文件, [戳一戳], [图片], [表情] } def is_valid_msg(msg): # 检查是否是纯图片/表情 if re.sub(r\[图片\]|\[表情\], , msg).strip() : return False # 检查是否包含无效关键词 return not any(word in msg for word in invalid_set)3.2 角色识别与区分AI训练需要明确区分对话双方。在QQ数据中我发送的消息 → 对应AI助手的输出对方发送的消息 → 对应用户的输入但有个坑QQ记录的昵称可能是备注名或QQ昵称。我的解决方案是让用户自己确认哪些昵称属于我# 从所有消息中提取出现的昵称 nicknames set() for msg in messages: if msg.sender not in contacts: nicknames.add(msg.sender) # 用户手动过滤非本人昵称 my_names {小明, 明明} # 用户自己确认3.3 话题分割策略连续对话应该保持话题连贯性。我通过两个指标自动分割话题时间间隔 6小时对话轮次 10轮实现代码def should_split(last_msg, current_msg): time_gap current_msg.time - last_msg.time return time_gap.total_seconds() 6*36004. 完整转换流程实现4.1 核心数据结构使用namedtuple来组织消息数据from collections import namedtuple from datetime import datetime Msg namedtuple(Msg, [time, role, content]) # 示例 msg Msg( timedatetime(2023,5,20,10,0,0), roleuser, content你好啊 )4.2 转换主流程def convert_to_jsonl(input_txt, output_jsonl): with open(input_txt, r, encodingutf-8) as f_in, \ open(output_jsonl, w, encodingutf-8) as f_out: buffer [] last_role None for line in f_in: if is_msg_start(line): # 检测消息开头 time, role, content parse_msg(line) if not is_valid(content): continue if role ! last_role and buffer: write_buffer(buffer, f_out) buffer [] buffer.append((role, content)) last_role role if buffer: # 写入剩余内容 write_buffer(buffer, f_out) def write_buffer(buffer, file): # 确保是user和assistant交替 for i in range(0, len(buffer)-1, 2): user_msg buffer[i] bot_msg buffer[i1] if user_msg[0] ! user: user_msg, bot_msg bot_msg, user_msg json.dump({ conversations: [ {role: user, content: user_msg[1]}, {role: assistant, content: bot_msg[1]} ] }, file, ensure_asciiFalse) file.write(\n)4.3 隐私保护处理在写入最终文件前建议做敏感信息替换def sanitize_content(content): # 替换电话号码 content re.sub(r1[3-9]\d{9}, PHONE, content) # 替换地址 content re.sub(r[省市县区].?(路|街|号), ADDRESS, content) return content5. 进阶优化技巧5.1 处理连续多行消息有些人习惯分段发消息可以适当合并def merge_multi_line(msgs): merged [] temp_msg None for msg in msgs: if temp_msg and msg.role temp_msg.role: # 相同发送者合并内容 temp_msg temp_msg._replace( contenttemp_msg.content msg.content ) else: if temp_msg: merged.append(temp_msg) temp_msg msg if temp_msg: merged.append(temp_msg) return merged5.2 表情符号处理方案两种可选方案完全删除content re.sub(r\[.*?\], , content)替换为文字描述content re.sub(r\[图片\], [图片], content)推荐后者可以保留更多语义信息。5.3 批量处理工具开发我用PyQt5做了个图形化工具主要功能选择文件夹批量处理可视化确认昵称进度显示错误日志核心代码结构QQConverter/ ├── main.py # 主界面 ├── converter.py # 核心转换逻辑 └── utils.py # 辅助函数6. 最终效果验证转换后的jsonl文件示例{conversations: [{role: user, content: 周末去哪玩}, {role: assistant, content: 去爬山怎么样}]} {conversations: [{role: user, content: 几点集合}, {role: assistant, content: 早上8点地铁站见}]}适合直接用于主流AI框架训练from transformers import Trainer trainer Trainer( modelmodel, train_datasetload_dataset(json, data_filesconverted.jsonl) ) trainer.train()我在实际项目中用这种方法处理了超过10万条聊天记录训练出的AI助手能很好地模仿个人语言风格。有个朋友甚至说他分不清哪个是真人回复哪个是AI生成的。