📋摘要
本报告针对"为初中数学教师构建本地化结构化题库"的需求进行可行性分析,并给出完整的技术选型与实施方案。
核心结论
- 可行性:完全可行。当前大模型 + OCR + 向量数据库技术已成熟,能覆盖 80% 以上的自动化需求
- 推荐技术栈:Python FastAPI + Vue 3 + PostgreSQL + DeepSeek API + Typst
- 核心难点:数学公式识别、题目自动切分、多维标签体系设计
- 预估工时:8-10 周可达到生产可用状态
- 预估成本:云端 LLM 调用约 50 元/千题,零服务器投入
1可行性评估
时机、技术、成本三方面均已具备,核心瓶颈在"标签体系"和"公式识别"的工程化落地。
• PaddleOCR、pix2tex 等开源 OCR 工具已可用
• 本地化部署成本可控
• 数据完全自主可控,教学资源安全
• 题目自动切分需 LLM 二次加工
• 多维标签需要本地化标准体系
• 去重需向量检索支持
• 排版美观影响最终交付
2技术栈
所有选型均优先考虑中文生态友好 + 数学场景适配 + 长期可维护。
2.1 后端
| 类别 | 选型 | 理由 |
|---|---|---|
| 语言 | Python 3.11+ | LLM 生态最全、解析库丰富 |
| 框架 | FastAPI | 异步高性能、自动生成 OpenAPI 文档 |
| ORM | SQLAlchemy 2.0(异步) | 成熟稳定、迁移工具完善 |
| 迁移 | Alembic | 数据库版本管理 |
| 任务队列 | Celery + Redis | 处理耗时的 PDF 解析、AI 切题 |
| LLM SDK | openai(兼容 DeepSeek/Qwen) | 一套接口对接多家 |
2.2 前端
| 类别 | 选型 | 理由 |
|---|---|---|
| 框架 | Vue 3 + TypeScript + Vite | 国内生态成熟、SSR/CSR 灵活 |
| UI 库 | Naive UI | 中文文档好、组件丰富、TS 优先 |
| 状态 | Pinia | Vue 3 官方推荐 |
| 编辑器 | Tiptap + KaTeX | 支持 LaTeX 公式实时预览 |
| HTTP | ofetch / axios | 简洁、拦截器完善 |
2.3 数据与 AI
| 层 | 选型 | 用途 |
|---|---|---|
| 主数据库 | PostgreSQL 16 + pgvector | 结构化数据 + 题目向量 |
| 缓存/队列 | Redis | 分页缓存、Celery 队列 |
| 主 LLM | DeepSeek-V3 API | 切题、打标、解析生成(性价比王) |
| 备用 LLM | 智谱 GLM-4 / 通义 Qwen-Max | 主备切换 |
| 本地 LLM | Ollama + Qwen2.5-14B | 完全离线备用 |
| Embedding | bge-m3 / text-embedding-v3 | 题目向量化、去重 |
2.4 文档解析
| 场景 | 工具 | 备选 |
|---|---|---|
| Word (.docx) | python-docx | — |
| 文字型 PDF | PyMuPDF (fitz) | pdfplumber |
| 扫描型 PDF / 图片 | PaddleOCR | Tesseract |
| 数学公式 | pix2tex (LaTeX-OCR) | MathPix API(付费但准) |
| 几何图形 | 原图存储 + 文本描述 | Qwen2.5-VL 多模态 |
2.5 排版输出
| 格式 | 主选 | 转换链路 |
|---|---|---|
| PDF(主) | Typst(强烈推荐) | Typst 直出 / Pandoc + LaTeX |
| Word | python-docx | 公式转 OMML 或图片 |
| Markdown | Jinja2 模板直出 | 公式用 $...$ / $$...$$ |
| 通用互转 | Pandoc | MD ↔ Word ↔ PDF |
相比 LaTeX,Typst 学习曲线平缓、编译秒级、公式渲染极其美观。一个 .typ 文件就能生成专业级数学试卷。强烈建议优先掌握。官网:typst.app
3系统架构
采用"本地为主 + 云端模型为辅"的混合架构,数据完全自主可控。
4数据库设计
核心 9 张表,结构化标签 + 向量检索双引擎,新增媒体资源管理表支持图片去重与复用。
4.1 核心表概览
如:一元二次方程 → 判别式
4.2 题目表核心字段
CREATE TABLE questions (
id BIGSERIAL PRIMARY KEY,
stem TEXT NOT NULL, -- 题干(LaTeX/Markdown)
stem_html TEXT, -- 渲染后 HTML(缓存)
question_type VARCHAR(20), -- choice/fill/solution/proof
difficulty SMALLINT DEFAULT 3, -- 1-5
answer TEXT,
analysis TEXT, -- 解析
source_id BIGINT REFERENCES sources(id),
source_page INT,
checksum VARCHAR(64), -- 内容 hash(去重辅助)
embedding VECTOR(1024), -- pgvector 向量
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
is_active BOOLEAN DEFAULT TRUE
);
CREATE INDEX idx_questions_embedding ON questions
USING ivfflat (embedding vector_cosine_ops);
4.7 媒体资源管理(图片 / 公式 / 附件)
Word/PDF 试卷中的图片素材采用对象存储 + 数据库路径映射方案,支持 MD5 去重和题目-图片解耦。
option_images/ — 选项图片
analysis_images/ — 解析图片
formula_images/ — 公式截图
文件名:{uuid}_{用途}_{关联ID}.{ext}
• 解耦关联:一图可用于多题
• 分级 URL:配图可 CDN 加速
• 尺寸记录:便于前端自适应渲染
-- 媒体资源表(统一管理所有二进制素材)
CREATE TABLE media_resource (
id BIGSERIAL PRIMARY KEY,
uuid VARCHAR(36) UNIQUE NOT NULL,
original_name VARCHAR(255) NOT NULL,
storage_path VARCHAR(512) NOT NULL,
access_url VARCHAR(512),
file_size BIGINT NOT NULL,
mime_type VARCHAR(50) NOT NULL,
md5_hash VARCHAR(32),
width INT,
height INT,
source VARCHAR(50) DEFAULT 'upload',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_media_md5 ON media_resource(md5_hash)
WHERE md5_hash IS NOT NULL;
-- 题目-媒体关联表(解耦:一张图可用于多个题目)
CREATE TABLE question_media (
id BIGSERIAL PRIMARY KEY,
question_id BIGINT NOT NULL REFERENCES questions(id) ON DELETE CASCADE,
media_id BIGINT NOT NULL REFERENCES media_resource(id) ON DELETE CASCADE,
usage_type VARCHAR(20) NOT NULL, -- stem/option/analysis/attachment
display_order INT DEFAULT 0,
alt_text VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (question_id, media_id, usage_type)
);
5项目结构
前后端分离、模块化分层、模板与代码分离。
math-bank/
├── backend/
│ ├── app/
│ │ ├── api/ # FastAPI 路由
│ │ ├── core/ # 配置、日志、安全
│ │ ├── models/ # SQLAlchemy 模型
│ │ ├── schemas/ # Pydantic
│ │ ├── services/
│ │ │ ├── parser/ # 文档解析(pdf/docx/ocr)
│ │ │ ├── llm/ # LLM 客户端 + Prompts
│ │ │ ├── tagger/ # 自动打标
│ │ │ ├── dedup/ # 向量去重
│ │ │ └── exporter/ # Word/PDF/MD 导出
│ │ ├── db/
│ │ └── main.py
│ ├── alembic/
│ ├── tests/
│ └── pyproject.toml
├── frontend/
│ ├── src/
│ │ ├── views/
│ │ │ ├── QuestionBank/ # 题库浏览/编辑
│ │ │ ├── PaperBuilder/ # 组卷
│ │ │ ├── Importer/ # 试卷导入
│ │ │ └── KnowledgeTree/ # 知识点管理
│ │ ├── components/
│ │ │ ├── MathEditor.vue # Tiptap + LaTeX
│ │ │ ├── QuestionCard.vue
│ │ │ └── TagSelector.vue
│ │ ├── api/
│ │ └── stores/
│ ├── package.json
│ └── vite.config.ts
├── templates/ # 排版模板
│ ├── typst/ # Typst 模板(主)
│ └── word/ # Word 模板
├── data/
│ ├── sources/ # 原始试卷
│ ├── images/ # 题目图片
│ └── exports/ # 导出文件
└── docker-compose.yml
6核心模块
四个核心模块构成系统的"AI 流水线"。
6.1 题目切分(最关键)
SPLIT_PROMPT = """你是初中数学题目切分专家。
输入是一份试卷的纯文本(可能含 LaTeX 公式 $...$),
需要切分成独立题目。
要求:
1. 准确识别每道题的题号
2. 分离题干、选项(小问)、答案、解析
3. 保留 LaTeX 公式原样
4. 几何题保留 [图:description] 占位
输出严格 JSON:
{
"questions": [
{
"number": "1",
"stem": "题干文字...",
"options": ["A. ...", "B. ..."],
"sub_questions": [{"label": "(1)", "stem": "..."}],
"answer": "...",
"analysis": "...",
"knowledge_points": ["一元二次方程", "求根公式"],
"difficulty": 3
}
]
}"""
6.2 PDF 解析流程
- 用
PyMuPDF提取文字 + 图片位置 - 扫描型 PDF 走
PaddleOCR - 公式片段识别用
pix2tex转 LaTeX - 几何图直接保留原图,文字里插入
[图:hash]占位 - 拼接后送 LLM 二次切题
6.3 自动打标
基于本地知识点字典(如"人教版"),LLM 从字典中选最匹配的 1-3 个标签。Prompt 包含完整字典和题型示例,确保输出稳定。
6.4 向量去重
async def find_duplicates(question_text: str, threshold=0.92):
embedding = await embed(question_text)
sql = text("""
SELECT id, stem,
1 - (embedding <=> :vec) AS similarity
FROM questions
WHERE 1 - (embedding <=> :vec) > :threshold
ORDER BY embedding <=> :vec
LIMIT 5
""")
return await db.execute(sql, {"vec": embedding, "threshold": threshold})
• > 0.95:几乎肯定是同题
• 0.85 - 0.95:可能是"姐妹题"(同模板换数字)
• < 0.85:基本无关
6.7 智能组卷模块
智能组卷本质是带约束的随机抽样,目标是在限定条件下生成一套均衡的试卷。
6.7.1 组卷约束输入
| 约束类型 | 示例 | 说明 |
|---|---|---|
| 总分要求 | 100 分 | 最终试卷总分数 |
| 难度分布 | 易 30% / 中 50% / 难 20% | 各难度题目占比 |
| 题型配比 | 选择 20 / 填空 10 / 解答 5 | 各题型题目数量 |
| 知识点覆盖 | 必含 [101, 102],禁含 [205] | 强制包含 / 排除的知识点 |
| 作答时间 | 120 分钟 | 按题型均耗时估算 |
6.7.2 算法对比
| 算法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| 贪心算法 | 实现简单、速度快 | 难以保证整体均衡 | 生产环境首选 |
| 遗传算法 | 全局搜索能力强 | 计算量大、参数调优复杂 | 复杂约束优化 |
| 约束规划(CP-SAT) | 硬约束精确求解 | 需 OR-Tools 依赖 | 精确约束组卷 |
6.7.3 推荐算法:分层贪心 + 知识点约束检查
class SmartPaperGenerator:
"""分层贪心智能组卷生成器"""
def generate(self, constraint: PaperConstraint) -> list[Question]:
# 1. 按题型+难度分桶
buckets = self._group_by_type_and_difficulty()
# 2. 按题型贪心抽取
selected = []
for qtype, count in constraint.type_distribution.items():
diff_counts = self._distribute_difficulty(count, constraint.difficulty_ratio)
for diff, diff_count in diff_counts.items():
candidates = buckets.get((qtype, diff), [])
picked = self._pick_randomly(candidates, diff_count)
selected.extend(picked)
# 3. 知识点覆盖校验(缺必含则替换)
selected = self._ensure_knowledge_coverage(selected, constraint)
# 4. 禁知识点过滤
selected = self._exclude_forbidden_kps(selected, constraint)
# 5. 分配分值(使总分为目标总分)
selected = self._allocate_scores(selected, constraint.total_score)
return selected
def _ensure_knowledge_coverage(self, selected, constraint):
"""确保必含知识点被覆盖,缺则替换"""
if not constraint.required_kps:
return selected
covered = set()
for q in selected:
covered.update(q.knowledge_points)
for kp_id in constraint.required_kps:
if kp_id not in covered:
replacement = self._find_replacement(selected, kp_id, constraint)
if replacement:
weakest = min(selected, key=lambda q: len(
set(q.knowledge_points) & set(constraint.required_kps)))
selected[selected.index(weakest)] = replacement
return selected
6.7.4 智能组卷扩展功能
POST /api/papers/generate
输入:总分、题型分布、难度比例、必含/禁含知识点
输出:生成后的试卷详情(含题目列表、分值、预估时长)
7实施路线
5 个阶段、约 8-10 周可达到生产可用。
• 数据库表 + Alembic
• 题目/标签/试卷 CRUD API
• 简单前端列表页
• 知识点树形管理
• 多条件组合检索
• 手动录入 200 道题跑通流程
• LLM 切题 + 自动打标
• 人工校对界面
• 题目图片、公式关联
• Typst / Word / Markdown 模板
• 一键导出试卷 + 答案 + 解析
• 配套解析自动生成
• 错题本预备(学生端)
8关键建议
动手前先把 7-9 年级数学知识点整理成树。可以参考人教版 / 北师大版目录。这是整个系统的骨架,AI 帮不了您,必须您自己定。
"LLM 切题 + 人工校对"是最稳妥方案。AI 处理 80% 体力活,您花 20% 时间校对质量。自动打标的 5-10% 误差也靠人工修正。
前端 KaTeX 实时预览,所见即所得。比 Word 公式编辑器快 10 倍。一旦习惯,回不去。
LaTeX 的现代替代品。编译秒级、公式漂亮、模板即代码。组卷导出首选。
建议马上开始的 3 件事:
① 建数据库 + 写题目 CRUD API(半天)
② 前端列表 + Tiptap 编辑器(一天)
③ 手动录入 50 道题,跑通"录入→检索→导出"(一天)
跑通后再加 PDF 解析、LLM 切题。
9成本估算
| 项目 | 一次性投入 | 月度成本 |
|---|---|---|
| 云端 LLM API(DeepSeek) | — | 50-200 元(看题量) |
| 本地部署(可选) | 5000-10000 元(GPU 服务器) | 电费 ~100 元 |
| 自己开发 | 1-2 人月工时 | — |
| 找人定制开发 | 3-8 万元 | — |
典型场景:首次导入 1000 道题,预计 DeepSeek 调用成本 30-50 元,之后日常使用更低。
🚀下一步行动
从最小闭环开始,逐步迭代。
- 本周:搭项目骨架(FastAPI + Vue + Docker Compose)
- 第 2 周:手动录入 50-200 道题,跑通"录入→检索→导出 Markdown"
- 第 3-4 周:建知识点体系 + Tiptap 编辑器集成
- 第 5 周起:接入 Word/PDF 解析 + LLM 切题
- 第 8-9 周:组卷引擎 + Typst 模板 + 三格式导出
📌 核心提醒
技术方案和工具都已经成熟,真正的难点不在技术,而在您自己梳理清楚"标签体系"和"组卷规则"。这两件事想明白了,剩下的 80% AI 都能干。