跟课上下文服务:AI伴学产品的记忆管理中枢设计与实践
2026/9/8 12:06:27 网站建设 项目流程

1. 项目背景与核心问题拆解

1.1 “研途灵伴”到底想解决什么问题

先交代一下背景。研途灵伴这个项目,定位是面向考研人群的AI伴学产品。考研复习的场景和平时刷短视频、看公开课完全不一样,它有几个非常突出的特点:学习周期长(通常半年到一年)、内容密度高(政治、英语、数学、专业课各有各的知识体系)、学生状态波动大(前期迷茫、中期疲惫、后期焦虑)。市面上的通用大模型产品虽然能回答问题,但它们不知道你学到哪了、不知道你今天刚听完哪一章、更不知道你昨天做错的题和今天要听的内容之间有什么关联。结果就是,学生问出来的问题很“散”,AI给的回答也很“散”,形不成真正的学习闭环。

我做这个项目的核心切入点,就是标题里那个词——“跟课上下文”。什么叫跟课上下文?说白了,就是把“学生当前正在学什么”这件事变成可计算、可维护、可传递的系统状态。比如一个学生正在听张宇的强化班第12讲,中间遇到一个极限运算没听懂,他随手问AI“这一步为什么能直接等价无穷小替换”,如果AI不知道他正听到第12讲的哪一秒、不知道前面讲过什么铺垫,那它只能给一个通用解释。但如果有跟课上下文服务,AI就能结合“当前课程切片内容 + 这节课的知识点图谱 + 学生历史掌握情况”给出针对性回答,甚至能主动提示“这个知识点你上周在1800题里错过两次”。

这个服务解决的核心痛点有三个。第一,消除“问非所学”的割裂感,让AI的回答始终贴着学生当下的学习进度。第二,让学习数据产生连续性,今天的行为能成为明天的上下文。第三,把“课程内容”从静态的视频/讲义变成动态的知识载体,让每一秒的课程内容都具备被检索、被关联、被追问的能力。

1.2 为什么跟课场景需要独立的上下文服务

这个项目最开始的形态其实不是独立服务,而是把跟课逻辑写在应用层里,直接调用大模型接口,靠prompt拼接课程信息和用户问题。当时觉得“上下文”嘛,不就是把当前视频的标题、章节、字幕文本塞进prompt里,让大模型看着回答就行了。但跑了不到一个月,问题全暴露了。

首先是上下文规模的失控。一节考研数学强化课通常是1.5到2小时,字幕文本转出来大概1.5万到2万字符,加上课程讲义、知识点图谱、用户历史错题记录,一次请求的上下文有可能堆到3万字符以上。先不说token成本,很多大模型对超长上下文的注意力衰减非常明显,它根本记不住你塞在中间位置的关键信息,回答质量直线下降。

其次是状态的碎片化。用户听10分钟课,暂停,问一个问题,再继续听5分钟,又拉回去重听,再问一个相关但不同角度的问题。如果每次请求都是无状态的、重新拼装上下文,那服务端根本不知道“用户上一次问的问题是哪一道”“那道题最后有没有解决”“他拉回去重听说明刚才没听懂”。这些信息全部丢失,跟课就变成了一次性的问答,而不是持续的学习陪伴。

还有一个很现实的问题是成本。通用大模型的接口调用是按token计费的,如果每个跟课问题都带上两万字符的课程原文,一个月下来成本非常吓人。而且考研学生的高峰使用时段高度集中,晚7点到11点,concurrent请求一上来,如果没有独立的上下文服务做缓存、裁剪、优先级调度,后端接口的响应时间和费用都会同时失控。

所以,把“跟课上下文”从应用层里抽出来,做成一个独立的服务层,不是技术上的洁癖,而是这个业务场景天然要求上下文具备“可持久化”“可选择性地注入”“可增量更新”这几个能力。这就像你请了一个私人家教,他不可能每次都让你把整本教材重新念一遍给他听,他脑子里得有你的学习档案,并且能根据你现在的进度判断该调用哪部分记忆。研途灵伴的跟课上下文服务,本质上就是在给AI做这个“记忆管理中枢”。

2. 整体方案设计与架构选型

