// @ts-nocheck export function stripHints(md) { return String(md || '') .replace(//gi, '') .replace(/\n{3,}/g, '\n\n') .trim(); } export function splitModule(mdBlock) { const lines = String(mdBlock || '').split(/\r?\n/); const tables = []; const hints = []; let currentTable = null; let hintBuf = null; const flushTable = () => { if (!currentTable) return; currentTable.md = String(currentTable.md || '').trimEnd(); if (currentTable.md) tables.push(currentTable); currentTable = null; }; const flushHint = (raw) => { const m = String(raw || '').match(//i); if (m) { const text = m[1].replace(/\s+/g, ' ').trim(); if (text) hints.push(text); } }; for (const line of lines) { const trimmed = line.trim(); if (hintBuf !== null) { hintBuf += `\n${line}`; if (trimmed.includes('-->')) { flushHint(hintBuf); hintBuf = null; } continue; } if (/^')) flushHint(trimmed); else hintBuf = line; continue; } if (/^###\s+/.test(trimmed)) { flushTable(); currentTable = { title: trimmed.replace(/^###\s+/, '').trim(), md: '' }; continue; } if (trimmed.startsWith('|')) { if (!currentTable) currentTable = { title: '', md: '' }; currentTable.md += `${line}\n`; continue; } if (currentTable && currentTable.md) flushTable(); } if (hintBuf !== null) flushHint(`${hintBuf}-->`); flushTable(); return { tables, hints }; } export function parsePidMap(meta) { if (!meta || typeof meta !== 'object') return {}; const m = meta.pidMap; if (!m || typeof m !== 'object') return {}; return m; } export function parseMainPid(meta) { if (!meta || typeof meta !== 'object') return null; return meta.mainPid || null; } export function parseCandidates(meta) { if (!meta || typeof meta !== 'object') return null; return Array.isArray(meta.candidates) ? meta.candidates : null; } export function replacePlaceholders(md, pidMap, opts = {}) { if (!md) return { text: md, warnings: [] }; if (!pidMap || typeof pidMap !== 'object') return { text: md, warnings: [] }; const warnings = []; let text = String(md); text = text.replace(/\[(?:NAME|STATION|CAR|TRAIN|TEAM):(emp_[a-z0-9]+)\]/gi, (m, pid) => { const entry = pidMap[pid]; if (entry?.name) return entry.name; warnings.push({ token: m, pid, kind: 'NAME' }); return m; }); text = text.replace(/\[ID:(emp_[a-z0-9]+)\]/gi, (m, pid) => { const entry = pidMap[pid]; if (entry?.id != null) return String(entry.id); warnings.push({ token: m, pid, kind: 'ID' }); return m; }); if (opts.replaceBare) { text = text.replace(/\bemp_[a-z0-9]+\b/gi, (m) => { const entry = pidMap[m]; if (entry?.id != null) return String(entry.id); warnings.push({ token: m, pid: m, kind: 'BARE' }); return m; }); } return { text, warnings }; } export function replacePlaceholdersText(md, pidMap, opts) { return replacePlaceholders(md, pidMap, opts).text; } export function parseModules(md) { const text = String(md || '').replace(/^\uFEFF/, '').trim(); if (!text) return { title: '', modules: [] }; const lines = text.split(/\r?\n/); let title = ''; const raw = []; let current = null; for (const line of lines) { if (/^#\s+[^#]/.test(line) && !current && raw.length === 0) { title = line.replace(/^#\s+/, '').trim(); continue; } if (/^##\s+/.test(line)) { if (current) raw.push(current); current = { name: line.replace(/^##\s+/, '').trim(), lines: [] }; continue; } if (current) { current.lines.push(line); } else if (line.trim() && !title) { current = { name: '概况', lines: [line] }; } else if (current === null && line.trim() && title) { current = { name: title, lines: [line] }; title = ''; } } if (current) raw.push(current); const modules = raw.map((block) => { const { tables, hints } = splitModule(block.lines.join('\n')); return { name: block.name, tables, hints }; }).filter((m) => m.tables.length > 0 || m.hints.length > 0 || m.name); return { title, modules }; } export function assembleModuleMarkdown(mod, analysis) { const chunks = []; if (mod?.name) chunks.push(`## ${mod.name}`); for (const t of mod?.tables || []) { if (t.title) chunks.push(`### ${t.title}`); if (t.md) chunks.push(String(t.md).trim()); } const a = String(analysis || '').trim(); if (a) chunks.push(a); return chunks.join('\n\n'); } export function assembleReportMarkdown(title, modulesWithAnalysis) { const chunks = []; if (title) chunks.push(`# ${title}`); for (const item of modulesWithAnalysis || []) { chunks.push(assembleModuleMarkdown(item.mod, item.analysis)); } return chunks.filter(Boolean).join('\n\n').trim(); } export function compactTablesForPrompt(tables, maxRows = 36) { const blocks = []; for (const t of tables || []) { const md = String(t?.md || '').trim(); if (!md) continue; const lines = md.split(/\r?\n/).filter((l) => l.trim()); if (!lines.length) continue; const title = String(t.title || '').trim(); let body = lines; if (lines.length > maxRows + 2) { const head = lines.slice(0, 2); const data = lines.slice(2); const keep = Math.max(8, maxRows); const headPart = data.slice(0, Math.ceil(keep * 0.7)); const tailPart = data.slice(-Math.floor(keep * 0.3)); const omitted = data.length - headPart.length - tailPart.length; body = [ ...head, ...headPart, `| … | (其余 ${omitted} 行已省略,分析时勿臆造被省略行的具体内容) |`, ...tailPart, ]; } blocks.push([ title ? `### ${title}` : '### 数据表', body.join('\n'), ].join('\n')); } return blocks.join('\n\n').trim(); } export function buildAnalysisSystemPrompt(opts = {}) { const kind = opts.reportKind === 'team' ? 'team' : opts.reportKind === 'person' ? 'person' : ''; const isSingleEmployee = !!opts.mainPid; const tokenConstraint = isSingleEmployee ? `【token 使用规范 - 单员工】 - 表格/HINT 里的 [NAME:emp_xxx] 是占位符,对应本次分析的员工(具体假名见用户消息) - 引用员工时:可以用「该员工」代词,或直接复制 [NAME:emp_xxx] token,都接受 - 严禁编造姓名(如"张三""李四") - 严禁拆解 token(如"emp_a3b8c1 可能是...") - 严禁在输出里出现 emp_xxx 假名字面量(应输出 [NAME:emp_xxx] 或「该员工」)` : `【token 使用规范 - 多员工对比】 - 当前上下文涉及多名员工,每人对应唯一的 [NAME:emp_xxx] 令牌 - 对比分析时,必须严格使用 [NAME:emp_xxx] 令牌指代(例:「[NAME:emp_a3b8c1] 绩效优于 [NAME:emp_f2c9d0]」) - 严禁使用代词:「该员工」「前者」「后者」「第一名」等模糊指代,在多员工场景下会让用户混淆 - 严禁编造姓名:你看不到真实姓名,不要猜(如「张三」「李四」) - 严禁拆解 token:不要解释 emp_xxx 可能对应谁 - token 是占位符,直接复制使用即可`; const kindRule = kind === 'person' ? '\n【口径】全程保持个人口径,不要写「部分人员」「班组内」「排班策略」「人员调配」等班组管理表述。\n' : ''; return `【模块分析任务 - 通用规则】 你是业务分析助理。界面已直渲报告表格;用户消息会给你同一份数据与提纲,请**基于表内数字与事实**写分析。 ${tokenConstraint} ${kindRule} 排版(必须严格遵守): 用下面三个小标题,每个标题单独成行,标题用 **加粗**: **总体表现** (结合表内关键数字概括水平/对比/含义) **主要问题** (结合薄弱项、异常行、波动指出问题;无明显问题则写「本模块未见突出短板」并引用依据) **改进建议** - 建议一(具体、可执行,对应上面问题) - 建议二 - 建议三(可选) 硬性要求: 1. 分析必须引用或呼应表里的具体数据(得分/人次/排名/分类名等),禁止空泛套话 2. 正文禁止输出任何表格符号(|)或 Markdown 表,禁止输出 # / ## / ### 3. 不要编造表与提纲之外的数字;全文简体中文 4. 若开启思考:只写对数据的解读与判断(因果/对比/风险),严禁复述输入清单、严禁约束自检清单`; } export function buildAnalysisPrompt(moduleName, hints, opts = {}) { const list = (hints || []).map((h) => `- ${h}`).join('\n') || '- (本模块无额外提纲;请主要依据下方数据表分析)'; const kind = opts.reportKind === 'team' ? 'team' : opts.reportKind === 'person' ? 'person' : ''; const dataBlock = compactTablesForPrompt(opts.tables || []); const extras = []; if (opts.mainPid) { extras.push(`本次分析的员工假名为 ${opts.mainPid}(对应表格/HINT 里的 [NAME:emp_xxx] 占位符)。`); } if (moduleName === '综合评价') { extras.push('若提及前五/后五,只能写进散文句子(如「领先梯队有张三、李四…」),严禁输出任何排名表、名单表或 GFM 表格。'); if (kind === 'person') { extras.push('本报告为**个人**综合评价:只写该员工定位/优势/业务短板与针对本人的建议;严禁班组人事管理话术(兼职借调清单、排班调配、谈心谈话机制、人员结构均衡、班组共性问题等)。'); } } if (kind === 'person' && (moduleName === '基本信息' || moduleName === '六维评分')) { extras.push('备注仅可原样点出本人状态;无备注则不要提兼职/借调;禁止升维成班组共性问题或管理建议。'); } const summaryExtra = extras.length ? `\n本模块特别约束:\n${extras.map((e) => `- ${e}`).join('\n')}\n` : ''; const dataSection = dataBlock ? `【本模块数据(只读,界面已展示;禁止在回复中重打表格或复制 | 行)】\n${dataBlock}\n` : '【本模块数据】暂无表格,仅依据提纲分析。\n'; return `请为模块「${moduleName}」写结构化分析(写透即可,不设字数上限)。 ${summaryExtra} ${dataSection} 【分析要点(HINT,可与表对照,勿只复述 HINT)】 ${list} 按 system 中【模块分析任务 - 通用规则】的三个加粗小标题与硬性要求输出,不要输出标题行或表格。`; } export function inferReportKind(title) { const t = String(title || ''); if (/个人.*年度报告|个人报告/.test(t)) return 'person'; if (/班组.*年度报告|班组报告|班组概况/.test(t)) return 'team'; return ''; } export function buildCrossModulePrompt(prevModules, reportKind) { const kind = reportKind === 'team' ? 'team' : reportKind === 'person' ? 'person' : ''; const dataBlock = (prevModules || []) .filter(m => m && m.name && m.name !== '综合评价') .map(m => { const hintsTxt = (m.hints || []).map(h => ` - ${h}`).join('\n'); const tablesTxt = compactTablesForPrompt(m.tables || [], 8); return `### ${m.name}\n${hintsTxt ? `\n关键事实:\n${hintsTxt}` : ''}${tablesTxt ? `\n${tablesTxt}` : ''}`; }) .join('\n\n---\n\n'); const scopeWord = kind === 'team' ? '该班组' : '该员工'; const scopeWordLine = kind === 'team' ? '全线路' : ''; return `【跨模块深度诊断】 你是 ${kind === 'team' ? '班组' : '员工'}能力评估专家。基于下方全部 ${prevModules?.length || 0} 个模块的事实摘要与表格,写一份跨模块深度诊断。 【分析对象】${scopeWord}${scopeWordLine ? `(${scopeWordLine})` : ''}本年度全部模块数据 【全部模块数据】 ${dataBlock || '(暂无模块数据,请基于常识与口径要求输出谨慎分析)'} 【任务要求】 1. **跨模块关联**(必含 ≥2 个): 找出不同模块数据之间的关联。例: - 公里数异常月份是否与考核扣分月份吻合 - 实操弱项类别与故障处置短板是否一致 - 六维评分弱项与对应模块明细是否对应 - 嘉奖记录与六维优势维度是否匹配 2. **根因分析**(必含): 对每个薄弱维度给出根因假设(基于数据,禁止臆造) 3. **优先级矩阵**(必须输出 2×2 真表格): 用「紧急重要」矩阵排序短板,**严格按下表模板填**(允许且只允许此表用 GFM 表格语法): | 紧急度 \ 重要性 | 高重要性 | 中低重要性 | |----------------|---------|-----------| | **高紧急度**(立即做) | {填短板A,引用数据} | {填短板B} | | **低紧急度**(计划做) | {填短板C} | {填短板D} | 表格下方用 100-150 字说明:为什么这么排、先补哪个、预期收益 4. **风险预警**: 基于综合数据,识别 1-2 个下一年度风险点 【口径要求】(强制) - 第三人称(${scopeWord}/姓名),严禁「您」「本人」「自己」 - 管理层视角,聚焦安全/合规/效能/队伍建设 - 诊断部分 1200-1500 字,自然语言段落(优先级矩阵表格除外) - 每个论断必须引用至少 1 个数据点 - 不重复各模块已写过的描述,只写综合洞察 - 不输出排名表/名单表,前五后五只能写进散文句子里 - 除「优先级矩阵」表格之外,**禁止在正文其他位置输出任何表格符号 |** 排版(必须严格遵守): 用下面四个小标题,每个标题单独成行,标题用 **加粗**: **跨模块关联** (至少 2 条关联,每条引用数据,纯文字段落) **根因分析** (对每个弱项给根因假设,纯文字段落) **优先级矩阵** (必须输出上面的 2×2 GFM 表格 + 100-150 字说明) **风险预警** (下一年 1-2 个风险点,纯文字段落) 硬性要求: 1. 除「优先级矩阵」表格外,正文禁止输出 | 或 # / ## / ### 2. 全文简体中文 3. 不要复述模块已写过的描述 4. 若开启思考:思考过程**只写对数据的解读、因果推理、关联验证**,严禁复述约束/排版/格式/口径/检查要求,严禁自检清单`; }