feat(rvw,asl): RVW V3.0 smart review + ASL deep research history + stability

RVW module (V3.0 Smart Review Enhancement):
- Add LLM data validation via PromptService (RVW_DATA_VALIDATION)
- Add ClinicalAssessmentSkill with FINER-based evaluation (RVW_CLINICAL)
- Remove all numeric scores from UI (editorial, methodology, overall)
- Implement partial_completed status with Promise.allSettled
- Add error_details JSON field to ReviewTask for granular failure info
- Fix overallStatus logic: warning status now counts as success
- Restructure ForensicsReport: per-table LLM results, remove top-level block
- Refactor ClinicalReport: structured collapsible sections
- Increase all skill timeouts to 300s for long manuscripts (20+ pages)
- Increase DataForensics LLM timeout to 180s, pg-boss to 15min
- Executor default fallback timeout 30s -> 60s

ASL module:
- Add deep research history with sidebar accordion UI
- Implement waterfall flow for historical task display
- Upgrade Unifuncs DeepSearch API from S2 to S3 with fallback
- Add ASL_SR module seed for admin configurability
- Fix new search button inconsistency

Docs:
- Update RVW module status to V3.0
- Update deployment changelist
- Add 0305 deployment summary

DB Migration:
- Add error_details JSONB column to rvw_schema.review_tasks

Tested: All 4 review modules verified, partial completion working
Made-with: Cursor
This commit is contained in:
2026-03-07 19:24:21 +08:00
parent 91ae80888e
commit 87655ea7e6
46 changed files with 2929 additions and 511 deletions

View File

@@ -0,0 +1,75 @@
/**
* RVW稿件审查模块 - 临床专业评估服务
* @module rvw/services/clinicalService
*
* 使用 PromptService 获取 RVW_CLINICAL prompt
* 返回纯 Markdown 报告(无分数)
*/
import { LLMFactory } from '../../../common/llm/adapters/LLMFactory.js';
import { ModelType } from '../../../common/llm/adapters/types.js';
import { logger } from '../../../common/logging/index.js';
import { prisma } from '../../../config/database.js';
import { getPromptService } from '../../../common/prompt/index.js';
export interface ClinicalReviewResult {
report: string;
summary: string;
}
/**
* 临床专业评估
* @param text 稿件文本
* @param modelType 模型类型
* @param userId 用户ID用于灰度预览判断
* @returns 评估结果Markdown 报告 + 摘要)
*/
export async function reviewClinical(
text: string,
modelType: ModelType = 'deepseek-v3',
userId?: string
): Promise<ClinicalReviewResult> {
try {
const promptService = getPromptService(prisma);
const { content: systemPrompt, isDraft } = await promptService.get(
'RVW_CLINICAL',
{},
{ userId }
);
if (isDraft) {
logger.info('[RVW:Clinical] 使用 DRAFT 版本 Prompt调试模式', { userId });
}
const messages = [
{ role: 'system' as const, content: systemPrompt },
{ role: 'user' as const, content: `请对以下医学稿件进行临床专业评估:\n\n${text}` },
];
logger.info('[RVW:Clinical] 开始临床专业评估', { modelType });
const llmAdapter = LLMFactory.getAdapter(modelType);
const response = await llmAdapter.chat(messages, {
temperature: 0.3,
maxTokens: 8000,
});
const content = response.content ?? '';
logger.info('[RVW:Clinical] 评估完成', {
modelType,
responseLength: content.length,
});
// 提取摘要取第一段非标题文本最多200字
const lines = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
const summary = lines.length > 0
? lines[0].trim().substring(0, 200)
: '临床专业评估已完成';
return { report: content, summary };
} catch (error) {
logger.error('[RVW:Clinical] 临床专业评估失败', {
error: error instanceof Error ? error.message : 'Unknown error',
stack: error instanceof Error ? error.stack : undefined,
});
throw new Error(`临床专业评估失败: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}