「本地AI错题纠正小程序」指的是把 OCR、知识点匹配、错因诊断这些 AI 能力部署在本地环境(手机端或内网服务器),由小程序负责采集与交互的一类应用。相比直接把题目图片丢给公有云大模型,本地化方案的核心诉求通常有三个:数据不出内网、弱网可用、推理成本可控。本文按工程实现顺序,讲清楚这类小程序的技术选型、链路设计和落地时要避开的坑。
一、先明确「本地」到底落在哪一层
很多团队一开始就把「本地 AI」理解成「模型跑在手机里」,结果在真机上撞墙。实际上可落地的方案有三档,需要按场景选:
-
**纯端侧**:模型通过 WASM 或小程序原生推理框架运行在手机内。优点是零网络依赖、隐私;缺点是小程序容器对算力、内存、包体积限制严格,只适合做轻量 OCR、手写数字识别、简单分类这类小模型。
-
**内网服务器推理**:模型部署在学校或机构的本地服务器,小程序通过内网 HTTPS 调用。这是目前常见、工程上稳的形态,可以跑 7B 级别量化模型或完整的 OCR + 向量检索链路。
-
**端云混合**:端侧做预处理和缓存,复杂推理回落到内网服务。
结论是:**错题纠正的「纠正」环节放在内网服务,「采集」环节放在端侧**,这个分工在成本与体验之间平衡。
二、整体架构与技术选型
参考同类多端教育应用的通用做法,用户端用 uniapp(Vue 语法)一套代码适配小程序、H5、公众号和 App;服务端用 Spring Boot + MyBatis Plus + MySQL;管理后台用 Vue + Element UI。AI 推理服务单独拆成一个独立的进程或容器,与业务服务通过内网 HTTP 通信,这样模型迭代不需要重启业务服务。
分层如下:
-
**采集层(小程序)**:拍照/相册、裁剪、透视校正、压缩、题目框选。
-
**识别层(内网)**:OCR 提取题干文本,公式单独走公式识别模型。
-
**理解层(内网)**:文本向量化 → 向量库检索相似题与知识点 → 错因分类模型输出错因标签。
-
**反馈层(小程序)**:按提示等级逐步给出引导,而不是直接抛答案。
-
**沉淀层(MySQL + 本地缓存)**:错题本、复习计划、掌握度曲线。
向量检索建议先用轻量方案起步,比如 FAISS 或 PostgreSQL 的 pgvector,题目量在十万级以内完全够用,不必一上来就上独立向量数据库。
三、错题纠正链路的代码实现
1. 小程序端采集与上传
拍照后不要直接上传原图,先做压缩和区域裁剪,能显著降低内网带宽压力:
javascript
```javascript
// uniapp:拍照 + 压缩 + 上传
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
success: async (res) => {
const path = res.tempFilePaths[0];
// 压缩到长边 1600px,质量 80,足够 OCR 使用
const compressed = await new Promise((resolve) => {
uni.compressImage({ src: path, quality: 80, success: (r) => resolve(r.tempFilePath) });
});
uni.uploadFile({
url: `${BASE_URL}/api/mistake/recognize`,
filePath: compressed,
name: 'file',
header: { Authorization: `Bearer ${token}` },
success: (r) => handleResult(JSON.parse(r.data))
});
}
});
```
2. 服务端识别与错因诊断接口
业务侧只暴露一个入口,内部串联 OCR、检索、诊断三步,避免小程序端多次往返:
java
```java
@RestController
@RequestMapping("/api/mistake")
public class MistakeController {
private final OcrClient ocrClient; // 内网 OCR 服务
private final VectorSearchClient vectorClient; // 内网向量检索
private final DiagnoseClient diagnoseClient; // 错因分类模型
@PostMapping("/recognize")
public Result<MistakeVO> recognize(@RequestParam MultipartFile file,
@RequestParam Long studentId) {
String imageUrl = storageService.save(file);
// 1. 题干识别
OcrResult ocr = ocrClient.recognize(imageUrl);
// 2. 知识点与相似题召回
List<Float> embedding = vectorClient.embed(ocr.getText());
List<SimilarQuestion> similar = vectorClient.search(embedding, 5);
// 3. 错因诊断:概念不清 / 计算失误 / 审题偏差
DiagnoseResult diagnose = diagnoseClient.classify(
ocr.getText(), similar, studentId);
return Result.ok(MistakeVO.of(ocr, similar, diagnose, imageUrl));
}
}
```
3. 分步提示的设计
错题纠正容易做错的地方是「一上来就给答案」。建议把反馈拆成三级提示,逐级释放:
-
L1:指出考查的知识点,不给任何解题步骤;
-
L2:给出步的思路,留出后续推理空间;
-
L3:给出完整解析与同类题推荐。
等级由学生点击次数或答题正确率驱动,这个策略写在服务端,方便后续按学段调整。
四、错题数据模型与复习调度
错题本不是「收藏夹」,它的价值在于调度。表结构建议把「题目」和「复习记录」拆开,一张表存题目,另一张表存每次复习的结果:
java
```sql
CREATE TABLE mistake_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id BIGINT NOT NULL,
subject VARCHAR(32),
knowledge_tag VARCHAR(64),
stem_text TEXT,
image_url VARCHAR(255),
error_type VARCHAR(32),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
KEY idx_student_subject (student_id, subject)
);
CREATE TABLE review_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
item_id BIGINT NOT NULL,
quality TINYINT, -- 0~5,本次回忆质量
ease DECIMAL(4,2), -- 难度因子
interval_d INT, -- 下次间隔(天)
next_time DATETIME,
KEY idx_item (item_id)
);
```
调度算法可以直接用 SM-2 的简化版,实现很短,但对复习节奏的改善很明显:
java
```java
public class Sm2Scheduler {
public ReviewLog schedule(ReviewLog last, int quality) {
double ease = last == null ? 2.5 : last.getEase();
int interval = last == null ? 1 : last.getIntervalD();
// 难度因子更新,做上下限保护
ease = ease + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02));
ease = Math.max(1.3, Math.min(2.8, ease));
if (quality < 3) {
interval = 1; // 回忆失败,明天重来
} else if (interval == 1) {
interval = 3;
} else {
interval = (int) Math.round(interval * ease);
}
ReviewLog log = new ReviewLog();
log.setQuality(quality);
log.setEase(ease);
log.setIntervalD(interval);
log.setNextTime(LocalDateTime.now().plusDays(interval));
return log;
}
}
```
五、落地时的几个工程坑
**OCR 是准确率瓶颈,不是模型。** 手写体、竖式计算、带图的几何题,识别失败率远高于印刷体。建议在上传前引导用户框选题干区域,并在识别结果页提供「手动修正文本」入口,修正后的文本回写向量库,等于持续做数据回流。
**小程序包体积与 WASM。** 如果确实要做端侧推理,模型体积控制在几 MB 级别,且要准备降级路径------部分低端机不支持相关能力时自动切到内网服务。
**推理服务要限流。** 内网服务器算力有限,OCR 和诊断模型要做队列与并发上限,否则一次班级规模的集中提交会把服务打满。建议按学生维度做令牌桶限流。
**冷启动缓存。** 常用知识点向量和小模型可以常驻内存,避免每次请求重新加载。
**隐私边界要写进代码。** 如果题目图片只在本地留存,存储路径要做定期清理策略,并在接口层做数据脱敏,这既是合规要求,也是本地化方案的立身之本。
六、FAQ
**Q:本地AI错题纠正小程序一定要用大模型吗?**
不一定。错因分类用微调后的小模型(如 BERT 级别的分类模型)效果往往更稳定,也更省算力。大模型更适合用来生成解析文本和变式题。
**Q:题目量不大,还需要向量数据库吗?**
十万题以内,pgvector 或 FAISS 足够,检索延迟通常在毫秒级。数据量再上一个量级再考虑独立向量库。
**Q:小程序端能直接跑 OCR 吗?**
可以做轻量场景,但受包体积和机型限制,兼容性成本高。更稳的做法是端侧做图像预处理,识别放在内网服务。
**Q:怎么衡量纠正效果?**
建议同时看三个指标:同类题复现正确率、复习计划完成率、错题二次出错间隔。只看单次答题正确率容易被蒙对的情况误导。
**Q:能不能不联网使用?**
采集和复习调度可以离线,但推理环节需要内网可达。若要完全离线,只能把模型下沉到端侧,功能范围要做相应收敛。