# 自定义规则导入体验改进设计 ## 背景与问题 当前自定义规则导入功能支持六种文件格式,其中五种(`.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 显示为只读 ``(`import-preview.ts` 的 `renderRuleCard`)。改为 `` 元素: - 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 改可编辑为向后兼容改动(原 `` 改 ``,收集逻辑已支持)。`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 提交