教育AI, RAG, vLLM, 私有化部署, 知识图谱
前言
去年秋天,我们团队接手了新疆一所高校的AI助教助学平台建设项目。这篇文章解决的是:在边疆地区网络条件参差、多语言教学场景复杂的情况下,如何把AI助教从PPT概念落成能跑起来的系统。适合正在做教育信息化、智慧校园的开发和产品同学看。你能收获一套8模块的架构拆解、几个真实踩过的坑,以及可直接参考的代码片段。
先说结论:这活儿比想象中难。不是模型调不通,是场景太碎。
问题背景
这所高校有汉族、维吾尔族、哈萨克族等多个民族的学生,部分课程是双语授课。教务处给的需求清单列了8件事:AI助教备课、智能学伴答疑、作业批改评价、个性化学习路径、知识图谱导学、口语/语言伴练、学情画像预警、论文/报告辅助评审。
听起来像8个独立功能,其实底层是同一套东西——知识库、用户画像、大模型推理、业务编排。如果按8个系统分别建,运维能累死人。
我们当时面临三个硬约束。第一,学校机房只有4张A100,还得跟其他科研任务抢。第二,部分学生宿舍网络延迟高,高峰期丢包率能到8%左右。第三,维吾尔语、哈萨克语的语料稀缺,直接上通用模型效果很差。
一位教务处的老师跟我说了句实在话:“我们不指望AI能替代老师,能帮老师省下30%的重复劳动就行。”这句话定了整个项目的基调。
原理:8件套怎么收敛成一套架构
我们的思路是“一个中台+八个场景插件”。中台负责模型路由、知识检索、用户画像、日志审计;八个模块只做业务逻辑和Prompt编排。
整体分层是这样的:
┌─────────────────────────────────────────────┐ │ 接入层:Web / 小程序 / 教室大屏 / 钉钉 │ ├─────────────────────────────────────────────┤ │ 场景层:8个AI助教模块(插件化) │ ├─────────────────────────────────────────────┤ │ 中台层:模型路由 | RAG检索 | 画像引擎 │ ├─────────────────────────────────────────────┤ │ 数据层:向量库 | 图数据库 | MySQL | MinIO │ ├─────────────────────────────────────────────┤ │ 基础设施:GPU调度 | 缓存 | 消息队列 │ └─────────────────────────────────────────────┘核心是RAG(检索增强生成)。老师的课件、历年试卷、教材PDF全部切片入库,学生提问时先检索再生成,避免模型胡说。
知识图谱那块单独用了图数据库。课程知识点做成节点,先修关系做成边,导学的时候按拓扑排序推荐学习顺序。
学情画像预警是定时任务,每天凌晨跑批,把学生的作业成绩、答疑频次、视频观看时长算成特征向量,低于阈值就推送给辅导员。
实操:几个关键模块的实现
1. RAG检索的混合召回
纯向量检索在专业术语上容易翻车。我们用了BM25+向量的混合召回,再加重排序。
fromrank_bm25importBM25Okapiimportnumpyasnpdefhybrid_retrieve(query,vector_db,bm25_index,top_k=5):# 向量召回vec_results=vector_db.search(query,top_k=top_k*2)# BM25召回tokenized=query.split()bm25_scores=bm25_index.get_scores(tokenized)bm25_top=np.argsort(bm25_scores)[-top_k*2:]# 简单加权融合,权重是调出来的merged={}fordocinvec_results:merged[doc.id]=merged.get(doc.id,0)+doc.score*0.6foridxinbm25_top:doc_id=bm25_index.doc_ids[idx]merged[doc_id]=merged.get(doc_id,0)+bm25_scores[idx]*0.4ranked=sorted(merged.items(),key=lambdax:x[1],reverse=True)returnranked[:top_k]权重0.6和0.4不是拍脑袋,是拿200条真实学生提问测出来的。
2. 作业批改的评分Prompt
作业批改最怕模型给分飘忽。我们的做法是让模型先输出评分维度,再给分,最后给评语。
GRADING_PROMPT=""" 你是课程助教。请按以下步骤批改作业: 1. 逐条列出参考答案的得分点 2. 对照学生答案,标记命中/遗漏/错误 3. 按得分点给分,总分不超过{max_score} 4. 输出JSON:{{"score": int, "hits": [], "misses": [], "comment": ""}} 参考答案:{reference} 学生答案:{student_answer} """结构化输出是关键,不然后面没法入库统计。
3. 口语伴练的流式处理
口语这块延迟要求高。我们用WebSocket做流式ASR,识别完立刻送模型评分,边识别边反馈。
constws=newWebSocket('wss://api.school.edu/ai/speaking');ws.onopen=()=>{ws.send(JSON.stringify({action:'start',lang:'ug'}));};ws.onmessage=(e)=>{constdata=JSON.parse(e.data);if(data.type==='partial'){renderSubtitle(data.text);}elseif(data.type==='score'){showPronunciationScore(data.accuracy,data.fluency);}};维吾尔语的ASR我们用了开源模型微调,识别率从最初的62%提到了81%左右。剩下的19%主要卡在方言口音上。
踩坑记录
坑一:向量库选型反复横跳。一开始用Milvus,部署在学校的K8s上,结果运维不熟,集群三天两头挂。后来换成PGVector,直接跑在已有的PostgreSQL上,虽然性能差点,但稳定压倒一切。
坑二:多语言Prompt串味。同一个Prompt模板,中文问效果很好,维吾尔语问就答非所问。后来给每种语言单独维护了Prompt模板,工作量翻倍但效果立竿见影。
坑三:学情预警误报。最初阈值设得太敏感,有个学生因为生病请假一周,系统连推了5条预警给辅导员。后来加了“连续异常”判断,单次波动不触发。
坑四:GPU抢占。科研任务一跑,推理服务就排队。我们最后做了个简单的优先级队列,教学请求优先级高于科研批处理,用Redis的Sorted Set实现,几十行代码搞定。
坑五:论文辅助评审的合规问题。这个模块最敏感。我们只做格式检查、引用规范、逻辑连贯性提示,不碰学术观点评价。评审结果只给老师看,不直接给学生。
总结
这套平台上线跑了大概半年,日活从最初的200多涨到1800左右。老师用得最多的是备课和作业批改,学生用得最多的是答疑和口语伴练。
如果让我重新做一遍,我会把知识图谱和学情画像合并成一个数据底座,少建一套图数据库。另外,多语言场景一定要在项目初期就做语料评估,别等到开发中期才发现数据不够。
教育AI这事,技术只占三成,剩下七成是对教学场景的理解。想了解某个模块的具体实现,欢迎在评论区聊聊。