diff --git a/src/config/__tests__/config-manager.test.ts b/src/config/__tests__/config-manager.test.ts index c9f1eca..10c422b 100644 --- a/src/config/__tests__/config-manager.test.ts +++ b/src/config/__tests__/config-manager.test.ts @@ -28,6 +28,7 @@ const SCHEMA_KEYS = [ 'OPENAI_MODEL', 'CUSTOM_SUMMARY_PROMPT', 'CUSTOM_LINE_COMMENT_PROMPT', + 'GLOBAL_PROMPT', 'FEISHU_WEBHOOK_URL', 'FEISHU_WEBHOOK_SECRET', 'PORT', diff --git a/src/config/config-manager.ts b/src/config/config-manager.ts index 01c6fa7..af96f92 100644 --- a/src/config/config-manager.ts +++ b/src/config/config-manager.ts @@ -46,6 +46,7 @@ const envSchema = z.object({ OPENAI_MODEL: z.string().default('gpt-4o-mini'), CUSTOM_SUMMARY_PROMPT: z.string().optional(), CUSTOM_LINE_COMMENT_PROMPT: z.string().optional(), + GLOBAL_PROMPT: z.string().optional(), // Feishu FEISHU_WEBHOOK_URL: z.preprocess( @@ -118,6 +119,7 @@ export interface AppConfig { model: string; customSummaryPrompt: string | undefined; customLineCommentPrompt: string | undefined; + globalPrompt: string | undefined; }; feishu: { webhookUrl: string | undefined; @@ -271,6 +273,7 @@ class ConfigManager { model: env.OPENAI_MODEL, customSummaryPrompt: env.CUSTOM_SUMMARY_PROMPT, customLineCommentPrompt: env.CUSTOM_LINE_COMMENT_PROMPT, + globalPrompt: env.GLOBAL_PROMPT, }, feishu: { webhookUrl: env.FEISHU_WEBHOOK_URL, diff --git a/src/config/config-schema.ts b/src/config/config-schema.ts index 4eeee0a..3239e0b 100644 --- a/src/config/config-schema.ts +++ b/src/config/config-schema.ts @@ -159,6 +159,14 @@ export const CONFIG_FIELDS: ConfigFieldMeta[] = [ type: 'text', sensitive: false, }, + { + envKey: 'GLOBAL_PROMPT', + group: 'openai', + label: '全局提示词', + description: '附加到所有 LLM 调用的系统提示词中(例如:"请始终使用中文回复")', + type: 'text', + sensitive: false, + }, // ── 飞书 ──────────────────────────────────────────────────────────────── { diff --git a/src/controllers/config.ts b/src/controllers/config.ts index 989eea2..1c76e62 100644 --- a/src/controllers/config.ts +++ b/src/controllers/config.ts @@ -50,6 +50,8 @@ function getEffectiveValue( return current.openai.customSummaryPrompt; case 'CUSTOM_LINE_COMMENT_PROMPT': return current.openai.customLineCommentPrompt; + case 'GLOBAL_PROMPT': + return current.openai.globalPrompt; // Feishu case 'FEISHU_WEBHOOK_URL': return current.feishu.webhookUrl; diff --git a/src/review/agents/critic-agent.ts b/src/review/agents/critic-agent.ts index eeedd2a..3be61ee 100644 --- a/src/review/agents/critic-agent.ts +++ b/src/review/agents/critic-agent.ts @@ -1,5 +1,7 @@ import OpenAI from 'openai'; import { logger } from '../../utils/logger'; +import { withGlobalPrompt } from '../../utils/global-prompt'; +import config from '../../config'; import { Finding, ReviewContext } from '../types'; export interface CritiqueResult { @@ -76,7 +78,7 @@ ${context.diff.slice(0, 3000)} messages: [ { role: 'system', - content: '你是严格的代码审查质量评估专家,以高标准评估findings的质量。', + content: withGlobalPrompt('你是严格的代码审查质量评估专家,以高标准评估findings的质量。', config.openai.globalPrompt), }, { role: 'user', content: prompt }, ], @@ -165,7 +167,7 @@ ${context.diff.slice(0, 2000)} messages: [ { role: 'system', - content: '你是代码审查质量评估专家。', + content: withGlobalPrompt('你是代码审查质量评估专家。', config.openai.globalPrompt), }, { role: 'user', content: prompt }, ], diff --git a/src/review/agents/debate-orchestrator.ts b/src/review/agents/debate-orchestrator.ts index 54f25b9..d861d08 100644 --- a/src/review/agents/debate-orchestrator.ts +++ b/src/review/agents/debate-orchestrator.ts @@ -1,5 +1,7 @@ import OpenAI from 'openai'; import { logger } from '../../utils/logger'; +import { withGlobalPrompt } from '../../utils/global-prompt'; +import config from '../../config'; import { Finding, FindingSeverity } from '../types'; import { SpecialistAgent } from './specialist-agent'; @@ -108,7 +110,7 @@ export class DebateOrchestrator { messages: [ { role: 'system', - content: `你是${agentName},从你的专业角度独立评估代码问题。`, + content: withGlobalPrompt(`你是${agentName},从你的专业角度独立评估代码问题。`, config.openai.globalPrompt), }, { role: 'user', content: prompt }, ], @@ -185,7 +187,7 @@ ${otherOpinions messages: [ { role: 'system', - content: `你是${agentName},根据同行意见重新评估,但也要坚持你的专业判断。`, + content: withGlobalPrompt(`你是${agentName},根据同行意见重新评估,但也要坚持你的专业判断。`, config.openai.globalPrompt), }, { role: 'user', content: prompt }, ], diff --git a/src/review/agents/reflexion-agent.ts b/src/review/agents/reflexion-agent.ts index c4f8a06..72b1cd4 100644 --- a/src/review/agents/reflexion-agent.ts +++ b/src/review/agents/reflexion-agent.ts @@ -1,6 +1,8 @@ import { createHash } from 'node:crypto'; import OpenAI from 'openai'; import { logger } from '../../utils/logger'; +import { withGlobalPrompt } from '../../utils/global-prompt'; +import config from '../../config'; import { LearningSystem } from '../learning/learning-system'; import { findingResponseSchema } from '../schema/finding-schema'; import { ToolRegistry } from '../tools/registry'; @@ -147,7 +149,7 @@ ${context.diff.slice(0, 3000)} messages: [ { role: 'system', - content: `你是${this.agentName},根据批评反馈改进审查结果。`, + content: withGlobalPrompt(`你是${this.agentName},根据批评反馈改进审查结果。`, config.openai.globalPrompt), }, { role: 'user', content: prompt }, ], diff --git a/src/services/ai-review.ts b/src/services/ai-review.ts index 5ff2109..b9f2fb0 100644 --- a/src/services/ai-review.ts +++ b/src/services/ai-review.ts @@ -1,6 +1,7 @@ import OpenAI from 'openai'; import config from '../config'; import { logger } from '../utils/logger'; +import { withGlobalPrompt } from '../utils/global-prompt'; import { PullRequestFile, giteaService } from './gitea'; // 创建OpenAI客户端 @@ -206,7 +207,7 @@ export const aiReviewService = { { role: 'system', content: - '你是一个专业的代码审查助手,擅长识别代码中的严重问题和bug。你会查看代码的完整上下文,而不是为了评论而评论。如无明显问题,应给予简短肯定。', + withGlobalPrompt('你是一个专业的代码审查助手,擅长识别代码中的严重问题和bug。你会查看代码的完整上下文,而不是为了评论而评论。如无明显问题,应给予简短肯定。', config.openai.globalPrompt), }, { role: 'user', content: summaryPrompt }, ], @@ -278,7 +279,7 @@ export const aiReviewService = { { role: 'system', content: - '你是一个谨慎的代码审查助手,只对有明显bug或严重问题的代码行提供评论。大多数情况下,如果代码没有严重问题,你应该返回空数组。请以JSON格式返回结果。', + withGlobalPrompt('你是一个谨慎的代码审查助手,只对有明显bug或严重问题的代码行提供评论。大多数情况下,如果代码没有严重问题,你应该返回空数组。请以JSON格式返回结果。', config.openai.globalPrompt), }, { role: 'user', content: filePrompt }, ], diff --git a/src/utils/global-prompt.ts b/src/utils/global-prompt.ts new file mode 100644 index 0000000..8bc5c30 --- /dev/null +++ b/src/utils/global-prompt.ts @@ -0,0 +1,12 @@ +/** + * Helper to inject the global prompt into LLM system messages. + * + * If globalPrompt is non-empty, it is appended to the original system content + * separated by a blank line. Otherwise the original content is returned as-is. + */ +export function withGlobalPrompt(systemContent: string, globalPrompt: string | undefined): string { + if (!globalPrompt || globalPrompt.trim() === '') { + return systemContent; + } + return `${systemContent}\n\n${globalPrompt}`; +}