Files
ai-device/docs/归档(包含调机流程).html
2026-06-11 16:28:00 +08:00

470 lines
24 KiB
HTML
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.
<!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>