Files
life-echo/docs/TODO.md
Kevin 3121d1384d WIP: memory system improvements (in progress)
Interview/chat prompt layers, reply planner, style profiles, memory
injection, interview meta store, and related tests. Work not finished.

Made-with: Cursor
2026-04-22 16:56:28 +08:00

7.6 KiB
Raw Permalink Blame History

结论先说 如果你要走你选的这条路:

先让模型做“本轮重点判断”,再生成回复

那这不是只修一个点,而是在修仓库里一个更普遍的反模式:

系统过早承诺 micro-decisions模型只能在被预分配好的轨道里写答案。

这个反模式在你们的 chat 链路里确实存在多处。

一类:和这次问题本质相同

  1. interview_turn_plan.py 这是最典型的同类问题。

它先把复杂语义压成 3 个 mode

emotion_first memoir_push follow_user_only 然后再进一步规定:

要不要问 最多几个问句 是否必须问 问句必须挂哪类锚点 这里不是在“给模型方向”,而是在替模型做回合级编排决定。

interview_turn_plan.py Lines 157-170 def plan_interview_turn( *, current_stage: str, empty_slots: list[str], normalized_user_message: str, memory_evidence_text: str, stage_switched_this_turn: bool, ) -> InterviewTurnPlan: """ 粗规则(可迭代): - 情绪浓:先共情,不强推叙述槽搜集问。 - 刚切换人生阶段:跟着用户节奏,不做「新阶段问卷首开」。 - 当前阶段无空槽:深度跟进,不重启盘点。 - 默认memoir_push锁一个主槽 + 挂钩摘录。 """

interview_turn_plan.py Lines 288-290

  • 回忆推进memoir_push:对用户可见回复中须有恰好一个开放式回忆追问, 且意图明显在补足下面「主追问方向」;问句必须挂住挂钩摘录用户本轮原词(二者至少其一)。 这和你指出的问题是同一种: 模型还没理解“这轮到底最重要的是什么”,系统已经先替它决定“这轮应该长成什么样”。
  1. prompt_layers.py 这里是第二个高危点,而且和上面叠加。

它不只是给高层原则,而是在定义很多具体动作脚本,比如:

连续两轮没问,这轮必须问 问句最多一个 情绪重也不能连续三轮不问 更偏好从体感和画面往下长

prompt_layers.py Lines 242-243

  • 追问义务回正(防多轮零问):通读近期你方(助手)的连续回复:若已连续两轮都没有任何问句...则本轮必须在短承接之后给出恰好一条带锚的开放式问...
  • 纯跑题 ≠ 情绪红灯:若用户本轮几乎只有寒暄、天气、泛泛近况、社交客气,而没有人生经历实质——不适用「整轮只陪不问」... 这是同类问题,因为它把“什么时候该追、什么时候该停、什么时候该纯承接”写成了比较硬的流程规则。 这种规则不是安全边界,而是对中间思考过程的强干预。
  1. orchestrator.py 的 scene_cues 这也是同类问题,只是形式不同。

系统在正式生成前,先偷偷把“场景氛围提示”塞进 context

orchestrator.py Lines 296-300 scene_cues = extract_scene_cues(normalized_user_message) if scene_cues: cue_block = "\n".join(f"- {c}" for c in scene_cues) scene_hint = f"\n\n[场景氛围提示——可借用这些感官细节自然接话,不要原样抄]\n{cue_block}" memory_evidence_text = (memory_evidence_text or "") + scene_hint 这本质上也是“替模型预判重点”,而且还是单边偏置。 系统没有同等强度的 relationship_cues / identity_cues却有 scene_cues 特权通道。

所以如果你上“先让模型判断重点”的方案,这里几乎一定也要一起收口。

  1. reply_planner.py 这个文件很有意思。 它其实已经是一个“两阶段生成”的雏形了,但目前它做得太窄,没有判断本轮重点,只是在微调几个受限字段:

