模拟面试进行得越久,历史问题、回答、评分和薄弱点就越多。如果把全部原文放进每次 LLM 请求,成本和噪音会持续增长;如果只保留最近几轮,面试官又会忘记前面已经确认过的信息。

FEMentor 将这个问题拆成短期上下文和长期记忆。

最近原文与较早摘要

buildInterviewContextWindow 会按时间倒序读取本场作答。在字符预算内,最近轮次保留问题、回答、分数和弱点原文;超出预算的较早轮次进入 overflowTurns,再压缩为:

{
  "summary": "较早历史摘要",
  "open_points": ["仍未补足的问题"]
}

当前实现为最近原文设置约 2200 字符预算,为摘要设置约 900 字符上限。最终上下文由“较早摘要 + 最近原文”组成。

这种方式并不是无损压缩。它主动放弃逐字重现,优先保留后续评分和追问仍然需要的信息。open_points 则避免摘要只记录已经发生的事情,而忘记尚未解决的问题。

LLM 失败时仍要有上下文

压缩不能成为主流程的新单点故障。因此系统为摘要和长期记忆都保留规则 fallback:即使模型调用失败,也可以从已讨论主题、优势和弱点中生成较简单的结果。

fallback 的质量不一定等同于模型摘要,但它保证一次压缩失败不会让整场面试中断。

长期记忆在会话结束后生成

短期上下文服务当前面试,长期记忆则在复盘阶段统一提炼。它不记录每一次口误,而是输出跨场次仍然有价值的结构:

  • stable_strengths:多轮都成立的优势;
  • stable_weaknesses:反复出现的弱点;
  • project_signals:项目能力信号;
  • role_fit_signals:与目标岗位的匹配信号;
  • recommended_focus:下一阶段练习重点。

结构化结果用于复盘和后续练习,Markdown memory 只作为补充日志,并不是唯一真源。

记忆首先是生命周期设计

我最终没有把“记住所有对话”当作目标。当前会话需要保真度,历史轮次需要压缩,跨场次信息需要稳定性,三者的生命周期不同。

记忆系统的价值不在于保存最多文本,而在于知道哪些信息什么时候仍然有用,以及模型失败时系统还能保留多少能力。