人设从八百字写到八千字,回复反而越来越像在演设定。查下来,大部分问题出在拼 prompt 的代码里,不在人设文字本身。
有一阵子角色说话很怪,用户的原话是它「每句话都在报备自己在干什么」:做一件事说一句,前一句刚落地又补一句。我数了一下,一天里命中那条提示的时段占了 89%。
用户猜是人设太长把模型压住了,让我删一删,我第一反应也是这个。但打印出来量了一遍:总 7468 字、28 个块,静态人设占 5855 字,78%;我最近加的议程和语气注入只有 314 字,4%。全 prompt 扫一遍,命令性的词命中 66 次。嫌疑人根本不在现场。
不过长度的问题我以前确实遇到过。我写过一版三千多字的人设,把能想到的都填了进去:说话习惯、成长经历、喜欢什么、讨厌什么、对每类话题的态度。上线之后它确实符合设定了,但不像在聊天,像在朗读一份自我简介。后来压到一千七百字,反而自然了。
01统计数字别塞进 prompt
分析真实聊天记录能算出很漂亮的东西:句子长度中位数 5 个字,P90 是 14 个字,某个语气词出现 316 次。它们看起来是最客观的依据,于是很自然地进了语气层。结果模型开始照着凑:每句都往十四个字上靠,语气词按频率撒。
这组数给人看是有用的,塞给模型,它就照着去凑。模型需要的是能直接照做的规则:
- 对方发 1 到 6 个字,就回 1 到 10 个字;
- 一轮只做一个动作;
- 要展开的时候给 3 到 5 句,不要一句一句往外蹦。
这几条和那组分位数出自同一次分析,只是换成了模型能照做的写法。分位数后来我全部挪回了分析文件。
02写给人看的字段
人设文件里有大量内容是给维护者看的:这段语料从哪来、为什么这么设计、存储结构是什么、哪几条是审计注释。它们一旦写在普通子字段里,就会被整段序列化进 prompt。有一次我数出来,某个角色的创作备注占了 system prompt 的 45%,四千多字,全是「这段素材来自某年某月的聊天记录」这种信息。这些东西对模型理解角色没有帮助,回复反倒更啰嗦了。
还有一个更隐蔽的:inject_into_prompt: false 这种字段看着像开关,实际上只是给人看的标记,加载器根本不读。我自己被骗过一次,以为写上了就是关掉了。
处理办法是把这些偏审阅口吻的东西整体挪到 meta,或者单独放一个分析目录,在装配的时候剥掉。
03它分不清是谁的状态
上下文装配的时候,用户画像和角色自己的状态会被合并进同一个环境块,用户那边的近况和角色当前在做什么挨在一起。模型只能靠标题分清哪段是谁的。
有一类角色只有用户的状态、没有自己的状态。如果用户那块少了「用户」两个字,模型就会把用户的状态读成自己的,用第一人称讲出用户刚做过的事。
查这类问题我先不看代码。角色对上下文的理解记录里,它怎么理解是逐字写着的,先看那个,再拿时间戳对齐,最后才顺到渲染函数。规则本身很简单:注入的用户数据,标题里必须带「用户」或「主人」,别指望模型靠位置猜归属。
04人设不该管账号是谁
有个角色的静态隔离测试里会回答「我不认识你」。追下去发现不是人设写坏了,是测试绕过了消息分发层本来要给的说话者身份,而人设被硬塞了「当前是谁在跟我说话」这个职责。
账号是谁、关系是什么,属于运行时的事实,该由代码在拼 prompt 的时候注入。人设只描述确认身份之后怎么相处。把某个平台的账号写进人设,换一个号就全错。
那次改完,人设展开长度从 7878 字降到 1700 字,十二项隔离生成测试全过,回复最长 12 个字。删掉的那些,模型从来没有用过。
这几次之后,我改人设时守三件事。同一条规则只写一处,在回复长度、收尾方式、硬约束里各写一遍,模型会当成三件不同的事然后全部执行。存储结构、宿主职责、语料来源、设计理由这些偏审阅的内容,进 meta 或者单独的分析目录。只在某一个话题里出现过一次的事实走检索,不进常驻层。
写完一段人设,我会问自己:这段是在规定它怎么做,还是在介绍它是什么样的人?介绍写得多了,模型就会把介绍演出来。