
1. 问题从哪来AI 会话的“失忆症”与学习场景的“连续感”我用 AI 辅助学习的时间不短了最头疼的问题从来不是模型不够聪明而是它总记不住“我们刚才说到哪了”。今天开个对话让 AI 讲 TCP 三次握手明天想继续深入聊拥塞控制结果新会话里的 AI 一脸无辜完全不认得我。每次都要重新自我介绍、重新描述我之前学到哪、重新贴一遍背景资料这种重复劳动非常消磨学习的连续感。后来我想明白一件事AI 辅助学习的真正瓶颈不在模型能力而在上下文管理。尤其是当学习过程被拆成多个 Session——比如上午看理论、下午做练习、晚上复盘错题——Session 之间怎么衔接直接决定了学习效率。这也是为什么我开始自己做一个小工具核心就两个命令/handoff和/teach。它们的任务很明确/handoff负责把当前会话的上下文“打包带走”/teach负责让 AI 基于这份上下文接续讲课。这套方案我用了几个月实测下来学习状态被打断的概率明显下降今天把完整的设计思路和实现细节写出来。这篇文章适合两类人一类是跟我一样长期用 AI 辅助学习、被上下文丢失折磨过的普通用户另一类是正在做 AI 对话工具、Agent 或教育类产品的开发者。前者可以直接抄走这套命令的交互设计后者可以借鉴底层的上下文建模和持久化方案。我会从需求拆解、数据结构、核心实现到问题排查一步步讲透。2. 整体设计思路为什么“上下文交接”比“无限上下文”更靠谱2.1 别指望模型自己记住一切一开始我试过两种被动的方案。第一种是把所有对话历史一股脑塞进新会话的上下文窗口让模型“自己读一遍刚才聊了什么”。这种方式在小数据量下勉强能用但对话一长就会出问题token 数量爆炸、输入成本飙升、模型在长文本中抓不住重点。第二种是直接在每个新会话里复述一遍背景这等于让用户做人工上下文搬运工体验非常差。做了一段时间之后我才意识到问题的本质不是“上下文太短”而是“上下文没有结构化”。模型本身具备读取长文的能力但它缺少一条“主线”——哪些信息是当前学习阶段的核心哪些是边缘细节哪些是下一步要解决的问题。如果我们能把对话历史压缩成一条条结构化的关键信息让新会话的 AI 在开场就能拿到这条主线它就完全不需要记住全部对话记录。这个思路就是我设计/handoff的出发点不是把昨天的聊天记录原封不动搬过来而是把它加工成一个“学习档案包”里面包含主题、进度、关键知识点、疑问列表、学习偏好。这个档案包体积小、结构清晰、便于索引任何一个新 Session 拿到它都能在两三句话之内恢复“我们上次聊到哪了”的状态。2.2 学习场景比通用对话更需要“状态感知”通用对话场景下上下文断了重新聊问题不大。但学习场景不一样学习是一个强依赖连续性的过程。你今天学傅里叶变换明天学卷积后天把两者联系起来理解如果每一步都从零开始你永远在复习基础概念永远到不了深层理解。学习场景对上下文管理提出了几个特殊要求。第一是进度追踪必须知道用户已经掌握了什么、还没掌握什么不能每次都用同一套完整流程从头讲。第二是疑问沉淀学习过程中产生的疑问往往不是当场就能解决的有些问题需要在后续学习中找到答案如果疑问没有被记录和继续追问它们就消失了。第三是教学风格的连续性有的用户喜欢比喻式讲解有的喜欢公式推导有的需要代码示例这些偏好如果每次都得重新说明会让人非常烦躁。/teach就是为这些需求设计的。它不是一个静态的“讲课”命令而是会读取/handoff生成的档案包根据当前进度、疑问列表和偏好来动态组织一次教学会话。这两条命令一前一后形成闭环学完一段 → 打包上下文 → 下次继续 → 基于档案讲课 → 再打包新的上下文。2.3 两个命令职责单一组合起来才有力量/handoff和/teach我刻意设计成职责完全分开的这也是从工程实践里踩坑踩出来的教训。最早我把它们合并成一个命令结果既要负责导出上下文又要负责启动教学参数多到记不住而且其中一个环节出错就会影响到另一个功能。拆开之后/handoff只做一件事生成档案包。/teach也只做一件事读取档案包并讲授新内容。这样的拆法还有一个好处——档案包是可移植的中间产物。这意味着我可以在终端里用/handoff打包上下文然后把文件发到手机上的另一个 AI 客户端继续学习也可以把档案包分享给朋友让他用/teach命令基于同一份上下文做协作学习。上下文变成了一个显式的、可管理的文件而不是藏在 Session 后台的隐式状态这为很多进阶玩法打开了空间。3. 设计档案结构与上下文压缩算法3.1 学习档案包的 JSON 结构档案包是/handoff的核心产出我把它定义为一个 JSON 文件每个字段都有明确的语义。这个结构不需要特别复杂但字段命名要一看就懂后续扩展也方便。{ schema_version: 1.0, session_id: sess_8f3a2b1c, created_at: 2024-06-18T14:32:00Z, topic: { title: TCP/IP 网络基础, current_subtopic: TCP 拥塞控制, stage: 进阶概念理解 }, progress: { completed: [三次握手, 四次挥手, 滑动窗口], in_progress: 拥塞控制算法, next_up: [TCP 粘包问题, UDP 与 TCP 的对比选择] }, key_points: [ { concept: 拥塞控制, understanding_level: medium, summary: 防止过多数据注入网络导致链路堵塞主要靠窗口机制调节发送速率 } ], questions: [ { question: BBR 算法和传统的 Reno/Cubic 在拥塞判断上的本质区别是什么, status: open, created_at: 2024-06-17T10:20:00Z } ], learning_style: { preference: analogy_first, wants_code_examples: true, wants_visual_descriptions: false, depth: moderate, pace: normal }, context_footprint: { tokens_compressed_from: 18520, tokens_compressed_to: 1420, compression_ratio: 13.0 } }各个字段的作用我简单说一下。topic用于记录当前学的东西其中current_subtopic和stage是最关键的因为它们决定了/teach下一节课从哪里切入。progress是可勾选的清单key_points是从历史对话中提炼出的重要概念每条都带understanding_level方便后续教学做难度调节。questions是历史疑问池这个很妙因为很多疑问放一段时间后随着知识面的扩展会自己想通但也有不少疑问需要用新学的知识来回答保留疑问池就能形成“前置提问→后续解决”的闭环。learning_style是存储用户偏好的一块比如喜欢先打比方再上定义需要代码示例讲解节奏正常。这些信息通常需要用户主动设置但也可以通过分析历史提问方式自动推断两种方式我都在做。context_footprint纯粹是审计信息记录这包是从多大的对话历史里压缩出来的方便我观察压缩比是否健康。3.2 上下文压缩的两条路线提取式与摘要式把冗长的聊天记录变成上面的 JSON需要分两步先提取结构化信息再压缩非结构化内容。提取式是定规则从对话中识别用户问过的知识点、AI 讲过的概念、用户标记过“明白了”或“没懂”的片段。摘要式则依赖大模型的能力让 AI 通读对话记录后直接输出档案。我实际采用的是混合策略。定规则部分用来抓取“硬信息”——比如模块名、文件名、章节号、代码示例、链接这些信息提取准确率很高。大模型部分用来做“软信息”——比如提炼某个概念的用户理解水平、识别用户反复追问的薄弱点、判断用户的讲解偏好。软信息靠规则很难做对但让模型理解一段对话的情感倾向和认知状态效果就相当不错。压缩时要做 token 估算我用的经验值是“中文字符与 token 比例大约 1:0.6 到 1:1 之间”具体取决于分词器。压缩前可以先用系统提示词中的 token 统计工具算出历史对话的总 token 数然后设定一个目标压缩比比如“压缩到原规模的 8% 到 10%”。一场 18520 个 token 的对话压缩到 1420 个 token比例大约 13:1知识密度提升非常明显。注意压缩的关键不是让总结尽可能长而是让总结尽可能“可操作”。一份 3000 字的压缩摘要如果全是正确的废话对后续教学没有价值。真正有价值的是“用户在哪里卡住了”“卡住的原因可能是什么”“下一步最应该讲什么”。3.3 Session 持久化与档案包的存储策略档案包生成之后放在哪里直接关系到跨 Session 恢复的体验。我先后试过几种方案。最简单的是存在本地工作目录里文件名带时间戳和主题关键词比如handoff_20240618_tcp_congestion.json。这种方式的好处是用户可以直观地看到和管理档案缺点是不太适合自动化流程——新 Session 要恢复时得手动指定用哪个文件。后来我改用一个固定目录 索引文件的结构。在用户主目录下建一个.ai-learning/sessions/目录每个 Session 的档案包以session_id为文件名存储同时维护一个index.json记录每个 Session 的主题、更新时间、状态active / archived / shared。这样/teach命令启动时可以先查索引文件按时间或主题挑选最近的档案包实现半自动化的恢复。还有一个细节是版本管理。同一个 Session 多次/handoff会生成多个版本我会保留最近 5 个版本而不是只留 1 个。为什么因为学习方向可能会调整用户可能觉得“上次压缩时丢了重要的细节”想回溯到更早的版本重新压缩。保留版本历史用不了多少磁盘空间但能救命。.ai-learning/ ├── sessions/ │ ├── index.json │ ├── sess_8f3a2b1c_v1.json │ ├── sess_8f3a2b1c_v2.json │ └── sess_a11f22cc_v1.json └── config.jsonconfig.json里存全局偏好比如默认的压缩比、教学语言、是否自动提交疑问池等。这样用户只要配置一次就能在所有 Session 中生效。4. 核心实现/handoff 打包上下文、/teach 接续讲课4.1 /handoff 的实现步骤与关键代码/handoff的核心流程是从当前上下文提取信息 → 压缩成档案 → 写入磁盘。我用 Python 实现了一个简化版本主要依赖两个大模型调用一个做信息提取一个做摘要压缩。实际操作中可以只调用一次让模型直接输出结构化 JSON但分开做更稳——信息提取的质量不会因为摘要环节的偏差而丢失。import json import datetime import hashlib def generate_session_id(chat_text): raw chat_text datetime.datetime.now().isoformat() return sess_ hashlib.sha1(raw.encode()).hexdigest()[:8] def extract_hard_info(chat_text): 规则提取硬信息代码块、链接、专有名词等 key_points [] # 这里用正则提取代码块中的函数名、命令名 code_blocks re.findall(r.*?, chat_text, re.S) for block in code_blocks: key_points.append({type: code_example, content: block[:200]}) return key_points def summarize_soft_info(chat_text, api_client): 模型提取软信息理解状态、疑问点、偏好 prompt f 请阅读以下AI教学对话记录提取四个方面 1. user当前最困惑的问题如果明显存在 2. 已经学完的知识点列表 3. 一个最值得记入疑问池的未解决问题 4. user偏好的讲解风格 对话记录 {chat_text[-6000:]} 以JSON格式返回字段为confusions, completed_points, pending_question, style_preference resp api_client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], response_format{type: json_object} ) return json.loads(resp.choices[0].message.content) def handoff(chat_text, api_client): session_id generate_session_id(chat_text) hard extract_hard_info(chat_text) soft summarize_soft_info(chat_text, api_client) archive { schema_version: 1.0, session_id: session_id, created_at: datetime.datetime.now().isoformat(), topic: {title: soft.get(current_topic, 未命名)}, progress: {completed: soft.get(completed_points, []), in_progress: soft.get(confusions, [])}, key_points: hard, questions: [{question: q, status: open} for q in soft.get(pending_question, [])], learning_style: {preference: soft.get(style_preference, unknown)}, context_footprint: { tokens_compressed_from: estimate_tokens(chat_text), tokens_compressed_to: estimate_tokens(json.dumps(archive)) } } save_archive(archive) return archive实际工程里extract_hard_info和summarize_soft_info返回的结果需要合并去重避免同一个知识点被记录两次。save_archive会先读index.json把新档案的信息追加进去再写回索引。这里有一个重要的并发处理细节如果用户同时打开多个终端跑/handoff索引写入可能冲突我用了一个简单的文件锁——写入前先创建.lock文件写入完成后删除避免两个进程同时改索引。4.2 /teach 的 Prompt 模板与教学流程/teach的职责是读取档案包然后组织一次有针对性的教学。AI 教学的本质是“基于学生当前认知状态讲授新内容”所以/teach的 Prompt 必须建立在档案包提供的状态之上而不是让 AI 凭空自由发挥。/teach [topic_name] --from handoff.json --focus BBR与Cubic对比参数解析过程不复杂--from指定档案包路径--focus是本次教学想聚焦的子话题。接着系统会组装一段教学 Promptdef build_teach_prompt(archive, focus, user_input): completed 、.join(archive[progress][completed]) pending 、.join([q[question] for q in archive[questions]]) style archive[learning_style] style_block if style[wants_code_examples]: style_block - 每讲一个概念必须给出可运行的最小代码示例或命令示例\n if style[preference] analogy_first: style_block - 先用生活化类比引入概念再用严谨定义归纳\n prompt f 你是一个耐心的技术导师请根据以下学生档案进行教学 【当前学习状态】 - 已掌握主题: {completed} - 正在进行: {focus} - 待解决的疑问池: {pending if pending else 暂无} 【学生偏好】 {style_block} 【本次教学目标】 围绕 {focus} 概念讲清楚 1. 这个概念解决了什么问题为什么这个问题的解决是必要的 2. 核心原理至少给出一个类比 一个严谨解释 3. 一个实际案例展示这个概念在真实系统中的样子 4. 讲完后给出 3 道自测题分别覆盖基础理解、原理推导、实际应用 【对话限制】 - 每次回复控制在 4 个自然段以内便于分步讨论 - 如果学生提出疑问池中的问题优先在本次教学中尝试回答 - 讲解中发现学生理解有误及时纠正不要将错就错 本次学生提问或指令{user_input} return prompt这个 Prompt 模板有几个设计亮点。第一它把“教学目标”拆成了四个可执行的小任务避免 AI 只讲概念不验证理解。第二它在每次授课开头都会引入“疑问池”把历史遗留问题放在当前知识背景下重新审视很多疑问在新章节的内容出现后自动就通了。第三它用“对话限制”约束回答长度保证教学是分步进行的而不是一次倒一大桶知识。/teach进行中/handoff也在后台持续工作。每次教学结束后系统会把这节课的对话追加到 Session 的原始记录中并自动更新档案包的progress和questions。这样/teach之前跑一次/handoff和/teach之后跑一次/handoff其结果是不一样的——后者的档案包状态是刷新过的。4.3 上下文窗口溢出与滑窗策略Session 对话历史无限增长后上下文窗口溢出是绕不开的问题。我的策略是三级缓存热数据最近 2 轮对话全量保留温数据最近 10 轮对话保留摘录冷数据更早的对话只保留档案包中的结构化信息。这样在/handoff时模型只需要处理热数据加温数据压缩成本低实时性高。如果用户坚持要把全部历史都拿来做压缩我会设置一个 token 上限比如 32000。超过上限的部分按时间分段喂给模型分段之间确保有 10% 的重叠区间防止关键信息落在分段边界被截断。重叠区间的设计非常重要因为对话中的某个概念可能在上一段的结尾才刚被提及下一段开头就给出了定义如果没有重叠模型很容易丢掉这个关联。还有一个投机取巧但很实用的技巧对于超长历史中的重复性信息比如 AI 反复介绍同一个概念多次压缩时只保留第一次最完整的讲解和最后一次用户最细节的追问中间的冗余内容全部丢弃。这能大幅减少 token 消耗而且信息的完整性基本不受影响。5. 实操演示从“学完就忘”到“无缝接续”的完整流程5.1 场景一昨天学完 TCP今天接着学 BBR我第一次调试这套工具的完整流程就是用 TCP 协议的学习做测试。昨天我跟 AI 聊了二十分钟 TCP 拥塞控制把基本概念理清了但到 BBR 算法时因为时间不够只留了一个疑问“BBR 和 Cubic 判断网络拥塞的方式有什么本质不同”。如果按照以前的习惯今天开新会话这个疑问大概率就丢了。昨天结束时我执行了/handoff系统自动生成了一个档案包包含已完成主题列表、进行中的子主题BBR 算法、疑问池BBR vs Cubic。今天新开一个 Session先运行ls ~/.ai-learning/sessions/找到最新档案然后执行/teach TCP BBR 拥塞控制算法 --from handoff_20240617_tcp_congestion.json --focus BBR与Cubic对比/teach读到档案包后开场白就变成了“根据你的学习记录你已经理解了三次握手、滑动窗口和传统拥塞控制的基本思路昨天留下的问题是 BBR 与 Cubic 的本质区别我们今天就从这里切入。”看到这句话的瞬间我确定这套方案是值的。它不只是省了我重新介绍自己背景的时间更重要的是它让 AI 的教学起点精确地停在了我“昨天卡住的地方”而不是从头再走一遍。5.2 场景二把档案包转给另一个 AI 客户端有一天我在电脑上学习整理完 PHP 数组和字符串函数临时要出门只剩手机。以前这种场景学习只能中断但/handoff生成的档案包改变了这个局面。我通过网盘把 JSON 文件同步到手机上在手机端的 AI 应用里粘贴了一段封装的指令让它读取文件内容并执行teach角色。这个体验极其顺滑手机端的 AI 并不知道我昨天在电脑上聊了些什么但通过档案包里的completed列表和questions池它非常自然地接续了话题。后来我意识到这其实是/handoff设计里最被低估的价值——上下文不再被绑定到某个特定客户端或者某个特定 Session而是变成了一份可以自由移动的资料。只要目标客户端支持读 JSON 的指令就能无缝接管学习进度。6. 常见问题与排查技巧那些踩过的坑6.1 档案包丢失或无法读取最直观的问题是用户手滑把 JSON 文件删了或者移动了目录/teach找不到档案。/teach的错误提示写得越友好越好我现在的做法是如果找不到--from指定的文件自动列出最近的 5 个可用档案让用户确认是否要用最近的备份。如果文件损坏第一步检查 schema 版本。我的 schema 版本在迭代过程中变了好几次偶尔老档案的字段在新代码里读不到所以我后来加了一个自动迁移函数版本不匹配时先按旧版解析再对缺失字段补默认值。这个方法不完美但能在大多数情况下避免“一地鸡毛”。6.2 上下文压缩后细节丢失压缩必然会丢信息关键是怎么保证丢的都是“该丢的”。有一次我在学习某个框架的 API压缩后生成的 key_points 把动词都保留下来了时间、参数、依赖版本全丢了导致/teach在讲案例时给出的示例代码版本过旧跑不起来。排查后发现是提取硬信息时的正则写得太宽松把《代码块中的内容全部截断到 200 字符》这条规则用得太粗暴核心参数被截掉了。修正方案是在 key_points 中对代码示例单独存储一个长度更长的字段不做硬性截断而是让模型判断哪些部分是关键。同时我加了版本号提取规则凡是v1.x.x、jdk-17这类模式都会被单独抓出来放进environment字段。6.3 /teach 讲解内容偏离当前进度/teach偶尔会“跑偏”比如学生正在学 BBRAI 却开始花大量篇幅讲 TCP 首部格式这属于更早就学过的内容。一开始我以为是 Prompt 写得不够清晰后来发现是档案包里的completed列表太粗糙它只写了“三次握手、四次挥手”但没有标注“熟练程度”AI 不知道用户大概不需要复习这些。解决方法是给 key_points 增加一个mastery_level字段取值范围是 low / medium / high。/handoff时通过分析用户的提问深度来推断如果一个主题用户问的是“是什么”理解程度标注为 low问“为什么这样设计”是 medium问“有哪些极端场景和业界最优解”是 high。/teach在读取这个字段后对 high 的知识点只在上下文边缘提一句对 low 的则安排快速回顾。6.4 Session Token 过期与调试会话卡住在实际开发中我还遇到过一些 Session 层面的怪问题这里分享一个和调试器相关的真实案例。我在调试一个基于本地代码库的 AI 辅助学习插件时终端经常卡住提示pending authentication: please accept debugging session on the device这不是上下文管理本身的问题而是调试会话的一个常见坑调试器在等待设备端的授权确认但终端界面没有显式提示用户去设备上点“允许”。排查思路分三步第一检查设备端是否有弹窗未处理第二看会话是否因为等待认证而进入了挂起状态如果是主动拒绝并重新发起第三确认 Session 的认证超时时间配置是否合理太短会导致频繁中断太长会让用户误以为程序卡死。这个案例给我一个启发Session 管理并不仅限于对话历史的持久化还包括底层运行环境的生命周期管理。设计/handoff时最好把运行环境的状态也一并打包进档案——比如当前用的是什么调试模式、依赖服务是否健康、有没有未完成的认证流程。把这些状态记录在案能避免很多“上下文没丢但环境丢了”的尴尬。7. 进阶让 /handoff 包支持多人协作与长期知识库工具稳定运行之后我开始琢磨一个问题既然/handoff能生成一个可移动的学习状态那它为什么不能变成多人协作的载体我尝试了一个“学习小组共享”方案。小组里每个人维护自己的档案包定期用/handoff导出。组员之间可以互相加载对方的档案包用/teach给对方讲自己擅长的内容。因为是基于结构化档案的加载者能在几分钟内掌握对方的学习进度这种“知识交接”效率远超口头同步。更进一步我把这个档案包和笔记系统打通。每次/handoff生成的 key_points 和 questions 会自动同步到个人知识库中相当于给每次学习自动生成了一份结构化的摘要。长期积累之后这些档案本身就成了一个可以搜索、可以回顾的知识资产。比起翻聊天记录翻 JSON 档案的体验要好得多——因为档案里的信息是清洗过的、提炼过的而不是原始的流水账。我还试验过用档案包生成“遗忘曲线复习计划”。因为档案中有每次学习的时间戳和主题列表我可以按日期统计哪些知识点已经两周没接触了然后自动生成复习任务在/teach里以“快速回顾”的形式插入。这个功能对工具型技能如 Linux 命令、编程语言 API特别有效对抗遗忘的效果比盲目重学强得多。8. 写在最后工具再强也别忘了学习本身的目标几个月用下来我最大的感受是好的上下文管理不应该是让 AI 替你记住一切而是让 AI 和你共享一个清晰的“认知地图”。/handoff和/teach只是我实现这个目标的具体载体它们的核心方法论——显式建模学习状态、结构化的上下文交换、基于状态的个性化教学——其实可以用在很多产品里。我实际使用中发现这套工具的受益对象并不仅限于“学技术的人”。我妻子用它来备考职业资格证书她的档案包里全是法律条文、案例分析她用/teach让 AI 从“出题人视角”给她讲知识点效果相当不错。小孩子学历史故事的时候我也会帮她跑一次/handoff隔几天再继续讲省得每次都要重新回忆故事背景。最后分享一个我在迭代中反复提醒自己的心得别为了让工具显得智能而过度设计。/handoff和/teach最初只是一个 JSON 文件加一个 Prompt 模板后来我一度想给它加上关系图谱、记忆权重、自动对话摘要等等功能结果把简单的东西做复杂了。最后我砍掉一半功能回到“先打包再讲课”这个简单逻辑上反而顺手了很多。好的工具应该像剪刀两个刀刃各干各的活合在一起才能把纸剪开。