fix(小宝预警): 过滤无效页面刷新建议

This commit is contained in:
Script Generator
2026-06-30 09:44:41 +08:00
parent 50e76c8068
commit 4cecf2d77a
6 changed files with 97 additions and 7 deletions

View File

@@ -1,7 +1,7 @@
import { AiService } from './ai.service';
import type { AiGatewayService } from './ai-gateway.service';
import { DECOMPOSE_SYSTEM_PROMPT, DECOMPOSE_TOOL_INPUT_SCHEMA } from './prompts/decompose';
import { RISK_INTERPRET_TOOL_INPUT_SCHEMA } from './prompts/risk-interpret';
import { RISK_INTERPRET_SYSTEM_PROMPT, RISK_INTERPRET_TOOL_INPUT_SCHEMA } from './prompts/risk-interpret';
import { plainToInstance } from 'class-transformer';
import { validateSync } from 'class-validator';
import { RiskInterpretDto } from './dto/risk-interpret.dto';
@@ -104,6 +104,38 @@ describe('AiService', () => {
expect(callTool.mock.calls[0][0].userPrompt).toContain('风险上升');
});
it('removes invalid page refresh suggestions from risk interpretation results', async () => {
const callTool = jest.fn().mockResolvedValue({
toolName: 'submit_risk_interpretation',
toolInput: {
summary: '预计延期',
why: ['P1 Bug 增加'],
forecast: '预计 2026-07-04 可发',
suggestedActions: [
'手动刷新小宝预警页面,等待风险自动更新',
'优先修复 P1 Bug 并安排测试复测',
],
ownerHints: ['测试负责人同步失败用例'],
generatedAt: '2026-06-29T01:00:00.000Z',
},
rawModel: 'test-model',
inputTokens: 10,
outputTokens: 20,
});
const gateway = {
getActiveProvider: jest.fn().mockResolvedValue({ callTool }),
getActiveModel: jest.fn().mockResolvedValue('test-model'),
} as unknown as AiGatewayService;
const service = new AiService(gateway);
const result = await service.interpretRisk(createRiskRequest());
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.result.suggestedActions).toEqual(['优先修复 P1 Bug 并安排测试复测']);
}
});
it('returns PARSE_ERROR when risk provider returns malformed payload', async () => {
const gateway = {
getActiveProvider: jest.fn().mockResolvedValue({
@@ -211,6 +243,11 @@ describe('AiService', () => {
expect((RISK_INTERPRET_TOOL_INPUT_SCHEMA as any).additionalProperties).toBe(false);
});
it('tells the risk model not to suggest page refresh or manual update triggers', () => {
expect(RISK_INTERPRET_SYSTEM_PROMPT).toContain('不要建议刷新页面');
expect(RISK_INTERPRET_SYSTEM_PROMPT).toContain('系统会自动更新');
});
it('returns PARSE_ERROR when provider reports invalid tool arguments', async () => {
const gateway = {
getActiveProvider: jest.fn().mockResolvedValue({

View File

@@ -29,6 +29,13 @@ const DECOMPOSE_CONTEXT_CHAR_STEPS = [1800, 1200, 800] as const;
const PROTOTYPE_FETCH_TIMEOUT_MS = 15000;
const PROTOTYPE_FETCH_ATTEMPTS = 3;
const PROTOTYPE_FETCH_RETRY_DELAY_MS = 500;
const PAGE_REFRESH_ADVICE_PATTERNS = [
/(刷新|重新加载|重载).*(页面|浏览器|小宝|预警)/i,
/(页面|浏览器|小宝|预警).*(刷新|重新加载|重载)/i,
/(手动|主动).*(触发|刷新).*(更新|预警|分析)/i,
/(manual|manually).*(refresh|reload|trigger)/i,
/(refresh|reload).*(page|browser|xiaobao|warning)/i,
];
@Injectable()
export class AiService {
@@ -222,8 +229,8 @@ export class AiService {
summary,
why,
forecast,
suggestedActions,
ownerHints,
suggestedActions: this.filterPageRefreshAdvice(suggestedActions),
ownerHints: this.filterPageRefreshAdvice(ownerHints),
generatedAt,
};
if (input.recommendedReleaseWindow !== undefined) {
@@ -234,6 +241,14 @@ export class AiService {
return insight;
}
private filterPageRefreshAdvice(items: string[]): string[] {
return items.filter((item) => !this.isPageRefreshAdvice(item));
}
private isPageRefreshAdvice(text: string): boolean {
return PAGE_REFRESH_ADVICE_PATTERNS.some((pattern) => pattern.test(text));
}
private readString(value: unknown): string | null {
return typeof value === 'string' ? value : null;
}

View File

@@ -13,6 +13,8 @@ export const RISK_INTERPRET_SYSTEM_PROMPT = `你是 FTB 项目管理系统中的
输入中的 signals 是规则引擎压缩后的结构化风险信号,优先用它判断 Bug、测试失败、阻塞、静默风险和临近发版的变化。
如果 confidence 低,必须提醒“预测可信度较低,需补充数据”。
系统会自动更新小宝预警结果,用户不需要手动触发页面刷新或重新打开页面。
suggestedActions 只能给业务动作,例如修复 Bug、补充日报、复测失败用例、解除阻塞、调整排期或确认负责人不要建议刷新页面、手动触发更新、重新加载浏览器或等待页面自动更新。
输出必须通过 submit_risk_interpretation 工具返回,不要输出自然语言正文或 Markdown。`;