```
feat(engine): 优化智能体循环中的助手消息处理逻辑 - 在没有工具调用时才添加助手消息到上下文 - 确保工具调用响应正确添加到消息上下文中 - 修复了消息构建的条件逻辑 fix(cron): 改进定时任务调度的时间解析功能 - 添加正则表达式导入用于时间显示解析 - 实现从显示文本中提取毫秒间隔的功能 - 增强整数转换的安全性,避免类型错误 - 优化定时任务配置的解析逻辑 feat(outlook): 增强Outlook集成的功能和稳定性 - 将默认超时时间从10秒增加到180秒 - 为状态检查函数添加可选的验证参数 - 串行执行邮件概览获取操作而非并行 - 改进连接状态验证逻辑 feat(channel): 添加设备名称作为会话标识的选项 - 为终端WebSocket适配器添加新的配置选项 - 实现基于设备名称生成会话对等ID的功能 - 记录原始对等ID和设备名称的元数据 - 支持从设备名称创建会话对等ID feat(skills): 完善技能学习评估系统和进度跟踪 - 在应用启动时自动调度待评估的技能草稿 - 为技能评估工作创建独立的循环工厂 - 实现异步技能评估任务的取消和清理机制 - 添加技能评估进度报告和状态跟踪功能 - 扩展会话列表API以包含更多详细信息 - 防止对不存在的会话进行操作 - 优化技能草稿提交和评估的业务逻辑 perf(skills): 提升技能评估的并发性能 - 实现并行技能案例评估以提高效率 - 添加最大并行案例数的环境变量控制 - 实现实时评估进度更新和回调机制 - 优化评估过程中的资源管理和同步 refactor(services): 创建隔离的智能体循环实例 - 添加创建独立智能体循环的工厂方法 - 确保新循环继承运行时服务配置 - 支持技能评估等需要隔离环境的场景 ```
This commit is contained in:
@ -130,6 +130,16 @@ export default function SkillsPage() {
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!drafts.some((draft) => draft.eval_status === 'pending')) return;
|
||||
const timer = window.setInterval(() => {
|
||||
void listSkillDrafts()
|
||||
.then((items) => setDrafts(Array.isArray(items) ? items : []))
|
||||
.catch(() => null);
|
||||
}, 5000);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [drafts]);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab(normalizeSkillsTab(searchParams?.get('tab')));
|
||||
}, [searchParams]);
|
||||
@ -825,7 +835,8 @@ function DraftCard({
|
||||
safety?.suggested_fix,
|
||||
].filter(Boolean).join('\n');
|
||||
const safetyBlocksReview = Boolean(safety && (!safety.passed || safety.risk_level === 'critical'));
|
||||
const submitBlocked = draft.status !== 'draft' || safetyBlocksReview;
|
||||
const canRetryEval = draft.status === 'in_review' && draft.eval_status === 'failed';
|
||||
const submitBlocked = (draft.status !== 'draft' && !canRetryEval) || safetyBlocksReview;
|
||||
const rejectBlocked = !REJECTABLE_DRAFT_STATUSES.has(draft.status);
|
||||
const canPublishLabel = publishBlocked
|
||||
? publishBlockReason(draft, t)
|
||||
@ -912,7 +923,7 @@ function DraftCard({
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="outline" size="sm" className="h-11" disabled={busy || submitBlocked} onClick={() => void onSubmit()}>
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
{t('送审', 'Submit')}
|
||||
{canRetryEval ? t('重试评估', 'Retry eval') : t('送审', 'Submit')}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-11" disabled={busy || rejectBlocked} onClick={() => void onReject()}>
|
||||
<XCircle className="mr-2 h-4 w-4" />
|
||||
@ -988,7 +999,12 @@ function DraftCard({
|
||||
|
||||
<div className="mt-3 grid min-w-0 gap-3 md:grid-cols-2">
|
||||
<SafetyReportPanel report={safety} />
|
||||
<EvalReportPanel report={evalReport} />
|
||||
<EvalReportPanel
|
||||
report={evalReport}
|
||||
status={draft.eval_status}
|
||||
error={draft.eval_error}
|
||||
progress={draft.eval_progress}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -1111,10 +1127,55 @@ function lineDiffSummary(baseContent: string, proposedContent: string): { added:
|
||||
return { added, removed, changed };
|
||||
}
|
||||
|
||||
function EvalReportPanel({ report }: { report?: SkillDraftEvalReport | null }) {
|
||||
function EvalReportPanel({
|
||||
report,
|
||||
status,
|
||||
error,
|
||||
progress,
|
||||
}: {
|
||||
report?: SkillDraftEvalReport | null;
|
||||
status?: SkillDraft['eval_status'];
|
||||
error?: string | null;
|
||||
progress?: SkillDraft['eval_progress'];
|
||||
}) {
|
||||
const { locale } = useAppI18n();
|
||||
const t = (zh: string, en: string) => pickAppText(locale, zh, en);
|
||||
if (!report) {
|
||||
if (status === 'pending') {
|
||||
const completedArms = Math.max(0, Number(progress?.completed_arms || 0));
|
||||
const totalArms = Math.max(0, Number(progress?.total_arms || 0));
|
||||
const progressText = totalArms > 0
|
||||
? t(
|
||||
`评估正在后台运行:已完成 ${completedArms}/${totalArms} 次回放(共 ${progress?.total_cases || 10} 个案例,每个案例包含 baseline 和 candidate)。`,
|
||||
`Evaluation is running: ${completedArms}/${totalArms} replays completed (${progress?.total_cases || 10} cases, each with baseline and candidate).`
|
||||
)
|
||||
: t('评估正在准备案例,完成后会自动更新。', 'Evaluation cases are being prepared and will update automatically.');
|
||||
return (
|
||||
<ReadablePanel
|
||||
icon={<Loader2 className="h-4 w-4 animate-spin" />}
|
||||
title={t('评估报告', 'Eval report')}
|
||||
empty={progressText}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (status === 'failed') {
|
||||
return (
|
||||
<ReadablePanel
|
||||
icon={<BarChart3 className="h-4 w-4 text-destructive" />}
|
||||
title={t('评估报告', 'Eval report')}
|
||||
empty={`${t('评估失败,可再次点击送审重试。', 'Evaluation failed. Submit again to retry.')} ${error || ''}`.trim()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (status === 'not_applicable') {
|
||||
return (
|
||||
<ReadablePanel
|
||||
icon={<BarChart3 className="h-4 w-4" />}
|
||||
title={t('评估报告', 'Eval report')}
|
||||
empty={t('该草稿没有关联学习候选,不运行 replay eval。', 'This draft has no linked learning candidate, so replay eval does not run.')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ReadablePanel
|
||||
icon={<BarChart3 className="h-4 w-4" />}
|
||||
|
||||
Reference in New Issue
Block a user