Files
pj0235-eai_agentplatform/TOP_CODING_RULES.md
T
eaiadminandClaude Code 6194c4733e docs: CODING_RULES 更名为 TOP_CODING_RULES 并补齐通用规则与踩坑清单
改名(对齐 pj0034 的 TOP_ 前缀):
- CODING_RULES.md → TOP_CODING_RULES.md,同步 5 处引用
  (CLAUDE.md 目录树与启动仪式、PROJECT_STATE.md 三处、DR03、DR04、文件内 G06 自引用)

补齐通用部分 G09–G18(承接 pj0034 同名文件的通用规则,按本项目
Go / Vue3 / SQLite / Ubuntu 技术栈改写):
- G09 登录态与接口必须无状态化
- G10 配置化优先,禁止写死环境细节
- G11 素材必须可追溯,不可静默修改
- G12 长耗时任务必须异步化
- G13 先跑通主线,再细化和优化
- G14 Git 不抢戏 — 收工时统一提示一次
- G15 禁止通用名启动入口文件,启动逻辑必须在 start_dev_10231_10232.sh
- G16 Windows 侧 .ps1 脚本统一 UTF-8 with BOM
- G17 脚本内禁止兼容式依赖回退,必须固定单一工具链
- G18 仓库应尽量支持拷贝后直接运行
不抄的(逐条判断本项目不适用):Python 虚拟环境、Playwright/clickflow E2E、
OSS 多租户、Amazon 平台规范检测;该判断已写进文件头说明。

G04 补第 6–10 条:全绿≠跑通、报告事实而非意图、读清用户的 bug 描述、
修 UI bug 追完整渲染链、跨区移动数据后验另一端完整性。

新增 P06 常见技术陷阱清单(11 条,全部来自本项目真实事故或核过代码的事实):
AutoMigrate 只加不删、探测脚本用完即删、worker_run.status 恒为 done、
seed 只补空字段、禁止按进程名模糊匹配杀进程、Element Plus persistent
留隐藏 DOM、动用户数据前先备份再列清单、hash 路由改 hash 不重载、
gofmt 不要整个目录 -w、git remote 禁嵌明文凭据、禁引第三方受限素材。

顺手修三处不一致:P01 索引名过时(培训平台→通用数字员工平台)、
DR03 引用的编号在重组后指向了错规则(第 6 条→G07)、第二部分标题层级。

版号 V1.0 → V1.1。

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

