Files
2026Technology-Competition/docs/superpowers/specs/2026-07-26-custom-rule-import-ux-design.md
T
范智鹏 effcf30802 feat: 适配器 i18n + Provider 动态注册 + SetupView 重构 + jars 资源
- 适配器 i18n 接入(eslint/pmd/sql-lint/stylelint)
- Provider 动态注册机制(registry.ts + providers.json + factory 重构)
- SetupView 全面重构(setupView.ts 新增 600+ 行)
- i18n 消息扩展(messages.ts +210 行)
- 规则导入流程优化(import-service / prompt-builder)
- 新增 PMD jars 依赖及测试用例
2026-07-28 22:57:15 +08:00

264 lines
14 KiB
Markdown

# 自定义规则导入体验改进设计
## 背景与问题
当前自定义规则导入功能支持六种文件格式,其中五种(`.md` / `.txt` / `.docx` / `.xlsx` / `.pptx`)经由 AI 转换为结构化 YAML,`.yaml` / `.yml` 则直接复制落盘。转换流水线本身已跑通,但用户体验存在一个隐性断层:文件扩展名虽宽松,为了让 AI 准确识别规则字段,用户实际上仍需按某种"事实上的格式"组织内容——Excel 要表头语义化、文本要分段清晰、字段顺序要合理。格式门槛从文件扩展名转移到了内容结构上,用户不知道该怎么写,AI 转换结果也因此不稳定。
具体表现有两点。其一,系统提示词只规定了输出字段(id / severity / description / message 等),对输入几乎没有任何描述与容忍说明,AI 遇到松散内容时行为不可预期。其二,解析侧 `parseImportableYaml` 对必填字段采取"缺一即丢弃"的静默策略,`severity` 缺失或非法、`id` 缺失都会让整条规则消失,用户在预览面板根本看不到被丢弃的规则,无从修正。
## 目标
降低输入内容组织门槛,让 AI 能处理自然语言段落、松散列表、混合表格等各种写法;同时给用户最小化的内容指引,消除"不知道怎么组织内容"的困惑。核心字段(id / severity)由 AI 基于规则内容自动推断,解析侧提供兜底,预览阶段可人工修正。
## 方案概览
三处改动协同发挥作用:
1. **提示词增强** — 明确告知 AI 接受松散输入,主动推断 id 与 severity
2. **解析侧兜底** — severity 缺失降级为 warning,id 缺失生成占位符,丢弃可见
3. **预览与入口** — id 改可编辑,占位 id 高亮提示,导入入口加简短指引
设计原则是"AI 推断为主,解析兜底为底,人工预览为终"。AI 推断承担大部分场景,解析兜底保证下限不崩,预览阶段保留人工否决权。
## 改动一:提示词增强
位置:`src/rules/converters/prompt-builder.ts` 与六个转换器的 `buildSystemPrompt`
当前提示词只规定了输出字段清单,对输入形态只字未提。增强内容如下。
### 输入容忍说明
在系统提示词开头增加输入形态描述,告知 AI 接受以下任一形态:
- 自然语言段落(一段话描述多条规则)
- 无序列表(每条规则一行或一段)
- 表格(列名不固定,语义可推断)
- 混合形式(段落 + 列表 + 表格组合)
明确要求 AI 不得因输入形态非标准而拒绝转换,应主动从松散描述中提取规则语义。
### 非规则内容过滤
导入文件的内容不全是规则,可能混入项目介绍、背景说明、代码示例、章节标题等非规则内容。增加要求:
- AI 应识别并跳过非规则内容,只将真正的规则转为 YAML 条目
- 代码示例、项目介绍等仅作为上下文帮助理解规则语义,本身不输出为规则
- 若某段内容无法判断为规则(既无规则意图也无违反提示),直接忽略,不强行转成规则
- 此要求与解析侧兜底呼应:即使 AI 误将非规则内容输出为条目,解析时因 description 与 message 同时缺失会被丢弃
### id 推断要求
当前提示词仅说明"id: 规则唯一标识(kebab-case 英文)",未要求 AI 主动生成。改为:
- 每条规则**必须**输出 `id`,基于规则描述内容自动生成
- id 规范:kebab-case 英文、语义化、简短(如 `no-console-log``avoid-magic-number``require-error-handling`)
- 输入中即使无显式 id 标识,也要根据 description / message 的语义推断出合适的 id
- 多条规则间 id 不得重复
### severity 推断要求
当前提示词仅列出 severity 三档,未说明缺失时如何处理。增加:
- severity 必须输出,按规则语义推断:`error`(会导致 bug / 安全问题 / 数据损坏)、`warning`(潜在问题 / 不良实践)、`info`(风格 / 可读性建议)
- 输入中即使无显式严重级别,也要根据规则描述的后果严重程度推断
### description / message 互推要求
当前提示词将 description 与 message 视为两个独立必填字段。两者本意相近:description 偏"规则是什么",message 偏"违反时说什么"。增加:
- 两者必须至少输出一项;若输入只暗示了规则内容而未区分"描述"与"提示",AI 应据已有信息推断并补全另一项
- description 缺失时,由 message 反推简短描述;message 缺失时,由 description 推导违反提示
- 两者语义可相近,无需强行区分口吻
### 输入输出对照示例
在提示词中附 2-3 个"松散输入 → 标准输出"的对照示例,锚定 AI 的行为预期。示例应覆盖不同输入形态,包括含非规则内容的混合输入:
```
输入(松散段落):
"不要用 console.log,生产环境会泄露信息。还有不要留下未使用的变量,看着乱。"
输出:
- id: no-console-log
severity: warning
description: 禁止使用 console.log
message: 请使用 logger 工具替代 console.log
duplicateLevel: none
- id: no-unused-vars
severity: warning
description: 禁止未使用的变量
message: 未使用的变量应删除或注释
duplicateOf: eslint/no-unused-vars
duplicateLevel: exact
```
```
输入(含非规则内容的混合输入):
"本项目是一个电商后台管理系统,主要使用 Java + Spring Boot 开发。
代码规范要求:Service 层方法必须有日志记录,方便排查问题。
示例代码:
public void createOrder(Order order) { ... }
另外,Controller 层返回值统一用 Result 包装,不要直接返回 Map。"
输出:
- id: require-service-logging
severity: warning
description: Service 层方法必须有日志记录
message: Service 方法缺少日志记录,请补充以便排查问题
languages: [java]
duplicateLevel: none
- id: require-result-wrapper
severity: warning
description: Controller 返回值必须用 Result 包装
message: 请用 Result 包装返回值,不要直接返回 Map
languages: [java]
duplicateLevel: none
```
第二个示例中,项目介绍与代码示例均未转为规则,仅提取出两条真正的代码规范。示例的作用是让 AI 理解"一段话也能拆成多条规则"且"非规则内容应跳过",而非要求用户按此格式输入。
## 改动二:解析侧兜底
位置:`src/rules/import-service.ts``parseImportableYaml`
### 当前行为
`parseImportableYaml` 过滤条件为 `item.id && item.severity && item.description && item.message`,缺任一即丢弃,且丢弃不可见。
### 新行为
| 字段 | 缺失或非法时 | 处理 |
|------|-------------|------|
| `id` | 缺失 | 生成占位 id `rule-${序号}`(rule-1、rule-2…) |
| `severity` | 缺失或非三档之一 | 降级为 `warning` |
| `description` / `message` | 仅一项缺失 | 用存在的那个填充缺失项(description↔message 互为兜底) |
| `description``message` | 同时缺失 | 丢弃该条 |
`description``message` 本意相近,均为规则的文本表达:前者偏"规则是什么",后者偏"违反时说什么"。两者只要有一项存在,即可作为另一项的兜底来源(直接复制,或由 AI 在转换阶段据一项推断另一项);两者同时缺失,说明这段内容根本不是一条规则,丢弃合理。id 与 severity 属于"可推断字段",缺失时兜底补全而非丢弃。
### 丢弃无需可视化
被丢弃的内容因"既无 description 也无 message",本就不是规则,不属于"字段不全的规则",因此不设 `droppedCount` 字段,预览面板也不提示丢弃数。解析后的 `ImportableRule` 数组只包含确为规则的内容,预览展示与确认流程保持简洁。
## 改动三:预览与入口
位置:`src/rules/import-preview.ts``src/views/setupView.ts``src/i18n/messages.ts`
### 预览面板 id 可编辑
当前预览面板的 id 显示为只读 `<span>`(`import-preview.ts``renderRuleCard`)。改为 `<input>` 元素:
- id 输入框可编辑,用户可修改 AI 生成的 id 或补充占位 id
- 占位 id(`rule-N`)用高亮样式提示"请补充或确认"(如橙色边框 + 提示文案)
- `collectEditedRules()` 已收集 id,无需改动收集逻辑
### 确认校验扩展
当前 `validate()` 仅校验 description 与 message 非空。扩展为:
- id 非空(占位 id `rule-N` 视为非空,但高亮提示用户确认)
- description 非空
- message 非空
任一为空则阻止提交,在 `validationError` 区域显示具体提示。
### 导入入口指引
`setupView.ts` 的文件选择按钮旁增加一行简短说明,告知用户:
- 支持自由文本:一段话、列表、表格均可
- 每条规则尽量说清"查什么 + 违反时提示什么"
- 不强制字段顺序和格式,id 与严重级别可由系统自动推断
文案走 i18n,新增对应 key。
## 字段处理策略总表
改动完成后,四个核心字段的全链路处理策略如下:
| 字段 | AI 推断 | 解析兜底 | 预览可编辑 | 确认校验 |
|------|---------|---------|-----------|---------|
| `id` | 必生成,基于内容语义 | 缺则占位 `rule-N` | 是(改为 input) | 非空 |
| `severity` | 必输出,按语义推断 | 缺/非法→`warning` | 是(已可) | — |
| `description` | 必输出,缺失时由 message 推 | 缺则用 message 填充 | 是(已可) | 非空 |
| `message` | 必输出,缺失时由 description 推 | 缺则用 description 填充 | 是(已可) | 非空 |
`description``message` 同时缺失时丢弃该条(非规则,不提示)。`languages` / `excludeLanguages` / `duplicateOf` / `duplicateLevel` / `duplicateReason` 字段处理逻辑不变,维持现状。
## 改动文件清单
| 文件 | 改动内容 |
|------|---------|
| `src/rules/converters/prompt-builder.ts` | 提示词增强:输入容忍说明 + 非规则内容过滤 + id/severity 推断要求 + description/message 互推要求 + 对照示例 |
| `src/rules/converters/md-converter.ts` | 同步 `buildSystemPrompt` 增强内容(与 prompt-builder 对齐) |
| `src/rules/converters/txt-converter.ts` | 同上 |
| `src/rules/converters/docx-converter.ts` | 同上 |
| `src/rules/converters/excel-converter.ts` | 同上,表格输入说明略调 |
| `src/rules/converters/pptx-converter.ts` | 同上 |
| `src/rules/import-service.ts` | `parseImportableYaml` 放宽:severity 兜底、id 占位、description/message 互填 |
| `src/rules/import-preview.ts` | id 改可编辑 input + 占位 id 高亮 + 确认校验加 id 非空 |
| `src/views/setupView.ts` | 导入入口加简短内容指引文案 |
| `src/i18n/messages.ts` | 新增文案 key:占位提示、指引文案 |
注:六个转换器的 `buildSystemPrompt` 当前各自重复了完整提示词。改动一时可考虑将公共部分抽到 `prompt-builder.ts` 统一构建,减少重复;但为控制改动范围,本次仍保持各转换器独立维护提示词,仅同步增强内容。是否抽公共函数留待实现阶段判断。
## 测试更新
### 现有测试核查
| 测试文件 | 核查点 |
|---------|-------|
| `src/test/import-dedup.test.ts` | 是否依赖"缺 id 即丢弃"的旧行为;若是,同步调整断言 |
| `src/test/rule-filter.test.ts` | 是否依赖 severity 严格校验;`loadActiveRules` 行为不变,预期无影响 |
### 新增测试
| 测试场景 | 验证点 |
|---------|-------|
| severity 缺失 | 解析后降级为 `warning`,规则保留 |
| severity 非法值 | 解析后降级为 `warning`,规则保留 |
| id 缺失 | 解析后生成占位 id `rule-N`,规则保留 |
| description 缺失、message 存在 | 解析后 description 由 message 填充,规则保留 |
| message 缺失、description 存在 | 解析后 message 由 description 填充,规则保留 |
| description 与 message 同时缺失 | 该条被丢弃(非规则) |
| 正常完整输入 | 行为不变,无回归 |
## 不改动的部分
- 文件格式扩展名范围不变(仍六种)
- YAML 直传路径不变(不经 AI,本就最宽松)
- 预览 Webview 的整体布局与交互逻辑不变(仅 id 区域改动)
- 去重检测逻辑不变(`prompt-builder.ts``buildDedupPromptSection` 不动)
- 规则应用阶段不变(`rule-filter.ts``filterForDocument` 不动)
## 风险与权衡
### AI 推断的不确定性
改动一依赖 AI 行为,效果有不确定性:AI 可能生成不理想的 id(如过长、非 kebab-case、语义不准)。缓解措施:解析兜底保证下限(规则不丢失),预览阶段用户可逐条修正 id。即使 AI 推断不完美,也比"规则直接消失"好。
### 放宽丢弃条件的副作用
改动二放宽了丢弃条件,可能引入语义不完整的规则(如 id 为占位符、severity 兜底为 warning 但实际应是 error、description 与 message 文本雷同)。缓解措施:预览阶段用户仍可逐条删除/注释/修正,占位 id 高亮提示用户确认。可控。
### 提示词膨胀
六个转换器各维护一份完整提示词,改动一需同步六处,提示词长度增加。缓解措施:实现阶段评估是否抽公共函数到 `prompt-builder.ts`,若改动范围可控则保持现状。
### 兼容性
预览面板 id 改可编辑为向后兼容改动(原 `<span>``<input>`,收集逻辑已支持)。`parseImportableYaml` 放宽丢弃条件不影响现有调用方,原"完整字段才进入预览"的规则仍正常通过。
## 验证顺序
按项目规范 `lint → compile → test`:
1. `npm run lint` — ESLint 通过
2. `npm run compile` — TypeScript 编译通过
3. `npm test` — 全部测试通过,含新增测试用例
4. F5 启动 Extension Dev Host,手动验证:
- 导入松散文本(一段话描述多条规则),确认 AI 正确拆分并生成 id
- 导入缺 severity 的 YAML,确认降级为 warning 且规则保留
- 预览面板修改 id,确认可编辑
- 确认校验阻止空 id 提交