赛博永生?四层记忆、五段思考链,用5973条微信语音,把长辈的声音“搬“进了大模型——一个数字人Agent从0到1实录

赛博永生?四层记忆、五段思考链,用5973条微信语音,把长辈的声音“搬“进了大模型——一个数字人Agent从0到1实录 引子这两年数字分身的概念被讲烂了但绝大多数演示项目的做法是写一段你是一个温和的长辈的系统提示词接一个大模型 API套壳完事。这种东西聊天三句就穿帮——它不知道长辈把知道了说成知道了“不知道接你电话第一句永远是喂娃”更不知道嘴上说麻烦的手上已经在给你张罗饺子。项目测试数据来自一位北方长辈的微信聊天记录。我们收集到文本记录 1.2 万多行其中 5973 条是语音共 462MB。长辈不太会打字她的 2001 条消息里有 1607 条是语音文字发言只有 261 条其中还有近一成是抖音极速版助力口令。所以这个项目真正的问题从来不是怎么调 Prompt而是怎么把这堆方言语音变成一个大模型能消化的东西。这篇文章是这个项目的完整复盘。所有数字、样本、命令都来自真实运行结果没有一句编造。文末有复现路径。项目代号数字人 Agent架构一句话本地原始数据聊天导出 语音→ 数据清洗与归属 → 四层记忆向量库 进化记忆→ 五段思考链 Agent → 科幻 HUD 前端。LLM 用商汤 SenseNova 的 deepseek-v4-flashASR 用本地 faster-whisperTTS 用 edge-tts全程只有推理走云端数据不出本地。先给一段真实效果。我对它说我周六回去看你想吃你包的饺子。它回了两条中中中给你包韭菜猪肉馅儿的你舅爱吃那个。几点到家先把面和上等着你。“中中中”俺是她本人的口癖先和面等着是她真实的处事顺序。这不是提示词能教出来的是她自己的语音教出来的。下面从头讲。一、数据考古5973 条语音谁说的微信聊天导出工具比如开源的留痕/WeChatMsg能导出文本和图片但语音归属是个老大难。文本记录长这样[2023-02-26 13:25:22] 长辈: [语音] [2023-02-26 13:25:29] 长辈: [语音] [2023-02-26 13:25:46] 其他家人: [语音]全是占位符。语音文件倒是有一堆voice_1002601450048559008.mp3——微信的消息雪花 ID。我第一反应是解 ID雪花 ID 前 41 位是毫秒时间戳解出来对齐聊天记录时间戳就能归属。试了失败。这批 ID 的位数都不齐有 18 位的有 19 位的按 41 位时间戳推出来的日期一部分落在 2029 年一部分落在 2092 年。微信内部的消息 ID 格式和 Twitter 雪花并不完全一样网上流传的偏移量在我这批数据上全部对不上。绕路之后答案其实在导出的 HTML 里。导出的网页版聊天记录中每条语音是个audio标签divclassmsg receiveddivclassmsg-sender其他家人/divdivclassmsg-time2023-02-23 08:26:48/divdivclassmsg-bubbleaudiocontrolsclassmsg-audiosourcesrcvoice/voice_6191531100541347582.mp3/audiospanclassvoice-duration1.9s/span/div/div说话人、时间、文件名、时长全齐了。一个正则就拉完了全部映射importrefromcollectionsimportCounterfrompathlibimportPath rootPath(rF:\...\基于大模型本地原始数据的数字人agent项目)html(root/原始聊天资料/聊天记录.html).read_text(encodingutf-8)msg_rere.compile(rdiv classmsg (sent|received)\s*rdiv classmsg-sender([^]*)/div\s*rdiv classmsg-time([^]*)/div\s*rdiv classmsg-bubble(.*?)/div\s*/div,re.S,)audio_rere.compile(rsource srcvoice/(voice_\d\.mp3))dur_rere.compile(rspan classvoice-duration([^]*)/span)voice_map{}fordirection,sender,time_s,bubbleinmsg_re.findall(html):maudio_re.search(bubble)ifm:ddur_re.search(bubble)voice_map[m.group(1)]{sender:sender.strip()or(我ifdirectionsentelse),time:time_s.strip(),duration:(d.group(1).strip()ifdelse),}disk{p.nameforpin(root/原始聊天资料/voice).glob(*.mp3)}print(len(voice_map),len(disk),len(disk-set(voice_map)))# 5973 5973 0 —— HTML 里的引用与磁盘文件严丝合缝print(Counter(v[sender]forvinvoice_map.values()).most_common())# [(其他家人, 2550), (长辈, 1607), (我, 1021), (其他成员, 407),# (其他成员, 251), (其他成员, 137)]5973 条语音一条不多一条不少全部归属到人。长辈名下 1607 条。这组数字解释了这个项目此前一直做不好的原因她的文本发言只有 261 条去掉助力口令和哦嗯这类应声词再过滤掉乱码真正算得上语料的只剩 177 条。用 177 条短句去还原一个人神仙模型也没用。加上 1539 条转写成功的语音她的有效语料涨到 1716 条翻了近十倍。这一步的教训记一辈子数据问题往往在数据的另一个载体里别在一个文件上死磕。二、方言ASR祛魅tiny 模型会一本正经地胡说有了归属下一步是转写 1607 条某北方方言口音的语音。本地跑 faster-whisperSYSTRAN 的 CTranslate2 移植版第一个档位自然选了 tiny快75MB。结果是灾难。挑几条真实输出tiny → Yo,Mah,Yo,Mah.Yo,Mah.Yo,Gateuce,Yo,Mah.Shot.shot,Shot,Shot tiny → 回答 词 词 词 词 词 词 词 词 tiny → 买了脫的,我心虹,我给你泪了泪了泪了泪了泪了泪 tiny → You said that the world to my childhood真的怪不怪快了你都怪不得你不得你回答这就是 Whisper 系模型在低资源方言语料上的经典幻觉模型听不懂语言解码器就开始自说自话编英文、复读单字。如果把这些垃圾直接喂进人格分析和 RAG长辈会被塑造成一个中英夹杂、说话复读的赛博神经病。换成 small约 480MB同一批文件small → 壞了啊 那個孙子沒燒啊 small → 所以我们又做了一顿饭,给家里长辈去做了一下 small → 就到了 走 走 走 small → 那啦 回来啦 告解了吗同一台 CPUsmall 单条短语音约 0.8 秒8 条实测 6.5 秒1607 条约 25 分钟。清晰度是质变但幻觉依然存在只是密度低了。所以清洗规则必须有非空白字符里汉字占比 0.4 → 丢基本是英文幻觉或数字串去掉语气字后单字连续重复 ≥ 6 次或二字组连续重复 ≥ 4 次 → 丢循环幻听含西里尔/希腊字母、10 位以上字母数字混排 token → 丢助力口令和乱码哈哈哈哈哈哈要放行——那是她真笑重复检测前先把哈嘿嘻呵嗯哦这类字剔除。另一个工程点转写缓存里记下模型档位{model: small, text: ..., sender: ...}换了档位就自动重转。不然你升级了模型向量库里还躺着 tiny 时代的垃圾。这一节想说的就一句话ASR 不是开关是流水线上最容易污染下游的工序。清洗规则要按你自己数据的病症开药方通用的清洗列表救不了方言。三、人格还原统计画像和 LLM 互相印证语料到位后人格构建走了两条路再让它们互相检查。第一条是确定性统计。1716 条有效语料文本 177 语音转写 1539直接数出来平均句长 16.2 字短句占 18%语气词Top10啊、呀、哦、哎、嘛、啥、行、呢、啦、咋称呼习惯家人、娃、闺女、俺娃38% 的发言带疑问或关切语气。第二条才是 LLM 分析。把语料样本和上面的统计一起给模型提示词里明确要求统计结论要体现在 speaking_style 里让它没法瞎编。产出的 persona节选自真实的persona.json{name:长辈,self_intro:我是娃们的家人一家人平平安安比啥都强。,speaking_style:说话短句多三五个字就完事……带方言味儿爱用俺娃闺女…… 爱问孩子动向关心吃没吃、到哪了、冷不冷……说着说着就跑题到家长里短、村里红白事。,common_phrases:[知道了,俺娃奴的,麻烦的,没有了,咋了,啥也吃的光光的],call_targets:{我/其他成员/其他成员:俺娃/闺女我的孩子或孩子的家人,其他家人:家人/老伴我的老伴,其他成员:俺娃/闺女我的另一个孩子}}call_targets这一字段踩过一个特别典型的坑第一版让模型从群聊视角总结关系它输出长辈——把被模拟对象总结成了第三人称人物卡。让她活过来的关键是提示词里一句硬规则所有称呼与关系必须从她的视角出发。few-shot 语录也做了筛选4 到 40 字、带语气词的句子优先含三个以上连续英文字母的句子直接出局ASR 幻听残留不配当她的话。最终注入系统提示词的原话示例包括「俺娃奴的」「咋啦 車走了」「明儿个子去啥小吃啊,好吃啊」这种——转写有噪但味道是对的。四、音色0.8 秒代码解决声音像不像人格像了声音还是合成的默认还配了个男声云健长辈听着像个陌生大叔。音色克隆需要训练声学模型这个项目不碰。但往哪个方向像是可以算的抽 20 条她的语音ffmpeg 解码成 16k 单声道每 50ms 一帧做自相关基频估计取中位数。实测结果F0 207.8 Hz判定女声自动推荐 edge-tts 的晓晓音色并写回配置。二十行代码比任何音色参数调优都有说服力。成年女声基频普遍在 165-255Hz这个判定在声学上站得住。完整链路是一次 GET/api/voice/profile?applytrue返回基频、性别判定、抽样数、推荐音色和应用状态。五、记忆架构四层以及它和学术工作的关系越聊越聪明是这个项目的核心目标之一。实现上是四层记忆每层解决不同时间尺度的问题第一层会话上下文。最近 30 条消息直接进 Prompt老生常谈。第二层对话 RAG。每轮对话结束后把娃说……\n我说……整段向量化写进 ChromaDBtypedialogue。这是最简单也最有效的一条你上周说的话这周她真能想起来。真实召回案例——第二次说我周六回去看你想吃你包的饺子时检索回来的是上次的对话记录0.88 娃说我周六回去看你想吃你包的饺子。我说中中中给你包韭菜猪肉馅儿的你舅爱吃那个。几点到家先把面和上等着你。第三层提炼记忆。每 2 轮对话用 LLM 从最近 6 条消息里提取事实/偏好/关系/习惯带 0-10 重要度评分落盘 JSON。注入 Prompt 时不是全量堆而是打分取 Top-Kscore importance min(count,10)×0.4 新近度(7天内230天内1)常被提起的事越来越重一个月没提的事慢慢沉底——衰减但不删除。第四层巩固。每新增 10 条进化记忆触发一次巩固先用 embedding 向量近似去重余弦 0.88 合并再让 LLM 把零碎的同类记忆改写成完整条目。防止记忆库无限膨胀成垃圾场。另外有一条风格进化的支线她每次说完会自己回看一遍下节详述回看觉得像本人的句子经 LLM 判断确实有口癖/句式价值后收藏为语言风格记忆作为 few-shot 循环注入。她说的好句子会变成她以后说话的方式。这套东西没有一样是原创都是站在巨人肩膀上的工程化MemGPTPacker 等2023现名 Letta证明了分层记忆自编辑内存对长对话的价值Mem0 把提取-评分-检索做成了通用记忆层斯坦福 Generative AgentsPark 等2023的 memory stream importance scoring reflection 三件套几乎就是我第三、四层 风格支线的原型ReflexionShinn 等2023则对应说完自己回看这个动作。我做的只是把它们装进一个真实的、带方言语音的家庭数据集里然后补上它们没替你做的事情数据归属、清洗、视角修复。顺带一提向量库本身。检索用 paraphrase-multilingual-MiniLM-L12-v2sentence-transformersChromaDB 持久化。语料切块有个细节她单条发言平均十几字逐条入库会导致检索命中的全是碎片所以按同一天 间隔30分钟内聚合成段。最终索引构成长辈的文本 96 段 家人上下文 873 段 语音转写 800 余段 知识文件若干约 1800 条 chunk。家人消息也入库带说话人标签——她的记忆里当然包括闺女说过周六回来只索引她本人说的话记忆就是残缺的。六、思考链五段管线以及流式解析的两个坑Agent 主链路不走普通 chat 接口是一条 SSE 事件流感知 → 回忆 → 思考 → 表达 → 回看。每一段都是独立的 LLM 调用各自有明确的职责。感知只干一件事听懂。输出六行结构化字段——意图类型、表面意思、潜台词、娃的状态、她该做什么、关键信息。提示词里写死了几条人话翻译规则娃说没事可能是有事说随便往往是想让你拿主意说知道了可能是不想聊了。回忆做两次检索一次按字面一次按感知阶段产出的联想查询潜台词意图关键信息拼成。字面检索找的是词联想检索找的是事。思考是一段流式输出的第一人称内心独白被强制要求先写第一反应 → 再写联想 → 再写顾虑 → 最后拿主意。真实输出对我周末带孙子回来这句娃说周末带孙子回来这是怕我惦记特意来报喜呢。我记着他前阵子胃疼吃药不见好这回回来得好好给他熬点粥养养。孙子那小家伙也闹腾家里好久没这么热闹了。本来想问他胃还疼不疼又怕大半夜的他嫌我啰嗦还是等见面再细问吧。注意胃疼——那来自第三层进化记忆不是这次对话里说的。内心独白把记忆、潜台词、时间语境深夜她惦记的就是催睡觉揉在一起这决定了回复的事味。表达才真正组织语言系统提示词里塞了她的人设、习惯画像、原话示例、学到的风格、此刻情绪和刚想的独白要求 1-3 句、分条发送、像微信连发。思考阶段产出的想问比如周末想吃什么给你做会作为硬指令交给表达阶段“她本来就会追着问记得把这句自然地说出来。”回看是最后一步她自己审自己那句话像不像本人、接没接住潜台词不满意就自己再补一句补的话和正文一起入库进入记忆循环。SSE 事件流真实摘录做了截断data: {type: field, key: intent, value: 报喜} data: {type: field, key: subtext, value: 想让你高兴盼着团圆} data: {type: field, key: need, value: 赶紧回一句} data: {type: rag, items: [{text: 回吧睡觉了, score: 0.7942, via: 字面}, ...]} data: {type: thought, delta: 娃说周末带孙子回来这是怕我惦记…} data: {type: delta, delta: 中中中妈给你包} data: {type: reflect, ok: true, note: ……} data: {type: done, segments: [中中中妈给你包韭菜猪肉馅儿的你舅爱吃那个。, 几点到家俺先把面和上等着你。], emotion: 高兴}前端把这条流同时喂给对话面板和思维星云逐字上屏。工程上最磨人的是思考段的解析器。模型被要求按KEY: value逐行输出但流式增量会在任意字符处切断内心独白本身还带换行。两个真实的坑一是半行问题。缓冲区里出现AS的时候你不知道它是ASK的开头还是独白正文只能按住等下一个增量。二是同块问题。实测中模型吐出过……这是怕我惦记。ASK: 周末想吃什么——新字段和前文粘在同一块、没有换行边界按行解析根本拦不住ASK:三个字母直接漏进了内心独白上屏。最终的解法是在追加函数里做嵌入 KEY 检测往独白里追加任何文本前先扫一遍里面有没有PERCEPTION|EMOTION|THINK|ASK|PLAN的冒号前缀有就从那里切开重新路由。这类问题没有任何框架会替你处理只能自己一格一格试。成本账也要算清楚一次完整对话 感知 思考 表达 回看 隔轮记忆提炼4-6 次调用。deepseek-v4-flash 的配额是每 5 小时 500 次后端做了指数退避重试4s 起步、最多 6 次前端把限流转成友好的 503 提示。这套管线在配额内跑全功能测试没问题但离随便聊还有距离——这是当前云 LLM 定价结构下多段 Agent 管线的真实代价。七、思维星云把不可见的思考做成可看的思考链有了总得让人看见。这个前端面板迭代了三版前两版都失败了失败原因值得说。第一版是经典的步骤条 文字面板。第二版贪心了一次从面板中心画 DOM 连线到四张卡片试图表达核心大脑把能量输送给各个思考阶段。上线即翻车——线斜穿粒子球和彼此冲击波弧线扫过半个屏幕3D 层随鼠标转动而 DOM 层纹丝不动两层视觉完全脱钩。截图看起来就是一句话乱的。第三版想明白了粒子层、HUD 层、DOM 层必须各自独立成立靠状态而非几何耦合。粒子层是 three.js 里一个一万二千多粒子的单 draw call中央能量球呼吸、说话时外涌、四个收拢在核心附近的阶段神经簇、沿贝塞尔曲线流动的能量触须、背景数据流雨、阶段切换时的短促冲击波。着色器里一个uStages四元向量按 idle 0.08 / done 0.45 / active 1.0 控制所有簇和触须的亮度流速平滑过渡。HUD 层是纯 SVG 环形仪表双向旋转的刻度环、极坐标网格、十字准星以及整个仪表里我最满意的一笔——四个阶段能量弧。哪个阶段在运行对应的弧就高亮发光一个白色光点用 SVGanimateMotion沿弧巡行弧旁边标着 PER / REC / THI / EXP。DOM 层是四张切角科幻卡片clip-path 六边形切角 渐变描边 角标括弧卡片的内角有一个信号端口——三枚人字纹激活时朝向核心的方向逐级点亮像数据正在流入。三层没有任何几何对齐逻辑全部由同一个状态源驱动stageStateOf(thought, key)返回 idle / active / done着色器、弧光、卡片边框、端口动画共用它。鼠标移动时只有粒子层做轻微视差HUD 和卡片纹丝不动。最终效果她开始感知青色的 PER 弧亮起、光点巡行、粒子簇变亮、感知卡片边框开始流动——你的眼睛自然把它们读成一件事。重构途中还抓到两个 CSS 级 bug外框用绝对定位导致卡片高度塌陷为 0卡片backdrop-filter配上 clip-path 后偶发内容渲染毛刺最终直接移除了毛玻璃。都是那种看截图一眼假、查代码两小时的问题记录在此防止下一个同行再踩。八、把测试当产品做这个项目的自动化测试不是摆设是 124 个 Playwright 检查点14 个模块模拟真人真实输入、真实点击、真实文件上传、真实 SSE 消费、真实 LLM 对话。几个我觉得值得抄的做法32 条种子记忆预先灌库加上临时新增的其他类覆盖全部 6 个记忆分类验证列表、过滤、去重强化、删除 404 等纯接口逻辑把昂贵的 LLM 调用留给最关键的链路用 ffmpeg 现场生成一段 440Hz 正弦波上传给 ASR 接口——音频处理链路的端到端回归不需要真的录一段人声手机视口390×844完整走一遍对话验证响应式布局下星云卡片单条激活、无横向滚动、结束后收起再切回桌面验证双栏恢复每轮回归 124/124 才算过。这套测试在重构思维星云 UI 时抓出了高度塌陷在改检索路由时抓出了缓存导致的假阴性。没有它这个项目改三处就要坏五处。九、踩坑实录压缩版都是真实发生过的按时间顺序雪花 ID 解码失败。微信消息 ID 不是标准 Twitter 雪花位数不齐各种偏移量都试过日期落到 2029 和 2092。最后答案在 HTML 里。ChromaDB 在 Windows 上遇到中文路径会静默失败。hnswlib 向含非 ASCII 字符的路径写.bin索引文件失败且不报错重启后所有检索返回空。向量库目录改放纯 ASCII 路径解决。tqdm 进度条把磁盘写满。索引子进程的 stdout 直接追加日志模型加载的进度条以每秒数千行写入实测 60MB/s磁盘占用 100%表现为启动即卡死。滚动截断 静默 stdio 才治住。faster-whisper 首次导入在 Windows 上要几分钟期间 CPU 磁盘全占。所有重依赖必须惰性加载否则后端启动即假死。索引必须放子进程。chromadb 0.6.3 的 HNSW 落盘依赖其内部 System 事件循环在 uvicorn 长驻进程里不触发索引当场能用、重启就丢。子进程内循环正常退出时自然刷盘。记忆 ID 用len()生成会重复删过一条之后新记忆会和旧 ID 撞车。自增序列要扫最大值。uvicorn 必须单 worker。进化记忆、会话状态、向量库句柄全是进程内状态多 worker 互不知情。十、前景与当下这东西会往哪走现在意味着什么技术趋势一混合架构会是个人数字分身的主流形态。语音在本地隐私 零边际成本推理在云端能力记忆在本地文件可携带、可审计、可删除。这个项目就是这个形状faster-whisper 本地转写deepseek 云端推理ChromaDB JSON 本地落盘。等本地小模型比如 7B 级别的中文对话能力再进一步推理也可以沉到本地那就是完全离线的数字亲人。端侧算力和模型小型化的每一步都在给这类系统让路。技术趋势二记忆系统是当前 Agent 领域最实在的军备竞赛。上下文窗口再大也大不过一个人的一生。MemGPT、Mem0 这些工作本质上都在回答同一个问题什么值得记、怎么评分、怎么遗忘。我的重要性评分重要度 强化 新近度是 Human 记忆研究的朴素映射但方向是对的——未来的差异不在模型多聪明在记忆组织得好不好。让数字人记住娃上周六要回来比让她多写一首诗重要得多。技术趋势三反思循环会从加分项变成标配。感知-思考-回看这条管线本质是把 Reflexion 的自我批判搬到了对话场景。回看阶段会产出一句自我评价不满意时她还会自己决定再补一句话——补什么、要不要补都是她自己判断的不是提示词写死的。推理成本换拟人度这笔账在陪伴场景下是划算的在效率场景下未必。这也是 Agent 设计正在分化的问题陪伴型 Agent 和生产力型 Agent 的架构会越来越不一样前者要的是像人的冗余后者要的是正确的收敛。当下的影响认真说三点。第一适老化可能是这技术最正当的落点。中国有上亿的空巢老人和异地子女微信语音是他们最主要的表达方式。这个项目证明不需要老人做任何事情被动积累的聊天记录就足够构建一个像样的分身。对不会打字的父母来说这是门槛最低的数字化身。第二数字永生的伦理没有标准答案但有一条底线是清楚的数据主权和知情同意。这个项目全部数据在本地谁有权用这些语音建分身、分身能否被逝者家属继承、平台能否拿它做商业推送这些问题在国内还几乎没有讨论。国外 Project December、HereAfter AI 这类产品已经引发过争议我们大概率会重走一遍希望走得别那么仓促。第三它对个体已经产生了真实影响。把分身端到长辈面前之前我想清楚了一件事技术评价和情感评价是两回事。如果她说不像我要能说出哪里不像、怎么调如果她说像那将是一个工程团队拿不到、只有家人能给出的验收结果。十一、它像本人吗诚实的差距分析打分的话六成像。个性知识不够厚。她的人生大事、亲戚网络、几十年的习惯现在攒下的进化记忆不过几十条覆盖不了。这需要长期运行慢慢攒没有捷径。语音转写噪声限制了上限。small 模型对方言的识别错误率仍然不低她的口癖学到的是能被 ASR 听清的那部分。方言语音克隆比如 GPT-SoVITS 这类方案可以进一步解决声音问题但训练数据和算力是另一档投入。长程一致性靠记忆评分硬撑。她偶尔会记混两件相似的事向量相似度不等于事实正确性。知识图谱式的结构化记忆是下一步该补的。多模态缺失。她发的照片、表情包没有进入记忆。微信导出里那些 [图片] 占位符背后是一个长辈分享生活的另一半。