2.1 服务定位与技术选型

这个服务在整体架构里的位置,我习惯把它画成三层:最上层是应用客户端(App/小程序/web),中间层是课堂状态服务和问答服务,最底层才是大模型引擎和向量数据库。跟课上下文服务属于中间层里最核心的一个基础组件,它不直接面向用户,但每一个跟课相关的功能——随堂问答、知识点推送、课堂笔记生成、课后复习卡片——都要通过它获取“当前语境”。

选型时有几个考量维度。第一是状态存储用什么。一开始考虑过Redis直接存上下文JSON,但发现上下文结构太复杂,里面有课程切片的引用、知识点ID数组、用户学习轨迹、临时对话记录,这种半结构化、需要频繁局部更新的数据,用Redis硬存会导致序列化/反序列化的开销非常大。后来改成了PostgreSQL + Redis两级结构:PostgreSQL存永久性的学习档案和上下文生命周期数据,Redis只做热点会话的短时缓存和读写加速。

第二是异步任务这块。课程视频的切片、字幕的清洗、知识点的关联这些都属于离线计算,不需要在用户请求的链路上实时完成。但跟课回答问题需要“即时感”,用户暂停视频后发问,最多等他两三秒,再久体验就垮了。所以整体采用gRPC同步接口处理实时问答,用消息队列(Kafka)承接异步任务,比如“更新学习轨迹”“预计算下一个知识点的关联内容”。

第三是检索方案。课程上下文里经常要“找到当前讲解的知识点在历史课程里第一次出现的位置”,以及“找到该知识点相关联的已学内容”。这类需求用全文字搜索不够精准,必须引入向量检索。我们最后用了主流的向量数据库方案,把课程切片和知识点描述做embedding,通过余弦相似度召回相关片段,再做粗排和精排。考虑到团队维护成本和稳定性,向量库选了偏工程化、运维简单的方案,没有自己折腾分布式索引。

技术栈定的很快:Go写核心服务(并发性能好、部署简单,团队本来就熟),Python写离线算法部分(主要处理文本切片、embedding、知识图谱构建),PostgreSQL存业务数据,Redis做热缓存,Kafka串起异步流水线,向量数据库承担语义召回。这些选型都不是追求新潮,全是看在团队规模、维护成本和业务场景下的综合分。

2.2 上下文建模的核心思路

这是整个项目里我想得最久、也认为最值得复盘的部分。所谓上下文,绝不是一个简单的“把最近的聊天记录存下来”的Buffered Memory,它必须是分层的、结构化的。我把它拆成四层:

第一层是基础上下文。包括用户的基本信息:考研年份、目标院校层次、报考专业、当前复习阶段(基础/强化/冲刺)、使用的教材体系(比如数学是跟张宇还是汤家凤)。这一层是“这个人是谁”,全局有效,所有问答都需要带上,但不怎么变化。

第二层是课程态上下文。这是跟课服务的核心。它描述的是“用户此刻正在经历什么”。我定义了一个称为CurrentSession的抽象结构:当前课程ID、当前视频的进度位置、所属章节、本节课覆盖的核心知识点ID列表、当前正在讲解的切片索引、本切片对应的字幕文本摘要。这个Session不是静态的,用户每次拖动进度条、暂停、播放、倍速切换,客户端都会上报心跳,服务端实时更新Session状态。

第三层是历史学习档案。这里记的是用户的长期数据:学过的课程列表、每节课的完成度、历史提问记录及对应知识点、做题正确率曲线、易错点集合。这层的作用是让AI知道“这个人以前学过什么、哪里薄弱”,从而在回答时能主动关联历史内容。

第四层是即时对话缓冲。也就是当前这次问答会话里最近几轮的对话内容。它和其他三层不同,是短时态,只在一次会话周期内有效,不需要跨天保存。

初始化状态时,服务端会先从PostgreSQL加载第一层和第三层数据,再从Redis或实时消息里恢复第二层状态,最后在问答服务启动新会话时创建第四层缓冲。实际效果是,一个用户发起跟课提问,服务端拼出来的Prompt不再是“一段课程字幕 + 一个用户问题”,而是一个结构化的上下文块:用户档案摘要 + 当前课程切片内容 + 相关知识点历史记录 + 最近对话摘要 + 用户问题。这个结构的好处是,大模型能明确区分“这是谁在问”“他在学什么”“他以前哪里不会”“他刚刚问了什么”,而不是把一堆文字糊在一起让它自己分辨。

