我花了3个月,把一个终端 AI Agent 搬进了浏览器——踩坑全记录
一、为什么要把终端 AI Agent 搬进浏览器2025年初我在终端里跑通了一个基于 LangChain 的 AI Agent能自动执行代码、调用工具、处理文件。功能很强大但每次演示都要打开终端、激活虚拟环境、敲命令——同事看了直摇头「这玩意儿能不能在浏览器里点一下就跑」于是我决定花3个月时间把这个终端 AI Agent 完整地搬到浏览器里。这篇文章记录了我踩过的所有坑希望能帮你少走弯路。二、技术选型从 WebSocket 到 Web Worker第一个问题就是终端里的 Agent 是 Python 写的浏览器里跑不了 Python。怎么办方案一后端代理 WebSocket最直接的想法后端起一个 Python 进程浏览器通过 WebSocket 发送指令后端执行后把结果推回来。这个方案我用了两周踩了三个坑连接管理每个用户一个进程服务器内存直接爆炸状态同步Agent 的中间状态思考链、工具调用很难实时推给前端断连恢复浏览器刷新一下Agent 状态全丢了方案二WebAssembly Python 运行时用 Pyodide 在浏览器里直接跑 Python试了之后发现Pyodide 不支持 subprocessAgent 调不了系统命令等于废了一半。方案三前端重写 Web Worker最终方案用 TypeScript 重写 Agent 核心逻辑在 Web Worker 里运行。Worker 里跑 Agent 循环主线程只负责渲染 UI。这样既解决了进程管理问题又天然支持断连恢复——Worker 在页面关闭前一直活着。三、踩坑实录那些让我熬夜的 Bug坑1Web Worker 里的异步陷阱Agent 的核心是循环调用 LLM每次调用都是异步的。在 Worker 里写异步循环时我犯了一个经典错误// 错误写法循环里直接 await导致事件循环阻塞 while (true) { const result await callLLM(messages); messages.push(result); } // 正确写法用递归 setTimeout 让出事件循环 function agentLoop() { callLLM(messages).then(result { messages.push(result); if (!isDone) setTimeout(agentLoop, 0); }); }这个 Bug 让我排查了两天最后用 Chrome DevTools 的 Performance 面板才发现事件循环被阻塞了。坑2流式输出的渲染性能Agent 的思考过程是流式输出的每生成一个 token 就要更新一次 UI。一开始我用 React 的 setState 直接更新结果页面卡成 PPT。解决方案用requestAnimationFrame做节流每 16ms 批量更新一次 DOM。同时把长文本用contenteditable的选区操作来增量插入避免全量替换。坑3工具调用的沙箱隔离Agent 需要执行代码、读写文件但在浏览器里不能直接操作文件系统。我用了OPFSOrigin Private File System来做沙箱文件系统用iframe sandbox来执行用户代码。但 OPFS 的 API 是异步的Agent 的工具调用却是同步的至少从 Agent 的角度看。最后我封装了一个ToolBridge把异步操作包装成 PromiseAgent 通过await bridge.call(writeFile, ...)来调用。四、架构演进从单体到微前端项目做到第二个月代码量已经超过 2 万行。Agent 核心、工具系统、UI 组件、状态管理全部耦合在一起改一个地方崩一片。我做了三件事来解耦Agent 核心独立成包用agent/core管理 LLM 调用、思考循环、记忆管理工具系统插件化每个工具是一个独立的ToolPlugin通过接口注册到 AgentUI 层用微前端Agent 面板、文件浏览器、终端模拟器各自独立部署通过iframe postMessage通信五、性能优化从 5 秒到 500 毫秒搬进浏览器后最直观的问题是慢。终端里秒回的 Agent在浏览器里要等 5 秒才有反应。优化路径LLM 调用缓存相同的 prompt 组合直接走缓存减少重复请求Web Worker 预热页面加载时提前初始化 Worker用户点击「开始」时直接跑虚拟滚动Agent 的思考日志可能很长用虚拟滚动只渲染可视区域增量序列化Agent 状态用structuredClone做快照但只序列化变化的部分六、总结与建议3 个月下来我最大的感受是把终端工具搬进浏览器不是简单的「换个 UI」而是整个架构的重构。给想尝试的同学几点建议先想清楚边界哪些逻辑必须在浏览器里跑哪些可以放后端Web Worker 是你的朋友别在主线程里跑 Agent 循环做好状态持久化浏览器刷新是常态用 IndexedDB 存 Agent 状态性能监控要早做别等用户反馈卡顿才去排查最终成果是一个可以在浏览器里直接运行的 AI Agent 演示平台支持代码执行、文件操作、API 调用等 20 工具。虽然过程痛苦但看到用户在浏览器里点一下就能跑通 Agent一切都值了。

