import React, { useState, useEffect, useMemo, useRef, memo } from "react"; import { ReactFlow, Background, Controls, Handle, Position, applyNodeChanges, applyEdgeChanges, MarkerType, } from "@xyflow/react"; import { ArrowUpRight, ArrowRight, Activity, AudioLines, GitBranch, Clock3, Layers3, Plus, ChevronDown, ChevronRight, Download, Upload, RotateCcw, LayoutGrid, Trash2, Copy, Check, Search, BookOpen, SlidersHorizontal, Play, Pause, X, Info, Link2, Braces, Mic, Volume2, BrainCircuit, Workflow, ExternalLink, Menu, } from "lucide-react"; import { RECORDS, SOURCES, CUTOFF, RELEASE, EVIDENCE, METRICS, recordValue, HARDWARE, SIZE_OPTIONS, CALIBRATION, } from "./catalog.js"; import { DEFAULT_GLOBALS, KINDS, MODEL_KINDS, simulateGraph, estimateNode, validateGraph, validateDocument, autoLayout, getRecord, ms, } from "./engine.js"; import { PRESETS, makeDocument, nodeData, toDocument } from "./presets.js"; const STORE = "latency-lab-v1"; const icons = { input: Mic, asr: AudioLines, llm: BrainCircuit, speech: AudioLines, tts: Volume2, program: Braces, merge: GitBranch, output: Volume2, }; function loadSaved() { try { const d = JSON.parse(localStorage.getItem(STORE) || "null"); if (d && !validateDocument(d).length) return d; } catch {} return makeDocument(); } function download(name, value, type = "application/json") { const blob = new Blob([value], { type }), url = URL.createObjectURL(blob), a = document.createElement("a"); a.href = url; a.download = name; a.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); } const options = (entries) => entries.map(([value, label]) => ( )); function Field({ label, children, hint }) { return ( ); } function Num({ value, onChange, min = 0, max = 1e6, step = 1, label }) { return ( onChange(Math.min(max, Math.max(min, Number(e.target.value) || 0))) } /> ); } function Toggle({ checked, onChange, children }) { return ( ); } function Tag({ type = "neutral", children }) { return {children}; } function SourceLink({ id, children }) { const s = SOURCES[id]; return s ? ( {children || s.publisher} ) : null; } const StageNode = memo(function StageNode({ data, selected }) { const Icon = icons[data.kind] || Braces, r = data.result; return (
{data.kind !== "input" && ( )}
{KINDS[data.kind]} {data.maxSteps > 1 && ( ↻ {data.maxSteps} )}
{data.label}
{MODEL_KINDS.includes(data.kind) ? data.mode === "reference" ? getRecord(data.profileId)?.name || "来源参考" : `${data.arch === "moe" ? `${data.size}B / A${data.active}B` : `${data.size}B`} · ${HARDWARE.find((h) => h.id === data.hardware)?.name || "自定义"}` : data.kind === "output" ? "声音 / 最终结果的测量终点" : data.kind === "input" ? "t = 0 · 用户停说" : `${data.durationMs} ms · 可修改`}
首出 {r ? ms(r.first - r.start) : "—"} 完成 {r ? ms(r.end - r.start) : "—"}
{data.kind !== "output" && ( )}
); }); const nodeTypes = { stage: StageNode }; function Hero({ onStart, onCompare }) { return (
INFERENCE / VOICE / AGENTS{" "} 研究快照 2026.11

从模型的毫秒,
到对话的节奏。

看清不同尺寸模型的输出速度,
再把节点连起来,计算一场对话真正的等待。

公开来源逐条标注 理论与报告分开 估算参数可校准
一场对话的时间切片 ILLUSTRATION / ms
{Array.from({ length: 52 }, (_, i) => ( ))}
用户停说 模型持续处理 首段声音
输入流 持续监听
推理流 并行工作
输出流 流式合成
↗
开始得早,结束也要算清。

首token ≠ 首音频 ≠ 完整任务

{RECORDS.length} 条

带来源的性能记录

0.5–2,400 B

覆盖主干 / 总参数规模

170 ms

7B语音模型公开接续报告 来源

≤ 100 步

每个模型节点的loop预算

); } function Comparison({ addProfile }) { const [modality, setModality] = useState("all"), [evidence, setEvidence] = useState("all"), [metric, setMetric] = useState("all"), [query, setQuery] = useState(""), [expanded, setExpanded] = useState(null), [view, setView] = useState("records"), [sort, setSort] = useState("default"); const [hw, setHw] = useState("h200"), [cards, setCards] = useState(1), [prompt, setPrompt] = useState(1024), [tokens, setTokens] = useState(128), [spec, setSpec] = useState(1); const filtered = useMemo(() => { const rows = RECORDS.filter( (r) => (modality === "all" || r.modality === modality) && (evidence === "all" || r.evidence === evidence) && (metric === "all" || r.metric === metric) && `${r.name} ${r.hardware} ${r.params}` .toLowerCase() .includes(query.toLowerCase()), ); if (sort === "size") rows.sort((a, b) => (a.size ?? Infinity) - (b.size ?? Infinity)); return rows; }, [modality, evidence, metric, query, sort]); const exportCsv = () => { const rows = [ [ "模型", "模态", "参数口径", "指标", "数值", "单位", "硬件", "量化", "输入tokens", "并发", "证据", "时间", "来源", "限制", ], ...filtered.map((r) => [ r.name, r.modality, r.params, METRICS[r.metric], r.value ?? "", r.metric === "tps" ? "tok/s" : r.metric === "rtf" ? "RTF" : "ms", r.hardware, r.precision || "", r.input ?? "", r.concurrency ?? "", EVIDENCE[r.evidence], r.date, SOURCES[r.source].url, r.note || SOURCES[r.source].note, ]), ]; download( "model-latency-20261106.csv", "\uFEFF" + rows .map((r) => r.map((v) => '"' + String(v).replaceAll('"', '""') + '"').join(","), ) .join("\n"), "text/csv;charset=utf-8", ); }; return (
01 / THE EVIDENCE

模型时延对照

先比较同口径,再讨论极限。尺寸相同,部署条件仍可能相差很大。

{view === "records" ? ( <>
{[ ["all", "全部"], ["text", "纯文本"], ["speech", "端到端语音"], ["tts", "TTS组件"], ].map(([v, l]) => ( ))}
{filtered.length} 条记录 · 截止 {CUTOFF} 点击一行展开条件与限制
{filtered.map((r) => ( setExpanded(expanded === r.id ? null : r.id) } tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter") setExpanded(expanded === r.id ? null : r.id); }} > {expanded === r.id && ( )} ))}
模型 / 规模 指标与结果 部署条件 证据与来源
{r.name} {r.params} {r.active ? ` · 激活${r.active}B` : ""}{" "} {r.modality === "text" ? "文本" : r.modality === "tts" ? "TTS" : "语音"} {recordValue(r)} {r.scope} {r.metric === "tps" && ( ≈ {(1000 / r.value).toFixed(2)} ms/token )} {r.hardware} {r.precision || "精度未披露"} {r.concurrency ? ` · 并发${r.concurrency}` : ""} {EVIDENCE[r.evidence]} {r.date}
这条数字说明什么

{r.note || SOURCES[r.source].note}

输入: {r.input ? `${r.input.toLocaleString()} token` : r.inputSeconds ? `${r.inputSeconds}秒音频/视频` : "未充分披露"}  输出: {r.output ? `${r.output} token` : "未充分披露"}  {r.ttft ? `另报告首token ${r.ttft}ms。` : ""} {r.tps ? ` 另报告文本速度${r.tps} tok/s。` : ""} {r.rtf ? ` RTF ${r.rtf}。` : ""}

{SOURCES[r.source].title}
{r.value != null && r.metric !== "interrupt" && ( )}
{!filtered.length && (
没有匹配记录,试试其他筛选条件。
)}

“最佳”必须带条件。 历史晶圆级结果、单卡GPU、受控接受率、生产API观测不构成统一排行榜。公开报告也不等于本网站实测。

A MoE看两种尺寸。 总参数影响存储,激活参数影响每步计算;不能把30B-A3B当作30B稠密模型。

) : ( <>

{" "} FP8、模型已热身、低负载、不计网络与排队。以Qwen3/H20为速率锚点,硬件倍率和首token都是可校准的工程假设;不是硬件实测或物理下界。

{[0.5, 1.5, 3, 7, 8, 14, 27, 32, 70, 120, 235, 405].map( (size) => { const r = estimateNode({ ...nodeData("llm", "尺寸扫描"), hardware: hw, cards, size, inputTokens: prompt, outputTokens: tokens, speculative: spec, }); return ( ); }, )}
稠密模型规模 估计首token 估计输出速度 完整输出估计 可部署性 / 外推
{size}B 稠密 · FP8 {ms(r.first)} {r.rate.toFixed(1)} tok/s {(1000 / r.rate).toFixed(1)} ms/token {ms(r.end)} {r.warnings.find((w) => w.includes("显存")) ? ( 显存不足 ) : ( 估算 )} {r.warnings.find((w) => w.includes("显存")) || "参数变化仅代表此工程模型下的趋势。"}
)}
); } function NumberField({ data, patch, keyName, label, hint, min = 0, max = 1e6, step = 1, }) { return ( patch({ [keyName]: v })} min={min} max={max} step={step} /> ); } function NodeInspector({ node, result, patch, remove, duplicate }) { if (!node) return (

选择一个节点

点击流程中的节点,编辑模型、参数和loop步数。也可以先从左侧添加节点。

); const d = node.data, isModel = MODEL_KINDS.includes(d.kind), isText = d.kind === "llm"; const profiles = d.kind === "asr" ? [] : RECORDS.filter( (r) => r.value != null && r.metric !== "interrupt" && (isText ? r.modality === "text" : d.kind === "tts" ? r.modality === "tts" : r.modality === "speech"), ); const chooseProfile = (id) => { const r = getRecord(id); patch({ profileId: id, mode: "reference", ...(r?.size ? { size: r.size } : {}), ...(r?.active ? { active: r.active, arch: "moe" } : { arch: "dense" }), }); }; return ( <>
NODE CONFIG

节点设置

patch({ label: e.target.value })} />
{KINDS[d.kind]} {result && {result.label}}
{isModel && ( <> {d.mode === "reference" ? ( <> {getRecord(d.profileId) && (
{SOURCES[getRecord(d.profileId).source].title}

{getRecord(d.profileId).note || SOURCES[getRecord(d.profileId).source].note}

)} ) : ( <>
patch({ size: Math.max(0.1, Number(e.target.value) || 0.1), }) } />
{d.arch === "moe" && ( )}
)} {d.mode === "custom" && (

手动校准

{isText ? ( ) : ( )}
)} {isText && ( <>

一次推理的工作量

{d.mode === "estimate" && ( )}
)} {d.kind === "asr" && ( <> patch({ streamingInput: v })} > 用户说话时持续识别 {d.mode !== "custom" && ( )} )}

Agent / 模型 loop

逐步累计
{d.maxSteps > 1 && ( <> {isText && ( )} patch({ finalOnly: v })} > 仅最后一步向下游输出

关闭后首包可能是中间反馈。loop指节点内部重复调用,不代表真实对话往返轮数。

)}
patch({ resource: e.target.value })} /> )} {(d.kind === "program" || d.kind === "merge" || d.kind === "output") && ( )} {d.kind === "output" && ( <> patch({ target: v })} > 作为主结果测量终点 )} {d.kind === "input" && (

该节点代表用户停止说话。判停等待在“全局参数”中调整。流式识别可以提前处理用户说话期间的输入。

)} {result && (

本节点的时间

开始
{ms(result.start)}
首出
{ms(result.first)}
结束
{ms(result.end)}
{result.resourceWait > 0 && (
资源等待
{ms(result.resourceWait)}
)}
{result.assumptions.map((s, i) => (

{s}

))} {result.warnings.map((s, i) => (

{s}

))}
)} ); } function Timeline({ sim, nodes, selected, setSelected }) { const [playing, setPlaying] = useState(false), [cursor, setCursor] = useState(0), [stepsOpen, setStepsOpen] = useState(false), timer = useRef(); const total = sim.complete || 1; useEffect(() => { if (!playing) return; let start = performance.now(), from = cursor; const tick = (now) => { const next = from + (now - start) * (total / 6000); if (next >= total) { setCursor(total); setPlaying(false); } else { setCursor(next); timer.current = requestAnimationFrame(tick); } }; timer.current = requestAnimationFrame(tick); return () => cancelAnimationFrame(timer.current); }, [playing, total]); useEffect(() => { setCursor(0); setPlaying(false); }, [total]); const result = selected ? sim.results?.[selected] : null; return (

流程时间线 用户停说 = 0

绿色标出首结果关键路径。浅色区域是准备/等待,实色区域是输出。

0 {ms(total * 0.25)} {ms(total * 0.5)} {ms(total * 0.75)} {ms(total)}
{sim.order.map((id) => { const r = sim.results[id], width = Math.max(0.7, ((r.end - r.start) / total) * 100), left = (r.start / total) * 100, first = ((r.first - r.start) / Math.max(1, r.end - r.start)) * 100; return ( ); })}
{result?.steps.length > 1 && (
{stepsOpen && (
{result.steps.map((s) => ( ))}
步 输入 / 输出 / 思考 开始 首出 结束 步前工具等待
{s.step} {s.input} / {s.output} / {s.reasoning} {ms(s.start)} {ms(s.first)} {ms(s.end)} {ms(s.toolWait)}
)}
)}
); } function Studio({ doc, setDoc, selected, setSelected, toast }) { const nodes = doc.nodes, edges = doc.edges, g = doc.globals; const [flow, setFlow] = useState(null), [panel, setPanel] = useState("node"), [edgeId, setEdgeId] = useState(null), [from, setFrom] = useState(""), [to, setTo] = useState(""), [release, setRelease] = useState("complete"), [focus, setFocus] = useState(false), [focusSettings, setFocusSettings] = useState(false); const inputRef = useRef(), canvasRef = useRef(), history = useRef([]); const sim = useMemo(() => simulateGraph(nodes, edges, g), [nodes, edges, g]); useEffect(() => { if (!focus) return; const old = document.body.style.overflow; document.body.style.overflow = "hidden"; const escape = (e) => { if (e.key === "Escape") setFocus(false); }; window.addEventListener("keydown", escape); return () => { document.body.style.overflow = old; window.removeEventListener("keydown", escape); }; }, [focus]); const saveHistory = () => { history.current.push(structuredClone(doc)); if (history.current.length > 35) history.current.shift(); }; const update = (next) => { saveHistory(); setDoc(typeof next === "function" ? next(doc) : next); }; const patchNode = (patch) => update({ ...doc, nodes: nodes.map((n) => n.id === selected ? { ...n, data: { ...n.data, ...patch } } : n, ), }); const patchGlobals = (patch) => update({ ...doc, globals: { ...g, ...patch } }); const current = nodes.find((n) => n.id === selected), edge = edges.find((e) => e.id === edgeId); const fit = () => setTimeout(() => flow?.fitView({ padding: 0.18, duration: 300 }), 80); const choosePreset = (key) => { update(makeDocument(key)); setSelected(null); setEdgeId(null); fit(); }; const addNode = (kind) => { if (nodes.length >= 100) { toast("最多支持100个节点。"); return; } const id = `n-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 5)}`; const rect = canvasRef.current?.getBoundingClientRect(); const pos = flow && rect ? flow.screenToFlowPosition({ x: rect.x + rect.width / 2, y: rect.y + rect.height / 2, }) : { x: 100, y: 100 }; update({ ...doc, nodes: [ ...nodes, { id, type: "stage", position: pos, data: nodeData(kind, KINDS[kind]) }, ], }); setSelected(id); setPanel("node"); }; const connect = (c) => { if (!c.source || !c.target) { toast("请选择起点和终点。"); return; } if (edges.some((e) => e.source === c.source && e.target === c.target)) { toast("这两个节点已经相连。"); return; } const next = { id: `edge-${Date.now()}-${Math.random().toString(36).slice(2, 5)}`, type: "smoothstep", source: c.source, target: c.target, data: { release: c.release || "complete", tokens: 8 }, }; const err = validateGraph(nodes, [...edges, next]); if (err.length) { toast(err[0]); return; } update({ ...doc, edges: [...edges, next] }); setEdgeId(next.id); setPanel("edge"); }; const removeNode = () => { update({ ...doc, nodes: nodes.filter((n) => n.id !== selected), edges: edges.filter( (e) => e.source !== selected && e.target !== selected, ), }); setSelected(null); }; const duplicate = () => { if (!current) return; const id = `n-${Date.now().toString(36)}`; update({ ...doc, nodes: [ ...nodes, { ...structuredClone(current), id, position: { x: current.position.x + 35, y: current.position.y + 165 }, data: { ...current.data, label: current.data.label + " 副本" }, }, ], }); setSelected(id); }; const renderNodes = useMemo( () => nodes.map((n) => ({ ...n, selected: n.id === selected, data: { ...n.data, result: sim.results?.[n.id], critical: sim.criticalNodes?.has(n.id), }, })), [nodes, selected, sim], ); const renderEdges = useMemo( () => edges.map((e) => ({ ...e, selected: e.id === edgeId, animated: e.data?.release !== "complete" && !e.data?.control, label: e.data?.control ? "控制路径" : e.data?.release === "chunk" ? `${e.data.tokens || 8} token后` : e.data?.release === "first" ? "首包后" : "完成后", markerEnd: { type: MarkerType.ArrowClosed, width: 16, height: 16, color: sim.criticalEdges?.has(e.id) ? "#2d7854" : "#91a39a", }, style: { stroke: sim.criticalEdges?.has(e.id) ? "#2d7854" : "#91a39a", strokeWidth: sim.criticalEdges?.has(e.id) ? 2.2 : 1.5, ...(e.data?.control ? { strokeDasharray: "5 5" } : {}), }, labelStyle: { fill: "#50645b", fontSize: 10 }, labelBgStyle: { fill: "#f7f8f4" }, labelBgPadding: [5, 3], })), [edges, edgeId, sim], ); const exportDoc = () => download( "dialogue-flow.json", JSON.stringify(toDocument(nodes, edges, g, doc.name), null, 2), ); const importDoc = async (e) => { const file = e.target.files?.[0]; if (!file) return; try { if (file.size > 2e6) throw new Error("文件超过2MB。"); const parsed = JSON.parse(await file.text()); const err = validateDocument(parsed); if (err.length) throw new Error(err[0]); update(parsed); setSelected(null); fit(); toast("已导入流程。"); } catch (error) { toast(`导入失败:${error.message}`); } e.target.value = ""; }; const onChanges = (changes) => setDoc((prev) => ({ ...prev, nodes: applyNodeChanges(changes, prev.nodes), })); return (
02 / BUILD THE CONVERSATION

把一场对话,搭出来。

拖拽、连线、并行、汇总。改变一个节点,看等待如何沿流程传递。

自动保存在本机
从一个结构开始 {Object.entries(PRESETS) .filter(([k]) => k !== "blank") .map(([key, p]) => ( ))}
{doc.name || "自定义流程"} {!sim.errors.length && ( 首出 {ms(sim.first)} 完成 {ms(sim.complete)} )}
{focus && ( )}
setDoc((prev) => ({ ...prev, edges: applyEdgeChanges(changes, prev.edges), })) } onConnect={connect} onInit={setFlow} onNodeClick={(_, n) => { setSelected(n.id); setPanel("node"); if (focus) setFocusSettings(true); }} onEdgeClick={(_, e) => { setEdgeId(e.id); setPanel("edge"); if (focus) setFocusSettings(true); }} onNodeDragStart={saveHistory} onPaneClick={() => { setSelected(null); setEdgeId(null); }} onNodesDelete={(ns) => { setDoc((prev) => ({ ...prev, edges: prev.edges.filter( (e) => !ns.some((n) => n.id === e.source || n.id === e.target), ), })); }} isValidConnection={(c) => !validateGraph(nodes, [...edges, { id: "candidate", ...c }]) .length } fitView fitViewOptions={{ padding: 0.16 }} minZoom={0.16} maxZoom={1.6} deleteKeyCode={["Backspace", "Delete"]} connectOnClick selectionOnDrag={false} ariaLabelConfig={{ "controls.zoomIn.ariaLabel": "放大流程", "controls.zoomOut.ariaLabel": "缩小流程", "controls.fitView.ariaLabel": "显示完整流程", "controls.interactive.ariaLabel": "切换交互", }} > {!nodes.length && (

从第一个节点开始

点击左侧添加节点,或者加载一个示例结构。

)}
首结果关键路径 流式连接
{sim.errors.length ? (
{sim.errors.join(" ")}
) : ( <>
{sim.hasAudio ? "首段可播放音频" : "首个目标结果"} 方案估计 {ms(sim.first)} 敏感性范围 {ms(sim.range[0])} – {ms(sim.range[1])}
全部主目标完成 {ms(sim.complete)} 音频目标包含 {g.speechSeconds}s 自然播放
跑满loop预算 {ms(sim.maxFirst)} 首结果 / 完成 {ms(sim.maxComplete)}
持续处理负载 1 ? "warn-text" : ""}> {sim.rtf.toFixed(2)} RTF {sim.rtf > 1 ? "当前假设可能跟不上实时" : "理想资源下具备实时余量"}

范围只把模型和程序阶段乘0.75–1.5,固定网络/判停不变;不是p95或统计置信区间。改变模型、隐藏思考、共享资源或loop后会立即重算。打断控制能力仍需真实音频系统验证。

{sim.warnings.length > 0 && (
估算条件与待校准项 {sim.warnings.length}
    {sim.warnings.map((w, i) => (
  • {w}
  • ))}
)} { setSelected(id); setPanel("node"); }} /> )} {SIZE_OPTIONS.map((s) => (
); } function Method() { return (
03 / THE MODEL BEHIND THE MODEL

这些时间,怎么算。

结果的可信度,取决于计时边界和输入假设是否说清。

先定义你在等什么。

文本首token是模型开始生成的时间。隐藏思考可能发生在可见答案之前。语音首包是第一段可播放音频就绪的时间;终端播放还需要网络与缓冲。完整回复则要考虑所有输出和自然说话时长。

TTFT

首次模型token;对推理模型应另计首个可见答案。

TPOT

持续输出阶段每token平均耗时,近似1000÷token/s。

TTFA

首段可播放音频的时间,不能用文本TTFT代替。

RTF

处理/生成音频所需时间÷音频时长。小于1才有持续实时余量。

01

串行与并行

start(v) = max(各前驱满足条件的时刻)

默认等全部分支。选择“任一就绪”取min;前提是最早分支的结果可用。独立硬件分支可以重叠,共享资源组会增加排队。

02

流式开始,持续供给

首包 + (k − 1) / 输出速率

文本积累k个token后启动下游。完成时刻还受上游最终数据就绪约束,避免把提前启动误当作全部工作已完成。这里采用平均供给率,未模拟音素对齐、每帧抖动与流控背压。

03

Agent loop

Σ[首token + (思考 + 输出 − 1) / 速率] + (N − 1) × 工具等待

每步可增加上下文;默认仅最后一步对下游放行。最大步数定义预算情景,不保证真实任务能在预算内成功。选择预计步数后,同时显示跑满预算的结果。

04

参数规模外推

速率 = H20锚点 × 硬件系数 × 量化系数 × 并行收益

稠密锚点采用Qwen3官方FP8数据做对数插值;32B以上按反比外推。MoE用激活参数并乘额外开销。卡数收益上限4倍,不能靠无限加卡把时延降到零。

查看全部默认系数与计算限制

可复查的默认值

{HARDWARE.map((h) => ( ))}
硬件 相对H20速率系数
{h.name} {h.factor}×(假设)

量化速率系数:BF16 0.62,FP8 1,INT4 1.06。它们是近似值,不代表所有模型的固定规律。

首token与显存

Prefill默认8B/H20为18,000 token/s,固定开销14ms;随激活规模、缓存和硬件调整。缺少首token来源时使用该模型。公开记录中的硬件条件固定;更换条件应切回工程估算。

权重显存粗算:BF16 2字节/参数,FP8 1.05,INT4 0.55;另加max(2GB,8%)运行余量。这不是完整KV-cache容量规划。

未建模的因素

任务正确性、语音自然度、错误重试、冷启动、长上下文attention非线性、真实调度、网络抖动、设备音频延迟与多用户拥塞。模型间tokenizer也不同,不把token直接等同汉字。

怎样理解“理想时延”

统一条件估算采用已热身、低并发、稳定网络。端到端语音通过边听边处理缩短停说后的等待,不能因此声称复杂任务已完成。一个170ms的接续可以很自然,却不能代表检索、工具和多轮Agent也在170ms内结束。网站中的数据为公开资料调研,本次没有运行模型效果或硬件性能实验。

覆盖公开可核验的代表性尺寸与部署条件;不穷尽全部模型。未公开的参数规模保持未知,缺少首音频数据时也保留空缺。

); } function Sources() { return (
04 / PRIMARY SOURCES

让每个数字,都有来处。

截止 {CUTOFF} 查阅。只引用官方文档、官方模型卡或原始研究论文。

{Object.keys(SOURCES).length} 个一手来源
{Object.entries(SOURCES).map(([id, s], i) => (
{String(i + 1).padStart(2, "0")}
{s.title} {s.publisher} / {s.date}

{s.note}

))}
); } export default function App() { const [tab, setTab] = useState(() => location.hash === "#studio" ? "studio" : location.hash === "#method" ? "method" : location.hash === "#sources" ? "sources" : "overview", ), [doc, setDoc] = useState(loadSaved), [selected, setSelected] = useState(null), [message, setMessage] = useState(""), [mobileMenu, setMobileMenu] = useState(false); const toastTimer = useRef(); const toast = (text) => { setMessage(text); clearTimeout(toastTimer.current); toastTimer.current = setTimeout(() => setMessage(""), 4500); }; useEffect(() => { const timer = setTimeout(() => { try { localStorage.setItem( STORE, JSON.stringify( toDocument(doc.nodes, doc.edges, doc.globals, doc.name), ), ); } catch { toast("本机存储不可用,请导出JSON保存流程。"); } }, 500); return () => clearTimeout(timer); }, [doc]); const navigate = (t) => { setTab(t); setMobileMenu(false); history.replaceState(null, "", t === "overview" ? "#overview" : `#${t}`); window.scrollTo({ top: 0, behavior: "instant" }); }; const addProfile = (r) => { const id = `n-${Date.now().toString(36)}`, kind = r.modality === "text" ? "llm" : r.modality === "tts" ? "tts" : "speech", data = { ...nodeData(kind, r.name), mode: "reference", profileId: r.id, ...(r.size ? { size: r.size } : {}), ...(r.active ? { active: r.active, arch: "moe" } : {}), }; setDoc((prev) => ({ ...prev, name: "自定义对话流程", nodes: [ ...prev.nodes, { id, type: "stage", position: { x: 120, y: 120 + prev.nodes.length * 30 }, data, }, ], })); setSelected(id); navigate("studio"); toast("已添加参考节点;首包或持续速度缺失时会明确使用工程假设。"); }; return ( <>
{ e.preventDefault(); navigate("overview"); }} > 模型时延实验室LATENCY LAB
K1412 RESEARCH
{tab === "overview" && ( <> navigate("studio")} onCompare={() => document .getElementById("compare") .scrollIntoView({ behavior: "smooth" }) } />
FROM A NUMBER TO A SYSTEM

一个模型快,
整条链路也快吗?

把多个模型、工具和语音模块连起来,找出真正决定体验的那条路径。

)} {tab === "studio" && ( )}{" "} {tab === "method" && } {tab === "sources" && }
LATENCY LAB

公开证据 × 可校准的系统估算

资料截止 {CUTOFF} 源代码与数据
{RELEASE} · 本机计算与存储,无需模型API密钥
{message && (
{message}
)} ); }