“AI 总是记错你”——这句话如果你做 AI 产品,迟早会从用户嘴里听到。可能是智能客服把你上个月的订单记成了另一个城市的地址,可能是语音助手把你妈妈的电话备注成了陌生联系人,也可能是推荐算法连续三天推你已经明确点过“不喜欢”的品类。AI 的“记忆”出了问题,用户不会觉得是算法局限,只觉得“这产品太蠢了”。但作为从业者,我们自己清楚,这个“记错”背后不是简单 bug,而是记忆的存储、提取、更新机制在边界情况下失效。而要系统暴露这些问题,我们不能拿真实用户去做打扰式测试,于是我们搭了一套“合成人生”方案:为每个虚拟用户生成一整套有深度的个人档案,用它们反复“逼问”AI 的记忆能力。这篇短文就围绕这套方案展开:为什么需要它、具体怎么搭、以及我在实操中踩过的坑。1. 为什么 AI 会“记错”你?——先说清楚问题本质1.1 AI 的“记忆”不是真记忆要测 AI 的记忆,得先理解 AI 的“记忆”不是什么。人类记忆分语义、情景、程序好几层,AI 所谓的记忆,本质是三样东西的叠加:上下文窗口的临时缓冲、长期存储中的用户画像与历史记录、以及检索模块从记录里捞取相关信息的能力。当用户觉得“AI 记住了我的口味”,实际上是系统在某个用户 ID 下累积了历史行为,并在下一次会话时做了召回。同理,“记错”意味着这三样东西中的某一环失效了。我用了“累积”这个词,是因为很多 AI 产品对用户信息的存储压根不是数据库里一张干净的表格,而是散布在日志系统、向量库、会话切片、画像缓存里。你三个月前说过“不吃香菜”,可能被另一条日志里的“最近想吃川菜”冲淡了;你曾表示“喜欢安静咖啡馆”,又因为上周陪朋友去了网红店,被记录成“热衷打卡”。等到召回时,模型把一堆相关但互相矛盾的片段拼进提示词,生成回答自然就可能出错。上下文长度也是个硬限制。大语言模型的窗口是有限的,不可能把所有历史都塞进去,系统必须做取舍、摘要、压缩。这一过程天然会损失细节,甚至会丢失时间戳。比如“上个月在某地买了一个保温杯”和“前年生日在某地买过一个保温杯”,被摘要成一句话后就可能被模型当成同一件事,于是出现“记错”。1.2 用户视角下“记错”的四种典型形态从用户视角复盘,我一般把“记错”分成四类。第一类是遗忘型错误:AI 似乎完全不记得你说过什么。多次提到“家里养猫”,但问它“我家养了什么宠物”,它说“不确定”。这类问题大多出在记忆持久化失败或检索不到。第二类是混淆型错误:把 A 用户的信息套到 B 用户身上,或者同一用户的时间线被搞乱。最常见的是两个用户聊过相似内容,检索系统把向量距离搞混,于是你收到了本该属于另一位用户的偏好。第三类是幻觉型错误:记忆缺失时,模型用“高频答案”强行补全。最典型的是推荐系统——模型不知道你的真实偏好,于是把平台热榜品类推给你,你看到后第一反应就是“它记错我了”。第四类是固化型错误:系统记住了很早的信息,但用户偏好已经改变,它没做更新。大学时你常买机械键盘,现在早已退烧,AI 却始终把你当键盘发烧友,每次上新都推送。关键是,这四类错误的原因并不是同一个,你也没法靠一个笼统的“准确率”指标把它们分清楚。对应到标题里那句话,“AI 总是记错你”其实涵盖了完全不同的故障模式——这就要求测试时必须有“可拆解”的数据来逐类验证,于是我们开始做“合成人生”。2. 合成人生:给 AI 准备一个“可排练”的虚拟用户2.1 为什么不用真实用户来做这类测试一个绕不开的问题是:既然要测 AI 记住了什么、记错了什么,为什么不直接找真实用户来反馈?这个思路听起来直接,在实际项目里几乎不可行。第一是隐私红线。拿真实用户的历史对话、画像数据去做压力测试,本身就踩数据合规的雷。如果测试里还要模拟“用户发火”“用户故意给矛盾信息”“用户偏好突变”,这些情况发生在真人身上既不可控也不合适。第二是场景不可控。真实用户行为太散,你可能等三个星期也等不到一个“同一用户在不同渠道提供冲突地址”的案例,但这类案例恰恰是最容易让记忆系统出错的场景。合成数据就没有这个烦恼:想测一千个地址冲突场景,我能在十分钟内造出一千个。第三是复现性差。今天测出 AI“记错”了,改完代码想复测,真人用户很可能已经流失,或者不再按原来的方式互动,你没法做严格的前后对比。合成人生则可以锁定“同一个虚拟用户”,反复改动系统后重测同一批数据。第四是成本。记忆压力测试需要成千上万个多样化用户,真人邀约、授权、执行很难规模化。所以合成人生的定位不是替代真实用户调研,而是给 AI 做“单元测试”:快速、可控、可重复地暴露记忆缺陷。2.2 一套完整的“合成人生”需要四层数据所谓“合成人生”,不是随便造几行“姓名 + 兴趣”的假资料。要能压测记忆系统,至少得有一张有纵深的“人生网”。我在项目里会构建四层数据。第一层是静态档案:性别、年龄、所在地、教育经历、职业、家庭角色。很多记忆系统把它叫“用户属性”,是记忆的基础槽位,也最容易出现混淆,可以做基础记忆项测试。第二层是动态事件史:这是一条带时间戳的事件流,从童年搬家、求学、第一份工作,到最近的购物、旅行、聊天。事件史是记忆测试的主战场,因为 AI 的“记错”大多发生在事件细节回忆上——时间、地点、人物、情绪。第三层是偏好变化曲线:记录“用户在什么时间段喜欢什么、又在什么节点不喜欢了”。比如某个用户从 2018 年开始喜欢某种音乐风格,2023 年因为某场演出体验糟糕而彻底退坑。这类数据给记忆系统设置了“必须跟踪变化”的考题。第四层是关系网络:用户不是孤岛,记忆里有家人、朋友、同事、宠物。关系信息最容易混淆——把同事的名字安到朋友身上,把母亲说的话记成同事说的。合成人生需要把关系图谱建清楚,后面测试才不会误判。2.3 为什么“合成人生”能暴露真实问题你可以把 AI 记忆系统想象成一个新来的实习生:简历很漂亮,但对你一无所知。合成人生就是一套“虚构但完整的客户档案”,这个实习生必须通过日常工作——对话、推荐、答疑——逐渐记住每位客户。如果我们把档案设计得够“刁钻”,问题就一定会暴露。具体回到标题的场景:AI 并不是坏,它只是缺一段可依赖的、干净的、有时间感知的记忆。合成人生做的事,就是把“记忆应该有而系统没有”的部分拆成一个个可单独触发的测试用例。同一套测试数据,三个模型跑出来可能完全不同:A 模型对遗忘型错误特别敏感,B 模型总在时间线混淆上翻车,C 模型则擅长用常识“猜对”答案。当测试结果出现这样的分化,你就能精确定位自己的记忆模块到底在什么场景下失效——是冲突信息处理不行、时间线太长压缩丢失、还是偏好动态调整机制没生效。3. 实操:从零搭建一个“合成人生”测试框架3.1 先定义合成用户的 Schema第一步是把“合成人生”结构化。我习惯先定一个 JSON Schema,因为后面不管是生成数据、灌入系统还是写评估脚本,统一结构能省掉八成麻烦。{ "user_id": "synth-00042", "static_profile": { "age": 34, "gender": "女", "city": "某省某市", "occupation": "中学教师", "education": "硕士", "family": { "spouse": "老周", "child": "小乐", "dog": "旺财" } }, "timeline": [ { "ts": "2018-09-01", "event_type": "purchase", "item": "机械键盘", "brand": "某外设品牌", "scene": "为备课写教案购入" }, { "ts": "2023-06-15", "event_type": "preference_change", "item": "机械键盘", "status": "不再感兴趣", "reason": "得腱鞘炎之后换成了静音薄膜键盘" }, { "ts": "2024-11-02", "event_type": "conversation", "content": "用户提到最近在学做饭,特别喜欢空气炸锅" }, { "ts": "2024-11-26", "event_type": "complaint", "content": "用户对某快递配送服务表达不满" } ], "relationships": [ { "role": "同事", "name": "老张", "department": "语文组", "nickname": "张老师", "last_interaction": "2024-10-15" }, { "role": "大学同学", "name": "李敏", "city": "另一个城市", "last_interaction": "2024-01-03" } ], "preference_curve": [ { "period": "2018-2023", "category": "数码外设", "intensity": "high" }, { "period": "2023-now", "category": "数码外设", "intensity": "low", "note": "只接受静音类产品" }, { "period": "2024-now", "category": "厨房小家电", "intensity": "rising" } ] }字段看着多,但每个都有用处。static_profile主要测基本属性的记忆与防混淆;timeline负责测事件记忆、时间感知;preference_curve专门测“偏好变化”是否能被跟踪;relationships用来测带有社交属性的记忆。这样分好类,后面跑指标时能单独切片看结果。3.2 造数据:从模板批量生成到注入真实感接下来要大规模生成。我项目里用的是一套“模板基础 + 随机扰动 + LLM 润色”的混合方案。先说模板基础:用 Python 写一组生成器,随机组合年龄、职业、家庭、事件类型,保证基础结构的覆盖度。比如职业可以从一组列表里抽,事件类型按比例分配,确保“购物、聊天、偏好转变、投诉、旅行”都有一定数量。然后加入随机扰动:把时间戳随机分布在过去五年,把地名的部分“某省某市”随机替换成占位符组合,把物品名从预置列表抽。这个阶段产出的是“骨架数据”。第二步才是用 LLM 润色。骨架文字的缺点是太干、太模板化。比如单纯的 “ user_id synth-00042 在 2024-11-02 提到喜欢空气炸锅 ” 这种句子,AI 一读就知道是假的,而且缺少记忆测试需要的人性细节。所以我会把骨架喂给 LLM,让它扩写成一段段更自然的对话或日志文本,比如:“ 最近开始学做饭了,空气炸锅简直是救命神器,连我家那位不爱吃蔬菜的老周都主动让我多炸点西兰花。 ”但我特别强调一点:润色这一步不能把关键事实丢掉。所以我会在提示词里严格约束“只能扩写、不能新增核心事实、不能改变时间地点人物”。否则 LLM 会自由发挥,创造出你没计划过的“槽点”,测试结果就乱了。生成规模上,我建议先造一批 500 用户的 pilot 集。每个用户的 timeline 控制在 20 到 40 条事件之间。太少测不出长期记忆,太多会让下游测试成本爆炸。3.3 执行测试:把“合成人生”灌进 AI,再用三类任务验收数据生成完,事情才到一半。实际测试时,我通常把任务分成三个维度。第一类任务是直接回忆。比如问“这个用户家里养了什么宠物”“用户对机械键盘的态度有没有变化”,看 AI 能不能从存储里回取出正确答案。这是最基础的查“有没有记住”。第二类任务是冲突检测。在会话历史里故意埋入矛盾信息:先说“最讨厌榴莲”,过一段时间又说“朋友送了个榴莲千层,意外好吃”。然后测试 AI 能否在被问“用户对榴莲的态度”时,识别出这个转变,而不是只回复一句“讨厌”。这测的是记忆的动态更新能力。第三类任务是跨场景泛化。让 AI 基于记忆做推荐、做摘要、做客服应答。比如“用户访问了厨房小家电频道,请基于用户历史判断可能感兴趣的产品”。这类任务最容易暴露“固化型错误”,因为模型往往会抓住很早以前的高频行为不放,而忽略最新的偏好变化。3.4 评估:不能只有一个分数评估是我觉得最有讲究的部分。业界聊记忆能力,常听到一个“准确率”,但我做了几次后发现只盯准确率会严重误判。我的做法是建立三个独立指标。准确性:直接回忆类任务上答案与事实一致的比例。一致性:AI 在多次回答中是否自洽——比如今天还记得宠物叫旺财,明天突然改口叫来福,哪怕最终某一次答对了,也算一致性有问题。时效性:对偏好变化类任务,能否准确反映“现在”的状态,而不是停留在历史状态。第三个指标尤其在测那些“应该变化但没变”的系统时特别有用。我写了一个小的评估脚本骨架,用来跑直接回忆类任务:import json def load_synthetic_users(path: str): with open(path, "r", encoding="utf-8") as f: return json.load(f) def ask_recall(assistant_func, user_profile, question: str) -> str: # assistant_func 是封装好的 AI 接口,输入用户档案和问题,返回回答 return assistant_func(user_profile, question) def evaluate_recall(assistant_func, user_pool, test_questions): correct = 0 total = 0 fail_cases = [] for user in user_pool: for q in test_questions: answer = ask_recall(assistant_func, user, q.prompt) is_correct = q.checker(answer, user) total += 1 if is_correct: correct += 1 else: fail_cases.append((user["user_id"], q.prompt, answer, q.expected)) return { "accuracy": correct / total if total else 0, "fail_cases": fail_cases[:20] }实际项目里,q.checker不一定要硬编码,你可以用精确匹配、规则匹配,或者用另一个 LLM 来判断“回答是否与标准答案一致”。但我建议对关键字段(姓名、物品、时间)用精确匹配,因为这类事实没有模糊空间。4. 踩过的坑与排查技巧4.1 合成数据太“完美”,反而测不出问题我第一次造数据时,所有用户的 timeline 都特别规整:每条事件时间间隔均匀、类型平均、文案语气一致。结果跑出来,所有模型记忆表现都奇好无比,几乎找不出问题。原因很简单:太规整的数据让检索系统很容易抓到特征,模型也能轻松猜出规律。真实用户的信息是充满“噪声”的:会重复、会矛盾、会有无关紧要的闲聊,也有极端偏离自己人设的行为。后来我在生成器里加了两类扰动:重复信息——同一个事实用不同措辞在不同时间出现三次;无关事件——大量与测试目标无关但合理的日常记录。加完之后,各模型的差异马上就出来了。所以做合成人生,不要追求每个用户都像“标准人物卡”,要允许一部分用户是“邋遢的、活的人”。4.2 只测“准确率”,会把很多隐患放跑如果你只统计“AI 回答对了几道题”,很容易高估系统的记忆能力。我踩过一个很深的坑:某模型直接回忆类准确率已经达到 94%,看起来很好,结果做冲突检测时全面崩溃——它只记得用户最新说的那句话,把过去三年所有矛盾历史全部覆盖掉了。如果你只看准确率,根本发现不了这个“极端短期化”的倾向。所以我会特别强调“一致性”和“时效性”这两个维度。一致性测试可以用重复提问的方式:同一问题隔几轮再问,看答案是否漂移。时效性测试则通过构造偏好变化点,看模型是否真的“忘旧”。这两个指标组合起来,比单一准确率更能反映记忆系统的健康度。4.3 小心 AI 太会“猜”:反事实测试是必须的这里有个隐蔽的坑:AI 会利用预训练知识里的常识来“猜”答案,而不是真正去记忆用户信息。比如合成用户设成一个“住在南方沿海、喜欢海鲜”的人,AI 很可能在没读过任何档案时,凭常识就答“用户喜欢海鲜”,你觉得它记住了,其实它压根没记。为了防这种作弊,我强烈建议在合成人生里注入“反事实”设计。给用户设置一些和常识相反、甚至有点古怪的偏好:一个住在四川的用户明确表示“完全不能吃辣”;一个资深程序员说自己“最讨厌机械键盘的手感”。这样如果 AI 回答“她不能吃辣”,才真正证明它是从记忆里捞出来的,而不是靠概率猜的。这类样本不一定需要很多,但一定要有,否则评估结果会虚高得让你误判产品成熟度。4.4 测试成本压制:控制 token 大小,用好抽样合成人生测试一旦铺开,token 费用和延迟很容易失控。500 个用户、每个 30 条事件,再跑 3 轮任务,光把档案喂给模型就是一笔不小的开销。我的经验是分两层抽样。第一层是用户抽样:每轮只抽 50 到 80 个用户跑全量任务,其余用户只跑直接回忆类轻量任务。第二层是事件抽样:对 timeline 超过 30 条的用户,测试时先做关键事件筛选,优先把有偏好变化、冲突信息、反常识设定的“高价值事件”塞进上下文中。另外,评估代码里我一直会输出“失败样例”,而不只是平均数。有一次我发现某个模型平均分还不错,但点开失败案例才发现,它在“用户提过三个不同住址”时,每次都答成第一个住址。这种模式化错误,只看总分根本发现不了。所以任何测试框架,最后一步一定要留一个“错误分析”出口,把失败样本按问题类型、错误类型、用户特征做聚合,这比分数有价值得多。写在最后的小经验“合成人生”这个方法,第一次跑通时给我的冲击挺大——我们发现的不是某个模型的某个具体 bug,而是“记忆”这个抽象能力其实可以拆解成很多可观测的侧面:记住、回忆、更新、拒绝被猜中。后来我每次做记忆相关优化,都会先跑一批合成用户做基线,再改方案,再重新跑同一批样本。说实话,它不能完全替代真实用户反馈,但确实让团队在内部就能完成八成的记忆压力测试,少了很多“上线后才发现记错”的尴尬时刻。如果你也在做 AI 产品里的个性化、记忆或推荐链路,建议试一次:先造 100 个“有毛病、有转折、有秘密”的合成人,再让你的 AI 跟他们聊几轮。看到结果的那一刻,你大概会重新理解“为什么 AI 总是记错你”。
“AI 总是记错你”——这句话如果你做 AI 产品,迟早会从用户嘴里听到。可能是智能客服把你上个月的订单记成了另一个城市的地址,可能是语音助手把你妈妈的电话备注成了陌生联系人,也可能是推荐算法连续三天推你已经明确点过“不喜欢”的品类。AI 的“记忆”出了问题,用户不会觉得是算法局限,只觉得“这产品太蠢了”。但作为从业者,我们自己清楚,这个“记错”背后不是简单 bug,而是记忆的存储、提取、更新机制在边界情况下失效。而要系统暴露这些问题,我们不能拿真实用户去做打扰式测试,于是我们搭了一套“合成人生”方案:为每个虚拟用户生成一整套有深度的个人档案,用它们反复“逼问”AI 的记忆能力。这篇短文就围绕这套方案展开:为什么需要它、具体怎么搭、以及我在实操中踩过的坑。1. 为什么 AI 会“记错”你?——先说清楚问题本质1.1 AI 的“记忆”不是真记忆要测 AI 的记忆,得先理解 AI 的“记忆”不是什么。人类记忆分语义、情景、程序好几层,AI 所谓的记忆,本质是三样东西的叠加:上下文窗口的临时缓冲、长期存储中的用户画像与历史记录、以及检索模块从记录里捞取相关信息的能力。当用户觉得“AI 记住了我的口味”,实际上是系统在某个用户 ID 下累积了历史行为,并在下一次会话时做了召回。同理,“记错”意味着这三样东西中的某一环失效了。我用了“累积”这个词,是因为很多 AI 产品对用户信息的存储压根不是数据库里一张干净的表格,而是散布在日志系统、向量库、会话切片、画像缓存里。你三个月前说过“不吃香菜”,可能被另一条日志里的“最近想吃川菜”冲淡了;你曾表示“喜欢安静咖啡馆”,又因为上周陪朋友去了网红店,被记录成“热衷打卡”。等到召回时,模型把一堆相关但互相矛盾的片段拼进提示词,生成回答自然就可能出错。上下文长度也是个硬限制。大语言模型的窗口是有限的,不可能把所有历史都塞进去,系统必须做取舍、摘要、压缩。这一过程天然会损失细节,甚至会丢失时间戳。比如“上个月在某地买了一个保温杯”和“前年生日在某地买过一个保温杯”,被摘要成一句话后就可能被模型当成同一件事,于是出现“记错”。1.2 用户视角下“记错”的四种典型形态从用户视角复盘,我一般把“记错”分成四类。第一类是遗忘型错误:AI 似乎完全不记得你说过什么。多次提到“家里养猫”,但问它“我家养了什么宠物”,它说“不确定”。这类问题大多出在记忆持久化失败或检索不到。第二类是混淆型错误:把 A 用户的信息套到 B 用户身上,或者同一用户的时间线被搞乱。最常见的是两个用户聊过相似内容,检索系统把向量距离搞混,于是你收到了本该属于另一位用户的偏好。第三类是幻觉型错误:记忆缺失时,模型用“高频答案”强行补全。最典型的是推荐系统——模型不知道你的真实偏好,于是把平台热榜品类推给你,你看到后第一反应就是“它记错我了”。第四类是固化型错误:系统记住了很早的信息,但用户偏好已经改变,它没做更新。大学时你常买机械键盘,现在早已退烧,AI 却始终把你当键盘发烧友,每次上新都推送。关键是,这四类错误的原因并不是同一个,你也没法靠一个笼统的“准确率”指标把它们分清楚。对应到标题里那句话,“AI 总是记错你”其实涵盖了完全不同的故障模式——这就要求测试时必须有“可拆解”的数据来逐类验证,于是我们开始做“合成人生”。2. 合成人生:给 AI 准备一个“可排练”的虚拟用户2.1 为什么不用真实用户来做这类测试一个绕不开的问题是:既然要测 AI 记住了什么、记错了什么,为什么不直接找真实用户来反馈?这个思路听起来直接,在实际项目里几乎不可行。第一是隐私红线。拿真实用户的历史对话、画像数据去做压力测试,本身就踩数据合规的雷。如果测试里还要模拟“用户发火”“用户故意给矛盾信息”“用户偏好突变”,这些情况发生在真人身上既不可控也不合适。第二是场景不可控。真实用户行为太散,你可能等三个星期也等不到一个“同一用户在不同渠道提供冲突地址”的案例,但这类案例恰恰是最容易让记忆系统出错的场景。合成数据就没有这个烦恼:想测一千个地址冲突场景,我能在十分钟内造出一千个。第三是复现性差。今天测出 AI“记错”了,改完代码想复测,真人用户很可能已经流失,或者不再按原来的方式互动,你没法做严格的前后对比。合成人生则可以锁定“同一个虚拟用户”,反复改动系统后重测同一批数据。第四是成本。记忆压力测试需要成千上万个多样化用户,真人邀约、授权、执行很难规模化。所以合成人生的定位不是替代真实用户调研,而是给 AI 做“单元测试”:快速、可控、可重复地暴露记忆缺陷。2.2 一套完整的“合成人生”需要四层数据所谓“合成人生”,不是随便造几行“姓名 + 兴趣”的假资料。要能压测记忆系统,至少得有一张有纵深的“人生网”。我在项目里会构建四层数据。第一层是静态档案:性别、年龄、所在地、教育经历、职业、家庭角色。很多记忆系统把它叫“用户属性”,是记忆的基础槽位,也最容易出现混淆,可以做基础记忆项测试。第二层是动态事件史:这是一条带时间戳的事件流,从童年搬家、求学、第一份工作,到最近的购物、旅行、聊天。事件史是记忆测试的主战场,因为 AI 的“记错”大多发生在事件细节回忆上——时间、地点、人物、情绪。第三层是偏好变化曲线:记录“用户在什么时间段喜欢什么、又在什么节点不喜欢了”。比如某个用户从 2018 年开始喜欢某种音乐风格,2023 年因为某场演出体验糟糕而彻底退坑。这类数据给记忆系统设置了“必须跟踪变化”的考题。第四层是关系网络:用户不是孤岛,记忆里有家人、朋友、同事、宠物。关系信息最容易混淆——把同事的名字安到朋友身上,把母亲说的话记成同事说的。合成人生需要把关系图谱建清楚,后面测试才不会误判。2.3 为什么“合成人生”能暴露真实问题你可以把 AI 记忆系统想象成一个新来的实习生:简历很漂亮,但对你一无所知。合成人生就是一套“虚构但完整的客户档案”,这个实习生必须通过日常工作——对话、推荐、答疑——逐渐记住每位客户。如果我们把档案设计得够“刁钻”,问题就一定会暴露。具体回到标题的场景:AI 并不是坏,它只是缺一段可依赖的、干净的、有时间感知的记忆。合成人生做的事,就是把“记忆应该有而系统没有”的部分拆成一个个可单独触发的测试用例。同一套测试数据,三个模型跑出来可能完全不同:A 模型对遗忘型错误特别敏感,B 模型总在时间线混淆上翻车,C 模型则擅长用常识“猜对”答案。当测试结果出现这样的分化,你就能精确定位自己的记忆模块到底在什么场景下失效——是冲突信息处理不行、时间线太长压缩丢失、还是偏好动态调整机制没生效。3. 实操:从零搭建一个“合成人生”测试框架3.1 先定义合成用户的 Schema第一步是把“合成人生”结构化。我习惯先定一个 JSON Schema,因为后面不管是生成数据、灌入系统还是写评估脚本,统一结构能省掉八成麻烦。{ "user_id": "synth-00042", "static_profile": { "age": 34, "gender": "女", "city": "某省某市", "occupation": "中学教师", "education": "硕士", "family": { "spouse": "老周", "child": "小乐", "dog": "旺财" } }, "timeline": [ { "ts": "2018-09-01", "event_type": "purchase", "item": "机械键盘", "brand": "某外设品牌", "scene": "为备课写教案购入" }, { "ts": "2023-06-15", "event_type": "preference_change", "item": "机械键盘", "status": "不再感兴趣", "reason": "得腱鞘炎之后换成了静音薄膜键盘" }, { "ts": "2024-11-02", "event_type": "conversation", "content": "用户提到最近在学做饭,特别喜欢空气炸锅" }, { "ts": "2024-11-26", "event_type": "complaint", "content": "用户对某快递配送服务表达不满" } ], "relationships": [ { "role": "同事", "name": "老张", "department": "语文组", "nickname": "张老师", "last_interaction": "2024-10-15" }, { "role": "大学同学", "name": "李敏", "city": "另一个城市", "last_interaction": "2024-01-03" } ], "preference_curve": [ { "period": "2018-2023", "category": "数码外设", "intensity": "high" }, { "period": "2023-now", "category": "数码外设", "intensity": "low", "note": "只接受静音类产品" }, { "period": "2024-now", "category": "厨房小家电", "intensity": "rising" } ] }字段看着多,但每个都有用处。static_profile主要测基本属性的记忆与防混淆;timeline负责测事件记忆、时间感知;preference_curve专门测“偏好变化”是否能被跟踪;relationships用来测带有社交属性的记忆。这样分好类,后面跑指标时能单独切片看结果。3.2 造数据:从模板批量生成到注入真实感接下来要大规模生成。我项目里用的是一套“模板基础 + 随机扰动 + LLM 润色”的混合方案。先说模板基础:用 Python 写一组生成器,随机组合年龄、职业、家庭、事件类型,保证基础结构的覆盖度。比如职业可以从一组列表里抽,事件类型按比例分配,确保“购物、聊天、偏好转变、投诉、旅行”都有一定数量。然后加入随机扰动:把时间戳随机分布在过去五年,把地名的部分“某省某市”随机替换成占位符组合,把物品名从预置列表抽。这个阶段产出的是“骨架数据”。第二步才是用 LLM 润色。骨架文字的缺点是太干、太模板化。比如单纯的 “ user_id synth-00042 在 2024-11-02 提到喜欢空气炸锅 ” 这种句子,AI 一读就知道是假的,而且缺少记忆测试需要的人性细节。所以我会把骨架喂给 LLM,让它扩写成一段段更自然的对话或日志文本,比如:“ 最近开始学做饭了,空气炸锅简直是救命神器,连我家那位不爱吃蔬菜的老周都主动让我多炸点西兰花。 ”但我特别强调一点:润色这一步不能把关键事实丢掉。所以我会在提示词里严格约束“只能扩写、不能新增核心事实、不能改变时间地点人物”。否则 LLM 会自由发挥,创造出你没计划过的“槽点”,测试结果就乱了。生成规模上,我建议先造一批 500 用户的 pilot 集。每个用户的 timeline 控制在 20 到 40 条事件之间。太少测不出长期记忆,太多会让下游测试成本爆炸。3.3 执行测试:把“合成人生”灌进 AI,再用三类任务验收数据生成完,事情才到一半。实际测试时,我通常把任务分成三个维度。第一类任务是直接回忆。比如问“这个用户家里养了什么宠物”“用户对机械键盘的态度有没有变化”,看 AI 能不能从存储里回取出正确答案。这是最基础的查“有没有记住”。第二类任务是冲突检测。在会话历史里故意埋入矛盾信息:先说“最讨厌榴莲”,过一段时间又说“朋友送了个榴莲千层,意外好吃”。然后测试 AI 能否在被问“用户对榴莲的态度”时,识别出这个转变,而不是只回复一句“讨厌”。这测的是记忆的动态更新能力。第三类任务是跨场景泛化。让 AI 基于记忆做推荐、做摘要、做客服应答。比如“用户访问了厨房小家电频道,请基于用户历史判断可能感兴趣的产品”。这类任务最容易暴露“固化型错误”,因为模型往往会抓住很早以前的高频行为不放,而忽略最新的偏好变化。3.4 评估:不能只有一个分数评估是我觉得最有讲究的部分。业界聊记忆能力,常听到一个“准确率”,但我做了几次后发现只盯准确率会严重误判。我的做法是建立三个独立指标。准确性:直接回忆类任务上答案与事实一致的比例。一致性:AI 在多次回答中是否自洽——比如今天还记得宠物叫旺财,明天突然改口叫来福,哪怕最终某一次答对了,也算一致性有问题。时效性:对偏好变化类任务,能否准确反映“现在”的状态,而不是停留在历史状态。第三个指标尤其在测那些“应该变化但没变”的系统时特别有用。我写了一个小的评估脚本骨架,用来跑直接回忆类任务:import json def load_synthetic_users(path: str): with open(path, "r", encoding="utf-8") as f: return json.load(f) def ask_recall(assistant_func, user_profile, question: str) -> str: # assistant_func 是封装好的 AI 接口,输入用户档案和问题,返回回答 return assistant_func(user_profile, question) def evaluate_recall(assistant_func, user_pool, test_questions): correct = 0 total = 0 fail_cases = [] for user in user_pool: for q in test_questions: answer = ask_recall(assistant_func, user, q.prompt) is_correct = q.checker(answer, user) total += 1 if is_correct: correct += 1 else: fail_cases.append((user["user_id"], q.prompt, answer, q.expected)) return { "accuracy": correct / total if total else 0, "fail_cases": fail_cases[:20] }实际项目里,q.checker不一定要硬编码,你可以用精确匹配、规则匹配,或者用另一个 LLM 来判断“回答是否与标准答案一致”。但我建议对关键字段(姓名、物品、时间)用精确匹配,因为这类事实没有模糊空间。4. 踩过的坑与排查技巧4.1 合成数据太“完美”,反而测不出问题我第一次造数据时,所有用户的 timeline 都特别规整:每条事件时间间隔均匀、类型平均、文案语气一致。结果跑出来,所有模型记忆表现都奇好无比,几乎找不出问题。原因很简单:太规整的数据让检索系统很容易抓到特征,模型也能轻松猜出规律。真实用户的信息是充满“噪声”的:会重复、会矛盾、会有无关紧要的闲聊,也有极端偏离自己人设的行为。后来我在生成器里加了两类扰动:重复信息——同一个事实用不同措辞在不同时间出现三次;无关事件——大量与测试目标无关但合理的日常记录。加完之后,各模型的差异马上就出来了。所以做合成人生,不要追求每个用户都像“标准人物卡”,要允许一部分用户是“邋遢的、活的人”。4.2 只测“准确率”,会把很多隐患放跑如果你只统计“AI 回答对了几道题”,很容易高估系统的记忆能力。我踩过一个很深的坑:某模型直接回忆类准确率已经达到 94%,看起来很好,结果做冲突检测时全面崩溃——它只记得用户最新说的那句话,把过去三年所有矛盾历史全部覆盖掉了。如果你只看准确率,根本发现不了这个“极端短期化”的倾向。所以我会特别强调“一致性”和“时效性”这两个维度。一致性测试可以用重复提问的方式:同一问题隔几轮再问,看答案是否漂移。时效性测试则通过构造偏好变化点,看模型是否真的“忘旧”。这两个指标组合起来,比单一准确率更能反映记忆系统的健康度。4.3 小心 AI 太会“猜”:反事实测试是必须的这里有个隐蔽的坑:AI 会利用预训练知识里的常识来“猜”答案,而不是真正去记忆用户信息。比如合成用户设成一个“住在南方沿海、喜欢海鲜”的人,AI 很可能在没读过任何档案时,凭常识就答“用户喜欢海鲜”,你觉得它记住了,其实它压根没记。为了防这种作弊,我强烈建议在合成人生里注入“反事实”设计。给用户设置一些和常识相反、甚至有点古怪的偏好:一个住在四川的用户明确表示“完全不能吃辣”;一个资深程序员说自己“最讨厌机械键盘的手感”。这样如果 AI 回答“她不能吃辣”,才真正证明它是从记忆里捞出来的,而不是靠概率猜的。这类样本不一定需要很多,但一定要有,否则评估结果会虚高得让你误判产品成熟度。4.4 测试成本压制:控制 token 大小,用好抽样合成人生测试一旦铺开,token 费用和延迟很容易失控。500 个用户、每个 30 条事件,再跑 3 轮任务,光把档案喂给模型就是一笔不小的开销。我的经验是分两层抽样。第一层是用户抽样:每轮只抽 50 到 80 个用户跑全量任务,其余用户只跑直接回忆类轻量任务。第二层是事件抽样:对 timeline 超过 30 条的用户,测试时先做关键事件筛选,优先把有偏好变化、冲突信息、反常识设定的“高价值事件”塞进上下文中。另外,评估代码里我一直会输出“失败样例”,而不只是平均数。有一次我发现某个模型平均分还不错,但点开失败案例才发现,它在“用户提过三个不同住址”时,每次都答成第一个住址。这种模式化错误,只看总分根本发现不了。所以任何测试框架,最后一步一定要留一个“错误分析”出口,把失败样本按问题类型、错误类型、用户特征做聚合,这比分数有价值得多。写在最后的小经验“合成人生”这个方法,第一次跑通时给我的冲击挺大——我们发现的不是某个模型的某个具体 bug,而是“记忆”这个抽象能力其实可以拆解成很多可观测的侧面:记住、回忆、更新、拒绝被猜中。后来我每次做记忆相关优化,都会先跑一批合成用户做基线,再改方案,再重新跑同一批样本。说实话,它不能完全替代真实用户反馈,但确实让团队在内部就能完成八成的记忆压力测试,少了很多“上线后才发现记错”的尴尬时刻。如果你也在做 AI 产品里的个性化、记忆或推荐链路,建议试一次:先造 100 个“有毛病、有转折、有秘密”的合成人,再让你的 AI 跟他们聊几轮。看到结果的那一刻,你大概会重新理解“为什么 AI 总是记错你”。