Commit 0ed020c3948396e69a535081cd887c93f7760fe9

Authored by yibo
1 parent 4c6d2325

fix(agents): 训练对话挂在 org-innovator 名下时,前端不再靠猜找链接

招聘自动训练与「重新训练」按钮都是给 org-innovator 发消息、由它去执行训练,
所以对话记录一直落在 org-innovator 的会话库里,而不是被训练的那个 agent 自己——
conversationId 里带目标 agent 的名字只是命名巧合,容易让人在错的 agent 页面下找不到记录
(今天实测:手工模板训练页删除前产生的一条记录就复现过这个问题)。

- company.js /hire:训练 conversationId 提前算好并通过 res.json 带回给前端
  (不再等 setTimeout 回调里才生成,那时响应早发完了)
- CompanyDashboard.vue 重新训练按钮:发完请求立即 window.open 到正确的
  /agent/org-innovator/{convId}
- CompanyDashboard.vue 招聘弹窗:雇佣触发自动训练时,弹窗给出可复制的正确链接
  (不自动开新标签页——这条训练要等 agent-creator 先写完定义,立刻打开大概率是空的)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
server/routes/company.js
... ... @@ -411,7 +411,12 @@ router.post(&#39;/:companyId/hire&#39;, async (req, res) =&gt; {
411 411 });
412 412  
413 413 // ── 核心逻辑扩展:自动补充定义 + 搜索注入 3 个初始 Skill + Innovator 训练 ──
  414 + // trainConversationId 提前算好(不等 setTimeout 里再算):训练对话实际挂在
  415 + // org-innovator 名下(这条 fetch 打的是它),前端要能拼出正确链接直接跳过去,
  416 + // 就得在 res.json 之前拿到这个值 —— 放进 setTimeout 回调里再算,响应早发完了。
  417 + let trainConversationId = null;
414 418 if (result.status === 'training' || result.needsTraining) {
  419 + trainConversationId = `train-${req.params.companyId}-${role}-${Date.now().toString(36)}`;
415 420 (async () => {
416 421 try {
417 422 console.log(`[Hire] 🚀 自动流水线启动: 为新员工 ${role} 安排 [定义 → 技能注入 → 训练]`);
... ... @@ -497,7 +502,7 @@ router.post(&#39;/:companyId/hire&#39;, async (req, res) =&gt; {
497 502 headers: { 'Content-Type': 'application/json' },
498 503 body: JSON.stringify({
499 504 message: `公司 ${req.params.companyId} 的员工 ${role} 骨架和基础技能已注入完毕。请你使用 FDE pipeline 对该 Agent ${namespacedId} (特别是它的技能)进行评测和进化训练,直到 Benchmark 达标 (Score ≥ 95)。训练过程包括 Hunt(验证数据), Digest, Evolver 等。请立即开始评估和训练,并保持向我汇报。`,
500   - conversationId: `train-${req.params.companyId}-${role}-${Date.now().toString(36)}`
  505 + conversationId: trainConversationId
501 506 })
502 507 }).catch(e => console.error('[Hire] Innovator 请求错误:', e.message));
503 508 }, 5000);
... ... @@ -519,7 +524,9 @@ router.post(&#39;/:companyId/hire&#39;, async (req, res) =&gt; {
519 524 console.warn(`[Hire] ⚠️ 心跳挂载跳过: ${hbErr.message}`);
520 525 }
521 526  
522   - res.json(result);
  527 + // trainConversationId 只在真的触发了训练时非空——前端凭它拼
  528 + // `/agent/org-innovator/{trainConversationId}` 直达训练对话,不用再去猜挂在哪个 agent 下面。
  529 + res.json({ ...result, trainConversationId });
523 530 } catch (err) {
524 531 res.status(400).json({ error: err.message });
525 532 }
... ...
web/src/views/CompanyDashboard.vue
... ... @@ -930,18 +930,27 @@ async function submitHire() {
930 930 hiring.value = true;
931 931 try {
932 932 const companyId = selectedCompany.value.id;
  933 + const trainConvIds = [];
933 934 for (const roleId of selectedTemplates.value) {
934 935 const tpl = templates.value.find(t => t.id === roleId);
935 936 if (!tpl) continue;
936   - await axios.post(`/api/company/${companyId}/hire`, {
  937 + const { data } = await axios.post(`/api/company/${companyId}/hire`, {
937 938 role: roleId,
938 939 type: tpl?.type,
939 940 description: tpl?.description
940 941 });
  942 + if (data?.trainConversationId) trainConvIds.push(data.trainConversationId);
941 943 }
942 944 showHireModal.value = false;
943 945 await loadCompanyStatus(companyId);
944 946 await loadCompanyAgents(companyId);
  947 + if (trainConvIds.length > 0) {
  948 + // 训练对话挂在 org-innovator 名下,且要等 agent-creator 填完定义后才真正开始
  949 + // (company.js /hire 里有 5 秒+delay),链接现在打开大概率是空的,所以不自动开新标签页,
  950 + // 只把正确地址交出来,用户过一会儿自己点。
  951 + const links = trainConvIds.map(id => `${window.location.origin}/agent/org-innovator/${id}`).join('\n')
  952 + alert(`✅ 雇佣成功,已自动排队 Innovator 训练。\n训练对话稍后可在这里查看(挂在 org-innovator 名下,不是新员工自己):\n${links}`)
  953 + }
945 954 } catch (e) {
946 955 alert(`❌ 雇佣失败: ${e.response?.data?.error || e.message}`);
947 956 } finally {
... ... @@ -1411,17 +1420,19 @@ ${trainInstructions.value}
1411 1420 3. 使用更新后的标准调用 Evolver 进行训练。完成全部 FDE 后向上级汇报最终结果。`
1412 1421  
1413 1422 // 发送请求,但不阻塞 UI
1414   - // org-innovator:27-naming §3.4 批次把 `innovator` 改名后这里没跟着改,
1415   - // 「重新训练」按钮从 2026-08-16 起一直静默 404(36 号文档阶段二实测发现)。
  1423 + const trainConvId = `train-${selectedCompany.value.id}-${agentId}-${Date.now().toString(36)}`
1416 1424 axios.post(`/api/agent/org-innovator/chat`, {
1417 1425 message,
1418   - conversationId: `train-${selectedCompany.value.id}-${agentId}-${Date.now().toString(36)}`,
  1426 + conversationId: trainConvId,
1419 1427 }).catch(e => {
1420 1428 console.error('Retrain task error:', e)
1421 1429 alert(`❌ 训练任务异常结束: ${e.message}`)
1422 1430 })
1423 1431  
1424   - alert(`✅ 训练指令已发送给 Innovator,将开始在后台进行自动评估和重训 ${agentId}\n您可以在 Agent 的实时状态面板中观察进展。`)
  1432 + // 训练对话实际挂在 org-innovator 名下(不是被训练的 agentId 自己),
  1433 + // 直接开新标签页跳过去,别指望用户自己拼对 URL。
  1434 + window.open(`/agent/org-innovator/${trainConvId}`, '_blank')
  1435 + alert(`✅ 训练指令已发送给 Innovator,将开始在后台进行自动评估和重训 ${agentId}\n训练对话已在新标签页打开(挂在 org-innovator 名下)。`)
1425 1436 showTrainModal.value = false
1426 1437 } catch (e) {
1427 1438 alert(`❌ 发送训练指令失败: ${e.message}`)
... ...