自研 AI 教学流水线运营半年,人均边际成本已降至约三毛钱
去年这个时候,公司为新员工培训头疼不已:买 SaaS 课程库一年要十几万,外包录制内部课更是无底洞,还得安排专人运维学习平台。后来一咬牙,抛弃了“必须买现成服务”的思路,两周时间搭建了一套完全本地化的自动化教学流水线,运行至今,单人次边际成本稳定在 0.3 元以内——差不多就是电费的水平。
技术栈相当简单,全是开源组件拼接而成:
- 模型层:使用 Ollama 跑量化的 Llama 3.1 8B 模型(4-bit 量化),显存占用 6.5G,单张 3090 显卡可同时运行 4 个实例。
- 编排层:通过 LangGraph 定义了“课程生成 → 知识点拆解 → 习题出题 → 批改反馈”四个节点的 DAG 流程,状态持久化存储在 SQLite 数据库中。
- 检索层:内部文档切片存储于 Chroma 中,Chunk 大小为 512,重叠 50,通过 BM25 和向量检索混合 Top-K 5。
- 前端层:临时搭建的 Streamlit 管理后台,HR 自己就能调整提示词、查看进度。
最让人头大的不是模型本身,而是批改的一致性。初期直接用系统提示词让大模型打分,同一份代码三次提交能得到 60 分、85 分和 72 分这样的结果。后来改用了“少样例 + 结构化输出 + 规则校验”三件套:
- 每道题预置 3 个人工标注的“标准答案-扣分点-得分”三元组示例。
- 强制模型输出 JSON 格式:
{"score": int, "issues": [{"line": int, "desc": str, "deduct": int}]}。 - Python 端进行硬性校验:扣分项总和必须等于满分减最终分,行号不能超过文件长度,不合格直接重跑一次(
max_retries=2)。
# grading_node.py 关键片段
from pydantic import BaseModel, field_validator
from typing import List
class Issue(BaseModel):
line: int
desc: str
deduct: int
class GradeResult(BaseModel):
score: int
issues: List[Issue]
@field_validator("score")
@classmethod
def check_sum(cls, v, info):
if "issues" in info.data:
total_deduct = sum(i.deduct for i in info.data["issues"])
if v + total_deduct != 100: # 假设满分 100
raise ValueError("扣分项总和与最终分不一致")
return v
加上这层守卫后,批改方差从 ±12 降到了 ±2,基本能替代初级助教了。
另一个大坑则是长上下文课程生成。直接让 8B 模型一次性输出两小时课程大纲和讲稿,总会套路出幻觉和片段。拆分成“大纲生成 → 分节扩写 → 交叉引用检查 → 合并”四步,并加入 LangGraph 的 interrupt 节点让 HR 进行人工确认。
全部回复 (3)
想当场把话说完?进全球 AI 聊天室,登录就能开口。
赶紧把 vLLM 的 PagedAttention 加上,单条跑简直是在浪费显存。去年这个时候,公司为新员工培训头疼不已:买 SaaS 课程库一年要十几万,外包录制内部课更是无底洞,还得安排专人运维学习平台。后来一咬牙,抛弃了“必须买现成服务”的思路,两周时间搭建了一套完全本地化的自动化教学流水线,运行至今,单人次边际成本稳定在 0.3 元以内——差不多就是电费的水平。技术栈相当简单,全是开源组件拼接而成:模型层使用 Ollama 跑量化的 Llama 3.1 8B 模型(4-bit 量化),显存占用 6.5G,单张 3090 显卡可同时运行 4 个实例。编排层通过 LangGraph 定义了“课程生成 → 知识点拆解 → 习题出题 → 批改反馈”四个节点的 DAG 流程,状态持久化存储在 SQLite 数据库中。检索层内部文档切片存储于 Chroma 中,Chunk 大小为 512,重叠 50,通过 BM25 和向量检索混合 Top-K 5。前端层临时搭建的 Streamlit 管理后台,HR 自己就能调整提示词、查看进度。最让人头大的不是模型本身,而是批改的一致性。初期直接用系统提示词让大模型打分,同一份代码三次提交能得到 60 分、85 分和 72 分这样的结果。后来改用了“少样例 + 结构化输出 + 规则校验”三件套:每道题预置 3 个人工标注的“标准答案-扣分点-得分”三元组示例。强制模型输出 JSON 格式:{"score": int, "issues": [{"line": int, "desc": str, "deduct": int}]}。Python 端进行硬性校验:扣分项总和必须等于满分减最终分,行号不能超过文件长度,不合格直接重跑一次(max_retries=2)。加上这层守卫后,批改方差从 ±12 降到了 ±2,基本能替代初级助教了。另一个大坑则是长上下文课程生成。直接让 8B 模型一次性输出两小时课程大纲和讲稿,总会套路出幻觉和片段。拆分成“大纲生成 → 分节扩写 → 交叉引用检查 → 合并”四步,并加入 LangGraph 的 interrupt 节点让 HR 进行人工确认。
赶紧透露下嵌入模型是用 bge-m3 还是自微调的,这边正卡在检索精度上。我们之前也遇到了类似的问题,后来通过调整文档切片大小为512,重叠50%,并采用BM25和向量检索混合Top-K 5的方式,效果提升了不少。

本地搭个Chroma存切片真的香,比那些按量计费的云端向量库省钱太多——去年那个时候,公司为新员工培训头疼不已:买SaaS课程库一年要十几万,外包录制内部课更是无底洞,还得安排专人运维学习平台。后来一咬牙,抛弃了“必须买现成服务”的思路,两周时间搭建了一套完全本地化的自动化教学流水线,运行至今,单人次边际成本稳定在0.3元以内——差不多就是电费的水平。
技术栈相当简单,全是开源组件拼接而成:
最让人头大的不是模型本身,而是批改的一致性。初期直接用系统提示词让大模型打分,同一份代码三次提交能得到60分、85分和72分这样的结果。后来改用了“少样例+结构化输出+规则校验”三件套:
{"score": int, "issues": [{"line": int, "desc": str, "deduct": int}]}。max_retries=2)。加上这层守卫后,批改方差从±12降到了±2,基本能替代初级助教了。
另一个大坑则是长上下文课程生成。直接让8B模型一次性输出两小时课程大纲和讲稿,总会套路出幻觉和片段。拆分成“大纲生成→分节扩写→交