[{"content":" 写作日期:2026-09-03。本文描述的系统行为均基于 2026-09-03 在作者本机正在运行的 Hermes Agent(任务上下文标注为 v0.16)上的现场观察与第一手工具面,而非照抄宣传材料——作者写作时正作为 dispatcher 派生的 worker 运行在该系统的看板上。凡未能现场核验的时效性内容(版本号、变更日志、外部系统细节)均已标注,并统一列入附录 A 核验清单,引用前请以官方仓库与文档为准。\nHermes Agent v0.16 Kanban Swarm 功能深度解析(面向 AI 开发者) 你写过这样的代码吗:一个 Agent 会话跑长任务,中途断网、进程被杀、上下文爆炸,一切归零;或者你想让\u0026quot;调研 Agent\u0026quot;和\u0026quot;写作 Agent\u0026quot;接力,却发现它们活在各自的进程里,谁也看不见谁。\nHermes Agent v0.16 的 Kanban Swarm 解决的就是这件事:用一块持久化的 SQLite 看板,把多个独立 Agent(每个是一个 profile,各自有独立的配置、会话、技能与记忆)组织成一支能接力、能并行、能失败重试、能跨进程存活的\u0026quot;工人队伍\u0026quot;。看板是它们唯一的共识层——任务状态、依赖关系、交接产物,全部落在磁盘上,进程死了看板还在。\n这篇文章不打算复述 README。作者写作时正被 dispatcher 派生、以 worker 身份跑在这块看板上(任务 t_b1e3c641),文中的状态机、工具面、调度语义来自对现场事件日志的直接观察与本文写作时实时可用的工具 schema;配置项与 CLI 动词来自随环境安装的官方 skill 参考(v3.2.0)。我们从数据模型讲起,一路拆到调度器、worker 协议与编排模式。\n1. 为什么 Agent 需要一块\u0026quot;看板\u0026quot; 先给 Kanban Swarm 一个坐标系。Hermes 的多智能体能力按\u0026quot;存活时间\u0026quot;分成三层:\n系统 形态 存活 典型用途 delegate_task(委托) 同一进程内派生子代理,隔离上下文 分钟级,进程退出即丢 并行推理子任务、短时侦察 cronjob(定时) 独立会话按调度触发,结果投递 跨进程、持久 周期巡检、定时报告 Kanban Swarm 跨 profile 的任务队列 + 工作区 + 事件账本 跨进程、持久、可接力 多 Agent 流水线、长任务、需要人工/评审介入的工作流 关键分界在\u0026quot;接力\u0026quot;:delegate 的子代理不知道彼此的产出,父进程一死全部蒸发;cron 只解决\u0026quot;到点跑一次\u0026quot;。Kanban Swarm 解决的是分工与交接——任务卡在板上流转,任何 profile 派生出的 worker 都能认领、执行、写回结构化交接信息,下游任务在上游 done 之前不会启动。\n对 AI 开发者来说,它本质上是一个以 Agent(而非函数)为执行单元、以 SQLite 为存储、以事件日志为账本的分布式任务队列——只不过\u0026quot;分布式\u0026quot;发生在同一台机器的多个 Hermes profile 进程之间。\n2. 核心概念与数据模型 看板上的基本对象是任务卡(task),整个系统围绕它建模:\nBoard(看板):一套任务与事件的容器,持久化为 SQLite 数据库(默认 ~/.hermes/kanban.db,亦可通过 HERMES_KANBAN_DB 指定;每块板还有自己的 boards 目录,如本机 ~/.hermes/kanban/boards/blog/ 下存放各任务的 workspaces)。 Task(任务卡):标题、正文(body,即规格/验收标准)、状态、优先级、assignee(执行者 profile)、workspace 类型与路径、parents/children 依赖边、当前 run、事件流、评论线程、附件。 Assignees(执行者):任务指派给一个 profile(如 orchestrator、researcher-a、writer)。dispatcher 会在对应 profile 名下派生一个全新会话作为 worker。 Run / Attempt(运行/尝试):每次被调度执行记为一次 run,带独立的 run_id;同一任务可多次运行(重试),历史 run 的 outcome/summary/metadata 全部保留——这是\u0026quot;可审计\u0026quot;的来源。 Event(事件):任务的每次状态迁移都追加一条带时间戳与 run_id 的事件(created → claimed → spawned → heartbeat → completed/blocked…),构成完整账本。 Comment(评论):线程化的持久备注,跨 run 可见,是 worker 之间与人工之间传递上下文的通道。 Attachment(附件):真实文件(≤25MB),base64 或 URL 均可挂到任务上,供下游 worker 与订阅者下载。 任务 ID 形如 t_\u0026lt;hex\u0026gt;(现场可见 t_e34e3886、t_b1e3c641);workspace 目录形如 \u0026lt;board\u0026gt;/workspaces/\u0026lt;task_id\u0026gt;(现场:/Users/wangjp/.hermes/kanban/boards/blog/workspaces/t_b1e3c641)。\n3. 任务状态机:一张卡的一生 现场事件日志与工具 schema 可以还原出完整状态机:\n┌─ triage(待细化,可选) ─┐ 创建 → todo(有未完成 parent) ──→ ready ──→ running(claimed+spawned) │(无 parent 或 parent 全 done) │ └─────────────────────────────────────────┤ ▼ done ← complete ── running ── request_review → review ── approve → done │ └─ request_changes → 重回 ready/原实现者 └─ block → blocked(按原因分类,见 §6) archived:终态归档(任一终态后可归档) 各阶段语义(均为现场第一手):\n创建:kanban_create 生成卡。若给了 parents=[...],卡进入 todo 并被依赖门控:只有所有 parent 都 done,才自动提升为 ready——这就是用看板表达 DAG 的方式,依赖写进卡结构而非靠人记。triage: true 则先落到 triage 列,等待 specifier 把正文补全再开工。 就绪与认领:dispatcher 周期性扫描 ready 卡,按 assignee profile 原子认领(claim),写入锁与过期时间。现场观察到的 claim 事件形如:{\u0026quot;lock\u0026quot;: \u0026quot;\u0026lt;host\u0026gt;:\u0026lt;pid\u0026gt;\u0026quot;, \u0026quot;expires\u0026quot;: \u0026lt;ts\u0026gt;, \u0026quot;run_id\u0026quot;: 2}——锁标识了\u0026quot;谁在跑\u0026quot;,过期时间给调度器回收依据。 派生:认领后 dispatcher 在 assignee 名下 spawn 一个真实进程(现场事件 {\u0026quot;pid\u0026quot;: 53716}),worker 会话带着任务上下文启动。 执行:worker 读卡、进 workspace 干活,期间以心跳保持存活(见 §5)。 收尾,三条路: kanban_complete:直接完成,写 summary(人读的一句话)与 metadata(机器读的结构化事实,如 changed_files/tests_run/决策)。若板上预建了 review/QA 子任务,complete 是释放它们的唯一正确动作——下游评审卡因 parent 门控而自动就绪。 kanban_request_review:实现与自测都完成、但需要人工/评审 profile 把关,卡进 review 列;评审者可 complete 放行、request_changes 打回实现者、或 block 升级外部问题。评审不算阻塞,重复轮转不会误触发 block-loop 升级。 kanban_block:遇到真正的外部阻塞(缺凭据、等人工决策、能力墙),按 kind 归类后停止。 一个容易踩的坑:任务若在卡上既挂 review 子任务又把自己置为 review-required(或同卡请求评审),会同时卡死两条流水线——下游评审子任务因 parent 未 done 永远不启动,本卡又停在 review 无人接管。正确姿势是二选一:有预建评审子任务就 complete 释放它;没有就在本卡上 request_review。\n4. 调度器(Dispatcher):谁在推动一切 看板本身是被动的;真正推动任务流转的是一个常驻组件——dispatcher。默认情况下它跑在 Hermes gateway 进程内(kanban.dispatch_in_gateway: true),也可以独立运行(hermes kanban daemon)。它的职责循环:\n提升:把 parent 全 done 的 todo 卡提升为 ready; 认领:按优先级(priority 仅作 tiebreaker)与 assignee 原子认领 ready 卡; 派生:spawn assignee profile 的 worker 会话; 回收:处理僵死与超时(见下); 失败熔断:连续派生失败达到阈值后把卡自动置为 blocked,而不是无限重试。 调度相关的关键参数(来自官方 skill 参考,取值以 hermes config/文档现场为准):\nkanban.dispatch_stale_timeout_seconds:默认 4 小时——任务超过该时长且最近一小时无心跳,dispatcher 判定 worker 已死,回收(reclaim)并无惩罚地重新排队为 ready。所谓\u0026quot;无惩罚\u0026quot;:回收不增加失败计数,任务只是回到队列等下一次调度。 心跳门限:最近一小时必须有心跳,否则可能被回收——这正是协议要求\u0026quot;可能超过 1 小时的任务必须每小时至少一次 kanban_heartbeat\u0026ldquo;的原因。 kanban.failure_limit:默认 2——连续 spawn 失败(如 profile 不存在、环境坏了)累计到阈值,卡自动 blocked 需要人工介入,避免空转。 kanban.dispatch_in_gateway:dispatcher 是否寄生在 gateway 内,默认开。 max_runtime_seconds(建卡参数):单次 run 的硬性时长上限,超时 dispatcher SIGTERM worker 并以 timed_out 结局重新排队。 锁与回收的配合是这套系统最像正经调度器的地方:claim 写入 lock 与 expires(现场观察认领后约 15 分钟过期),配合 4 小时 stale 超时与心跳门限,能容忍 worker 进程崩溃、机器休眠、网络抖动——最坏情况是任务被重新排队重跑一遍,而不是永远卡在 running 列。\n5. Worker 侧协议:你被 spawn 之后 dispatcher 派生出的 worker 会话会注入一组聚焦的 kanban_* 工具(由环境变量 HERMES_KANBAN_TASK 门控;普通会话默认零 kanban 工具面,profile 显式开启 kanban toolset 才在任务外可见看板)。worker 的协议可以总结为七步:\nOrient:kanban_show 读卡。返回体里除了标题/正文,还预格式化好 worker_context:assignee、状态、workspace、该 profile 的近期工作、本任务的历史尝试(若你是重试,能看到上一次的 summary+metadata)、完整评论线程——把\u0026quot;我接手时该知道什么\u0026quot;一次性喂给你,不用考古。 进工作区:cd $HERMES_KANBAN_WORKSPACE。工作区分三种(建卡时 workspace_kind 决定): scratch(默认):一次性临时目录,任务完成即删除——交付物要写进卡外持久位置或作为 artifact 提交,否则蒸发; dir:共享的绝对路径目录,多任务可见; worktree:git worktree,主仓库旁挂独立分支(分支名 wt/\u0026lt;task_id\u0026gt; 或 $HERMES_KANBAN_BRANCH),多个并行 agent 改同一仓库不打架; 若卡关联 project,则工作区为 \u0026lt;repo\u0026gt;/.worktrees/\u0026lt;task-id\u0026gt;,分支名确定性为 \u0026lt;project-slug\u0026gt;/\u0026lt;task-id\u0026gt;。 心跳:长操作(训练、编码、抓取)期间隔几分钟 kanban_heartbeat(note=...);可能超过 1 小时的任务必须每小时至少一次,否则可能被回收。现场事件日志显示本板心跳约 60 秒一次的节奏(如 10:41:41、10:42:01、10:43:01……)。 阻塞而非猜测:真遇到无法推断的人工决策(缺凭据、UX 选择、付费墙),kanban_block(kind=...) 并停手;禁止在无头 worker 里 clarify——没有活人回答,只会超时并让任务无声卡在 running。 收尾交接:完成时在 kanban_complete/request_review 上写 summary + metadata。这是跨 run、跨 worker、跨进程的唯一交接面:summary 给人类读(1-3 句干了什么),metadata 给机器读(结构化事实:changed_files、tests_run、决策、下一步)。绝不在这些持久字段里放密钥/token/PII。 产物:交付文件放 artifacts=[绝对路径](工具顶层参数;塞在 metadata 里的路径不会被上传)。文件必须在完成时真实存在于磁盘。scratch 工作区内的文件会在清理前被复制到任务附件;25MB 上限。 后续工作开卡,不顺手做:发现衍生工作,kanban_create(title=..., assignee=\u0026lt;对口的 specialist profile\u0026gt;, parents=[当前卡]) 交给正确的人,而不是 scope creep 进下一件事。 worker 侧还有两条硬纪律:看板操作一律走 kanban_* 工具,不要 shell 出去敲 hermes kanban \u0026lt;verb\u0026gt;(工具在本地/docker/modal/ssh 各种终端后端下都一致);完成时若 kanban_create 返回了新卡 id,必须在 created_cards 里如实登记——内核会校验 id 真实性,幻影 id 直接拒绝完成,防止下游自动化引用到不存在的卡。\n6. Block 的四种原因:阻塞也要结构化 kanban_block 的 kind 参数把\u0026quot;为什么停\u0026quot;结构化,决定任务去向:\nkind 含义 去向 dependency 等另一任务/上游产出 回 todo,该任务完成时自动恢复调度,无需人工 needs_input 需要人类决策/回答 上浮给人 capability 硬墙:无权限、缺凭据、任何 agent 都做不到 上浮给人 transient 暂时性故障,可能自己会好 上浮给人(或稍后重试) 两个防呆机制:同一原因反复 unblock → block 会被自动升级到 triage 让人工裁决(防循环空转);评审轮转(§3)不算阻塞,不计入 unblock-loop 检测,所以评审可以来回多轮而不会误触发升级。\n7. 编排模式:Orchestrator 怎么用看板放一支队伍 Kanban Swarm 最典型的用法是分解-派发(fan-out):一个 orchestrator 卡把人话目标拆成若干 specialist 子卡(每个 assignee 是一个对口的 profile、parents=[orchestrator卡] 表达依赖),然后 orchestrator 自己 complete——子卡随 parent done 自动就绪,dispatcher 依次派生对应 profile 开工;下游 fan-in 卡把所有子卡列为 parents,全部 done 后才启动合成。\n运行规则里藏着几条反直觉的工程约束:\n决策所有权在 orchestrator,不在 worker。命名方案、schema、文件格式、API 形态这类设计决策,必须在 fan-out 前定死,并写进每张子卡的 body——worker 看不到兄弟卡上下文,跨卡共享的决策必须逐卡携带,否则两个子树会各自拍板同一问题。 assignee 必须真实存在。dispatcher 会静默丢弃 assignee 不存在的卡(永远躺在 ready 无人领)。开工前 hermes profile list 核一遍。 hotspot 纪律:若你的改动反复撞上同一文件、或你动到的文件出现在别的卡评论里,别闷头往上叠——在卡上留 hotspot: \u0026lt;path\u0026gt; — \u0026lt;原因\u0026gt; 评论并在完成 metadata 里复述,让 orchestrator 有机会在更多活落地前分解该文件。 派生不指派给自己。衍生任务要交给对口的 specialist profile,而不是 orchestrator 顺手做掉。 8. 现场实证:这块板上的真实事件流 与其抽象描述,不如直接看本板真实事件日志。姊妹文章任务 t_e34e3886(\u0026ldquo;写一篇关于 WebLLM 的深度文章\u0026rdquo;,assignee=orchestrator)的完整轨迹:\ncreated {status: ready, workspace_kind: scratch, parents: []} 10:42:46 claimed {lock: \u0026#34;\u0026lt;host\u0026gt;:\u0026lt;pid\u0026gt;\u0026#34;, run_id: 1} 10:43:28 spawned {pid: 52416} 10:43:29 tip_scratch_workspace (提醒:scratch 完成任务即删,产物要放持久位置) 10:43:28 heartbeat ×9(间隔约 60 秒) 10:43:41 → 10:52:50 completed summary+metadata,artifacts=[webllm-browser-inference.md] 10:53:46 同一块板、40 秒后创建的本任务 t_b1e3c641(本文):\ncreated {status: ready, parents: [], workspace_kind: scratch} 10:54:26 claimed {lock: \u0026#34;\u0026lt;host\u0026gt;:42910\u0026#34;, expires: \u0026lt;认领后900s\u0026gt;, run_id: 2} 10:54:30 spawned {pid: 53716} 10:54:30 heartbeat ×N(约 60 秒节奏) 10:54:41 → … 可以观察到的系统行为:\n两个任务先后被同一 dispatcher 认领、spawn 成独立进程,互不干扰——多任务并行是常态而非特例; run_id: 2 说明本卡已是第二次 run(首次可能因环境问题被回收/失败重排),而重试时 worker_context 会携带上次尝试的 summary——断点续跑的信息基础; 心跳稳定在分钟级,任何一次中断超过 1 小时都会触发调度器回收; 前一个任务把最终文章写进了卡外持久目录(/Users/wangjp/aicode/),而非会被删除的 scratch workspace,并在完成时声明为 artifact——这正是 §5 纪律的活教材。 9. 可靠性设计:什么让看板值得信任 拆开看,这套系统的可靠性来自几个正交的机制:\n持久化账本:SQLite 落盘 + 每步状态迁移成事件。进程崩溃、机器重启后,看板如实还原到最后一个事件——没有内存态可丢。 认领锁 + 过期回收:claim 带锁与过期,配合 stale 超时与心跳,处理 worker 蒸发(崩溃/休眠/被杀)。回收无惩罚重新排队,失败计数不增加——把\u0026quot;worker 死了\u0026quot;当作常态而非事故。 失败计数只记派生失败:真正计入 failure_limit 的是 spawn 本身失败(环境问题),而不是任务执行失败——执行失败通过重新排队 + 历史尝试上下文解决。 幻影引用拒绝:created_cards 里的 id 必须来自真实的 kanban_create 返回值,内核校验,防止交接信息引用不存在的卡。 产物存在性校验:artifacts 路径在完成时必须真实存在,缺失则任务保持 in-flight 让你修路径——杜绝\u0026quot;声称完成但文件不存在\u0026rdquo;。 结构化交接面:summary/metadata 的 schema 化让下游 worker 与自动化都能消费,而不是依赖解析自然语言。 对 AI 开发者的启示:这套设计与传统 job queue 的可靠性模型同构(持久化队列、租约/心跳、死信、幂等重试),区别在于执行单元是有推理能力的 Agent——所以它还多了\u0026quot;上下文携带\u0026quot;(历史尝试注入 worker_context)与\u0026quot;结构化交接\u0026quot;两层,这是纯函数式任务系统没有的需求。\n10. 上手:一条命令从零到有 CLI 入口是 hermes kanban \u0026lt;verb\u0026gt;(verb 全集以 hermes kanban --help 为准,以下为官方 skill 参考所列常见项):init、create、list/ls、show、assign、link/unlink、comment、complete、block/unblock、archive、tail、watch、stats、runs、log、dispatch、daemon、gc。\n最小闭环示例(命令行形态,参数名与 kanban_* 工具一致):\n# 1. 建板(若还没有) hermes kanban init --board blog # 2. 建一张卡,交给 researcher-a;等它做完再轮到 writer(父卡模式) hermes kanban create --board blog --title \u0026#34;调研 WebGPU 现状\u0026#34; --assignee researcher-a hermes kanban create --board blog --title \u0026#34;基于调研写文章\u0026#34; --assignee writer \\ --parents \u0026lt;调研卡id\u0026gt; --workspace worktree # 3. 观察 hermes kanban list --board blog # 状态总览 hermes kanban show \u0026lt;task_id\u0026gt; # 单卡全貌:事件/评论/历史尝试 hermes kanban tail \u0026lt;task_id\u0026gt; # 事件流 hermes kanban stats # 吞吐/耗时统计 # 4. 人工介入 hermes kanban comment \u0026lt;task_id\u0026gt; -m \u0026#34;补充验收标准:需要附录A\u0026#34; hermes kanban unblock \u0026lt;task_id\u0026gt; # 解除阻塞 想让自己(或自己写的工具)成为 worker,就把一个 profile 指给卡:dispatcher 会在该 profile 名下 spawn 会话,注入 HERMES_KANBAN_TASK/HERMES_KANBAN_WORKSPACE/HERMES_KANBAN_BOARD 环境变量与聚焦的 kanban_* 工具面。想用 LLM 直接驱动整条流水线,就把 orchestrator 卡交给 orchestrator profile,让它 fan-out——§7 的整套编排模式开箱即用。\n11. 局限与边界(诚实清单) 写到这里,也该说清楚它不是什么:\n不是分布式任务队列:默认单机、单 gateway。跨机器需要额外的后端编排(terminal backend 可配 docker/ssh/modal,但看板本身不是为跨地域吞吐设计的)。 不是实时调度器:dispatcher 周期性轮询,任务从 ready 到 spawn 有秒级延迟;不适合毫秒级任务分发。 评审/阻塞依赖人工:needs_input/capability 阻塞与 review 列都需要真人或显式配置的 reviewer profile 推动,否则任务停在原地。 Agent 执行不可完全预测:同一个卡重跑可能产出不同结果(LLM 本质);系统用\u0026quot;历史尝试注入上下文 + 结构化交接\u0026quot;缓解,但不保证确定性——设计流水线时要让下游对上游产出做校验而非盲信。 正文(body)是灵魂:调度器不负责理解任务,卡正文写得含糊,worker 只能阻塞或猜。规格质量直接决定 swarm 质量。 12. 结语 Kanban Swarm 给多 Agent 协作提供的不是又一个\u0026quot;Agent 聊天群\u0026quot;,而是一个有状态、有依赖、有账本、可交接的工作系统:把 Agent 当工人,把 SQLite 当车间地板,把事件日志当考勤表。对 AI 开发者而言,它的设计最值得借鉴的是三件事:用结构化交接面(schema 化的 summary/metadata)替代自然语言传话;用持久化状态机 + 租约心跳处理 Agent 进程的不可靠;用依赖门控把 DAG 显式建进任务结构。\n这套模式并不绑定 Hermes——任何想用多个 LLM Agent 可靠地协作完成长任务的系统,都可以照这个蓝图搭自己的看板。\n附录 A:发布前核验清单 本文写作环境无网络、无 shell,以下时效性内容未能现场核验,发布前请逐项确认(标注处为写作时依据的来源):\n版本号与功能归属:文中\u0026quot;v0.16\u0026quot;取自任务上下文标注;Kanban 在官方文档中的规范名称与 v0.16 release notes 请核对 https://github.com/NousResearch/hermes-agent 的 Releases 与 hermes --version。 官方 Kanban 文档页:https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban (写作时该 URL 来自随环境安装的官方 skill 参考 v3.2.0,内容未抓取核验)。 文档索引:https://hermes-agent.nousresearch.com/docs/llms.txt —— 一行一个功能的完整索引,查\u0026quot;kanban\u0026quot;可定位最新页面。 CLI 动词与参数:以本机 hermes kanban --help、hermes kanban \u0026lt;verb\u0026gt; --help 为准;配置键(dispatch_stale_timeout_seconds=4h、failure_limit=2、dispatch_in_gateway=true)以 hermes config 与文档为准。 默认值:心跳门限\u0026quot;1 小时\u0026quot;、\u0026ldquo;stale 4 小时\u0026rdquo;、\u0026ldquo;failure_limit 2\u0026rdquo;、\u0026ldquo;附件 25MB\u0026rdquo; 均来自官方 skill 参考与本文运行环境的系统协议文本,未对源码逐行核对。 数据路径:默认 ~/.hermes/kanban.db、boards 目录布局来自本机现场(boards/blog/workspaces/…),不同安装版本可能不同。 术语核对:官方文档对该功能的称呼(本文按任务标题使用 \u0026ldquo;Kanban Swarm\u0026rdquo;;skill 参考中称 \u0026ldquo;Kanban(multi-agent work queue)\u0026quot;)——发布前统一口径。 外部对照:§1 对比表中 delegate_task/cronjob 的行为边界来自官方 skill 参考,未抓取文档页复核。 附录 B:术语表 术语 含义 profile 一套独立的 Hermes 实例配置(模型/会话/技能/记忆),worker 的身份 dispatcher 常驻调度器:提升、认领、spawn、回收、熔断 worker dispatcher 在 assignee profile 名下派生的执行会话 claim 调度器对 ready 卡的原子认领,带锁与过期 run 一次调度执行;run_id 全局递增 parents / children 任务依赖边;child 在全部 parent done 前停留在 todo workspace_kind scratch / dir / worktree:worker 的落盘环境 heartbeat worker 的存活信号,超时门限约 1 小时 reclaim 回收僵死 run,无惩罚重新排队 artifact 完成任务时声明上传的交付文件(绝对路径,须真实存在) review run 一次评审轮转:request_review → complete / request_changes / block ","permalink":"https://smilex-blog.pages.dev/posts/hermes-v016-kanban-swarm/","summary":"\u003cblockquote\u003e\n\u003cp\u003e写作日期:2026-09-03。本文描述的系统行为均基于 2026-09-03 在作者本机正在运行的 Hermes Agent(任务上下文标注为 v0.16)上的\u003cstrong\u003e现场观察与第一手工具面\u003c/strong\u003e,而非照抄宣传材料——作者写作时正作为 dispatcher 派生的 worker 运行在该系统的看板上。凡未能现场核验的时效性内容(版本号、变更日志、外部系统细节)均已标注,并统一列入附录 A 核验清单,引用前请以官方仓库与文档为准。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"hermes-agent-v016-kanban-swarm-功能深度解析面向-ai-开发者\"\u003eHermes Agent v0.16 Kanban Swarm 功能深度解析(面向 AI 开发者)\u003c/h1\u003e\n\u003cp\u003e你写过这样的代码吗:一个 Agent 会话跑长任务,中途断网、进程被杀、上下文爆炸,一切归零;或者你想让\u0026quot;调研 Agent\u0026quot;和\u0026quot;写作 Agent\u0026quot;接力,却发现它们活在各自的进程里,谁也看不见谁。\u003c/p\u003e\n\u003cp\u003eHermes Agent v0.16 的 \u003cstrong\u003eKanban Swarm\u003c/strong\u003e 解决的就是这件事:用一块\u003cstrong\u003e持久化的 SQLite 看板\u003c/strong\u003e,把多个独立 Agent(每个是一个 \u003cem\u003eprofile\u003c/em\u003e,各自有独立的配置、会话、技能与记忆)组织成一支能接力、能并行、能失败重试、能跨进程存活的\u0026quot;工人队伍\u0026quot;。看板是它们唯一的共识层——任务状态、依赖关系、交接产物,全部落在磁盘上,进程死了看板还在。\u003c/p\u003e\n\u003cp\u003e这篇文章不打算复述 README。作者写作时正被 dispatcher 派生、以 worker 身份跑在这块看板上(任务 \u003ccode\u003et_b1e3c641\u003c/code\u003e),文中的状态机、工具面、调度语义来自对现场事件日志的直接观察与本文写作时实时可用的工具 schema;配置项与 CLI 动词来自随环境安装的官方 skill 参考(v3.2.0)。我们从数据模型讲起,一路拆到调度器、worker 协议与编排模式。\u003c/p\u003e\n\u003ch2 id=\"1-为什么-agent-需要一块看板\"\u003e1. 为什么 Agent 需要一块\u0026quot;看板\u0026quot;\u003c/h2\u003e\n\u003cp\u003e先给 Kanban Swarm 一个坐标系。Hermes 的多智能体能力按\u0026quot;存活时间\u0026quot;分成三层:\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e系统\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e形态\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e存活\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e典型用途\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003edelegate_task\u003c/code\u003e(委托)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e同一进程内派生子代理,隔离上下文\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e分钟级,\u003cstrong\u003e进程退出即丢\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e并行推理子任务、短时侦察\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003ecronjob\u003c/code\u003e(定时)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e独立会话按调度触发,结果投递\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e跨进程、持久\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e周期巡检、定时报告\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eKanban Swarm\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e跨 profile 的任务队列 + 工作区 + 事件账本\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e跨进程、持久、\u003cstrong\u003e可接力\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e多 Agent 流水线、长任务、需要人工/评审介入的工作流\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e关键分界在\u0026quot;接力\u0026quot;:delegate 的子代理不知道彼此的产出,父进程一死全部蒸发;cron 只解决\u0026quot;到点跑一次\u0026quot;。Kanban Swarm 解决的是\u003cstrong\u003e分工与交接\u003c/strong\u003e——任务卡在板上流转,任何 profile 派生出的 worker 都能认领、执行、写回结构化交接信息,下游任务在上游 \u003ccode\u003edone\u003c/code\u003e 之前不会启动。\u003c/p\u003e","title":"Hermes Agent v0.16 Kanban Swarm 功能深度解析(面向 AI 开发者)"},{"content":" 写作日期:2026-09-03。文中所有时效性信息(版本号、模型清单、性能数字)均标注了官方核验入口,引用前请以官方仓库最新状态为准。\n在浏览器里跑大模型:WebLLM 端侧推理深度解析 打开一个网页,等上十几秒,就能和一个完全运行在你电脑里的 80 亿参数模型对话——提示词不出设备、没有服务器账单、断网也能用。这不是演示动画,而是 WebLLM 这类项目已经做到的日常。\n这篇文章从编译器一路拆到浏览器 API,讲清楚三件事:模型是怎么被\u0026quot;编译\u0026quot;进浏览器的、推理时 GPU 上到底发生了什么、以及它离\u0026quot;替代云 API\u0026quot;还有多远。\n1. 为什么要把大模型塞进浏览器 大模型推理的主流形态是服务器端:你的输入上传到数据中心的 GPU,生成结果再传回来。它成熟、强大,但有四个结构性代价:\n隐私:提示词与输出都经过第三方服务器,敏感数据(医疗、法律、代码)天然不适合; 成本:按 token 计费,长会话、批量任务会持续产生费用; 延迟:每一轮都要走网络往返,且受服务器负载影响; 可用性:离线场景(飞机、偏远地区、内网)完全不可用。 端侧推理(On-Device Inference)是这些问题的答案——把模型放在用户自己的设备上跑。而浏览器是覆盖面最大的\u0026quot;端\u0026quot;:无需安装、天然跨平台、自动获得沙箱安全边界,用户只要打开一个 URL。过去浏览器推理是痴人说梦,因为缺一块拼图:GPU 通用计算能力。这块拼图在 2023 年补齐了,名字叫 WebGPU。\n2. 为什么是现在:WebGPU 补齐的拼图 浏览器跑神经网络的尝试由来已久,技术路线经历了三个阶段:\n纯 WASM 时代:把推理代码编译成 WebAssembly 在 CPU 上跑。可行,但只能吃 CPU,跑大模型慢得没有实用价值。 WebGL2 时代:WebGL 本质是图形 API,要拿它做通用计算,得把数据编码成纹理、把计算伪装成渲染,别扭且低效。 WebGPU 时代(2023 起):WebGPU 是现代图形 API(如 Vulkan/Metal/DX12)在浏览器里的投影,提供了真正的 compute shader(计算着色器) 和通用 buffer,让浏览器能直接、高效地驱动 GPU 做大规模并行数值计算——这正是 LLM 推理的核心形态。 对 LLM 推理而言,WebGPU 的三个特性缺一不可:\nCompute shader:矩阵乘法、注意力计算等核心算子可以直接写成 WGSL 计算内核,在 GPU 上大规模并行执行; 通用 buffer + 显式内存管理:权重可以驻留在 GPU 显存里反复读取,而不是每次从 CPU 拷贝; 低开销、贴近现代 GPU 抽象:相比 WebGL 的纹理解码绕路,性能损失小得多。 浏览器支持方面,Chrome/Edge 自 2023 年 5 月的 Chrome 113 起在桌面默认启用 WebGPU,是最稳妥的目标平台;Safari 自 18 起随系统提供 WebGPU;Firefox 也在 2025 年年中起逐步默认启用(先 Windows,后扩展到其他平台,具体版本以 Firefox 发布说明为准)。移动端与各浏览器实现细节差异较大,落地前务必用 webgpureport.org 或 caniuse 复核目标用户群的浏览器版本。\n3. 模型是怎么\u0026quot;走进\u0026quot;浏览器的:编译管线 很多人以为\u0026quot;浏览器跑模型\u0026quot;就是把 Hugging Face 上的 safetensors 权重下载下来,用某个 JS 库加载——这是最大的误解。现代推理引擎走的是另一条路:先在编译期把模型变成针对目标硬件优化的可执行产物,浏览器只负责运行它。\n3.1 从 PyTorch 权重到 WGSL 内核 WebLLM 属于 MLC-LLM 项目家族。MLC-LLM 构建在 Apache TVM(具体是 TVM Unity 运行时)之上,是一条模型编译管线:\n读入模型结构与权重(PyTorch/Hugging Face 格式); 经过 TVM 的图优化与算子编译,把模型翻译成针对 WebGPU 的计算内核(WGSL compute shader)与运行调度代码; 内核与 TVM runtime 一起打包成浏览器可加载的 wasm 产物(WebLLM 术语里叫 model_lib); 权重本身则被转换(含量化)成 MLC 的专属格式。 关键含义:模型与编译产物是一一对应的。 每个\u0026quot;模型 + 量化方式\u0026quot;组合,都对应一份专属的预编译产物。这就是为什么 WebLLM 不能像 transformers.js 那样\u0026quot;随便拿个 ONNX 模型就跑\u0026quot;——它的每个模型都要先经过 MLC 编译管线,这是它性能潜力的来源,也是它生态受限的原因(下文第 10 节展开)。\n3.2 量化:浏览器推理的生命线 权重多大,决定了模型能不能塞进浏览器。一个直观公式:权重体积 ≈ 参数量 × 每参数位宽 ÷ 8。80 亿参数 fp16 权重约 16GB,任何浏览器都扛不住;但压缩到 4-bit 后约 4~5GB,高端一点的设备就能接受。\nMLC 有一套自己的量化命名,形如 q4f16_1,拆开看:\nq4:权重量化到 4 bit; f16:scale(缩放因子)用 fp16 存储; _1:分组方式/变体编号(通常是按组量化,组内共享一个 scale)。 常见的还有 q3f16_1、q4f16_ft(调优变体)以及 q0f32(32 位未量化基线,用于对照)。注意:MLC 用的是自己的量化格式,不是 GGUF——GGUF 是 llama.cpp 生态的格式,MLC 侧与 GGUF 的关系只是\u0026quot;转换工具链\u0026quot;,WebLLM 运行时并不直接消费 GGUF。\n量化的意义在浏览器场景被放大,因为推理速度直接受内存带宽约束(详见第 7 节):权重瘦身不仅省显存,还直接提速。\n3.3 分发:模型库与按需下载 编译好的模型(权重 + 配套 wasm)发布在 Hugging Face 的 mlc-ai 组织下,仓库命名形如 Llama-...-q4f16_1-MLC。WebLLM 包内自带一张默认模型清单(config),应用可以按模型 id 直接引用,引擎负责下载权重、加载并运行。\n这种分发模式带来一个工程现实:首次冷启动要下载数百 MB 到数 GB 的权重。下载体验(进度、断点、缓存)是 WebLLM 应用的第一道用户体验关,官方 SDK 提供初始化进度回调,工程上通常还要配合缓存策略(第 5 节再谈)。\n4. 运行时解剖:推理时 GPU 上发生了什么 4.1 加载阶段 CreateMLCEngine(modelId) 这行代码背后是一连串动作:\n解析模型 id,定位权重与 wasm 产物的 URL; 下载权重 + 加载 wasm runtime(TVM 运行时); 请求 WebGPU 设备,把编译好的 compute shader 加载到 GPU; 把权重写入 GPU buffer,按配置分配 KV cache; 引擎就绪,开始响应请求。 初始化进度回调(initProgressCallback)会把每一步的进度推给 UI——在权重较大的场景,这一步可能持续数十秒,必须有良好的进度与失败(如不支持 WebGPU)呈现。\n4.2 Prefill 与 Decode:同一模型,两种截然不同的计算 大模型生成一次回复,内部其实是两个计算阶段:\nPrefill(预填充):一次性并行处理用户输入的整段 token,计算每个位置对后续输出的贡献。它是算力密集型的——矩阵乘法可以在 GPU 上千路并行,吃得越饱跑得越快。 Decode(解码):逐 token 自回归生成。每个新 token 都要读取全部权重,是带宽密集型的,几乎无法并行(串行依赖)。Decode 速度直接决定你看到的\u0026quot;打字机效果\u0026quot;有多流畅。 WebLLM 继承了这一套语义,并且会对超长输入的 prefill 做分块处理,避免一次提交把 UI 和 GPU 卡死。\n4.3 KV Cache:上下文从哪里来 模型能\u0026quot;记住\u0026quot;对话历史,靠的是 KV cache——把历史 token 的注意力中间结果缓存下来,避免每生成一个 token 就重算一遍。KV cache 的大小直接决定最大上下文长度,WebLLM 中由应用配置里的 kv_cache 参数(页/块数量)控制:页越多,能容纳的上下文越长,占用显存也越大。\n这就是为什么上下文长度不是免费的:想在浏览器里跑长文档分析,先回答显存够不够。\n4.4 流式输出 真实对话产品必须逐字吐出结果,而不是等全部生成完。WebLLM 的接口支持流式(stream),引擎在 worker 内逐增量生成并通过异步迭代器推回主线程,UI 边收边渲染,配合 WebGPU 的分块 prefill,观感上与云 API 的流式输出几乎一致。\n5. 为什么推理不能占着主线程:Worker 架构 浏览器的主线程负责渲染与交互。LLM 推理是重计算 + 可能持续数十秒的长任务,如果直接在页面主线程跑,结果就是:页面冻结、按钮点不动、滚动像幻灯片。\nWebLLM 的成熟形态是把引擎放进 Web Worker——一个与主线程并行、不阻塞 UI 的后台线程:\nWorker 内部创建真正的推理引擎,处理下载、GPU 初始化、prefill/decode; 主线程与 worker 之间通过消息通信,只交换\u0026quot;请求\u0026quot;与\u0026quot;增量结果\u0026quot;,页面全程流畅; 官方同时提供 service worker 方向的变体,用于更好地管理模型缓存与生命周期。 工程上还有一个容易被忽略的点:模型下载与 GPU 就绪的进度、失败重试、多会话并发都应该封装在 worker 层,让 UI 层只面对一个干净的异步接口。\n6. 上手:最小可用示例 以下代码基于 2024–2025 年间长期稳定的 v0.2.x API 形态。API 名称与模型 id 随版本演进,写代码前请对照官方 README 的示例。\n// 主线程极简用法(实际项目请用 worker,见下文) import * as webllm from \u0026#34;@mlc-ai/web-llm\u0026#34;; const engine = await webllm.CreateMLCEngine( \u0026#34;\u0026lt;模型 id,以官方 config.ts 清单为准\u0026gt;\u0026#34;, { initProgressCallback: (report) =\u0026gt; { console.log(\u0026#34;初始化:\u0026#34;, report.text, report.progress); }, } ); // OpenAI 风格对话接口 const reply = await engine.chat.completions.create({ messages: [{ role: \u0026#34;user\u0026#34;, content: \u0026#34;用一句话解释什么是 KV cache\u0026#34; }], stream: true, // 流式:返回异步迭代器,逐增量输出 }); for await (const chunk of reply) { process.stdout.write(chunk.choices[0]?.delta.content ?? \u0026#34;\u0026#34;); } // 切换模型 await engine.reload(\u0026#34;\u0026lt;另一个模型 id\u0026gt;\u0026#34;); 生产级形态是把引擎放进 worker:\n// worker.js —— 引擎真正运行的地方 import * as webllm from \u0026#34;@mlc-ai/web-llm\u0026#34;; new webllm.WebWorkerMLCEngineHandler(); // 主线程 —— 与 worker 对话 const engine = await webllm.CreateWebWorkerMLCEngine( new Worker(new URL(\u0026#34;./worker.js\u0026#34;, import.meta.url), { type: \u0026#34;module\u0026#34; }), \u0026#34;\u0026lt;模型 id\u0026gt;\u0026#34;, { initProgressCallback } ); 模型 id 不是随便填的——必须是官方预编译清单里的条目(权重和 wasm 都为你准备好了)。去官方 README 或 npm 包内的模型清单里挑一个与你的目标设备匹配的(显存小选小模型,追求质量选大模型)。\n7. 性能:天花板与瓶颈在哪里 不搬未经核验的数字,讲清楚决定性能的物理规律,你就知道该期待什么、该优化什么。\n7.1 Decode 是带宽游戏 自回归解码时,每生成一个 token 都要把全部权重从显存读一遍。于是:decode 速度 ≈ 显存带宽 ÷ 权重字节数。\n权重越小(q4 \u0026lt; fp16),每 token 读取的字节越少,速度越快——量化直接换速度; 显存带宽是硬指标,取决于用户的 GPU/集显,浏览器无法改变它; 这就是为什么 1B 模型能跑出比 8B 模型快数倍的体验:它每步只需读 1/8 的字节。 7.2 Prefill 是算力游戏 输入处理阶段是并行矩阵乘,受 GPU 算力(FLOPS)约束。分块 prefill 让长输入也能平滑推进,但总耗时与输入长度线性相关。\n7.3 浏览器层的额外开销 同硬件下,浏览器 WebGPU 推理通常慢于原生(CUDA/Metal)推理:驱动层抽象、提交与同步开销、无法像原生那样吃满全部硬件特性,都是损耗来源。差距有多大,请以官方 README 中带测量条件的 benchmark 表为准(注意口径:模型 + 量化 + 设备 + 浏览器 + 是否含下载时间,缺一不可)。\n7.4 冷启动:被低估的\u0026quot;性能\u0026quot; 用户感知的性能不只是 token/s,还有从打开页面到第一句话的时间:下载权重(数百 MB 到数 GB)+ 初始化 WebGPU + 加载内核 + prefill。对 8B 级模型,这可能是几十秒。工程上要用缓存、预加载、清晰的进度设计来消化这段等待。\n8. 隐私与安全:本地推理的承诺与边界 WebLLM 的安全模型很简单也很强:全程客户端推理,没有服务器转发。提示词与输出在设备上产生、在设备上消费,除模型下载本身外没有任何网络传输。对隐私敏感场景(本地文档分析、私有代码助手),这是云 API 给不了的承诺。\n几点诚实的边界:\n模型下载本身是网络行为:权重来自 Hugging Face 或你自建的镜像,供应链上仍可被观察(用哪个模型是可见的,内容不可见); 浏览器沙箱是双刃剑:页面被沙箱隔离,恶意网页无法越界读文件,但这也意味着推理无法直接访问本地任意资源(需要用户授权机制配合); 开源可审计:WebLLM(Apache-2.0)与 MLC-LLM 代码公开,安全团队可以审计推理链路本身。 9. 现实的局限:诚实清单 在决定\u0026quot;用 WebLLM 做生产\u0026quot;之前,把这些局限摆在桌面上:\n内存墙:整个权重必须驻留 GPU/浏览器内存,上下文越长 KV cache 越大。8B 级模型只适合内存充裕的设备,低端笔记本与手机直接出局或只能跑小模型; 下载墙:首次加载数百 MB 到数 GB,弱网环境劝退; 速度墙:同硬件不如原生推理,复杂任务(长文档、长生成)等待时间长; 单浏览器约束:依赖 WebGPU 支持与实现质量,worker 生命周期随页面刷新重置,移动端能力更弱; 生态墙:模型必须经过 MLC 编译管线,官方清单之外的新模型需要自己编译(见下节对比); 无服务端管理:没有集中式的用量统计、模型热更新、A/B 与审计,运营能力要自己补。 10. 生态对照:WebLLM 不是唯一的浏览器推理路线 路线 引擎基础 强项 代价 WebLLM MLC/TVM 深度编译 → WGSL 内核 为 WebGPU 逐模型定制,吞吐上限高,prefill/KV cache 控制精细 模型需专属编译,清单受官方支持面约束 transformers.js ONNX Runtime(WebGPU/WASM/WebNN) 生态广、模型多(含多模态),上手快,HF 全家桶无缝 通用 ONNX 内核非逐模型定制,极致性能通常不及深度编译路线 llama.cpp Web/GGUF 移植 llama.cpp 编译到 WASM/WebGPU GGUF 权重即下即用,社区活跃 浏览器端工程成熟度与调优参差 ONNX Runtime Web 微软 ORT 的 Web 后端 底层引擎级能力,可直接调用 偏底层,需要自己搭上层 一句话选型:要生态广度与上手速度选 transformers.js,要 WebGPU 上的极致性能与精细控制选 WebLLM;GGUF 系适合\u0026quot;权重现成优先\u0026quot;的玩家,ONNX Runtime Web 则是自建引擎的地基。 这不是谁取代谁的关系——同一产品完全可以按模型类型混用。\n11. 值得盯的方向 以下方向代表了浏览器端推理的演进趋势,是否/何时进入 WebLLM 官方能力,以仓库的 News、Releases 与 CHANGELOG 为准:\n多模态:把视觉等非文本输入纳入浏览器推理(历史上 WebLLM 以文本为主); 结构化输出 / JSON mode:让模型输出可被程序直接消费的格式,是 Agent 类应用的前提; 推测解码等加速技术:用一个小草稿模型猜多个 token 再让大模型一次验证,绕开 decode 的串行瓶颈; 模型本地缓存:利用 OPFS(源私有文件系统)等能力把权重持久化在本地,让\u0026quot;二次打开秒加载\u0026quot;; WebGPU 跨浏览器收敛:随着 Safari/Firefox 的实现成熟,碎片化问题逐步缓解。 12. 结语 WebLLM 把\u0026quot;模型编译\u0026quot;这门通常属于数据中心的技术,搬到了每一个用户的浏览器里。它用 TVM/MLC 的深度编译换来了浏览器内推理的上限,也用这种方式提醒我们:端侧 AI 的竞争本质上是生态广度与硬件效率之间的权衡。\n对你来说,值得记住的判断框架是:隐私敏感、设备可控、模型不必最大的场景,浏览器推理已经可以上岗;而它每一年都在变得更小、更快、更省——下次打开那个网页时,等待的时间大概又短了一点。\n附录 A:时效性核验清单 本文写作于 2026-09-03,以下信息变化快,引用前请现场核验:\nnpm 最新版本与发布日期:https://www.npmjs.com/package/@mlc-ai/web-llm 模型清单与官方示例代码:https://github.com/mlc-ai/web-llm (README + src/config.ts) Release / 近期功能(多模态、结构化输出、推测解码等):https://github.com/mlc-ai/web-llm/releases 与 CHANGELOG 性能 benchmark(注意设备/口径):WebLLM README benchmark 章节 + https://mlc.ai/blog/ WebGPU 浏览器支持:https://webgpureport.org 、https://caniuse.com/webgpu 编译工具链(自定义模型时用):https://llm.mlc.ai/docs/ 、https://github.com/mlc-ai/mlc-llm 附录 B:参考链接 WebLLM 仓库:https://github.com/mlc-ai/web-llm MLC-LLM 仓库:https://github.com/mlc-ai/mlc-llm 预编译模型库:https://huggingface.co/mlc-ai 官方文档:https://llm.mlc.ai/docs/ MLC 博客(WebGPU-LLM 系列):https://mlc.ai/blog/ transformers.js:https://github.com/huggingface/transformers.js ONNX Runtime Web:https://onnxruntime.ai/ llama.cpp:https://github.com/ggml-org/llama.cpp ","permalink":"https://smilex-blog.pages.dev/posts/webllm-browser-inference/","summary":"\u003cblockquote\u003e\n\u003cp\u003e写作日期:2026-09-03。文中所有时效性信息(版本号、模型清单、性能数字)均标注了官方核验入口,引用前请以官方仓库最新状态为准。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"在浏览器里跑大模型webllm-端侧推理深度解析\"\u003e在浏览器里跑大模型:WebLLM 端侧推理深度解析\u003c/h1\u003e\n\u003cp\u003e打开一个网页,等上十几秒,就能和一个完全运行在你电脑里的 80 亿参数模型对话——提示词不出设备、没有服务器账单、断网也能用。这不是演示动画,而是 WebLLM 这类项目已经做到的日常。\u003c/p\u003e\n\u003cp\u003e这篇文章从编译器一路拆到浏览器 API,讲清楚三件事:\u003cstrong\u003e模型是怎么被\u0026quot;编译\u0026quot;进浏览器的、推理时 GPU 上到底发生了什么、以及它离\u0026quot;替代云 API\u0026quot;还有多远\u003c/strong\u003e。\u003c/p\u003e\n\u003ch2 id=\"1-为什么要把大模型塞进浏览器\"\u003e1. 为什么要把大模型塞进浏览器\u003c/h2\u003e\n\u003cp\u003e大模型推理的主流形态是服务器端:你的输入上传到数据中心的 GPU,生成结果再传回来。它成熟、强大,但有四个结构性代价:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e隐私\u003c/strong\u003e:提示词与输出都经过第三方服务器,敏感数据(医疗、法律、代码)天然不适合;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e成本\u003c/strong\u003e:按 token 计费,长会话、批量任务会持续产生费用;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e延迟\u003c/strong\u003e:每一轮都要走网络往返,且受服务器负载影响;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e可用性\u003c/strong\u003e:离线场景(飞机、偏远地区、内网)完全不可用。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e端侧推理(On-Device Inference)是这些问题的答案——把模型放在用户自己的设备上跑。而\u003cstrong\u003e浏览器是覆盖面最大的\u0026quot;端\u0026quot;\u003c/strong\u003e:无需安装、天然跨平台、自动获得沙箱安全边界,用户只要打开一个 URL。过去浏览器推理是痴人说梦,因为缺一块拼图:\u003cstrong\u003eGPU 通用计算能力\u003c/strong\u003e。这块拼图在 2023 年补齐了,名字叫 WebGPU。\u003c/p\u003e\n\u003ch2 id=\"2-为什么是现在webgpu-补齐的拼图\"\u003e2. 为什么是现在:WebGPU 补齐的拼图\u003c/h2\u003e\n\u003cp\u003e浏览器跑神经网络的尝试由来已久,技术路线经历了三个阶段:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e纯 WASM 时代\u003c/strong\u003e:把推理代码编译成 WebAssembly 在 CPU 上跑。可行,但只能吃 CPU,跑大模型慢得没有实用价值。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWebGL2 时代\u003c/strong\u003e:WebGL 本质是图形 API,要拿它做通用计算,得把数据编码成纹理、把计算伪装成渲染,别扭且低效。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWebGPU 时代(2023 起)\u003c/strong\u003e:WebGPU 是现代图形 API(如 Vulkan/Metal/DX12)在浏览器里的投影,提供了真正的 \u003cstrong\u003ecompute shader(计算着色器)\u003c/strong\u003e 和通用 buffer,让浏览器能直接、高效地驱动 GPU 做大规模并行数值计算——这正是 LLM 推理的核心形态。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e对 LLM 推理而言,WebGPU 的三个特性缺一不可:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eCompute shader\u003c/strong\u003e:矩阵乘法、注意力计算等核心算子可以直接写成 WGSL 计算内核,在 GPU 上大规模并行执行;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e通用 buffer + 显式内存管理\u003c/strong\u003e:权重可以驻留在 GPU 显存里反复读取,而不是每次从 CPU 拷贝;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e低开销、贴近现代 GPU 抽象\u003c/strong\u003e:相比 WebGL 的纹理解码绕路,性能损失小得多。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e浏览器支持方面,Chrome/Edge 自 2023 年 5 月的 Chrome 113 起在桌面默认启用 WebGPU,是最稳妥的目标平台;Safari 自 18 起随系统提供 WebGPU;Firefox 也在 2025 年年中起逐步默认启用(先 Windows,后扩展到其他平台,具体版本以 Firefox 发布说明为准)。\u003cstrong\u003e移动端与各浏览器实现细节差异较大,落地前务必用 webgpureport.org 或 caniuse 复核目标用户群的浏览器版本。\u003c/strong\u003e\u003c/p\u003e","title":"在浏览器里跑大模型:WebLLM 端侧推理深度解析"}]