15 KiB
去掉自动生成知识点并改为基于 KnowledgeSource 出题 - 修订计划
修订日期:2026-07-05
对照仓库:rag-worker/api-server/ios-projects/AIStudyApp
1. 目标确认
本次改造的目标不是“优化自动生成知识点”,而是把系统职责重新切开:
- 上传资料后的 RAG 导入流程只负责解析、分块、向量化、索引,不再自动产出候选知识点。
- 知识点生成改为用户手动触发,继续沿用现有
generate-knowledge流程。 - Quiz 生成不再依赖
KnowledgeItem,改为直接基于KnowledgeSource + KnowledgeChunk原文出题。 - iOS 侧同步移除“候选审批”相关死页面/死路由/死 API,并补齐“按资料出题”的客户端能力。
2. 现状核对
2.1 已确认符合目标的部分
-
手动 AI 解析知识点链路已经存在:
api-server/src/modules/knowledge-source/knowledge-source.service.tsapi-server/src/modules/ai/workflows/knowledge-import.workflow.tsios-projects/AIStudyApp/AIStudyApp/Core/Services/API/KnowledgeSourceAPI.swiftios-projects/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialReaderView.swift
-
Quiz 的 prompt / schema 基础设施已经存在:
api-server/src/modules/ai/prompts/quiz-generation.prompt.tsapi-server/src/modules/ai/prompts/schemas/quiz-generation.schema.ts
-
Prisma 已支持 Quiz 挂载资料来源与题目来源块:
api-server/prisma/schema.prismaQuiz.sourceTypeQuiz.sourceIdQuizQuestion.sourceBlockIds
2.2 与目标冲突的现状
-
Python RAG Worker 仍然会自动生成候选知识点:
rag-worker/pipelines/import_pipeline.pyrag-worker/candidate_generator.pyrag-worker/api_client.py
-
API Server 仍保留完整的 ImportCandidate 审批链路:
api-server/src/modules/import-candidate/*api-server/src/modules/rag/internal-rag.controller.tsapi-server/src/modules/rag/rag.module.tsapi-server/src/app.module.ts
-
iOS 仍保留候选审批页面与 API:
ios-projects/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swiftios-projects/AIStudyApp/AIStudyApp/Core/Navigation/Route.swiftios-projects/AIStudyApp/AIStudyApp/Core/Services/API/ImportAPI.swift
-
当前 Quiz 仍然直接从
KnowledgeItem拼题:api-server/src/modules/quiz/quiz.service.ts
-
ai-job的 quiz snapshot 体系仍然以KnowledgeItem为输入,不符合“从资料原文出题”:api-server/src/modules/ai-job/quiz-generation-snapshot-builder.tsapi-server/src/modules/ai-job/quiz-generation-registration.service.tsapi-server/src/modules/ai-job/quiz-generation-job-definition.ts- 对应 spec 文件
-
iOS 当前 Quiz API 只支持按知识库生成,不支持按资料生成:
ios-projects/AIStudyApp/AIStudyApp/Core/Models/APIModels.swiftios-projects/AIStudyApp/AIStudyApp/Core/Services/API/QuizAPI.swiftios-projects/AIStudyApp/AIStudyApp/Features/Quiz/QuizViews.swift
3. 修订后的执行范围
本次改造不应只做你原计划里的 3 个点,而应拆成 5 个批次。
批次 1:去掉 Worker 自动候选知识点生成
1.1 Python 主流程
文件:
rag-worker/pipelines/import_pipeline.py
修改:
- 删除
generate_candidatesimport。 - 删除
save_candidatesimport。 - 删除
GENERATING_CANDIDATES状态更新与整段候选生成逻辑。 - 更新文件头部注释,导入流程从“下载→解析→清洗→切片→embedding→Qdrant→AI 候选”改为“下载→解析→清洗→切片→embedding→Qdrant”。
1.2 Python 死代码清理
文件:
rag-worker/candidate_generator.pyrag-worker/api_client.py
修改建议:
- 若确认后续完全不再保留候选知识点机制,删除
candidate_generator.py。 - 删除
api_client.py中save_candidates()。 - 若短期内想保留历史兼容,可先不删文件,但必须确保运行时不再被引用。
1.3 API 端导入状态枚举同步
文件:
api-server/src/modules/document-import/document-import.repository.ts
修改:
findStaleJobs()中移除GENERATING_CANDIDATES,避免恢复逻辑继续认为该状态合法。
1.4 Internal RAG API 同步清理
文件:
api-server/src/modules/rag/internal-rag.controller.ts
修改:
- 删除
/internal/rag/candidates保存入口。 - 删除
ImportCandidateRepository注入。 - 删除“候选创建成功后将
learningStatus置为 completed”的逻辑。
注意:
learningStatus 的完成语义需要改成“手动 AI 解析成功后完成”,不能再绑定到上传流程。
批次 2:删除 ImportCandidate 审批体系
这是原计划漏掉的最大后端范围。
2.1 API 模块清理
文件:
api-server/src/modules/import-candidate/import-candidate.controller.tsapi-server/src/modules/import-candidate/import-candidate.service.tsapi-server/src/modules/import-candidate/import-candidate.repository.tsapi-server/src/modules/import-candidate/import-candidate.module.ts
修改策略二选一:
- 如果决定彻底废弃候选知识点体系:删除整个模块,并移出所有引用。
- 如果担心一次删太多:先停用对外入口与 worker 写入口,但保留模块代码一版作为过渡。
建议选项:
优先选 彻底废弃。因为当前产品目标已经变为“知识点只能手动生成”,保留审批模块只会制造错误认知和死维护成本。
2.2 模块依赖清理
文件:
api-server/src/modules/rag/rag.module.tsapi-server/src/app.module.ts
修改:
- 移除
ImportCandidateModuleimport。 - 清理随之失效的 provider / constructor 依赖。
2.3 数据模型处理
文件:
api-server/prisma/schema.prisma
建议分两步:
- 第一阶段先保留
ImportCandidate表,不再写入、不再读取,避免一次触发大迁移。 - 第二阶段若确认无回滚需求,再执行 Prisma migration 删除
ImportCandidate模型及关联字段。
这样更稳,先完成行为切换,再做数据库瘦身。
批次 3:改造 Quiz 生成链路为基于资料原文
3.1 新增 Workflow
新文件:
api-server/src/modules/ai/workflows/quiz-generation.workflow.ts
实现建议:
- 注入
AiGatewayService与PrismaService。 - 根据
sourceIds查询KnowledgeChunk。 - 对原文做长度控制,避免 prompt 失控。
- 把
chunk.id一并带入 prompt 上下文,便于返回sourceBlockIds。 - 调用
promptKey = quiz-generation,按 schema 解析。
建议输入结构:
{
userId: string;
knowledgeBaseId: string;
sourceIds: string[];
questionCount: number;
}
3.2 Prompt 改写
文件:
api-server/src/modules/ai/prompts/quiz-generation.prompt.ts
当前问题:
系统提示明确写的是“根据知识点内容创建题目”,这与新目标冲突。
修改方向:
- 明确输入是“学习资料原文/资料分块”。
- 要求题目依据资料事实,不依赖知识点摘要。
- 要求输出尽量回填
sourceBlockIds。 - 继续保留
choice/fill/judge三种题型,兼容现有 iOS 作答页。
3.3 Schema 核对
文件:
api-server/src/modules/ai/prompts/schemas/quiz-generation.schema.ts
当前 schema 基本够用,但要确认:
answer的编码规则继续兼容现有判分逻辑:choice用选项索引字符串judge用"true"/"false"fill用标准答案文本
sourceBlockIds可选且允许返回多个 chunk id。
3.4 注册到 AiModule
文件:
api-server/src/modules/ai/ai.module.ts
修改:
- 将
QuizGenerationWorkflow加入 providers。 - 将
QuizGenerationWorkflow加入 exports。
3.5 改写 QuizService
文件:
api-server/src/modules/quiz/quiz.service.tsapi-server/src/modules/quiz/quiz.module.ts
修改:
create()不再查询knowledgeItem.findMany()。- 改为根据
dto.sourceType / dto.sourceId / dto.knowledgeBaseId决定出题范围。 - 查询
KnowledgeSource,然后将sourceIds传给QuizGenerationWorkflow。 - AI 返回结果后落库到
Quiz与QuizQuestion。 quiz.title可优先使用dto.title,其次result.quizTitle,最后兜底“自测”。quiz.description建议写入result.quizDescription,避免浪费模型输出。
3.6 DTO / Controller 收口
文件:
api-server/src/modules/quiz/quiz.controller.ts
修改建议:
- 不要继续使用
dto: any。 - 显式定义 create dto,至少包含:
knowledgeBaseId: stringtitle?: stringsourceType?: 'kb' | 'source'sourceId?: stringquestionCount?: number
这样才能和 iOS 的“按整库出题 / 按单资料出题”契约对齐。
批次 4:处理 ai-job 的 quiz snapshot 旧设计
这是原计划第二个漏项。
4.1 必须先决策 ai-job 是否仍承载 Quiz 生成
当前仓库同时存在两套 Quiz 生成路径:
quiz.service.ts里的同步生成逻辑。ai-job/quiz-generation-*里的统一 Job Engine 迁移体系。
如果本次不处理这个分叉,后面一定会出现“一个入口按原文出题,一个入口按知识点出题”的双标行为。
4.2 建议方案
建议分两阶段:
- 本次先让线上主链路
QuizService.create()切到KnowledgeSource + KnowledgeChunk。 - 同步把
ai-job/quiz-generation-snapshot-builder.ts改成同样基于 source/chunk 构建 snapshot,至少保证未来迁移方向一致。
4.3 需要修改的点
文件:
api-server/src/modules/ai-job/quiz-generation-snapshot-builder.tsapi-server/src/modules/ai-job/quiz-generation-registration.service.spec.tsapi-server/src/modules/ai-job/quiz-generation-snapshot-builder.spec.ts(如存在)api-server/docs/architecture/m-ai-07-quiz-generation-migration-contract.md
修改:
- snapshot 输入不能再以
knowledgeItems为核心。 - 应改为
knowledgeSources + chunks的截断快照。 knowledgePointIds这类字段若无业务价值,应移除或重新定义。- 契约文档需要同步说明“资料原文出题”而非“知识点出题”。
如果这批不做,至少要在计划里标成 必须跟进,不能留空。
批次 5:iOS 清理候选审批,并补齐按资料出题
5.1 删除候选审批页面与 API
文件:
ios-projects/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swiftios-projects/AIStudyApp/AIStudyApp/Core/Navigation/Route.swiftios-projects/AIStudyApp/AIStudyApp/Core/Services/API/ImportAPI.swiftios-projects/AIStudyApp/AIStudyApp/Core/Services/APIService.swift
修改:
- 删除
ImportReviewPage。 - 删除
Route.importReview(sourceId:)及对应 label / destination。 - 删除
ImportCandidatemodel。 - 删除
ImportCandidateService。 - 同步清理注释与文档中“ImportAPI = DocumentImport + ImportCandidate”的描述。
5.2 扩展 Quiz 创建请求
文件:
ios-projects/AIStudyApp/AIStudyApp/Core/Models/APIModels.swiftios-projects/AIStudyApp/AIStudyApp/Core/Services/API/QuizAPI.swift
修改:
QuizGenerateRequest需要扩展为:
struct QuizGenerateRequest: Codable {
let knowledgeBaseId: String
let sourceType: String?
let sourceId: String?
let questionCount: Int
}
QuizService.generate(...)需要支持:- 按知识库生成
- 按单资料生成
5.3 Quiz UI 文案与交互修正
文件:
ios-projects/AIStudyApp/AIStudyApp/Features/Quiz/QuizViews.swiftios-projects/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swiftios-projects/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialDetailView.swift
修改方向:
QuizViews.swift中“基于知识库内容自动生成测验题目”文案应改为“基于资料内容 AI 生成测验题目”。- 知识库详情页仍可保留“按整库生成测验”入口。
- 资料详情页或资料阅读页建议新增“按当前资料生成测验”入口。
- 如果资料维度新增入口,返回列表/详情时可考虑展示题目来源范围。
5.4 iOS 解码模型补齐
文件:
ios-projects/AIStudyApp/AIStudyApp/Core/Models/Quiz.swift
建议:
- 为
Quiz增加sourceType、sourceId、description字段,便于后续 UI 展示。 - 如果后端继续返回
type而不是questionType,要确认当前QuizQuestion的解码是否正确,否则会出现题型丢失。
4. 推荐执行顺序
第一阶段:先切行为,不动大表
- 批次 1:停掉 Worker 自动候选生成。
- 批次 3:让 Quiz 改为基于资料原文生成。
- 批次 5:iOS 侧删除候选审批死代码,并补齐按资料出题请求参数。
这三步做完,核心业务目标已经成立。
第二阶段:再收废弃系统
- 批次 2:下线 ImportCandidate 模块。
- 批次 4:统一 ai-job quiz snapshot 设计。
- 最后再决定是否通过 Prisma migration 删除
ImportCandidate表及关联字段。
5. 验证清单
后端验证
rag-worker/pipelines/import_pipeline.py运行后不再发出 candidates 请求。api-server中不再出现GENERATING_CANDIDATES相关运行状态依赖。- 调用
POST /quizzes:- 传
knowledgeBaseId能成功出题 - 传
sourceType=source + sourceId能成功按单资料出题
- 传
- 当资料存在但还没有
KnowledgeItem时,Quiz 仍能正常生成。 - 当资料没有 chunk 时,接口返回明确错误,而不是空题或 500。
iOS 验证
- 编译通过,无
ImportReviewPage/ImportCandidateService残余引用。 - 资料阅读页手动 AI 解析仍能正常生成
KnowledgeItem。 - 知识库页生成测验正常。
- 单资料页生成测验正常。
- Quiz 作答、提交、结果页不受影响。
命令建议
python3 -m py_compile /Users/Admin1/Documents/Project/rag-worker/pipelines/import_pipeline.py
cd /Users/Admin1/Documents/Project/api-server && npx tsc --noEmit
iOS 仍需在 Xcode 中完整编译验证。
6. 风险与注意事项
KnowledgeSource.learningStatus目前与候选生成存在隐式绑定,移除候选后要重新定义其状态流转。DocumentImportWorker与 Python RAG Worker 是两条不同导入链路:- 纯文本导入:NestJS Worker 直接 AI 提取知识点
- 文件导入:Python Worker 只做解析索引 这两条链路的产品语义要在文档里写清楚。
ai-job的 Quiz 迁移体系如果暂时不改,后续会再次引入“按知识点出题”的旧逻辑。- 若后续决定删除
ImportCandidate表,需要先确认生产上没有后台/运营侧依赖该数据。
7. 结论
原计划方向基本正确,但范围明显偏小。
最关键的补充有三项:
- 不只是删 Worker 候选生成,还要同步处理
api-server的import-candidate体系。 - 不只是改
quiz.service.ts,还要同步处理ai-job/quiz-generation-*的旧快照设计。 - iOS 不只是删审批页,还要补上“按资料出题”的请求模型、API 调用和入口。
按本修订版执行,才是一次完整的业务语义切换。