2.3 为什么不用“全量历史对话”策略

有一个方向我们一开始就排除了:把用户所有历史对话全部累积当成上下文喂给大模型。很多做AI陪伴产品的团队喜欢这么干,觉得上下文越长越“懂你”。但实际工程里这个方案在长周期学习场景下根本走不通。第一,成本不可控,考研用户学习半年,对话记录可能有几十万token,总费用几何级上涨。第二,信息密度太低,六个月前的对话对当前这节课的问题几乎没有任何帮助,反而干扰模型判断重点。第三,计算延迟拉高,上下文越长首字延迟越高,学习场景里用户等不起。

我们的思路是“选择性记忆”:由服务端根据当前课程态和历史学习档案,动态决定哪些信息值得进入上下文。比如用户当前在学“中值定理”相关的课程,服务端会自动检索他在历史记录里关于中值定理的提问和错题,如果发现三天前他刚问过“拉格朗日中值定理的辅助函数怎么构造”,这次回答就会主动带上那条历史。反过来,如果用户之前问的是“虚拟语气”这种英语问题,跟当前数学课没有任何关联,就完全不会进入上下文。这种动态筛选策略,既控制成本,又保证了信息的相关性。

3. 跟课上下文服务的关键实现

3.1 课程内容感知与切片

跟课服务一切的地基,是对“课程内容”的数字化改造。原始课程是视频,视频里有画面、有语音、有字幕,但这些都不能直接被上下文服务使用。我们做的第一件事,是把每一节课制作成结构化的课程切片。

切片不是简单按固定时间戳切段,而是按“知识点边界”切。比如张宇某节课前20分钟讲概念,中间30分钟讲例题,后面15分钟讲易错点,这中间存在明确的语义边界。我们用两种方式混合判断边界:一是字幕文本的语义变化,通过embedding计算相邻句子之间的语义相似度,如果相似度骤降,说明可能进入了新的知识点;二是人工标注的核心知识点清单,教研团队对每节课预先列出知识点大纲,切片时把字幕文本对齐到大纲的ID上。

一个完整的切片单元包含:切片ID、所属课程ID、章节ID、时间范围(startTime/endTime)、字幕文本、知识点ID列表、难度系数、切片类型(概念讲解/例题演示/易错点提醒/总结回顾)。这样切完之后,整节课就变成了一条知识链,每个切片都和其他切片有前置/后置关系。比如“等价无穷小替换”这个知识点的切片,它的前置切片可能是“极限的四则运算”和“常见的等价无穷小公式”,后置切片是“泰勒公式展开”。

平时用户听课,客户端每15秒上报一次播放进度(含当前视频时间点),服务端根据时间点定位到具体切片,并更新Session里的currentSlice。查询的时候,就能做到“用户正在看哪一秒,上下文就知道他在学哪个知识点”,这个秒级感知能力是所有随堂功能的基础。

3.2 上下文状态的维护与更新

整个服务里最需要小心处理的,是上下文状态的并发和一致性问题。用户在听课过程中动作非常频碎:拖动进度条、暂停、切后台再回来、倍速切换、重听上一段、突然点进弹出的知识点卡片。这些动作都会触发客户端上报状态变更,如果服务端每次都全量更新Session,数据库压力会非常大,而且容易产生竞态。

我当时的方案是“增量更新 + 事件溯源”。客户端上报的状态事件只有几类:PLAY(播放)、PAUSE(暂停)、SEEK(拖动进度)、RATE_CHANGE(倍速切换)、HEARTBEAT(心跳)。服务端收到事件后,不是去覆盖整个Session,而是把事件追加到Redis里的一个短期事件流里,然后由事件处理器计算出最新的Session状态。比如用户连续拖动了三次进度条,最终停在18分26秒,处理器只需要关心最终位置,而不需要关心中间路过了哪些点。这样既减少了状态更新次数,又保留了用户行为的轨迹,后续做行为分析也有数据。