memory_usage reply_shape memory_reference_style

reply_planner.py Lines 57-94 def _build_reply_planner_prompt( *, plan: InterviewTurnPlan, user_message_preview: str, memory_evidence_preview: str, ) -> str: ... ## 当前规则基线(已由服务端算出,通常应尊重) - mode: {plan.mode} - memory_usage: {plan.memory_usage} - reply_shape: {plan.reply_shape} ... 这说明你们其实已经有一个很自然的插槽去做你想要的方案 A。 只是现在这个 planner 不是“判断重点”,而是“在既定重点下微调姿势”。 所以它解决不了你说的问题。

二类:有点像,但不是核心病灶 5. interview_agent.py 的重复问句守卫 这里有两层后处理:

apply_duplicate_question_guard dedupe 失败后再做一次 LLM retry

interview_agent.py Lines 82-86 out, deduped = apply_duplicate_question_guard( out, state=memoir_state, recent_questions=recent_questions, )

interview_agent.py Lines 51-57 上一版模型输出因包含与「最近已问过的问题」或「已确认事实」重复的问句,已被系统弃用。请重新写一整条回复

  • 仍须遵守上文全部主规则;
  • 先贴着用户本轮原话承接半句到一两句(可有画面感);
  • 禁止再用与刚才同义、仅换说法的确认型问句; ... 这类逻辑有副作用,但我不会把它视作和你这次 case 完全同级的问题。 原因是它更像“去重护栏”,不是“决定本轮重点”。它可能让模型更难问出正确问题,但它不是主要在替模型定义重点。

我的判断是:它是次级 handcuff不是主 handcuff。

  1. prompts_profile.py 的资料收集 choreography 这个模块里也有比较明确的流程约束:

prompts_profile.py Lines 132-137

  1. 先接住...
  2. 话题优先...
  3. 资料穿插...
  4. 轮换...
  5. 每次最多 12 个资料相关问点... 这个也有点像“替模型编舞”,但这里我会更宽容。 因为这个任务本身就更窄:

目标明确 信息字段有限 风险主要是漏字段和重复问 所以这里的 choreography 不优雅,但不一定是严重问题。 它不像正式访谈主链那样,直接压扁复杂的人类语义。

三类:不是这类问题,应该保留 7. 自传污染边界 / 禁编造 / memoir fidelity 这些不是 handcuff而是必要边界。

比如:

不准把用户经历写成助手自己的经历 不准编造未提及的人名、地点、对话 memoir 生成不能越过事实边界 这些属于 safety / fidelity contract不是“替模型做重点判断”。

所以如果你要上方案 A我会明确区分

该放开的,是重点判断与表达路径。 不该放开的,是事实边界与身份边界。

所以,是否“有类似问题”? 我的判断是:

有,而且主要集中在 chat 主回复链路 最明显的同类问题是这 4 个:

api/app/agents/chat/interview_turn_plan.py api/app/agents/chat/prompt_layers.py api/app/agents/chat/orchestrator.py api/app/agents/chat/reply_planner.py不是坏但能力位置放错了 它们共同的问题不是“规则太多”这么简单,而是:

系统在多个地方分别替模型决定“这轮重要什么、怎么组织、该不该问、问什么形状”,导致模型没有真正做一次独立的 salience judgment。

这和你想引入的方案 A正好是正面冲突的。

如果你选方案 A哪里最适合承接它 最现实的一点是: 你们已经有一个两阶段接口,就是 reply_planner.py。

但如果要真做“先判断本轮重点再生成”我不会把它继续定位成现在这种“reply_shape 微调器”,而会把它升级成:

focus planner / salience planner 它先输出类似这样的隐藏判定:

primary_focus secondary_focus why_now should_ask candidate_anchor_span 然后正式生成模型再基于这个来写回复。

也就是说,你们不是没地方放这个方案,而是现在已经有一个很接近的位置,只是它做的事太弱、太晚。