startup-plan/技术设计/去掉自动生成知识点并改为基于KnowledgeSource出题-修订计划.md

469 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 去掉自动生成知识点并改为基于 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. 契约文档需要同步说明“资料原文出题”而非“知识点出题”。
如果这批不做,至少要在计划里标成 `必须跟进`,不能留空。
---
### 批次 5iOS 清理候选审批,并补齐按资料出题
#### 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. 批次 5iOS 侧删除候选审批死代码,并补齐按资料出题请求参数。
这三步做完,核心业务目标已经成立。
### 第二阶段:再收废弃系统
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 调用和入口。
按本修订版执行,才是一次完整的业务语义切换。