Session状态更新还有一个关键点:知识点的激活与失活。当用户从切片A跳到切片B,如果A和B属于同一个知识点,不需要额外处理;如果跳到了不同知识点,服务端要更新activeKnowledgePoints数组,把旧知识点标记为“已离开”,可能还要触发一个异步任务,把“用户在A切片停留了多久”“是否完整看完”“有没有产生提问”汇总到用户的历史学习档案里。这个沉淀动作是异步做的,不会阻塞用户当前的操作。

3.3 与上层应用的对接方式

跟课上下文服务对所有上层应用暴露的接口不多,总共就五个核心RPC:

第一个是ReportState,负责接收客户端上报的播放状态事件,这个接口设计成幂等的,客户端网络重试不会导致状态错乱。第二个是GetContext,上层问答服务在收到用户问题时调用,传入用户ID和当前会话ID,返回组装好的结构化上下文块。第三个是AskQuestion,这是问答服务通过gRPC直接调用上下文服务的“一站式”接口,内部封装了“取上下文 → 检索关联历史 → 组装Prompt → 调用大模型 → 返回答案”的完整链路。第四个是GetKnowledgeState,用于查询用户对某个知识点的掌握状态,支撑知识点图谱页面的渲染。第五个是SyncLearningRecord,外部系统(如刷题模块)把用户做题数据同步进上下文服务,更新学习档案。

为了让上层应用接入简单,所有接口统一走内部网关,用标准化的Proto定义,各端只要拉取SDK就能使用。QPS高峰期时,GetContext是调用量最大的接口,因为它被问答、笔记生成、知识点推荐好几个功能复用。为了扛住峰值,我们在Redis里给每个活跃Session加了一层本地缓存,默认TTL五分钟,只要用户还在听课,上下文就可以直接从缓存读取,几乎零延迟。

4. 踩过的坑与问题排查实录

4.1 上下文漂移:最隐蔽的坑

项目上线大概第二周,我们就收到一个奇怪的反馈:有学生反映,AI在回答“这题为什么要讨论a的取值”时,给出的是“根据您刚才学习的一元二次方程求根公式……”,可他当时正在听的是概率论的课。查了很久才发现,这是Session漂移问题。

原因出在客户端上报状态时,用的是“设备本地时间 + 课堂ID”,但学生同时打开了两个界面(比如一边听课一边翻上一节的笔记页),两个界面都在上报状态,后上报的请求覆盖了先上报的,导致Session被错误的课程信息抢占。那段时间正好赶上部分用户跨端登录(iPad听课、手机提问),问题就更加明显。

修复方案是给Session增加一个“场景互斥锁”:每个用户同一时刻只允许存在一个正在活跃的课堂Session,后激活的Session会先把旧Session置为挂起,而不是直接覆盖。同时客户端上报时增加了使用场景字段(课中页/笔记页/练习页),只有课中页上报的状态才允许修改currentCourse和currentSlice,其他页面的请求只能读取上下文,不能写入。经过这轮修复,上下文漂移的问题基本绝迹。

4.2 长视频切片检索延迟超标

跟课问答的链路是:用户提问 → 取当前切片 → 做相关知识点的向量检索 → 组装Prompt → 调用大模型 → 返回结果。其中向量检索这一步,在课程数量到几百节之后开始明显变慢,p95从原来的800ms涨到了2.3秒。整个链路跑下来用户侧经常超过5秒,这在学习场景里已经属于不可接受了。

排查发现,向量库里的集合没有做分区,所有课程的切片全部混在一起。我们做的优化是“课程维度预过滤 + 知识点标签倒排粗筛”:查询时先根据当前Session里的课程ID放入白名单,再结合切片的知识点ID做倒排粗筛,只对命中的几十个候选切片做向量精排。这样检索量从原来的几万条降到了几十条,检索耗时直接压到了200ms以内。另外,切片粒度大的课程,还会在建索引时做一次子切片拆分,让召回结果和用户当前位置的匹配更精确。

4.3 大模型回答中的知识断层问题

