Files
pj0235-eai_agentplatform/docs/完整分析_pj0231_vs_pj006-zhilianyuan2.md
T
eaiadminandClaude Code 16d63de4e1 chore: 工作台产品化进行中的改动
把工作区里其余在制品一并入库,主要是工作台产品化的推进:

  后端:新增 capability_definition / project / my_app_center / office_skill
        接口与 action_definition / skill_definition / project / user_app_center
        模型,config 加路由健康上报。
  前端:新增 frontend/src/skills(Office 技能与 workbuddy 复刻)、
        项目管理、应用中心、能力目录页,以及配套 api / store / config;
        聊天侧新增 SpecialistChip / SpecialistPanel / SkillStrip / AppChatRail
        等组件。
  清理:移除旧 views/tools 下的单页工具(已并入工作台)、_frozen 冻结组件、
        cmd/inspect_oa_debug 调试入口,以及两份调试笔记。
  其它:文档与启动脚本同步。

(这批改动与上一提交的 SY23 工作并行进行,此前已在同一工作区内交织。)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 21:32:35 +08:00

384 lines
17 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.
# pj0231-eai_agentplatforming 完整分析报告(对比 + 差距 + 路线图 + 设计稿)
> 版本:V1.0 | 日期:2026-08-16
> 对比对象:pj006-zhilianyuan2(智练猿)
> 范围:知识库能力、考试能力、测试/自测能力、岗位与知识对应能力
> 原则:以**实际代码**为准(本项目 PRD 文档存在过时,见 §1.3)
>
> **注:** 本文基于 2026-08-16 代码快照(后端 Go + Gin + GORM,检索为 brute-force 余弦 + bge-m3);数据库与向量栈已决策迁移至 **MySQL 8.0 + FAISS**,见 `docs/db_schema.md` / `docs/deploy.md`。
---
## 目录
- Part A —— 现状对比
- Part B —— 差距汇总与反向差异
- Part C —— 改进路线图
- Part D —— 设计稿:岗位与知识对应能力(核心)
- Part E —— 设计要点:其余改进项
- Part F —— 待拍板决策点与下一步
---
# Part A —— 现状对比
## A1. 一句话结论
两个项目**不是同一量级**:
- **pj0231** 是一个**极简单租户、内部专属培训平台**——Go 后端约 4,900 行,11 张表,2 个角色,聚焦「看资料 → AI 答疑 → 考试验收」的窄闭环。
- **pj006** 是一个**多租户、多角色、带完整 AI 对练 / 岗位能力模型的通用销售培训平台**——Python 后端约 1.7 万行 + 独立 engine 库,23 版 schema、40+ 张表,4 个前端。
在四个对比维度上,pj006 全部**能力更全、更深**;但 pj0231 在个别点上有「更实」的实现(见 §B3)。
## A2. 系统定位与技术架构
| 维度 | pj0231 | pj006 |
|---|---|---|
| 定位 | 博昇内部培训(单企业) | 通用 AI 销售培训平台(多企业 SaaS) |
| 后端 | Go + Gin + GORM + SQLite | Python 3.10 + FastAPI + SQLite(per-org 分库) |
| 前端 | 1 个:Vue3 + Vite + Element Plus | 4 个:app / orgadmin / admin / platformadmin |
| 角色 | 2(employee / admin) | 多级(学员 / 组织管理员 oadmin / 平台管理员 padmin / 教练 trainer) |
| 租户 | 单租户 | 多租户(`orgs` 注册表 + 每 org 一个 `<org>.db`) |
| 代码量 | ~4,900 行 Go | ~17,200 行 Python + `zlylibs/engine` |
| 数据表 | 11 张 | 40+ 张(schema v23) |
| AI 底座 | 内网 OpenAI 兼容接口(Ollama LLM + bge-m3) | OpenAI 兼容路由(trainee/coach/eval 三 slot) |
## A3. 重要前提:pj0231 文档已过时
- `docs/PRD.md` 写「FastAPI + MySQL + 不做向量检索」;实际代码是 **Go + Gin + GORM + SQLite**,检索已实现**向量(brute-force 余弦 + bge-m3)**。
- 本项目权威信息以 `eai_agentplatform/CLAUDE.md` 与**实际代码**为准。
---
## A4. 知识库能力对比
| 能力 | pj0231 | pj006 |
|---|---|---|
| 素材摄入 | ✅ 上传→审批→转换→切片(真实跑通) | ⚠️ 知识工厂骨架完备,Phase 1 解析抽取「不真跑、停 pending」 |
| 文档转换 | LibreOffice + pdftotext,切片入 `knowledge_chunk` | 设计 parse/structured/extraction_blocks 多阶段管线,未实跑 |
| 审批流 | ✅ `pending/approved/rejected`,审批前置 | ✅ 更细:`working_knowledge_points` 待复核 + `kb_publish_records` 发布链 |
| 知识组织 | 扁平 `knowledge_chunk` + `knowledge_source` | 多层:`knowledge_points` → `knowledge_meta` → `knowledge_units`(统一主干)→ `knowledge_chunks`(带 `embedding_status`) |
| 术语/规则 | ❌ 无 | ✅ `knowledge_terms`、`industry_rules` |
| 检索 | ✅ **混合检索**(向量余弦 + 关键词兜底) | ⚠️ 实跑 jieba 分词 + LIKE;向量仅文档宣称 |
| 自动出题 | ❌ 无 | ✅ 设计:`working_questions` 自动生成(待复核) |
| 知识-题目/场景关联 | 仅题目挂 `course_id` | ✅ `question_knowledge_units`、`scene_knowledge_units`、`knowledge_unit_links` |
## A5. 考试能力对比
| 能力 | pj0231 | pj006 |
|---|---|---|
| 题型 | 单选 / 多选 / 判断 | 单选 / 多选 / 判断 / **简答 essay** |
| 简答评分 | ❌ 无 | ✅ LLM 按 rubric 评分,得分率持久化 `exam_session_grades` |
| 组卷 | ✅ 按知识域 + 随机抽题 | ⚠️ 固定 `question_ids` 列表;蓝图组卷未接考试服务 |
| 考试次数 | 正式考只允许一次 | ✅ `max_attempts` + `deadline` |
| 自测 | ✅ 自测(即时对错、不存记录) | ✅ 练习走独立 `practice` 模块 |
| 判分 | 确定性比对 | 选择题确定性 + 简答 LLM |
| 成绩分析 | 仅存得分/正确率/明细 | ✅ 维度能力分析(weak/ok/strong)+ 薄弱题 + 改进建议 |
| 错题联动 | ❌ 无 | ✅ 交卷错题自动入 `mistake_records` |
## A6. 测试 / 自测 / 评估能力对比
### A6.1 面向学员(产品能力)
| 能力 | pj0231 | pj006 |
|---|---|---|
| 自测练习 | ✅ 自测(不存记录) | ✅ 每日一练 `practice`(游戏化:段位/星/连击/经验/积分) |
| 错题本 | ❌ 无 | ✅ `mistake_records`(练习+考试聚合) |
| 笔记/收藏 | ❌ 无 | ✅ `study_notes` + 收藏 |
| 学习档案/学情 | ❌ **PRD 明确禁止** | ✅ 学习档案 dashboard + 能力对标 |
| AI 对练复盘评分 | ❌ 无 | ✅ `dialogue_review_scores` + `ReviewScoreService` |
### A6.2 面向软件工程(自动化测试)
| 维度 | pj0231 | pj006 |
|---|---|---|
| 后端测试 | ❌ **0 个 `_test.go`** | ✅ 31 个 pytest 文件 |
| 引擎测试 | 无 engine 层 | ✅ `zlylibs/engine/tests` 约 20 个 |
| 测试策略文档 | ❌ 无 | ✅ `docs/12_Test_Strategy` |
| CI/依赖管理 | 无 | ✅ pyproject dev deps |
## A7. 岗位与知识对应能力(差距最大维度)
pj0231 是**结构性缺失**:用户只有 `role`,完全没有岗位概念,也没有岗位→知识→考试的能力链。
| 能力 | pj0231 | pj006 |
|---|---|---|
| 岗位表 | ❌ 无 | ✅ `positions` |
| 岗位↔用户绑定 | ❌ 无 | ✅ `user_positions` |
| 岗位能力模型(洋葱) | ❌ 无 | ✅ `competency_layers` + `competency_levels`(L1-L4)+ `competency_details` |
| 能力模型自动生成 | ❌ 无 | ✅ `generate_competency_model` |
| 岗位↔知识映射 | ❌ 无 | ✅ `position_knowledge_units` + `auto_match` |
| 岗位考试蓝图 | ❌ 无 | ✅ `position_exam_blueprints` |
| 标准岗位/职级代码 | ❌ 无 | ✅ `SD01_Standard_Job_Codes` |
| 岗位驱动学练考 | ❌ 无 | ✅ A11 学习 / A12 练习 / A13 蓝图组卷 |
---
# Part B —— 差距汇总与反向差异
## B1. 差距严重度总表
| 维度 | pj0231 现状 | 差距严重度 | 是否 pj0231 定位内该补 |
|---|---|---|---|
| 岗位与知识对应 | 完全缺失 | 🔴 高 | ✅ 是(本次核心诉求) |
| 错题本 / 学习闭环 | 完全缺失 | 🟠 中高 | ✅ 是(考试「考完即止」无闭环) |
| 简答题 + LLM 评分 | 缺失 | 🟠 中 | ✅ 是(LLM 底座已有,成本低) |
| 自动化测试 | 0 测试 | 🟠 中高(工程风险) | ✅ 是 |
| 多租户 / NPC 人格 / KPI | 缺失 | 🔵 低 | ❌ 否(与「极简内网单企业」冲突) |
| 向量检索 | ✅ 已实现 | — | —(反而领先 pj006) |
| 随机 + 按域组卷 | ✅ 已实现 | — | —(反而领先 pj006) |
## B2. 反向差异:pj0231 反而更「实」的点
1. **向量检索是真实现**:pj0231 混合检索端到端可用;pj006 实跑 jieba 关键词,向量仅字段预留。
2. **随机 + 按知识域自动抽题组卷**:pj0231 支持;pj006 考试为固定题目列表。
3. **文档→文本→检索链路真实跑通**:pj0231 LibreOffice/pdftotext 切片入库完整;pj006 知识工厂 Phase 1 未实跑。
4. **AI 算力点计费 + 审计日志**:pj0231 有 `ai_call_log`;pj006 未见同等计费。
---
# Part C —— 改进路线图
## C1. 优先级(按 ROI)
| 优先级 | 项 | 理由 |
|---|---|---|
| P0 | 岗位模型 + 岗位-知识映射 + 岗位驱动考试 | 最本质差距,本次核心诉求 |
| P1 | 学员「我的岗位应学清单」+ 成绩按岗位聚合 | P0 的自然延伸 |
| P2 | 错题本 + 轻量成绩档案 | 补考试闭环 |
| P2 | 简答题 + LLM 评分 | 复用 LLM 底座,成本低 |
| P2 | 后端自动化测试 | 消除 0 测试隐患 |
| 不做 | 多租户 / NPC 人格 / HEP 引擎 / KPI | 与极简定位冲突 |
## C2. 分阶段总览
```
P0(本次落地):岗位 + 映射 + 岗位驱动考试组卷
↓
P1:我的岗位应学清单 · 成绩按岗位聚合 · 岗位考试蓝图
↓
P2:错题本 · 简答题 LLM 评分 · 自动化测试
```
---
# Part D —— 设计稿:岗位与知识对应能力(核心)
> 完整设计见《设计稿_岗位与知识对应能力.md》,此处为完整版正文。
## D1. 目标与原则
**目标**:落地最小岗位驱动闭环 —— `岗位 → 用户绑定 → 岗位知识映射 → 岗位驱动考试组卷`。
**原则**:保持极简;复用现有 `AutoMigrate` / `RequireAdmin` / `pickQuestions`;级别词汇对齐 pj006 L1-L4 但**不建独立级别表**;单岗位优先。
## D2. 改动点清单
| 现有资产 | 位置 | 改动 |
|---|---|---|
| `User` | `internal/model/user.go` | 加列 `PositionID *uint`(可空) |
| `ExamPaper` | `internal/model/exam_paper.go` | 加列 `PositionID *uint`(可空) |
| 迁移 | `internal/store/db.go` | AutoMigrate 追加 2 张新表 |
| 路由 | `internal/api/router.go` | 新增岗位端点(admin 组) |
| 抽题 | `internal/api/exam.go` `pickQuestions` | 增加岗位知识映射过滤分支 |
| 种子 | `internal/store/seed.go` | 可选预置默认岗位 |
## D3. 数据模型
### 3.1 `position`(岗位表)
```go
type Position struct {
ID uint `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:32;uniqueIndex;not null" json:"code"`
Name string `gorm:"size:64;not null" json:"name"`
Description string `gorm:"type:text" json:"description"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Position) TableName() string { return "position" }
```
### 3.2 `position_knowledge`(岗位知识映射,核心)
```go
type PositionKnowledge struct {
ID uint `gorm:"primaryKey" json:"id"`
PositionID uint `gorm:"not null;index" json:"position_id"`
Domain string `gorm:"size:16;not null;index" json:"domain"` // company / product / sales
CourseID *uint `gorm:"index" json:"course_id"` // 可空
ProductID *uint `gorm:"index" json:"product_id"` // 可空
RequiredLevel string `gorm:"size:16;not null;default:L1" json:"required_level"` // L1/L2/L3/L4
Weight float64 `gorm:"not null;default:1" json:"weight"`
IsMandatory bool `gorm:"not null;default:true" json:"is_mandatory"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PositionKnowledge) TableName() string { return "position_knowledge" }
```
### 3.3 级别常量
```go
const (
LevelL1 = "L1" // 入门级
LevelL2 = "L2" // 稳定执行级
LevelL3 = "L3" // 复杂处理级
LevelL4 = "L4" // 体系策略级
)
var validLevels = map[string]bool{LevelL1: true, LevelL2: true, LevelL3: true, LevelL4: true}
```
## D4. API 设计
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | `/api/positions` | admin | 岗位列表 |
| POST | `/api/positions` | admin | 新建岗位 |
| PUT | `/api/positions/:id` | admin | 编辑岗位 |
| DELETE | `/api/positions/:id` | admin | 停用(软删) |
| GET | `/api/positions/:id/knowledge` | admin | 岗位知识映射列表 |
| PUT | `/api/positions/:id/knowledge` | admin | 保存映射(整表覆盖) |
| PUT | `/api/users/:id/position` | admin | 设置/清除用户岗位 |
| GET | `/api/my/position` | 员工 | 我的岗位 + 应学范围(P1) |
## D5. 岗位驱动考试组卷(核心算法改动)
修改 `pickQuestions`,在 `PositionID != nil` 时按岗位知识映射圈定题池,否则回退原 `domain` 抽题:
```go
func pickQuestions(p model.ExamPaper) ([]model.Question, error) {
pool := store.DB.Where("status = ?", "active")
if p.PositionID != nil {
var pks []model.PositionKnowledge
if err := store.DB.Where("position_id = ?", *p.PositionID).Find(&pks).Error; err != nil {
return nil, err
}
domains := make([]string, 0, len(pks))
courseIDs := make([]uint, 0, len(pks))
for _, pk := range pks {
domains = append(domains, pk.Domain)
if pk.CourseID != nil {
courseIDs = append(courseIDs, *pk.CourseID)
}
}
domains = dedupe(domains)
if len(domains) > 0 {
pool = pool.Where("domain IN ?", domains)
}
if len(courseIDs) > 0 {
pool = pool.Where("course_id IN ? OR course_id IS NULL", courseIDs)
}
} else {
domains := splitDomains(p.Domain)
if len(domains) > 0 {
pool = pool.Where("domain IN ?", domains)
}
}
var qs []model.Question
if err := pool.Order("id ASC").Find(&qs).Error; err != nil {
return nil, err
}
if len(qs) < p.QuestionCount {
return nil, fmt.Errorf("题库题目不足:需要 %d,可用 %d", p.QuestionCount, len(qs))
}
if p.Randomize {
rand.Shuffle(len(qs), func(i, j int) { qs[i], qs[j] = qs[j], qs[i] })
}
return qs[:p.QuestionCount], nil
}
```
配套:`CreatePaper` / `UpdatePaper` 追加校验——岗位考试必须已配置至少一条知识映射,否则拒绝。
## D6. 前端设计
- 导航:管理员「系统管理」下拉新增 **「岗位管理」**。
- 页面:岗位列表(CRUD)+ 岗位知识映射编辑面板(domain 下拉 + 课程/产品下拉 + L1-L4 + 权重 + 必学开关)。
- 改造:用户管理页加「岗位」下拉;考试配置表单加「关联岗位」下拉。
## D7. 分阶段
- **P0**:表 + API + 岗位 CRUD + 用户设岗 + 岗位驱动组卷 + 前端岗位管理页。
- **P1**:学员「我的岗位应学清单」+ 成绩按岗位聚合 + 岗位考试蓝图。
- **P2**:见 Part E。
## D8. 明确不做
❌ 不建洋葱三层模型;❌ 不引入多租户;❌ 不做 LLM 自动生成能力模型;❌ 不做多岗位绑定;❌ 不做学情分析。
---
# Part E —— 设计要点:其余改进项
## E1. 错题本 + 轻量成绩档案(P2)
> ⚠️ 与 PRD 冲突需先拍板:PRD 第 7 节「无学情分析」。**错题本**是学员自助工具(不做管理层分析),可不冲突;**学习档案/能力雷达**属「学情分析」范畴,需产品重新确认是否放开。
### 数据模型
```go
// MistakeRecord 错题本
type MistakeRecord struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
QuestionID uint `gorm:"not null;index" json:"question_id"`
Source string `gorm:"size:16;not null" json:"source"` // self_test / formal
UserAnswer string `gorm:"type:text" json:"user_answer"`
CorrectAnswer string `gorm:"type:text" json:"correct_answer"`
Resolved bool `gorm:"not null;default:false;index" json:"resolved"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (MistakeRecord) TableName() string { return "mistake_record" }
```
### 联动点
- 正式考 `ExamSubmit` 判分循环里,错题 `INSERT` 进 `mistake_record`(`source=formal`);自测可选不写。
- 学员端「我的错题本」页:列表 + 「已掌握」标记(`resolved=true`)。
## E2. 简答题 + LLM 评分(P2)
### 数据模型改动
- `question.type` 增加 `essay`;`answer` 存 rubric 评分要点文本;`options` 空。
- `exam_record.detail_json` 已存答题明细,可直接承载简答文本。
### 评分流程
- 复用 `internal/ai/llm.go`,新增「评分」能力路由(或在 `ai_call_log.capability` 加 `essay_grade`)。
- 交卷时对 `essay` 题调 LLM 按 rubric 打分(0~1 得分率),持久化到 `detail_json`。
- 选择题仍确定性判分;总分 = Σ得分率/题数×100(对齐 pj006 算法)。
## E3. 自动化测试(P2)
- **后端**:新增 `_test.go`,优先覆盖纯逻辑函数:`isCorrect`(判分)、`splitDomains`、`pickQuestions`(组卷)、`ai.Retrieve` 的 `splitTerms`/`cosine`。
- **命令**:`go test ./...`;接入 `start_dev` 或 CI 脚本。
- **前端**:可选,暂缓。
---
# Part F —— 待拍板决策点与下一步
## F1. 需产品/技术拍板
1. **岗位考试课程过滤语义**(D5):岗位绑定具体课程时,抽题用 `course_id IN ?`(严格)还是 `course_id IN ? OR course_id IS NULL`(宽松含域匹配)?
2. **错题本 vs 学情分析边界**(E1):PRD 禁止「学情分析」,错题本是否属于允许范围?
3. **岗位知识映射去重**(D3.2):是否加唯一约束防重复?
4. **简答题是否本期做**:P0 还是 P2?
## F2. 建议的下一步
1. 确认 F1 四个决策点;
2. 进入 P0 编码(改动文件已列于 D2,共 6 处);
3. P0 完成后补 `isCorrect`/`pickQuestions` 单测(借 P0 一并补测试债)。
---
> 本文件为整合版:Part A/B/C 来自《对比分析_pj0231_vs_pj006-zhilianyuan2.md》,Part D 整合自《设计稿_岗位与知识对应能力.md》,Part E 为新增其余改进项设计要点。