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