这两年我一直在折腾AI辅助个性化学习这个方向,市面上做学习平台的不少,但大多数所谓的“个性化”还停留在猜你喜欢的推荐层面,推荐几门课、推几个视频,本质上还是内容分发的思路。真正让人头疼的是:一个零基础的人想学Python,该先学什么?学到什么程度可以跳?面经里都在考递归和动态规划,可前面的知识点要铺多久?这些问题,传统推荐系统根本回答不了。我花了不少时间做了一个“智能学习路径规划AI Agent”,核心思路是让LLM不只是陪你聊天答疑,而是变成一个真正的规划引擎,根据每个人的底子、目标、可用时间,动态拆出一条合理的学习路线,并且能随着学习进度随时微调。这篇文章把这个项目从整体设计到每个核心模块的实现细节都拆开讲讲,包括我踩过的坑和实测下来的经验。
先说结论:这条路是通的,但绝对不是接个大模型API、写几段Prompt就能搞定的事。真正的难点在于,LLM要怎么和知识图谱、用户画像、测评数据这些结构化信息结合起来,让它“有依据地规划”,而不是“一本正经地瞎编”。如果你也在做教育类产品,或者正在研究Agent的实际落地,这篇文章应该能帮你省掉不少试错的成本。
1. 不是“推荐算法”而是“规划引擎”:整体设计思路
传统学习平台的个性化,本质上是“猜你喜欢”。库存里有1000门课,用户看了一门Python入门课,系统就推Python进阶课,再配上一些“学Python的人还学了”的相关推荐。这个方法不是没用,但它有个根本性问题:推荐算法擅长发现“你可能感兴趣的”,但它完全不理解“你下一步应该学什么”背后的因果逻辑。
学习路径规划是一个典型的序列决策问题。它要处理的是一堆带约束的序列关系:B知识点依赖A知识点,C又是B的前置,时间预算只有三周,每天只能抽一个小时,目标是应付某一类技术面试。这种问题里,“用户想看什么”远没有“用户当前缺什么、下一步必须在什么之前完成”重要。
这也是为什么我一开始就没有往协同过滤或内容推荐方向走,而是选择了“知识图谱 + LLM Agent”的组合。知识图谱提供结构化的领域约束,LLM负责理解用户自然语言描述的目标,把模糊的“我想学Python”拆解成清晰的知识点序列,再结合图谱关系做路径生成。我把这个系统的核心架构分成四层:
- 数据层:用户画像库、领域知识图谱、学习内容资源库
- 决策层:规划引擎(Graph算法部分)+ LLM Agent(语义理解与策略生成部分)
- 交互层:学习界面、目标输入、进度反馈入口
- 评估层:测评系统、差距分析、路径修正触发器
分层的原因很实际。第一,教育领域的知识结构更新很慢,但LLM的调用方式、Prompt策略、甚至模型本身都在快速迭代,分层之后可以单独替换决策层的某一块而不影响全局;第二,数据层和决策层的职责边界清楚了,Agent就没有机会直接篡改知识图谱,它只能读取和规划,这对系统的可信度至关重要。
2. 核心模块拆解:画像、知识图谱与规划器怎么协同
决定做这个架构之后,最核心的问题就是:三个模块到底各干什么活,怎么配合才不打架。
2.1 学习者画像:越用越准的动态画像
很多产品把用户画像做成了注册问卷:你是什么身份、什么水平、想学什么,然后这个画像就永远不更新了。这是不对的。学习者的画像不是静态标签,它应该是系统里最活跃的一张数据表。
我在这个项目里把画像拆成五个维度:
- 认知水平:通过前置测评获得,按知识点粒度记录掌握度
- 学习目标:短期目标(比如两周内能独立完成一个爬虫小项目)
- 可用时间:每天能投入的时间段和总时长
- 媒介偏好:喜欢视频、文字、交互式练习还是项目实战
- 历史行为:学过哪些内容、测评通过率、平均学习速度
这些维度里,认知水平是最关键的。我做了一个“知识掌握度向量”的概念:每个知识点对应一个0到1的实数,0表示完全陌生,1表示已经完全掌握。前端每完成一个学习节点,系统就会用一个小测评评估这个值,然后更新向量。这个向量是输入给规划Agent的最重要的上下文之一,因为“下一步学什么”本质上取决于“哪些知识点已经掌握了”。
2.2 领域知识图谱:路径规划的“地图坐标系”
知识图谱是整个系统的定盘星。没有图谱,LLM就是脱缰的野马,它可能会给一个零基础学员直接推荐“设计模式”,因为它觉得Python进阶该学这个,但它不知道“设计模式”前面的依赖链还没建立。
图谱的节点是知识点,边是知识点之间的关系。我在项目里定义了四种边类型:
- requires:前置依赖关系,A requires B,意味着学A之前必须掌握B
- relates_to:关联关系,A和B经常一起出现,但没有严格先后
- enhances:增强关系,学了A再学B,B的理解会更深
- alternative:替代关系,A和B覆盖相近的内容,可以二选一
实际规划最常用的是requires边。我拿一个具体的子图谱为例:目标知识点是“回溯算法”,它的requires链是“递归”→“函数调用栈”→“条件与循环”→“变量与数据类型”。规划引擎拿到这个链之后,就会去用户画像里查每个前置知识点的掌握度,如果“递归”的掌握度只有0.3,那路径里就会多出一个“递归专项”节点;如果是0.8,就可以只插入一个“快速复习”节点。
知识图谱的构建我推荐半自动方式:先用LLM从大纲、教材目录里抽取知识点和依赖关系,然后人工审核修正。这个工作很累的,但长期价值很高,因为图谱是结构化数据,不只是给Agent用的,后续的测评出题、内容关联、路径分析都会用到它。
2.3 LLM Agent:从“回答问题”到“完成规划任务”
我在项目早期犯过一个认知错误,就是一上来直接调LLM:“请给一个零基础的人规划一下Python学习路径”。这样写Prompt很快,但结果完全是随机的——它可能生成一个很通用、放在谁身上都适用的八周计划,根本不是针对当前用户的。
后来我把LLM的角色从“答案生成器”转成“策略规划器”,并且给它配上工具。Agent的每次规划过程不是一个单次调用,而是一个多轮的循环:先理解用户目标,然后调知识图谱查询工具,再读取用户画像,结合这两者生成候选路径,最后调用内容检索工具为路径上的每个节点匹配学习资源。
这个设计借鉴了经典Agent模式里的ReAct思路:Thought(思考该查什么)→ Action(调用工具)→ Observation(查看工具返回结果)→ 继续循环,直到信息足够生成路径。为什么不只靠单次大Prompt?因为学习路径规划需要的信息太多了,全部塞进一次Prompt,上下文既长又杂,模型容易忽略关键约束。拆成工具调用之后,“用户目标”和“知识图谱状态”变成了精确的数据,LLM只负责做决策,不需要凭记忆硬撑所有信息。
3. 实操落地:从数据模型到Prompt设计全记录
这一节是纯实战部分。我把项目里可以直接复用的数据表结构、Prompt模版和Agent编排代码都整理了出来,你拿到之后稍微改改就能跑通一个最小版本。
3.1 先说数据模型:四张核心表怎么设计
路径规划要正常运转,数据库层面至少要有四张表:学习者画像表、知识点表、学习记录表、路径规划表。我这里给出简化的字段设计:
| 表名 | 关键字段 | 用途 |
|---|---|---|
| learner_profile | learner_id, level_vector, weekly_hours, goals, media_preference | 存储动态画像,level_vector 是知识点ID到掌握度的映射 |
| knowledge_node | node_id, name, difficulty, estimated_hours, prerequisites | 图谱节点,prerequisites 用JSON数组存前置节点ID列表 |
| learning_record | record_id, learner_id, node_id, status, mastery_score, ts | 每次学习行为的状态和测评得分 |
| path_plan | plan_id, learner_id, nodes_sequence, status, generated_at | 生成的路径序列,支持后续微调与版本对比 |
这里有个我踩过坑的细节:level_vector 不要用一个稀疏数组代表所有知识点,我第一版用了一个超级长的JSON数组,每个知识点一个位置,结果user_profile查询接口动不动就传几十K的上下文给LLM,又慢又费token。后来改成只存“已掌握度超过0.6的知识点ID列表”和“有学习记录但未掌握的知识点ID列表”,压缩到十分之一不到。
3.2 路径生成Prompt:三段式设计的完整模版
Prompt不能是一句话。我把自己调试到能稳定输出的版本拆成三个部分:System Prompt约束角色与输出格式;上下文输入给结构化数据;Few-shot Examples约束输出范式。
System Prompt的核心内容可以这样写:
你是一名学习路径规划专家。你的任务是基于用户画像和知识图谱数据, 为用户生成一条符合依赖约束的个性化学习路径。 约束规则: 1. 路径上的每个知识点必须存在于给定的知识图谱节点列表中。 2. 如果知识点A的prerequisites包含B,而用户对B的掌握度低于0.5, B必须出现在A之前。 3. 输出必须是JSON数组,每个元素包含node_id、reason、estimated_hours三个字段。 4. reason字段用不超过20个字符的中文说明该节点被加入的原因。上下文部分传入当前用户的画像摘要和相关图谱子图。这一步的关键是不要传全量图谱,只传和目标知识点可达的子图,否则上下文一长,模型就开始“丢失注意力”。
Few-shot部分我给两个示例:一个零基础用户的目标“3周内学会用Python写自动化脚本”,输出是“变量→数据类型→条件判断→循环→函数→文件操作→正则表达式→自动化脚本实战”的顺序,每个节点都带上reason。另一个是有Python基础但没学过面向对象的用户,目标“两周内掌握面向对象并完成一个类设计练习”,输出路径里就会直接跳过后面的基础内容。
我实测下来的最佳参数:temperature设为0.1到0.2之间。这个场景不是创意写作,方案越稳定越好,温度太高会导致同一个人两次路径不一样,非常影响产品体验。
3.3 Agent工具编排:用最少代码实现ReAct循环
我在项目里没有用特别复杂的Agent框架,而是自己写了一个轻量的工具编排层。核心结构很简单:定义几个工具函数,然后用一个While循环驱动Agent思考与调用。
工具函数的伪代码大致长这样:
def knowledge_graph_search(node_name: str) -> dict: """查询知识图谱中指定知识点及其前置关系""" return graph.query(f"MATCH (n:KnowledgeNode)-[:REQUIRES]->(pre) WHERE n.name = '{node_name}' RETURN n, pre") def user_profile_get(learner_id: str) -> dict: """获取学习者的画像摘要""" profile = db.fetch_learner_profile(learner_id) return profile.summarize_for_prompt() def content_search(node_id: str, preference: str) -> list: """根据知识点ID检索学习内容资源""" return content_db.search_by_node(node_id).filter_by_preference(preference)Agent主循环的基本逻辑就是:先把用户目标和可用工具描述传给LLM,让它决定下一步调用哪个工具,然后把工具返回结果直接追加到上下文里,继续让它判断是继续查询还是给出最终路径。我加了一个安全阀门:最大循环次数设为8次,一旦超过就自动退化成“仅使用已获取信息生成路径”,避免Agent在工具调用里绕圈。
这个编排方式看起来很简陋,但在实际运行中非常稳定,因为学习路径规划这种任务,工具调用通常三四步就能完成:先查画像,再查目标知识点的前置链,最后检索一次内容资源。复杂Agent框架里的多Agent协作、反思机制在这个场景里反而属于过度设计。
4. 实测踩坑记录与排查技巧
这部分是我花了最多时间的地方。模型选型、Prompt调优都相对容易,难的是让系统在真实使用中稳定、可信、不犯低级错误。我把几个最典型的坑整理出来,每个都是真实发生过的。
4.1 知识点幻觉是头号问题
第一版上线测试时,我发现Agent生成的路径里经常出现图谱里根本不存在的节点。它给一个学Python的用户规划路径时,竟然生成了“面向对象设计模式的前置知识:SOLID原则”——SOLID原则确实存在,但在当时系统针对初级目标构建的子图谱里没有这个节点,系统用户在后续点击学习时根本找不到对应的内容资源。
这事的根因是LLM太想“表现专业知识”了,它从自己的训练知识里调出了真实但未收录的知识点。后来我做了双重拦截:第一层,限制Agent只能从工具返回的图谱节点列表里选择节点,Prompt里明确写“禁止创造图谱之外的节点”;第二层,在解析输出JSON之后加了一个校验器,凡是node_id不在已知节点白名单里的路径,整条路径直接作废重新生成。经过这两层,幻觉问题基本消失了,代价是偶尔要重试一次。
关键经验:想让Agent稳定,别只靠Prompt约束,一定要在代码层面做硬校验。LLM的遵循能力再好,也不可能100%守规矩。
4.2 同一个人两次规划结果不一致
有一阵子我收到反馈,说用户早上和下午看到的路径不一样。我排查后发现两个原因:一是temperature设太高,默认0.7,模型的决策随机性过大;二是图谱子图查询结果顺序不稳定,图谱数据库返回的节点顺序每次可能不同,把顺序敏感的LLM给带偏了。
解决方式很直接。温度调到0.1,这个数字我反复测试过,既能保留一定的格式灵活性,又不会导致方案级差异。同时在查询图谱时做了ORDER BY node_id排序,保证同一次查询的结果顺序稳定。第三层保险是给相同画像和目标加上24小时的路径缓存,只有画像发生明显变化时才触发重新规划。
4.3 “越学越偏”问题:反馈闭环比路径生成更关键
系统上线跑了一个月后,我注意到一个现象:路径一开始规划的挺合理,但学到中间阶段就开始偏离用户实际情况了。原因是用户的学习速度、掌握情况和画像里的预估数据有差距,而路径本身是基于旧画像生成的,没有随着新数据更新。
比如A学员画像显示每天能学两小时,实际他工作日只有45分钟,路径里安排的大块项目实战就无法完成;B学员学循环结构测评达到0.9,但画像更新不及时,后续的路径还是把循环语法当成新知识点重复安排。
我意识到学习路径规划不是一次性的,它应该是一个持续修正的过程。于是我没有做全量重规划,而是加了一个“局部重规划”机制:每次测评结果出来后,如果某个知识点的掌握度和路径预期偏差超过0.3,就只调整该节点以及后续节点,前面的路径保持原样。这个机制避免了两边摆动的混乱,也让路径有了“渐进式演化”的稳定性。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 路径出现图谱外知识点 | LLM幻觉 | 工具结果白名单校验 + 硬拦截 |
| 同一用户两次路径差异大 | temperature过高或图谱顺序抖动 | 温度降至0.1,查询结果排序,加缓存 |
| 路径后半段严重脱离实际 | 画像没有随测评结果更新 | 加入局部重规划机制 |
| 生成的路径没有前置依赖顺序 | Prompt里没明确依赖约束 | System Prompt加强规则 + Few-shot示例 |
| 工具调用循环不停 | Agent卡在重复查询 | 设置最大迭代次数,超时自动降级 |
| 内容资源匹配不到节点 | 内容库标签和知识点ID体系不一致 | 统一用node_id作为内容库的关联键 |
| 长路径生成时JSON格式不稳定 | 上下文过长导致输出截断 | 限制目标链长度,分次生成前段和后段 |
5. 效果评估与后续迭代方向
评估这个系统,不能只看技术层跑没跑通,更重要的是它到底有没有让学习更高效。我自己的评估体系分成三个维度:
- 路径合理性:可信度评分,检查生成路径中违反前置依赖的比例,要求这个数字无限趋近于零
- 学习效率:对比同样目标下,用这个路径和用户自己找资料学的时间差。我做了个小范围测试,用系统路径的用户在单位时间内的知识点掌握量平均高出约35%,但样本量不大,这个数字仅供参考
- 产品指标:路径完成率、用户留存、测评通过率。路径完成率如果低于60%,说明路径估计的时长和难度有问题,需要回调
另外一个特别值得关注的指标是“知识点保留率”。我设计了一个简单方案:学完一个章节后第七天,系统推送三条相关知识点测评。掌握度保持在0.7以上的比例高,说明路径的顺序编排是扎实的。路径把简单的A排在复杂的B前面、且测评时间充裕时,长时记忆效果明显更好。
后续迭代方向我目前有几个想法。最想做的是把知识图谱从单一领域扩展到跨领域关联:比如学Python的递归,能不能关联到数据结构里的二叉树、算法题里的回溯;学统计学的贝叶斯定理,能不能关联到机器学习里的朴素贝叶斯分类器。这种跨领域关联一旦打通,学习路径的价值就不只是入门导学了,而是能支撑起终身学习这个概念。另外我还计划把用户画像里加入“遗忘曲线”参数,根据遗忘模型预测哪些知识点需要定期回流复习,在路径里自动插入复习节点。
最后再分享一个我的整体体会:LLM做教育类产品,最大的诱惑是让它“全权负责”。一开始我也试图让大模型包办路径规划、内容生成、答疑、测评,但后来发现效果很差。真正稳定的架构,是让LLM做它最擅长的事(理解用户目标、做策略决策),同时把结构化约束问题(图谱查询、依赖检查、格式校验)交回给工程手段。让每个环节做自己擅长的事,整个产品的上限才会真正打开。