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

14 KiB

自定义规则导入体验改进设计

背景与问题

当前自定义规则导入功能支持六种文件格式,其中五种(.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-logavoid-magic-numberrequire-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.tsparseImportableYaml

当前行为

parseImportableYaml 过滤条件为 item.id && item.severity && item.description && item.message,缺任一即丢弃,且丢弃不可见。

新行为

字段 缺失或非法时 处理
id 缺失 生成占位 id rule-${序号}(rule-1、rule-2…)
severity 缺失或非三档之一 降级为 warning
description / message 仅一项缺失 用存在的那个填充缺失项(description↔message 互为兜底)
descriptionmessage 同时缺失 丢弃该条

descriptionmessage 本意相近,均为规则的文本表达:前者偏"规则是什么",后者偏"违反时说什么"。两者只要有一项存在,即可作为另一项的兜底来源(直接复制,或由 AI 在转换阶段据一项推断另一项);两者同时缺失,说明这段内容根本不是一条规则,丢弃合理。id 与 severity 属于"可推断字段",缺失时兜底补全而非丢弃。

丢弃无需可视化

被丢弃的内容因"既无 description 也无 message",本就不是规则,不属于"字段不全的规则",因此不设 droppedCount 字段,预览面板也不提示丢弃数。解析后的 ImportableRule 数组只包含确为规则的内容,预览展示与确认流程保持简洁。

改动三:预览与入口

位置:src/rules/import-preview.tssrc/views/setupView.tssrc/i18n/messages.ts

预览面板 id 可编辑

当前预览面板的 id 显示为只读 <span>(import-preview.tsrenderRuleCard)。改为 <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 填充 是(已可) 非空

descriptionmessage 同时缺失时丢弃该条(非规则,不提示)。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.tsbuildDedupPromptSection 不动)
  • 规则应用阶段不变(rule-filter.tsfilterForDocument 不动)

风险与权衡

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 提交