日期2026-06-22项目关键词错题本、复习推荐、题集小测、基础跟课上下文、OCR、Agent 工具层、联调收口摘要这篇文章是我在“研途灵伴”项目中的个人总结。项目里我主要负责 server 端学习专项业务和 Agent 学习域工具层具体包括错题本、复习推荐、题集小测、基础跟课上下文、OCR 与文件安全、学习专项接口支撑以及后期的系统调试和缺陷收口。回头看这段开发过程我做的核心事情不是单独写几个接口而是把“答疑产生错题、错题进入复习、复习再验证、小测继续沉淀、学习数据可被 Agent 调用”这条学习闭环尽量接稳。目录我在项目中的分工定位学习专项在系统里的作用错题本让错误变成可继续利用的数据复习推荐让错题回到每日学习计划题集与小测把练习、判分和再练接起来基础跟课上下文为课堂能力链路提供后端底座OCR 与文件安全补齐截图题和图片资源链路学习专项接口支撑减少联调时的猜测Agent 工具层让智能体可以查证和行动后期调试与缺陷收口收获与不足总结一、我在项目中的分工定位在项目最终分工里我的定位是server 端学习专项业务与 Agent 工具层的第一责任人。这个分工对应的代码范围比较集中错题本模块api/v1/wrong_book.py、services/wrong_book.py复习推荐模块api/v1/review.py、services/review.py题集与小测模块api/v1/quiz.py、services/quiz.py、question_bank.py、question_import.py、question_dedup.py基础跟课上下文api/v1/course_context.py、services/course_context.py、course_note.pyOCR 与文件安全api/v1/ocr.py、services/ocr.py、api/v1/files.py学习专项接口支撑api/v1/learning_support.py、services/learning_support.pyAgent 学习域工具层agents/react/tools/read_tools.py、write_tools.py、activity_tools.py中与学习域相关的工具如果从产品链路看我负责的部分主要位于“学习闭环”的中段和后段。用户在聊天、截图答疑或基础跟课上下文中产生学习行为后端需要把这些行为沉淀成错题、复习任务、小测记录和可消费的学习证据再提供给计划、报告以及 Agent 调用。所以我后面写代码时一直在盯一个问题这些模块不能只是各自能跑还要真的能接到一起。二、学习专项在系统里的作用“研途灵伴”不是一个普通聊天软件它的主线是考研学习陪伴。对用户来说一次完整的学习闭环大概是这样的用户在学习或答疑中遇到问题系统把问题沉淀为错题错题生成后续复习任务复习任务进入今日计划用户通过小测或再练验证掌握情况系统根据结果继续更新错题、复习和学习状态Agent 在对话中读取这些事实再给出更贴近个人状态的建议。我负责的模块基本都围绕这条链路展开。最开始做错题本和题集小测时我更关注接口本身能不能写通。后来到了联调阶段我明显感觉到后端真正麻烦的地方不是“新增一条记录”而是这条记录后面还能不能继续被复习、计划、报告和 Agent 稳定使用。比如一条错题如果只存题干和答案它最多只能用来展示如果它还包含知识点、图片、错误次数、掌握状态和来源记录它就可以继续参与复习推荐、定向小测、学习状态分析和 Agent 工具调用。这个区别很关键也决定了我后面实现学习专项时的思路尽量让学习行为沉淀成后续可利用的数据而不是一次性流水。三、错题本让错误变成可继续利用的数据错题本是我最早负责的核心模块之一。这部分主要覆盖多来源错题写入、同题去重合并、标签和知识点维护、查询与状态更新、错题再练、错题图片展示以及高频错点和状态中心相关的数据支撑。我一开始最关注的是“同一道题反复做错”这个场景。如果用户重复做错同一道题系统不应该在错题本里不断新增很多条相似记录。这样页面会乱统计也会失真。所以我在错题写入时处理了同题合并逻辑把重复错误沉淀到同一条错题记录上并更新错误次数、重复错误标记和相关标签。这块后来也经历了几轮修复。比如错题详情里的标签和知识点可能重复展示错题图片在列表或详情里缺失错题再练按钮只记录了意图但没有真正进入小测。这些问题不是那种“接口直接不可用”的大问题但会影响用户能不能把错题继续拿来复习。后期我把错题再练补成了一条更完整的链路从错题本进入小测页自动启动对应错题的答题会话如果能回溯原题就复用题库原题如果原题缺失就从错题记录补录为可练题目并保留来源标记。这样错题本不再只是“看错题”而是真的能回到练习环节。四、复习推荐让错题回到每日学习计划复习推荐模块的目标是让错题不要停在历史记录里而是能按时间重新推回用户面前。这部分我主要处理了遗忘曲线分级间隔、复习任务自动生成、到期复习查询、掌握度更新与回写、复习任务向日计划镜像插入以及和错题本、计划模块之间的联动修复。这里最容易出问题的地方是复习任务和日计划之间的关系。复习任务本身属于 review 模块但用户每天真正看到的是今日计划。如果复习任务已经生成却没有进入计划用户可能根本不会接触到它如果计划里只是复制一段文本又没有和真实复习任务关联完成状态也没法回写。所以我在实现时重点处理了复习任务向日计划的镜像插入。这样复习任务既能保留自己的业务状态也能在今日计划里被用户看到和执行。这块也让我对“模块边界”有了更实际的理解。复习模块不能直接变成计划模块但它必须给计划模块提供稳定的数据和动作入口。两边要分开也要接得上。五、题集与小测把练习、判分和再练接起来题集与小测模块是学习闭环里的“再验证”环节。这部分包括题集筛选与题目抽取、小测创建与提交、客观题规则判分、主观题智能判分与降级处理、题库导入与去重、小测结果回看、错题加入错题本以及错题来源的小测再练。小测模块里我最在意的是结果一致性。一场小测应该只读取属于它自己的作答记录不能串到其他会话用户重复提交时后端也不能重复写入或产生不一致结果。所以我在这部分做了会话级作答映射和重复提交保护让“小测结果”和“小测会话”之间保持明确关系。主观题判分也是这块比较典型的技术点。模型可用时当然希望它能给出更接近人工判断的结果但模型返回不稳定时系统不能直接崩掉。所以我给主观题判分补了 JSON 解析容错和降级策略尽量保证模型输出不完全理想时系统仍然能给出相对稳定、可解释的结果。后期联调时小测模块还暴露过几个真实使用问题错题再练跳转后 422、题目图片缺失、自动启动重复触发。最后我通过传递wrong_question_ids、补齐images字段、增加自动启动防重保护等方式把这些问题收掉。这些修复让我意识到很多问题只有走到真实页面链路才会暴露。后端接口单独测通过是一层前端点击之后能不能进入正确状态是另一层。六、基础跟课上下文为课堂能力链路提供后端底座跟课相关能力在项目里后来扩展得比较多包括窗口选择、屏幕追踪、实时字幕、多窗口体验、课堂记忆生成和据课出题等。这里需要说清楚分工边界前端侧的跟课采集、字幕层和多窗口交互由吕明翰完成更高级的课堂记忆生成和据课出题后端也主要由吕明翰负责我负责的是基础后端上下文部分也就是让后端能接住桌面端传来的跟课数据整理成后续服务可以消费的上下文。具体处理的内容跟课会话开始与结束管理关键帧接收与 OCR 结果入库相邻帧 OCR 文本去重避免静止画面产生大量冗余内容滚动上下文缓冲为问答服务提供可消费的课程上下文课堂笔记写入与帧关联end接口的幂等处理与后续课堂记忆链路的触发衔接。这里最关键的不是”上传图片”本身而是会话语义。如果同一个用户同时存在不同学习会话、不同窗口标题、不同跟踪模式后端不能随便把它们合到一个 active 会话里——否则接口表面看起来还在工作但上下文已经被污染了。我把会话复用条件收紧为同一用户、同一学习会话、同一窗口标题、同一跟踪模式四者都一致才允许复用否则宁可返回冲突也不让上下文悄悄混在一起。在课堂记忆相关部分我的原则是守住边界基础跟课上下文服务只负责采集和缓冲过程数据不越过边界去包办课堂记忆的结构化生成——跟课结束时只做触发衔接。这样如果后续记忆生成策略调整不会影响已有的采集链路也给更高级的跟课能力留出了清晰接口。七、OCR 与文件安全补齐截图题和图片资源链路OCR 与文件安全看起来是支撑模块但它对截图答疑、错题图片和小测图片都很重要。这部分主要处理截图 OCR 封装、OCR 失败降级、上传文件按用户归属分目录存储、文件下载归属校验以及错题、小测、课堂关键帧等图片资源的安全访问。这里我比较关注的是“图片资源不能只在本地路径里能看到”。桌面端页面真正展示图片时需要通过受保护资源接口加载。如果后端列表响应没有返回图片字段或者前端没有走受保护图片加载逻辑用户就会看到文字题干却看不到原题截图。后期我修复错题图片和小测图片问题时基本就是围绕这条链路排查后端 Schema 是否返回images前端类型是否接住页面是否渲染图片 URL 是否走受保护资源加载。这类问题看起来细但对学习产品很实际。很多截图题没有图片就无法复习错题本和小测也会失去原题语境。八、学习专项接口支撑减少联调时的猜测学习专项接口支撑是我后面补的一层“连接件”。当时错题、复习、计划、状态这些服务都已经各自具备能力但前端学习页真正接入时会遇到两个问题页面要从很多不同接口拼数据聊天动作执行成功后前端不知道应该刷新哪些页面或模块。所以我补了 learning-support 相关接口包括学习概览聚合、动作目录以及聊天动作执行结果里的result_payload和refresh_targets。这部分我写得比较克制没有在聚合层重写业务规则而是复用已有 service把计划、错题、复习、状态这些数据整理成前端能直接消费的结构。这层在联调阶段的价值比较明显。比如用户在聊天里点击“加入错题本”或“安排复习”之后前端不用猜到底该刷新错题页、复习页、计划页还是状态面板后端可以明确返回刷新目标。这也是我对接口层工作的一点体会接口不是把数据库字段吐出去就结束了它还要减少协作里的不确定性。九、Agent 工具层让智能体可以查证和行动项目后期 Agent 层从原来的编排路由扩展成了 ReAct 工具调用体系。最终分工里Agent 层由三人分担郝子旭负责运行时主循环和工具注册护栏runtime.py、registry.py我负责学习域工具层和学习域数据源吕明翰负责前端会话与工具步骤展示。我负责的不是 Agent 运行时本身而是让 Agent 在学习场景里有真实工具可用。当用户问”我今天有哪些复习””帮我把这道题加入错题本””根据我的薄弱点安排一下练习”Agent 不能靠模型编一段回答而应该能调用工具、读取真实数据、写回结果。工具清单我在agents/react/tools/的三个工具文件里实现了学习域相关的全部工具读工具read_tools.py学习域部分查询用户错题列表支持按知识点、标签、掌握状态筛选查询到期复习任务查询题集和小测历史记录查询跟课上下文和课堂记忆摘要查询学习状态与薄弱知识点写工具write_tools.py学习域部分将题目写入错题本调用wrong_bookservice含去重逻辑安排复习任务调用reviewservice含遗忘曲线分级创建定向小测按知识点或错题 ID 抽题写工具统一携带refresh_targets告知前端哪些页面需要刷新活动工具activity_tools.py学习域部分启动一场针对指定错题或知识点的小测会话以卡片形式呈现课堂记忆摘要触发学习状态快照更新工具设计原则在工具层里我重点关注几件事读工具提供可信事实不让 Agent 凭空猜学习数据写工具必须走既有 service不绕过业务规则保持和前端页面直接操作的一致性写入后携带refresh_targets让前端能精确刷新受影响的页面而不是全页重载工具返回区分两层给模型看的 observation文字事实摘要和给前端展示的 cards/actions结构化卡片与可点击动作工具边界对齐真实模块学习域工具只调用学习域 service不跨越到计划、情绪等其他域。这部分让我对 Agent 的理解变得更具体。真正有用的 Agent不是回答里多写几句”我会帮你”而是它能在合适的时候查数据、调用工具、写回结果并且这些动作受系统规则约束。我的工作就是给它提供学习域里那部分可信的数据源和动作入口。十、后期调试与缺陷收口除了模块实现我后期还承担了一部分调试、缺陷排查和兼容修复工作。比较典型的包括合并冲突处理错题再练链路修复标签去重小测图片展示错题图片展示饮食记录体验修复中的部分联动问题前后端字段不一致排查学习域全链路回归业务语义问题沉淀。其中合并冲突处理给我的印象比较深。那一轮不是简单地选择某一边代码而是要确认 master 基线、保留已有主链路再把分叉里的有效能力兼容合并回来。最后还要跑全量回归确认没有把计划、错题、小测、情绪等已有链路带坏。后期这些修复让我感受到项目越接近交付越不能只看“我这个模块有没有实现”。很多问题发生在模块交界处字段有没有对齐、路由有没有挂上、页面跳转有没有带参数、状态更新后页面有没有刷新。这些细节不一定最显眼但它们决定了项目最终是不是能被顺畅演示和使用。十一、收获与不足这次项目对我最大的锻炼是让我更具体地理解了后端业务模块和系统链路之间的关系。以前我写一个模块容易把重点放在接口是否能跑通。但这次负责学习专项以后我会更习惯去想这条数据后面谁会用重复提交会不会写脏状态更新有没有回写页面刷新目标有没有告诉前端Agent 调工具时能不能拿到可信事实这次写入有没有绕开已有 service这些问题一开始会让开发变慢但后面联调时会省很多事。当然我也有一些不足。第一早期有些接口和公共协议同步得不够及时。代码里已经支持了新字段但文档没有立刻跟上后面联调时容易让别人不确定是否可以依赖。第二部分页面真实使用问题是到后期联调才暴露出来的。比如错题再练跳转、小测图片、自动启动防重这些如果更早按真实用户路径做端到端验证应该能提前发现。第三我对 Agent 工具层的理解也是逐步形成的。刚开始更关注工具能不能调用后面才更重视工具的安全边界、刷新目标、写工具副作用和前端展示之间的关系。对我来说这次项目最有价值的地方就在这里它不是只让我写完功能而是让我经历了从实现、联调、返修到交付收口的完整过程。十二、总结如果用一句话总结我在”研途灵伴”里的工作我会说我负责把学习过程中的错题、复习、小测、基础跟课上下文和图片资源沉淀成稳定的后端能力并把这些能力接入学习闭环和 Agent 工具层。从最早的错题本和题集小测到复习推荐、基础跟课上下文、OCR 文件安全再到学习专项接口和 Agent 工具层我做的很多工作都不只是单个接口而是在处理”学习数据如何继续被使用”这个问题。这次项目让我更清楚地认识到后端开发并不是把 CRUD 写完就结束。真正影响系统质量的往往是去重、幂等、状态同步、模块边界、协议口径和联调体验这些细节。现在回头看我负责的模块已经从”能记录学习行为”逐步走到了”能支撑学习闭环和智能体调用”。这也是我在这个项目中最有成就感的一点。