# Step 10 — Phase 4.2: AI 引擎 + Schema **依赖**: Step 09 **参考设计**: §5.3, §5.4, §5.5, §5.6 ## 目标 实现 AI 审查引擎:两并行请求(自定义规则评估 + 翻译深度审查)、JSON 解析、错误降级。 ## 新建文件 | # | 文件 | 说明 | |---|------|------| | 1 | `src/ai/schema.ts` | AI 响应结构类型定义 | | 2 | `src/ai/engine.ts` | `runAIReview()` 主函数 | --- ## 1. `src/ai/schema.ts` ```typescript export interface TranslatedDiagnostic { originalRuleId: string; translatedMessage: string; translatedSuggestion: string; codeDiff?: string; } export interface CustomRuleResult { ruleId: string; line: number; severity: 'error' | 'warning' | 'info'; message: string; } export interface AIFinding { ruleId: string; severity: 'error' | 'warning' | 'info'; category: 'bug' | 'performance' | 'security' | 'style' | 'design'; title: string; description: string; suggestion: string; codeDiff?: string; line: number; } export interface AIResponse { translatedDiagnostics: TranslatedDiagnostic[]; customRuleResults: CustomRuleResult[]; findings: AIFinding[]; } export interface AIEngineResult { customRuleResults: CustomRuleResult[]; translatedDiagnostics: TranslatedDiagnostic[]; findings: AIFinding[]; degraded: boolean; error?: string; } ``` --- ## 2. `src/ai/engine.ts` ```typescript import * as vscode from 'vscode'; import { AIProvider } from './providers/base'; import { createProvider } from './factory'; import { getAIConfig, getApiKey } from '../config'; import { LinterDiagnostic } from '../types'; import { CustomRule } from '../rules/yaml-parser'; import { AIEngineResult, CustomRuleResult, TranslatedDiagnostic, AIFinding, } from './schema'; function buildCustomRulePrompt(rules: CustomRule[]): string { return rules.map(r => `- [${r.id}] (${r.severity}) ${r.description}` ).join('\n'); } function buildLinterDiagnosticsPrompt(diagnostics: LinterDiagnostic[]): string { return diagnostics.map(d => `- [${d.ruleId}] L${d.range.start.line + 1}: ${d.message}` ).join('\n'); } function addLineNumbers(code: string): string { return code.split('\n').map((line, i) => `${String(i + 1).padStart(4, ' ')}| ${line}`).join('\n'); } function parseJsonResponse(raw: string): object { const trimmed = raw.trim(); const start = trimmed.indexOf('{'); const end = trimmed.lastIndexOf('}'); if (start === -1 || end === -1) { throw new Error('响应中未找到 JSON'); } return JSON.parse(trimmed.substring(start, end + 1)); } const CUSTOM_RULE_SYSTEM_PROMPT = `你是代码规则审查员,只评估以下自定义规则是否被违反。 理解语义而非文本匹配。 仅输出 JSON,格式: { "customRuleResults": [{ "ruleId": "规则ID", "line": 行号, "severity": "error|warning|info", "message": "触发描述" }] } 如果没有违反任何规则,返回空数组。`; const DEEP_REVIEW_SYSTEM_PROMPT = `你是资深代码审查专家,完成两个任务: 1. 将英文静态分析结果翻译为输出语言,并补充修复建议 2. 深度审查代码,发现静态分析未覆盖的问题 重点:安全漏洞、逻辑错误、性能问题、设计缺陷 不要重复静态分析已报告的问题。 仅输出 JSON,格式: { "translatedDiagnostics": [{ "originalRuleId": "原始ID", "translatedMessage": "翻译", "translatedSuggestion": "建议", "codeDiff": "可选" }], "findings": [{ "ruleId": "kebab-case", "severity": "error|warning|info", "category": "bug|performance|security|style|design", "title": "标题", "description": "描述", "suggestion": "建议", "codeDiff": "可选", "line": 行号 }] }`; export async function runAIReview( context: vscode.ExtensionContext, code: string, staticDiagnostics: LinterDiagnostic[], customRules: CustomRule[] ): Promise { const config = getAIConfig(); const apiKey = await getApiKey(context); if (!apiKey) { return { customRuleResults: [], translatedDiagnostics: [], findings: [], degraded: true, error: '未配置 API Key', }; } let provider: AIProvider; try { provider = createProvider(config.provider, apiKey, config.baseUrl); } catch (err) { return { customRuleResults: [], translatedDiagnostics: [], findings: [], degraded: true, error: `创建 Provider 失败: ${err instanceof Error ? err.message : String(err)}`, }; } const options = { model: config.model, temperature: config.temperature, timeoutMs: config.timeout * 1000, }; const numberedCode = addLineNumbers(code); const requestA = customRules.length > 0 ? provider.chat( CUSTOM_RULE_SYSTEM_PROMPT, `## 自定义规则\n${buildCustomRulePrompt(customRules)}\n\n## 代码(带行号)\n${numberedCode}`, options ) : Promise.resolve('{}'); const requestB = provider.chat( `${DEEP_REVIEW_SYSTEM_PROMPT}\n输出语言:${config.outputLanguage}`, `## 代码(带行号)\n${numberedCode}\n\n## 静态分析结果(英文)\n${buildLinterDiagnosticsPrompt(staticDiagnostics)}`, options ); const [resultA, resultB] = await Promise.allSettled([requestA, requestB]); const errors: string[] = []; let customRuleResults: CustomRuleResult[] = []; if (resultA.status === 'fulfilled') { try { const parsed = parseJsonResponse(resultA.value) as { customRuleResults?: CustomRuleResult[] }; customRuleResults = (parsed.customRuleResults ?? []).map(r => ({ ...r, ruleId: `custom:${r.ruleId}`, })); } catch { errors.push('自定义规则响应解析失败'); } } else { errors.push(`自定义规则请求失败: ${resultA.reason}`); } let translatedDiagnostics: TranslatedDiagnostic[] = []; let findings: AIFinding[] = []; if (resultB.status === 'fulfilled') { try { const parsed = parseJsonResponse(resultB.value) as { translatedDiagnostics?: TranslatedDiagnostic[]; findings?: AIFinding[]; }; translatedDiagnostics = parsed.translatedDiagnostics ?? []; findings = parsed.findings ?? []; } catch { errors.push('AI 审查响应解析失败'); } } else { errors.push(`AI 审查请求失败: ${resultB.reason}`); } const degraded = errors.length > 0; return { customRuleResults, translatedDiagnostics, findings, degraded, error: errors.join('; '), }; } ``` --- ## 关键逻辑 **两并行请求**: | 请求 | 内容 | System Prompt | |------|------|---------------| | A | 自定义规则评估(需完整代码) | 只评估规则、语义理解、输出 JSON | | B | 翻译 + 深度审查(需静态分析结果) | 翻译诊断 + 深度审查、不重复静态分析、输出 JSON | **降级策略**: 1. `Promise.allSettled` 确保单请求失败不影响另一个 2. API Key 未配置 → 所有 AI 功能降级 3. JSON 解析失败 → 该请求降级,记录错误 4. Provider 创建失败 → 全部降级 **Prompt 设计**: - 请求 A: 注入自定义规则的 description 列表 - 请求 B: 注入静态分析英文诊断列表 + 输出语言配置 --- ## 验收 - [ ] 2 个文件创建完成 - [ ] `npm run compile` 通过 - [ ] `npm run lint` 通过