- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
28 KiB
28 KiB
测试用例 — AI自进化功能模块
1. 需求依据
- PRD路径:
/data/share/project/docs/PRD-self-evolution.md - 文档版本:v1.0(2026-05-26)
- 功能概述:Qihu-AIFlow平台AI自进化功能。每个用户的AI在对话中持续学习进化,前端通过「AI进化驾驶舱」展示进化过程。深度整合xSkill开源框架,实现对话轨迹→原子任务→技能聚类→技能生成→灰度验证的完整流水线。
需求侧功能模块(共6大模块)
| 编号 | 需求模块 | PRD章节 | 功能点数 |
|---|---|---|---|
| R1 | 默认全局持续进化 | 7.2-功能一 | 5 |
| R2 | 对话自动分析(xSkill流水线) | 7.2-功能二 | 7 |
| R3 | 技能库管理 | 7.2-功能三 | 6 |
| R4 | AI进化驾驶舱(4个子面板) | 7.2-功能四 | 14 |
| R5 | 与AI聊天无缝衔接 | 7.2-功能五 | 5 |
| R6 | 数据隔离(用户级别) | 7.2-功能六 | 6 |
2. 代码功能模块概览
2.1 后端代码模块
| 模块 | 说明 | 主要代码位置 |
|---|---|---|
| 技能管理API | 技能CRUD、冻结/解冻、历史/轨迹查询 | backend/python/app/api/evolution.py:33-139 |
| 进化事件API | 进化事件列表查询 | backend/python/app/api/evolution.py:143-157 |
| 驾驶舱数据API | 技能树/时间线/统计/画像4面板数据 | backend/python/app/api/evolution.py:160-202 |
| 轨迹提交API | 对话轨迹提交入口 | backend/python/app/api/evolution.py:205-214 |
| EvolutionService | 核心业务逻辑层,12个公开方法 | backend/python/app/services/evolution_service.py |
| 数据模型(ORM) | 4张表定义 | backend/python/app/models.py:741-792 |
| 路由注册 | prefix="/app/api/evolution" | backend/python/main.py:140 |
2.2 前端代码模块
| 模块 | 说明 | 主要代码位置 |
|---|---|---|
| AI进化驾驶舱页面 | 主页面组件(4面板切换+数据加载) | frontend/src/pages/front/EvolutionDashboard.tsx (631行) |
| 驾驶舱样式 | 完整CSS样式 | frontend/src/pages/front/EvolutionDashboard.css (1019行) |
| 技能图谱面板 | Panel A:按分类分组展示技能节点 | EvolutionDashboard.tsx:48-152 |
| 进化时间线面板 | Panel B:垂直时间线+事件筛选 | EvolutionDashboard.tsx:155-201 |
| 技能使用统计面板 | Panel C:排行+趋势图+饼图+提醒 | EvolutionDashboard.tsx:205-340 |
| AI能力画像面板 | Panel D:等级环+雷达图+标签云+建议 | EvolutionDashboard.tsx:344-500 |
| evolutionAPI | 前端API调用定义(15个方法) | frontend/src/api/index.ts:415-455 |
| 类型定义 | TypeScript interfaces | frontend/src/types/index.ts:381-478 |
| 路由配置 | #/evolution |
frontend/src/App.tsx:98 |
| 导航入口 | 侧边栏"AI进化" | frontend/src/components/layout/FrontLayout.tsx:14 |
3. 需求与代码差异说明
3.1 PRD中未在代码中体现的内容
| 编号 | PRD功能点 | 状态 | 说明 |
|---|---|---|---|
| GAP-01 | 技能回滚:POST /skills/{name}/rollback/{version} | ❌ 未实现 | PRD明确要求回滚到历史版本,但API中无此端点,前端无回滚UI |
| GAP-02 | 对话溯源跳转:点击来源对话可跳转到原始对话记录 | ❌ 未实现 | PRD要求"点击可跳转到原始对话记录",前端技能详情弹窗中有来源轨迹按钮但仅调用API(evolutionAPI.getSkillTrajectories),后端 get_skill_trajectories 返回的是用户最近20条轨迹而非该技能的关联轨迹 |
| GAP-03 | 新技能萌芽动画 | ❌ 未实现 | PRD面板A中要求"新技能生成时,技能树上有「萌芽」动画效果",前端代码中未找到相关CSS动画 |
| GAP-04 | 时间线导出 | ❌ 未实现 | PRD面板B要求"支持将进化时间线导出为图片或PDF分享",前端代码中无导出功能 |
| GAP-05 | 画像分享卡片 | ❌ 未实现 | PRD面板D要求"支持将AI能力画像生成为分享卡片",前端代码中无此功能 |
| GAP-06 | Skill加载提示标签 | ❌ 未实现 | PRD功能五要求"在消息下方显示小标签:🚀 使用了你的专属技能:XXX",代码中未见此实现 |
| GAP-07 | 对话结束钩子(自动提交轨迹) | ❌ 未自动触发 | PRD要求对话结束时自动提交轨迹,但当前仅提供手动API入口 /trajectory/submit,前端ChatPage中未见对话结束自动调用的钩子 |
| GAP-08 | Watcher线程管理 | ❌ 未实现 | PRD要求用户注册后自动启动watcher线程持续进化,UserEvolutionConfig模型已定义但服务层无读写该表的代码,watcher状态管理未实现 |
| GAP-09 | xSkill管线自动调度 | ❌ 未实现 | PRD要求轨迹进入xSkill流水线自动处理,但后端仅实现轨迹落盘(status=discovered),后续状态流转(splitting→...→done)依赖外部xSkill进程 |
| GAP-10 | 统计图表的交互联动 | ❌ 未实现 | PRD面板C要求"点击统计图表中的任意项可跳转到对应技能详情",前端StatisticsPanel中无此交互 |
3.2 PRD中未覆盖但代码中存在的功能模块
| 编号 | 代码模块 | 位置 | 说明 | 建议产品确认 |
|---|---|---|---|---|
| EXTRA-01 | evolutionAPI.submitTrajectory 提供独立的手动轨迹提交入口 | api/index.ts:453 |
前端API层暴露了轨迹提交方法,但在当前驾驶舱页面中未被任何组件调用 | 是否需要在驾驶舱中增加手动提交轨迹的入口? |
| EXTRA-02 | 能力维度雷达图使用内联SVG实现 | EvolutionDashboard.tsx:398-466 |
前端用内联SVG而非第三方图表库绘制雷达图,包含5层参考多边形、轴线、数据多边形 | 技术决策,无需产品确认 |
4. 测试用例
4.1 技能管理 API
TC-SKILL-01 [P0] 验证获取当前用户技能列表
- 前置条件:用户已登录;数据库中存在该用户的技能数据
- 步骤:
[1] 使用有效JWT Token调用
GET /app/api/evolution/skills[2] 检查响应状态码和结构 - 预期结果:
- HTTP 200,响应包含
code: 0 data为数组,每项包含id, skill_name, description, tags, category, state, use_count, version, created_at, updated_at- 仅返回当前用户的技能数据,不包含其他用户的技能
- HTTP 200,响应包含
- 备注:默认按
updated_at降序排列
TC-SKILL-02 [P0] 验证按状态过滤技能
- 前置条件:数据库中存在不同状态的技能
- 步骤:
[1] 调用
GET /app/api/evolution/skills?state=baby[2] 调用GET /app/api/evolution/skills?state=main[3] 调用GET /app/api/evolution/skills?state=frozen[4] 调用GET /app/api/evolution/skills?state=staging - 预期结果:
- 每次返回的技能
state字段均匹配查询参数 - 传入无效状态值时系统行为一致(返回空列表或400错误)
- 每次返回的技能
- 备注:P0 覆盖核心过滤逻辑
TC-SKILL-03 [P0] 验证按关键词搜索技能
- 前置条件:数据库中存在技能名包含"python"和"写作"的技能
- 步骤:
[1] 调用
GET /app/api/evolution/skills?keyword=python[2] 调用GET /app/api/evolution/skills?keyword=写作[3] 调用GET /app/api/evolution/skills?keyword=不存在的关键词 - 预期结果:
- [1][2] 返回
skill_name包含关键词的技能 - [3] 返回空列表
- [1][2] 返回
- 备注:模糊匹配
TC-SKILL-04 [P1] 验证技能列表排序功能
- 前置条件:数据库中存在多条技能记录
- 步骤:
[1] 调用
GET /app/api/evolution/skills?sort_by=use_count&order=desc[2] 调用GET /app/api/evolution/skills?sort_by=created_at&order=asc[3] 调用GET /app/api/evolution/skills?sort_by=invalid_field - 预期结果:
- [1] 按使用次数降序排列
- [2] 按创建时间升序排列
- [3] 返回错误或默认排序回退
TC-SKILL-05 [P0] 验证获取技能详情
- 前置条件:数据库中存在技能名为"fix_python_bug"的技能
- 步骤:
[1] 调用
GET /app/api/evolution/skills/fix_python_bug[2] 调用GET /app/api/evolution/skills/不存在的技能名 - 预期结果:
- [1] HTTP 200,返回完整技能信息(含
skill_md_content字段) - [2] HTTP 404,提示技能不存在
- [1] HTTP 200,返回完整技能信息(含
- 备注:如果
skill_md_path文件存在,skill_md_content应包含文件内容
TC-SKILL-06 [P0] 验证编辑技能元数据
- 前置条件:存在可编辑的技能
- 步骤:
[1] 调用
PUT /app/api/evolution/skills/{skill_name},body:{"description": "新描述", "tags": ["新标签"], "category": "新分类"}[2] 再次获取该技能详情,检查变更 [3] 检查进化事件表中是否记录了编辑事件 - 预期结果:
- [1] HTTP 200,返回更新后的技能对象,
version字段 +1 - [2] 技能详情反映变更
- [3]
evolution_events表中新增一条 event_type="edited" 的记录,含 version_before/version_after
- [1] HTTP 200,返回更新后的技能对象,
- 备注:仅可编辑 description, tags, category
TC-SKILL-07 [P0] 验证删除技能
- 前置条件:存在可删除的技能
- 步骤:
[1] 记录删除前的技能列表总数 N
[2] 调用
DELETE /app/api/evolution/skills/{skill_name}[3] 调用GET /app/api/evolution/skills检查技能列表 [4] 调用GET /app/api/evolution/skills/{skill_name}确认已不存在 [5] 检查进化事件表中是否记录了删除事件 - 预期结果:
- [2] HTTP 200
- [3] 技能列表总数为 N-1
- [4] HTTP 404
- [5]
evolution_events表中新增 event_type="deleted" 的记录
- 备注:删除后事件表仍保留历史记录
TC-SKILL-08 [P0] 验证冻结技能
- 前置条件:存在一个 state 为 "main" 的技能
- 步骤:
[1] 调用
POST /app/api/evolution/skills/{skill_name}/freeze[2] 调用GET /app/api/evolution/skills/{skill_name}确认状态 [3] 对已冻结的技能再次调用 freeze - 预期结果:
- [1] HTTP 200,state 变为 "frozen"
- [2] state 为 "frozen"
- [3] 幂等操作,不报错(state 保持 "frozen")
- 事件表中新增 event_type="frozen" 的记录
TC-SKILL-09 [P0] 验证解冻技能
- 前置条件:存在一个 state 为 "frozen" 的技能
- 步骤:
[1] 调用
POST /app/api/evolution/skills/{skill_name}/unfreeze[2] 调用GET /app/api/evolution/skills/{skill_name}确认状态 - 预期结果:
- [1] HTTP 200,state 变为 "main"
- [2] state 为 "main"
- 事件表中新增 event_type="unfrozen" 的记录
TC-SKILL-10 [P1] 验证技能版本历史查询
- 前置条件:某技能经历多次编辑/冻结/解冻操作
- 步骤:
[1] 调用
GET /app/api/evolution/skills/{skill_name}/history[2] 检查返回的事件列表 - 预期结果:
- HTTP 200,返回按照
created_at排序的事件列表 - 每项包含 event_type, description, version_before, version_after, created_at
- 支持未操作过的技能(返回空数组)
- HTTP 200,返回按照
TC-SKILL-11 [P1] 验证技能来源轨迹查询
- 前置条件:存在技能和对应的轨迹数据
- 步骤:
[1] 调用
GET /app/api/evolution/skills/{skill_name}/trajectories - 预期结果:
- HTTP 200
- 返回该技能的关联轨迹列表(最多20条)
- 备注:后端
get_skill_trajectories当前仅按 user_id 过滤,未按 skill_name 过滤——此为缺陷,已记录在 3.1 GAP-02
4.2 进化事件 API
TC-EVENT-01 [P0] 验证获取进化事件列表
- 前置条件:数据库中已有多条进化事件记录
- 步骤:
[1] 调用
GET /app/api/evolution/events[2] 检查返回的事件列表 - 预期结果:
- HTTP 200,最多返回50条(默认 limit)
- 每项包含 id, skill_name, event_type, description, version_before, version_after, created_at
- 仅返回当前用户的进化事件
TC-EVENT-02 [P1] 验证按事件类型筛选
- 前置条件:数据库中有不同类型的进化事件
- 步骤:
[1] 调用
GET /app/api/evolution/events?event_type=created[2] 调用GET /app/api/evolution/events?event_type=frozen[3] 调用GET /app/api/evolution/events?event_type=edited - 预期结果:每次返回的事件类型均匹配筛选条件
TC-EVENT-03 [P1] 验证按技能名筛选事件
- 前置条件:某技能有多个关联事件
- 步骤:
[1] 调用
GET /app/api/evolution/events?skill_name={skill_name} - 预期结果:仅返回该技能相关的进化事件
TC-EVENT-04 [P2] 验证limit参数边界
- 前置条件:数据库中有大量事件记录
- 步骤:
[1] 调用
GET /app/api/evolution/events?limit=1[2] 调用GET /app/api/evolution/events?limit=200[3] 调用GET /app/api/evolution/events?limit=201[4] 调用GET /app/api/evolution/events?limit=0 - 预期结果:
- [1] 返回1条
- [2] 返回最多200条
- [3] 返回错误或限制到200
- [4] 返回错误或默认50条
4.3 驾驶舱面板 API
TC-DASH-01 [P0] 验证技能图谱数据 (dashboard/skill-tree)
- 前置条件:用户拥有多个分类的技能
- 步骤:
[1] 调用
GET /app/api/evolution/dashboard/skill-tree[2] 检查返回结构 - 预期结果:
- HTTP 200
data.categories为数组,每组包含name和skills[]data包含统计字段:total_count, main_count, baby_count, staging_count, frozen_count- 技能数据仅限当前用户
TC-DASH-02 [P0] 验证进化时间线数据 (dashboard/timeline)
- 前置条件:用户有进化事件记录
- 步骤:
[1] 调用
GET /app/api/evolution/dashboard/timeline[2] 检查返回数据 - 预期结果:
- HTTP 200
- 返回事件列表,按 created_at 降序
- 支持
event_type筛选参数 - 无事件时返回空数组
TC-DASH-03 [P0] 验证技能使用统计 (dashboard/statistics)
- 前置条件:用户有使用统计的历史数据
- 步骤:
[1] 调用
GET /app/api/evolution/dashboard/statistics[2] 检查返回数据结构 - 预期结果:
- HTTP 200
data包含字段:ranking[](TOP N排行)、total_use_count、category_distribution[]、daily_trend[]、low_use_skills[]ranking中每项含skill_name, category, use_count, state, last_used_atdaily_trend数据点含date, count
TC-DASH-04 [P0] 验证AI能力画像 (dashboard/profile)
- 前置条件:用户有足够的技能数据支撑画像计算
- 步骤:
[1] 调用
GET /app/api/evolution/dashboard/profile[2] 检查返回结构 - 预期结果:
- HTTP 200
data包含字段:level(1-10的等级)、level_title(等级称号)、total_skills、active_skills、staging_skills、baby_skills、total_use_countdata.dimensions为数组,每项含label, value, maxValuedata.tags为能力标签数组data.growth_suggestions为成长建议数组
TC-DASH-05 [P1] 验证驾驶舱数据空态响应
- 前置条件:用户为新用户,无任何进化数据
- 步骤: [1] 分别调用4个驾驶舱API端点 [2] 检查响应
- 预期结果:均返回 HTTP 200,数据字段为空数组或零值,不报错
4.4 轨迹提交 API
TC-TRAJ-01 [P0] 验证提交对话轨迹
- 前置条件:用户已登录
- 步骤:
[1] 调用
POST /app/api/evolution/trajectory/submitbody:{"conversation_id": "conv-001", "messages": [{"role": "user", "content": "帮我写一个Python函数"}, {"role": "assistant", "content": "好的..."}]}[2] 检查响应 [3] 检查数据库evolution_trajectories表中新增记录 [4] 检查文件系统中轨迹文件是否生成 - 预期结果:
- [1] HTTP 200,
message为"轨迹已提交" - [2]
data包含 id, conversation_id, file_path, status="discovered", atom_count=0 - [3] 数据库中有新记录,
user_id为当前用户 - [4] 轨迹文件存在于
/data/share/project/data/users/{user_id}/trajectories/traj_{id}/traj_{id}.md
- [1] HTTP 200,
TC-TRAJ-02 [P1] 验证提交轨迹消息格式兼容性
- 前置条件:用户已登录
- 步骤: [1] 提交包含多条轮次对话的轨迹 [2] 读取生成的轨迹文件内容
- 预期结果:
- 轨迹文件格式为xSkill兼容的Markdown:
# Conversation Trajectory+## User/## Assistant交替分段 token_used正确累计所有消息的 tokens 字段
- 轨迹文件格式为xSkill兼容的Markdown:
TC-TRAJ-03 [P1] 验证轨迹提交数据隔离
- 前置条件:两个不同用户(userA 和 userB)
- 步骤:
[1] userA 提交一个轨迹
[2] userB 调用
GET /app/api/evolution/events查询事件 [3] userB 调用GET /app/api/evolution/skills查询技能 - 预期结果:
- [2] userB 看不到 userA 的轨迹数据
- [3] userB 的技能数据不包含 userA 的技能
TC-TRAJ-04 [P2] 验证提交空消息列表
- 前置条件:用户已登录
- 步骤:
[1] 调用
POST /app/api/evolution/trajectory/submit,body:{"conversation_id": "conv-empty", "messages": []} - 预期结果:
- 返回成功(或合理的错误提示"消息列表不能为空")
- 轨迹状态正确记录
4.5 数据隔离
TC-ISOL-01 [P0] 验证用户A不能访问用户B的技能
- 前置条件:userA 和 userB 各自有技能数据
- 步骤:
[1] 使用 userA 的Token调用
GET /app/api/evolution/skills[2] 使用 userB 的Token调用GET /app/api/evolution/skills[3] 使用 userA 的Token调用GET /app/api/evolution/skills/{userB的skill_name} - 预期结果:
- [1] 仅返回 userA 的技能
- [2] 仅返回 userB 的技能
- [3] HTTP 404,userA 无法通过猜解技能名获取 userB 的数据
TC-ISOL-02 [P0] 验证用户A不能操作用户B的技能
- 前置条件:userA 和 userB 各自有技能
- 步骤:
[1] 使用 userA 的Token调用
PUT /app/api/evolution/skills/{userB的skill_name}尝试编辑 [2] 使用 userA 的Token调用DELETE /app/api/evolution/skills/{userB的skill_name}尝试删除 [3] 使用 userA 的Token调用POST /app/api/evolution/skills/{userB的skill_name}/freeze尝试冻结 - 预期结果:所有操作返回 HTTP 404(技能不存在),不修改 userB 的数据
TC-ISOL-03 [P1] 验证用户A不能查看用户B的进化事件
- 前置条件:两个用户都有进化事件
- 步骤:
[1] 使用 userA 的Token调用
GET /app/api/evolution/events[2] 检查返回列表中是否出现 userB 的技能名 - 预期结果:不包含任何 userB 相关的事件
TC-ISOL-04 [P1] 验证用户A不能查看用户B的驾驶舱数据
- 前置条件:两个用户都有进化数据
- 步骤: [1] 使用 userA 的Token分别调用4个驾驶舱API [2] 检查返回数据中是否包含 userB 的技能/事件信息
- 预期结果:所有驾驶舱API仅返回当前用户的数据
4.6 前端—AI进化驾驶舱页面
TC-FE-01 [P0] 验证驾驶舱页面加载
- 前置条件:用户已登录(admin账号)
- 步骤:
[1] 导航到
#/evolution[2] 观察页面加载过程 [3] 检查页面标题和统计摘要 - 预期结果:
- 页面正常渲染,显示"AI进化驾驶舱"标题
- 加载过程中显示 loading spinner + "正在加载进化数据..." 文字
- 加载完成后显示统计数字(技能总数/已成熟/萌芽中)
- Console 无JS错误
TC-FE-02 [P0] 验证技能图谱面板(Panel A)
- 前置条件:用户有技能数据
- 步骤: [1] 切换到"技能图谱"面板 [2] 检查技能节点展示 [3] 鼠标悬停在技能节点上 [4] 点击某个技能节点
- 预期结果:
- 技能按 category 分组展示,不同状态用不同颜色标识(baby=粉色/main=绿色/staging=橙色/frozen=灰色)
- hover 显示 tooltip(技能名+描述+版本+更新时间)
- 点击弹出详情弹窗(名称/状态/版本/使用次数/分类/标签/操作按钮)
- 底部有状态色标图例
- 无技能时显示空态
TC-FE-03 [P0] 验证进化时间线面板(Panel B)
- 前置条件:用户有进化事件数据
- 步骤: [1] 切换到"进化时间线"面板 [2] 检查时间线布局 [3] 点击不同的筛选按钮(全部/诞生/晋升/里程碑/更新/冻结/退回) [4] 检查事件卡片内容
- 预期结果:
- 垂直时间线展示,左侧圆点+连接线,右侧卡片
- 卡片显示:事件类型+技能名+描述+时间戳
- 筛选按钮正常工作,筛选结果正确
- 无事件时显示空态
TC-FE-04 [P0] 验证技能使用统计面板(Panel C)
- 前置条件:用户有使用统计数据
- 步骤: [1] 切换到"技能使用统计"面板 [2] 检查技能使用排行 [3] 检查调用趋势图 [4] 检查类别分布饼图 [5] 检查低使用技能提醒(如有)
- 预期结果:
- 排行条形图正常渲染,Top 3 带紫色渐变标记
- 趋势柱状图显示 Top 4 技能
- 饼图中心显示总调用次数,图例显示各分类占比
- 超过30天未使用的技能显示黄色警告提醒
- 无数据时显示"暂无数据"
- Panel切换时按需加载统计数据
TC-FE-05 [P0] 验证AI能力画像面板(Panel D)
- 前置条件:用户有足够的技能数据
- 步骤: [1] 切换到"AI能力画像"面板 [2] 检查等级卡片 [3] 检查能力雷达图 [4] 检查能力标签云 [5] 检查成长建议
- 预期结果:
- 等级卡片显示环形进度条(level/10)+ 等级数字 + 称号 + 四项统计
- 雷达图显示能力维度(文字创作/数据分析/代码能力/知识问答/流程自动化/创意生成等)
- 标签云有4级尺寸和6种颜色循环
- 成长建议列表以黄色星标开头
- 无画像数据时显示空态
TC-FE-06 [P1] 验证面板切换
- 前置条件:页面已加载
- 步骤: [1] 依次点击4个面板切换按钮 [2] 来回切换 panels [3] 切换过程中检查加载状态
- 预期结果:
- 面板切换流畅,当前面板高亮
- statistics 和 profile 面板首次进入时显示"加载中..."
- 返回已加载过的面板不重新请求数据
TC-FE-07 [P1] 验证错误状态和重试
- 前置条件:页面已加载
- 步骤: [1] 模拟后端API返回错误(或断开网络) [2] 观察页面反应 [3] 点击"重试"按钮
- 预期结果:
- 页面显示错误消息
- "重试"按钮可见且可用
- 点击"重试"重新发起API请求
TC-FE-08 [P1] 验证未登录状态
- 前置条件:用户未登录
- 步骤:
[1] 清除 localStorage 中的 token
[2] 导航到
#/evolution - 预期结果:自动重定向到
#/login
TC-FE-09 [P2] 验证CSS样式完整性
- 前置条件:页面正常加载
- 步骤: [1] 检查所有面板的CSS类名是否存在 [2] 检查loading spinner动画是否流畅 [3] 检查响应式布局(不同分辨率下)
- 预期结果:
- 所有CSS类在
EvolutionDashboard.css中有定义 - 旋转动画正常工作
- 各分辨率下布局正常
- 所有CSS类在
4.7 前端—技能管理UI(技能详情弹窗中的操作)
TC-FE-MGR-01 [P1] 验证技能详情弹窗中显示操作按钮
- 前置条件:在技能图谱面板中点击某技能节点
- 步骤: [1] 检查弹窗内容 [2] 确认操作按钮可见
- 预期结果:
- 弹窗显示技能完整信息
- 包含编辑/删除/冻结(或解冻)等操作按钮
- 操作按钮根据技能状态正确切换(已冻结显示"解冻",否则显示"冻结")
TC-FE-MGR-02 [P1] 验证前端调用冻结API
- 前置条件:弹窗中显示"冻结"按钮
- 步骤: [1] 点击"冻结"按钮 [2] 观察反馈 [3] 刷新技能列表确认状态变化
- 预期结果:
- API调用
/app/api/evolution/skills/{name}/freeze成功 - 技能状态变为"frozen"
- 前端显示操作成功提示
- API调用
TC-FE-MGR-03 [P1] 验证前端调用删除API
- 前置条件:弹窗中显示"删除"按钮
- 步骤: [1] 点击"删除"按钮 [2] 确认删除(如有确认弹窗) [3] 观察技能列表变化
- 预期结果:
- API调用
DELETE /app/api/evolution/skills/{name}成功 - 技能从列表中移除
- 前端显示删除成功提示
- API调用
4.8 数据库表结构验证
TC-DB-01 [P0] 验证 evolution_skills 表结构
- 前置条件:数据库连接正常
- 步骤: [1] 查询表结构 [2] 验证所有字段存在且类型正确
- 预期结果:表包含字段: id(PK), user_id(FK+INDEX), skill_name, description, tags(JSON), category, state, use_count, version, skill_md_path, git_commit_sha, last_used_at, created_at, updated_at
TC-DB-02 [P0] 验证 evolution_events 表结构
- 前置条件:数据库连接正常
- 步骤: [1] 查询表结构
- 预期结果:表包含字段: id(PK), user_id(FK+INDEX), skill_name, event_type, description, version_before, version_after, created_at
TC-DB-03 [P0] 验证 evolution_trajectories 表结构
- 前置条件:数据库连接正常
- 步骤: [1] 查询表结构
- 预期结果:表包含字段: id(PK), user_id(FK+INDEX), conversation_id, file_path, status, atom_count, token_used, created_at, processed_at
TC-DB-04 [P0] 验证 user_evolution_config 表结构
- 前置条件:数据库连接正常
- 步骤: [1] 查询表结构
- 预期结果:表包含字段: id(PK), user_id(FK+UNIQUE+INDEX), skill_dir_path, trajectory_dir_path, watcher_status, created_at, updated_at
4.9 技能状态流转验证
TC-STATE-01 [P0] 验证技能状态 baby → main
- 前置条件:存在 state="baby" 的技能
- 步骤:
[1] 通过数据层直接更新该技能 state="main"
[2] 调用
GET /app/api/evolution/skills/{skill_name}确认状态 - 预期结果:状态成功变更为 main
TC-STATE-02 [P0] 验证技能状态 main → staging → main
- 前置条件:存在 state="main" 的技能
- 步骤: [1] 更新 state="staging" [2] 查询确认 [3] 更新 state="main" [4] 查询确认
- 预期结果:状态变更成功
TC-STATE-03 [P0] 验证技能状态 main → frozen → main
- 前置条件:存在 state="main" 的技能
- 步骤: [1] 调用 freeze API [2] 确认 state="frozen" [3] 调用 unfreeze API [4] 确认 state="main"
- 预期结果:完整走通 freeze→unfreeze 循环,事件表记录完整
5. 测试用例统计
| 类别 | P0 | P1 | P2 | 合计 |
|---|---|---|---|---|
| 技能管理 API | 7 | 2 | 0 | 9 |
| 进化事件 API | 1 | 2 | 1 | 4 |
| 驾驶舱面板 API | 4 | 1 | 0 | 5 |
| 轨迹提交 API | 1 | 2 | 1 | 4 |
| 数据隔离 | 2 | 2 | 0 | 4 |
| 前端驾驶舱页面 | 4 | 3 | 1 | 8 |
| 前端技能管理UI | 0 | 3 | 0 | 3 |
| 数据库表结构 | 4 | 0 | 0 | 4 |
| 技能状态流转 | 3 | 0 | 0 | 3 |
| 合计 | 26 | 15 | 3 | 44 |
6. PRD未覆盖的代码功能模块(需产品确认)
| 编号 | 模块 | 代码位置 | 建议确认项 |
|---|---|---|---|
| EXTRA-01 | 独立轨迹提交API | api/index.ts:453 |
驾驶舱页面是否需要手动轨迹提交入口? |
| EXTRA-02 | SVG内联雷达图 | EvolutionDashboard.tsx:398-466 |
技术决策,无需产品确认 |
7. 测试环境要求
- 后端服务:Python FastAPI 服务运行于 localhost:18080
- 前端服务:React 应用通过 Nginx 代理运行于 8080 端口
- 数据库:MySQL 8 (localhost:3306, root/pwd2026)
- 认证方式:JWT Bearer Token,通过 POST /app/api/auth/login 获取
- 测试用户:admin (管理员), demo_user (普通用户)
文档生成时间:2026-05-29
文档版本:v1.0
基于代码版本:evolution.py (215行) + evolution_service.py (473行) + EvolutionDashboard.tsx (631行)
PRD版本:v1.0 (2026-05-26)