好感/信赖系统重构计划
TRUST REFACTOR
2026-09-17 与用户逐条讨论后定稿。四条决策、三阶段实施、每阶段可独立上线与回滚。 现状分析见本文件"附录:现状与问题"(依据
app/systems/trust_manager.py等的只读审计)。
一、四条设计原则
- 三维必须是独立来源的观察 —— Mayer, Davis & Schoorman (1995) 的 ability / benevolence / integrity 模型强调三者是独立输入。我们现在的做法正相反:维度由 LLM 随手选、 不确定就均摊,三维实际退化成一维。
- 自我暴露(self-disclosure)是亲密的引擎 —— Nikolajczyk et al. (2026)《Beyond Human Bonds》(Human-Machine Communication 13: 185-204):关系阶段式发展, self-disclosure 在加深亲密上起关键作用。我们现在只看"模型觉得这轮回复有多深", 玩家说了多少关于自己的事完全没用上。
- 阶段(事件驱动)+ 数值(渐变)并存 —— 数值管渐变,阶段管"能做什么"与仪式感。
- 反馈即奖励 —— 数值可以隐藏,但原因必须可见(养成类设计的常识: 好感点在关键处跳变并立刻给反馈)。
二、锁定的决策(2026-09-17 用户定的)
| # | 决策 | 结论 |
|---|---|---|
| 1 | 维度分类 | 混合:规则优先(覆盖常见情况)+ LLM 兜底(只输出类型与幅度) |
| 2 | 24h 上限 | 按维度分开,各 20(原来是三维共享总量 10) |
| 3 | 阶段 | 阶段 = 最低亲密度下限:初始 25;跨过 50 → 下限 50;跨过 75 → 下限 75 |
| 4 | 界面 | (a):只给方向与原因(▲+2 因为你说了小时候的事),不露绝对值与阈值 |
补充默认(用户认可):
- 地板按维度分别生效:某一维跨过 50,只把那一维的地板抬到 50;
- 判据用该维度自己的历史最高值(不是当前值、不是 overall)→ 掉下去再回来不会重复触发阶段;
- 阶段除数值门槛再加事件条件(如"她主动找过你一次""共同经历一个剧情节点")—— 否则一天聊满就能拿到"亲密",阶段会变得廉价。
⚠️ 24h 各 20 意味着三维合计 60/天,从 25 到"亲密"(80) 最快 1 天。 这是有意的(聊得多就该涨得快);"时间闸门"交给阶段,而不是交给冷却。
三、数据模型
{
"dimensions": { // 保留:三维不变、权重不变(0.35/0.35/0.30)、0-100
"professional": {"value": 62.4, "history": [...]},
"emotional": {"value": 48.1, "history": [...]},
"rapport": {"value": 55.0, "history": [...]}
},
"stage": { // ★ 新增
"key": "trusting",
"floor": 50, // 当前生效的地板(25/50/75)
"peak": {"professional": 62.4, "emotional": 51.0, "rapport": 55.0}, // 各维历史最高
"since": 1737000000,
"history": [{"from": "neutral", "to": "trusting", "why": "她第一次主动找你", "ts": 0}]
},
"signals": { // ★ 新增:推进器的原料(自己统计,不靠 LLM)
"self_disclosure_7d": 6,
"care_given_7d": 3,
"shared_moments": 12
},
"history": [ // 扩展:每条都能回答"为什么"
{"dim": "emotional", "change": 2, "source": "chat",
"why": "你说了小时候的事", "ts": 0}
]
}
向后兼容:老文件没有 stage / signals → 读时按维度数值反推初始阶段与地板;
trust_profiles.py 的 381 份干员起点档案完全不动。
四、增量规则:分类 → 幅度 → 加成 → 闸门
玩家消息 + 她的回复
① 分类(规则优先,LLM 兜底) → 决定维度
② 幅度(LLM 只在该维度内打 1-3)
③ self-disclosure 加成(不经 LLM) → emotional 额外 +1(每轮最多 1)
④ 闸门(15 分钟冷却 + 每维 24h 20)
规则层:
| 命中 | 维度 |
|---|---|
| 技术提问、方案、任务、承诺兑现、靠谱/不靠谱 | professional |
| 情绪表达(我难过/我累/我怕)、关怀她的问句、安慰 | emotional |
| 玩笑、梗、共同回忆、"你还记得…"、撒娇调侃 | rapport |
LLM 兜底层:只输出 {type: pro|emo|rap|none, gain: 1-3} —— 不再让它"顺手选维度再打分"。
五、衰减与不对称
| 现在 | 改成 | |
|---|---|---|
| 触发 | 读 get_trust() 时结算(读界面就改数据) |
后台每天一次 |
| 形态 | 7 天后一次性块状扣 | 渐进(每天 −0.5 起,闲置越久系数越大) |
| 下限 | 固定 25 | 阶段地板(25/50/75) |
| 负面 | −1/−2/−3 | ×1.5-2,且恢复更慢(扣掉的情感需先经过 N 次正面互动才回升) |
六、门槛集中
TRUST_GATES = {
"doctor_dark_side": ("emotional", "intimate"), # 博士暗面
"insider_rumors": ("professional", 60), # 内幕风声(原挂 overall,不合理)
"loan_limit_basic": ("overall", 60),
"loan_limit_high": ("overall", 80),
"she_can_refuse": ("overall", 55),
"unmask_easter_egg": ("emotional", 60),
}
以后新增门槛必须来这里登记,否则又会随手挑一个数。
七、界面(a 方案)
情感亲密 ▲ +2 因为你说了小时候的事
专业信任 — 最近没什么变化
默契度 ▼ −1 你有三天没接她的梗了
不显示数值、不显示阈值、不显示"距离下一阶段多远"。 (复用了档案页那座"里程碑"块 —— 它现在是空壳。)
八、三阶段实施
| 阶段 | 内容 | 验收 |
|---|---|---|
| A | 露原因(界面)+ 门槛集中 + 后台统计修好 + 衰减移出 getter 改后台渐进 | 玩家能看到"为什么";后台信赖数字不再恒 0 |
| B | 维度分类(混合)+ self-disclosure 信号 + 日上限改各 20 | 三维曲线开始分化;后台加"信赖从哪来"饼图 |
| C | 阶段地板(25/50/75)+ 阶段推进话术 + 事件条件 + 不对称 | 阶段日志;她推进阶段时说的那句话 |
每阶段独立可上线、可回滚。B 会改数值行为,上线后要用固定输入探针
(scripts/repeat_probe.py 那套)观察曲线是否符合预期。
九、怎么验证(不靠感觉)
- 固定输入探针:同一批消息跑 20 轮,看三维是否按类型分化;
- 来源分布:后台加"这周信赖从哪来"(按
history.source/dim聚合)—— 这张图会告诉我们 self-disclosure 实际占多少,调参才有依据; - 阶段日志:谁在什么时候因为什么进了新阶段。
附录:现状与问题(只读审计,2026-09-17)
实现:app/systems/trust_manager.py(817 行单例)+ trust_profiles.py(381 份起点档案)
response_interceptor.py(LLM 打分器)。存储按 用户 × 线(身份人设) 隔离:lines/{line}/trust_{op}.json,读时回落到memory/{op}_trust.json。
问题清单(按严重程度):
- 三维退化成一维:维度由 LLM 随手选、不确定就均摊 0.35/0.35/0.30 → 三个数字总是一起动;
- self-disclosure 完全没用上(文献里最强的推进器);
- 没有阶段:只有连续数值,玩家感受不到"我们到哪一步了";
- 玩家不知道"为什么":
history里有change,界面不显示 → 无法形成策略; - 增长被冷却/上限主导(15 分钟 + 24h 总量 10)→ 曲线由计时器决定,不由剧情决定;
- 三条路径绕规则:动态留言 +0.5 绕过冷却;送礼 1-4 点有独立预算;扣减绕过全部 → 同一件事四种规则,没人能说出"我这样做一天能涨多少";
- 衰减块状且藏在 getter 里:读一次界面就结算一次(观测改变被观测值);
- 门槛值散落、口径不一:博士暗面挂
emotional、内幕挂overall,无依据可循; - 清空记忆 ≠ 归零:purge 后按剧情档案重新播种(阿米娅仍是 55/60/50),与 UI 承诺不符;
- 不对称性没建模:涨受上限压制、扣的分值太小,"说错话"没有代价。
已修的 bug(阶段 A 的一部分):
- 后台"信赖全景"恒为 0 ——
_scan_trust读data.get("overall"),而 trust 文件 从不落盘该键;已改成从dimensions现算,并补上按线路径(原来只扫memory/); scripts/calibrate_trust_baseline.py同样只扫老路径 → 补上lines/*/trust_*.json。