恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Python实战:从微信聊天记录清洗到专属聊天机器人微调
首页
资讯中心
/
Python实战:从微信聊天记录清洗到专属聊天机器人微调
Python实战:从微信聊天记录清洗到专属聊天机器人微调
发布时间:2026/10/9 12:58:47
简介基于Python从微信聊天记录中训练专属聊天机器人的完整项目资料包适合毕业设计、课程设计及个人项目实战。内容涵盖源码、开发文档、模型训练流程与详细运行教程无论初学者还是进阶开发者都能借助清晰的目录结构快速上手。包内共有8个文件包含3个Python脚本分别对应聊天记录解密、数据预处理、模型API调用等环节、3张程序运行效果图、1份Markdown开发文档及授权文件压缩包整体大小约151KB轻量易部署。目前已有278人学习/下载项目源码经过严格测试可直接参考并在此基础上延展功能。通过这份资料读者既可以理解微信聊天数据清洗与格式化处理方式也可以学习如何调用大模型接口完成个性化对话训练同时还能借鉴项目整体架构与代码规范为同类AI应用开发打下扎实基础。1. 从微信聊天记录到“专属聊天机器人”这题的核心不是模型是语料用 Python 把微信聊天记录洗成训练语料再微调一个中文对话模型最后得到一个能模仿你自己或某个联系人说话风格的专属聊天机器人——这是近两年 Python NLP 毕设里很稳的一个题目。它同时解决两个真实痛点不用满世界找公开对话数据集你手机里就有最真实的个人口语语料项目完整覆盖了数据清洗、模型训练、部署评估全流程答辩时有东西可讲。适合两类人做毕业设计或课程设计的学生以及真想给自己留一个“说话风格副本”的开发者。整条链路是导出、解密、清洗、配对、微调、部署每一步都有现成的坑下面按这条线完整走一遍。2. 微信数据库解密与聊天记录导出先把“会说话的语料”拿到手2.1 微信数据库解密备份为什么是加密的导出工具到底做了什么微信聊天记录不会以明文 JSON 躺在手机或电脑里。移动端本地库一般是 SQLite 格式但被 SQLCipher 加密过直接打开只会得到一堆乱码。PC 端相对好处理一点登录后本地会生成缓存目录里面是若干带随机字符串目录名的文件夹存着账号信息、图片缓存和消息数据库文件。数据库文件的命名和路径各个版本不一样但类型基本是加密 SQLite。网上常见的微信导出工具做的事情本质是同一套流程用本机登录态里能拿到的密钥去解锁这个 SQLite 文件然后把 message、contact 这类表导出成 txt、csv 或 json。注意密钥来自你本机已经登录的微信不是去爆破什么东西所以这一类工具在“导自己的聊天记录”这个场景下是安全的。导出时建议直接选 json 或 csv 格式txt 格式会丢字段后面训练还要重新解析纯属给自己加活。这里要区分两类需求。如果你想学的对象是某个人单聊记录就够了如果你想做的是群聊里某个“话痨”的风格复刻那需要群聊记录里有 ta 的发言同时保留消息发送者字段。导出工具默认导出的是当天全部会话做毕设时第一件事就是先把目标数据挑出来别全量灌进训练脚本。另外导出的时间字段多数是 Unix 毫秒时间戳部分工具会转成 ISO 字符串这在后面清洗时会影响排序逻辑建议导出时看一眼样例再动手。2.2 用 Python 把导出的 JSON 整理成干净的聊天 CSV拿到导出文件后我一般先写一个十几行的脚本把 JSON 转成 CSV顺便过滤掉非文本消息。这步不做的话后续训练脚本会被图片消息、语音消息、系统提示搅得没法看。下面的脚本假设导出格式是 JSON 数组每条消息包含 type、sender、time、content 四个字段字段名因工具而异需要对照你手里的样例改。import json, csv, re from pathlib import Path def load_exported_chat(path: Path): 读取微信导出工具输出的 JSON每一项是一条消息 with open(path, r, encodingutf-8) as f: return json.load(f) def is_text_msg(msg): # 多数工具用 type1 表示文本消息图片/语音/视频/系统消息是其他值 return msg.get(type) 1 def parse_text_content(raw): # 微信文本消息经常带着 XML 外壳或转义符号先把标签剥掉 raw re.sub(r[^], , raw) return raw.strip() def export_to_csv(msgs, out_csv, max_len512): writer csv.writer(out_csv) writer.writerow([sender, time, content]) for m in msgs: if not is_text_msg(m): continue content parse_text_content(m.get(content, )) if not content or len(content) max_len: continue writer.writerow([m.get(sender, ), m.get(time, 0), content]) if __name__ __main__: msgs load_exported_chat(exported_messages.json) with open(chat.csv, w, newline, encodingutf-8-sig) as f: export_to_csv(msgs, f)这段脚本有两个容易被忽略的细节。第一CSV 写入用utf-8-sig而不是utf-8否则你用 Excel 打开 csv 时中文会乱码后续人工抽验语料时会非常痛苦。第二max_len512用来过滤超长转发内容这类消息通常是公众号长文、聊天记录截图转文字对训练对话模型没有正向帮助反而会占掉模型的最大长度窗口。如果你导出的字段里 type 不是数字而是字符串把is_text_msg里改成字符串比较就行。字段对应不上是这个环节最常见的卡点不要硬套先打印一条消息看看结构。3. 语料清洗与对话对构造决定机器人“像不像”的一步3.1 清洗规则系统消息、图片语音和超长转发怎么滤掉很多人拿到 csv 就急着训练结果模型学出来的回复大量是“嗯”“好的”“哈哈哈”或者干脆复读系统提示。原因很简单聊天记录里约三分之一是图片消息、语音通话、撤回提示、表情包这些在导出工具里可能没有 content或者 content 是“[图片]”“[语音]”这类占位文本。占位文本一旦进入训练语料模型就会把“[图片]”当成一个高频词来学习生成时动不动来一个“[图片]”看着非常弱智。清洗规则我一般按这个顺序来先按 type 过滤掉非文本消息再过滤掉content为空、长度小于 2 或大于 512 的消息然后用正则去掉残留的 XML 标签和微信特有的占位符最后按发送者名字过滤只保留目标对象。短消息过滤要谨慎。像“嗯”“好”这种回复在真实对话里占比很高如果全删掉语料会失真如果全保留模型会退化成复读机。我的做法是保留但降权长度 1 到 4 个字的回复在构造训练样本时按 30% 的概率采样其他正常样本全部保留。这样既保留了日常口语的节奏感又不会让高频短句把数据分布带偏。这一步直接决定模型生成结果的多样性值得花一晚上调阈值。import random, re def clean_content(content): # 去掉 XML 标签、HTML 实体和微信常见占位符 content re.sub(r[^], , content) content content.replace(lt;, ).replace(gt;, ) content re.sub(r\[(图片|语音|视频|表情|链接|小程序)\], , content) return content.strip() def should_keep(content): if not content or len(content) 2 or len(content) 512: return False # 高频短回复降权保留避免模型复读 if len(content) 4 and random.random() 0.3: return False return True这里有个方向性问题你到底是想要一个“像某个人的聊天机器人”还是想要一个“聊天能力很强的通用机器人”。这两个目标对清洗策略的要求恰好相反。前者要尽量保留目标人的口头禅、错别字、方言惯用表达甚至不要纠正语法后者则要做更激进的正规化。做毕业设计时建议锚定前者因为“像”是可感知、可展示、答辩时能讲出故事的效果而“通用能力强”在个人语料规模下基本做不到。3.2 构建“提问-回复”样本时间窗口、指定说话人与多轮上下文清洗完的消息还是流水账模型训练需要的是“输入-回复”对。最朴素的做法把同一个会话按时间排序取相邻两条消息前一条是输入后一条是回复。但这个做法在群聊里会翻车因为相邻两条消息可能是两个不认识的人各说各话强行配对会让模型学到答非所问。单聊相对安全但也要处理长间隔。我用两个约束来切分样本时间间隔超过 20 分钟就断开不跨段配对群聊场景只关心目标说话人发的消息输入取 ta 发言之前的若干条消息回复取 ta 最新一条发言。这样构造出来的样本才是“在某个上下文里目标人物会说这句话”。import csv, json from datetime import datetime from collections import defaultdict MAX_GAP_MINUTES 20 # 超过 20 分钟不回复视为话题断开 MAX_CONTEXT 3 # 输入最多拼接前 3 条消息 def to_ts(ts_str): # 优先解析 ISO 字符串否则按毫秒时间戳处理 if T in ts_str or - in ts_str: return datetime.fromisoformat(ts_str).timestamp() return int(ts_str) / 1000 def build_samples(csv_path, target_senderNone, max_len128): rows list(csv.DictReader(open(csv_path, encodingutf-8-sig))) rows.sort(keylambda r: to_ts(r[time])) samples [] for i in range(1, len(rows)): prev, cur rows[i - 1], rows[i] if to_ts(cur[time]) - to_ts(prev[time]) MAX_GAP_MINUTES * 60: continue if target_sender and cur[sender] ! target_sender: continue context .join( rows[j][content] for j in range(max(0, i - MAX_CONTEXT), i) ) if context.strip() and cur[content].strip(): samples.append({ input: context[-max_len:], # 截断超长上下文 response: cur[content][:max_len] }) return samples samples build_samples(chat.csv, target_sender小明) with open(train.jsonl, w, encodingutf-8) as f: for s in samples: f.write(json.dumps(s, ensure_asciiFalse) \n) print(samples:, len(samples))MAX_CONTEXT3是综合考虑效果和训练成本的默认值。微信聊天大多是短对话上下文太长会稀释目标回复的权重让模型分不清该模仿谁太短又缺少语境。如果你导出的单聊记录特别密可以把MAX_GAP_MINUTES提到 30如果是工作群那种整天刷屏的建议降到 5宁可少要样本也不要拿噪声样本污染模型。生成 train.jsonl 后抽查 50 条样本看看是否存在“答非所问”的配对人工筛一遍比调参管用。4. 模型选型与模型训练检索式、GPT 微调还是大模型 LoRA4.1 三种模型路线的对比与选型逻辑聊天机器人的实现路线大体有三类选哪条不取决于哪个效果好取决于你的数据量、显卡和答辩预期。检索式方案不训练任何神经网络把语料里的“输入-回复”对做成向量库用户提问时用相似度召回最接近的回复。优点是训练成本几乎为零回复一定来自真实聊天记录不会胡说缺点是只能回答语料里出现过的话换个说法就答不上来。如果你的聊天记录只有几千条这是性价比最高的保底方案。第二类是中小型 GPT 模型微调这也是标题里“模型训练”四个字真正对应的核心工作。用 HuggingFace 上的中文预训练 GPT2 权重做续写式微调把“上下文 回复”拼成一段文本让模型学会接着写。参数量在 100M 左右单卡 6 到 8GB 显存就能跑效果上限比检索式高一个量级。前提是训练样本最好有一万对以上低于这个量级模型会严重依赖预训练先验学不出你想要的个人风格。第三类是大模型 LoRA 微调比如 7B 参数级别的开源模型挂 LoRA 适配器。效果上限最高但需要 16GB 以上显存训练时间长而且对个人风格的模仿并不一定比中小模型好多少——大模型太“聪明”会不自觉地往通用表达上拽。我的建议很直接毕业设计选第二类课程设计选第一类加第二类的组合第三类只做对比实验写进论文不要作为主方案。方案训练成本显存要求生成风格最低数据量毕设适配度检索式向量召回低不需要复读原话稳定几千条可用适合时间紧GPT2 中文微调中6~8GB能生成新句1 万对以上推荐主线大模型 LoRA高16GB 以上上限高但风格稀释2 万对以上只做对比4.2 用 HuggingFace Trainer 微调一个中文 GPT 模型最小可运行训练脚本微调脚本用 HuggingFace 的 Trainer核心代码可以压缩到四十行以内。模型选uer/gpt2-chinese-cluecorpussmall这是社区常用的中文 GPT2 预训练权重兼容 AutoModelForCausalLM。如果你所在环境无法在线下载模型就先把权重下载到本地把下面的模型名换成本地目录路径其余代码不变。# train_gpt.py import json from datasets import Dataset from transformers import ( AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments ) def load_samples(path): rows [json.loads(line) for line in open(path, encodingutf-8)] return Dataset.from_list(rows) def tokenize(batch, tokenizer, max_len128): # 把输入和回复拼成一段文本用 [SEP] 分隔让模型做续写 src [i [SEP] r for i, r in zip(batch[input], batch[response])] enc tokenizer(src, truncationTrue, max_lengthmax_len, paddingmax_length) enc[labels] enc[input_ids].copy() return enc ds load_samples(train.jsonl).train_test_split(test_size0.05, seed42) tokenizer AutoTokenizer.from_pretrained(uer/gpt2-chinese-cluecorpussmall) tokenizer.pad_token tokenizer.eos_token # GPT2 没有默认 pad 位用 eos 代替 model AutoModelForCausalLM.from_pretrained(uer/gpt2-chinese-cluecorpussmall) train_ds ds[train].map(lambda b: tokenize(b, tokenizer), batchedTrue) eval_ds ds[test].map(lambda b: tokenize(b, tokenizer), batchedTrue) args TrainingArguments( output_dirchatbot_model, num_train_epochs3, # 语料少就 3 轮超过 5 轮容易过拟合 per_device_train_batch_size8, # 显存不足就降到 4 或 2 per_device_eval_batch_size8, gradient_accumulation_steps2, # 等效 batch_size 8 * 2 16 learning_rate5e-5, warmup_ratio0.1, logging_steps50, save_strategyepoch, eval_strategyepoch, fp16True, # 半精度训练加快速度并减少显存占用 ) Trainer(modelmodel, argsargs, train_datasettrain_ds, eval_dataseteval_ds).train()这段脚本里几个参数值得说清楚。num_train_epochs别贪多微信语料本身是高度重复的口语训练 3 轮左右 loss 就降到底了继续训练模型会把语料里的固定句式背下来生成结果全是原句搬运真要提升效果优先加数据而不是加轮数。batch_size8在 8GB 显存下配合max_len128是安全值如果你只有 6GB 显存先把 batch 降到 4再开gradient_accumulation_steps4不要硬扛。learning_rate5e-5是对话微调的常用起点loss 震荡就降到 3e-5loss 降不动就提到 8e-5这是微调超参里最值得花时间调的一个。4.3 显存不够怎么办免费 GPU 额度和四组关键训练参数“显存不够”是 QQ 机器人、微信机器人这类中文对话项目里最常见的劝退原因但 100M 参数的 GPT2 真的不算大模型。现在常见的免费 Notebook 平台都会送单卡 16GB 左右的 GPU 时长跑这个规模的微调完全够用关键是要做好三件事把 train.jsonl 先上传到平台而不是训练时再下载训练脚本里加上save_strategyepoch每轮都存 checkpoint防止会话超时白白跑掉一整晚单次训练时长控制在平台限额以内epoch 间断了就从 checkpoint 续训。微调不是炼丹不需要追求一张顶级卡稳定跑完比峰值性能重要得多。显存优化的四组参数按优先级调先降per_device_train_batch_size这个最直接再开gradient_accumulation_steps补偿 batch 下降带来的收敛变慢然后开fp16几乎无损最后才考虑把max_len从 128 降到 96。反过来如果你显存很宽裕优先升max_len而不是 batch因为微信对话的上下文长度对效果的影响比单步更新次数更明显。5. 避坑清单微信语料训练机器人最容易翻车的 5 个地方5.1 数据侧的坑XML 标签、高频短回复、群聊错配现象一训练完生成的回复里带着lt;msggt;、lt;siggt;这类标签串模型一本正经地输出 XML。原因是微信文本消息导出后经常自带 XML 壳清洗脚本里正则没覆盖嵌套标签或者先做了配对再做清洗导致脏文本已经进了训练集。解决清洗必须在配对之前用更狠的正则把所有尖括号内容全部剥掉清洗完打印 10 条样本肉眼确认没有标签残留再进训练。现象二模型训练后 loss 正常下降但机器人只会回“嗯”“哈哈”“好的”三句话不离这些高频短句。原因是短回复在微信语料里占比可能超过一半模型学到的是概率分布而不是对话意图。解决构造样本时对长度小于等于 4 的回复做降权随机采样把占比压到 30% 以下同时把长度小于 2 的回复直接丢弃这类数据就算完美生成出来也没有展示价值。现象三群聊样本里大量“答非所问”输入是 A 在问问题输出是 B 在跟 C 聊天。原因是只看时间相邻就配对没考虑说话人切换。解决指定target_sender只把目标人物的回复当作训练标签输入上下文里可以包含其他人的发言但回复必须是目标人物说的。如果你想把多个人的风格都学进来就分别按人构建两份训练集训两个模型不要混在一个模型里。5.2 训练与交付侧的坑显存 OOM、微信机器人封号风险现象四训练跑到第 500 步时直接 OOM日志里报 CUDA out of memory。原因通常是 max_len 和 batch_size 同时开大Transformer 的显存占用随序列长度线性增长但这只是压垮显存的最后一根稻草前面已经埋了雷。解决检查是不是数据集里混进了超长文本导致动态 padding 时某条异常长。给tokenize里的max_len128加truncationTrue只解决截断问题你还需要在清洗阶段把超过 512 字的文本提前过滤掉双保险。显存不足时按上一节优先级调参先 batch 再长度。现象五模型训练完成后想直接把机器人挂到微信号上实现“自动回复”结果账号被限制登录严重时被封号。这跟 QQ 聊天机器人有开放协议的情况不一样微信个人号没有官方机器人接口任何用第三方脚本控制个人号收发的行为都违反平台规则风险是真实存在的不是网上说的“玄学”。解决展示方式避开“控制微信号”这个方案把训练好的模型部署成命令行对话或 Web 接口演示时把聊天窗口投屏给人看同样能讲清楚项目价值。涉及真实聊天记录时把联系人备注名、手机号、微信号全部脱敏只保留文本内容这既是对你朋友的尊重也是答辩时保护自己的必要动作。6. 把模型跑起来命令行对话、效果验证与毕设文档组织6.1 命令行对话脚本与三个采样参数训练完成后的成果需要一个能当场演示的入口。命令行对话是最稳妥的展示形态不依赖网络不涉及微信接口。加载训练好的模型目录写一个while True循环即可。from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer AutoTokenizer.from_pretrained(chatbot_model) model AutoModelForCausalLM.from_pretrained(chatbot_model) model.eval() hist while True: q input(你: ).strip() if not q: continue hist (hist q [SEP])[-256:] # 只保留最近内容防止上下文无限膨胀 inputs tokenizer(hist, return_tensorspt) out model.generate( **inputs, max_new_tokens32, # 短回复更安全16~32 字够用 do_sampleTrue, # 采样生成而不是贪心搜索 temperature0.8, # 太高胡说太低复读0.7~0.9 之间试 top_p0.9, repetition_penalty1.05, # 抑制复读 pad_token_idtokenizer.eos_token_id, ) reply tokenizer.decode(out[0][inputs[input_ids].shape[1]:], skip_special_tokensTrue) print(机器人:, reply) hist hist reply三个采样参数里temperature对风格影响最大。对着同一个问题多试几组值0.7 偏保守、0.9 偏发散这是微调后最该手动调的体验参数。repetition_penalty对付复读很有效但调太高会让句子变得生硬1.05 起步。6.2 效果验证和开发文档怎么组织效果验证分两层。自动指标用验证集困惑度微调后相比预训练权重困惑度明显下降就说明学到了语料分布但困惑度不能说明“像不像”所以必须补人工评估准备 20 到 30 个真实问题按“通顺、像目标人物、答非所问”三个维度打分。Bleu 这类指标在开放对话上基本没有参考价值答辩时主动讲清楚这一点反而加分。开发文档按五个文件组织需求分析、数据集构建说明、模型选型对比、训练配置与日志、测试用例与演示脚本。训练日志里截图保留 loss 下降曲线、显存占用、最终困惑度这些是答辩时最有力的过程证据。我自己的习惯是把清洗脚本、配对脚本、训练脚本拆成三个独立文件而不是一个大而全的 main.py。因为数据清洗和模型训练的参数是分开调的混在一起每次调参都要重新跑数据浪费时间。这个习惯帮我避开了很多次“改了训练参数结果发现数据也要重做”的尴尬。这个方向值不值得做我的判断是值得——它完整覆盖了 NLP 项目从数据到部署的所有环节而且数据是独一份的做出来的机器人是真正“专属”的。希望帮到你。本文还有配套的精品资源点击获取