阿里云 Milvus 知识库可以把教材、教案、题目与标准答案建成可检索的题库助手:学生用自然语言提问即可拿到讲解与原文依据,也可以上传题目图片,从题库中找出相似题并结合教材讲解。
方案说明
整体链路与通过阿里云Milvus知识库搭建智能客服问答应用完全一致:控制台定义标签 → 按标签批量导入资料 → 发布版本 → SDK 检索(可按标签过滤、可附带图片)→ 大模型生成带来源标注的回答 → Flask 提供问答页面。工程代码(kb_client.py、upload.py、app.py、templates/index.html、start.sh)请直接复用该文,本文只说明教育场景需要改动的部分:学科与知识点标签、图片资料的处理方式、图片查询的能力边界、以及公式展示。
建议第一版只选择一个年级、一个学科的 5~20 篇资料,验证后再扩大范围。整体耗时约 25~40 分钟,其中文档解析耗时取决于资料数量与大小。
前提条件
已创建 Milvus 知识库并记录知识库 ID(形如
kd-803ae9b10cc31)。创建一个全模态知识库。当前控制台创建知识库时固定使用全模态(ALL_MODAL)类型,页面上没有其他选项,因此无需额外判断;该属性创建后不可修改,可在知识库详情页的基本信息中查看数据类型。需要注意的是,结构化类型的知识库只接受 xlsx、xls、csv、jsonl、faq 五种格式,上传图片会直接返回参数错误,因此题库这类含图资料必须使用全模态知识库。
已使用主账号创建 RAM 用户、勾选使用永久 AccessKey 访问,并授予系统策略
AliyunMilvusFullAccess。已准备支持 OpenAI
chat/completions协议的大模型地址与 API Key。本地已安装 Python 3.8 或以上版本,并已按上文教程建好工程目录。
步骤一:定义年级与知识点标签
在知识库详情页的基本信息下方单击标签 > 管理,添加以下四个标签(类型均为 string):
标签名 | 说明 | 示例值 |
grade | 年级 | 八年级 |
subject | 学科 | 数学、物理 |
knowledgePoint | 知识点 | 二次函数、勾股定理 |
questionType | 资料类型 | 教材、练习题、标准答案 |
标签值支持中文,可直接使用年级、学科与知识点的中文名称。
标签管理对话框是整体保存的。对话框打开后标签列表为异步加载,请等到已有标签全部显示出来,再添加新标签并单击完成;否则可能以"空列表 + 新标签"整体覆盖,导致已有标签定义被清除。
步骤二:准备资料与导入清单
把资料放进
documents/目录,支持 PDF、DOCX、Markdown、TXT 与图片。建议教材、题目、标准答案成套上传:讲解时模型会同时引用教材中的方法、题目原文与答案中的评分要点,回答完整度明显更高。创建
documents.jsonl,为每篇资料标注年级、学科、知识点与资料类型。{"path": "documents/math-grade8-textbook.pdf", "metadata": {"grade": "八年级", "subject": "数学", "knowledgePoint": "二次函数", "questionType": "教材"}} {"path": "documents/math-quadratic-problem.png", "metadata": {"grade": "八年级", "subject": "数学", "knowledgePoint": "二次函数", "questionType": "练习题"}} {"path": "documents/math-quadratic-answers.docx", "metadata": {"grade": "八年级", "subject": "数学", "knowledgePoint": "二次函数", "questionType": "标准答案"}} {"path": "documents/math-pythagorean-exercise.docx", "metadata": {"grade": "八年级", "subject": "数学", "knowledgePoint": "勾股定理", "questionType": "练习题"}}关于图片资料,需要了解其入库方式:图片会先经过文字识别(OCR)转成文本,再按文本切片建立索引。因此:
图中文字能否被识别,决定这张图能否被检索到。纯图形(没有文字的几何图、函数图象)几乎无法被命中,不适合作为独立资料上传,建议与含文字的题干放在同一张图或同一篇文档中。
题目图片应保持清晰、字号足够、尽量使用印刷体。识别结果可能出现偏差(例如把顿号识别成其他符号、把变量
x识别成乘号×),数学符号尤其容易受影响。上传后建议到数据管理页单击查看切片,确认识别出的文本与图中内容一致,再发布版本。
题目与解析多为短条目,可在知识库详情页的处理策略中单击创建策略调整切片粒度。最大分段长度的单位是字符(默认 512),建议设置为 580~770 字符(约相当于 384~512 tokens),尽量让"题干 + 解析"保持在同一个切片内。
步骤三:配置检索参数与提示词
在 config.json 中按教育场景调整 retrieval 与 scenario。
{
"retrieval": {
"page_size": 8,
"candidate_count": 64,
"min_score": 0.2,
"semantic_weight": 0.75,
"enable_query_expansion": true,
"rerank_model_name": "qwen3-rerank",
"tag_filter": {
"relation": "and",
"conditions": [
{"field": "subject", "op": "=", "value": "数学"}
]
}
},
"scenario": {
"title": "教育题库检索与讲解",
"system_prompt": "你是教学助理。只依据检索到的教材、题目和标准答案讲解;先给思路,再给步骤,最后给答案,并标注[来源N];公式请使用纯文本书写,不要使用 LaTeX 语法;若检索到的题目与用户描述不一致,必须明确指出差异,不要直接套用题库中的答案;资料不足时不要猜测标准答案。",
"image_enabled": true,
"sample_questions": [
"二次函数顶点式如何求最大值?",
"找一道使用勾股定理的例题并讲解",
"这道题考查了哪些知识点?"
]
}
}参数说明:
semantic_weight=0.75并启用qwen3-rerank:学生提问多为自然语言题意描述,语义权重更高有利于相似题匹配。需注意重排分数与向量分数不是同一量纲:最终分数的算法是
score ≈ (1-semantic_weight) × keywordScore + semantic_weight × semanticScore(另可能叠加 rank feature),未启用重排时semanticScore是向量相似度,启用重排后它变成重排模型分数,而min_score始终在这个最终分数上过滤。因此实测同一问题在启用重排后返回条数由 5 条降为 4 条,是阈值与新量纲共同作用的结果,并不说明重排让召回变差。没有跨语料通用的推荐组合:建议先把
min_score设为 0 取回一批结果并人工标注相关性,再按召回与误召回的分布选阈值;每次切换重排模型、开关重排或调整semantic_weight后都要重新校准。tag_filter固定subject:可有效防止跨学科误召回。实测在subject=数学过滤下提问物理知识点,返回 0 条,模型会按提示词回答"资料不足"。op实际只有=、in、not in三个运算符生效。写成eq、==、equal、like等别名会返回400 Unsupported tag filter operator,导致每次提问都失败;而≠、>、<、≥、≤、empty、not empty、start with、end with虽然出现在该报错列出的 "Supported operators" 清单中,实测条件会被静默忽略、返回未经过滤的全部数据。其中
contains、not contains只是in/not in的别名,并不是字符串包含,按字符串片段传值只会返回 0 条。需要按区间筛选时请用
in枚举取值,需要判断标签为空时请用= "";配置后务必与不加过滤的结果对比总条数,确认过滤生效。system_prompt中建议明确"公式使用纯文本":教学类提示词容易让模型输出 LaTeX,而示例页面用<pre>纯文本展示,公式不会渲染,会显示为原始的美元符号与反斜杠。若希望保留 LaTeX,请在页面中引入 KaTeX 或 MathJax。
步骤四:图片查询的用法与能力边界
app.py 的 /api/ask 接受可选的 image_url 参数,并透传给 SearchKnowledgeBase 的 image 字段:
curl -sS http://127.0.0.1:7860/api/ask \
-H 'Content-Type: application/json' \
-d '{"question":"这道题考查了哪些知识点?","image_url":"https://<可公网访问的图片地址>"}'图片对指代不明的提问帮助最大。实测同一个问题「这道题考查了哪些知识点?」:不带图片时命中的是勾股定理练习(相关度 0.431,并不是想问的题);带上二次函数题目图片后,命中的是题库中的二次函数题目(0.583),可见图片为检索提供了关键语义。
图片只参与检索,不会被送给大模型。 系统的行为是"根据图片在题库中找出最相似的题目,然后依据检索到的资料讲解",不是"识别并解答图片中的题目"。若上传的是一道题库中不存在的新题,系统会拿最相似的库内题目作答,答案可能与图中题目不同且不易察觉。例如上传 y=-(x-1)²+4(最大值为 4)时,若题库中存在 y=-2(x-3)²+5,回答可能给出最大值 5。 因此建议:在页面上提示"图片用于查找题库中的相似题";在提示词中要求模型指出检索结果与用户描述的差异;因此这项能力应当被称为「图片辅助检索」或「以图搜题」,不能对外表述为「拍照解题」。若确实需要解答图中的新题,必须由应用层把原图另外传给支持多模态输入的大模型,并要求它同时核对检索到的资料,而不是只依赖知识库检索。
图片的识别能力有以下边界,准备题库素材前需要了解:
格式:建议只使用 JPG、JPEG、PNG、GIF。底层文件识别可能还接受 WebP、TIFF 等格式,但没有统一的对外约定,不建议依赖。
大小:接口没有单独的图片字节数或像素硬上限,但实际会受上传网关、图像解码、内存以及图文转换模型服务的共同限制,超大图仍可能失败。
识别准确率:手写体、数学公式、几何图形、坐标系都没有承诺的准确率指标。纯图形可能由图文转换生成一段描述,但不保证能被检索命中。本文实测中,
x被识别成×、顿号被识别成「丶」,数学符号尤其需要人工抽查。没有识别质量阈值:只要图片能被解码且流程未报错,文档状态就是处理完成,即使识别出的文本很少或有错误;只有解码失败或所需模型调用报错才会显示处理失败。因此上传后必须到数据管理页抽查切片文本,不能只看状态。
关于图片地址,image_url 必须是可公网访问的地址,服务端会校验:
传入内容 | 服务端返回 |
内网或本机地址(如 |
|
非图片资源的链接 |
|
无法解析的域名 |
|
留空 | 退化为普通文字检索(正常行为) |
最后是回显原题图的问题。检索结果里有一个 images 字段,用于返回切片关联的图片(服务端会为已持久化的图片生成短期有效的签名地址)。该字段本身是已实现的能力,并非预留的空字段;如果命中了图片文档但它仍然返回空,说明这批资料在解析切片阶段没有把图片 ID 持久化到对应切片,或者签名关联没有建立,属于需要按具体文档排查的链路问题,没有任何请求参数可以开启它。因此不要把「一直为空」当作产品设计,也不需要为此长期自行维护一份「文件名 → 公网图片地址」的映射表;在它为空期间,页面可以先降级展示识别出的题目文本。
步骤五:上传、发布与验证
上传资料(
MetaFields对整批生效,脚本会先按标签分组再分批提交)。python upload.py --manifest documents.jsonl到控制台数据管理页确认资料状态为处理完成,并对图片资料单击查看切片核对识别文本。
在版本管理页单击发布版本,完成三步向导后确认新版本状态为已发布。
重要同时最多只能存在 3 个已发布版本。达到上限后发布版本按钮会置灰,而页面仍会显示"当前有 N 条待发布变更",需要先在版本记录中删除不再需要的旧版本。版本删除不可恢复。题库通常每学期或每单元都会补充资料,建议只保留"当前版本 + 最近一个历史版本"。
启动服务并验证。
python app.py按以下四条验收:
页面能正常打开(教育场景会额外显示题目图片 URL 输入框),提交问题后同时返回答案与检索来源。
回答中的
[来源N]能在下方来源区找到对应的资料切片。提问题库未覆盖的内容时,回答明确说明资料不足,而不是补写答案。
补充资料并重新发布版本后,页面能检索到新内容。
建议再补一条针对语义召回的验证:用只出现在资料内容里、不出现在文件名中的信息提问(例如题目编号),确认能命中对应资料。这条同样适用于检查图片是否被正确识别入库。
教育场景注意事项
教材、题目、答案成套上传,并用
questionType区分,便于按需过滤(例如只给学生检索"练习题",给教师检索"标准答案")。按学科建立独立入口:
tag_filter固定subject后,该入口只能回答该学科的问题,示例问题也应保持同一学科,否则学生提问其他学科只会得到"资料不足"。答案类资料建议单独控制访问:若不希望学生直接拿到标准答案,可在学生入口用
questionType not in ["标准答案"]之类的条件过滤。检索结果的
images字段当前取不到图片地址,页面展示的是识别后的文本。该字段本身是已实现的能力,取不到值属于解析链路未把图片关联到切片,不需要为此长期自行维护「文件名 → 图片地址」的映射表:短期内页面可先降级展示识别文本,需要回显原题图时再按documentId关联导入清单临时处理。关键结论建议保留人工核对,并提示学生以正式教材与教师讲解为准。
常见问题
现象 | 原因与处理 |
提问返回 500,日志显示 |
|
加了标签过滤但结果条数与不加过滤时完全一样 | 使用了不生效的运算符(如 |
图片资料上传后搜不到 | 依次确认:知识库数据类型是否支持图片;数据状态是否为处理完成;查看切片中识别出的文本是否为空或与图中不符(纯图形、字号过小、手写体都可能导致识别失败)。 |
上传图片提问,回答的却是另一道题 | 预期行为。图片只用于检索相似题,模型讲解的是检索到的题目,详见步骤四。 |
提问返回 |
|
页面上公式显示成 | 模型输出了 LaTeX 而页面按纯文本展示。在提示词中要求使用纯文本公式,或在页面引入公式渲染库。 |
启用重排后结果反而变少 | 重排分数与向量分数量纲不同,与 |
按标签过滤后总是 0 条 | 标签名拼写与写入时不一致(接口不会报错,只返回 0 条)。在知识库详情页 → 标签 → 管理中核对。 |
上传返回 | 整批文件都因同名被去重。修改文件名或先在控制台删除旧数据。 |
检索返回 | 尚未发布版本,或指定的版本号不存在。 |