改名(对齐 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>
523 lines
36 KiB
Markdown
523 lines
36 KiB
Markdown
# 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 永久锁定。* |