diff --git a/src/ai/engine.ts b/src/ai/engine.ts index e3c9cf2..4a6d000 100644 --- a/src/ai/engine.ts +++ b/src/ai/engine.ts @@ -9,7 +9,7 @@ import type { TranslatedDiagnostic, AIFinding, } from './schema'; -import { t } from '../i18n/messages'; +import { t, getLanguage } from '../i18n/messages'; function buildCustomRulePrompt(rules: CustomRule[]): string { return rules.map(r => @@ -61,7 +61,7 @@ function parseJsonResponse(raw: string): object { const start = trimmed.indexOf('{'); const end = trimmed.lastIndexOf('}'); if (start === -1 || end === -1) { - throw new Error(`响应中未找到 JSON。原始响应(前200字符):${trimmed.slice(0, 200)}`); + throw new Error(t('engine.jsonNotFound', { 0: trimmed.slice(0, 200) })); } let jsonStr = trimmed.substring(start, end + 1); try { @@ -71,29 +71,103 @@ function parseJsonResponse(raw: string): object { try { return JSON.parse(jsonStr); } catch { - throw new Error(`JSON 解析失败。原始响应(前200字符):${jsonStr.slice(0, 200)}`); + throw new Error(t('engine.jsonParseFail', { 0: jsonStr.slice(0, 200) })); } } } -const CUSTOM_RULE_SYSTEM_PROMPT = `你是代码规则审查员,只评估以下自定义规则是否被违反。 +function buildCustomRuleSystemPrompt(): string { + const lang = getLanguage(); + if (lang === 'ja') { + return `あなたはコードルールレビュアーです。以下のカスタムルールに違反しているかどうかのみを評価してください。 +意味を理解し、テキストの一致ではなく判断してください。 +JSONのみを出力、形式: +{ "customRuleResults": [{ "ruleId": "ルールID", "line": 行番号, "severity": "error|warning|info", "message": "違反の説明" }] } +ルールに違反していない場合は空の配列を返してください。 + +出力言語:ja`; + } + if (lang === 'en') { + return `You are a code rule reviewer. Only evaluate whether the following custom rules are violated. +Understand semantics, not text matching. +Output JSON only, format: +{ "customRuleResults": [{ "ruleId": "rule id", "line": line number, "severity": "error|warning|info", "message": "violation description" }] } +If no rules are violated, return an empty array. + +Output language: en`; + } + return `你是代码规则审查员,只评估以下自定义规则是否被违反。 理解语义而非文本匹配。 仅输出 JSON,格式: { "customRuleResults": [{ "ruleId": "规则ID", "line": 行号, "severity": "error|warning|info", "message": "触发描述" }] } -如果没有违反任何规则,返回空数组。`; +如果没有违反任何规则,返回空数组。 -const DEEP_REVIEW_SYSTEM_PROMPT = `你是资深代码审查专家,完成两个任务: +输出语言:zh-CN`; +} + +function buildDeepReviewSystemPrompt(): string { + const lang = getLanguage(); + if (lang === 'ja') { + return `あなたはシニアコードレビュー専門家です。2つのタスクを実行してください: +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": 行番号 }] +} + +出力言語:ja`; + } + if (lang === 'en') { + return `You are a senior code review expert. Complete two tasks: +1. Translate English static analysis results into the output language and add fix suggestions +2. Deeply review the code to find issues not covered by static analysis +Focus on: security vulnerabilities, logic errors, performance issues, design flaws +Do not duplicate issues already reported by static analysis. + +Output JSON only. Double quotes in strings must be escaped with \\". +Format: +{ + "translatedDiagnostics": [{ "originalRuleId": "original id", "translatedMessage": "translated message", "translatedSuggestion": "suggestion", "codeDiff": "optional" }], + "findings": [{ "ruleId": "kebab-case", "severity": "error|warning|info", "category": "bug|performance|security|style|design", "title": "title", "description": "description", "suggestion": "suggestion", "codeDiff": "optional", "line": line number }] +} + +Output language: en`; + } + return `你是资深代码审查专家,完成两个任务: 1. 将英文静态分析结果翻译为输出语言,并补充修复建议 2. 深度审查代码,发现静态分析未覆盖的问题 重点:安全漏洞、逻辑错误、性能问题、设计缺陷 不要重复静态分析已报告的问题。 -仅输出 JSON,字符串中的双引号必须用 \" 转义。 +仅输出 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": 行号 }] -}`; +} + +输出语言:zh-CN`; +} + +function buildUserPromptCustomRules(customRules: CustomRule[], numberedCode: string): string { + const lang = getLanguage(); + const label = lang === 'ja' ? 'カスタムルール' : lang === 'en' ? 'Custom Rules' : '自定义规则'; + const codeLabel = lang === 'ja' ? 'コード(行番号付き)' : lang === 'en' ? 'Code (with line numbers)' : '代码(带行号)'; + return `## ${label}\n${buildCustomRulePrompt(customRules)}\n\n## ${codeLabel}\n${numberedCode}`; +} + +function buildUserPromptDeepReview(numberedCode: string, staticDiagnostics: LinterDiagnostic[]): string { + const lang = getLanguage(); + const codeLabel = lang === 'ja' ? 'コード(行番号付き)' : lang === 'en' ? 'Code (with line numbers)' : '代码(带行号)'; + const resultLabel = lang === 'ja' ? '静的解析結果' : lang === 'en' ? 'Static Analysis Results' : '静态分析结果(英文)'; + return `## ${codeLabel}\n${numberedCode}\n\n## ${resultLabel}\n${buildLinterDiagnosticsPrompt(staticDiagnostics)}`; +} export async function runAIReview( context: vscode.ExtensionContext, @@ -141,15 +215,15 @@ export async function runAIReview( const requestA = customRules.length > 0 ? provider.chat( - CUSTOM_RULE_SYSTEM_PROMPT, - `## 自定义规则\n${buildCustomRulePrompt(customRules)}\n\n## 代码(带行号)\n${numberedCode}`, + buildCustomRuleSystemPrompt(), + buildUserPromptCustomRules(customRules, numberedCode), options ) : Promise.resolve('{}'); const requestB = provider.chat( - `${DEEP_REVIEW_SYSTEM_PROMPT}\n输出语言:${getAIOutputLanguage()}`, - `## 代码(带行号)\n${numberedCode}\n\n## 静态分析结果(英文)\n${buildLinterDiagnosticsPrompt(staticDiagnostics)}`, + buildDeepReviewSystemPrompt(), + buildUserPromptDeepReview(numberedCode, staticDiagnostics), options ); diff --git a/src/views/setupView.ts b/src/views/setupView.ts index afe1eb1..6bdfb88 100644 --- a/src/views/setupView.ts +++ b/src/views/setupView.ts @@ -13,16 +13,7 @@ import { TxtConverter } from '../rules/converters/txt-converter'; import { ExcelConverter } from '../rules/converters/excel-converter'; import { DocxConverter } from '../rules/converters/docx-converter'; import { PptxConverter } from '../rules/converters/pptx-converter'; -import { t, onLanguageChange, Language } from '../i18n/messages'; - -function getLanguageLabel(lang: string): string { - switch (lang) { - case 'zh-CN': return t('lang.zhCN'); - case 'en': return t('lang.en'); - case 'ja': return t('lang.ja'); - default: return lang; - } -} +import { t, onLanguageChange } from '../i18n/messages'; export class SetupViewProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; @@ -146,7 +137,6 @@ export class SetupViewProvider implements vscode.WebviewViewProvider { baseUrl: baseUrlConfigured ? config.baseUrl : '', baseUrlConfigured, language: config.outputLanguage, - languageLabel: getLanguageLabel(config.outputLanguage), apiKeyConfigured, }, providers: getAllProviderMeta(), @@ -599,9 +589,9 @@ input::placeholder { color: var(--vscode-input-placeholderForeground, var(--vsco