
最近几年AI 学习工具层出不穷但用过的人大概率都有一种相似的感觉它很聪明却总是“记不住你”。上周问过的一个薄弱知识点这周再问它像第一次见面一样从头讲一遍。你反复出错的同类型题目它也不会主动察觉更不会帮你调整练习方向。问题出在哪里很多人归咎于大模型不够强但更关键的原因是产品架构本身没有为“成长”设计。港大最近开源的一套 AI 学习系统讨论度之所以高恰恰因为它在架构上做了一个关键选择不是简单的“大模型 聊天窗口”而是采用原生 Agent 架构把知识库和错题集设计成 Agent 的长期记忆。从公开材料反映的设计思路看这套系统更像一个“数字家教”而不只是一个“AI 答疑机器人”。它记住你的知识薄弱点根据错题数据调整讲解策略让知识库与错题集伴随整个学习过程不断迭代。这篇文章不打算重复功能介绍而是从技术角度拆解三件事原生 Agent 架构如何编排学习流程、知识库 RAG 如何实现知识沉淀、错题集如何作为长期记忆驱动成长。另外我会给出一个可运行的最小原型代码覆盖数据库设计、检索工具、Agent 编排、Docker 部署和验证方法尽量让读完之后不是只记住概念而是可以动手复刻。1. 这篇文章真正要解决的问题先给一个明确判断市面上一大批 AI 学习工具的瓶颈不是模型能力而是“无状态”。所谓无状态指的是每一次对话都是独立的系统不记得学生上个月错在哪道题、对哪个知识点理解偏了、适合哪种讲解风格。大模型本身有上下文窗口但上下文窗口不等于记忆窗口之外全是空白。于是用户每次都要重新描述自己的水平AI 每次都要从头开始试探。这套港大开源系统的设计重点正好回应了下面三个问题。1.1 为什么大多数 AI 学习工具像“玩具”很多产品把大模型接入一个学习 App配上提示词就可以做题目问答。体验上单次问答效果可能不错但一旦考察“持续学习”这个维度就会露馅。学生没有动态能力画像题库没有关联到知识点错题数据没有回流到下一次讲解策略中。本质上它还是一本会说话的教科书不是一个会观察、会调整、会记录的老师。这也是“AI 教育”落地多年却很难形成复利效应的核心原因系统没有随着使用过程变得更懂用户。1.2 Agent、知识库、错题集三者为什么必须在一起如果你接触过 RAG 应用可能会觉得知识库好理解错题集不就是一张数据表吗但这里的关键在于Agent 架构把三者连接成了闭环。Agent 负责拆解学习目标、决定调用哪些工具知识库负责提供学科内容解决“讲什么”错题集负责提供个人学情解决“针对谁讲”。缺少 Agent知识库和错题集只是两个被动接口缺少错题集知识库再强也无法个性化缺少知识库错题集只能记录错误无法给学生补充正确的讲解材料。三者协同才称得上“伴随成长”。1.3 什么样的读者最应该读这篇文章如果你正在做教育类 AI 产品这篇文章可以帮助你理清学习 Agent 的核心数据模型和流程编排思路。如果你在做 Agent RAG 应用开发这篇文章里的记忆分层、工具注册、状态循环也通用可以迁移到企业知识助手、培训系统等场景。即使你只是对开源 AI 项目感兴趣这篇文章也能给你一个判断标准一个 AI 系统是否值得学习不要只看它接入了什么模型而要看它有没有为自己的“记忆”搭好架构。2. 原生 Agent 架构学习系统为什么需要“计划-执行-反思”在 CSDN 语境下Agent 并不是一个很玄的概念。简单理解Agent 是一个能够根据目标自主规划步骤、调用外部工具、观察执行结果并调整策略的程序。普通聊天机器人的流程是“用户输入 - 模型生成 - 输出”Agent 的流程是“用户目标 - 规划 - 工具调用 - 结果观察 - 再规划 - 最终输出”。学习场景天然适合 Agent因为学习本身就是一个多步骤闭环诊断、讲解、练习、反馈、复习。下面用一个对比说明传统方案和 Agent 方案的差异。维度传统问答式学习工具Agent 式学习系统学生提问直接返回答案先解析目标判断是讲解、练习还是诊断知识获取靠模型训练时记忆优先从知识库检索最新、最准确的内容个人学情每次对话都从零开始读取错题集和掌握度结合历史数据回应错误处理答错后重新生成一段解释记录错误类型更新掌握度安排后续复习多步骤任务无法自动拆解可自动规划“先诊断再讲解再出题巩固”从这套系统的设计逻辑看原生 Agent 架构的意义不是让 AI 显得更智能而是让学习流程中的每一步都有迹可循、有状态可记录。2.1 Agent 的三层记忆设计如果把 Agent 想象成一个辅导老师它的记忆至少应该分成三层会话级记忆记录当前这次对话中学生问了什么老师讲了什么最近几步的上下文。这一层用对话历史即可实现。知识库级记忆学科文档、讲义、标准答案、拓展资料。这一层不依赖单个学生的学情属于公共知识。成长级记忆学生的错题记录、知识点掌握度、复习历史、能力变化曲线。这一层是伴随学生长期更新、最能产生个性化价值的记忆。很多人做 AI 学习产品只做了第一层和第二层第三层要么没做要么只是简单存了一张错题表没有和 Agent 的决策逻辑打通。而这套系统最有借鉴意义的地方恰恰是把成长级记忆当作 Agent 的核心状态来驱动每一次对话策略。3. 知识库模块RAG 不仅是检索更是知识沉淀知识库是 AI 学习系统的基础设施。没有知识库大模型只能依靠训练数据里的知识无法覆盖新教材、新题型、校本课程等私有内容。RAG检索增强生成是目前最主流的技术路线但很多实现只是“文档切片-向量化-检索-拼接 prompt ”效果却不够理想。原因往往不是 RAG 本身不行而是把不同类型的知识都塞进了同一个向量库。3.1 知识分层的必要性这套系统在设计上比较值得学习的点是对知识做了分层处理非结构化文档教材章节、讲义、题目解析适合分块后存入向量库用于语义检索。结构化数据知识点清单、知识点之间的前置关系、题目与知识点的关联适合存入关系型数据库或图数据库用于精确查询和路径推理。个人学情数据错题记录、答题记录、掌握度评分更适合放入带事务保障的 OLTP 数据库而不是向量库。如果你把所有资料都向量化看起来“都能搜到”但实际上既丢失了知识点之间的结构关系也无法支撑类似“这个学生的薄弱知识点前置依赖是什么”这类推理需求。下面的表格给出三种存储在这类系统中的分工建议。存储类型适合的内容典型查询代表技术向量数据库讲义文档、题目解析、教材片段语义相似度召回Qdrant、Milvus、Chroma、FAISS关系数据库学生、题目、错题、掌握度、知识点主数据精确查询、统计、事务更新PostgreSQL、SQLite图数据库知识点前置/依赖关系、课程结构多跳关系路径分析Neo4j、JanusGraph3.2 标准 RAG 流程在知识库中如何落地从资料处理到用户可感知的检索增强完整链路可以拆成八个阶段文档接入支持 PDF、Word、Markdown、网页等格式。文档解析提取正文、公式、图片说明去掉页眉页脚。分块策略按标题层级和语义边界切分保证每块内容自包含。向量化使用 Embedding 模型生成向量。索引存储写入向量数据库同时保留文档元数据。查询改写Agent 会根据学生当前问题改写检索 query。召回与重排粗召回 top 50再用重排序模型选择 top 5 到 10。生成与引用大模型结合检索片段生成回答并标注引用来源。在实际项目中分块策略和重排序对回答质量的影响往往比更换大模型更明显。如果你发现 Agent 的回答经常答非所问优先排查这两个环节而不是急着换更强的模型。4. 错题集模块系统“记住学生”的关键设计很多学习产品都做错题本功能但大多数只是“收藏夹”把做错的题和正确答案存下来最多加一个解释。从信息架构上看这样无法支撑个性化教学。错题集要真正驱动学习应该成为一套结构化、可计算、随使用不断更新的学情数据。4.1 错题不能只存“题”更要存“错因”一道题做错可能有多种原因概念理解偏差、计算粗心、审题失误、方法选择错误。不同错因对应的教学策略完全不同。如果错误类型没有结构化Agent 就只能笼统地重新讲解一遍无法做到针对性辅导。合理的错题数据模型应该至少包含这些信息题目内容与正确答案学生的作答内容错误类型概念错误、方法错误、计算错误、审题错误关联的知识点 ID当前掌握度评分复习次数与最近复习时间下次复习时间。其中“掌握度”是一个动态值每次答题和复习后都需要更新。更新规则可以简单设计也可以使用艾宾浩斯遗忘曲线或更复杂的知识追踪模型。4.2 错题集与 Agent 的关系既是状态也是工具在这套系统中错题集不仅仅是一张表。Agent 在制定学习计划时会把“读取错题集”当作一个标准工具调用在一次练习结束后又会把“更新错题集”作为工具调用来执行。换句话说错题集是 Agent 的“工作记忆”在长期维度上的落盘。它既可以被查询也可以被更新还能支持 Agent 做决策。例如 Agent 收到学生请求“帮我复习一下一元二次方程”它不会直接开始讲概念而是先查询错题集看看这个学生之前在这个知识点上的错误类型再决定是重点讲概念还是多出几道变式题巩固。这个设计思路的价值在于错题集从一个静态记录工具变成了 Agent 决策回路中的核心状态源。5. 环境准备与前置条件在开始写代码之前先明确运行环境和需要准备的外部依赖。下面的版本要求以常见实践为准具体版本请结合你实际安装的版本确认本文重点演示通用思路。5.1 运行环境操作系统Linux / macOS / Windows推荐 WSL2 或 Docker DesktopPython3.10 或以上包管理工具pip 或 poetryDocker可选但推荐安装用于快速启动 PostgreSQL 和向量数据库5.2 外部依赖组件这套最小原型需要两类存储PostgreSQL存储学生、题目、错题集等结构化数据向量数据库存储知识库文档向量示例会使用 Qdrant你也可以替换为 Milvus 或 Chroma。如果只是本机开发验证PostgreSQL 也可以用 SQLite 替代但生产环境建议仍然使用 PostgreSQL因为事务和并发能力更强。5.3 模型服务Agent 的模型调用层需要支持两种能力对话生成负责规划和最终回答Embedding 生成负责把文本向量化用于知识库检索。对话生成可以接入 OpenAI 兼容接口也可以使用本地 Ollama 部署的开源模型。Embedding 推荐使用开源的中文 Embedding 模型例如 BGE 系列或 M3E具体选择以实际可达性为准。为避免强依赖外部框架本文示例不会依赖 LangChain 等重量级库而是用纯 Python 演示 Agent 的编排思路方便你理解底层逻辑后自行替换为生产框架。6. 核心流程拆解一次学习会话如何跑通理解了概念我们看一个完整的学习会话在 Agent 系统中是如何运转的。假设学生登录系统后输入“我最近一元二次方程总是算错怎么办”6.1 学习会话九步流程目标解析Agent 识别这是一个“诊断 练习 讲解”的复合请求而不是单纯的知识问答。加载学生画像从数据库读取学生基本信息、最近错题、各知识点掌握度。查询错题集调用错题集工具获取该学生在“一元二次方程”知识点上的近期错误记录。制定计划Agent 根据画像和错题数据决定先做诊断再针对错因讲解最后出同类型巩固题。调用知识库检索从向量数据库中检索“一元二次方程”相关的概念、例题和易错点。生成个性化讲解结合检索内容、学生错因和掌握度生成讲解文本并在关键位置引用知识库来源。布置诊断题根据错题集中暴露的薄弱点从题库中选择 2 到 3 道同知识点、不同难度的题目。处理反馈学生提交答案后Agent 判断正误更新该题对应知识点的掌握度。更新错题集新增或更新错题记录计算下次复习时间并把本次会话的关键信息写入长期记忆。6.2 Agent 工具注册表设计上面流程中Agent 的每一次外部交互都可以抽象成一次工具调用。工具注册表是 Agent 编排的核心常见结构如下{ tools: [ {name: student_profile.get, description: 获取学生画像与能力概览}, {name: wrong_book.recent, description: 查询学生最近错题}, {name: knowledge.search, description: 从知识库中检索学科内容}, {name: quiz.generate, description: 根据知识点和难度生成练习题}, {name: wrong_book.update, description: 更新错题记录与掌握度} ] }模型通过工具描述决定何时调用哪个工具工具执行结果再回到模型中模型基于结果生成最终回答。这也是原生 Agent 架构和传统函数调用的本质区别工具不是被固定流程顺序调用的而是由模型根据当前目标动态规划。7. 完整示例代码实现下面给出一个最小可运行的原型重点演示三类模块错题集数据库结构、知识库检索工具、Agent 编排逻辑。代码以教学演示为目标生产环境需要补充异常处理、鉴权、监控等内容。7.1 错题集数据库设计文件路径schema.sqlCREATE TABLE IF NOT EXISTS student ( id TEXT PRIMARY KEY, name TEXT NOT NULL, grade_level TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS knowledge_point ( id TEXT PRIMARY KEY, name TEXT NOT NULL, subject TEXT NOT NULL, parent_id TEXT, prerequisites TEXT, FOREIGN KEY (parent_id) REFERENCES knowledge_point(id) ); CREATE TABLE IF NOT EXISTS question ( id TEXT PRIMARY KEY, content TEXT NOT NULL, answer TEXT NOT NULL, explanation TEXT, knowledge_point_id TEXT NOT NULL REFERENCES knowledge_point(id), difficulty REAL NOT NULL DEFAULT 0.5, source TEXT ); CREATE TABLE IF NOT EXISTS wrong_book ( id TEXT PRIMARY KEY, student_id TEXT NOT NULL REFERENCES student(id), question_id TEXT NOT NULL REFERENCES question(id), student_answer TEXT NOT NULL, error_type TEXT NOT NULL, mastery REAL NOT NULL DEFAULT 0.0, review_count INTEGER NOT NULL DEFAULT 0, next_review_at TIMESTAMP, last_review_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(student_id, question_id) );这里最关键的字段是error_type和mastery。error_type可以让 Agent 判断错因mastery让 Agent 了解当前掌握程度。如果不储存这两个字段错题集就只是流水账无法支撑个性化决策。7.2 知识库检索工具封装文件路径tools/retriever.pyimport numpy as np from typing import Dict, List, Optional class SimpleVectorStore: 教学演示用简单向量检索生产环境建议替换为 Qdrant / Milvus / FAISS。 def __init__(self, dimension: int 384): self.dimension dimension self.documents: List[Dict] [] self.vectors: List[np.ndarray] [] def add_document( self, doc_id: str, text: str, embedding: List[float], metadata: Optional[Dict] None, ) - None: self.documents.append({ id: doc_id, text: text, metadata: metadata or {}, }) self.vectors.append(np.array(embedding, dtypenp.float32)) def search(self, query_embedding: List[float], top_k: int 5) - List[Dict]: if not self.vectors: return [] q np.array(query_embedding, dtypenp.float32) scores [] for idx, vec in enumerate(self.vectors): dot float(np.dot(q, vec)) norm float(np.linalg.norm(q) * np.linalg.norm(vec) 1e-8) scores.append((idx, dot / norm)) scores.sort(keylambda x: x[1], reverseTrue) return [ { doc_id: self.documents[idx][id], text: self.documents[idx][text], metadata: self.documents[idx][metadata], score: score, } for idx, score in scores[:top_k] ]这个类理解起来很简单add_document写入文档和向量search用余弦相似度召回最相关的文档。实际工程中向量化工作由 Embedding 模型完成向量存储和检索由向量数据库完成这里的实现只是为了让你在没有外部依赖时也能快速跑通逻辑。7.3 Agent 编排核心逻辑文件路径agent/learning_agent.pyimport json from typing import Callable, Dict, List class LearningAgent: 最小学习 Agent动态规划工具调用并基于工具结果生成最终回答。 def __init__(self, tools: Dict[str, Callable], student_id: str): self.tools tools self.student_id student_id self.messages: List[Dict] [] def run(self, user_input: str, model_call: Callable, max_steps: int 3) - str: plan model_call( systemself._build_system_prompt(), useruser_input, ) for step in plan.get(steps, [])[:max_steps]: tool_name step.get(tool) if tool_name not in self.tools: continue args step.get(args, {}) args[student_id] self.student_id try: result self.tools[tool_name](**args) except Exception as exc: result {error: str(exc)} self.messages.append({ role: tool, tool: tool_name, result: result, }) final_payload { user_input: user_input, tool_messages: self.messages, } return model_call( systemself._build_system_prompt(), userjson.dumps(final_payload, ensure_asciiFalse), ) def _build_system_prompt(self) - str: tool_descriptions \n.join( f- {name}: {self._describe_tool(name)} for name in self.tools ) return ( 你是一名学习助手。请根据学生问题规划工具调用步骤 再结合工具结果生成个性化回答。可用工具如下\n f{tool_descriptions} ) def _describe_tool(self, name: str) - str: descriptions { wrong_book.recent: 查询学生最近错题返回题目、错误类型、掌握度, knowledge.search: 从知识库中检索相关学科内容, quiz.generate: 根据知识点和难度生成练习题, } return descriptions.get(name, 无描述)这段代码演示了 Agent 的最小循环模型输出一个工具调用计划Agent 按计划调用工具工具结果写回消息列表最后模型基于完整上下文生成最终回答。实际生产项目中model_call可以替换为 OpenAI 客户端、Ollama 客户端或企业内部模型服务接口plan的解析方式也会更严谨建议使用结构化输出约束格式。7.4 Docker Compose 快速启动存储服务文件路径docker-compose.ymlversion: 3.8 services: postgres: image: postgres:15-alpine environment: POSTGRES_USER: learning POSTGRES_PASSWORD: learning123 POSTGRES_DB: learning_agent ports: - 5432:5432 volumes: - pg_data:/var/lib/postgresql/data qdrant: image: qdrant/qdrant:latest ports: - 6333:6333 - 6334:6334 volumes: - qdrant_data:/qdrant/storage volumes: pg_data: qdrant_data:7.5 运行与验证启动存储服务docker compose up -d检查服务状态docker compose ps预期输出中postgres和qdrant两个服务状态为Up。如果状态为Restarting先查看日志docker compose logs postgres docker compose logs qdrant初始化数据库表psql -h localhost -U learning -d learning_agent -f schema.sql运行 Agent 最小测试时可以使用下面的命令思路python -m agent.learning_agent --question 我最近一元二次方程总是算错怎么办需要说明的是由于示例中的model_call尚未接入真实模型你需要在调用前定义该函数。验证时可以先查看工具调用计划和消息记录是否符合预期再逐步接入真实模型观察最终回答质量。8. 常见问题与排查思路这一节列出 Agent 学习系统落地时最容易遇到的几个问题以及对应的排查方式。问题现象可能原因排查方式解决方案Agent 回答与知识库内容无关检索召回结果不相关打印检索到的 top_k 文本检查提问与文档片段相似度优化分块策略增加重排序环节或替换更适合中文的 Embedding 模型检索不到私有文档内容文档分块过大或过小导致语义丢失查看分块结果检查块长度和边界按标题层级分块保持每块 200 到 500 字左右并保留元数据错题集出现重复记录同一道题被多次插入检查表结构是否有唯一约束对(student_id, question_id)建立唯一索引使用INSERT ... ON CONFLICT更新掌握度长期不变化每次答题后没有触发更新逻辑检查 Agent 工具调用中是否包含wrong_book.update在完成答题反馈后强制调用更新工具并落库长对话丢失关键上下文只使用单轮模型调用查看 messages 列表是否累积了多轮状态增加记忆缓冲把最近多轮对话和错题摘要一起传给模型本地模型回答延迟高模型推理速度慢检索耗时高分别统计模型调用与检索耗时模型量化部署检索 top_k 调小增加缓存层工具调用格式不稳定模型输出的 plan 不是合法 JSON查看原始模型输出使用结构化输出或 function calling 机制避免让模型自由输出每个项目的具体表现会有差异但排查顺序建议遵循“先看数据再看调用链最后看模型输出”的原则。大部分问题并不是模型不够聪明而是数据没准备好、检索结果不对或状态没有正确传递。9. 最佳实践与工程建议把最小原型扩展为生产级系统时有几个工程问题值得提前规划。9.1 数据安全与最小权限教育数据涉及未成年人隐私必须格外谨慎。建议遵循最小权限原则Agent 只能访问当前学生自身的学习数据不能跨越学生边界读取他人数据。知识库内容可以分为公开库和班级私有库通过知识库 ID 做隔离。所有对错题集的修改都应记录操作日志并且支持人工纠偏。9.2 建立评估集而不是靠感觉调 Prompt学习场景的每次回答质量都可以用“是否针对学生错因”“是否引用准确知识点”“讲解是否清晰”等维度评估。建议准备一套固定的评估题目集覆盖概念讲解、错题诊断、巩固出题三类任务每次修改 Prompt、检索策略或模型版本后用这套评估集回归验证。没有评估集你无法判断改动是变好还是变坏。9.3 知识库和错题集都要版本化知识库内容会持续更新错题集记录的学生状态也会不断变化。生产环境建议为知识库文档增加版本号切换新版本前保留旧版本快照方便回滚。错题集更新时建议保留变更历史表即使 Agent 错误更新了掌握度也能恢复到上一版本。9.4 保留人机协同的纠偏入口无论 Agent 规划得多好都可能出现错误判断。系统应该允许学生或教师对 Agent 的建议进行反馈这道题的错因判断是否正确推荐练习的难度是否合适。这些反馈数据回流后可以成为评估 Agent 表现的重要依据也能持续改善后续决策。10. 总结与后续学习方向回到文章开头的问题为什么多数 AI 学习工具无法伴随用户成长因为它们在架构上没有为成长设计。港大这套开源系统真正的参考价值在于展示了原生 Agent 架构如何把“知识库、错题集、学习闭环”整合成一个有记忆、能迭代的系统。技术细节可以复制但这个架构思路才是内核。如果你接下来想实践建议不要一上来就追求完整产品而是先从一个最小的场景切入选择一个知识点录入少量讲义配上十几道题跑通“提问 - 检索 - 错因记录 - 下次复习引用”的闭环。再逐步加入更多知识点、更复杂的评估逻辑和更强的模型。本文给出的代码是教学级的最小演示你可以在这个基础上往两个方向深入一是把工具调用和状态管理迁移到更成熟的 Agent 框架比如 LangGraph 或自研的状态编排服务二是把错题集的掌握度更新策略从简单的规则升级为知识追踪模型让系统的个性化能力再上一个台阶。教育与 Agent 的结合还处于早期但“有记忆、会成长”的设计方向大概率是接下来值得持续投入的赛道。