上线一段时间后,我们发现即使上下文拼装得再精细,大模型偶尔还是会出现“知识断层”:它引用了上下文里没提到的概念。有一次我拿一个学生的真实问题做测试:他在听线性代数“特征值与特征向量”这节,问“为什么特征值之和等于矩阵的迹”,AI回答时突然插入了一句“正如您之前学过的对角化定理”,但该学生的历史档案里根本没有系统学过对角化定理的相关内容。

这个问题出在组装Prompt时没有显式约束大模型的引用边界。后来我们在Prompt模板里加了一段强引导:你只能引用本次提供的上下文信息,不得假设用户掌握未出现在上下文中的知识点;如果问题涉及前置知识,请先从上下文的knowledge_history字段中确认用户是否学过,并明确标注“这需要前置知识:XXX”。这看起来是个很傻的修复,但实际效果立竿见影,胡编乱引的比例降了八成。

还有一个更细的点,是上下文里有冲突信息时的处理。比如用户学习档案里显示“已完成第8讲”,但实际课程进度只有第5讲(用户可能是跳着看的)。这时如果AI看到档案就默认用户掌握第8讲的内容,就会产生误导。我们增加了“可信度标记”:从实时Session里拿到的进度视为最高可信,从历史档案里拿到的进度则标记为“历史记录、可能存在跳跃”,并要求大模型遇到不一致时,以实时上下文为准。这类微调单独看都是小事,合在一起就是体验和幻觉的巨大差距。

4.4 常见问题速查表

如果你也要做类似的服务,下面这几条排查经验可以直接存下来做参考。Session数据错乱,优先检查前端是否在多页面并发上报、后端是否做了互斥锁。上下文内容过于陈旧,检查缓存的TTL设置是否合理,建议实时类数据TTL不超过5分钟。向量检索结果相关度差,先看是否做了课程维度的预过滤,再查切片的embedding是否按知识点边界切分。Prompt上下文太长导致超时,检查是否把历史全量塞进去了,要改成结构化筛选后注入。高峰期大模型调用激增,除了限流,还要做上下文级别的缓存,同一个切片+同一个用户短期内相同的问题直接命中缓存。

5. 复盘心得与改进方向

跟课上下文服务从立项到稳定运行,我最大的体感是:这类AI产品做得好不好,拼的不是模型的聪明程度,而是“围绕模型搭的那一圈基础设施”够不够细致。模型是发动机,跟课上下文服务就是油箱、是仪表盘、是减震系统。发动机马力再大,油箱漏油、仪表盘数据失真,车子照样开不远。

几个值得沉淀的经验,我简单列一下。第一,上下文服务建模时先想清楚状态分层,全局信息、课程态、历史档案、即时会话这四类要严格分开,不要混在一个JSON里,不然后期维护和排查会让你痛不欲生。第二,客户端的状态上报必须带着明确的场景标识,否则会出现跨页面互相覆盖的竞态问题,我们为此吃过亏。第三,向量检索一定要做预过滤,一上来就全局向量的方案在数据量增长后会是很痛的瓶颈。第四,Prompt里的知识边界约束必须写死,AI的学习建议宁可保守一点,也不要给出用户根本没学过的“前置知识”。

后续规划里有几个方向我觉得值得继续做。一是把当前基于单次Session的上下文,升级为跨天的“连续学习档案”,让学生第二天打开App时,AI能知道“你昨天学到第几节、哪个点卡住了、建议今天从哪开始”。二是增加上下文服务对多模态的支持,现在课程里除了字幕还有板书截图和老师的圈画动作,这些视觉信息如果能转化为上下文的一部分,跟课问答的准确度会再上一个台阶。三是把上下文服务做成可配置化,让教研团队可以不改代码,直接在后台编排不同科目、不同课型的上下文策略,毕竟政治课和数学课的跟课逻辑差别很大。

最后分享一个小技巧,也是我们踩过几次坑之后总结出来的:上下文服务上线前,一定要准备一套“状态回放工具”。它能根据用户的操作日志,完整回放某个时刻的Session状态、上下文内容和实际返回答案。没有这套工具,排查问题基本靠猜,有了它,很多诡异的问题一眼就能定位。做这类AI中间层服务的同学,建议优先把这件事做扎实。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询