aps-agent/build-manual-docx.js

596 lines
33 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import fs from "node:fs";
import path from "node:path";
import {
AlignmentType,
BorderStyle,
Document,
Footer,
Header,
HeadingLevel,
ImportedXmlComponent,
Packer,
PageBreak,
PageNumber,
Paragraph,
ShadingType,
Table,
TableCell,
TableRow,
TextRun,
WidthType,
convertInchesToTwip,
} from "docx";
const outputPath = process.argv[2];
if (!outputPath) {
throw new Error("Usage: node build-manual-docx.js /absolute/path/output.docx");
}
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
const T = String.raw;
const palette = {
dark: "1F2A36",
primary: "2F4858",
light: "6B7A86",
border: "8C979F",
fill: "F2F5F7",
};
const fontBody = {
ascii: "Times New Roman",
hAnsi: "Times New Roman",
cs: "Times New Roman",
eastAsia: "SimSun",
};
const fontHead = {
ascii: "Times New Roman",
hAnsi: "Times New Roman",
cs: "Times New Roman",
eastAsia: "SimHei",
};
const fontCode = {
ascii: "Consolas",
hAnsi: "Consolas",
cs: "Consolas",
eastAsia: "SimSun",
};
const run = (text, options = {}) =>
new TextRun({ text, font: fontBody, size: 24, ...options });
const para = (children, options = {}) =>
new Paragraph({
spacing: { after: 120, line: 360 },
...options,
children: Array.isArray(children) ? children : [children],
});
// 正文:小四宋体,首行缩进两字符,1.5 倍行距
const body = (text) =>
para(run(text), {
indent: { firstLine: 480 },
alignment: AlignmentType.JUSTIFIED,
});
// 章标题:三号黑体;节标题:四号黑体;小节:小四黑体
const h1 = (text) =>
para(new TextRun({ text, font: fontHead, bold: true, size: 32, color: palette.dark }), {
heading: HeadingLevel.HEADING_1,
spacing: { before: 360, after: 240 },
});
const h2 = (text) =>
para(new TextRun({ text, font: fontHead, bold: true, size: 28, color: palette.dark }), {
heading: HeadingLevel.HEADING_2,
spacing: { before: 280, after: 160 },
});
const h3 = (text) =>
para(new TextRun({ text, font: fontHead, bold: true, size: 24, color: palette.dark }), {
heading: HeadingLevel.HEADING_3,
spacing: { before: 200, after: 120 },
});
const bullet = (text) =>
para(run(text), { bullet: { level: 0 }, indent: { left: 480 } });
const note = (text) =>
para(run(text, { italics: true, color: palette.light, size: 21 }), {
indent: { firstLine: 480 },
});
const codeBlock = (lines) =>
lines.map(
(line) =>
new Paragraph({
spacing: { after: 40, line: 280 },
indent: { left: 480 },
shading: { type: ShadingType.CLEAR, fill: palette.fill },
children: [
new TextRun({ text: line === "" ? " " : line, font: fontCode, size: 20 }),
],
})
);
// 学术三线表:顶线、栏目线、底线,无竖线与内部横线
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const table3 = (headers, rows, widths) => {
const total = widths.reduce((a, b) => a + b, 0);
const cellPara = (text, bold) =>
new Paragraph({
spacing: { after: 40, line: 300 },
children: [
new TextRun({ text, font: bold ? fontHead : fontBody, size: 21, bold: !!bold }),
],
});
const mkCell = (text, i, opts = {}) =>
new TableCell({
children: [cellPara(text, opts.bold)],
width: { size: widths[i], type: WidthType.DXA },
margins: { top: 80, bottom: 80, left: 100, right: 100 },
borders: opts.borders,
});
const headerCells = headers.map((text, i) =>
mkCell(text, i, {
bold: true,
borders: {
top: { style: BorderStyle.SINGLE, size: 12, color: palette.dark },
bottom: { style: BorderStyle.SINGLE, size: 6, color: palette.dark },
left: noBorder,
right: noBorder,
},
})
);
const bodyRows = rows.map((row, ri) => {
const isLast = ri === rows.length - 1;
return new TableRow({
children: row.map((text, i) =>
mkCell(String(text), i, {
borders: {
top: noBorder,
bottom: isLast
? { style: BorderStyle.SINGLE, size: 12, color: palette.dark }
: noBorder,
left: noBorder,
right: noBorder,
},
})
),
});
});
return new Table({
width: { size: total, type: WidthType.DXA },
columnWidths: widths,
rows: [new TableRow({ children: headerCells, tableHeader: true }), ...bodyRows],
});
};
const caption = (text) =>
para(new TextRun({ text, font: fontHead, size: 21, bold: true, color: palette.primary }), {
alignment: AlignmentType.CENTER,
spacing: { before: 120, after: 80 },
});
const xmlEscape = (value) =>
String(value)
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;");
const toc = (entries) => {
const cached = entries
.map(({ title: entryTitle, level, page }) => {
const indent = Math.max(0, level - 1) * 360;
return `<w:p>
<w:pPr>
<w:pStyle w:val="TOC${level}"/>
<w:tabs><w:tab w:val="right" w:leader="dot" w:pos="9000"/></w:tabs>
<w:ind w:left="${indent}"/>
</w:pPr>
<w:r><w:t>${xmlEscape(entryTitle)}</w:t></w:r>
<w:r><w:tab/></w:r>
<w:r><w:t>${xmlEscape(page)}</w:t></w:r>
</w:p>`;
})
.join("");
return ImportedXmlComponent.fromXmlString(`<w:sdt xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:sdtPr><w:alias w:val="目录"/></w:sdtPr>
<w:sdtContent>
<w:p>
<w:r>
<w:fldChar w:fldCharType="begin" w:dirty="true"/>
<w:instrText xml:space="preserve"> TOC \\o &quot;1-3&quot; \\h \\z \\u </w:instrText>
<w:fldChar w:fldCharType="separate"/>
</w:r>
</w:p>
${cached}
<w:p><w:r><w:fldChar w:fldCharType="end"/></w:r></w:p>
</w:sdtContent>
</w:sdt>`).root[0];
};
// ---------------------------------------------------------------------------
// 内容流:{t:'h1'|'h2'|'h3'|'p'|'b'|'note'|'code'|'tbl'|'cap'|'pagebreak'}
// ---------------------------------------------------------------------------
const flow = [];
const tocEntries = [];
let estPage = 4;
const H1 = (x) => {
tocEntries.push({ title: x, level: 1, page: estPage++ });
flow.push({ t: "h1", x });
};
const H2 = (x) => {
flow.push({ t: "h2", x });
};
const H3 = (x) => flow.push({ t: "h3", x });
const P = (x) => flow.push({ t: "p", x });
const B = (x) => flow.push({ t: "b", x });
const N = (x) => flow.push({ t: "note", x });
const CODE = (...lines) => flow.push({ t: "code", x: lines });
const TBL = (cap, headers, rows, widths) =>
flow.push({ t: "tbl", cap, h: headers, r: rows, w: widths });
const PAGEBREAK = () => flow.push({ t: "pagebreak" });
// ===================== 第一章 =====================
H1(T`第一章 系统概述`);
H2(T`1.1 系统定位`);
P(T`工业智核 APS 计划排产智能体是一套以“自然语言对话 + 可视化排产工作台”双交互范式为核心的高级计划与排产系统。系统左侧为对话通道,右侧为可视化工作台,用户以一句话发起业务动作,右侧视图随即刷新,形成“左说右动”的闭环交互。`);
P(T`系统核心业务链路为:销售订单 → MRP 分解(自制 / 采购 / 委外)→ 多策略试排(规则引擎 / CP-SAT / 遗传算法 / NSGA-II)→ 方案对比 → P2 确认发布 → MES 下发 → 报工回流。`);
H2(T`1.2 双排产模型`);
P(T`系统并行维护两条排产轨道,分别对应不同的生产组织形态,界面对当前所走链路予以明确标识,禁止混谈。`);
TBL(T`表 1-1 双排产模型对比`,
[T`轨道`, T`资源模型`, T`适用场景`],
[
[T`固定轨`, T`产线选线 + 工位占槽(RuleEngine / CP / GA / HYBRID)`, T`产线固定的常规离散制造`],
[T`柔性轨`, T`能力池 + 虚拟产线(PoolEngine)`, T`多品种小批量、设备可移动(如线束 / PDU 共线)`],
],
[1600, 4200, 3400]);
H2(T`1.3 安全与治理原则`);
P(T`系统一切写操作均经门禁确认卡执行:P2 级动作出具单次确认卡,P3 级外部副作用动作执行二次确认;执行前自动建立检查点,全程写入审计哈希链。试排仅产出 DRAFT 草稿版本,系统不自动发布;回滚执行前自动再次存档,构成可恢复的安全网。`);
// ===================== 第二章 =====================
H1(T`第二章 运行环境与系统启动`);
H2(T`2.1 交付形态`);
TBL(T`表 2-1 Web 与桌面双形态对比`,
[T`形态`, T`数据位置`, T`启动方式`, T`账号体系`],
[
[T`Web`, T`仓库 server/data/(或 APS_HOME)`, T`npm run dev:web,浏览器访问 http://localhost:5173`, T`JMS 企业账号登录`],
[T`桌面`, T`C:\\Users\\<用户>\\.aps(与开发环境隔离)`, T`npm run dev:desktop 或安装 release 目录下的 NSIS 安装包`, T`免账号,授权码激活`],
],
[1100, 2900, 3100, 2100]);
H2(T`2.2 依赖安装`);
P(T`首次部署需完成 Python 与 Node 双侧依赖安装,在仓库根目录依次执行:`);
CODE(
T`cd <aps-agent-repo>`,
T`python -m venv .venv`,
T`.venv\\Scripts\\activate`,
T`pip install -r requirements.txt`,
T`copy .env.example .env`,
T`npm run bootstrap`
);
H2(T`2.3 Web 端登录配置`);
P(T`Web 端联调用户登录前,须在 .env 文件中配置 JMS 地址与本地会话密钥:`);
CODE(
T`APS_AUTH_PROVIDER=jms`,
T`JMS_AUTH_BASE_URL=https://<your-jms-host>`,
T`JMS_AUTH_SESSION_SECRET=至少32个随机字符`,
T`APS_LICENSE_PROVIDER=mock`
);
P(T`如需限制为单一企业,可追加配置 JMS_AUTH_TENANT_CODE 与 JMS_AUTH_TENANT_NAME。JMS 当前无自助注册与自助找回密码接口,账号创建与密码重置由所属企业管理员处理。`);
H2(T`2.4 启动与打包`);
B(T`开发运行(Web):npm run dev:web,前端 http://localhost:5173,API http://localhost:8000;`);
B(T`开发运行(桌面):npm run dev:desktop,启动 Electron 窗口;`);
B(T`仅起后端:npm run dev:server,OpenAPI 文档位于 http://localhost:8000/docs;`);
B(T`打包 Web:npm run build:web,产物为 apps/web/dist 静态站点;`);
B(T`打包桌面:npm run build:desktop,产物为 apps/desktop/release 下的安装包。`);
// ===================== 第三章 =====================
H1(T`第三章 登录授权与界面导览`);
H2(T`3.1 登录与授权`);
P(T`Web 端:打开页面后选择所属企业(企业列表自 JMS 读取),输入真实账号密码登录。桌面端:输入授权码激活,开发演示授权码为 APS-HOUR-DEMO、APS-DAY-DEMO、APS-WEEK-DEMO、APS-MONTH-DEMO、APS-YEAR-DEMO、APS-PERP-DEMO。`);
H2(T`3.2 界面布局`);
P(T`主界面由四区构成:顶部为中文菜单栏(按功能开关过滤);左侧栏承载项目 / 任务与订单、主数据等快捷入口;中部为对话区与工作台;右侧为工具导轨,依次提供多智能体编排、DXF 图纸、柔性工作台、知识库、治理台等工具入口。`);
P(T`对话流中内嵌方案卡、确认卡、知识引用、报告等富交互块,可直接点按操作。右侧视口包括:固定 / 柔性甘特、负荷、交期、分桶计划、利用率、对比表、KPI 仪表盘、产能池与时间线。`);
H2(T`3.3 快捷交互`);
P(T`Ctrl+K 唤起命令面板,可快速跳转任意面板与视图,条目随功能开关配置过滤;当前展开的工具被禁用时面板自动隐藏。`);
// ===================== 第四章 =====================
H1(T`第四章 数据准备`);
H2(T`4.1 工程目录导入`);
P(T`于左侧栏选择“新建任务 / 新建项目”,将工程目录指向数据文件夹(内含订单、物料、BOM、工艺、设备、班历等 Excel/CSV 文件),随后在对话框输入“分析一下数据文件”。系统执行项目深度分析,输出订单、物料、工艺、设备、BOM 计数及结构化诊断,并自动导入排产世界。诊断按“有效 / 阻断(blocking)/ 跳过”三档统计,阻断项定位至具体文件与行号。`);
H2(T`4.2 Excel/CSV 导入管线`);
P(T`主数据面板与订单面板均设“导入”入口。导入遵循“表头映射 → 预览校验 → P2 确认落库”三段式管线,支持订单、物料、BOM、工艺路线、设备、模具、工序、区域八类对象。`);
H2(T`4.3 DXF 工程图纸识别`);
P(T`右侧导轨“图纸”入口支持上传 DXF、填写本地路径或扫描工程目录。系统自动解析标题栏(图号、名称、比例、版本,逐字段标注置信度与来源),生成支持缩放、平移与图层开关的 SVG 预览;物料候选经人工核对后生成 P2 确认卡,批准后方写入主数据。`);
N(T`图纸未承载的字段(材质、工时、外协属性、BOM 子件数量等)一律显式标记 missing,绝不编造,必须经人工确认。`);
H2(T`4.4 演示数据装载`);
P(T`柔性排产演示可装载康尼真实单数据,在仓库根目录执行下列脚本,或以口令“导入完整生产路线”替换演示数据:`);
CODE(T`.venv\\Scripts\\python.exe scripts\\load_kangni.py`);
P(T`另提供纯内存、确定性结果的一键全流程模拟脚本(无需启动服务),适用于演示与验收:`);
CODE(T`.venv\\Scripts\\python.exe scripts\\flex_simulation.py`);
H2(T`4.5 主数据录入顺序`);
P(T`主数据之间存在严格依赖关系,录入必须遵循如下顺序:先工厂 / 产线,再物料,再 BOM,再工序,再工艺路线,再产线-产品绑定,最后录入订单。任一环节缺失,后续排产必然失败或结果“找不到依据”。`);
// ===================== 第五章 =====================
H1(T`第五章 主数据维护`);
P(T`侧栏“主数据”面板按页签组织,全部编辑动作生成 P2 确认卡,经批准后执行;执行前自动建立检查点并写入审计链。主数据修改仅影响其后生成的新排产版本,历史版本不回写。`);
TBL(T`表 5-1 主数据页签与能力`,
[T`页签`, T`维护内容`, T`权力等级`],
[
[T`资源`, T`工厂→车间→产线→工位→设备资源树;产线编辑 / 停启用(停用产线不进入新排产)`, T`编辑 P2`],
[T`物料与 BOM`, T`库存 / 在途 / 安全库存 / 前置期;BOM 明细用量与关键料`, T`P2`],
[T`日历与维保`, T`班次 / 日历只读;维保窗口新增 / 取消,联动设备避让`, T`P2`],
[T`工艺`, T`工艺步骤工时、外协标记`, T`P2`],
[T`换型`, T`产品族×产品族换型时间矩阵(口令“查看换型矩阵”)`, T`P2`],
[T`柔性资源`, T`设备虚拟档案(能力集合、可移动性、移动耗时、当前位置、工装适配)与能力池摘要`, T`只读 + Excel 导入`],
],
[1500, 5700, 2000]);
// ===================== 第六章 =====================
H1(T`第六章 订单管理`);
H2(T`6.1 销售订单`);
P(T`左侧栏“订单”面板提供订单列表与明细维护,新增、编辑、取消、完成均走 P2 确认卡。状态为 CANCELLED 或 COMPLETED 的订单不进入新的排产版本;仅 APPROVED 状态订单参与排产。`);
H2(T`6.2 订单池与审核`);
P(T`订单池以泳道看板组织,支持提交、批准、驳回与批量批准。对应口令:“订单池”“提交审核”“批量批准”。`);
H2(T`6.3 预测订单`);
P(T`预测订单台账默认不进入正式排产;可通过“预测纳入试排”执行 what-if 分析,确认后以“预测转正”转为正式 APPROVED 销售订单。相关口令:“查看预测”“新建预测”“预测纳入试排”“预测转正”。`);
H2(T`6.4 紧急插单`);
P(T`订单面板设插单向导,亦可使用口令“插单快评”查看影响评估,再以“采用插单”应用。固定轨执行 rush.evaluate / rush.apply 流程,柔性轨走 flex.rush;当局部修复扰动超阈值时自动升级为全量重排并出具 P2 确认卡。`);
H2(T`6.5 订单追溯`);
P(T`任一订单支持一键追溯完整单据链:订单本体(客户、交期、产品、数量、状态)→ 主数据依据(BOM、工艺路线、库存)→ MRP 分解结果 → 排产版本与工单 → 缺失项(无 BOM / 无路线 / 未绑定产线 / 库存不足)。柔性轨道另沿 FO→BOM→虚拟产线→工单→设备负荷链路追溯。`);
// ===================== 第七章 =====================
H1(T`第七章 物料需求分解(MRP)`);
P(T`入口为订单面板“分解建议”“分解此单”,或对话口令“分解订单 SO-001”“执行MRP分解”“生成采购建议”。分解逻辑全部源自主数据,修改主数据即改变分解结果。`);
TBL(T`表 7-1 MRP 分解三分支`,
[T`分支`, T`依据`, T`产出`],
[
[T`自制`, T`订单明细成品 + 工艺路线`, T`自制建议,交由排产引擎生成生产订单与工单`],
[T`采购`, T`BOM 净需求 = 毛需求 − 库存 − 在途 − 已下达供应`, T`采购建议 DRAFT:数量与建议下单日(按前置期倒排)`],
[T`委外`, T`工艺步骤外协标记`, T`委外建议 DRAFT:产品、工序、数量、需求日`],
],
[1200, 3800, 4200]);
B(T`分解为 P1 级动作:仅写建议草稿,幂等替换,可重复执行;`);
B(T`下达经口令“下达MRP建议单”触发 P2 确认卡,批准后 DRAFT 转 RELEASED,并自动补全预计到料日,供齐套检查引用;`);
B(T`多层 BOM 完整展开:半成品缺少工艺路线时不伪装为采购建议,缺工艺条目予以明示。`);
// ===================== 第八章 =====================
H1(T`第八章 排产操作`);
H2(T`8.1 固定轨试排`);
P(T`口令示例:“试排一版交期优先”“按产能均衡排一下”“换型最小化试排”“战役合并试排”。试排产出 DRAFT 版本与冲突清单,右侧甘特 / 负荷 / 交期视图即时刷新。`);
B(T`策略模板:DELIVERY_FIRST(交期优先)、FIFO、CAPACITY_BALANCE(产能均衡)、CHANGEOVER_MIN(换型最小化)、COST_FIRST、COMPREHENSIVE、CAMPAIGN(批次合并);`);
B(T`求解引擎:RULE、CP-SAT(工序级 + Cumulative 聚合容量)、GA、NSGA-II(三目标 Pareto)、HYBRID;`);
B(T`滚动时域:realtime60m(1 分钟粒度)、short2h、mid2d、long7d 多级窗口。`);
H2(T`8.2 柔性轨排产`);
P(T`口令“跑一版柔性排产”或经右侧“柔性工作台”操作。系统按订单工艺路线逐工序自能力池动态取用设备,临时拼装虚拟产线(计入模具换型与设备移动耗时),用完即散、设备归还池中。柔性甘特支持“虚拟线预演”,并提供正排 / 倒排 / 瓶颈锚三模式对比。`);
TBL(T`表 8-1 柔性排产接口参数(POST /api/flex/schedule)`,
[T`参数`, T`取值`, T`含义`],
[
[T`sortMode`, T`ASC / DESC / BOTTLENECK`, T`正排 / 倒排 / 瓶颈锚(默认瓶颈锚)`],
[T`orderIds`, T`订单 id 数组`, T`仅排指定订单,缺省排全部`],
],
[1800, 3200, 4200]);
H2(T`8.3 方案对比与采用`);
P(T`口令“对比几种策略”触发沙盒内多策略并行试排,返回方案卡组(KPI、差异、Pareto 前沿与偏好推荐)。点击“采用此方案”将所选策略落入主干草稿。须注意:对比不等于发布,采用前主干世界保持不变。`);
H2(T`8.4 发布、存档与回滚`);
TBL(T`表 8-2 版本生命周期操作`,
[T`操作`, T`口令`, T`权级`, T`语义`],
[
[T`发布`, T`发布这个版本`, T`P2`, T`DRAFT→PUBLISHED;仅完整 DRAFT 可发布,执行前自动建档`],
[T`存档`, T`存个档`, T`P1`, T`对话上下文与世界状态成对快照`],
[T`回滚`, T`回滚 / 点时间线检查点`, T`P2`, T`回滚前自动再次存档(安全网)`],
[T`重置`, T`清空数据重新初始化`, T`P2`, T`重播演示种子(电子装配演示工厂)`],
],
[1200, 2700, 1000, 4300]);
H2(T`8.5 工单拖拽调程`);
P(T`固定与柔性甘特均支持直接拖动工单:15 分钟吸附、拖动过程实时校验、松手生成 P2 确认卡,批准后形成新版本。`);
H2(T`8.6 冲突处理`);
P(T`冲突中心按冲突类型给出修复建议:安全修复为 P1 一键应用;涉及重排的出具 P2 确认卡。硬阻断(缺料、无可用设备等)展示聚合计数、具体对象、证据与修复入口,绝不显示绿色“排产完成”。`);
// ===================== 第九章 =====================
H1(T`第九章 计划层功能`);
P(T`计划层(Advanced Planning)在排产前提供中长期粗能力评估与决策支持,各项能力及对应口令如下表。`);
TBL(T`表 9-1 计划层能力一览`,
[T`能力`, T`口令`, T`功能说明`],
[
[T`时间分桶计划`, T`时间分桶 / 混合分桶`, T`近 7 日 / 中 4 周 / 远月混合时段建模`],
[T`粗能力评估`, T`有限产能粗评 / 无限产能粗评 / 粗能力对比`, T`FINITE / INFINITE 双模式对照`],
[T`可行性分析`, T`可行性分析 / 能否按期`, T`累计粗能力对交期需求,定位缺口桶与订单 slack`],
[T`库存投影`, T`库存投影`, T`期初 + 在途 + 毛需求 + 到货的日 / 周库存曲线,断料与安全库存告警`],
[T`产能平衡`, T`削峰 / 产能平衡`, T`超载桶向空档挪动建议,前后负荷对照`],
[T`产供决策`, T`产供决策 / 加班建议`, T`残留缺口沿加班→扩线→外协给出结构化建议`],
[T`交期承诺`, T`对话询价 / 柔性工作台询价`, T`询单插入模拟,输出乐观 / 预计 / 悲观三区间与资源缺口`],
[T`瓶颈产能`, T`视口“产能池”`, T`日产能 = 瓶颈工序可用工时 ÷ 单件工时;设备停机 / 调区即时重算,含 4h / 1d / 1w 预测与 85 / 95 预警`],
],
[1900, 2700, 4600]);
// ===================== 第十章 =====================
H1(T`第十章 动态调度与异常重排`);
P(T`系统对生产异常实施 L1–L4 分级重排机制,扰动逐级扩大,确认门禁随级别提升。`);
TBL(T`表 10-1 分级重排机制`,
[T`级别`, T`典型触发`, T`重排行为`],
[
[T`L1`, T`设备故障(优先)`, T`池内备机换机,最小扰动`],
[T`L2`, T`短窗变化`, T`窗内重排,P2 确认`],
[T`L3`, T`日级变化`, T`日窗重排,P2 确认`],
[T`L4`, T`重大异常`, T`全局重排,P2 确认`],
],
[1100, 2900, 5200]);
P(T`典型场景:可移动设备故障一台时,能力池存在冗余,交期几乎不受影响;单一瓶颈设备(如唯一激光焊接机)故障时,系统立即报告“无可用设备”,并提示启用备机、外协或重新议定交期。WMS 缺料事件触发“缺料 → 重排 → 确认 → MES 下发 → 回执”全链路闭环。`);
// ===================== 第十一章 =====================
H1(T`第十一章 结果视图与报表`);
TBL(T`表 11-1 结果视口一览`,
[T`视口`, T`内容`],
[
[T`甘特`, T`固定 / 柔性双轨,工单拖拽与冲突高亮`],
[T`负荷 / 利用率`, T`产线与设备负荷、柔性设备 7 日热力、班组并发触顶提示`],
[T`交期看板`, T`订单准时 / 延误一览`],
[T`对比表`, T`固定策略与柔性三模式 KPI 矩阵,最优格高亮并标注相对 delta`],
[T`KPI 仪表盘`, T`利用率 / 准时率 / 在制 / 延误汇总(口令“打开仪表盘”)`],
[T`产能池`, T`池负载率、瓶颈色标、短中长期预测、设备调配 What-if`],
[T`时间线`, T`检查点列表,点击即回滚`],
],
[2100, 7100]);
P(T`报表经口令“生成日报”“版本对比”生成,数字取自冻结快照模板,不经大模型改写。审计支持哈希链校验、Merkle 树、断链检测与归档加密导出。`);
// ===================== 第十二章 =====================
H1(T`第十二章 MES 下发与执行闭环`);
P(T`已发布版本经柔性工作台“MES 执行”或下发口令进入下发流程:P3 二次确认后,Mock MES 幂等下发。报工回流经 mes.report(P1)回报进度与完工,订单整单完工状态联动推进。订单页阶段状态取自持久化的闭环问题与 MES 事实,刷新后不退回静态“待确认”。`);
// ===================== 第十三章 =====================
H1(T`第十三章 多智能体编排`);
P(T`右侧栏“多智能体编排”面板支持按需组建智能体团队并自动分发执行,操作步骤如下。`);
B(T`创建智能体:输入名称并选择角色(数据解析 / MRP 分解 / 柔性排产 / 报告等);`);
B(T`挂载 Goal:填写标题并选用模板(如“解析→分解→下达→排产→报告”五任务模板),看门狗随即开始监控;`);
B(T`分发执行:看门狗按能力边界将任务分发给空闲智能体,无合适智能体时自动新建;`);
B(T`消息总线:实时显示任务接力过程,亦可手动向任意智能体发消息,对方回报自身状态与能力边界;`);
B(T`看门狗机制:RUNNING 超期(默认 45 秒,可一键调至 10 秒用于演示)触发收回重排,重试耗尽升级 ATTENTION;全部任务完成自动结案 ACHIEVED;任务失败诚实落 FAILED 并升级,不伪报成功。`);
P(T`重复演示前,先在看门狗区执行一次“恢复”(或调用 POST /api/mesh/reset)。`);
// ===================== 第十四章 =====================
H1(T`第十四章 对话口令速查`);
TBL(T`表 14-1 自然语言口令速查表`,
[T`类别`, T`口令`],
[
[T`数据`, T`分析一下数据文件 / 导入完整生产路线 / 清空数据重新初始化`],
[T`MRP`, T`执行MRP分解 / 分解订单 SO-001 / 生成采购建议 / 下达MRP建议单`],
[T`排产`, T`试排一版交期优先 / 按产能均衡排一下 / 换型最小化试排 / 战役合并试排 / 跑一版柔性排产 / 三模式对比`],
[T`方案`, T`对比几种策略 / 采用此方案 / 发布这个版本`],
[T`插单`, T`插单快评 / 采用插单`],
[T`计划层`, T`时间分桶 / 可行性分析 / 能否按期 / 库存投影 / 削峰 / 产供决策 / 加班建议`],
[T`订单`, T`订单池 / 提交审核 / 批量批准 / 查看预测 / 新建预测 / 预测纳入试排 / 预测转正`],
[T`主数据`, T`查看换型矩阵 / 提高VIP权重到10`],
[T`知识`, T`换线有什么规定 / @知识:标题`],
[T`报告`, T`生成日报 / 版本对比 / 打开仪表盘`],
[T`存档`, T`存个档 / 回滚`],
],
[1500, 7700]);
N(T`意图管线为“正则快路 → LLM JSON”两级结构;LLM 超时或非法输出自动降级为正则意图,知识未命中时系统明确告知,不编造 SOP。`);
// ===================== 第十五章 =====================
H1(T`第十五章 治理与安全`);
H2(T`15.1 门禁分级`);
P(T`门禁按 P0–P3 四级划分:P0 只读;P1 写草稿直通;P2 单次确认卡;P3 外部副作用二次确认(当前用于 MES 下发)。审批角色可按动作分级配置。所有写操作的唯一执行通道为 /api/actions/confirm,不存在绕过确认卡的直接执行接口。`);
H2(T`15.2 审计与约束`);
P(T`每次执行均写入审计链,哈希链可校验,支持断链检测(治理台显示断链徽标)、Merkle 树、归档加密与导出。约束配置中心提供 13 类约束目录,可配置启停与硬 / 软属性,硬约束拦截发布。行业规则包支持 SOP 向排产约束的编译,预览后 P2 应用。`);
H2(T`15.3 功能开关`);
P(T`配置文件 server/data/features.json 控制 12 个功能键(visual / orders / master / flex / timeline / drawing / mesh / knowledge / skills / gov / regen / settings)的界面可见性。修改文件后刷新浏览器即生效,无需重启;文件缺失或损坏时全部默认开启(fail-open)。功能开关仅隐藏入口,不替代 Harness 门禁。`);
// ===================== 第十六章 =====================
H1(T`第十六章 常见问题与验收`);
H2(T`16.1 常见问题`);
TBL(T`表 16-1 常见问题与处置`,
[T`问题`, T`处置方法`],
[
[T`排产失败提示缺失项`, T`按第 4.5 节顺序检查主数据,并以订单“追溯”定位具体缺失`],
[T`排产被硬阻断(缺料 / 无设备)`, T`查看冲突中心证据:缺料先下达采购建议;无设备查停用、维保与故障状态`],
[T`试排后想撤回`, T`时间线点击上一检查点回滚(P2),回滚前系统自动再存档`],
[T`三模式对比结果一致`, T`产能宽裕时三模式趋同,压缩交期制造压力后分化`],
[T`删除前端“项目”后数据仍在`, T`项目属前端工作区,排产世界在后端,清世界用“清空数据重新初始化”`],
[T`Web 无法登录`, T`检查 .env 的 JMS 配置与会话密钥;账号问题联系企业管理员`],
[T`查看接口定义`, T`启动后端后访问 http://localhost:8000/docs`],
],
[3100, 6100]);
H2(T`16.2 验收快速路径`);
B(T`口令“分析一下数据文件”,工程目录导入成功;`);
B(T`口令“执行MRP分解”,采购建议出现;口令“下达MRP建议单”,批准后转 RELEASED;`);
B(T`口令“对比几种策略”,方案卡出现后采用,右侧甘特刷新;`);
B(T`口令“存个档”,点击检查点回滚并完成 P2 确认;`);
B(T`口令“跑一版柔性排产”,虚拟产线与工单出现;`);
B(T`口令“发布这个版本”,P2 批准后版本转 PUBLISHED;`);
B(T`回归测试:pytest tests/golden -q(当前基线 1071 项通过,1 项既有失败 test_preference_features,与业务无关)。`);
// ===================== 文档装配 =====================
const renderItem = (item) => {
switch (item.t) {
case "h1": return [h1(item.x)];
case "h2": return [h2(item.x)];
case "h3": return [h3(item.x)];
case "p": return [body(item.x)];
case "b": return [bullet(item.x)];
case "note": return [note(item.x)];
case "code": return codeBlock(item.x);
case "tbl": return [caption(item.cap), table3(item.h, item.r, item.w), para(run(""))];
case "pagebreak": return [new Paragraph({ children: [new PageBreak()] })];
default: return [];
}
};
const coverChildren = [
para(run(T`工业智核 APS 计划排产智能体`, { bold: true, size: 48, color: palette.dark, font: fontHead }), {
alignment: AlignmentType.CENTER,
spacing: { before: 3600, after: 240 },
}),
para(run(T`操 作 手 册`, { bold: true, size: 44, color: palette.dark, font: fontHead }), {
alignment: AlignmentType.CENTER,
spacing: { after: 1200 },
}),
para(run(T`—— 基于系统当前能力的标准作业规程 ——`, { size: 24, color: palette.light }), {
alignment: AlignmentType.CENTER,
spacing: { after: 2400 },
}),
para(run(T`文档版本:V1.0`, { size: 24 }), { alignment: AlignmentType.CENTER, spacing: { after: 160 } }),
para(run(T`编制日期:2026 年 9 月 1 日`, { size: 24 }), { alignment: AlignmentType.CENTER, spacing: { after: 160 } }),
para(run(T`适用对象:计划员、实施顾问、演示与验收人员`, { size: 24 }), { alignment: AlignmentType.CENTER }),
new Paragraph({ children: [new PageBreak()] }),
];
const prefaceChildren = [
h1(T`前 言`),
body(T`本手册依据工业智核 APS 计划排产智能体当前代码能力编制,内容对齐产品功能清单(docs/product/features.md)、核心业务旅程(docs/product/journeys.md)及截至 2026 年 8 月 26 日的变更记录(含 round-65 至 round-67、DXF-01 图纸识别、FF-01 功能开关)。`),
body(T`手册共十六章:第一至三章介绍系统定位、运行环境与界面;第四至八章按业务顺序阐述数据准备、主数据、订单、MRP 与排产操作;第九至十三章阐述计划层、动态调度、结果视图、MES 闭环与多智能体编排;第十四至十六章给出口令速查、治理安全与常见问题处置。`),
body(T`本手册仅描述当前代码真实路径。凡标注权级(P0–P3)的操作,其执行均受门禁确认卡约束,任何绕过确认的执行通道在系统中不存在。`),
new Paragraph({ children: [new PageBreak()] }),
para(run(T`目 录`, { bold: true, size: 32, color: palette.dark, font: fontHead }), {
alignment: AlignmentType.CENTER,
spacing: { before: 240, after: 240 },
}),
note(T`打开文档后,右键目录选择“更新域”即可刷新页码。`),
toc(tocEntries),
new Paragraph({ children: [new PageBreak()] }),
];
const bodyChildren = [];
for (const item of flow) {
bodyChildren.push(...renderItem(item));
}
const doc = new Document({
features: { updateFields: true },
sections: [
{
properties: {
page: {
size: { width: 11906, height: 16838 },
margin: { top: 1440, bottom: 1440, left: 1560, right: 1560 },
},
},
headers: {
default: new Header({
children: [
para(run(T`工业智核 APS 计划排产智能体 · 操作手册`, { size: 18, color: palette.light }), {
alignment: AlignmentType.CENTER,
spacing: { after: 0 },
}),
],
}),
},
footers: {
default: new Footer({
children: [
para(new TextRun({ children: [PageNumber.CURRENT], size: 18 }), {
alignment: AlignmentType.CENTER,
spacing: { after: 0 },
}),
],
}),
},
children: [...coverChildren, ...prefaceChildren, ...bodyChildren],
},
],
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync(outputPath, buffer);