Files
aurak/docs/plans/2026-04-20-assessment-system-complete-plan.md
T
Developer 0a9588abb7 feat: implement QuestionBank CRUD with pagination and template query
- Add pagination support to findAll (page, limit query params)
- Add findByTemplateId method to service
- Add GET /by-template/:templateId endpoint to controller
- Service already includes CRUD for QuestionBank and QuestionBankItem
2026-04-23 17:19:11 +08:00

593 lines
19 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.
# AuraK 评估系统完整实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 构建完整的AI人才评估系统,包含题库管理、维度均衡抽取、审核流程、评估统计
**Architecture:**
- 题库通过模板间接关联知识库分组
- 评估时从题库维度均衡抽取,不足时实时生成备用
- 管理员通过独立页面管理题库和查看统计
**Tech Stack:** NestJS + TypeORM + LangGraph + React + TypeScript
---
## 一、问题与方案对照表
| # | 待修复问题 | 解决思路 | 对应Task |
|---|------------|----------|----------|
| 1 | 评分计算错误 | 已修复 | ✅ |
| 2 | 题目维度分配不均 | 已修复 | ✅ |
| 3 | 多轮对话中断 | 已修复 | ✅ |
| 4 | 计数器显示错误 | 已修复 | ✅ |
| 5 | 历史问答查询 | 统计页面 | Task 4 |
| 6 | 知识覆盖度来源 | 用户不 care | - |
| 7 | 实时反馈无内容 | 用户确认已有内容 | - |
| 8 | 预生成题目功能 | 题库+抽取 | Task 1,2,3 |
| 9 | 题目生成不稳定 | 题库前置 | Task 1,7 |
| 10 | 题目来源不是知识库 | 题库+范围控制 | Task 1,3 |
| 11 | 选择题实际无选项 | 题库管理 | Task 1,5 |
---
## 二、系统架构
### 2.1 数据层级
```
KnowledgeGroup(知识库分组)
AssessmentTemplate(模板)→ 题目数量、知识库配置
QuestionBank(题库)→ 审核状态
QuestionBankItem(题目)→ 维度/难度/答案
评估抽取使用
```
### 2.2 评估流程
```
开始评估
1. 加载模板配置(题目数量、维度配置)
2. 检查已发布题库题目数量
充足 → 维度均衡随机抽取 → 进入答题
不足 → 实时生成题目(备用) → 进入答题
3. 用户答题 → 即时评分 → 实时反馈
4. 追问(如Grader判断需要)
5. 答题完成 → 生成报告 + 记录统计
```
---
## 三、数据模型
### 3.1 QuestionBank(题库)
```typescript
// server/src/assessment/entities/question-bank.entity.ts
@Entity('question_banks')
export class QuestionBank {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'template_id' })
templateId: string;
@ManyToOne(() => AssessmentTemplate)
@JoinColumn({ name: 'template_id' })
template: AssessmentTemplate;
@Column({ name: 'name' })
name: string; // 题库名称
@Column({ type: 'text', description: '题库描述' })
description: string;
@Column({ type: 'enum', enum: QuestionBankStatus, default: QuestionBankStatus.DRAFT })
status: QuestionBankStatus; // DRAFT | PENDING_REVIEW | PUBLISHED
@Column({ name: 'created_by' })
createdBy: string;
@Column({ name: 'reviewed_by', nullable: true })
reviewedBy: string;
@Column({ name: 'reviewed_at', nullable: true })
reviewedAt: Date;
@Column({ name: 'review_comment', nullable: true })
reviewComment: string; // 审核意见
@OneToMany(() => QuestionBankItem, item => item.bank)
items: QuestionBankItem[];
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
}
enum QuestionBankStatus {
DRAFT = 'DRAFT', // 草稿
PENDING_REVIEW = 'PENDING_REVIEW', // 待审核
PUBLISHED = 'PUBLISHED', // 已发布
REJECTED = 'REJECTED', // 审核拒绝
}
```
### 3.2 QuestionBankItem(题目)
```typescript
// server/src/assessment/entities/question-bank-item.entity.ts
@Entity('question_bank_items')
export class QuestionBankItem {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'bank_id' })
bankId: string;
@ManyToOne(() => QuestionBank, bank => bank.items)
@JoinColumn({ name: 'bank_id' })
bank: QuestionBank;
@Column({ type: 'text', name: 'question_text' })
questionText: string;
@Column({ type: 'enum', enum: QuestionType })
questionType: QuestionType; // SHORT_ANSWER | MULTIPLE_CHOICE | TRUE_FALSE
@Column({ type: 'simple-json', nullable: true })
options: string[]; // 选择题选项 ["A. xxx", "B. xxx", ...]
@Column({ type: 'text', nullable: true })
correctAnswer: string; // 正确答案
@Column({ type: 'simple-json', name: 'key_points' })
keyPoints: string[]; // 关键点 ["point1", "point2"]
@Column({ type: 'enum', enum: QuestionDifficulty })
difficulty: QuestionDifficulty; // STANDARD | ADVANCED | SPECIALIST
@Column({ type: 'enum', enum: QuestionDimension })
dimension: QuestionDimension; // PROMPT | LLM | IDE | DEV_PATTERN | WORK_CAPABILITY
@Column({ type: 'text', name: ' basis', nullable: true })
basis: string; // 出题依据
@Column({ name: 'created_by' })
createdBy: string;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
}
enum QuestionType {
SHORT_ANSWER = 'SHORT_ANSWER', // 简答题
MULTIPLE_CHOICE = 'MULTIPLE_CHOICE', // 选择题
TRUE_FALSE = 'TRUE_FALSE', // 判断题
}
enum QuestionDifficulty {
STANDARD = 'STANDARD', // 标准
ADVANCED = 'ADVANCED', // 进阶
SPECIALIST = 'SPECIALIST', // 专家
}
enum QuestionDimension {
PROMPT = 'PROMPT', // 提示词
LLM = 'LLM', // LLM原理
IDE = 'IDE', // IDE协作
DEV_PATTERN = 'DEV_PATTERN', // 开发范式
WORK_CAPABILITY = 'WORK_CAPABILITY', // 工作能力
}
```
### 3.3 评估统计
```typescript
// API返回结构
interface AssessmentStats {
// 统计卡片
totalAttempts: number; // 总考核次数
highestScore: number; // 最高分
averageScore: number; // 平均分
completionRate: number; // 完成率
// 得分趋势(可选用于图表)
scoreTrend: { date: string; score: number }[];
// 历史记录
recentRecords: {
id: string;
knowledgeBase: string;
template: string;
score: number;
status: 'IN_PROGRESS' | 'COMPLETED';
createdAt: string;
}[];
}
```
---
## 四、API设计
### 4.1 题库管理API
```typescript
// 题库CRUD
POST /api/question-banks // 创建题库
GET /api/question-banks // 列表(带分页)
GET /api/question-banks/:id // 详情(含题目)
PUT /api/question-banks/:id // 更新题库信息
DELETE /api/question-banks/:id // 删除题库
// 题目管理
POST /api/question-banks/:bankId/items // 添加单题
PUT /api/question-banks/:bankId/items/:id // 更新题目
DELETE /api/question-banks/:bankId/items/:id // 删除题目
// 批量操作
POST /api/question-banks/:bankId/batch-add // 批量添加题目(AI生成或Excel导入)
POST /api/question-banks/:bankId/generate // AI批量生成待审核
// 审核流程
PUT /api/question-banks/:id/submit // 提交审核(草稿→待审核)
PUT /api/question-banks/:id/review // 审核(通过/拒绝,附带意见)
PUT /api/question-banks/:id/publish // 发布(审核通过→已发布)
PUT /api/question-banks/:id/unpublish // 下架
// 模板关联
GET /api/question-banks/by-template/:templateId // 按模板查询题库
```
### 4.2 统计API
```typescript
// 评估统计
GET /api/assessment/stats // 当前用户统计
GET /api/assessment/stats/admin // 管理员统计(所有用户)
Query: startDate, endDate, templateId, knowledgeGroupId
```
---
## 五、前端页面设计
### 5.1 题库管理页面(QuestionBankView
```
┌──────────────────────────────────────────────────────────────────────────┐
│ 题库管理 [新建题库] │
├────────────┬─────────────────────────────────────────────────────┤
│ 我的题库 │ 筛选: [模板▼] [状态▼] [维度▼] [难度▼] [搜索...] │
│ ├─────────────────────────────────────────────────────┤
│ □ AI开发 │ ┌────┬────────┬────────┬──────┬────────────┐ │
│ □ Python │ │选择│ 题目内容│ 题型 │ 维度 │ 难度 │ 操作│ │
│ │ ├────┼────────┼────────┼──────┼────────────┤ │
│ │ │ ○ │xxx │ 简答 │ llm │ 标准 │编辑│ │
│ │ │ ○ │xxx │ 选择 │ ide │ 进阶 │编辑│ │
│ │ │ ○ │xxx │ 判断 │ Work │ 专家 │删除│ │
│ │ └────┴────────┴────────┴──────┴────────────┘ │
│ │ [批量导入] │
│ ├─────────────────────────────────────────────────────┤
│ │ [AI批量生成] [提交审核] [全选] [取消选择] │
└────────────┴─────────────────────────────────────────────────────┘
```
### 5.2 题库详情/编辑面板
```
┌────────────────────────────────────┐
│ 编辑题目 [保存] │
├────────────────────────────────────┤
│ 题目内容 │
│ ┌──────────────────────────────┐ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ 题型: ●简答 ○选择 ○判断 │
│ │
│ [选择题时显示] │
│ 选项: │
│ A. ___________________ │
│ B. ___________________ │
│ C. ___________________ │
│ D. ___________________ │
│ │
│ 正确答案: ________________ │
│ │
│ 关键点: │
│ _________________________ │
│ _________________________ │
│ │
│ 维度: [prompt▼] 难度: [标准▼] │
│ │
│ 出题依据: │
│ _________________________ │
└────────────────────────────────────┘
```
### 5.3 统计页面(AssessmentStatsView
```
┌─────────────────────────────────────────────────────────────────┐
│ 评估统计 [导出] │
├─────────────────────────────────────────────────────────────────┤
│ 筛选: [时间范围▼] [知识库▼] [重置] │
├─────────────────────────────────────────────────────────────────┤
│ 总考核 │ 最高分 │ 平均分 │ 完成率 │
│ 15 │ 9.2 │ 7.8 │ 80% │
├─────────────────────────────────────────────────────────────────┤
│ 得分趋势(折线图 - 可选) │
│ 10 ____ │
│ 8 ____▓▓▓____▓▓ │
│ 6 ____▓▓▓▓▓____▓▓▓▓___ │
│ 4 _______________________________________ │
│ 2 ____________________________________________│
│ 04/10 04/12 04/14 04/16 04/18 │
├─────────────────────────────────────────────────────────────────┤
│ 历史记录 │
│ 日期 知识库 模板 得分 状态 操作│
│ 04/18 AI开发 AI基础 9.2 完成 查看 │
│ 04/15 Python 编程基础 7.5 完成 查看 │
│ 04/12 AI开发 AI基础 8.1 完成 查看 │
│ 04/10 - LLM原理 - 进行中 查看 │
└─────────────────────────────────────────────────────────────────┘
```
---
## 六、实施计划
### Task 1: 题库管理功能(核心)
**Files:**
- Create: `server/src/assessment/entities/question-bank.entity.ts`
- Create: `server/src/assessment/entities/question-bank-item.entity.ts`
- Create: `server/src/assessment/question-bank.service.ts`
- Create: `server/src/assessment/question-bank.controller.ts`
- Modify: `server/src/assessment/assessment.module.ts` (import)
- Create: 迁移文件 `add_question_banks.sql`
**实施步骤:**
1. 创建 QuestionBank 实体 + 迁移
2. 创建 QuestionBankItem 实体 + 迁移
3. 实现 QuestionBankServiceCRUD
4. 实现 QuestionBankControllerAPI
5. 注册模块
**验收标准:**
- [ ] 可以创建题库
- [ ] 可以添加/编辑/删除题目
- [ ] 可以设置题目维度/难度/答案
- [ ] 支持审核流程(草稿→待审核→发布)
---
### Task 2: AI批量生成
**目标:** 调用AI批量生成题目,待人工审核
**Files:**
- Modify: `server/src/assessment/question-bank.service.ts` (generate)
- Modify: `server/src/assessment/graph/nodes/generator.node.ts` (复用)
**实施步骤:**
1. 实现批量生成逻辑
2. 调用generator node生成题目
3. 保存为草稿状态
4. 提供审核界面
**验收标准:**
- [ ] 点击生成按钮,调用AI生成题目
- [ ] 生成的题目进入草稿状态
- [ ] 可以批量审核通过
---
### Task 3: 题目抽取逻
**目标:** 评估时从题库维度均衡随机抽取
**Files:**
- Modify: `server/src/assessment/question-bank.service.ts` (selectQuestions)
**实施步骤:**
1. 实现维度均衡抽取算法
2. 处理题目不足情况
3. 单元测试
**验收标准:**
- [ ] 抽取的题目维度大致均衡
- [ ] 不重复抽取同一题目
- [ ] 数量不足时有补充逻辑
---
### Task 4: 评估接入题库
**目标:** 评估优先使用题库题目
**Files:**
- Modify: `server/src/assessment/assessment.service.ts` (startSession)
**实施步骤:**
1. 修改startSession逻辑
2. 先查询题库
3. 题库不足时使用实时生成
4. 测试完整流程
**验收标准:**
- [ ] 有题库时优先从题库抽取
- [ ] 题库不足时使用实时生成
- [ ] 两者都可正常工作
---
### Task 5: 统计页面
**目标:** 独立统计页面
**Files:**
- Create: `server/src/assessment/assessment.controller.ts` (stats API)
- Create: `web/src/services/assessmentStatsService.ts`
- Create: `web/components/views/AssessmentStatsView.tsx`
- Modify: `web/index.tsx` (路由)
- Modify: `web/components/layouts/WorkspaceLayout.tsx` (侧边栏)
**验收标准:**
- [ ] 统计卡片显示正确
- [ ] 支持时间筛选
- [ ] 管理员可查看所有用户
- [ ] 历史记录表格完整
---
### Task 6: 选择题渲染
**目标:** 前端支持选择题渲染
**Files:**
- Modify: `web/components/views/AssessmentView.tsx`
**验收标准:**
- [ ] 选择题显示选项按钮
- [ ] 点击选项提交答案
---
### Task 7: 判断题渲染
**目标:** 前端支持判断题渲染
**Files:**
- Modify: `web/components/views/AssessmentView.tsx`
**验收标准:**
- [ ] 判断题显示True/False按钮
- [ ] 点击提交答案
---
### Task 8: 稳定性优化
**目标:** 提高AI生成稳定性
**Files:**
- Modify: `server/src/assessment/graph/nodes/generator.node.ts`
**实施步骤:**
1. 添加重试逻辑
2. 优化prompt
3. 错误处理
---
## 七、执行顺序
```
Task 1 (题库管理)
Task 2 (AI批量生成)
Task 3 (抽取逻辑)
Task 4 (评估接入)
Task 5 (统计页面)
Task 6 (选择题)
Task 7 (判断题)
Task 8 (稳定性)
```
---
## 八、技术要点
### 8.1 维度均衡算法
```typescript
// 伪代码
function selectQuestions(bankId: string, count: number): Question[] {
const allItems = getPublishedItems(bankId);
const dimensions = ['prompt', 'llm', 'ide', 'devPattern', 'workCapability'];
const selected: Question[] = [];
const usedIds = new Set<string>();
// 轮询从各维度选取
let dimIdx = 0;
while (selected.length < count && dimIdx < count * dimensions.length) {
const dim = dimensions[dimIdx % dimensions.length];
const available = allItems.filter(
(i) => i.dimension === dim && !usedIds.has(i.id)
);
if (available.length > 0) {
const random = available[Math.floor(Math.random() * available.length)];
selected.push(random);
usedIds.add(random.id);
}
dimIdx++;
}
// 补充不足的题目(随机)
if (selected.length < count) {
const remaining = allItems.filter((i) => !usedIds.has(i.id));
// ... 随机补充
}
return selected;
}
```
### 8.2 审核工作流
```
草稿(DRAFT) → 提交审核 → 待审核(PENDING_REVIEW)
审核通过 → 已发布(PUBLISHED)
审核拒绝 → 草稿(DRAFT) + 意见
```
---
## 九、注意事项
1. **权限控制**:题库管理、统计只对管理员开放
2. **数据隔离**:用户只能看到自己的评估记录,管理员可见所有
3. **维度映射**:确保题目维度与评估维度一致
4. **题库状态**:只有已发布的题目才能被抽取
5. **回退机制**:题库不足时使用实时生成作为备用
---
## Plan Complete
**Two execution options:**
**1. Subagent-Driven (this session)** - 继续在此会话,使用 subagent 逐个执行任务
**2. Parallel Session (separate)** - 开新会话使用 executing-plans,分批执行
**Which approach?**