523 lines
36 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.
# eai_agentplatform 博昇 AI 数字员工平台(EAI Agent Platform)— 编码与调试最高准则
> **版本:V1.1**
> **日期:2026-09-17**
> **状态:必须强制执行 (Highest Priority)**
> **适用范围:eai_agentplatform(EAI 数字员工平台)后端(Go)、前端(Vue3)、数据库(SQLite)、AI 检索/对话、考试引擎、素材上传与审批**
> **AI 助手启动任何任务前必须先读取并确认本文件。**
---
> **整理说明**:参考 pj034-oeamgt TOP_CODING_RULES.md 结构体系重组。
> **分组方式**:第一部分是 EAIHub 博昇 AI 中心通用规则;第二部分是本项目专用规则。
> **编号方式**:第一部分 G01-G18;第二部分 P01-P06。
>
> **V1.1 补充说明**:本次只做加法,不删减、不压缩原有规则正文。
> 通用部分新增 G09-G18(承接 pj0034 同名文件的通用规则,按本项目 Go / Vue3 / SQLite / Ubuntu 技术栈改写,
> 不适用的部分——如 Python 虚拟环境、Playwright E2E、OSS 多租户——明确不抄);
> G04 补充第 6-10 条(来自两个项目共同踩过的坑);本项目新增 P06 常见技术陷阱清单。
---
# 第一部分:通用开发规则
> **适用范围**:适用于 eai_agentplatform 及 EAIHub 下其他涉及 Go/Vue3/AI 的项目。
> **使用方式**:新任务开始前应先通读本部分;项目专用规则(P)在遵守本部分基础上叠加。
## 索引
- G01:深度调试日志 — 全链路埋点 + 特殊日志文件
- G02:Fail Fast 与零静默兜底
- G03:变量命名锚定 — 防命名漂移
- G04:测试与验收 — 完成判定必须靠事实
- G05:安全迁移与重构流程
- G06:AI 助手行为规范
- G07:交互控件可用态颜色统一
- G08:分层清晰,禁止前后端职责串线
- G09:登录态与接口必须无状态化
- G10:配置化优先,禁止写死环境细节
- G11:素材必须可追溯,不可静默修改
- G12:长耗时任务必须异步化
- G13:先跑通主线,再细化和优化
- G14:Git 不抢戏 — 收工时统一提示一次
- G15:禁止通用名启动入口文件,启动逻辑必须在 start_dev_10231_10232.sh
- G16:Windows 侧 .ps1 脚本统一 UTF-8 with BOM
- G17:脚本内禁止兼容式依赖回退,必须固定单一工具链
- G18:仓库应尽量支持拷贝后直接运行
---
## G01 最高原则:深度调试日志 (Special Log & Console Print)
**任何**涉及功能异常、逻辑排错、API 失败或模板渲染问题的任务,必须遵循以下调试流程:
1. **强制全链路埋点**:禁止盲目猜测,必须在后端路由、中间件、服务层以及前端 JS 关键回调中,大量写入过程性输出。
2. **统一特殊日志文件**:
- 路径:`debuglog/backend_YYYY-MM-DD.log`(按天生成,由 `start_dev_*.sh` 落盘)
- 内容:必须包含 `[时间戳] [模块名] [详细描述]`
- 必须包含:请求参数、Session 状态、关键业务变量
- **脱敏红线**:落盘前必须脱敏/删除敏感信息(Authorization/Cookie/JWT/密码/API Key 等)
3. **同步控制台输出**:所有写入调试日志的内容必须同步 `print` 到 Console,以便开发者实时观察。
4. **排查先读日志**:在提出任何修复方案前,必须先调用读取工具检查该日志。
### 关键埋点清单
| 埋点位置 | 必须记录内容 |
|---|---|
| API 调用 | 请求参数、响应状态、异常栈 |
| 数据库交互 | 查询关键参数、结果集摘要 |
| 文件操作 | 上传/转换/提取的文件路径、大小、状态 |
| AI LLM 调用 | 发送给 AI 的完整 Payload + 返回原始正文;落盘前脱敏密钥/令牌 |
---
## G02 最高原则:Fail Fast 与零静默兜底 (Fail Fast & Zero-Fallback)
1. **禁止隐式回退**:所有涉及配置、运行时资产的读取,**严禁使用硬编码的默认值进行静默兜底**。
2. **配置/字段缺失即报错**:如果代码依赖某项配置或 JSON 字段且其缺失,必须立即抛出异常并终止流程。
3. **禁止入口层吞错**:路由入口不得 `try/except` 后静默放行;凡关键身份、权限、业务校验失败,必须返回明确错误(4xx/5xx)并阻断流程。
4. **禁止"先跑通再修正"策略**:不得为"先可用"加入 hardcode 默认值、临时跳过校验等行为。这类行为视为质量事故。
5. **错误可见性强制**:任何违反业务规则或数据约束的问题,必须对开发者显式可见(日志 + 返回错误 + 可复现路径),禁止隐藏真实错误来源。
6. **图片不做 OCR**:严格按照 PRD V1.1 规定,图片(png/jpg/jpeg)不做 OCR、不进 AI 文本库。
### 反例与正例
| | 反例 ❌ | 正例 ✅ |
|---|---|---|
| A | 文件审批状态未知时默认视为"已通过" | 状态非 `approved` 则拦截并返回明确错误 |
| B | LLM 配置缺失时使用硬编码的默认地址 | 配置缺失立即报错,引导管理员在系统参数补充 |
| C | 考试 session 不存在时返回空结果 | Session 不存在返回 404 + 明确错误信息 |
---
## G03 原则:变量命名锚定 — 防命名漂移 (Identity Anchoring)
1. **变量名前缀强制化**:所有业务相关变量必须带明确前缀(如 `media_file_id`, `exam_session_key`, `product_code`)。禁止使用 `id`, `data`, `res` 等模糊命名。
2. **变量名全链路同步**:同一业务参数在 API、Service、Model 层必须保持变量名完全一致。
3. **最小长度约束**:变量名原则上不短于 5 个字符(循环索引除外)。
4. **AI 引用已定义标识符必须按字符复制**:AI 在生成或修改代码时,引用任何**已在项目中定义过**的标识符,必须先 Read/Grep 找到定义处,**按字符原样复制**,禁止自行改写大小写或分隔符。例如 `user_id` 不应被写成 `userId` 或 `uid`。
---
## G04 最高原则:测试与验收 — 「我说完成」必须靠观察的事实
1. **完成判定必须看 exit code,禁止仅看屏幕末尾文字**:命令后立刻 `echo $?` 或 `if [ $? -ne 0 ]`;链式命令必须确认每一段都退出 0。
2. **修改既存文件前必须 Read 整文件**,禁止凭印象 Edit。
3. **新依赖必须同步进 `go.mod` / `package.json`**:任何新 import 出现必检查此包是否在依赖清单中。
4. **测试/build 失败时禁止"再试一次"侥幸**:失败原因必须先找出来。
5. **声明完成前的最小验证清单**:
- [ ] 后端:`go build ./...` 无编译错误 且 `go test ./...` 通过
- [ ] 前端:`npm run build` exit 0
- [ ] API smoke:`/api/health` 返回 200,管理员 login 返回 200
- [ ] 任何新 import 在依赖清单中
6. **「测试套件全绿」不等于「功能跑通」**:单测覆盖的是代码路径,端到端要单独验证(curl / 浏览器 / 真实演示)。特别针对 UI:type-check + build 通过**不代表**用户能用,必须真起 dev server 在浏览器里点一遍。
7. **报告事实而非意图**:写"x 通过 y 测试"之前必须真跑过;不能写"应该可以工作""理论上没问题"。
8. **读清楚用户的 bug 描述,逐字理解,不凭经验脑补**:遇到歧义表述,先用自己的话 paraphrase 确认("你是说这个数字不该出现,还是说数字放错位置了?"),而不是直接动手。
- 规则:**用户描述 bug 时,默认用户是对的。先确信自己没看懂,不确信用户没说清。**
- 同理适用于"我没看懂你的意思"——这通常意味着回答里实现细节太多,需要换成平实语言重讲,而不是重复一遍原话。
9. **修 UI 显示 bug 必须追完整渲染链**:数据定义 → 数据构建 → 判断函数 → 模板条件,四个环节逐一确认,不能只改一环就宣布完成。遇到动态拼接的值(模板字面量 key、派生字段),先 Read 构建代码确认**实际格式**再写检查条件。
10. **跨区移动数据后,另一端的完整性必须显式验证**:从集合 A 移到集合 B 的条目,若后来又从 B 移除,必须检查要不要放回 A;改完 grep 一遍预期的 key 是否都在预期位置,`git diff` 扫一眼删除行是否多于预期。
- 这不是技术判断失误,是**注意力切换导致的数据完整性断裂**——修 bug 时只想着"删掉有问题的条目",没意识到这次删除附带"恢复到原处"的义务。
---
## G05 原则:安全迁移与重构流程 (Secure Migration)
1. **非简化原则**:重构不得以简化逻辑为目的,必须保留所有原始业务深度。
2. **实质性内容保护**:除非内容明确放错位置、存在重复副本或已经完成等价迁移验证,否则不得删除原有实质性内容与技术细节。
3. **全量备份**:大规模操作前,将原始文件完整备份。
4. **文件安全强制**:
- 文件扩展名白名单校验(仅允许 ppt/pptx/pdf/doc/docx/mp4/png/jpg/jpeg)
- 文件名重命名为存储 UUID,杜绝路径穿越
- 上传目录对静态预览只读,禁止直接执行
---
## G06 原则:AI 助手行为规范
1. **任务启动预读**:AI 助手在接收到新任务后的第一步操作中,必须读取本项目根目录下的 `TOP_CODING_RULES.md` 与 `eai_agentplatform/CLAUDE.md`。
2. **持续追踪**:直到问题完全解决并经由日志或接口验证通过前,不得结束任务。
3. **新对话启动仪式**:新对话必须先读 `eai_agentplatform/PROJECT_STATE.md` → `TOP_CODING_RULES.md`,再开始干活。
4. **发现规则与实现冲突时**,优先提醒并修正,不得静默忽略。
5. **Git 同步状态必须用 `-sb` 显式确认**:首次执行 git 相关操作时,必须使用 `git status -sb` 或 `git status -v` 而非简短版本,确保能清晰看到本地与远端的 ahead/behind 关系。禁止依赖 `git diff HEAD origin/HEAD` 单条链式命令判断,因其 `&&` 链在中间出错时会导致后续输出丢失。
6. **Git 检查必须指定分支名**:使用 `origin/<branch>`(如 `origin/main`)而非 `origin/HEAD`,避免 `origin/HEAD` 歧义。
7. **验证输出后再总结**:在给出 git 状态结论前,必须确认所有关键信息(ahead/behind、clean/dirty、branch)均已成功读取,不得在未确认完整性的情况下返回结论。
---
## G07 原则:交互控件可用态颜色统一规范
1. **按钮可用态统一蓝底**:所有可点击的关键交互按钮必须使用蓝色底(推荐 `#1677ff`)。
2. **按钮不可用态统一灰底**:所有不可点击按钮必须使用灰色底(推荐 `#cbd5e1`)与灰色文字(推荐 `#64748b`),并保持 `cursor: not-allowed`。
3. **状态变化必须实时联动视觉**:按钮的 `disabled` 状态变化后,底色必须立即同步变化。
4. **Element Plus 严格类型约束**:`el-tag` / `el-button` 的 type prop 禁止传入 `""` 或 `null`;无条件匹配时应传 `undefined`。
---
## G08 原则:分层清晰,禁止前后端职责串线
1. **前端只负责**:页面渲染、用户交互、表单收集、数据展示
2. **后端必须负责**:登录认证、权限校验、文件操作、AI 接口调用、业务判分逻辑、数据库操作
3. **前端不可以直接持有 JWT Secret 或 AI API Key** — 敏感凭据只在后端
4. **前端可以做格式和必填校验,但后端必须再次做强校验**
5. **所有权限以后端鉴权为准**:前端路由守卫仅作 UX 隐藏,不作为安全边界
---
## G09 原则:登录态与接口必须无状态化
1. **认证统一**:JWT(golang-jwt)+ bcrypt,角色只有 `employee` / `admin` 两级,不引入第三级与复杂 RBAC。
2. **每个受保护接口在入口层完成鉴权**:token 解析、用户识别、角色校验都在路由与中间件(`middleware.CurrentUser`)完成,不得下推到 service 层靠调用方自觉。
3. **API 默认无状态**:不在进程内保存登录态、会话、当前任务等跨请求状态;一切跨请求状态落库(`system_config` / 任务表 / 会话表),进程重启后行为一致。
4. **前端路由守卫只做 UX 隐藏**,不是安全边界;权限以后端判定为准(关联 G08)。
5. **无状态不等于无审计**:状态落库时必须带时间戳与归属(谁、什么时候改的),否则线上出问题只能靠猜。
### 关联
- 关联 G08:权限判定在后端,前端只负责隐藏入口
- 关联 G02:token 无效/缺失必须明确返回 401,不静默放行为匿名用户
---
## G10 原则:配置化优先,禁止写死环境细节
1. **禁止写死 `localhost`、固定端口、服务器绝对路径**:端口只在启动脚本里定义一次(`BACKEND_PORT=10232` / `FRONTEND_PORT=10231`),其它脚本引用它,禁止各处复制造成漂移。
2. **AI 相关的一切走配置**:LLM `base_url` / `api_key` / `model`、embedding 地址、JWT 密钥一律走 `system_config` 表 → `.env` 两层,缺失时按 G02 立即报错(501),不得硬编码默认地址兜底。
3. **可调参数不写进代码常量**:检索 top-k、切片长度、相似度阈值、积分规则这类"迟早要调"的值,集中放 `system_config` 或集中常量文件,调参不改代码、不重新编译。
4. **新增环境变量必须同步三处**:`.env` 样例(`backend-go/deploy/`)、启动脚本、`deploy/DELIVERY.md`。只改代码不改这三处,等于把坑留给下一次整盘克隆。
5. **禁止把"我这台机器能跑"的路径写进代码**:数据目录、日志目录、知识源目录都从配置派生——交付形态是裸进程 + systemd,换台机器路径就变。
### 关联
- 关联 G02:配置缺失 Fail Fast,不用默认值静默兜底
- 关联 G18:能拷到新机器跑,前提就是没有写死的环境细节
---
## G11 原则:素材必须可追溯,不可静默修改
1. **状态只前进、不跳变**:素材 `pending → approved / rejected`,不允许绕过审批直接改 `approved`。
2. **每一步留痕**:上传、转换、审批、入库、删除都要能回答"谁、什么时候、把它变成了什么状态";驳回必须写 `reject_reason`。
3. **已 `approved` 的素材原文件不可覆盖**:要改就新增素材/新版本,历史切片保留,检索结果能回溯到那份原文。
4. **AI 产出必须能对上来源**:一次 AI 调用记录 `ai_route_id` / `model` / `specialist_key`(`ai_call_log`),能回答"这句话是哪个专员、用哪个模型说的"。
5. **不做"看起来还在、其实已失效"的中间态**:删除就是删除(硬删或明确逻辑删),状态字段与前端展示必须一致,禁止靠前端过滤掩盖脏数据。
### 关联
- 关联 G02:审批状态未知时拦截,不默认放行
- 关联 P02 / P04:审批是入库转换的前置条件
- 关联 P06.7:删除与批量修改前先备份、先列清单
---
## G12 原则:长耗时任务必须异步化
1. **重活不占请求**:文档转换(LibreOffice → pdftotext)、批量提取、批量 embedding、批处理入库一律异步执行,API 层不得阻塞等待。
2. **提交即返回**:接口立刻返回任务标识与当前状态,进度靠状态查询;本项目固定用轮询(`GET /api/media/{id}/status`),不上 WebSocket。
3. **状态必须四态齐全**:`pending / running / success / failed`;`failed` 必须带失败原因,不允许只留一个没有下文的失败状态。
4. **异步失败不得静默**:日志 + 状态字段 + 前端可见,至少占两处;只在后台 print 一句不算。
5. **判断进度不要看 `worker_run.status`**:该字段恒为 `done`(见 P06.3),执行进度看任务自身的状态字段。
### 关联
- 关联 G01:异步任务每一步都要埋点,否则失败后无迹可查
- 关联 G11:状态变化要留痕,异步任务尤其
---
## G13 最高原则:先跑通主线,再细化和优化
1. **顺序硬规则**:主线端到端跑通(能演示)→ 修真实发现的 bug → 量级出现后才做性能优化。三段不可跳序。
2. **禁止"未来优化"占位代码**:不写 `// TODO: 上缓存` `// 后续换向量库` `// 假如有 10 万 QPS` 这类注释。理由:
- 优化的前提是先有真实流量画像,没量级先做 = 凭空猜
- 占位 TODO 会被反复重读但永远不做,变成纯认知负担
- 真要优化时,前面的代码已被改过 N 次,原 TODO 的假设大概率已失效
3. **决定优化前必须先算三笔账**:
- **当前量级**:用户数 / 单日调用次数 / 单次数据量
- **优化收益**:省了多少延迟 / 多少钱 / 用户体验差多少
- **运维成本**:新增依赖 / 新增分支路径 / 测试矩阵变大
- 收益 < 成本 → 不做,哪怕技术上很优雅
4. **本项目已拍板的「不做」清单**(不要重新提议):
- 不上 Milvus / FAISS / Elasticsearch(P02:brute-force 余弦够用)
- 不上 Celery / Redis 类外部队列(Go 侧 goroutine + 状态表够用)
- 不上 WebSocket(轮询够用)
- 不做图片 OCR(G02.6)、不做视频转码与 ASR(D06)
- 不做社交社区 / 讨论区(D19)
5. **正确做法**:当前阶段把主线打磨到能跑、能演示、能收集真实反馈;量级出现后(单日调用 >1 万 / 单文件 >5MB / 用户开始抱怨慢)再针对**真实瓶颈**优化,且优化必须有前后对比数据。
6. **AI 助手特别提示**:被"行业最佳实践""业内通用做法""理论上更优"诱导往优化方向走时,先回到本条算量级、收益、成本,再决定做不做。
### 关联
- 关联 G04:优化效果必须靠前后对比数据说话,不靠直觉
- 关联 P02:检索方案的边界已定,不在本条重复讨论
---
## G14 原则:Git 不抢戏 — 收工时统一提示一次
> dev 阶段 git 是后台事务,不是讨论焦点。
1. **不重点讨论**:不在回答里大段解释 commit / branch / rename 细节,一句话带过即可。
2. **不列为 TODO**:commit / push / 分支操作不进任务清单,不算未完成项。
3. **收工时提示一次**:一天工作结束时用一行说明当天有没有未提交改动、要不要提交;其余时间不主动提。
4. **用户显式要求时照做**:用户说提交就提交、说推送才推送 —— 本条只约束 AI 的主动行为。
5. **要提交就要提交得干净**:
- 一次提交只装一件事,多件事分开提交(例:功能改动与在制品清理分两次)
- 提交信息写「为什么」,不只写「改了什么」
- 共享文件里混有并行改动、他人改动时,在提交正文里**如实写明**,不假装全是自己这次的改动
- 提交前扫一遍待提交内容有无密钥、大文件、运行期数据(参见 P06.10)
6. **状态核查按 G06.5-G06.7 执行**:`git status -sb`、显式指定 `origin/<branch>`,不用 `origin/HEAD`。
### 关联
- 关联 G13:git 卫生属于优化项,不阻塞主线
- 关联 P06.10:remote 里嵌明文凭据是提交前必查项
---
## G15 最高原则:禁止通用名启动入口文件,启动逻辑必须在 start_dev_10231_10232.sh
1. **禁止**在仓库里出现 `run.sh` / `start.sh` / `boot.sh` / `run.py` 这类通用名入口文件。
2. **唯一启动入口是 `start_dev_10231_10232.sh`**(Windows 侧为同名 `.ps1`):端口检查、占用进程处理、依赖 preflight、日志落盘(`debuglog/`)全部在这一层做。
3. **server 只做业务**:Go 后端不内置"帮你起前端 / 建库 / 改端口"的启动魔法;`go run` 绕过脚本时缺环境准备,属于个人排障手段,不得写进文档当作标准启动方式。
4. **确需封装时文件名必须带项目前缀**(如 `launch_eai.sh`),不可用通用名 —— AI 助手与新人搜索 "start/run" 时最先撞上的就是通用名文件,会绕过全部环境准备。
5. **为什么危险**:绕过端口检查与进程清理后,症状表现为"改了代码不生效""端口被占"这类查不出根因的怪事。
### 关联
- 关联 G18:启动脚本承担环境准备与自检
- 关联 P06.5:开发服务常驻 10231/10232,禁止按进程名模糊匹配杀进程
---
## G16 最高原则:Windows 侧 .ps1 脚本统一 UTF-8 with BOM
> **适用前提**:本仓库以 Ubuntu 为主(裸进程 + systemd),`start_dev_10231_10232.ps1` 是唯一的 Windows 侧脚本;本节只约束 `.ps1`,不影响 `.sh`。
1. **`.ps1` 必须保存为 UTF-8 with BOM**:Windows PowerShell 5.x 下,含中文注释/中文路径/中文输出的无 BOM 脚本会乱码、参数误读、解析失败。
2. **现状标注(2026-09-17)**:当前 `start_dev_10231_10232.ps1` 是**纯 ASCII、无 BOM**。一旦要往里加中文,必须**先补 BOM 再写中文**,不能直接存成无 BOM。
3. **注释优先用英文**:降低中文编码风险,也便于 AI 助手稳定编辑。
4. **执行策略被阻止时必须给出可运行命令**:使用说明同时提供 `.\start_dev_10231_10232.ps1` 与 `powershell -ExecutionPolicy Bypass -File .\start_dev_10231_10232.ps1`;禁止把"自己去改执行策略"当唯一方案。
5. **端口常量必须与 `.sh` 一致**:10231(前端)/ 10232(后端)在两侧脚本中保持同步,禁止某一侧临时改端口造成漂移(见 G10.1)。
### 关联
- 关联 G10:端口是配置,不是各脚本各写一份的常量
- 关联 G17:工具链与编码统一,同属可移植性
---
## G17 最高原则:脚本内禁止兼容式依赖回退,必须固定单一工具链
1. **一类工具只固定一种**:禁止写"有 pnpm 用 pnpm,没有就退回 npm/yarn"这类兼容分支。
2. **本项目前端固定 `npm`**(仓库内是 `package-lock.json`):安装 `npm install`、开发 `npm run dev`、构建 `npm run build`。凡文档、脚本、排障说明统一用 `npm`,不出现 pnpm / yarn 的第二套命令。
3. **后端固定 Go 工具链**:`CGO_ENABLED=0` 静态构建,不引入第二套构建方式。
4. **机器缺依赖就 fail fast**:缺 node / npm / go 直接报错并给出安装提示(`start_dev_10231_10232.sh` 的 preflight 就是这么做的),不许偷偷改走另一套工具链把流程"救活"。
### 关联
- 关联 G02:依赖缺失是错误状态,不是可回退状态
- 关联 G18:环境准备脚本幂等的前提是工具链唯一
---
## G18 最高原则:仓库应尽量支持拷贝后直接运行
1. **交付目标不是"作者机器能跑"**,而是"拷到另一台机器(或整盘克隆后)按文档和脚本能跑起来"。本项目交付形态是单二进制 + systemd + Clonezilla 整盘克隆(D14)。
2. **启动脚本必须做 preflight**:`start_dev_10231_10232.sh` 启动前检查 `go` / `node` / `npm` / `curl` / `lsof|ss`,缺哪个就报出安装命令并**整体失败**,不允许"检查失败也继续起"。
3. **补准备必须幂等**:已装则跳过、未装才装;启动脚本不得假设作者本机残留环境一定存在。
4. **交付物与文档对齐**:`backend-go/deploy/`(systemd unit、env 样例、清理脚本、`DELIVERY.md`)是交付事实源;改了启动方式 / 环境变量 / 数据目录,必须同步这里。
5. **禁止同一套依赖在多个脚本里重复安装**,只因为"这样比较保险"——重复安装迟早版本不一致。
### 关联
- 关联 G10:配置化是"拷了能跑"的前提
- 关联 G15 / G16 / G17:入口唯一、编码统一、工具链固定,同属可移植性
---
# 第二部分:eai_agentplatform 项目专用规则
> **适用范围**:仅适用于博昇 AI 数字员工平台项目。
> **使用方式**:本部分在通用规则之上叠加。若两者看似冲突,应先检查是否为项目专用规则对通用规则的场景化收敛。
## 索引
- P01:平台定位 — 通用数字员工平台(对齐 D20/D25/D26)
- P02:知识库安全与检索边界
- P03:考试判分与记录规则
- P04:素材上传与审批流程
- P05:AI PathCoach 对话安全边界
- P06:常见技术陷阱清单(本项目真实踩坑)
---
## P01 最高原则:平台定位 — 通用数字员工平台(对齐 D20/D25/D26)
1. 本项目是 **EAI 通用数字员工平台(Agent Platform)**:以「数字员工/专家、技能、长程 App」为核心对象,Chat 优先界面承载新建任务/对话,知识库作为组织级内建 App 有独立入口(对齐 D20「先通用、后定制」、D25 六层架构第 5 层对象层、D26 路由三类命名)。不再被描述为「博昇内部培训平台 / AI 对话机器人平台」。
2. **首批四类通用数字员工**(对齐 D20):知识运营、培训考试、内容生成、任务推进;后续再沉淀行业包与企业半定制。(原有产品知识/销售培训/考试等业务能力收敛为内部 App 注入其中。)
3. **业务对象统一为一级对象**(对齐 D21/D25/D26):Expert(专家/专员)、Skill(技能)、App(长程任务壳)。任务定义为「工作实例容器」,可挂载主 app、右栏 expert、后台 skill;消息流降级为对话轨迹,长程状态由 app 侧状态承载。
4. **知识库是组织级内建 App**(对齐 D25):素材审批 → 文本提取 → 知识切片 → 混合检索,有独立一级入口,不再只是后台能力。
5. 开始写某个模块前,先明确对应文档:
- `docs/04_Backend/BE*.md`(后端详细设计)
- `docs/06_Product_Lines/PL*.md`(产品原型)
- `docs/08_Design_Rules/DR*.md`(设计规则)
### 关联
- 关联 G08:前端不持有业务逻辑,AI PathCoach 调用必须走后端 API
- 关联 G02:配置/素材缺失必须报错,不静默兜底
---
## P02 最高原则:知识库安全与检索边界
1. **只存元数据,不存文件二进制**:数据库仅存储文件路径、大小、类型等元数据。
2. **素材文本存 knowledge_chunk 表**:审批通过的文档类素材经异步转换提取文本,切片写入。
3. **混合检索**:AI 知识检索 = 向量(Go 内 brute-force 余弦,Ollama bge-m3 embedding)+ 关键词兜底;数据量小无需 ANN/向量库(不上 Milvus/FAISS/Elasticsearch)。
4. **图片不进知识库**:png/jpg/jpeg 仅做存储预览,不做 OCR 提取,不进 AI 文本检索。
5. **审批前置**:素材只有 `approved` 状态才会触发异步转换;`pending` / `rejected` 的素材不进知识库。
6. **视频仅存储**:mp4 文件上传后仅做存储和预览,不做视频分析、不做帧提取。
### 关联
- 关联 G02:状态非 `approved` 的前端预览请求必须拦截
- 关联 G05:文件安全校验(扩展名 + UUID存储 + 只读预览)
- 关联 P04:审批是转换的前置条件
---
## P03 原则:考试判分与记录规则
1. **确定性判分**:单选/多选/判断题型采用确定性规则判分;**简答题(essay)除外**——按 D17 走后端 LLM 评分(复用 title_gen 路由、essay_grade 能力,不扣点数)。所有判分都在后端。
2. **判分逻辑必须在后端**:前端仅做选项展示和提交,不得在前端判分。
3. **自测 vs 正式考**:
- **自测(self_test)**:提交后立即显示正确答案 + 解析,不持久化成绩
- **正式考(formal)**:提交后判分落 `exam_record` 表,不显示正确答案
4. **考试试卷落库**:试卷为 DB 持久化配置(`exam_paper` 表),按配置从题库抽题,成绩与记录保存到 `exam_record`,不使用进程内字典兜底。
5. **正式考不可重做**:同一用户对同一正式考卷仅可提交一次(后端校验)。
---
## P04 原则:素材上传与审批流程
1. **管理员上传自动通过**:管理员(`role=admin`)上传的素材直接 `approved` + 立即触发异步转换。
2. **员工上传需审批**:员工上传后状态为 `pending`,管理员审批通过后才转 `approved`。
3. **驳回必须填写理由**:管理员驳回素材时,`reject_reason` 字段必填。
4. **异步转换管线**:审批通过 → 后台线程执行 `LibreOffice(ppt/docx→pdf) → pdftotext(提取文本) → 切片 → 写入 knowledge_chunk`。
5. **前端轮询状态**:前端通过 `GET /api/media/{id}/status` 轮询素材/转换状态,不使用 WebSocket。
6. **分片上传**:> 100MB 的视频文件走分片上传(init → chunk → complete)。
### 关联
- 关联 G02:状态/配置缺失必须报错
- 关联 G05:文件安全校验
- 关联 P02:审批是知识库转换的前置条件
---
## P05 原则:AI PathCoach 对话安全边界
1. **SSE 流式响应**:AI PathCoach 对话采用 SSE (Server-Sent Events),末包采集 token usage。
2. **上下文注入规则**:AI 回答基于知识库检索结果 + 当前页面上下文,禁止注入管理员凭据/内部配置。
3. **快捷动作限范围**:
- `scenario`(情景演练)— 使用预设 prompt 模拟客户对话
- `commission`(查佣金)— 检索产品佣金数据
- `compare`(产品对比)— 对比两个产品参数
4. **LLM 配置链**:system_config 表 → .env 文件两层优先级,缺失任何一项(base_url/api_key/model)抛 `501 LLMNotConfiguredError`。
5. **禁止功能**:AI PathCoach 不做图片生成、不做代码生成、不做外部 API 调用。
### 关联
- 关联 G02:配置缺失 Fail Fast(501),不静默兜底
- 关联 G01:AI LLM 调用完整 Payload + 返回正文必须埋点日志
- 关联 G08:AI 调用全程在后端,前端仅展示流式输出
---
## P06 最高原则:常见技术陷阱清单(本项目真实踩坑)
> 每条都来自本项目的真实事故或真实验证。遇到对应场景**先查本条,再动手**。
> 与 G13(先跑通主线)呼应:这里记的是"已经付过学费"的具体坑,不是设想。
### P06.1 GORM AutoMigrate 只加不删 —— 删列 / 改类型必须手写迁移
- **事实**:`store/db.go` 的 `AutoMigrate` 能自动加列(`specialist.rule_file_markdown`、`specialist.allowed_skills`、`ai_call_log.specialist_key` 三列已在开发库**副本**上实测加成功),但**不会**删列、不会改类型。
- **后果**:以为"改了 model 就完事",实际老列一直留在库里,代码与库结构悄悄分叉。
- **正确做法**:加字段 → 确认 model 在 `AutoMigrate` 列表里;删字段 / 改类型 → 写一次性迁移,或明确保留旧列并在文档里写明原因。
- **验证方式**:**改库结构后必须在数据库副本上跑一遍升级探测**,确认列真的加上、seed 真的填对,再动真库(动用户数据前先备份,见 P06.7)。
### P06.2 一次性探测脚本用完即删,别留成测试
- **事实**:为验证升级路径写过 `zz_upgrade_probe_test.go`,它第一步断言"这三列还不存在"。探测当时是对的,**但用户一重启服务,列就存在了,这个测试下次必红**。
- **规则**:断言"升级前状态"的探测代码是一次性工具,验完就删;留一个注定失败的测试比不留更糟——它会把"环境已经升级"误报成"代码坏了"。
- **正确做法**:要长期守护就写"升级**后**状态"的断言(列存在、seed 填对),而不是"升级前不存在"。
### P06.3 `worker_run.status` 恒为 `done`,别拿它当执行进度
- **事实**:`model/worker_run.go` 里 `Status` 的默认值是 `done`,后端没有任何一处写入别的值(全仓库只有 model 定义这一处出现该字段的赋值)。
- **后果**:以为能靠它判断"这一步跑完没有",实际永远是 done。
- **正确做法**:进度看任务自身的状态字段;run 与前后端的配对靠 `action_key` / `action_title`——前端 `specialistFlow.js` 的 `findLatestRun` 按两者之一匹配,`action_key` 由标题派生(`normalizeWorkerActionKey` / `normalizeActionKey`,两边规则必须一致)。
- **引申**:**配对字段来自中文标题,改一次文案就可能静默断链**——改标题必须同时确认配对没断。
### P06.4 种子数据只补空字段,不覆盖管理员改动
- **事实**:`seed.go` 的约定是"存在则只补空字段":`if existing.RuleFileMarkdown == "" { updates["rule_file_markdown"] = ... }`。
- **后果**:写成无条件 UPDATE,每次重启都会把管理员在后台手改的岗位说明书、技能绑定冲回种子值——用户改了半天,重启一次全白干。
- **守卫**:`seed_specialist_upsert_test.go` 用测试锁住这条约定,不要绕过它写无条件 UPDATE。
- **同理**:种子里的 key 拼错或重复必须在**启动时大声失败**(`applySpecialistRuleFiles` 就是这么做的),不能静默跳过。
### P06.5 开发服务常驻 10231/10232 —— 禁止按进程名模糊匹配杀进程
- **事实**:开发服务是常驻的,`pkill -f vite` / `pkill -f node` 这类模糊匹配会连用户正在用的那个一起杀掉。
- **正确做法**:按端口精确定位 PID(`lsof -ti:10231` / `ss -ltnp`)再杀单个 PID;`start_dev_10231_10232.sh` 已内置端口占用处理,优先用它。
- **引申**:任何"清理进程"的自动化脚本,默认按端口 / 精确 PID 定位,禁止按进程名模糊匹配。
### P06.6 Element Plus `persistent` 默认值会留下隐藏 DOM —— 曾因此误删真实任务
- **事实**:`el-dropdown` / `el-tooltip` 这类浮层组件默认 `persistent=true`,关闭后仍留一份节点在 DOM 里;任务行菜单曾因这个隐藏节点误删真实任务数据。
- **正确做法**:
- 这类组件一律显式 `:persistent="false"`(本项目 `MainLayout.vue` / `ProjectsPage.vue` / `ProjectDetailPage.vue` 已有先例与注释)
- 涉及删除的操作,脚本/代码自带数量校验:删几条、影响哪几条,先打印出来再执行
- **为什么**:隐藏 DOM 承载着真实数据的副本,"用户看不见"不等于"它不在"。
### P06.7 动用户数据前:先备份 → 再列清单 → 才动手
- **事实**:开发库里有用户的真实数据,不是可以随手重建的测试数据。
- **正确做法**:
- 动手前先备份(`data/backups/` 内置 `VACUUM INTO` 备份,D24;`cp` 主库在边服务边写时可能拷到写了一半的页)
- 把将要改动/删除的清单列出来(几条、哪些)再执行
- 需要探测结构变化时,**在副本上探测,不碰真库**
- **边界**:内置备份只防误删/误改/写坏,不防整盘损坏,跨机保存仍需人工。
### P06.8 前端路由是 hash 模式,改 hash 不会整页重载
- **事实**:路由用 `createWebHashHistory`。用 CDP / 自动化工具改 hash 时页面**不会**重新加载,于是出现"改了没生效"的假象,截图也可能截到旧状态。
- **正确做法**:改完 hash 显式 `Page.reload`,再截图 / 断言;用截图做验收前,先确认页面真的重载过。
### P06.9 `gofmt` 不要对整个目录 `-w`
- **事实**:仓库里有一部分文件在 HEAD 上本来就未格式化。直接 `gofmt -w internal/` 会把它们一起改,diff 里混进大量与本次任务无关的空行改动,评审时看不出哪行是真正改的。
- **正确做法**:先 `gofmt -l` 列清单,逐个确认该文件在 HEAD 上是否本来就干净;**只格式化本次真正改过的文件**,其余另开提交处理。
### P06.10 git remote 里禁止嵌明文凭据
- **事实**:本仓库的 remote URL 曾以 `https://用户名:令牌@域名/...` 形式保存明文令牌——任何能读这个仓库目录的人都能拿到它,`git remote -v` 也会把它打印出来。
- **正确做法**:凭据走 SSH key 或凭据管理器;发现已嵌入时,改 URL **并轮换该令牌**(只改 URL 不等于安全,令牌已经出现在历史输出与日志里)。
### P06.11 禁止引入第三方受限素材(Anthropic builtin-skills)
- **事实**:`builtin-skills/pdf/` 的 LICENSE 为 © 2025 Anthropic, PBC,含额外限制:禁止提取材料、禁止在服务之外保留副本、禁止复制、**禁止创作衍生作品**、禁止分发与再许可。
- **规则**:该目录目前已不在仓库中;**不得重新引入,也不得改写成"我们的 pdf 技能"**。需要 PDF 能力时自己实现或选许可允许的方案。
- **引申**:引入任何第三方素材/代码前,先读它的 LICENSE——"能用"和"能合法地用并交付"是两件事。
### 关联
- 关联 G04:这些坑的共同点是"看起来完成了,其实没有"——完成判定必须靠事实
- 关联 G11 / P04:数据可追溯、只补空字段,同属"不可静默修改"
- 关联 G12:异步任务的失败与进度不能靠 `worker_run.status`
- 关联 G14:提交前扫密钥与大文件(P06.10)
---
*注:本准则放置于项目根目录,作为全局 Rule 永久锁定。*