Update project and configurations

This commit is contained in:
Zou-Seay
2026-06-11 16:28:00 +08:00
parent 12d3922091
commit a29a91867d
237 changed files with 164880 additions and 90 deletions

View File

@@ -0,0 +1,469 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>工业 AI 交互画布:流程总览</title>
<style>
:root {
--bg: #f0f2f5;
--card: #fff;
--ink: #111827;
--muted: #667085;
--line: #d8dde6;
--soft: #f1f4f8;
--blue: #2563eb;
--green: #059669;
--amber: #d97706;
--violet: #7c3aed;
--dark: #111827;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: var(--bg); color: var(--ink); font-family: "PingFang SC", "Microsoft YaHei", Arial, sans-serif; font-size: 14px; }
button { font: inherit; cursor: pointer; border: none; }
.page { max-width: 1500px; margin: 0 auto; padding: 24px 24px 60px; }
/* Hero */
.hero { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
h1 { font-size: 22px; font-weight: 800; }
.subtitle { color: var(--muted); font-size: 13px; margin-top: 5px; line-height: 1.6; }
/* Layout */
.main-grid { display: grid; grid-template-columns: 252px minmax(0, 1fr); gap: 16px; align-items: start; }
/* Left panel */
.l-panel { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 18px; }
.panel-title { font-size: 14px; font-weight: 700; margin-bottom: 12px; color: var(--ink); }
.stack { display: grid; gap: 8px; }
.layer { display: grid; grid-template-columns: 30px 1fr; gap: 10px; align-items: center; border: 1px solid var(--line); border-left: 4px solid var(--dark); border-radius: 7px; padding: 9px 12px; }
.layer[data-c="blue"] { border-left-color: #2563eb; }
.layer[data-c="cyan"] { border-left-color: #0891b2; }
.layer[data-c="violet"] { border-left-color: #7c3aed; }
.layer[data-c="green"] { border-left-color: #059669; }
.layer[data-c="amber"] { border-left-color: #d97706; }
.layer[data-c="dark"] { border-left-color: #111827; }
.ln { display: flex; height: 24px; width: 24px; align-items: center; justify-content: center; border-radius: 50%; background: var(--soft); font-weight: 800; font-size: 12px; color: #334155; }
.lname { font-size: 12px; font-weight: 700; }
.ldesc { font-size: 11px; color: var(--muted); margin-top: 2px; line-height: 1.3; }
/* Right: flow panel */
.r-panel { background: var(--card); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
/* Controls bar */
.controls { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 20px; border-bottom: 1px solid var(--line); background: #fafbfc; flex-wrap: wrap; }
.tg { display: inline-flex; gap: 3px; padding: 3px; border: 1px solid var(--line); border-radius: 9px; background: var(--card); }
.tbtn { background: transparent; color: #475467; padding: 8px 14px; border-radius: 6px; font-size: 13px; font-weight: 600; transition: all .15s; white-space: nowrap; }
.tbtn.active-flow { background: var(--dark); color: #fff; }
.tbtn.active-ux { background: #4f46e5; color: #fff; }
.tbtn.active-tech { background: #0d9488; color: #fff; }
/* View indicator bar */
.view-bar { height: 4px; width: 100%; }
.view-bar.ux { background: linear-gradient(90deg, #818cf8, #a78bfa); }
.view-bar.tech { background: linear-gradient(90deg, #0d9488, #059669); }
/* Flow body */
.flow-body { padding: 22px 26px 30px; overflow-x: auto; }
.flow-meta { margin-bottom: 18px; }
.flow-meta h2 { font-size: 17px; font-weight: 800; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.flow-meta p { font-size: 13px; color: var(--muted); margin-top: 6px; line-height: 1.6; }
.badge { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600; padding: 2px 10px; border-radius: 20px; }
.badge-a { background: #dbeafe; color: #1d4ed8; }
.badge-b { background: #d1fae5; color: #065f46; }
.badge-ux { background: #ede9fe; color: #4c1d95; }
.badge-tech { background: #ccfbf1; color: #134e4a; }
/* Legend */
.legend { display: flex; gap: 10px; flex-wrap: wrap; padding: 9px 14px; background: var(--soft); border-radius: 8px; margin-bottom: 18px; }
.lg { display: flex; align-items: center; gap: 5px; font-size: 11px; color: #475467; }
.lg-dot { width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; }
/* ═══ FLOWCHART BASE ═══ */
.fc { display: flex; flex-direction: column; align-items: center; min-width: 580px; }
/* Nodes */
.nd { border-radius: 8px; padding: 9px 16px; text-align: center; border: 1.5px solid var(--line); background: #fff; }
.nd .nt { font-size: 13px; font-weight: 700; line-height: 1.4; }
.nd .ns { font-size: 11px; color: var(--muted); margin-top: 3px; line-height: 1.35; }
/* Node type variants */
.nd-start { background: var(--dark); border: none; border-radius: 22px; min-width: 180px; }
.nd-start .nt { color: #fff; font-size: 14px; }
.nd-start .ns { color: rgba(255,255,255,0.6); }
/* UX node types */
.nd-show { background: #f5f3ff; border-color: #a78bfa; } /* 画面显示 */
.nd-show .nt { color: #4c1d95; }
.nd-do { background: #ecfdf5; border-color: #34d399; } /* 用户操作 */
.nd-do .nt { color: #065f46; }
.nd-end { background: #f0fdf4; border-color: #86efac; } /* 结果/结束 */
.nd-end .nt { color: #14532d; }
.nd-trig { background: #fff7ed; border-color: #fdba74; } /* 触发条件/场景 */
.nd-trig .nt { color: #9a3412; }
.nd-tab { background: #f0f9ff; border-color: #7dd3fc; border-style: dashed; }
.nd-tab .nt { color: #0c4a6e; }
/* Tech node types */
.nd-route { background: #fffbeb; border-color: #fbbf24; }
.nd-route .nt { color: #78350f; }
.nd-bert { background: #f5f3ff; border-color: #c4b5fd; }
.nd-bert .nt { color: #5b21b6; }
.nd-llm { background: #fff7ed; border-color: #fdba74; }
.nd-llm .nt { color: #9a3412; }
.nd-tool { background: #dbeafe; border-color: #60a5fa; }
.nd-tool .nt { color: #1e3a8a; }
.nd-kb { background: #ecfdf5; border-color: #6ee7b7; }
.nd-kb .nt { color: #064e3b; }
.nd-art { background: #faf5ff; border-color: #c4b5fd; border-style: dashed; }
.nd-art .nt { color: #5b21b6; }
.nd-snap { background: #f0fdf4; border-color: #86efac; border-style: dashed; }
.nd-snap .nt { color: #14532d; }
.nd-minor { background: var(--soft); border-color: var(--line); }
.nd-minor .nt { color: #475467; }
/* Tags inside nodes */
.tag { display: inline-block; font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 3px; margin: 3px 2px 0; }
.t-bert { background: #ede9fe; color: #5b21b6; }
.t-llm { background: #fff7ed; color: #9a3412; }
.t-rule { background: #dcfce7; color: #166534; }
.t-tool { background: #dbeafe; color: #1e40af; }
.t-art { background: #faf5ff; color: #5b21b6; }
/* Connectors */
.dn { width: 2px; background: #c9d0db; height: 20px; flex-shrink: 0; }
.dn.tall { height: 30px; }
.arr { width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 7px solid #b0b9c8; flex-shrink: 0; }
/* Branch spread link */
.slink { position: relative; width: 100%; height: 24px; flex-shrink: 0; }
/* Branch columns */
.brow { display: flex; width: 100%; align-items: flex-start; }
.bcol { display: flex; flex-direction: column; align-items: center; flex: 1; padding: 0 7px; min-width: 0; }
/* Branch labels */
.blbl { padding: 4px 11px; border-radius: 20px; font-size: 11px; font-weight: 700; white-space: nowrap; }
.bl-b { background: #dbeafe; color: #1d4ed8; border: 1px solid #93c5fd; }
.bl-g { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.bl-a { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
.bl-v { background: #ede9fe; color: #4c1d95; border: 1px solid #a78bfa; }
.bl-gr { background: var(--soft); color: #475467; border: 1px solid var(--line); }
.bl-hit { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.bl-miss { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
/* Notes */
.fnote { font-size: 11px; color: #64748b; margin-top: 4px; text-align: center; line-height: 1.4; }
.fopt { font-size: 11px; color: #6d28d9; margin-top: 4px; text-align: center; font-style: italic; }
.fwarn { font-size: 11px; color: #b91c1c; margin-top: 4px; text-align: center; }
/* Divider */
.fc-sep { width: 100%; border: none; border-top: 1px dashed var(--line); margin: 4px 0; }
@media (max-width: 1080px) { .main-grid { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<main class="page">
<header class="hero">
<div>
<h1>工业 AI 交互画布 · 操作流程与技术链路</h1>
<p class="subtitle">选择视角,分别查看"用户界面交互路径"或"背后技术判断逻辑"。语音输入经过四阶段前置拦截后再进入 BERT NLU。</p>
</div>
</header>
<section class="main-grid">
<!-- Left: Architecture Layers -->
<aside class="l-panel">
<div class="panel-title">主链路层级</div>
<div class="stack">
<div class="layer" data-c="blue">
<div class="ln">1</div>
<div><div class="lname">输入层</div><div class="ldesc">文字 / 语音(ASR) / 点击</div></div>
</div>
<div class="layer" data-c="cyan">
<div class="ln">2</div>
<div><div class="lname">前置拦截层</div><div class="ldesc">停止词 → UI语音点击 → Slot填写 → BERT</div></div>
</div>
<div class="layer" data-c="violet">
<div class="ln">3</div>
<div><div class="lname">路由层</div><div class="ldesc">decision: execute / clarify / route_to_cloud</div></div>
</div>
<div class="layer" data-c="green">
<div class="ln">4</div>
<div><div class="lname">编排层</div><div class="ldesc">工具选择 / 知识检索 / Artifact 生成</div></div>
</div>
<div class="layer" data-c="amber">
<div class="ln">5</div>
<div><div class="lname">执行层</div><div class="ldesc">PLC / HMI / 知识库调用</div></div>
</div>
<div class="layer" data-c="dark">
<div class="ln">6</div>
<div><div class="lname">画布层</div><div class="ldesc">渲染 Artifact + 状态快照</div></div>
</div>
</div>
</aside>
<!-- Right: Flow Panel -->
<section class="r-panel">
<div class="controls">
<div class="tg" id="viewTabs">
<button class="tbtn active-ux" data-view="ux">👁 交互流程</button>
<button class="tbtn" data-view="tech">⚙️ 技术流程</button>
</div>
</div>
<div class="view-bar ux" id="viewBar"></div>
<div class="flow-body" id="flowBody"></div>
</section>
</section>
</main>
<script>
// ─── State ────────────────────────────────────────────────────────────────
const S = { view: 'ux' };
// ─── Helpers ──────────────────────────────────────────────────────────────
const dn = (h = 20) => `<div class="dn" style="height:${h}px"></div>`;
const arr = () => `<div class="arr"></div>`;
const da = (h = 20) => dn(h) + arr();
function nd(cls, title, sub = '', tags = []) {
const ts = tags.map(t => `<span class="tag t-${t}">${t.toUpperCase()}</span>`).join('');
return `<div class="nd ${cls}"><div class="nt">${title}</div>${sub ? `<div class="ns">${sub}</div>` : ''}${ts ? `<div>${ts}</div>` : ''}</div>`;
}
function blbl(text, cls) {
return `<div class="blbl ${cls}">${text}</div>`;
}
// Horizontal branch spread link: n = 2 or 3
function slink(n) {
if (n === 3) {
return `<div class="slink">
<div style="position:absolute;top:0;left:16.67%;right:16.67%;height:2px;background:#c9d0db;"></div>
<div style="position:absolute;top:0;left:calc(16.67% - 1px);width:2px;height:24px;background:#c9d0db;"></div>
<div style="position:absolute;top:0;left:calc(50% - 1px);width:2px;height:24px;background:#c9d0db;"></div>
<div style="position:absolute;top:0;right:calc(16.67% - 1px);width:2px;height:24px;background:#c9d0db;"></div>
</div>`;
}
return `<div class="slink">
<div style="position:absolute;top:0;left:25%;right:25%;height:2px;background:#c9d0db;"></div>
<div style="position:absolute;top:0;left:calc(25% - 1px);width:2px;height:24px;background:#c9d0db;"></div>
<div style="position:absolute;top:0;right:calc(25% - 1px);width:2px;height:24px;background:#c9d0db;"></div>
</div>`;
}
// ─── Legends ─────────────────────────────────────────────────────────────
const legendUX = `<div class="legend">
<div class="lg"><div class="lg-dot" style="background:#f5f3ff;border:1px solid #a78bfa;"></div> 界面展示内容</div>
<div class="lg"><div class="lg-dot" style="background:#ecfdf5;border:1px solid #34d399;"></div> 用户可做的操作</div>
<div class="lg"><div class="lg-dot" style="background:#f0fdf4;border:1px solid #86efac;"></div> 最终结果/结束态</div>
<div class="lg"><div class="lg-dot" style="background:#f0f9ff;border:1px solid #7dd3fc;border-style:dashed;"></div> Tab 分页展示(不覆盖主卡)</div>
</div>`;
const legendTech = `<div class="legend">
<div class="lg"><div class="lg-dot" style="background:#fffbeb;border:1px solid #fbbf24;"></div> 路由判断节点</div>
<div class="lg"><div class="lg-dot" style="background:#f5f3ff;border:1px solid #c4b5fd;"></div> BERT / NLU</div>
<div class="lg"><div class="lg-dot" style="background:#fff7ed;border:1px solid #fdba74;"></div> LLM 推断</div>
<div class="lg"><div class="lg-dot" style="background:#dbeafe;border:1px solid #60a5fa;"></div> 工具调用</div>
<div class="lg"><div class="lg-dot" style="background:#ecfdf5;border:1px solid #6ee7b7;"></div> 知识库检索</div>
<div class="lg"><div class="lg-dot" style="background:#faf5ff;border:1px solid #c4b5fd;border-style:dashed;"></div> Artifact 生成</div>
</div>`;
// ═══════════════════════════════════════════════════════════════════════════
// FLOW A · UX VIEW
// ═══════════════════════════════════════════════════════════════════════════
function renderA_UX() {
return `
<div class="flow-meta">
<h2>大流程 A · 普通对话 <span class="badge badge-a">无调机流程</span> <span class="badge badge-ux">👁 交互流程</span></h2>
<p>操作员从零开始发起一次输入。界面根据输入内容呈现三种不同的画布组件,操作员按提示进行确认或查阅。</p>
</div>
${legendUX}
<div class="fc">
${nd('nd-start', '👤 操作员输入文字或语音', '当前画布无激活流程')}
${da()}
${nd('nd-trig', '系统识别输入类型,分三种场景响应', '用户感知到的是画布出现了不同内容')}
${slink(3)}
<div class="brow">
<!-- Branch 1 -->
<div class="bcol">
${blbl('场景 1 · 操控设备', 'bl-b')}
${da()}
${nd('nd-show', '画布出现操控确认卡', '参数变更卡 或 设备动画卡')}
${da()}
${nd('nd-do', '操作员选择:确认或取消', '点击按钮 或 输入"确认/取消"')}
${da()}
${nd('nd-end', '画布显示执行结果', '成功 / 失败 / 重试')}
</div>
<!-- Branch 2 -->
<div class="bcol">
${blbl('场景 2 · 询问设备问题', 'bl-g')}
${da()}
${nd('nd-show', '画布出现知识教学卡', 'SOP / 报警处理 / 说明书内容')}
${da()}
${nd('nd-do', '操作员查阅内容', '可展开详情、查看步骤')}
${da()}
${nd('nd-end', '内容展示完毕')}
</div>
<!-- Branch 3 -->
<div class="bcol">
${blbl('场景 3 · 通用 / 无关', 'bl-gr')}
${da()}
${nd('nd-show', '对话框返回文字回答', '打招呼、天气、闲聊等内容')}
${da()}
${nd('nd-end', '回答完毕', '不影响工业主流程')}
<div class="fwarn">⚠ 非核心场景</div>
</div>
</div>
</div>
`;
}
// ═══════════════════════════════════════════════════════════════════════════
// FLOW A · TECH VIEW
// ═══════════════════════════════════════════════════════════════════════════
function renderA_Tech() {
return `
<div class="flow-meta">
<h2>大流程 A · 普通对话 <span class="badge badge-a">工控对话</span> <span class="badge badge-tech">⚙️ 技术流程</span></h2>
<p>语音输入经过四阶段前置拦截停止词→UI语音点击→Slot填写→BERT只有前三阶段全部未命中的输入才进入 BERT NLU再由 decision 字段驱动工具调用、知识检索或 LLM 作答。</p>
</div>
${legendTech}
<div class="fc">
${nd('nd-start', '语音 / 文字输入', '来自 ASR 转录文本 / 直接文字')}
${da()}
${nd('nd-route', '阶段 0 · 停止词检测', '命中 cancel 词表 → 直接生成 stop_action流程终止', ['rule'])}
<div class="fnote">词表来自 voice_aliases.yml · cancel_words静态构建</div>
${da()}
${nd('nd-route', '阶段 1 · UI 可见元素语音点击匹配', '优先级waiting_confirmation affirm/deny > 当前Artifact按钮 > 全局固定操作', ['rule'])}
${slink(2)}
<div class="brow">
<div class="bcol">
${blbl('命中 · 语音点击', 'bl-hit')}
${da()}
${nd('nd-tool', '生成 ActionEvent', 'actionId / artifactId / sourceText', ['rule'])}
${da()}
${nd('nd-snap', '画布状态机直接响应', '不调用 BERT不产生新 Artifact')}
</div>
<div class="bcol">
${blbl('未命中 · 继续', 'bl-miss')}
${da()}
${nd('nd-route', '阶段 1.5 · waiting_slot + inform 检测', 'session.status=waiting_slot AND 输入为数字/数值', ['rule'])}
${slink(2)}
<div class="brow" style="width:100%;">
<div class="bcol">
${blbl('命中 · 填槽', 'bl-hit')}
${da()}
${nd('nd-tool', 'fill_slots 接口', '直接补全当前 slot不走 BERT', ['rule'])}
</div>
<div class="bcol">
${blbl('未命中 · 进入 BERT', 'bl-miss')}
${da()}
${nd('nd-bert', '阶段 2 · BERT NLUintelligent_cabin', 'POST /api/v1/agent/chat\n返回 intent_id + decision + slots', ['bert'])}
<div class="fnote">inference 报错直接抛出,不降级</div>
</div>
</div>
</div>
</div>
${da(30)}
${nd('nd-route', 'decision 字段路由', 'execute / clarify / route_to_cloud / reject', ['rule'])}
${slink(3)}
<div class="brow">
<!-- Branch 1 -->
<div class="bcol">
${blbl('execute · 设备控制域', 'bl-b')}
${da()}
${nd('nd-route', 'domain = machine_control\nconfidence_grade = high\nintent_id = wirecut_*')}
${da()}
${nd('nd-tool', '工业工具调用', 'DBus 写参 / 设备控制指令', ['tool'])}
${da()}
${nd('nd-art', '生成 Artifact', 'ParameterChangeArtifact\nDeviceActionArtifact', ['art'])}
${da()}
${nd('nd-snap', '画布渲染 + 等待 ActionEvent')}
</div>
<!-- Branch 2 -->
<div class="bcol">
${blbl('route_to_cloud · 知识域', 'bl-g')}
${da()}
${nd('nd-route', 'domain = equipment_knowledge\n或 confidence 偏低')}
${da()}
${nd('nd-llm', 'LLM 语义兜底分析', '提取检索关键词', ['llm'])}
${da()}
${nd('nd-kb', '知识库检索', '说明书 / SOP / 报警手册', ['tool'])}
${da()}
${nd('nd-llm', 'LLM 组织检索结果', '生成教学结构', ['llm'])}
${da()}
${nd('nd-art', '生成 KnowledgeLessonArtifact', '', ['art'])}
${da()}
${nd('nd-snap', '画布渲染')}
</div>
<!-- Branch 3 -->
<div class="bcol">
${blbl('reject · smalltalk / fallback', 'bl-gr')}
${da()}
${nd('nd-route', 'domain = smalltalk\n或无法匹配工业 domain')}
${da()}
${nd('nd-llm', 'LLM 直接作答', '', ['llm'])}
${da()}
${nd('nd-minor', '文字回复,不生成 Artifact')}
<div class="fnote">不写入 ArtifactStore</div>
</div>
</div>
</div>
`;
}
// ─── Render Dispatch ──────────────────────────────────────────────────────
const renderers = {
ux: renderA_UX,
tech: renderA_Tech,
};
function render() {
const { view } = S;
// View tabs
document.querySelectorAll('#viewTabs .tbtn').forEach(b => {
b.classList.remove('active-ux', 'active-tech');
if (b.dataset.view === view) {
b.classList.add(view === 'ux' ? 'active-ux' : 'active-tech');
}
});
// Color bar
const bar = document.getElementById('viewBar');
bar.className = `view-bar ${view}`;
// Content
document.getElementById('flowBody').innerHTML = renderers[view]();
}
// ─── Events ───────────────────────────────────────────────────────────────
document.getElementById('viewTabs').addEventListener('click', e => {
const btn = e.target.closest('.tbtn');
if (btn && btn.dataset.view) { S.view = btn.dataset.view; render(); }
});
render();
</script>
</body>
</html>