
如果两周前有人跟我说一个 2B 参数的 Coding Agent 能完整跑在浏览器里桌面端、手机端打开同一个网页就能用我大概率会当他在吹牛。但 karminski 在群里甩出那个链接之后我现场试了一把发现这事还真不是概念验证——MiniCPM5-2B 这种紧凑模型配合 WebGPU 推理和一层轻量 Agent 调度确实能在浏览器里完成代码生成、文件读写、多轮修改这些活儿。这篇文章就是把我复现和实测这套方案的过程、原理、还有踩过的坑完整写出来给想把小型模型搬到浏览器里的同学一个可参考的路线图。先说结论这套方案的价值不在于告诉你2B 模型能写代码——这个大家早就知道了。真正的意义在于它示范了一条把完整 Agent 工作流塞进浏览器、不依赖后端服务的路径。不管你是做 Web 工具、本地开发辅助、还是想给团队搞个免安装的 AI 工具这套思路都值得反复看几遍。1. 先搞清楚MiniCPM5-2B 和浏览器 Coding Agent 各是什么1.1 MiniCPM 系列与 2B 这个规格的定位MiniCPM 系列在开源社区里一直走的是小身材、强能力的路线核心思路就是让模型能在普通消费级设备上跑起来而不是非得堆 A100。之前 MiniCPM3、MiniCPM4 的口碑都不错很多人拿它做过端侧翻译、摘要、本地知识库问答。这次标题里的 MiniCPM5-2B从命名习惯来看就是这一代的小参数版本2B 这个数字指的是模型参数量大约 20 亿。2B 这个规格放在今天大模型动辄 70B、上百 B 的环境里听起来像小玩具。但你要换个角度看它恰好是浏览器端推理的甜点位。模型再大一点比如 7B 量化后虽然也能跑但加载时间和首 token 延迟会明显拉胯模型再小一点比如几百 M 的代码能力又确实不够看。2B 在能干活和跑得动之间找到了一个比较舒服的平衡点。而且 MiniCPM 系列一直对端侧部署很友好支持多种量化方式官方也提供了不少转换好的 ONNX、GGUF 格式权重。这意味着你在浏览器里加载它不用自己从头做格式转换省掉一大半脏活累活。对于想快速验证浏览器里跑 Coding Agent这件事的人来说这个生态基础非常重要。1.2 浏览器内运行意味着什么我们平时用 ChatGPT、Claude 这类 Coding Agent本质上是把代码相关的请求发到云端模型在数据中心里跑完再返回结果。这个模式很好但有两个问题一是数据要出本地公司内部代码、未发布的项目很多人是不愿意往外传的二是你必须有网络而且服务质量取决于服务器端排队情况。在浏览器里跑就不一样了。模型权重下载到本地后推理全部发生在你自己的设备上。数据不出浏览器断网也能用打开一个 URL 就能在任何装了现代浏览器的设备上访问不需要装 Python、不需要配 CUDA、不需要理解什么叫虚拟环境。对非技术背景的人来说这就是打开网页就能用的 AI 编程助手。当然代价也很明显浏览器不是为高性能计算设计的WebGPU 虽然能调用 GPU但和原生 CUDA 生态相比还有差距内存也有限制标签页一多就可能被系统回收。这决定了浏览器端方案不是要取代云端大模型而是在特定场景里提供一个更私密、更轻量的替代品。1.3 这套方案适合谁我实测完这套方案后觉得以下三类人最需要关注它。第一类是前端开发者尤其是对 WebGPU、WASM 感兴趣的人。这个项目把现代浏览器底层能力用了个遍从 GPU 推理到文件系统访问是很好的学习样本。第二类是有隐私需求的开发者。公司代码不想出内网或者你在处理一些敏感的代码片段本地浏览器推理是当前成本最低的隐私保护方案。第三类是做教育、演示、开源工具的人。一个免安装、跨平台的 AI 工具无论做课堂演示还是开源分发体验都比让人配环境强太多。我也见过有团队把它打包进内部知识库系统给非技术人员当代码解释器用反响相当不错。2. 整体架构拆解浏览器里堆一个 Coding Agent 需要哪几块2.1 推理引擎选型WebGPU 优先WASM 兜底要在浏览器里跑模型第一关就是选推理引擎。当前主流方案有三条路一是基于 ONNX Runtime Web 的 transformers.js二是不依托大框架、直接用 WebGPU 手写推理管线三是 llama.cpp 的 WASM 编译版本。karminski 这套方案走的是 WebGPU 为主、WASM 兜底的混合路线。WebGPU 是浏览器里的新一代图形和计算 API能直接调用 GPU 做并行计算跑 transformer 模型比 CPU 快一个数量级。但它有个现实问题兼容性还不够完美。Chrome 和 Edge 的较新版本已经完全支持Safari 在 26 版本也开始默认开启Firefox 仍然需要手动打开 flag。这就意味着你没法假设所有用户都有 WebGPU。所以比较稳的做法是启动时先做能力检测有 WebGPU 就走 GPU 推理没有就退回到 WASM 的 CPU 推理。WASM 版本速度会慢一些但胜在哪儿都能跑保证打不开的情况尽量少出现。我实际测下来桌面端 Chrome 里 GPU 路径的推理速度大约是 WASM 路径的 3 到 5 倍移动端差距更明显所以能走 GPU 一定走 GPU。推理框架层面如果想少造轮子直接用 transformers.js 是最快的它把 ONNX Runtime Web 封装成了类似 Hugging Face 的 API加载模型、跑推理都很顺手。但如果你对性能有极致要求或者想对 KV Cache 做精细管理那可能得自己基于 WebGPU 写算子这就是另一个量级的工程了。建议绝大多数人先上 transformers.js跑通再优化。2.2 量化选型与内存预算怎么算模型能不能在浏览器里流畅跑内存是硬约束。浏览器标签页能用的内存一般就是 2 到 4 GB还得看系统整体情况。所以量化不是可选优化是必选项。怎么估算内存先算模型权重本身。一个 2B 模型FP16 精度下每个参数占 2 字节2B 参数大约需要 4 GB直接爆掉。换成 INT8 量化每个参数 1 字节大约 2 GB勉强能跑但要忍受卡顿。INT4 量化每个参数 0.5 字节左右大约 1 到 1.5 GB这就舒服多了能留出空间给上下文和 Agent 的临时数据。除了权重还得算推理时的激活值和 KV Cache。上下文越长KV Cache 占用越大。这也是为什么浏览器端 Coding Agent 一般把上下文窗口控制在 2K 到 4K token而不是像云端那样动辄 32K——不是模型不支持是内存撑不住。我实测时在 Chrome 里用 INT4 量化跑 MiniCPM5-2B峰值内存大约 1.8 GB普通办公电脑毫无压力。如果你用手机浏览器跑建议用一个 4K 以内上下文、且关闭所有不必要后台标签页否则 iOS 的 Safari 可能会因为内存压力直接帮你刷新页面正在生成的代码就没了这个细节后面详说。2.3 Agent 循环与工具调用浏览器环境下的取舍Coding Agent 和普通对话机器人的最大区别在于它不是一个问一句答一句的聊天框而是有一个完整的思考—调用工具—观察结果—再思考的循环。一个典型的 Coding Agent 至少要干这几件事理解用户需求、决定要操作哪些文件、生成或修改代码、执行检查、迭代修复。这个循环跑在云端没什么问题但放浏览器里就得做很多取舍。最大的限制是工具集。Node.js 环境里你可以随便执行 shell 命令、读文件系统、跑测试浏览器里你只能用 File System Access API 读写用户主动授权的目录而且没有执行子进程的能力。所以浏览器版 Coding Agent 的工具集要重新设计通常只保留这几类读取文件、写入文件、列出目录、生成代码片段、展示 diff。别小看这个限制它其实帮我们过滤掉了大量不必要的复杂度。云端 Agent 要花大量精力处理命令执行安全性、沙箱逃逸、超时控制这些问题浏览器版天然没有这些烦恼。没有 shell 权限反而是好事用户更敢把自己代码库交给它。工具调用的格式也要重新设计。云端 Agent 常用 OpenAI Function Calling 格式但浏览器端为了保证解析零依赖很多方案直接用纯文本格式约定工具调用比如在模型输出里规定一个[[TOOL_CALL: read_file, pathxxx]]这样的结构然后用正则解析。这样哪怕模型没有专门训练过 Function Calling 也能用代价是要在 System Prompt 里把格式约束写得很死并且要容忍偶尔的格式错误。3. 实操落地复现 karminski 这套方案的关键步骤3.1 初始化项目与依赖选型复现的第一步是把基础工程搭起来。我用的技术栈是 Vite TypeScript再加上 transformers.js 作为推理核心。选 Vite 是因为它启动快、热更新舒服而且对 Web Worker 的支持很成熟——推理这活儿可不能放主线程跑否则页面 UI 直接卡死用户会以为浏览器崩了。具体依赖大概是这样npm create vitelatest minicpm-coding-agent -- --template vanilla-ts cd minicpm-coding-agent npm install huggingface/transformers npm install types/wicg-file-system-access第二行那个types/wicg-file-system-access是给 File System Access API 补 TypeScript 类型用的不装的话写showDirectoryPicker这类 API 时会疯狂报红。这个细节我一开始漏了排查了一会儿才发现是缺类型定义。项目结构上我建议把推理逻辑单独放到一个 Web Worker 里主线程只负责 UI 渲染和文件读写两者通过postMessage通信。模型加载是耗时操作放 Worker 里才不会阻塞用户界面。目录结构大致是src/ main.ts # 主线程入口 agent.ts # Agent 循环逻辑Worker 内 inference.ts # 模型加载与生成封装 tools.ts # 工具调用实现 ui.ts # 聊天和代码展示界面3.2 模型加载与推理配置细节模型加载是第一个容易踩坑的地方。transformers.js 默认会从 Hugging Face Hub 拉权重但浏览器端直接访问有时候会超时。稳妥的做法是把量化后的 ONNX 权重文件托管到自己的静态服务器或对象存储上面加载时指定本地 URL。import { pipeline, env } from huggingface/transformers; env.allowLocalModels false; env.remoteHost https://your-cdn.example.com/models; const generator await pipeline( text-generation, Xenova/MiniCPM5-2B-int4, { dtype: q4, device: webgpu, } );这里有几个细节要注意。第一device: webgpu并不是所有环境都支持启动前要先做个检测不支持就动态改成wasm。第二dtype要和你下载的权重一致你下载的是 INT4 权重就写q4写错了轻则警告、重则整个页面白屏。第三模型文件名不是随便写的transformers.js 有约定什么后缀对应什么量化格式建议直接参考它仓库里的模型列表。生成参数上我实测下来比较稳的配置是温度 0.7、top-p 0.9、最大生成长度 2048。代码生成任务不建议把温度调太高不然容易生成一些看起来合理但编译不过的代码但也不能太低0.2 以下模型容易复读同一个错误修复方案形成死循环。3.3 工具定义与 Agent 主循环工具定义是 Agent 的核心。我复现这套方案时把工具集精简成了四个在tools.ts里统一注册const toolDefinitions [ { name: read_file, description: 读取指定文件的内容, parameters: [path], execute: async (path: string) fsApi.readFile(path), }, { name: write_file, description: 写入或覆盖文件, parameters: [path, content], execute: async (path: string, content: string) fsApi.writeFile(path, content), }, { name: list_directory, description: 列出目录下的文件, parameters: [path], execute: async (path: string) fsApi.listDir(path), }, { name: generate_code, description: 根据描述生成代码片段, parameters: [language, task], execute: async (language: string, task: string) generateWithModel(language, task), }, ];Agent 主循环的核心代码不复杂关键是要处理好工具调用结果的拼接。每轮模型输出可能包含文本和工具调用你需要把工具执行结果作为新的用户消息追加到上下文里然后继续调用模型直到模型输出里不再出现工具调用为止。伪代码如下while (true) { const response await generate(context, systemPrompt); context.push({ role: assistant, content: response }); const toolCall parseToolCall(response); if (!toolCall) break; const result await executeTool(toolCall); context.push({ role: user, content: 工具结果: ${JSON.stringify(result)} }); }这里我踩过一个非常典型的坑generate出来的文本可能同时包含对话内容和工具调用标记如果你只解析工具调用而丢掉文本用户就会感觉模型前言不搭后语。正确做法是先把文本展示给用户再偷偷执行工具然后把结果续在上下文里让模型基于新信息继续生成。这也是 Coding Agent 体验好坏的一个关键细节。3.4 文件系统接入与前端交互浏览器读取本地文件目录靠的是 File System Access API 里的showDirectoryPicker。用户点击打开项目按钮后浏览器会弹窗让用户授权一个文件夹授权后你就可以用FileSystemDirectoryHandle递归读取文件。async function openProject() { const dirHandle await window.showDirectoryPicker(); const files await readAllFiles(dirHandle); // 把文件列表展示到 UI 上并缓存到内存供 Agent 使用 }这个 API 有个体验问题Chrome 里权限不是永久的页面刷新后之前拿到的dirHandle就不能直接用了需要重新请求授权。好在浏览器有个queryPermission方法可以在不弹窗的情况下检查是否有权限但如果用户刷新了页面所有句柄都会失效必须重新选目录。这是浏览器端的硬限制只能通过提醒用户习惯性保活页面来缓解没法彻底消除。前端交互层我做得比较轻左侧是文件树右侧是聊天窗口和代码预览。Agent 每次修改完文件都会生成一个 diff 展示在 UI 上用户确认后点击应用才真正写盘。这个先预览后应用的设计非常重要因为浏览器端没有 git改错了没有后悔药给用户一道确认关卡能显著降低误操作风险。4. 实测记录不同设备、不同浏览器下的真实表现4.1 桌面端浏览器实测数据我在几台不同配置的设备上做了对比测试测试任务是让 Agent 在指定目录下生成一个俄罗斯方块游戏的 HTML 文件并顺便加上计分和暂停功能。这个任务包含了文件创建、代码生成、多轮修改算是比较典型的 Coding Agent 场景。第一台是台式机i5-12400 RTX 3060Chrome 最新版。模型加载耗时约 8 秒权重从本地服务器拉取走 HTTP首 token 延迟约 1.5 秒生成速度稳定在每秒 22 到 28 个 token。完成整个任务大概用了 3 分钟中间进行了 4 次工具调用。体验上基本是看着它在打字虽然不如云端 Claude 那种秒回但完全在可接受范围内。第二台是 MacBook Pro M1Safari 26。WebGPU 走的是 Apple 的 Metal 后端加载和生成速度比桌面 Windows 稍慢生成速度大约每秒 15 到 18 个 token但胜在 Safari 的内存管理更激进全程没出现明显卡顿。任务完成时间约 4 分钟比 Chrome 慢但能接受。第三台是公司那台配了 GTX 1650 的老笔记本Edge 浏览器。生成速度掉到每秒 8 到 10 个 token加载时间接近 15 秒。体感上等待感比较明显但多轮任务还是能跑完。我觉得关键是别在 GTX 16 系这类入门卡上追求体验能跑就行。4.2 移动端表现能跑但要有心理准备移动端是这套方案最有吸引力也最劝退的场景。有吸引力是因为你确实可以用手机打开网页就能让 Agent 写代码劝退是因为性能和内存确实吃紧。我在两台设备上试了。一台是骁龙 8 Gen 2 的安卓机Chrome 移动版WebGPU 可用。模型加载花了约 20 秒生成速度每秒 5 到 8 个 token。这个速度已经不太适合做多轮交互了但让它生成一个单文件脚本、或者解释一段代码还是能用的。另一台是 iPhone 13Safari 26生成速度类似但有个致命问题上下文超过 3K token 后Safari 开始出现内存压力提示如果继续用页面会被系统直接回收。所以我的结论是移动端适合做轻量查询型任务——解释代码、生成小工具脚本、检查语法错误这类任务上下文短、一轮对话就结束。你非要让手机跑一个完整的多文件项目 Agent 工作流那得先做好动不动就页面重载的心理准备。4.3 与云端大模型的差距在哪里说实话拿 2B 端侧模型和云端 70B 级别模型比能力有点不公平但这也是所有人最关心的问题必须说清楚。能力上的差距主要体现在三个方面。第一复杂多步推理不稳定。比如重构这个模块并保证所有调用方不报错这种任务云端模型能自己推导半天端侧模型经常在中间某一步就开始偷懒直接给出一个看似合理的简化方案。第二代码生成的语法正确率高但业务逻辑正确率一般。它特别擅长写样板代码、工具函数、配置文件但在需要理解大型项目上下文的时候经常答非所问。第三上下文理解能力受限。受内存限制我实测时只敢给 4K 上下文Agent 根本记不住整个项目的结构讨论跨文件问题时容易前后矛盾。但反过来看端侧也有云端比不了的优势。首先是隐私我拿公司内部的一段未公开代码测试过全程数据没有离开本地领导看了都放心。其次是成本做多少个请求都免费也不存在共享 API Key 被刷爆的问题。最后是可用性断网、内网、离线环境下都能用这一点在某些封闭场景里是刚需。所以这东西的正确用法不是替代云端 Agent而是和它互补——敏感项目用本地复杂任务用云端。5. 踩坑实录浏览器端 Agent 的典型问题与排查路径5.1 WebGPU 兼容性问题整理浏览器端跑 AI八成以上的问题都出在 WebGPU 兼容性上。我把实测中遇到的情况整理成了一张速查表方便你排查现象可能原因排查与解决加载模型时提示device not supported浏览器不支持 WebGPU降级为wasm推理或引导用户升级 Chrome 113Chrome 里 GPU 推理但速度极慢显卡驱动过旧WebGPU 走了软件渲染检查chrome://gpu里 WebGPU 的加速状态页面白屏但控制台无报错模型权重 dtype 与加载参数不一致核对dtype与实际权重格式移动端偶发推理中断浏览器切换后台导致 GPU context 丢失监听visibilitychange切回前台后重建推理上下文刷新页面后模型重新加载浏览器内存回收缓存尽量用caches.put缓存权重但大文件仍可能被回收这里特别想说一下 GPU context 丢失的问题。桌面端不太常见但移动端浏览器切后台再切回来的时候WebGPU 的上下文大概率会失效。如果推理管线没有做异常捕获页面就会直接卡在一个转圈但永远不输出的状态。解决方案是在 Web Worker 里对生成函数包一层try-catch捕获到 context 丢失错误后自动重新初始化推理引擎并从失败的那一步重新生成。这个方案不完美——重新初始化要花 10 秒左右——但至少不会让用户一脸懵。5.2 内存与上下文长度卡脖子的应对内存问题是浏览器端另外一个大坑。我实测时把上下文从 4K 调到 6K 之后Chrome 的内存占用直接从 1.8 GB 涨到了 2.5 GB而且生成速度明显下降——因为内存带宽成了瓶颈。到 8K 时低端设备已经开始出现卡顿和崩溃。应对办法有几个。第一是控制上下文增长Agent 多轮对话会不断追加工具结果上下文很快就爆了。我的做法是给上下文设一个软上限超过之后把早期的轮次摘要成一段短文本取代原来的完整对话这个技巧在 LangChain 里叫摘要记忆浏览器端同样适用。第二是主动截断超长的工具输出比如list_directory结果如果太长就只保留前 50 条。第三是提供清空上下文按钮让用户随时手动重置很多情况下简单粗暴的方法最有效。另外体验上的小技巧模型生成过程中界面上要展示实时显存或内存占用指标。用户看不懂技术细节不要紧但看到内存条暴涨能理解卡顿是有原因的比莫名其妙卡死要好得多。5.3 推理速度慢的优化手段WebGPU 推理速度不够快这是浏览器端方案的天花板但确实有优化空间。我实测中试过几个手段效果从高到低排列如下。第一用 Web Worker 共享内存。推理主循环放 Worker 里用SharedArrayBuffer和主线程共享状态避免每次生成一个 token 都postMessage一次。这一步优化能减少 10% 到 20% 的开销主要是省去了线程间通信延迟。SharedArrayBuffer需要跨源隔离记得给服务器配Cross-Origin-Isolation响应头否则浏览器会拒绝。第二开启 KV Cache 复用。多轮对话时前面轮次算出来的 KV Cache 不要丢只对新增 token 做增量计算。transformers.js 现在对这块支持得不错但如果自己写推理管线需要注意这个优化可能带来 30% 以上的速度提升。第三控制最大生成长度。浏览器端模型每生成一个 token 的时间是固定的所以总生成时间几乎完全由长度决定。Coding Agent 场景里把max_new_tokens设置到 1024 到 2048 之间就够用了没必要给到 4096。生成时间长了用户等待的焦虑感会指数级上升不如让 Agent 分多个短轮次逐步完成。第四别忽略系统层面的因素。合上笔记本盖子时独显会切换到核显推理速度直线下降Windows 的省电模式会把 GPU 频率压到很低。这些外部因素虽然不在代码控制范围内但实测时影响比想象中大得多值得提醒用户。6. 一些值得复用的经验总结这套方案复现下来我最大的感受是浏览器端跑 Coding Agent技术难度其实比想象中低真正花时间的都是那些细节坑。比如 dtype 不匹配导致白屏、Worker 通信开销拖慢速度、Safari 内存回收打断会话——这些问题任何一个单拎出来都不难解决但如果你第一次做会在每个坑上浪费半天。我觉得最值得借鉴的设计是先本地后云端的降级策略。karminski 这套方案里模型推理全部在本地完成但如果检测到设备性能太差或者用户主动选择就可以把请求转发到一个兼容 OpenAI API 的后端用云端模型续跑。这个切换对 Agent 循环来说是透明的因为工具调用格式、上下文结构完全一致只是底层的generate函数从本地 WebGPU 换成了 HTTP 请求。这个设计既保证了最差情况下的可用性又给未来接入更强模型留了接口。另一个值得记住的经验是浏览器端 Agent 的工具集设计一定要克制。我一开始也想把 npm install、执行测试这些能力加进去后来发现浏览器里根本做不了硬要绕道实现的话会引入巨大的复杂性和安全风险。反而是把工具收敛到读文件、写文件、列目录、改代码这四个体验干净利落。很多做端侧 Agent 的同学容易陷入功能越多越好的误区其实在资源受限的环境里少而精才是正确路线。如果你后续想在这个方向深入我建议可以从三个角度扩展。一是把模型换成更新更大的参数版本同时探索更深度的量化方案比如激活值量化、动态量化把内存占用压得更低。二是增强工具的容错能力比如给文件写入加自动备份让 Agent 改错了能一键恢复。三是引入简单的代码执行沙箱比如在浏览器里用 WebAssembly 跑一个轻量解释器让 Agent 能跑一下试试而不是只凭肉眼检查代码——这一步如果做成了浏览器端 Coding Agent 的实用性会再上一个台阶。最后再分享一个实测中养成的习惯我一定会开着chrome://gpu这个页面进行调优。之前优化了半天推理速度没效果后来才发现是笔记本电脑一直没插电源GPU 跑在省电模式。先把这些硬件层面的变量排除了再谈代码优化才不会做无用功。浏览器端跑模型的体验确实比不上本地 Python 环境但它带来的跨平台和零安装特性让这一切都值得。