相关新闻

吃透这 17 个概念,比 95% 的开发者更懂 AI

吃透这 17 个概念,比 95% 的开发者更懂 AI

1. 机器学习机器学习是人工智能的核心分支,指让计算机通过数据自动学习规律和模式,而不需要显式编程。它分为监督学习、无监督学习和强化学习三大类。理解机器学习是进入 AI 世界的第一道门槛。2. 深度学习深度学习是机器学习的一个子集,基于…

2026/10/13 5:45:08 阅读更多 →
500万-500w - 青龙面板自动签到脚本

500万-500w - 青龙面板自动签到脚本

自动抽奖、消息推送。这款自动化脚本帮你完成平台的日常签到和任务,解放双手,再也不用每天手动操作。功能介绍 「500万-500w」脚本支持以下功能: • 自动完成每日签到 • 自动领取奖励 • 支持多账号 • 签到结果通知 使用方法 1. 下载脚本并…

2026/10/10 22:20:31 阅读更多 →
Rapid SCADA开发指南:自定义工业监控功能的完整教程

Rapid SCADA开发指南:自定义工业监控功能的完整教程

Rapid SCADA开发指南:自定义工业监控功能的完整教程 【免费下载链接】scada Contains Rapid SCADA sources 项目地址: https://gitcode.com/gh_mirrors/sc/scada Rapid SCADA是一款功能强大的开源工业监控与数据采集系统,专为自动化工业控制系统设…

2026/10/1 23:17:07 阅读更多 →

最新新闻

Django+Flask智能营销系统:银行数字化转型实战

Django+Flask智能营销系统:银行数字化转型实战

1. 项目背景与核心价值在金融数字化转型浪潮中,银行业务营销正从传统人工经验驱动转向数据智能驱动。这个基于DjangoFlask的智能营销系统,正是利用爬虫技术获取多维金融数据,通过大数据分析生成客户画像,最终以可视化大屏呈现营销…

2026/10/14 10:10:46 阅读更多 →
从混乱到有序:打造本地优先的游戏库管理工具

从混乱到有序:打造本地优先的游戏库管理工具

最近我把游戏相关的一堆零散信息从脑子里和表格里拎了出来,整合成一个跑在局域网里的本地资料库小工具,代号 AnyPS5 。起因特别朴素:每次想开一个新游戏,我都得先花几分钟去回忆这台主机还剩多少空间、某张光盘到底通关没有、存…

2026/10/14 10:10:46 阅读更多 →
PHP服装购物系统开发实战:LAMP架构与电商解决方案

PHP服装购物系统开发实战:LAMP架构与电商解决方案

1. 项目概述:PHP木兮服装购物系统 这套基于PHP开发的服装购物系统,是我在电商领域摸爬滚打多年后沉淀下来的实战作品。系统采用经典的LAMP架构(LinuxApacheMySQLPHP),特别适合中小型服装商家快速搭建线上销售渠道。源码…

2026/10/14 10:10:46 阅读更多 →
CATIA零件设计必备:点线面创建方法详解与避坑指南

CATIA零件设计必备:点线面创建方法详解与避坑指南

简介:这套培训课件名为《点线面的创建》,是一份面向初学者的演示文稿教材。内容围绕零件设计工作台展开,重点讲解参考点、参考线和参考平面的构建方法。课件先介绍进入工作台的几种方式,然后系统演示点的生成:可通过坐…

2026/10/14 10:10:46 阅读更多 →
深入理解DiT:视频生成背后的统一架构

深入理解DiT:视频生成背后的统一架构

Sora刚发布那阵子,整个视频生成圈子几乎可以用"炸锅"来形容。大家看完官方放出的几个demo,第一反应是画质真高、时长真长、物理规律像模像样,第二反应才是琢磨:这玩意到底是怎么做的?OpenAI没发技术报告&…

2026/10/14 10:10:46 阅读更多 →
对话式地图助手落地实践:语言模型到地图动作的适配层设计

对话式地图助手落地实践:语言模型到地图动作的适配层设计

从聊天机器人一脚踩进地图场景的那段时间,我被一个问题折磨了很久:语言模型再能聊,跟地图引擎之间始终隔着一道墙。用户在对话框里说"帮我找滨河公园附近评分高的粤菜馆,然后规划一条地铁过去的路线",前端却…

2026/10/14 10:09:42 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →