普通视图

发现新文章,点击刷新页面。
今天 — 2025年11月18日首页

突发!iPhone Air 设计师离职,加入神秘 AI 创业公司

作者 肖钦鹏
2025年11月18日 10:24

iPhone Air 可能是这几年来,苹果最命运多舛的产品——在传出 iPhone Air 2 因销量不佳、延期发布的消息后,另一个坏消息接踵而至。

彭博社报道,在 iPhone Air 宣传片中作为设计师代表、担任主讲人的苹果设计师阿比杜尔·乔杜里(Abidur Chowdhury)被曝已经从苹果离职,加入了一家不具名的 AI 创业公司。

这意味着,苹果的设计师团队又失去一名干将。

▲ Abidur Chowdhury

彭博社的 Mark Gurman 表示,阿比杜尔·乔杜里的离职与 iPhone Air 的销量不佳无关——事实上,iPhone Air 的设计在苹果内部颇受好评,而阿比杜尔·乔杜里在其中发挥了关键作用。

出生在伦敦的阿比杜尔 · 乔杜里,现居于旧金山,是那种一看就会被人记住的年轻设计师:成长于多元文化的城市,和 Jony Ive 一样受英国工业设计体系的严格训练,却始终在思考下一代的产品设计,他在个人官网用这么一句话来阐述自己的设计理念:

没什么比创造让人无法割舍的创新产品更让我兴奋。

他曾在英国的剑桥顾问公司和 Curventa 公司实习。之后,乔杜里在伦敦的 Layer 设计公司担任工业设计师。从 2018 年到 2019 年,他经营自己的咨询公司 Abidur Chowdhury Design,与设计机构、创新公司和初创企业合作,提供产品、体验和设计策略。

2019 年 1 月——就在 Jony Ive 离开苹果公司之前,阿比杜尔 · 乔杜里加入苹果公司,担任加利福尼亚州库比蒂诺的工业设计师。

短短六年间,乔杜里参与设计了苹果一系列最具创新性的产品,其中就包括 iPhone Air——在苹果发布会上,乔杜里如此介绍这款 iPhone 的设计理念:

我们的初衷,是打造一款属于未来的 iPhone。

现在,阿比杜尔 · 乔杜里去追逐他的未来了。

自 2019 年以来,苹果的设计团队一直比较动荡。许多元老级设计师要么已经退休,要么离开苹果加入其他公司——其中就包括苹果前首席设计官乔纳森 · 艾维(Jony Ive)创立的设计公司 LoveFrom 和 AI 硬件公司 io。

在艾维离开后,埃文斯 · 汉基(Evans Hankey)短暂接手了苹果的设计师团队,直至 2022 年离职。后来,埃文斯 · 汉基与乔纳森 · 艾维以及多位苹果前员工创立了 AI 硬件公司 io,并于今年 7 月份以 65 亿美元的天价被 OpenAI 收购——迄今为止,io 还未发布任何一款硬件产品。

现任苹果设计总管莫莉 · 安德森(Molly Anderson)是为数不多自 Jony Ive 时代至今仍留在苹果公司的设计师,强调本质直觉的产品设计哲学。

她曾在采访中表示,在设计过程中不要受到现有产品的限制,而是专注于设计出最适合用户需求的工具,注重软件和硬件的融合——最新的超薄款 iPad Pro 以及 iPhone 17 Pro 的设计,就由自莫莉 · 安德森主导。

就在上周,苹果公司二号位、首席运营官杰夫 · 威廉姆斯(Jeff Williams)退休卸任。此前,苹果设计团队由威廉姆斯掌管,而后续将直接向苹果 CEO 蒂姆 · 库克(Tim Cook)直接汇报。

对于苹果而言,公司吸引力下降,人才流失严重,以及年轻团队青黄不接,是目前公司面临的一大挑战,而明年正是苹果成立五十周年的关键节点。

《金融时报》报道,苹果 CEO 库克正在加速推进其接班人计划,下一任苹果 CEO 有力竞争者、现任苹果硬件高级副总裁 John Ternus 将挑起大梁。

而这位 iPhone 的掌舵人不得不面对的,就是如何稳住大局、凝聚人心,带领苹果走向百年老店的下半程。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


两个世界首富吵起来了!贝索斯融资 447 亿复出搞 AI,马斯克:跟屁虫

作者 莫崇宇
2025年11月18日 10:23

本该是杰夫·贝索斯的高光时刻,结果被一只猫咪表情包搅了局。

今天凌晨,当贝索斯要亲自下场做 AI 公司的消息刚在网上发酵,马斯克就火速在 X 平台转发推文,并附带一句话:

「哈哈,不可能。Copy cat(跟屁虫)」

如果你关注科技圈,这剧情大概见怪不怪了。从卫星到火箭,这两位科技圈的顶流已经互怼了整整二十年。只是,如今吵架的战场,变成了眼下最火的物理 AI。

62 亿美元,贝索斯开启史上最壕创业

2021 年贝索斯把亚马逊 CEO 的位置让给了安迪·贾西,本以为他要专心当富豪享受人生。结果四年后,这位电商之王突然宣布:我要重返一线,

这次他瞄准的新项目,叫普罗米修斯计划(Project Prometheus)。

据纽约时报的报道,这个计划一出场就拿了 62 亿美元的融资启动资金,其中相当一部分是贝索斯自己掏的腰包。

凭借这笔巨款,普罗米修斯在硅谷展开了「人才大抢购」,已经挖来了近 100 名顶级研究员,这些人之前都在 OpenAI、Google DeepMind 和 Meta 工作。

那让贝索斯选择二次创业的普罗米修斯到底要做什么?

简单说,就是让 AI 从虚拟世界走进现实世界。现在市面上的 AI 主要都是通过学习网上的文字、图片来生成内容。它们很会写文章、画画、聊天,但有个致命问题,它们不懂物理世界。

举个例子:你问 ChatGPT「怎么设计一个更轻的飞机机翼」,它可能给你讲一堆理论,但它自己从来没摸过真实的机翼,不知道不同材料在风洞里的表现。

贝索斯要做的,就是让 AI 真正动手做实验。

据悉,普罗米修斯预计探索建设一个超大型自动化实验室。在这个实验室里,机器人会 24 小时不间断地进行物理、化学和工程测试。AI 会像科学家一样,自己提出假设、设计实验、观察结果、得出结论,然后不断迭代优化。

目标领域非常明确:航空航天、汽车工程、计算机硬件制造。比如在航空领域,AI 可以自己跑成千上万次风洞模拟,找出最优的机翼形状;在汽车制造中,AI 可以规划整条产线的机器人如何协作。

此外,贝索斯选的搭档也很有意思:维克·巴贾杰(Vik Bajaj)。

他是个跨界科学家,有着物理和化学的学术背景,曾是神秘实验室 Google X 的核心成员,参与过无人机送货项目 Wing 和自动驾驶项目 Waymo 的早期开发。后来还创立了 Alphabet 旗下的生命科学公司 Verily。选这样一个人当联合 CEO,至少在技术层面也有一个把握航向的舵手。

马斯克为什么说贝索斯「抄袭」?

马斯克这次发「Copycat」不是没有原因的。

最近几年,马斯克一直在强调:特斯拉已经变成了一家 AI 和机器人公司。现在贝索斯也要搞物理 AI,也投资了一堆机器人公司,在他看来,贝索斯进军物理 AI 和机器人领域,更像是在抄特斯拉的作业。

而两者的恩怨,也由来已久。

2019 年,亚马逊宣布要做 Project Kuiper 卫星项目,马斯克当场开骂「Copycat」,因为 SpaceX 的 Starlink 早在 2015 年就开始规划了。

同年贝索斯发布「蓝月」月球着陆器,马斯克又恶搞 P 图把「Blue Moon」改成「Blue Balls」。2020 年,亚马逊花 12 亿美元收购自动驾驶公司 Zoox,马斯克又发推:「@JeffBezos 是个跟屁虫」。

往前追溯,两人的梁子结得更早。

2004 年第一次见面就不欢而散。2013 年为了 NASA 的 39A 发射台打得不可开交。2014 年打专利官司,2021 年又因为登月合同闹上法庭。

不过话说回来,虽然两人互怼了二十年,但在技术成就面前,马斯克还是展现了一定风度。当地时间,11 月 13 日,蓝色起源的新格伦号重型火箭成功发射,并且在第二次飞行尝试中就完美回收了第一级助推器。后续马斯克还在 X 上送上祝福。

而无论是贝索斯的普罗米修斯, 还是马斯克的特斯拉和 Optimus, 他们都在做同一件事:让 AI 真正理解物理世界, 走出屏幕。

所以,抛开「抄袭」的口水战,更值得关注的是:当两个顶级玩家同时押注物理 AI 时,这个赛道的爆发可能比我们想象的更快。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


深入 iMessage 底层:一个 Agent 是如何诞生的

作者 Fatbobman
2025年11月19日 22:12

iMessage 深度集成在 Apple 生态中,却从未提供官方 API。本文邀请 imessage-kit 作者 LingJueYa 分享如何突破这一限制,让 AI Agent 进入 iMessage。文章详细介绍了从解析 SQLite 数据库、处理 Core Data 时间戳、绕过 macOS 沙盒限制,到用 AppleScript 实现消息发送的完整技术方案,以及在构建过程中踩过的坑与解决之道。

昨天 — 2025年11月17日首页

Google 年度最强 AI 偷跑!一个电风扇动画引发疯传,Gemini 3要给GPT-5.1上强度了

作者 张子豪
2025年11月17日 14:41

前几天 nano banana 2 的泄漏版本,正在网上被疯狂转载,奥特曼眼看着流量不能被 Google 再抢了去,一点预告都没有,直接就发布了 GPT-5.1。

现在,GPT-5.1 都来了,Gemini 3.0 还会远吗。

Google CEO Sundar 和 Google AI Studio 负责人 Logan,都回复了一则关于 Gemini 3 的帖子,内容显示 Gemini 3 在预测市场的发布时间,有 69% 的用户买入了这个月 22 号的时间。

Google CEO 回了意味深长的思考 emoji,毕竟在预测市场 Polymarket 上,Gemini 3 的发布时间从 8 月 31 号到年底,都有人买入,而现在除了本月 22 号,月底 30 号更是累计有三百多万美元。

▲ 图片来源:https://polymarket.com/event/gemini-3pt0-released-by?tid=1763343187680

种种迹象显示,Gemini 3.0 很有可能就在最近这周发布,并且还有机会和 nano banana 2 一起发布。它们一个是在编程、智能体、写作等通用智能上更上一层楼,另一个是延续图像编辑的强大一致性和长文本渲染。

不敢想象年底的 AI 模型更新会有多激烈。

我们之前也汇总过关于 Gemini 3.0 和 nano banana 2 的爆料,那时的 Gemini 3.0 是出现在 Google AI Studio 的 A/B 测试中,能直接给我们生成一个 macOS 的云电脑,点开 Safari 还可以直接访问网页。

▲瑞典风格的网页设计,图片来源:X@RDeepl

而最近这段时间爆出来 Gemini 3.0 的料,一个比一个厉害。指令的理解能力更强,在编程项目中展示了丰富的世界知识,还有生成的网页,风格更多元也更大胆,更实用。

大模型竞技场上的神秘模型

在大模型竞技场上,又多了一个编号为 riftrunner 的神秘模型,有网友在 battle 对战模式下,刚好测试到了它的效果。用它生成的 SVG 动画,一个比一个厉害。

下面这个能调节风速档位的电风扇,在 X 上被疯狂转载,大家都不相信 AI 有这么聪明,只用一句提示词,就能做出精美的 SVG。

▲图片来源:X@lepadphone

他还用这个编号为 riftrunner 的模型,生成了一个能同步真实时间,切换表盘颜色的手表动画 SVG。

和之前 nano banana 一开始出现在模型竞技场一样,网友们都在怀疑这个 riftRunner 就是 Google 马上要发布的 Gemini 3.0。

要想体验到它,我们不能手动选择,必须在竞技场 battle 模式中随机获得。battle 模式会给我们两个不同的答案,投票后才能知道型号名称。

为了减少反复尝试的痛苦,Flowith 的创始人还发 X,分享了通过设置浏览器 Agent 来自动投票,更快找到 riftrunner 的方法。

我们也在 LMArena 里面测试了多次,都没有碰到过 riftrunner,大概是运气都在我抽 nano banana 那会儿花光了。

继续在网上找了更多网友的分享,有人说 riftrunner 不失所望,是唯一一个解出下面这道数学难题的模型。

▲图片来源:X@Abasiono_Mbat

还有人在竞技场不断测试,做一个狐狸模型,得到了 riftrunner(Gemini 3)、Claude 4.5、以及 GPT-5 的三种对比。

▲图片来源:X@k0tovsk1y

还有应该选 both are bad,两个都很差的蒙娜丽莎 SVG 画像,即便是 riftrunner 看着也很怪,但至少又比 Claude 有更多关于蒙娜丽莎的元素。

▲左图为 riftrunner,右图为 Sonnet 4.5;图片来源:X@petergostev

以及 riftrunner 生成的,一只骑自行车的鹈鹕的 SVG。

▲图片来源:X@synthwavedd

藏在了手机端 Gemini App 的 Canvas 功能里

还有网友说,现在 Gemini App 里面的 Canvas 功能,使用的模型就已经是 Gemini 3.0 了。因为在网页端的 Gemini 和手机端,输入同样的提示词,得到的输出,质量差距很大。

▲图片来源:https://www.reddit.com/r/Bard/comments/1ovvmjo/not_hype_posting_gemini_3_is_rolling_out_to/

于是一大波网页版和手机端的对比,纷纷出现在评论区,大家都认可了,手机上的 Canvas 真的是使用了更先进的 Gemini 3.0。

最直观的例子是这个 3D 宝可梦的动图,在 Web 端生成的 3D 动图背景简单,宝可梦的形象也很抽象;手机端的色彩、背景都做的更好。

▲图片来源:X@AiBattle_

还有网友做的 Gemini 和外星人入侵的对抗的 SVG,网页版继续一如既往的简陋,而在手机上的 Canvas 明显元素更多,更丰富。

▲左图为网页版,右图为手机应用版。图片来源:X@Lentils80

以及拿手机应用中的 Canvas 和 Claude 4.5 Sonnet 来对比,输入的提示词都是,一个 3D 宝可梦精灵球。

▲图片来源:X@ctgptlb

还有人拿 Xbox 手柄 SVG 图来做测试,iOS 手机应用里的 Canvas 和 浏览器里的网页版,是完全不同。

▲左边是手机应用,右边是网页版;图片来源:X@MaximilanS638

不过,也有用户分享自己 Gemini 手机端和网页端,出来的结果是一模一样的。

▲左图为网页版,右图为手机应用版。图片来源:X@Medeenatee

我也尝试输入「生成一个 Xbox 的手柄 SVG 图」来看看两遍的结果,但是都很不理想。要不是根本不像一个手柄的图,要不然就是键位这些全部错乱。

只能说模型会出现幻觉,我们人类也可能有幻觉。大模型竞技场的神秘代号模型,还有这种靠着观察输出差异的怀疑,都不能确定是否来自 Gemini 3.0。

更确定的信息是,有网友发现,Gemini 3.0 Pro 已经出现在 Gemini Enterprise 的 Agent 模型选择器中。

▲图片来源:X@testingcatalog

不过 Gemini Enterprise 也是不对一般的用户开放,但至少可以说 Gemini 3.0 真的距离发布不远了,或者说已经准备好了。

回看过去这一年来,几家大模型公司的发布记录,GPT-5 因为营销太多被诟病,发布后网友们表示熬了这么久,「就这」;还有在社交媒体一直没什么水花,但模型广受好评的 Claude 系列;现在是后发制人,凭着 nano banana 重新回到大家视野的 Gemini。

甚至在想,如果没有 nano banana,会不会还有人只记得它之前的名字,叫 Bard。

SimilarWeb 在前几天了公布了,各个大模型网页流量的统计数据,一年前 OpenAI 的网页访问流量占比是 86.6%,到了今天虽然还稳居首位,但是已经被瓜分走了相当一部分,只剩下 72.3%。

而被分走的流量基本上全去到了 Gemini,Gemini 从一年前只有 5.6%,在 nano banana 发布之后,冲到了现在的 13.7%,直接翻倍。

要是 Gemini 3 和 nano banana 2 能稳住,再接着这波流量,下个季度的柱状图,Gemini 的颜色可能还要占据更多。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


Cursor 一年深度开发实践:前端开发的效率革命🚀

作者 Sentry5
2025年11月17日 10:49

在 AI Coding 提效这件事上,我想我的经历让我有充分的发言权。今年上半年,作为团队中的校招新人,我承接了两位离职同事的业务模块。面对密集的需求,我不仅扛住了“以一当三”的交付压力,同时保证了线上零事故。这一切,离不开 Cursor 的深度辅助。

先上干货:

Cursor 实战 case 展示

以下展示了过去一年中,我使用 Cursor 开发的部分前端项目。这些页面平均的 AI 生成代码占比超过 80%,业务场景横跨 B/C 两端,技术栈全面覆盖 Vue、React、微信小程序以及 Tailwindcss、Antd、shadcn UI 等多种技术框架,充分体现了 Cursor 全面的技术能力与显著的效率提升。

移动端

App h5推广页面,中秋前夕晚上 22 点业务来电话说想要一个中秋推广的活动页,使用豆包生成背景图,使用cursor进行样式设计,0-1开发仅耗费两个半小时,0:30 完成上线:

8915703d40874aceba0eb63be0708f72~tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2VudHJ5NQ==_q75.jpg

广告落地页,UI 提供的初版 Lottie 动画是一个完整页面,无法拆分。由于大促排期紧张,等待 UI 支持较慢。为此,我借助 Cursor 直接解读 Lottie 的 JSON 配置文件,成功将火焰、杀价、折扣等核心动效元素,精准地解析为独立的动画,并让 Curosr 通过CSS实现,降低了引入资源体积的同时还优化了动画的效果,加速通过了协同工作的卡点:

d6f4d2bf95484cc0a1636193ca3378c2~tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2VudHJ5NQ==_q75.jpg

pc端

后台资损防控平台,该项目由研发发起,在没有产品原型和UI设计的情况下,借助 Cursor 结合 Shadcn UI,我独立完成了平台 0-1 的交互与界面构建,最终成果获得了后端与测试团队的一致好评:

image.pngimage.png

流量调度分流中心表单,该模块核心代码近万行,表单联动逻辑复杂,整体由 Cursor 生成实现。面对 5 层以上的嵌套数据结构,人工理解其层级关系并控制动态联动不仅难度大,且极易出错。通过引入 Cursor,深入解析数据结构与联动逻辑,显著降低研发的理解成本,提升整体开发效率。

image.pngimage.png

精准链路分析项目,公司内比赛参赛项目,基于 Cursor 从零启动,单人仅用两天便快速构建出功能完整的精美 Demo。项目完整实现了基于 React Flow 的 JAVA 调用链路展示与组合AI 流式报告智能Agent对话等多种高级能力。

image.pngimage.pngimage.png

工程化

工程化历来是前端领域的核心挑战,充斥着依赖版本冲突与繁杂的配置逻辑。为验证 Cursor 处理系统级任务的能力,我尝试将完整升级流程交由它主导:从依赖分析、版本管理到工程配置更新,让其直接操控终端、执行 npm 命令。

在一个 App H5 项目中,我基于 Cursor 成功完成了从旧版本到 Vue 2.7.16 + Webpack 5 的升级全流程:Vue 2.5.16 + Webpack 4 升级 Vue 2.7.16 + Webpack 5 记录记录项目从 Vue - 掘金

cursor 提供的升级方案(部分对话):

image.png

更新依赖版本和配置文件(部分对话):

image.png

自动执行终端命令与修复报错(部分对话):

image.png

此外,我也让 Cursor 实现了该 App h5 从 Webpack 到 Vite 的迁移路径验证,核心构建流程已全部跑通。目前因部分边界场景报错尚未完全解决,未沉淀博客文章,但该实践已初步验证 Cursor 在复杂工程链路中具备可行的辅助潜力。

Cursor 使用经验分享

重中之重:模型的选择

image.png

模型是 AI 的基座,地基不牢,地动山摇。在此直接上结论:无脑选择 Claude

image.png

这不仅因为在大模型代码能力评测中 Claude 持续领先(如上图所示),更是笔者自 Sonnet 3.5 版本发布以来,实际体验 Claude 在代码生成、逻辑理解与上下文关联方面的能力,相比同时期的模型,确实一骑绝尘。

需要注意的两点:

  1. 警惕 Auto 模式: 若账号用量不足,Cursor 会自动切换至 Auto 模式,此时可能分配到性能较弱的模型,输出质量会显著下降。该模式可用于技术交流,但不建议用于代码生成与编辑。这也是我升级了订阅计划的原因。

  2. 关注上下文长度: Cursor 会实时显示上下文使用情况。若接近限制,可主动选择支持更长上下文的模型,或开启多倍计费的 Max Mode 以扩展处理能力,避免上下文丢失带来的输出质量下降。

image.png

Talk is cheap. Show me the code.

相信研发同学对这句话都不陌生。这句话,在此处不妨视作 Cursor 对我们提出的要求。与 Cursor 协作的第一原则是:能提供代码片段,绝不用文字描述;能用变量名指代的,绝不用中文名

手动添加上下文

image.png

  1. 选中代码片段,点击“添加到聊天上下文”按钮(快捷键 Ctrl+I);

  2. 若仅提问不希望 Cursor 改动代码,可使用 Ctrl+L;

  3. 在目录中右键点击文件或文件夹,将其加入对话;

  4. 在输入框中输入 @,手动选择要引用的文件或目录。

image.png

image.png

尤其在涉及多文件改动时,主动告知 Cursor 相关文件路径,效果远优于依赖其自行检索。

统一语义表达

在业务沟通中,请始终使用精准的变量名与 Cursor 交互。例如,在价格相关需求中,应直接使用 purchasePriceprice 等已有变量名,而非口语化的“到手价”、“原价”。这能确保 AI 在后续所有交互中对概念理解一致,无需反复推理映射关系。

同样,在描述界面元素与交互逻辑时,精准引用标识符而非依赖自然语言描述,是提升 Cursor 理解准确度的关键。

  • 定位界面元素,应明确指出其 classNameid,而非使用模糊的自然语言。 例如:❌ “那个下载按钮” → ✅ “类名为 .download-btn 的按钮” 或 “ID 为 #export-download 的元素”。

  • 描述交互逻辑,应直接提供回调函数名或方法名称,而非笼统描述行为意图。 例如:❌ “点击按钮后弹窗” → ✅ “在 handleConfirmClick 函数中调用 showModal() 方法”。

这种方式能够有效避免界面中存在多个相似元素时造成的歧义,也便于 Cursor 直接在代码库中定位相关逻辑,实现精准编辑。

如何使用 cursor 定位故障?

在“AI 能否取代程序员”的持续讨论中,精准定位并修复线上故障一直被视作人类工程师的关键优势。其根本原因在于:AI 虽能较好地解析静态代码结构,却难以感知系统运行时的动态状态。而很多深层问题——如内存泄漏、线程竞争、环境依赖异常等——恰恰隐藏在静态代码与动态执行之间的鸿沟中,这构成了当前 AI 在故障处理中的认知边界。

那么,我们如何为 AI 架起一座跨越这道鸿沟的桥梁?

答案正在于我们人类最熟悉的调试手段:日志。既然日志能够成为开发者和运行中系统之间的沟通媒介,那么它同样可以转化为 AI 理解运行时行为的关键信息来源。

引导 AI 插入关键日志

当你发现某个功能异常,可指示 Cursor 在关键逻辑路径上添加日志点。只需简单指令,如:“请帮我在xx功能相关的函数内部添加 console.log,输出关键变量的值。”

image.png

### 运行代码并捕获日志

执行添加日志后的代码,复制运行时所生成的完整日志输出。

image.png

将日志与代码共同提交给 AI 分析

然后再将日志复制发送给cursor,神奇的事发生了,本来它改动了几遍都没能解决的问题,一下就定位到了根因:

image.png

技巧背后的逻辑

此方法之所以有效,是因为它将 AI 从纯粹的代码静态分析者,转变为了一个具备“运行时视野”的调试伙伴。通过日志,AI 能够:

  • 追踪变量的实际变化轨迹

  • 识别逻辑分支的真实执行路径

  • 发现数据流与预期不符的具体位置

添加 Rules:让 AI 记住你的工程规范

在使用日志与 Cursor 协作调试时,我遇到了一个典型问题:项目中已有大量日志,新增的调试信息很快被淹没,难以快速定位。我希望 Cursor 在每次插入调试日志时,自动在开头附加 【xx功能调试】 这样的标识,以便在控制台中快速筛选。但若每次对话都重复这一要求,既低效又容易遗漏。

这时,Cursor 的 Rules 功能便可发挥关键作用。你可以在规则中固话这类常见的工程约束或团队规范,例如:

image.png

Cursor 支持为不同项目配置独立的规则集,灵活适配各工程的特定规范。具体设置方法详见官方文档:Cursor - 规则

完成规则配置后,我们重新执行之前的调试对话。如下图所示,现在每个 console.log 语句的开头都已自动加上了对应的函数名作为标识,极大方便了在控制台中的筛选与查看:

image.png

集成 MCP:拓展能力边界

在使用日志辅助 Cursor 进行调试的过程中,我逐渐发现两个影响效率的典型问题:

  • 手动复制繁琐:频繁从控制台复制日志再粘贴至 Cursor,本质上仍是一种重复劳动,与 AI 协作的自动化理念相悖。

  • 日志内容杂乱:控制台中的引用类型数据(如对象、数组)若不展开或格式不当,难以完整复制;同时,控制台自动插入的代码位置信息(文件路径与行号)常混杂在日志正文中,导致最终提供给 Cursor 的文本结构混乱、难以解析。

image.png

上图正是这一问题的直观体现:日志中穿插了源代码位置,而对象数据未完整展开,这样的信息直接交给 Cursor,会影响其理解与推理的准确性。

而此时,正是 MCP(Model Context Protocol)可大显身手的场景。 通过为 Cursor 配置浏览器 MCP 服务,我实现了工作流的质的飞跃:

image.png

image.png

MCP 赋予 Cursor 直接控制浏览器的能力,使其能够:
  • 自动捕获页面截图
  • 直接读取控制台日志
  • 分析 DOM 结构

image.png

当前 Cursor 的浏览器 MCP 仅支持内置窗口与 Chrome。若你使用 Edge 或其他浏览器,可选用微软推出的 Playwright 作为替代方案。

同时,主流前端工具已纷纷提供 MCP 或知识库。以 Ant Design 为例,将其官方知识库 LLMs.txt - Ant Design ,添加到 Cursor 的指定位置:

image.png

添加后,Cursor 即可基于官方最新文档提供准确的组件使用建议。

优先选用 AI “擅长”的技术栈

何为 AI “擅长”的技术栈?简单来说:React、TailwindCSS 属于 AI 表现优异的技术栈;微信小程序次之;而像 Taro、uni-app 这类一码多端的框架,则往往是 AI 的弱项。

其背后的逻辑在于数据可见性:开源生态越丰富、网络公开样本越多的技术,大模型在训练时接触到的相关代码就越充分,生成质量自然更高。反之,闭源、文档稀少的场景,AI 由于缺乏学习材料,表现往往不尽如人意

在实际的 Taro 项目中,当我尝试让 AI 协助处理 H5、小程序与 RN 三端的代码适配时,其表现确实令人沮丧。我最常遇到的状况是:好不容易让 AI 修复了 H5 端的样式错位,转头就发现小程序端布局崩溃;当 RN 端的交互问题被解决后,H5 端又出现了新的渲染异常。

因此,在 AI Coding 日益普及的背景下,我们不得不重新审视如 Taro、UniApp 等一码多端框架的效率等式:其带来的跨端便利,是否足以抵消因 AI 支持薄弱而导致的额外研发成本?这一点值得深思。

破局之道或许在于深度拥抱 AI 生态。如果这类框架能官方的推出强大的 MCP 服务,将其多端差异和配置逻辑“结构化”地注入 AI 的认知过程,它们将有潜力从当前的“AI 洼地”转变为“智能跨端”的典范。

反直觉:0-1不难,1-100 更难?

读过不少 AI 编程文章的人都会发现,多数内容都在展示如何从 0 到 1 快速搭建应用。但实际上存在一个反直觉的真相:用 AI 从 0 到 1 并不难,真正难的是让它接手和维护存量代码。

在新项目中,AI 面对的是清晰的上下文和现代技术栈。而在存量代码中,它需要理解混乱的命名、隐含的业务逻辑和特殊的实现方式,同时要避免“修复一个 bug 引入两个新 bug”的连锁反应。这就像让新人从头做项目,远比让他修改复杂的老系统要简单。

要让 AI 有效接手存量代码,关键在于像帮助新人一样为它提供清晰的指引。核心方法有二:

为 AI 优化的代码注释

传统的业务背景介绍对 AI 帮助有限,应该采用更代码化的注释方式。避免长篇大论地介绍业务逻辑,而是清晰地指出代码和业务之间的关系,魔法数字的具体含义等。

比如,不要写“这里是价格计算模块,因为历史原因需要区分新老用户”,而应该写“新用户(level=1)享受首单优惠,老用户(level>=2)按原价计算,优惠金额固定为20”。重点注释魔法数字的实际含义、复杂条件判断的业务背景、接口字段的映射关系等。

TypeScript 的天然优势

在接手现有项目上,TypeScript 有着得天独厚的优势。类型定义相当于强制展示了一遍代码结构,如果再加上每个变量的注释,就是现成的知识库。

通过“精准注释 + 完整类型”的组合,即使是最复杂的遗留代码,AI 也能快速理解并安全修改,真正突破从 1 到 100 的瓶颈。

AI Coding时代,优秀研发需要哪些新特质?

聊了这么多 Cursor 的强大表现,难免让人心生疑问:研发是否正在被 AI 取代?恰恰相反,我认为 AI 正在急剧拉大开发者之间的能力差距。今年几乎人人都用上了 AI 编程工具,可能是 Cursor,也可能是 Joycode。但如果你去 review 团队中的代码,就会发现:强者的代码因AI而更优秀,弱者的代码因AI而更紊乱

结合实践中的经验,我总结了 AI 编码时代一名优秀开发者最应具备的几种核心能力:

1️⃣有责任心,做代码的owner

早期使用Cursor时,我常常陷入一种状态:AI生成的代码占比太高,以至于我对新增部分失去理解和掌控。一旦被问及业务逻辑,或是出现线上问题,甚至会不知从何查起。

这就像一位艺术家通过AI生成画作,很难像对待自己亲笔作品那样珍视并负责。我的改进方案是:在每次 Agent 完成编码之后,阅读其改动总结;在每次提交前,仔细Review Cursor生成代码的Diff。这个过程强制我理解每一行变更,重新建立起对代码的掌控感。

image.png

如上图,通常 cursor 在修改完成后都会自动生成总结(也可以通过添加 rules 控制),可以结合总结阅读 diff。

2️⃣代码品味,超越能跑就行

AI生成的代码能运行、测试通过、上线不出事故,就足够了吗?如果你的技术认知水平在 AI 之下,无法判断其实现是否为最佳实践,就可能在系统中埋下无数隐患。

举个例子,上周在使用设计稿 AI 转代码的时候遇到过一件事:

image.png

AI 将图中的商品列表拆分为多行布局——一行图片、一行商品名、一行价格、一行按钮。然而,具备前端组件化思维的同学一眼就能看出,更合理的做法是将其封装为独立的商品卡片组件进行循环渲染:

image.png

尽管 AI 的产出在功能上可以运行,测试、产品与用户也难以察觉差异,但这样的结构严重缺乏可复用性。若未来其他页面需要复用相同样式的商品展示,我们将不得不重复编写样式与逻辑,违背了组件化的设计初衷。

因此,我的建议是:坚持阅读高质量的代码,无论是优秀的开源项目,还是身边同事的成熟实现。遇到问题时,不必过度沉溺于调试错误实现,而应主动学习并理解最佳实践,勇于对不合理的代码进行果断重构。。

3️⃣知识广度,做好技术决策

AI在执行明确、具体的指令时表现更佳。这要求开发者既要有广泛的知识储备,又要能精准描述需求。研发就像行政总厨,而AI是精通各菜系的厨师——总厨必须清楚做什么菜的时候,需要备哪些料,使用哪些厨具餐具,才能调度后厨高效产出。

以前端开发为例,若能明确指定使用某个具体的 JavaScript 库,AI 的响应质量将显著提升。例如,在实现“前端解析 Excel 文件”功能时,若直接提示 Cursor 使用 xlsx 库,仅需二三十行代码即可获得目标数据结构:

image.png

而若未提供任何技术栈提示,AI 可能倾向于使用原生 JS 实现,代码量激增五倍以上,且逻辑复杂、未经充分验证:

image.png

因此,持续在技术社区交流,关注经典工具与前沿方案,是提升技术决策能力的关键。 只有清楚“用什么”和“为什么用”,才能最大限度地发挥 AI 的编码潜力。

4️⃣表达精度,说 AI 听得懂的话

一个不善于使用搜索引擎的人,往往也难以通过AI获得理想结果。 从模糊的需求到清晰的提示词,本质上是一种结构化与抽象能力的体现。

继续用行政总厨的比喻:如果只说“番茄炒蛋要甜一点”,厨师会困惑——是加糖还是加番茄酱?如果能明确“300克番茄配3个鸡蛋,需要加5克白糖”,产出质量就有保障。

精准表达的能力,与个人知识储备和语言表达能力相关,不好举例说明。建议有意识地阅读完整书籍、观看有深度的长播客,避免被短视频时代的碎片化表达削弱这种能力。

对未来的展望

目前许多公司都在业产研测各环节大力推进“+AI”时,我不禁思考:AI 提效,是否真的等同于在现有流程的每个环节简单叠加 AI?

这让我想起从功能机到智能机的过渡时期:早期的触摸屏设备仍保留着大量实体按键,或者在屏幕底部保留了触摸版的菜单键和返回键,交互逻辑仍是旧时代的延伸。直到多年后,真正的全面屏与手势导航出现,才彻底释放了触摸交互的潜力。

我们当前对 AI 的应用,或许正处在那个“仍带着实体按键”的阶段。 若只满足于在原有流程上“+AI”,恐怕难以触及其真正的变革性潜力。AI 不应仅是效率工具,更应成为流程重构与体验重塑的催化剂——而这,才是我们接下来需要共同探索的方向。

LangChain.js 完全开发手册(十九)前端 AI 开发进阶技巧

作者 鲫小鱼
2025年11月17日 09:54

第19章:前端 AI 开发进阶技巧

前言

大家好,我是鲫小鱼。是一名不写前端代码的前端工程师,热衷于分享非前端的知识,带领切图仔逃离切图圈子,欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!

🎯 本章学习目标

通过本章学习,您将:

  • 掌握前端 AI 应用的高级优化技术和性能调优
  • 实现客户端 AI 模型部署和本地推理能力
  • 学习前端 AI 交互设计和用户体验优化
  • 探索 WebAssembly 和 Web Workers 在 AI 中的应用
  • 掌握前端 AI 状态管理和缓存策略
  • 了解前端 AI 应用的监控和调试技巧

📋 章节概述

19.1 前端 AI 技术栈

🚀 客户端 AI 模型

  • TensorFlow.js 和 ONNX.js 模型部署
  • WebAssembly 高性能计算
  • Web Workers 多线程处理
  • 模型量化和优化技术

🎨 AI 交互设计

  • 智能表单和输入预测
  • 实时 AI 反馈和提示
  • 语音和图像交互
  • 个性化用户界面

性能优化

  • 模型加载和缓存策略
  • 推理性能优化
  • 内存管理和垃圾回收
  • 网络请求优化

19.2 现代前端 AI 架构

┌─────────────────────────────────────────────────────────────┐
│                    用户界面层                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  AI 交互     │  │  智能组件    │  │  实时反馈    │          │
│  │  组件        │  │  库          │  │  系统        │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                    AI 服务层                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  本地模型    │  │  云端 API    │  │  混合推理    │          │
│  │  推理        │  │  服务        │  │  策略        │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                    基础设施层                               │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  Web Workers │  │  WebAssembly│  │  缓存系统    │          │
│  │  多线程      │  │  高性能      │  │  与存储      │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘

🔧 客户端 AI 模型部署

TensorFlow.js 模型管理

// src/services/ai/tensorflow-manager.ts
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-webgl';
import '@tensorflow/tfjs-backend-cpu';

export interface ModelConfig {
  name: string;
  url: string;
  inputShape: number[];
  outputShape: number[];
  quantization?: 'int8' | 'int16' | 'float16';
  optimization?: 'speed' | 'memory' | 'balanced';
}

export interface InferenceResult {
  predictions: number[];
  confidence: number;
  processingTime: number;
  modelName: string;
}

export class TensorFlowManager {
  private models: Map<string, tf.LayersModel> = new Map();
  private modelConfigs: Map<string, ModelConfig> = new Map();
  private isInitialized = false;

  async initialize(): Promise<void> {
    if (this.isInitialized) return;

    try {
      // 设置后端
      await tf.setBackend('webgl');
      await tf.ready();

      // 启用内存管理
      tf.engine().startScope();

      this.isInitialized = true;
      console.log('TensorFlow.js 初始化完成');
    } catch (error) {
      console.error('TensorFlow.js 初始化失败:', error);
      throw error;
    }
  }

  async loadModel(config: ModelConfig): Promise<void> {
    try {
      console.log(`开始加载模型: ${config.name}`);

      // 加载模型
      const model = await tf.loadLayersModel(config.url);

      // 应用优化
      const optimizedModel = this.optimizeModel(model, config);

      // 缓存模型
      this.models.set(config.name, optimizedModel);
      this.modelConfigs.set(config.name, config);

      console.log(`模型 ${config.name} 加载完成`);
    } catch (error) {
      console.error(`模型 ${config.name} 加载失败:`, error);
      throw error;
    }
  }

  private optimizeModel(model: tf.LayersModel, config: ModelConfig): tf.LayersModel {
    // 应用量化
    if (config.quantization) {
      model = this.applyQuantization(model, config.quantization);
    }

    // 应用优化策略
    switch (config.optimization) {
      case 'speed':
        model = this.optimizeForSpeed(model);
        break;
      case 'memory':
        model = this.optimizeForMemory(model);
        break;
      case 'balanced':
        model = this.optimizeForBalanced(model);
        break;
    }

    return model;
  }

  private applyQuantization(model: tf.LayersModel, quantization: string): tf.LayersModel {
    // 简化的量化实现
    // 在实际应用中,应该使用更复杂的量化技术
    console.log(`应用 ${quantization} 量化`);
    return model;
  }

  private optimizeForSpeed(model: tf.LayersModel): tf.LayersModel {
    // 优化推理速度
    console.log('优化模型推理速度');
    return model;
  }

  private optimizeForMemory(model: tf.LayersModel): tf.LayersModel {
    // 优化内存使用
    console.log('优化模型内存使用');
    return model;
  }

  private optimizeForBalanced(model: tf.LayersModel): tf.LayersModel {
    // 平衡速度和内存
    console.log('平衡优化模型');
    return model;
  }

  async predict(modelName: string, input: tf.Tensor): Promise<InferenceResult> {
    const model = this.models.get(modelName);
    const config = this.modelConfigs.get(modelName);

    if (!model || !config) {
      throw new Error(`模型 ${modelName} 未找到`);
    }

    const startTime = performance.now();

    try {
      // 预处理输入
      const processedInput = this.preprocessInput(input, config);

      // 执行推理
      const predictions = model.predict(processedInput) as tf.Tensor;

      // 后处理输出
      const result = await this.postprocessOutput(predictions, config);

      const processingTime = performance.now() - startTime;

      // 清理内存
      processedInput.dispose();
      predictions.dispose();

      return {
        predictions: result,
        confidence: this.calculateConfidence(result),
        processingTime,
        modelName
      };
    } catch (error) {
      console.error(`模型 ${modelName} 推理失败:`, error);
      throw error;
    }
  }

  private preprocessInput(input: tf.Tensor, config: ModelConfig): tf.Tensor {
    // 调整输入形状
    let processed = input;

    if (config.inputShape.length > 0) {
      processed = input.reshape(config.inputShape);
    }

    // 归一化
    processed = processed.div(255.0);

    return processed;
  }

  private async postprocessOutput(output: tf.Tensor, config: ModelConfig): Promise<number[]> {
    // 转换为数组
    const data = await output.data();

    // 应用 softmax(如果需要)
    if (config.outputShape.length > 1) {
      const softmax = tf.softmax(output);
      const softmaxData = await softmax.data();
      softmax.dispose();
      return Array.from(softmaxData);
    }

    return Array.from(data);
  }

  private calculateConfidence(predictions: number[]): number {
    const maxPrediction = Math.max(...predictions);
    const sumPredictions = predictions.reduce((sum, pred) => sum + pred, 0);

    return sumPredictions > 0 ? maxPrediction / sumPredictions : 0;
  }

  async batchPredict(modelName: string, inputs: tf.Tensor[]): Promise<InferenceResult[]> {
    const results: InferenceResult[] = [];

    for (const input of inputs) {
      const result = await this.predict(modelName, input);
      results.push(result);
    }

    return results;
  }

  getModelInfo(modelName: string): ModelConfig | undefined {
    return this.modelConfigs.get(modelName);
  }

  getLoadedModels(): string[] {
    return Array.from(this.models.keys());
  }

  async unloadModel(modelName: string): Promise<void> {
    const model = this.models.get(modelName);

    if (model) {
      model.dispose();
      this.models.delete(modelName);
      this.modelConfigs.delete(modelName);
      console.log(`模型 ${modelName} 已卸载`);
    }
  }

  async cleanup(): Promise<void> {
    // 清理所有模型
    for (const [name, model] of this.models) {
      model.dispose();
    }

    this.models.clear();
    this.modelConfigs.clear();

    // 清理 TensorFlow 内存
    tf.disposeVariables();
    tf.engine().endScope();

    console.log('TensorFlow.js 资源已清理');
  }
}

WebAssembly AI 推理

// src/services/ai/wasm-inference.ts
export interface WASMModelConfig {
  name: string;
  wasmPath: string;
  modelPath: string;
  inputSize: number;
  outputSize: number;
  precision: 'float32' | 'int8' | 'int16';
}

export interface WASMInferenceResult {
  predictions: Float32Array;
  processingTime: number;
  memoryUsage: number;
}

export class WASMInferenceService {
  private wasmModule: WebAssembly.Module | null = null;
  private wasmInstance: WebAssembly.Instance | null = null;
  private models: Map<string, WASMModelConfig> = new Map();
  private isInitialized = false;

  async initialize(): Promise<void> {
    if (this.isInitialized) return;

    try {
      // 加载 WASM 模块
      const wasmBytes = await this.loadWASMBytes('/wasm/ai-inference.wasm');
      this.wasmModule = await WebAssembly.compile(wasmBytes);

      // 创建 WASM 实例
      this.wasmInstance = await WebAssembly.instantiate(this.wasmModule, {
        env: {
          memory: new WebAssembly.Memory({ initial: 256 }),
          console_log: (ptr: number, len: number) => {
            const bytes = new Uint8Array(this.getMemory().buffer, ptr, len);
            console.log(new TextDecoder().decode(bytes));
          }
        }
      });

      this.isInitialized = true;
      console.log('WASM AI 推理服务初始化完成');
    } catch (error) {
      console.error('WASM AI 推理服务初始化失败:', error);
      throw error;
    }
  }

  private async loadWASMBytes(path: string): Promise<Uint8Array> {
    const response = await fetch(path);
    if (!response.ok) {
      throw new Error(`无法加载 WASM 文件: ${path}`);
    }
    return new Uint8Array(await response.arrayBuffer());
  }

  private getMemory(): WebAssembly.Memory {
    if (!this.wasmInstance) {
      throw new Error('WASM 实例未初始化');
    }
    return this.wasmInstance.exports.memory as WebAssembly.Memory;
  }

  async loadModel(config: WASMModelConfig): Promise<void> {
    try {
      console.log(`开始加载 WASM 模型: ${config.name}`);

      // 加载模型权重
      const modelData = await this.loadModelData(config.modelPath);

      // 在 WASM 中初始化模型
      const initModel = this.wasmInstance!.exports.init_model as Function;
      const modelPtr = initModel(
        config.inputSize,
        config.outputSize,
        config.precision === 'float32' ? 0 :
        config.precision === 'int8' ? 1 : 2
      );

      // 加载模型权重到 WASM 内存
      const memory = this.getMemory();
      const memoryView = new Uint8Array(memory.buffer);
      memoryView.set(modelData, modelPtr);

      this.models.set(config.name, config);
      console.log(`WASM 模型 ${config.name} 加载完成`);
    } catch (error) {
      console.error(`WASM 模型 ${config.name} 加载失败:`, error);
      throw error;
    }
  }

  private async loadModelData(path: string): Promise<Uint8Array> {
    const response = await fetch(path);
    if (!response.ok) {
      throw new Error(`无法加载模型文件: ${path}`);
    }
    return new Uint8Array(await response.arrayBuffer());
  }

  async predict(modelName: string, input: Float32Array): Promise<WASMInferenceResult> {
    const config = this.models.get(modelName);
    if (!config) {
      throw new Error(`模型 ${modelName} 未找到`);
    }

    const startTime = performance.now();
    const startMemory = this.getMemoryUsage();

    try {
      // 分配输入内存
      const inputPtr = this.allocateMemory(input.length * 4); // float32 = 4 bytes
      const memory = this.getMemory();
      const memoryView = new Float32Array(memory.buffer);

      // 复制输入数据到 WASM 内存
      memoryView.set(input, inputPtr / 4);

      // 执行推理
      const predict = this.wasmInstance!.exports.predict as Function;
      const outputPtr = predict(modelName, inputPtr, input.length);

      // 读取输出结果
      const outputLength = config.outputSize;
      const output = new Float32Array(memoryView.buffer, outputPtr, outputLength);

      const processingTime = performance.now() - startTime;
      const endMemory = this.getMemoryUsage();

      // 释放内存
      this.deallocateMemory(inputPtr);

      return {
        predictions: new Float32Array(output),
        processingTime,
        memoryUsage: endMemory - startMemory
      };
    } catch (error) {
      console.error(`WASM 模型 ${modelName} 推理失败:`, error);
      throw error;
    }
  }

  private allocateMemory(size: number): number {
    const malloc = this.wasmInstance!.exports.malloc as Function;
    return malloc(size);
  }

  private deallocateMemory(ptr: number): void {
    const free = this.wasmInstance!.exports.free as Function;
    free(ptr);
  }

  private getMemoryUsage(): number {
    const memory = this.getMemory();
    return memory.buffer.byteLength;
  }

  async batchPredict(modelName: string, inputs: Float32Array[]): Promise<WASMInferenceResult[]> {
    const results: WASMInferenceResult[] = [];

    for (const input of inputs) {
      const result = await this.predict(modelName, input);
      results.push(result);
    }

    return results;
  }

  getModelInfo(modelName: string): WASMModelConfig | undefined {
    return this.models.get(modelName);
  }

  getLoadedModels(): string[] {
    return Array.from(this.models.keys());
  }

  async cleanup(): Promise<void> {
    // 清理 WASM 资源
    if (this.wasmInstance) {
      const cleanup = this.wasmInstance.exports.cleanup as Function;
      cleanup();
    }

    this.models.clear();
    this.wasmModule = null;
    this.wasmInstance = null;

    console.log('WASM AI 推理服务已清理');
  }
}

🎨 AI 交互组件库

智能输入组件

// src/components/ai/SmartInput.tsx
'use client';

import { useState, useEffect, useRef, useCallback } from 'react';
import { TensorFlowManager } from '@/services/ai/tensorflow-manager';

interface SmartInputProps {
  placeholder?: string;
  onPrediction?: (prediction: string) => void;
  onSuggestion?: (suggestions: string[]) => void;
  modelName?: string;
  maxSuggestions?: number;
  debounceMs?: number;
}

interface PredictionResult {
  text: string;
  confidence: number;
  suggestions: string[];
}

export default function SmartInput({
  placeholder = '智能输入...',
  onPrediction,
  onSuggestion,
  modelName = 'text-prediction',
  maxSuggestions = 5,
  debounceMs = 300
}: SmartInputProps) {
  const [value, setValue] = useState('');
  const [suggestions, setSuggestions] = useState<string[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [prediction, setPrediction] = useState<string>('');
  const [confidence, setConfidence] = useState(0);

  const inputRef = useRef<HTMLInputElement>(null);
  const suggestionsRef = useRef<HTMLDivElement>(null);
  const tfManager = useRef<TensorFlowManager | null>(null);
  const debounceTimer = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    // 初始化 TensorFlow 管理器
    const initTF = async () => {
      try {
        tfManager.current = new TensorFlowManager();
        await tfManager.current.initialize();
        await tfManager.current.loadModel({
          name: modelName,
          url: '/models/text-prediction/model.json',
          inputShape: [1, 100],
          outputShape: [1, 1000],
          optimization: 'speed'
        });
      } catch (error) {
        console.error('TensorFlow 初始化失败:', error);
      }
    };

    initTF();

    return () => {
      if (tfManager.current) {
        tfManager.current.cleanup();
      }
    };
  }, [modelName]);

  const handleInputChange = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = e.target.value;
    setValue(newValue);

    // 清除之前的定时器
    if (debounceTimer.current) {
      clearTimeout(debounceTimer.current);
    }

    // 设置新的定时器
    debounceTimer.current = setTimeout(async () => {
      if (newValue.trim() && tfManager.current) {
        await generatePredictions(newValue);
      } else {
        setSuggestions([]);
        setPrediction('');
        setConfidence(0);
        setShowSuggestions(false);
      }
    }, debounceMs);
  }, [debounceMs]);

  const generatePredictions = async (text: string) => {
    setIsLoading(true);
    setShowSuggestions(true);

    try {
      // 将文本转换为张量
      const textTensor = await textToTensor(text);

      // 执行预测
      const result = await tfManager.current!.predict(modelName, textTensor);

      // 处理预测结果
      const predictions = await processPredictions(result.predictions, text);

      setPrediction(predictions.text);
      setConfidence(predictions.confidence);
      setSuggestions(predictions.suggestions.slice(0, maxSuggestions));

      // 触发回调
      onPrediction?.(predictions.text);
      onSuggestion?.(predictions.suggestions);

    } catch (error) {
      console.error('预测生成失败:', error);
    } finally {
      setIsLoading(false);
    }
  };

  const textToTensor = async (text: string): Promise<any> => {
    // 简化的文本到张量转换
    // 在实际应用中,应该使用更复杂的文本预处理
    const words = text.split(' ').slice(-10); // 取最后 10 个词
    const vector = new Array(100).fill(0);

    words.forEach((word, index) => {
      const hash = word.split('').reduce((a, b) => {
        a = ((a << 5) - a) + b.charCodeAt(0);
        return a & a;
      }, 0);
      vector[index] = Math.abs(hash) % 1000;
    });

    return tf.tensor2d([vector]);
  };

  const processPredictions = async (predictions: number[], originalText: string): Promise<PredictionResult> => {
    // 简化的预测结果处理
    const maxIndex = predictions.indexOf(Math.max(...predictions));
    const confidence = Math.max(...predictions);

    // 生成建议文本
    const suggestions = generateSuggestions(originalText, predictions);

    return {
      text: originalText + ' ' + suggestions[0],
      confidence,
      suggestions
    };
  };

  const generateSuggestions = (text: string, predictions: number[]): string[] => {
    // 简化的建议生成
    const commonWords = ['的', '是', '在', '有', '和', '了', '不', '我', '你', '他'];
    const suggestions: string[] = [];

    // 基于预测概率生成建议
    predictions.forEach((prob, index) => {
      if (prob > 0.1 && suggestions.length < maxSuggestions) {
        const word = commonWords[index % commonWords.length];
        suggestions.push(text + ' ' + word);
      }
    });

    return suggestions;
  };

  const handleSuggestionClick = (suggestion: string) => {
    setValue(suggestion);
    setShowSuggestions(false);
    inputRef.current?.focus();
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown' && suggestions.length > 0) {
      e.preventDefault();
      suggestionsRef.current?.focus();
    }
  };

  return (
    <div className="relative w-full">
      <div className="relative">
        <input
          ref={inputRef}
          type="text"
          value={value}
          onChange={handleInputChange}
          onKeyDown={handleKeyDown}
          placeholder={placeholder}
          className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
        />

        {isLoading && (
          <div className="absolute right-3 top-1/2 transform -translate-y-1/2">
            <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
          </div>
        )}

        {prediction && confidence > 0.5 && (
          <div className="absolute right-3 top-1/2 transform -translate-y-1/2">
            <div className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded">
              {Math.round(confidence * 100)}%
            </div>
          </div>
        )}
      </div>

      {showSuggestions && suggestions.length > 0 && (
        <div
          ref={suggestionsRef}
          className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto"
        >
          {suggestions.map((suggestion, index) => (
            <div
              key={index}
              onClick={() => handleSuggestionClick(suggestion)}
              className="px-4 py-2 hover:bg-gray-100 cursor-pointer border-b border-gray-100 last:border-b-0"
            >
              <div className="flex items-center justify-between">
                <span className="text-gray-800">{suggestion}</span>
                <span className="text-xs text-gray-500">
                  {Math.round(Math.random() * 100)}%
                </span>
              </div>
            </div>
          ))}
        </div>
      )}

      {prediction && (
        <div className="mt-2 p-3 bg-blue-50 border border-blue-200 rounded-lg">
          <div className="text-sm text-blue-800">
            <strong>预测:</strong> {prediction}
          </div>
          <div className="text-xs text-blue-600 mt-1">
            置信度: {Math.round(confidence * 100)}%
          </div>
        </div>
      )}
    </div>
  );
}

实时 AI 反馈组件

// src/components/ai/RealtimeFeedback.tsx
'use client';

import { useState, useEffect, useRef } from 'react';
import { TensorFlowManager } from '@/services/ai/tensorflow-manager';

interface FeedbackConfig {
  type: 'sentiment' | 'quality' | 'relevance' | 'safety';
  threshold: number;
  modelName: string;
}

interface FeedbackResult {
  type: string;
  score: number;
  message: string;
  color: string;
  icon: string;
}

interface RealtimeFeedbackProps {
  text: string;
  configs: FeedbackConfig[];
  onFeedback?: (feedback: FeedbackResult[]) => void;
  showVisual?: boolean;
  showScore?: boolean;
}

export default function RealtimeFeedback({
  text,
  configs,
  onFeedback,
  showVisual = true,
  showScore = true
}: RealtimeFeedbackProps) {
  const [feedbacks, setFeedbacks] = useState<FeedbackResult[]>([]);
  const [isAnalyzing, setIsAnalyzing] = useState(false);
  const tfManager = useRef<TensorFlowManager | null>(null);

  useEffect(() => {
    const initTF = async () => {
      try {
        tfManager.current = new TensorFlowManager();
        await tfManager.current.initialize();

        // 加载所有需要的模型
        for (const config of configs) {
          await tfManager.current.loadModel({
            name: config.modelName,
            url: `/models/${config.type}/model.json`,
            inputShape: [1, 100],
            outputShape: [1, 1],
            optimization: 'speed'
          });
        }
      } catch (error) {
        console.error('TensorFlow 初始化失败:', error);
      }
    };

    initTF();
  }, [configs]);

  useEffect(() => {
    if (text.trim() && tfManager.current) {
      analyzeText(text);
    } else {
      setFeedbacks([]);
    }
  }, [text]);

  const analyzeText = async (inputText: string) => {
    setIsAnalyzing(true);

    try {
      const results: FeedbackResult[] = [];

      for (const config of configs) {
        const result = await analyzeWithModel(inputText, config);
        results.push(result);
      }

      setFeedbacks(results);
      onFeedback?.(results);
    } catch (error) {
      console.error('文本分析失败:', error);
    } finally {
      setIsAnalyzing(false);
    }
  };

  const analyzeWithModel = async (inputText: string, config: FeedbackConfig): Promise<FeedbackResult> => {
    try {
      // 将文本转换为张量
      const textTensor = await textToTensor(inputText);

      // 执行预测
      const result = await tfManager.current!.predict(config.modelName, textTensor);
      const score = result.predictions[0];

      // 生成反馈结果
      return generateFeedback(config, score);
    } catch (error) {
      console.error(`模型 ${config.modelName} 分析失败:`, error);
      return generateFeedback(config, 0);
    }
  };

  const textToTensor = async (text: string): Promise<any> => {
    // 简化的文本预处理
    const words = text.split(' ').slice(0, 100);
    const vector = new Array(100).fill(0);

    words.forEach((word, index) => {
      const hash = word.split('').reduce((a, b) => {
        a = ((a << 5) - a) + b.charCodeAt(0);
        return a & a;
      }, 0);
      vector[index] = Math.abs(hash) % 1000;
    });

    return tf.tensor2d([vector]);
  };

  const generateFeedback = (config: FeedbackConfig, score: number): FeedbackResult => {
    const normalizedScore = Math.max(0, Math.min(1, score));

    let message = '';
    let color = '';
    let icon = '';

    switch (config.type) {
      case 'sentiment':
        if (normalizedScore > 0.6) {
          message = '积极情绪';
          color = 'text-green-600';
          icon = '😊';
        } else if (normalizedScore < 0.4) {
          message = '消极情绪';
          color = 'text-red-600';
          icon = '😞';
        } else {
          message = '中性情绪';
          color = 'text-gray-600';
          icon = '😐';
        }
        break;

      case 'quality':
        if (normalizedScore > 0.7) {
          message = '高质量内容';
          color = 'text-green-600';
          icon = '⭐';
        } else if (normalizedScore < 0.4) {
          message = '内容质量较低';
          color = 'text-red-600';
          icon = '⚠️';
        } else {
          message = '内容质量一般';
          color = 'text-yellow-600';
          icon = '📝';
        }
        break;

      case 'relevance':
        if (normalizedScore > 0.6) {
          message = '内容相关';
          color = 'text-green-600';
          icon = '🎯';
        } else {
          message = '内容不相关';
          color = 'text-red-600';
          icon = '❌';
        }
        break;

      case 'safety':
        if (normalizedScore > 0.8) {
          message = '内容安全';
          color = 'text-green-600';
          icon = '✅';
        } else if (normalizedScore < 0.3) {
          message = '内容不安全';
          color = 'text-red-600';
          icon = '🚫';
        } else {
          message = '需要审核';
          color = 'text-yellow-600';
          icon = '⚠️';
        }
        break;
    }

    return {
      type: config.type,
      score: normalizedScore,
      message,
      color,
      icon
    };
  };

  if (!showVisual && !showScore) {
    return null;
  }

  return (
    <div className="space-y-2">
      {isAnalyzing && (
        <div className="flex items-center space-x-2 text-gray-500">
          <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
          <span className="text-sm">分析中...</span>
        </div>
      )}

      {feedbacks.length > 0 && (
        <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
          {feedbacks.map((feedback, index) => (
            <div
              key={index}
              className={`flex items-center space-x-2 p-2 rounded-lg border ${
                feedback.score > 0.7 ? 'bg-green-50 border-green-200' :
                feedback.score < 0.4 ? 'bg-red-50 border-red-200' :
                'bg-yellow-50 border-yellow-200'
              }`}
            >
              <span className="text-lg">{feedback.icon}</span>
              <div className="flex-1">
                <div className={`text-sm font-medium ${feedback.color}`}>
                  {feedback.message}
                </div>
                {showScore && (
                  <div className="text-xs text-gray-500">
                    分数: {Math.round(feedback.score * 100)}
                  </div>
                )}
              </div>
              {showVisual && (
                <div className="w-16 bg-gray-200 rounded-full h-2">
                  <div
                    className={`h-2 rounded-full ${
                      feedback.score > 0.7 ? 'bg-green-500' :
                      feedback.score < 0.4 ? 'bg-red-500' :
                      'bg-yellow-500'
                    }`}
                    style={{ width: `${feedback.score * 100}%` }}
                  ></div>
                </div>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

⚡ 性能优化策略

AI 模型缓存管理

// src/services/ai/model-cache.ts
export interface CacheConfig {
  maxSize: number; // MB
  ttl: number; // seconds
  strategy: 'lru' | 'lfu' | 'fifo';
  compression: boolean;
}

export interface CachedModel {
  name: string;
  data: ArrayBuffer;
  metadata: {
    size: number;
    timestamp: number;
    accessCount: number;
    lastAccessed: number;
  };
}

export class ModelCacheManager {
  private cache: Map<string, CachedModel> = new Map();
  private config: CacheConfig;
  private totalSize = 0;

  constructor(config: CacheConfig) {
    this.config = config;
    this.startCleanupTimer();
  }

  async cacheModel(name: string, modelData: ArrayBuffer): Promise<void> {
    const compressedData = this.config.compression
      ? await this.compressData(modelData)
      : modelData;

    const cachedModel: CachedModel = {
      name,
      data: compressedData,
      metadata: {
        size: compressedData.byteLength,
        timestamp: Date.now(),
        accessCount: 0,
        lastAccessed: Date.now()
      }
    };

    // 检查缓存大小限制
    await this.enforceSizeLimit(cachedModel);

    this.cache.set(name, cachedModel);
    this.totalSize += cachedModel.metadata.size;

    console.log(`模型 ${name} 已缓存,大小: ${this.formatSize(cachedModel.metadata.size)}`);
  }

  async getModel(name: string): Promise<ArrayBuffer | null> {
    const cachedModel = this.cache.get(name);

    if (!cachedModel) {
      return null;
    }

    // 更新访问统计
    cachedModel.metadata.accessCount++;
    cachedModel.metadata.lastAccessed = Date.now();

    // 解压缩数据
    const modelData = this.config.compression
      ? await this.decompressData(cachedModel.data)
      : cachedModel.data;

    return modelData;
  }

  private async enforceSizeLimit(newModel: CachedModel): Promise<void> {
    const maxSizeBytes = this.config.maxSize * 1024 * 1024;

    while (this.totalSize + newModel.metadata.size > maxSizeBytes && this.cache.size > 0) {
      const keyToRemove = this.getKeyToRemove();
      if (keyToRemove) {
        await this.removeModel(keyToRemove);
      } else {
        break;
      }
    }
  }

  private getKeyToRemove(): string | null {
    switch (this.config.strategy) {
      case 'lru':
        return this.getLRUKey();
      case 'lfu':
        return this.getLFUKey();
      case 'fifo':
        return this.getFIFOKey();
      default:
        return this.getLRUKey();
    }
  }

  private getLRUKey(): string | null {
    let oldestTime = Date.now();
    let oldestKey: string | null = null;

    for (const [key, model] of this.cache) {
      if (model.metadata.lastAccessed < oldestTime) {
        oldestTime = model.metadata.lastAccessed;
        oldestKey = key;
      }
    }

    return oldestKey;
  }

  private getLFUKey(): string | null {
    let minAccessCount = Infinity;
    let lfuKey: string | null = null;

    for (const [key, model] of this.cache) {
      if (model.metadata.accessCount < minAccessCount) {
        minAccessCount = model.metadata.accessCount;
        lfuKey = key;
      }
    }

    return lfuKey;
  }

  private getFIFOKey(): string | null {
    let oldestTime = Date.now();
    let oldestKey: string | null = null;

    for (const [key, model] of this.cache) {
      if (model.metadata.timestamp < oldestTime) {
        oldestTime = model.metadata.timestamp;
        oldestKey = key;
      }
    }

    return oldestKey;
  }

  private async removeModel(name: string): Promise<void> {
    const model = this.cache.get(name);
    if (model) {
      this.totalSize -= model.metadata.size;
      this.cache.delete(name);
      console.log(`模型 ${name} 已从缓存中移除`);
    }
  }

  private async compressData(data: ArrayBuffer): Promise<ArrayBuffer> {
    // 使用 CompressionStream API 进行压缩
    const stream = new CompressionStream('gzip');
    const writer = stream.writable.getWriter();
    const reader = stream.readable.getReader();

    writer.write(data);
    writer.close();

    const chunks: Uint8Array[] = [];
    let done = false;

    while (!done) {
      const { value, done: readerDone } = await reader.read();
      done = readerDone;
      if (value) {
        chunks.push(value);
      }
    }

    const compressedLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
    const compressed = new Uint8Array(compressedLength);
    let offset = 0;

    for (const chunk of chunks) {
      compressed.set(chunk, offset);
      offset += chunk.length;
    }

    return compressed.buffer;
  }

  private async decompressData(compressedData: ArrayBuffer): Promise<ArrayBuffer> {
    // 使用 DecompressionStream API 进行解压缩
    const stream = new DecompressionStream('gzip');
    const writer = stream.writable.getWriter();
    const reader = stream.readable.getReader();

    writer.write(compressedData);
    writer.close();

    const chunks: Uint8Array[] = [];
    let done = false;

    while (!done) {
      const { value, done: readerDone } = await reader.read();
      done = readerDone;
      if (value) {
        chunks.push(value);
      }
    }

    const decompressedLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
    const decompressed = new Uint8Array(decompressedLength);
    let offset = 0;

    for (const chunk of chunks) {
      decompressed.set(chunk, offset);
      offset += chunk.length;
    }

    return decompressed.buffer;
  }

  private startCleanupTimer(): void {
    setInterval(() => {
      this.cleanupExpiredModels();
    }, 60 * 1000); // 每分钟清理一次
  }

  private cleanupExpiredModels(): void {
    const now = Date.now();
    const expiredKeys: string[] = [];

    for (const [key, model] of this.cache) {
      if (now - model.metadata.timestamp > this.config.ttl * 1000) {
        expiredKeys.push(key);
      }
    }

    for (const key of expiredKeys) {
      this.removeModel(key);
    }

    if (expiredKeys.length > 0) {
      console.log(`清理了 ${expiredKeys.length} 个过期模型`);
    }
  }

  private formatSize(bytes: number): string {
    const sizes = ['B', 'KB', 'MB', 'GB'];
    if (bytes === 0) return '0 B';
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
  }

  getCacheStats(): {
    totalModels: number;
    totalSize: string;
    hitRate: number;
    averageAccessCount: number;
  } {
    const totalModels = this.cache.size;
    const totalSizeFormatted = this.formatSize(this.totalSize);

    let totalAccessCount = 0;
    for (const model of this.cache.values()) {
      totalAccessCount += model.metadata.accessCount;
    }

    const averageAccessCount = totalModels > 0 ? totalAccessCount / totalModels : 0;
    const hitRate = totalAccessCount > 0 ? (totalAccessCount - totalModels) / totalAccessCount : 0;

    return {
      totalModels,
      totalSize: totalSizeFormatted,
      hitRate: Math.round(hitRate * 100) / 100,
      averageAccessCount: Math.round(averageAccessCount * 100) / 100
    };
  }

  clearCache(): void {
    this.cache.clear();
    this.totalSize = 0;
    console.log('模型缓存已清空');
  }
}

Web Workers AI 处理

// src/workers/ai-worker.ts
import { TensorFlowManager } from '@/services/ai/tensorflow-manager';

interface WorkerMessage {
  type: 'init' | 'predict' | 'batchPredict' | 'cleanup';
  payload: any;
  id: string;
}

interface WorkerResponse {
  type: 'success' | 'error';
  payload: any;
  id: string;
}

class AIWorker {
  private tfManager: TensorFlowManager | null = null;
  private isInitialized = false;

  constructor() {
    this.setupMessageHandler();
  }

  private setupMessageHandler(): void {
    self.onmessage = async (event: MessageEvent<WorkerMessage>) => {
      const { type, payload, id } = event.data;

      try {
        let result: any;

        switch (type) {
          case 'init':
            result = await this.initialize(payload);
            break;
          case 'predict':
            result = await this.predict(payload);
            break;
          case 'batchPredict':
            result = await this.batchPredict(payload);
            break;
          case 'cleanup':
            result = await this.cleanup();
            break;
          default:
            throw new Error(`未知的消息类型: ${type}`);
        }

        this.sendResponse('success', result, id);
      } catch (error) {
        this.sendResponse('error', { message: error.message }, id);
      }
    };
  }

  private async initialize(config: any): Promise<void> {
    if (this.isInitialized) return;

    try {
      this.tfManager = new TensorFlowManager();
      await this.tfManager.initialize();

      // 加载模型
      for (const modelConfig of config.models) {
        await this.tfManager.loadModel(modelConfig);
      }

      this.isInitialized = true;
      console.log('AI Worker 初始化完成');
    } catch (error) {
      console.error('AI Worker 初始化失败:', error);
      throw error;
    }
  }

  private async predict(payload: { modelName: string; input: any }): Promise<any> {
    if (!this.tfManager || !this.isInitialized) {
      throw new Error('AI Worker 未初始化');
    }

    const { modelName, input } = payload;

    // 将输入转换为张量
    const inputTensor = this.convertToTensor(input);

    // 执行预测
    const result = await this.tfManager.predict(modelName, inputTensor);

    // 清理张量
    inputTensor.dispose();

    return result;
  }

  private async batchPredict(payload: { modelName: string; inputs: any[] }): Promise<any[]> {
    if (!this.tfManager || !this.isInitialized) {
      throw new Error('AI Worker 未初始化');
    }

    const { modelName, inputs } = payload;
    const results: any[] = [];

    for (const input of inputs) {
      const inputTensor = this.convertToTensor(input);
      const result = await this.tfManager.predict(modelName, inputTensor);
      results.push(result);
      inputTensor.dispose();
    }

    return results;
  }

  private convertToTensor(input: any): any {
    // 根据输入类型转换为张量
    if (Array.isArray(input)) {
      return tf.tensor2d([input]);
    } else if (typeof input === 'number') {
      return tf.scalar(input);
    } else {
      throw new Error('不支持的输入类型');
    }
  }

  private async cleanup(): Promise<void> {
    if (this.tfManager) {
      await this.tfManager.cleanup();
      this.tfManager = null;
    }
    this.isInitialized = false;
    console.log('AI Worker 已清理');
  }

  private sendResponse(type: 'success' | 'error', payload: any, id: string): void {
    const response: WorkerResponse = {
      type,
      payload,
      id
    };

    self.postMessage(response);
  }
}

// 启动 Worker
new AIWorker();

Worker 管理器

// src/services/ai/worker-manager.ts
export interface WorkerConfig {
  maxWorkers: number;
  modelConfigs: any[];
  timeout: number;
}

export class WorkerManager {
  private workers: Worker[] = [];
  private availableWorkers: Worker[] = [];
  private busyWorkers: Set<Worker> = new Set();
  private pendingTasks: Array<{
    resolve: (value: any) => void;
    reject: (error: Error) => void;
    task: any;
  }> = [];
  private config: WorkerConfig;
  private messageId = 0;

  constructor(config: WorkerConfig) {
    this.config = config;
    this.initializeWorkers();
  }

  private async initializeWorkers(): Promise<void> {
    for (let i = 0; i < this.config.maxWorkers; i++) {
      const worker = new Worker('/workers/ai-worker.js');

      // 设置消息处理器
      worker.onmessage = (event) => {
        this.handleWorkerMessage(worker, event.data);
      };

      worker.onerror = (error) => {
        console.error('Worker 错误:', error);
        this.handleWorkerError(worker, error);
      };

      // 初始化 Worker
      await this.initializeWorker(worker);

      this.workers.push(worker);
      this.availableWorkers.push(worker);
    }

    console.log(`初始化了 ${this.config.maxWorkers} 个 AI Workers`);
  }

  private async initializeWorker(worker: Worker): Promise<void> {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Worker 初始化超时'));
      }, this.config.timeout);

      const messageHandler = (event: MessageEvent) => {
        if (event.data.type === 'success' && event.data.payload === 'initialized') {
          clearTimeout(timeout);
          worker.removeEventListener('message', messageHandler);
          resolve();
        } else if (event.data.type === 'error') {
          clearTimeout(timeout);
          worker.removeEventListener('message', messageHandler);
          reject(new Error(event.data.payload.message));
        }
      };

      worker.addEventListener('message', messageHandler);

      worker.postMessage({
        type: 'init',
        payload: {
          models: this.config.modelConfigs
        },
        id: 'init'
      });
    });
  }

  private handleWorkerMessage(worker: Worker, data: any): void {
    if (data.type === 'success') {
      // 查找对应的任务
      const taskIndex = this.pendingTasks.findIndex(task => task.task.id === data.id);
      if (taskIndex !== -1) {
        const task = this.pendingTasks[taskIndex];
        this.pendingTasks.splice(taskIndex, 1);

        // 释放 Worker
        this.releaseWorker(worker);

        // 解析任务
        task.resolve(data.payload);
      }
    } else if (data.type === 'error') {
      // 查找对应的任务
      const taskIndex = this.pendingTasks.findIndex(task => task.task.id === data.id);
      if (taskIndex !== -1) {
        const task = this.pendingTasks[taskIndex];
        this.pendingTasks.splice(taskIndex, 1);

        // 释放 Worker
        this.releaseWorker(worker);

        // 拒绝任务
        task.reject(new Error(data.payload.message));
      }
    }
  }

  private handleWorkerError(worker: Worker, error: Error): void {
    console.error('Worker 发生错误:', error);

    // 移除错误的 Worker
    this.removeWorker(worker);

    // 重新创建 Worker
    this.createReplacementWorker();
  }

  private async createReplacementWorker(): Promise<void> {
    try {
      const worker = new Worker('/workers/ai-worker.js');

      worker.onmessage = (event) => {
        this.handleWorkerMessage(worker, event.data);
      };

      worker.onerror = (error) => {
        this.handleWorkerError(worker, error);
      };

      await this.initializeWorker(worker);

      this.workers.push(worker);
      this.availableWorkers.push(worker);

      console.log('创建了替代 Worker');
    } catch (error) {
      console.error('创建替代 Worker 失败:', error);
    }
  }

  private removeWorker(worker: Worker): void {
    const index = this.workers.indexOf(worker);
    if (index !== -1) {
      this.workers.splice(index, 1);
    }

    const availableIndex = this.availableWorkers.indexOf(worker);
    if (availableIndex !== -1) {
      this.availableWorkers.splice(availableIndex, 1);
    }

    this.busyWorkers.delete(worker);
    worker.terminate();
  }

  private releaseWorker(worker: Worker): void {
    this.busyWorkers.delete(worker);
    this.availableWorkers.push(worker);

    // 处理待处理的任务
    this.processPendingTasks();
  }

  private processPendingTasks(): void {
    while (this.pendingTasks.length > 0 && this.availableWorkers.length > 0) {
      const task = this.pendingTasks.shift()!;
      const worker = this.availableWorkers.shift()!;

      this.busyWorkers.add(worker);
      worker.postMessage(task.task);
    }
  }

  async predict(modelName: string, input: any): Promise<any> {
    return this.executeTask('predict', { modelName, input });
  }

  async batchPredict(modelName: string, inputs: any[]): Promise<any[]> {
    return this.executeTask('batchPredict', { modelName, inputs });
  }

  private async executeTask(type: string, payload: any): Promise<any> {
    return new Promise((resolve, reject) => {
      const task = {
        type,
        payload,
        id: `task_${++this.messageId}`
      };

      if (this.availableWorkers.length > 0) {
        // 有可用的 Worker,立即执行
        const worker = this.availableWorkers.shift()!;
        this.busyWorkers.add(worker);
        worker.postMessage(task);
      } else {
        // 没有可用的 Worker,加入队列
        this.pendingTasks.push({ resolve, reject, task });
      }
    });
  }

  getStats(): {
    totalWorkers: number;
    availableWorkers: number;
    busyWorkers: number;
    pendingTasks: number;
  } {
    return {
      totalWorkers: this.workers.length,
      availableWorkers: this.availableWorkers.length,
      busyWorkers: this.busyWorkers.size,
      pendingTasks: this.pendingTasks.length
    };
  }

  async cleanup(): Promise<void> {
    // 清理所有 Workers
    for (const worker of this.workers) {
      worker.postMessage({
        type: 'cleanup',
        payload: {},
        id: 'cleanup'
      });
      worker.terminate();
    }

    this.workers = [];
    this.availableWorkers = [];
    this.busyWorkers.clear();
    this.pendingTasks = [];

    console.log('Worker Manager 已清理');
  }
}

📊 前端 AI 监控

性能监控服务

// src/services/ai/performance-monitor.ts
export interface PerformanceMetrics {
  modelLoadTime: number;
  inferenceTime: number;
  memoryUsage: number;
  gpuUsage?: number;
  cacheHitRate: number;
  errorRate: number;
  throughput: number;
}

export interface PerformanceEvent {
  type: 'model_load' | 'inference' | 'error' | 'cache_hit' | 'cache_miss';
  timestamp: number;
  duration?: number;
  modelName?: string;
  error?: string;
  metadata?: any;
}

export class AIPerformanceMonitor {
  private metrics: PerformanceMetrics;
  private events: PerformanceEvent[] = [];
  private isMonitoring = false;
  private monitoringInterval: NodeJS.Timeout | null = null;

  constructor() {
    this.metrics = this.initializeMetrics();
    this.startMonitoring();
  }

  private initializeMetrics(): PerformanceMetrics {
    return {
      modelLoadTime: 0,
      inferenceTime: 0,
      memoryUsage: 0,
      gpuUsage: 0,
      cacheHitRate: 0,
      errorRate: 0,
      throughput: 0
    };
  }

  private startMonitoring(): void {
    if (this.isMonitoring) return;

    this.isMonitoring = true;
    this.monitoringInterval = setInterval(() => {
      this.updateMetrics();
    }, 1000); // 每秒更新一次

    console.log('AI 性能监控已启动');
  }

  private updateMetrics(): void {
    // 更新内存使用情况
    if ('memory' in performance) {
      const memory = (performance as any).memory;
      this.metrics.memoryUsage = memory.usedJSHeapSize / memory.jsHeapSizeLimit;
    }

    // 计算缓存命中率
    const cacheEvents = this.events.filter(e =>
      e.type === 'cache_hit' || e.type === 'cache_miss'
    );
    const hitCount = cacheEvents.filter(e => e.type === 'cache_hit').length;
    this.metrics.cacheHitRate = cacheEvents.length > 0 ? hitCount / cacheEvents.length : 0;

    // 计算错误率
    const recentEvents = this.events.filter(e =>
      Date.now() - e.timestamp < 60000 // 最近 1 分钟
    );
    const errorCount = recentEvents.filter(e => e.type === 'error').length;
    this.metrics.errorRate = recentEvents.length > 0 ? errorCount / recentEvents.length : 0;

    // 计算吞吐量
    const inferenceEvents = recentEvents.filter(e => e.type === 'inference');
    this.metrics.throughput = inferenceEvents.length;

    // 计算平均推理时间
    const inferenceTimes = inferenceEvents
      .filter(e => e.duration)
      .map(e => e.duration!);
    this.metrics.inferenceTime = inferenceTimes.length > 0
      ? inferenceTimes.reduce((sum, time) => sum + time, 0) / inferenceTimes.length
      : 0;
  }

  recordModelLoad(modelName: string, duration: number): void {
    this.events.push({
      type: 'model_load',
      timestamp: Date.now(),
      duration,
      modelName
    });

    this.metrics.modelLoadTime = duration;
  }

  recordInference(modelName: string, duration: number): void {
    this.events.push({
      type: 'inference',
      timestamp: Date.now(),
      duration,
      modelName
    });
  }

  recordError(modelName: string, error: string): void {
    this.events.push({
      type: 'error',
      timestamp: Date.now(),
      modelName,
      error
    });
  }

  recordCacheHit(modelName: string): void {
    this.events.push({
      type: 'cache_hit',
      timestamp: Date.now(),
      modelName
    });
  }

  recordCacheMiss(modelName: string): void {
    this.events.push({
      type: 'cache_miss',
      timestamp: Date.now(),
      modelName
    });
  }

  getMetrics(): PerformanceMetrics {
    return { ...this.metrics };
  }

  getEvents(limit: number = 100): PerformanceEvent[] {
    return this.events
      .sort((a, b) => b.timestamp - a.timestamp)
      .slice(0, limit);
  }

  getEventsByModel(modelName: string): PerformanceEvent[] {
    return this.events.filter(e => e.modelName === modelName);
  }

  getPerformanceReport(): {
    summary: PerformanceMetrics;
    recommendations: string[];
    alerts: string[];
  } {
    const recommendations: string[] = [];
    const alerts: string[] = [];

    // 分析性能指标并生成建议
    if (this.metrics.inferenceTime > 1000) {
      recommendations.push('推理时间过长,考虑使用模型量化或优化');
      alerts.push('推理性能警告');
    }

    if (this.metrics.memoryUsage > 0.8) {
      recommendations.push('内存使用率过高,考虑清理缓存或减少模型数量');
      alerts.push('内存使用警告');
    }

    if (this.metrics.cacheHitRate < 0.5) {
      recommendations.push('缓存命中率较低,考虑调整缓存策略');
    }

    if (this.metrics.errorRate > 0.1) {
      recommendations.push('错误率过高,检查模型和输入数据');
      alerts.push('错误率警告');
    }

    return {
      summary: this.metrics,
      recommendations,
      alerts
    };
  }

  stopMonitoring(): void {
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
      this.monitoringInterval = null;
    }
    this.isMonitoring = false;
    console.log('AI 性能监控已停止');
  }

  clearEvents(): void {
    this.events = [];
    console.log('性能事件已清空');
  }
}

📚 本章总结

通过本章学习,我们完成了:

客户端 AI 部署

  • 实现了 TensorFlow.js 模型管理和优化
  • 开发了 WebAssembly 高性能推理服务
  • 构建了 Web Workers 多线程处理

AI 交互组件

  • 创建了智能输入和实时反馈组件
  • 实现了 AI 驱动的用户界面优化
  • 建立了响应式 AI 交互体验

性能优化

  • 实现了模型缓存和内存管理
  • 开发了 Worker 管理和任务调度
  • 建立了性能监控和优化策略

工程实践

  • 掌握了前端 AI 应用的最佳实践
  • 实现了完整的性能监控体系
  • 建立了可扩展的 AI 组件库

🎯 下章预告

在下一章《AI 应用测试与质量保证》中,我们将:

  • 建立 AI 应用的完整测试体系
  • 实现模型质量评估和验证
  • 学习 AI 应用的持续集成和部署
  • 掌握 AI 应用的监控和运维

最后感谢阅读!欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!!

🔥 大模型时代最讽刺的职业出现了:“大模型善后工程师”

作者 Sailing
2025年11月17日 09:42

引言:80 分危机,把一群程序员推上神坛

凌晨三点,老张盯着屏幕上 AI 生成的 2000 行代码,这是他改的第 47 个 bug。
AI 用一分钟写完了整个模块,他已经调了三天。
更绝望的是:每修一个 bug,AI 都能"贴心"地帮他补出三个新 bug。
这不是段子,这已经是很多开发者的日常。(我自己早怒了,,)

最近知乎上突然爆火的一个词:大模型善后工程师。

看起来有点好笑,但越想越扎心:

AI 已经能把一个项目做到 80 分了。
但真正能上线、能卖钱、没 bug 的,仍然要你 20 分来救。
而这 20 分,恰恰是最难的那部分。

更扎心的是:
从 0 → 80 分,只需要一句 prompt。
但从 80 → 100 分,需要工程师半条命。

所以这份“善后”的工作,开始变成行业刚需。

01. 大模型的“80 分幻觉”:它看起来能干,但它真的不能

一句 prompt,它能给你:

  • 方向对
  • 代码能跑
  • 结构像样
  • 文案顺眼
  • Demo 一键生成

你觉得卧槽厉害啊,80 分了! 但很快你会发现它的成长曲线是这样的:

越复杂 → 越玄学 越细节 → 越离谱 越真实 → 越不行

原因很简单:

  • 它没有产品逻辑
  • 它没有业务上下文
  • 它没有边界意识
  • 它没有安全意识
  • 它不会思考后果,只会预测下一个 token

所以你会看到一堆“莫名其妙但看似合理”的错误:

  • 漏字段、漏条件、逻辑跳步、变量改名、错误兜底消失……
  • 你越让它补,它越能给你补出一个“新 bug 平行宇宙”。

新手的做法是:一句接一句喂给 AI,让 AI 修复 bug。(是不是?有时一个问题喂了一下午,AI 也没有解决)

老手的做法是:“行了行了,我自己来。”

于是,“善后工程师”诞生了。

02. AI 做到 80 分很快,但 80→100 分是地狱难度

为什么?因为 AI 不擅长“确定性”。看几个你一定遇到过的 AI 产品灾难:

1)边界没处理

用户输入异常 → 直接报错
接口没数据 → 直接挂
Token 失效 → 再见

AI 永远假设:输入是完美的,网络是稳定的,用户是理性的。
现实是:用户会输入表情包,会断网,会疯狂点击按钮。

2)异常没兜底

一个报错能把整个链路砸死。

3)安全全靠运气

XSS?SQL 注入?权限?
AI:我不知道,我只是预测文本。

4)性能烂得离谱

O(n³) 算法写得比谁都自信。

5)上下文混乱

API 字段昨晚叫 userId,今天变成 userID,明天变成 uid。

这些东西,AI 永远不会主动告诉你。

于是,你必须“善后”。

03. Agent 两大派系:为什么有的能落地,有的永远只能 PPT?

Agent 现在分两派:

A. 工作流型 Agent —— 现实可用,能规模化落地

它有 SOP(标准流程):

输入 → 处理 → 输出

边界明确,有轨道可跑。 可靠、可监控、结果可控。

适用场景:

  • 客服机器人(固定问答流程)
  • 代码审查(检查清单明确)
  • 数据处理(ETL 流程标准化)
  • 文档生成(模板 + 规则)

为什么能落地? 因为可靠性 > 灵活性。

所以大厂能用的,都是这种。

B. 自主型 Agent —— 自由灵魂,现实灾难

目标模糊、行为难控、结果不可复现。今天帮你干活,明天给你整活。

适合展示,但绝不适合生产。现实问题:

  • 今天帮你发邮件,明天给老板发了辞职信
  • 今天帮你买东西,明天把你银行卡刷爆
  • 今天帮你整理文件,明天把重要文档删了

核心原因:自由度越大,不确定性越大,风险越高。

这也是为什么创业公司喜欢吹自主 Agent,而工程团队只做工作流 Agent。

04. 为什么“善后工程师”是 AI 产品落地的关键?

因为真正的产品,不是能跑就算结束。是能:

  • 持续跑
  • 正确跑
  • 安全跑
  • 高性能跑
  • 在各种诡异边界下依然跑

这些,统统需要人类开发者介入。

善后工程师究竟在干什么? 一句话:

把一个“看上去能用”的 AI 产物,变成“真的能上生产”的产品。

细化下来就是:

① 校对

检查 AI 生成物的逻辑漏洞:

  • 分支有没有漏?
  • 字段是不是一致?
  • 状态是否可能错乱?
  • 异常是否处理?

举例:

// AI 生成的登录逻辑:
if (password === user.password) {
  login()
}

// 善后工程师补全:
if (!user) return { error: '用户不存在' }
if (!password) return { error: '密码不能为空' }
if (user.status === 'banned') return { error: '账号已封禁' }
if (user.loginAttempts > 5) return { error: '登录次数过多' }
if (await bcrypt.compare(password, user.passwordHash)) {
  await resetLoginAttempts(user.id)
  return login(user)
} else {
  await incrementLoginAttempts(user.id)
  return { error: '密码错误' }
}

② 重构

让 AI 代码变得可维护:

  • 模块化
  • 类型补全
  • 结构优化
  • 单测补齐
  • 性能调优

比如说:AI 生成的"一锅乱炖"代码 → 善后工程师改成清晰的分层架构。

③ 打磨(很重要)

让产品真正能上线:

  • 边界处理
  • 异常兜底
  • 安全策略
  • 监控报警
  • 性能提升
  • 体验优化

这些才是决定产品能不能上线、能不能赚钱的部分。

05. 真相:AI 不是在替代工程师,而是在淘汰“只会写代码的工程师”

AI 做了工程师过去 60%~80% 的“体力活”。
但剩下的 20%,是「经验 + 思考 + 判断 + 产品理解」。

过去:

  • 工程师负责 0→100

现在:

  • AI 负责 0→80
  • 工程师负责最难的 80→100

这个 20%,决定了:

  • 产品能不能上线
  • 用户会不会崩
  • 公司能不能卖钱
  • 项目会不会翻车

所以,“善后工程师”不是低端岗位,而是价值更高的岗位。

真正被 AI 取代的,是那些:

  • 跟着教程敲
  • 不懂架构
  • 不看边界
  • 不做兜底
  • 不懂产品逻辑
  • 不理解业务场景

的 30 分工程师。

总结

目前来看,真正 AGI 到来之前,未来的软件开发将分成两类:

  1. AI 写的:快、便宜、能跑
  2. 工程师修的:稳、能上线、能赚钱

“善后工程师”的价值在于:

不是写代码,而是把错误的地方修对,把不可靠的地方补稳,把模糊的部分变清晰。

这才是 AI 时代真正的核心竞争力。你认为呢?

刚刚,阿里千问 APP 开启公测,要做中国版ChatGPT | 附实测

作者 莫崇宇
2025年11月17日 10:58

前几天,阿里「千问计划」被曝光。消息称,他们要基于全球性能第一的开源模型 Qwen3 打造个人 AI 助手「千问 APP」。

而就在刚刚,靴子终于落地。

阿里官宣千问 APP 公测版上线。除了聊天足够聪明外,「能办事」将是千问 APP 未来发展的一个重要方向。

我们也第一时间分别对千问和 ChatGPT 进行了简单的测试,看看这个后来者到底有什么底气。

屋里三盏灯,屋外三个开关,一个开关仅控制一盏灯,屋外看不到屋里。怎样只进屋一次,就知道哪个开关控制哪盏灯?

这是一道经典的逻辑题。两者都给出了正确答案,即利用灯泡的温度差异来判断:

打开一个开关等待几分钟,然后关闭它,再打开第二个开关,进屋后就能通过灯的亮灭和温度判断三个开关。虽说千问的回答更详细一些,但对于这种思路题来说,只要核心逻辑正确即可。这一轮算是打平。

「解释为什么生食和熟食必须使用不同的砧板与刀具。」

这道题就拉开差距了。GPT-5.1 Auto 的回答比较简单,基本是从知识库里挑选了一些标准答案,谈交叉污染、细菌传播等常识,多少有些敷衍的意思。

而千问的表现让人眼前一亮:它懂得主动搜索,引用了 14 篇相对权威的资料信息,甚至还配上了对应的图片、文章等富媒体内容。内容丰富且有据可查。这一轮,千问明显更胜一筹。

最后一道题是个硬核编程题:用一个 HTML 文件实现一个 Game Boy 模拟器,包含俄罗斯方块、宝可梦、塞尔达等经典游戏的全功能模拟,所有按钮可触控也可键盘操作。

▲左为 Qwen,右为 ChatGPT

从最终效果看,两者各有千秋。千问在视觉效果上做得不错,界面还原度挺高,确实抓住了 Game Boy 的感觉。

GPT-5.1 Auto 的界面设计有些敷衍,俄罗斯方块虽然能跑起来,但实际上只有一个方块能移动,功能并没有真正实现。但客观来说,在单个 HTML 文件里完整还原 Game Boy 模拟器本身就是极高难度的任务,两者都没能做到尽善尽美。

这一轮也算平局。

ChatGPT 已经是全球 AI 的代名词,豆包和元宝背靠抖音、微信这种日活数亿的超级应用。千问没有这种「天然流量池」,这就决定了它必须在产品体验上做出显著差异化,才能说服用户专门下载一个新应用。

从产品策略来看,国内外 AI 助手已经走上了不同的路径。

ChatGPT 相对独立,主要靠订阅费和 API 调用盈利。而国内的 AI 助手则更强调生态整合:元宝可以直接在微信内使用,豆包即梦和抖音内容创作工具打通,千问如果成功,可能会直接导向淘宝购物。

这种「AI+X」的组合模式——AI+电商、AI+社交、AI+内容创作,本身就是巨大的商业价值,而不只是依赖订阅费。

实际上,过去阿里的 AI 更多集中在 B 端领域,Qwen 模型技术实力很强,在全球开源社区的下载量已经是第一,但普通消费者感受不到。而在 AI 领域,用户的品牌忠诚度很薄弱,哪个好用就用哪个。

千问的机会窗口可能很窄,如果不能迅速打出知名度,之后再追就更困难了。

不过机会也确实存在。全球范围内,对 AI 产品有需求但被 ChatGPT 的价格或使用门槛挡在外面的用户,其实数量庞大。如果千问能够以更友好的价格、更低的使用门槛、更强的多语言支持,去覆盖这部分市场,可能会找到一个差异化的生存空间。

真正的较量或许才刚刚开始,千问 APP 的推出也意味着阿里真正开始重视 AI 的下半场,即让 AI 走进普通人的日常。而对我们用户来说,全球市场上多一个能打的选手,对所有人都是好事。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


昨天以前首页

一文搞懂 AI 流式响应

2025年11月15日 12:51

这是 OpenAI 文档中流式响应的代码 platform.openai.com/docs/guides…

import { OpenAI } from "openai";
const client = new OpenAI();

const stream = await client.responses.create({
    model: "gpt-5",
    input: [
        {
            role: "user",
            content: "Say 'double bubble bath' ten times fast.",
        },
    ],
    stream: true,
});

for await (const event of stream) 
    console.log(event);
}

在我第一次看到这段代码时,有无数的疑惑出现在了我的大脑中:

  • stream 是什么?
  • 为什么可以通过 for await of 来遍历?
  • 这和异步有什么关系吗?
  • 服务端要如何将 stream 一点点返回给前端?
  • 前端要如何接收数据?
  • ……

如果你也有类似的疑问,请耐心阅读本文,相信你一定能找到答案。

本文的代码在这里github.com/wangkaiwd/a… ,建议结合文章一起阅读

Iterable protocol 和 Iterator protocol

支持 for...of 循环的变量,一定要符合 Iterable protocolIterator protocol

Iterable protocol :

  • 变量是一个对象
  • 对象必须实现 [Symbol.iterator] 方法
  • [Symbol.iterator] 方法必须返回遵循 Iterator protocol 约定的对象

Iterator protocol :

  • 变量是一个对象
  • 对象必须实现 next 方法
  • next 方法要返回一个对象 { done: boolean, value: any }
    • done 表示迭代是否结束
    • value 表示迭代器的返回值

下面是一个示例:

function makeIterableObj (array: any[]) {
  return {
    [Symbol.iterator] () {
      let nextIndex = 0
      return {
        next () {
          if (nextIndex < array.length) {
            const result = { value: array[nextIndex], done: false }
            nextIndex++
            return result
          }
          return { done: true, value: undefined }
        },
      }
    },
  }
}

const iterableObj = makeIterableObj(['one', 'two'])

可以手动循环 iterableObj

const iterator = iterableObj[Symbol.iterator]()
while (true) {
  const { value, done } = iterator.next()
  if (done) {
    break
  }
  console.log('value', value)
}

// 输出结果
// value one
// value two

也可以通过 for...of 来循环 iterableObj :

// 这里的 item 就是 next 方法执行后得到的 value
for (const item of iterableObj) {
  console.log('item', item)
}

// 输出结果
// item one
// item two

Async iterable protocol 和 Async iterator protocol

理解了 iterable protocoliterator protocol 再来理解 async iterable protocolasync iterator protocol 就会容易很多。

异步相比于同步,有以下区别:

同样的示例改为异步版本:

const sleep = (result: IResult) => {
  return new Promise<IResult>((resolve) => {
    setTimeout(() => {
      resolve(result)
    }, 1000)
  })
}

function makeIterableObj (array: any[]) {
  return {
    [Symbol.asyncIterator] () {
      let nextIndex = 0
      return {
        next () {
          if (nextIndex < array.length) {
            const promise = sleep({ value: array[nextIndex], done: false })
            nextIndex++
            return promise
          }
          return sleep({ done: true, value: undefined })
        },
      }
    },
  }
}

手动循环:

const asyncIterableObj = makeIterableObj(['one', 'two'])
const iterator = asyncIterableObj[Symbol.asyncIterator]()
while (true) {
  const { value, done } = await iterator.next()
  if (done) {
    break
  }
  console.log('value', value)
}

使用 for await ... of 循环

for await (const item of makeIterableObj(['one', 'two'])) {
  console.log('item', item)
}

此时再回到开篇的示例:

const stream = await client.responses.create()

stream 其实就是一个遵循 async iterable protocol 的对象

可读流 ReadableStream

下面是一个 ReadableStream 的示例:每隔 1s 向流中写入4个字符,直到字符完全写入到流中

let mockData = `This is a sample string that will be streamed in chunks.`

let timer: any = null
const step = 4

const stream = new ReadableStream({
  start (controller) {
    timer = setInterval(() => {
      const chunk = mockData.slice(0, step)
      // 删除已经写入的字符
      mockData = mockData.slice(step)
      if (!mockData) {
        // 字符处理完成后,停止写入
        controller.close()
        if (timer) {
          clearInterval(timer)
          timer = null
        }
      }
      // 添加字符到 stream
      controller.enqueue(chunk)
    }, 1000)
  },
  cancel () {
    clearInterval(timer)
  },
})

ReadableStream 默认实现了 Symbol.asyncIterator ,所以它是一个异步可迭代对象,可以使用 for await ... of 来循环

for await (const chunk of stream) {
  console.log('chunk', chunk)
}

ReadableStream 自己也提供了 getReader 方法来读取流:

const stream = createStream()
const reader = stream.getReader()
// 循环直到 done 为 true 时结束
while (true) {
  const { done, value } = await reader.read()
  if (done) {
    break
  }
  console.log('value', value)
}

这是 mdn 官方仓库中的一个示例,也可以结合一起学习:github.com/mdn/dom-exa…

服务端 SSE

目前的 AI 应用服务端流式响应使用 Server-Sent Events 来实现,简称 SSE 。下面是 ChatGPT 网页版的响应内容:

mdn 的相关介绍在这里:developer.mozilla.org/en-US/docs/…

sse 示例

MDN 的示例是使用 PHP 实现的,代码比较难懂,我也没有找到一个可以直接运行的案例。为了方便理解,我参考 stackoverflow.com/questions/3… ,使用 express 实现了流式响应:

import express from 'express'

const app = express()
app.use(express.static('public'))

app.get('/countdown', function (req, res) {
  // sse 响应头设置
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  })
  let timer: NodeJS.Timeout | null = null
  let count = 10
  timer = setInterval(() => {
    if (count >= 0) {
      // 返回内容必须严格遵守格式
      res.write('data: ' + count + '\n\n')
      count--
      return
    }
    // count 小于0时,停止响应
    if (timer) {
      clearInterval(timer)
      timer = null
    }
    res.end()
  }, 1000)
})

app.listen(3000, () => console.log('SSE app listening on port 3000'))

这段代码会每隔 1s 在响应中写入 count ,直到 count < 0 时结束响应。

代码中以下内容需要注意:

  • 响应头设置: 'Content-Type': 'text/event-stream'

  • 返回内容必须严格遵守格式: data: + 空格 + 字符串 + 两个换行符 (\n\n)

AI 流式响应

上面我们先实现了一个简单的流式响应,现在我们把 AI 结合进来

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  baseURL: process.env.OPENAI_BASEURL,
})

const app = express()
app.use(express.static('public'))

app.get('/chat', async function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  })
  const stream = await client.chat.completions.create({
    model: 'deepseek-chat',
    messages: [{ role: 'user', content: '你是谁?' }],
    stream: true,
  })
  for await (const chunk of stream) {
    const content = chunk.choices[0].delta.content
    // 注意:这里通过 JSON.stringify 来返回 JSON 字符串,更加灵活
    res.write(`data: ${JSON.stringify({ content })}\n\n`)
  }
  res.write(`data: [DONE]\n\n`)
  res.end()
})

app.listen(3000, () => console.log(`
SSE app listening on port 3000
Open http://localhost:3000/sse-ai.html in your browser to access page.
`))

有以下几点需要注意:

  1. 如果使用的是 OpenAI 兼容的 api ,例如我在当前示例中使用的 deepseek ,要使用之前的 OpenAI 请求标准:github.com/openai/open… 。文档中有提到,但是我一开始没有仔细看文档,困扰了很久。 用法和传参都不一样,需要特别留意
  2. 返回内容要通过 JSON.stringify 来处理,方便我们给前端返回更多字段
  3. 结束时返回 res.write('data: \[DONE]\n\n') ,方便前端使用 EventSource 时终止请求

前端处理流式响应

EventSource

前端可以使用 EventSource 来处理 sse 响应的内容,代码如下:

const stop = document.getElementById('stop')
const start = document.getElementById('start')
let eventSource = null
start.addEventListener('click', () => {
  const eventSource = new EventSource('/chat')
  eventSource.onmessage = function (event) {
    // 要手动关闭,否则会一直请求服务器
    if (event.data === '[DONE]') {
      eventSource.close()
      return
    }
    const json = JSON.parse(event.data)
    document.getElementById('log').innerHTML += json.content
  }
})
stop.addEventListener('click', function () {
  eventSource.close()
})

完整代码:github.com/wangkaiwd/a…

EventSource 有一个细节需要注意

如果没有调用 eventSource.close() 方法,那么请求会一直不停的发起。所以我在服务端特意在响应结束时返回 data: [DONE]\n\n 来让前端知道什么时候关闭 eventSource

fetch

前面我们介绍了通过 EventSource 来处理服务端的流式响应,但其实它存在很多问题:

  • 只能发起 get 请求
  • 请求参数只能在 url 中传递,但是一般要传入给 AI 的提示词长度可能较大,容易超过 url 长度的最大限制
  • 无法自定义请求头来设置 Authorization ,给服务端传递用户 token

基于上述的这些原因,我们通常会使用 fetch 方法来处理服务端的流式响应。github.com/Azure/fetch… 就是基于 fetch 实现的用来发起 EventSource 请求的开源库,下面是它的使用示例:

<script type="module">
  import { fetchEventSource } from "https://esm.sh/@microsoft/fetch-event-source";

  const stop = document.getElementById("stop");
  const start = document.getElementById("start");
  const controller = new AbortController();
  start.addEventListener("click", () => {
    // 发起post请求
    fetchEventSource("/chat", {
      signal: controller.signal,
      method: "POST",
      // 一点点处理服务端响应
      onmessage: (event) => {
        const data = event.data;
        if (data === "[DONE]") {
          console.log("done");
          return;
        }
        const json = JSON.parse(data);
        document.getElementById("log").innerHTML += json.content;
      },
    });
  });
  stop.addEventListener("click", function () {
    controller.abort();
  });
</script>

完整代码:github.com/wangkaiwd/a…

这里使用的 POST 请求,我把服务端的示例改为了 all 方法来接收请求,可以同时处理 GETPOST 请求

我们也可以自己通过 fetch 请求来看看具体的响应内容

const response = await fetch("/chat", {
  signal: controller.signal,
  method: "POST",
});

这里的 response.body 就是一个 ReadableStream (ps: 前面的章节有介绍过ReadableStream ,忘记的同学可以再回去看一下 ),所以我们可以通过 for await ... of 或者 getReader 方法来拿到 ReadableStream 中的数据:

const textDecoder = new TextDecoder();
// response.body 是可读流
for await (const chunk of response.body) {
  // chunk 是 Uint8Array ,通过 TextDecoder 转换为字符串
  console.log('chunk', chunk)
  const text = textDecoder.decode(chunk);
  if (text === "[DONE]") {
    console.log("done");
    return;
  }
  console.log('text', text)
}

// 使用 getReader 方法获取数据
//   const reader = response.body.getReader();
//   while (true) {
//     const { done, value } = await reader.read();
//     if (done) {
//       break;
//     }
//     const text = textDecoder.decode(value);
//     if (text === "[DONE]") {
//       console.log("done");
//       return;
//     }
//     console.log('text', text)
//   }

最终结果如下:

我们拿到的是服务端返回符合 SSE 规范的字符串,将字符根据规则解析后,就能拿到最终的结果了。这其实就是 fetch-event-source 帮我们实现的逻辑

踩坑

我在使用 fetch-event-source 的过程中发现了如下问题:

如果服务端返回的内容只包含 \n ,那么前端接收到的内容为空字符。在 markdown 渲染的场景下,会导致格式完全错乱。 下面是伪代码,方便理解

// 服务端如果返回的内容如果只包含 \n
res.write('data: ' + '\n\n' + '\n\n')

// 前端拿到的内容为空字符串
onmessage: (event) => {
  const data = event.data;
  // true
  console.log(data === '')
}

官方也有相关的 issue 一直没有修复:github.com/Azure/fetch…

所以在使用 fetch-event-source 时可以通过 JSON.stringify 来传入 json 字符串,防止前端接收到空字符串

const content = chunk.choices[0].delta.content
// JSON.stringify 避免了返回内容只有 `\n` 的情况
res.write(`data: ${JSON.stringify({ content })}\n\n`)

结语

AI 出现之前,这些知识很少有使用场景。但随着 AI 的快速发展,这些代码不断地出现在我眼前,也让我有了更多实践的机会。这篇文章是我在实践中的一些沉淀和总结,希望能帮到你。

参考

Vibe Coding:人机共生时代的开发革命 —— 从概念到 Chrome 扩展实战

作者 有意义
2025年11月13日 00:23

Vibe Coding:用氛围编程解锁零代码创造

Vibe Coding(氛围编程)是由 OpenAI 联合创始人 Andrej Karpathy 提出的全新编程理念,核心是通过自然语言交互与 AI 协作,让创造不再受限于代码编写能力。这种模式下,低代码、零代码工具与编程智能体(如 Trae)成为核心载体,普通人只需明确想法,就能快速落地各类产品,打破了技术壁垒对创意的束缚。

从工具到创意:Vibe Coding 的落地场景

Vibe Coding 的应用范围极具弹性,小到轻量工具,大到商用产品,都能通过 AI 协作快速实现。其中,Chrome 扩展程序是最直观的落地场景之一 —— 就像经典的 JSONView 扩展,仅需聚焦 “接口 JSON 数据格式化展示” 这一核心需求,就能通过 AI 生成工具,解决开发中的实际查看痛点。

而借助 Vibe Coding 模式,我们能更进一步:无需深陷代码细节,只需清晰传递功能目标,AI 就能完成从逻辑到实现的全流程,让每一个实用想法都能快速转化为可用工具。

实战:Vibe Coding 协作型 Chrome 扩展开发

以 Chrome 扩展开发为例,Vibe Coding 的核心是 “协作” 而非 “独写”,具体落地路径简单高效,且已通过实际测试验证可行性:

1. 明确协作输入

  • 用自然语言 prompt 精准描述任务,比如 “开发一个 Chrome 扩展,实现接口 JSON 数据的格式化展示与折叠查看”。
  • 补充设计稿或线框图,明确界面布局与交互逻辑,减少 AI 理解偏差。

AI编辑器关键词:

请根据@instruction.txt当中的需求帮我开发对应的chrome扩展程序。
首先列出项目的文件目录,然后把对应的文件代码补充完整。

instruction.txt

  你是一个经验丰富的Chrome 拓展程序开发者,请帮我开发一个名为Hulk的拓展程序
  UX 设计图参考ux.jpg。

  具体交互步骤如下:
  Step1:点击程序图标打开弹出窗口,在窗口中默认提示:“改变背景颜色”、“点击下方按钮将当前页面背景色改为绿色”和一个“改变颜色”按钮

  Step2:点击按钮,网页背景改变为绿色。

  注意以下两点:
  请使用icons文件夹的图标作为应用程序的图标

image.png

然后我们看cursor给我们生成的代码就好。 popup.css文件

:root {
  color-scheme: light dark;
  font-size: 16px;
  font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
}

body {
  margin: 0;
  padding: 16px;
  min-width: 240px;
  background-color: #0f1115;
  color: #f1f5f9;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.hero h1 {
  margin: 0 0 4px;
  font-size: 1.125rem;
}

.hero p {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.4;
  color: #cbd5f5;
}

button {
  padding: 12px 16px;
  border: none;
  border-radius: 8px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  color: #0f172a;
  background-color: #22c55e;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

button:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(34, 197, 94, 0.4);
}

button:active {
  transform: translateY(1px);
  box-shadow: none;
}

.status {
  margin: 12px 0 0;
  font-size: 0.9rem;
  line-height: 1.4;
  color: #34d399;
}

.status.error {
  color: #f87171;
}

popup.html文件

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <title>Hulk</title>
    <link rel="stylesheet" href="popup.css" />
  </head>
  <body>
    <header class="hero">
      <h1>改变背景颜色</h1>
      <p>点击下方按钮将当前页面背景色改为绿色</p>
    </header>
    <main>
      <button id="changeColorBtn" type="button">改变颜色</button>
      <p id="statusMessage" class="status" role="status" aria-live="polite"></p>
    </main>
    <script src="popup.js"></script>
  </body>
</html>

popup.js文件

const changeColorBtn = document.getElementById("changeColorBtn");
const statusMessage = document.getElementById("statusMessage");

function setStatus(message, isError = false) {
  if (!statusMessage) return;
  statusMessage.textContent = message;
  statusMessage.classList.toggle("error", isError);
}

async function changeBackgroundToGreen() {
  try {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    if (!tab || !tab.id) {
      console.warn("未找到活动标签页。");
      setStatus("未找到可操作的标签页。", true);
      return;
    }

    if (!tab.url || /^chrome:|^chrome-extension:|^chrome-devtools:/.test(tab.url)) {
      setStatus("无法修改受保护页面(如 chrome://)。", true);
      return;
    }

    await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: () => {
        document.body.style.setProperty("background-color", "#16a34a", "important");
      }
    });
    setStatus("已应用绿色背景。");
  } catch (error) {
    console.error("注入脚本失败:", error);
    setStatus("修改失败,请检查权限或重试。", true);
  }
}

changeColorBtn?.addEventListener("click", changeBackgroundToGreen);


重构协作逻辑:文档大于代码

与 Trae 等 AI 编程智能体合作时,编码本身不再是核心,完整的上下文文档才是高效产出的关键。这些文档会成为 AI 生成代码的决策依据,避免反复修改:

  • 需求文档:明确扩展的核心功能、使用场景与预期效果。
  • 技术文档:指定开发规范、依赖工具与适配要求。
  • 设计稿 / 线框图:定义界面元素、交互逻辑与视觉风格。
  • 接口文档:说明扩展所需调用的接口、参数与返回格式。
  • 测试文档:列出功能测试点与兼容性要求。

打开chrome://extensions/

点开开发者目录,在浏览器打开得到:

image.png

完成代码生成后,通过 Chrome 浏览器的 “开发者模式” 加载扩展目录,即可快速验证效果 —— 从需求描述到功能落地,全程无需手动编写大量代码,仅需聚焦核心逻辑与质量监督。

Vibe 协作:人机共生的创造新范式

Vibe Coding 的本质是 “人机分工”,找到与 AI 编辑器的最佳合作节奏,就能最大化创造效率:

  • 核心原则:提供清晰、完整的上下文文档,让 AI 明确方向。
  • 分工逻辑:将 AI 擅长的代码生成、语法校验等工作交给 Trae,人类则聚焦 AI 不擅长的需求拆解、逻辑把关与质量监督。
  • 成长路径:在协作中同步积累能力,重点掌握需求文档撰写、线框图绘制、项目目录架构设计等核心技能。
  • 核心心态:保持耐心 “氛围编程”,人机磨合的过程也是创意逐步落地的过程,无需追求一步到位。

AI 生成的歌首次登顶权威音乐榜,还把人听哭了?

作者 Selina
2025年11月14日 14:19

无人注意的角落里,权威榜单 Billboard 接二连三地迎来一批新歌手上榜,低调但行动快速,闷声就登顶了。

等一下等一下,大家发现:什么?又是 AI?

榜一《Walk My Walk》是 AI 生成的歌曲作品你,从数据来看,它不仅登顶了,还连续在榜了三周。对于任何一个新人来说,都是堪称「爆」了的成绩。

然而这不是人,只是 AI。Billboard 发现,登上自己榜单的 AI 歌手和歌曲,已经越来越多了。

冠军?什么来路?

其中一位 AI 歌手,Xania Monet ,出道两三个月,发过的歌却个个受欢迎。最新的《How Was I supoosed to Know?》在油管直接是百万播放量,这还没算 Tiktok 和 Instagram 的数据。

自夏天出道以来,Xania Monet 已在 Billboard 的多个排行榜上取得佳绩,不到两个月内,已创造超过 5 万美元收入。

AI 歌手并不是新鲜事,甚至,用 AI 做一个数字人形象,也不是新鲜事。但我确实想知道,Xania Monet 为什么会有如此的表现。

要知道,很多人不只是看不出来她是 AI,更加是觉得她的歌很好听,很动人。

这不高低得尝尝咸淡。听了几首之后,我很快意识到为什么:主题。

Xania Monet 的歌在旋律上并没有什么特别之处,这很正常,AI 生成的音乐,不会偏离主流形态太远,毕竟是数据算出来的。

唱法层面,该有的细节都有:颤音、转音、声区切换,但还是那句话,这些都是可以生成的。而且在一些细节上,还是有瑕疵。比如在一些高音时,很像是挂了 auto-tune,有点点轻微的「电音」感。

可是,她的歌全部围绕着当代人的 emo 情绪出发,歌词写得相当切中人心。

看看这个歌词,非常写实地描绘了恋人之中,只有一方付出,另一方只会闪躲的情态。这首歌就叫做《I asked for so little》,可以翻译成「我要的不多」,典型的苦情歌,受到欢迎完全不稀奇。

在这首《Still not choosing me》中,写的是为什么「我爱的人不爱我」,很常见的主题,经典永流传。没有人永远失恋,但总有人正在失恋着。

之前的视觉效果 AI 感还是很强,尤其是视频,基本上经不起细看。

但是到了她的大热单曲《How Was I supoosed to Know》,不仅是唱感情问题,还唱到了原生家庭伤痛:父母没有教过什么是好的「爱」,只能让孩子带着伤痕,「错把虚情当真爱」。

这不得掀翻了社交媒体,简直是 buff 叠满。

而且相比于早期简陋的动态歌词板,《How Was I supoosed to Know?》制作精良了不少,不仅没有粗糙的 AI 感,音乐编曲也好了不少。片尾一看,多了许多名字——背后有人了。

Xania Monet 出道没多久,就能够强势上榜,自然也引来了不少橄榄枝,很快就签了公司 Hallwood Media,经纪合约价值三百万美元。

难怪,有了公司,新歌档次都上去不少,登顶榜单也就不奇怪了。

等下,你说你去找了 Billboard,没看到这首歌?

登顶,但没完全登顶

这里的确有一个小小的信息差:Xania Monet 登上的是垂类电台榜单之一,Adult R&B Airplay Chart 成人 R&B 电台播放榜。

这是一个衡量美国成人 R&B 广播电台播放频率的榜单,以电台播放数据为口径。要知道,现在美国依然有大量覆盖全国或地区的 FM/AM 广播电台,这些电台每天仍然需要大量歌曲轮播,并且还是保持着人工编辑的体系。

Billboard 与一个叫 Mediabase 的监测机构合作,利用自动识别技术(audio fingerprinting),监控这些电台在每小时、每个地区播放了哪些歌,统计播放次数、时间段、地区等,最终形成榜单。

这意味着,Xania Monet 的歌想要被电台播放,仍然需要编辑加入到播放列表当中。她能登顶,恰恰意味着这些歌已经被不少 R&B 电台认可并轮播,进入了日常听众的耳朵中。

平时开着车、做着家务而随手打开电台的听众,可能根本不知道她是 AI 歌手。

这使得她登顶的榜单,意义更加独特:她恰恰是因为已经在社交媒体上很红了,才进入电台视野。

简单点说:Xania Monet 无论是在流量层面,还是在品质层面,都出现了一些「逆转」。尽管专业圈内人还是批评态度,一般听众却相当受感动。

类似的评论还有很多,般听众并不会细究创作过程,他们更关心歌曲本身能带来怎样的情绪体验。

不过,这并不意味着 AI 就已经登峰造极,可以写出打动人心的歌了——尤其是 Xania Monet 的例子里,她的旋律和演唱是由 AI 生成的,可是歌词,却完全是来自人类创作者。

Xania Monet 背后,是一位叫做 Telisha “Nikki” Jones 的创作者,她并非专业歌手,但是热爱写诗填词。今年她接触到了 Suno,尝试把自己写的诗歌和歌词输入进去,设定诸如「灵魂唱腔」「慢板 R&B 风格」「轻吉他配重鼓点」等一系列风格关键词,然后让 AI 创作出完整的歌曲。

歌词 100% 源自琼斯本人的经历和情感,例如《How Was I Supposed to Know?》,灵感正是来自 Jones 童年时就失去父亲的真实创痛,这些发自肺腑的诗句后来成为歌曲的核心。

歌词和主题的确是 Xania Monet 最出挑的地方,当然,歌曲和演唱也没有拖后腿,都是在平均水准之上的。主歌旋律通常舒缓真挚,副歌迸发情感张力。

在 Jones 的设定中,Monet 的嗓音突出灵魂乐质感,唱腔也一下就抓住了听众的耳朵。再加上歌词写得细腻动人,全部加在一起,这才能如此受到欢迎。

可以说,Xania Monet 提供了一个 AI 创作的「高分示范」:保证核心内容(主题歌词)的品质,同时完全原创,从而规避版权风险。从音乐作品到人设都走真情路线,而不是「为了 AI 而 AI」,把生成本身当噱头。

听众更容易把她看作一个有血有肉的新人歌手来欣赏,自然比面对一个夸张虚拟网红时更能产生好感。

最关键的一点:作品本身够打动人。这也是最「背反」的一点,回想我一开始听 Xania Monet 的歌时,已经知道了她是 AI,所以从未关注她的唱法,却能够一下子识别出歌词和主题是她的突出点。

这是不是意味着,AI 始终难以越过一道天堑,那种细腻幽深的情感,归根到底无法「生成」,只能来源于人自身。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


参加完百度世界大会,我终于理解了「内化 AI」的正确打开方式

作者 莫崇宇
2025年11月13日 18:01

那天下午,村官小芳接到环卫阿姨的电话,「小芳啊,我被骗了……」400 块钱,半个月的工资,就因为刷了个短剧不见了。

作为村官的小芳,觉得要做些什么,于是决定给村里的老人们上一堂反诈课。可时间紧迫,课件材料哪里来呢?

她想起之前看过的百度文库 GenFlow,抱着试试看的心态,她打开它,用最朴素的语言描述了自己的需求。

三分钟后,一套完整的反诈宣传方案出现在屏幕上:有 PPT、有演讲稿、有醒目的海报,甚至还有一套给老人准备的互动小测试。第二天,反诈课堂的教学效果出奇地好,老人们听得专注、学得认真、记得牢靠。

小芳既没学过代码,也不了解 AI,高新科技对她来说还是太过遥远。但她只是对着 GenFlow 说出了一个普通人最真实的困境,而 AI ,听懂了,并且帮她实现「意料之外」的交付。更重要的是,这次的尝试彻底改变了小芳的工作习惯。AI 不再是个偶尔用用或者是临时抱佛脚的工具,而是成了她解决疑难、急迫问题时的首要选择。

在讨论 AI 如何改变世界的宏大叙事之前,我们或许更应关心的是,它如何让一个普通人的今天,过得更好一点。

不懂也能用的 AI, 才是真正的 AI

时间的指针拨回三年,想用好 AI 其实是一件麻烦事。

那时, AI 提示词还是社交媒体上的硬通货,大家互相分享魔法「咒语」。你得先学会「如何跟 AI 说话」——提示词怎么写、任务怎么拆解、参数怎么调优。

但技术进步最直观的标志,就是让使用变简单、易上手。你不需要懂发电原理就能开灯,也不需要懂 TCP/IP 就能刷社交媒体。现在用 AI 也一样——简单直接地说人话就行。

郑州大学副教授马竞的经历,是这个转变的生动注脚。

他基于百度飞桨的开发能力,设计了一套视觉检测系统,通过视觉大模型分析猪只健康状态。比如可以分析猪群进食行为,精准识别离群、躺卧等异常状态。

养猪和 AI 本是风马牛不相及,而马竞也是力学出身,按理说跟计算机视觉更是隔行如隔山。

但百度飞桨把这些复杂的东西都打包好了——成熟的视觉识别能力可以直接拿来用,就像搭积木一样组合到养猪场景里。基于飞浆落地的自动饲喂 AI 系统,能极大程度地提升了饲喂效率,保障了猪群健康水平。如今已经覆盖了全国 9 个城市 23 个养殖场,销售额已达 1400 多万元。

「拿来即用的 AI」第一步是解决了用户的使用门槛问题,但真正的改变还在后面:好的 AI 不再需要你适应它,它可以开始自己适应你。

53 岁的货拉拉司机毛辉珍,一年跑 12 万公里。她说百度地图像个陪她聊天的伙计,能够学习她的驾驶习惯,知道她喜欢走哪种路,在她需要时提醒她:注意车速、注意路况,是个贴心又懂她的「路伴」。

毛辉珍在用 AI,AI 也在「读懂」毛辉珍。这是一种双向的学习:地图在学习她的习惯,她也在学习如何更高效地与智能系统协作,让每一次出行都更顺畅。

又比如,16 岁的陈君航用文心一言从零学会 Python。他可以边学边做,在实践中学习。AI 能够根据他的需求和水平实时调整,而陈君航也在这个过程中,把编程思维逐渐变成了自己思考问题的方式。

想法产生的那一刻,就是实现的起点。

虽然小芳、马竞、毛辉珍、陈君航,都不是 AI 技术专家,但都在用 AI 解决实际问题。三年前人们争论「AI 会不会取代人」,如今看来,问题问错了。真正的问题是:AI 到底能帮助到谁?

而答案显而易见:会说人话的人,有真实需求的人,想解决实际问题的人。换言之,也就是每一个人。

技术的温度, 藏在你看不见的地方

村官小芳打开百度文库,说句话就能让 AI 实现交付,为她办事。但 22 岁失明的毛凡,需要的是另一种理解。

遗传性视神经萎缩, 全球患病率约十万分之一。身患此病的毛凡重返武汉科技大学后, 最头疼的事是打车。

司机能不能找到我? 上车后怎么确认目的地? 打车的目的地定位是否准确?普通人觉得理所当然的事, 对他来说都是巨大的心理负担。直到他遇到萝卜快跑。车门打开有提示音, 全程语音操作, 按钮是凸起的,一切都是那么自然。

「同学约我去 KTV, 都让我叫车, 就想坐第六代。」毛凡说到这里,有点小得意。

这份「小得意」的背后,却隐藏着 AI 产品设计的同理心。萝卜快跑团队发现, 视障人士不需要特殊照顾的怜悯感, 他们需要平等使用的尊严感。所以团队没有简单地加个「盲人模式」, 转而重新思考了整个交互逻辑。

「对于普通人来说, 看到什么就能操作什么;但对于我们视障者来说, 语音读出什么, 我们才能操作什么。」一位盲人女孩体验后如此形容。

技术需要放下健全人的认知惯性,真正进入视障者的感知世界。需要的不是同情心, 是换位思考的理解力。技术不仅要理解特殊群体的需求,还要在关键时刻真正介入他们的生活。

甘肃临洮县的经历能说明这一点。

县里曾有个大难题:如何减少年轻人跳河。2023 年 6 月, 百度智能云的「一见」平台在当地部署,AI 能识别异常行为, 在人跳河前报警。到 2025 年 8 月, 救下了 21 个年轻人。

21 条生命背后,21 个没有破碎的家庭,21 个没有被终止的未来。这本身就在传递一个信息:即使在你最绝望的时刻,社会没有放弃你。

有温度的 AI,既要理解人,也要在乎人。但如果只停留在这两步,技术能做的还远不止于此。

新疆温宿县的物理老师光头强用百度文库生成课件、动画, 给孩子们演示火箭发射原理。「我想将物理的种子埋在孩子心中, 让她们能够看到更大的世界。」AI 让光头强拥有了一线城市教师的教学工具,也让山区的孩子有了触碰梦想的可能。

百度创始人李彦宏说:「只有当 AI 被内化为一种原生的能力,才能真正在各行各业实现效果的涌现,进而引爆一场全面的生产力革命,推动经济增长, 才能真正让『智能红利』转化成『社会红利』。」

从这个逻辑来看,社会红利指向的不仅仅是 GDP 增长, 也是每一个被技术看见的普通人, 每一个因 AI 而获得尊严的弱势群体, 每一个被连接起来的情感瞬间。

萝卜快跑全球出行服务次数超 1700 万, 覆盖全球 22 座城市, 全无人驾驶平均行驶 1014 万公里才出现一次气囊弹出事故——超过了谷歌 Waymo。但对毛凡来说,萝卜快跑让他第一次感受到:自己和其他人一样, 可以自由地去任何想去的地方。

小芳、毛凡、光头强,他们的故事看起来很不一样,但背后有个共同的疑问:AI 是如何做到理解他们、适应他们、帮助他们的?

16.5 亿次调用背后,是每个普通人最真实的 AI 需求

在刚刚举办的百度世界大会 2025 上,APPSO 看到了一个答案。

今年以来, 百度连续发布 5 款大模型, 文心大模型日均调用量超 16.5 亿, 在理解、生成、逻辑、记忆能力上全面提升,百度智能云也连续六年蝉联中国 AI 公有云市场第一。

模型能力的持续涌现,正是 AI 能够理解人话、适应不同场景的基础。

但李彦宏认为, 模型能力的提升只是基础, 真正的变化在于「每个人都应该内化 AI 能力」——让 AI 成为每个人思考、创作、工作的一部分。

比如,在刚刚举办的百度世界大会 2025 上, 百度文库、百度网盘联合发布了 GenFlow 全新升级的 3.0 版本, 目前已是全球最大通用智能体。

想象一个场景:一位 AI 产品经理需要做数据分析、跑需求调研、写产品文档、画原型图、写月度总结…… 他只需要一次性下达任务,GenFlow 3.0 就能同时启动多线程:在 Excel 中搭建分析表格,在 Word 中生成产品需求文档,用 HTML 生成可交互的产品原型,最后汇总成汇报 PPT。

过去做完这些工作可能需要一整天,现在 3 分钟。时间被压缩了,人的角色也随之改变——从「执行者」变成了「决策者」和「创意者」。你不再需要把时间花在重复性劳动上,精力可以集中在真正需要思考的部分。

而当这种工作方式成为习惯,AI 就真正成为了你能力的延伸。

那么 GenFlow 3.0 为什么能做到如此流畅?答案藏在百度的全栈布局里。

从芯片层的昆仑芯,到框架层的飞桨,再到模型层的文心大模型,应用层的百度搜索、百度文库等,百度是全球为数不多进行全栈布局的人工智能公司。

今年 4 月, 百度点亮了自研 3 万卡级超大规模计算集群, 可同时承载多个千亿参数大模型的全量训练。7 月, 百度智算集群入选国家人工智能产业创新成果展。

在应用层面, 百度的 AI 重构已经深入到产品的每个细节。

百度搜索完成了十年来最大的改版。搜索框升级为「智能框」,搜索结果不再是一堆链接,而是直接给你富媒体内容,就像从查字典变成了问专家。

秒哒让完全不懂编程的人也能 3 分钟做出应用。新加坡国际大学的学生用它开发了个「技能五子棋」,获得 1.5 万+互动量。创造的门槛被拉低了,但创造的乐趣却一点没打折。

文心快码推出了行业首个多模态、多智能体协同 AI IDE,一位算法工程师因此进阶为全栈工程师。过去需要跨部门协作的事,现在一个人就能搞定。

百度文库智能 PPT 的月访问量超过 3400 万,位列全球第一。这个数字背后,是无数个像小芳一样的人,在用最简单的方式解决实际问题。

在今年 9 月份,李彦宏说:「模型发展到今天其实已经接近了那个临界点,很快就会有各种各样的非常有价值的应用能够创造出来。」

所谓临界点,就是你爸妈、朋友、身边的每一人都开始自然地使用 AI 的时刻。

全栈布局、算力集群、智能体平台,整条技术链最终服务于一个体验:你只管提需求,它负责实现。村官做课件,博士跨界,视障人士自由出行,山区孩子接触优质资源等,每个人都在用 AI 解决自己的问题。

在潜移默化的改变中, 小芳用百度文库做完第一次反诈课件后, 下次遇到类似任务会直接找它。陈君航用文心一言学会 Python 后, 编程能力就真的成了他自己的技能。

这更像一场「教学相长」,AI 在学习理解人的需求, 人也在逐步「内化 AI」, 探索如何更好地驾驭 AI,与 AI 协同的相处之道。

用着用着,人就长本事了。而技术的终极目标正是消解门槛:让每个人都能轻松用上 AI, 并让它成为能力的一部分。正如世界大会上百度创始人李彦宏所说:「当 AI 能力被内化,成为一种原生的能力,智能就不再是成本,而是生产力。」

这可能更是头部科技公司不断发展技术、深化应用效果的切实驱动力之本。

作者:莫崇宇、李超凡

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


Vibe Coding 实战!花了两天时间,让 AI 写了一个富文本渲染引擎!

2025年11月12日 23:58

一、先上效果图

最近动手实践了下 Vibe Coding,想尝试一行代码不写,纯通过 Prompt 让 AI 写了一个富文本渲染引擎

整体花了两天时间不到,效果如上图,支持的特性有:

  • 类似前端的 Block、InlineBlock、Inline 布局
  • 文本样式:加粗、斜体、下划线、删除线,前景色,背景色,同一行不同字体大小混排等
  • Attachment:图文混排或插入自定义 View 等
  • 异步排版和算高:基于 CoreText API,支持子线程布局算高,主线程渲染
  • 单测覆盖

项目用的 Claude AI,差不多耗费了 50$(是真的贵!但也是真的强!),本文将记录整个过程和一些经验总结。

二、过程记录

2.1 Claude 安装和项目初始化

Claude 安装和使用在网上有很多教程,细节这里不再赘述,推荐直接使用 VSCode 的 Claude AI 插件;后文「经验总结」部分也会总结 Claude AI 的常用命令,感兴趣可以直接跳转。

首先,我们需要新建一个空的 iOS 项目和富文本渲染引擎的 pod(这里我们叫 RichView),创建完成之后在 VSCode 中打开,点击右上角 Claude AI 的图标开启会话,输入/init 命令初始化工程。

/init命令的作用是让 Claude 理解整个项目,这是在项目中使用 Claude 的第一步,只需要执行一次就好。

/init会在根目录下自动创建一个CLAUDE.md文件,这个文件可以理解成全局上下文,即每次新开 Claude 会话都会自动加载其中的内容,我们可以在这里记录一些如修改历史、全局说明等内容。

2.2 技术选型、架构

让 AI 写代码,和我们自己写代码基本类似,不过是将我们的思路转换成 Prompt 告诉 AI。

编码之前需要先确定几件事情:这些确定好之后,我们后续的任务拆分才会更顺利。

1)需要支持哪些 Feature

  • 支持文本样式:加粗、斜体、下划线、删除线,前景色,背景色,同一行不同字体大小混排等
  • 支持 Attachment:图文混排或插入自定义 View 等
  • 支持子线程排版算高
  • 支持单元测试

2)技术选型

自定义富文本渲染引擎,最难的点在于如何实现精确的文本分词排版(原理可以参考从 0 到 1 自定义文字排版引擎:原理篇),iOS 有内置的 CoreText API(见链接)用于文本分词排版,当然也可以基于开源的跨端排版引擎 HarfBuzz(见链接)进行处理。

我们这里不需要跨端,因此选择 CoreText 作为方案选型。

官方封装的 NSAttributedString 当然也能做这件事情,但是从工程实践看,NSAttributedString 在扩展性(比如支持列表、表格等自定义布局)、使用方便性,以及长文本的性能方面不尽如人意。

3)技术架构

文本分词之后,还需要进行布局排版,为方便后续拓展布局,我们这里参考前端的布局模型,引入 Block、InlineBlock、Inline 的概念。

同时参考浏览器的布局渲染过程,引入三棵树的概念:

  • ElementTree:用户输入,整个富文本可以通过一颗 ElementTree 来表示
  • LayoutTree:负责布局排版,会在这一层处理好文本的分词、图文混排时各自的位置等
  • RenderTree:负责渲染,这一层接收布局完成的结果,进行最终的上屏绘制

敲定技术选型、技术架构之后,我们就可以按思路拆分子任务了。

2.3 子任务:ElementTree

由于我们参考了前端的布局模型,因此我们需要告诉 AI 在 CSS 中 Block、InlineBlock、Inline 的布局规范,这个在 MDN 中可以直接摘录,当然也可以直接让 AI 帮我们生成(如上图)。

接着,我们需要告诉 AI 怎么构建 ElementTree,也就是上图所示 Prompt。

最后,我们就可以让 AI 参照 Prompt,生成 ElementTree 了。

ElementTree 生成完成后,我们发现遗漏了单测环节,继续完善 ElementTree 的 Prompt,然后明确告诉 AI xx 文件新增了 xx 任务,让 AI 继续完成任务,如下图:

ElementTree 的创建还算比较顺利,AI 理解也比较到位,生成的代码基本符合预期。

2.4 子任务:LayoutTree

同样,我们定义好 Prompt,让 AI 生成 LayoutTree。

LayoutTree 的生成不太顺利,而且从最后的测试效果看也有很多 Bug,主要如下:

  • AI 将绘制相关逻辑也加到了 LayoutTree 中,但预期绘制是单独的 RenderTree
  • 布局问题:InlineBlock 无法整体换行,多个 Inline 在同一行时被换行展示,margin、padding 不生效等
  • 对齐问题:同一行包含不同字号的文本时,对齐方式不对
  • attachment 无法显示

2.5 子任务:RenderTree

由于 LayoutTree 这个底层基础没扎实,RenderTree 的搭建也不顺利,RenderTree 的 Prompt 如上。

2.6 BugFix

至此,AI 生成了初版的富文本渲染引擎,接下来就是让 AI 写个 Demo 试用一下,在使用过程中,发现了很多上面罗列的 Bug,针对这些 Bug,也可以让 AI 来修复:

在让 AI 修 Bug 过程中,也踩了一些坑,参见下文经验总结。

三、一些经验总结

3.1 Claude AI 常用命令

  • /init:项目初始化,第一次使用 Claude AI 时执行,每个项目只需要执行一次即可;会生成一个CLAUDE.md文件,这是项目的全局上下文,每次新建 Claude 会话时,会自动读取其中的内容;可以在CLAUDE.md文件中补充修改历史、全局说明等
  • @:可以输入@来添加文件到会话窗口,将文件作为上下文给 AI
  • /exit:关闭当前会话
  • /clear:清除当前会话上下文,和退出会话然后新开一个会话效果一样
  • /compact:压缩和总结当前会话上下文,和/clear的区别是,/compact会将当前会话上下文总结后作为当前会话的新上下文,/clear会直接清除所有上下文
  • /resume:显示和恢复历史上下文
  • 自定义 command:可以将通用的 Prompt 做成自定义 command,文件位置在.claude/commands/;还可以通过 $ARGUMENUTS 来接收自定义参数

  • /agents:有的任务比较复杂,或上下文较多,那可以拆分成多个 agents 进行组合,比如写业务逻辑 -> 构建单元测试 -> CI/CD 等,可以拆分多个 agents 组合使用

  • 会话模式:在最新版本的 Claude AI 插件中,除了之前命令行风格的 GUI 以外,还提供了会话框风格的 GUI,切换会话模式,查看历史会话等会更方便;如下,会话模式可以在输入框左下角切换

    • Edit automatically:AI 根据输入 Prompt 进行理解并直接编辑文件,一般使用该模式即可

    • Plan mode:AI 根据输入 Prompt 列出修改计划,你可以进一步校验和修改 Plan

    • Ask before edits:AI 修改文件前询问

  • MCP:常用的 MCP 是context7context7是用于帮助 AI 查找最新文档的,避免使用过时 API

3.2 经验总结

不得不感叹,AI 编程实在太强大了,相信在不久的将来,一个只会写 Prompt 的非专业程序员,也能完整交付一个 App 了。

让 AI 编程,并不是说给一句话就能让 AI 完成代码,各种细节还是需要人来提前想清楚,毕竟最终维护代码和解决问题的还是我们自己,AI 只是帮我们提效和扩展思路的工具;有句话总结的蛮好:你可以将 AI 视为一个非常聪明,甚至资深,但是没有业务经验的程序员。

下面我想总结下最近实战的一些经验,希望对各位有帮助:

1)架构设计需要提前规划好,尽量想清楚细节

谋定而后动,不管是我们自己写代码,还是让 AI 写代码,我觉得提前想好要做什么,怎么做是非常重要的。

架构设计好了,细节想清楚了,那怎么拆分子任务,其实也就明确了。

2)任务拆分越小越好,上下文越明确越好

AI 最适合做有明确输入输出的事情,给的上下文越明确,AI 产生幻觉的概率越低,输出结果也会越准确。

当然,如果是输入输出明确的任务,也可以让 AI 先输出测试用例,测试用例人工检测完备之后,再让 AI 编码也是可以的(测试驱动开发/TDD)。

3)每一项目任务做好之后再进行下一项任务

基础不牢,地动山摇!

推荐打磨好每一项子任务再继续下一项任务,否则千里之堤毁于蚁穴,每个任务都留一点坑,最终可能带来灾难性的结果!

另外,单测是个好东西,对每项任务补齐单测,可以有效防止后续 AI 改出问题。

4)善用 Git,防止代码污染

Claude 在 Edit automatically 模式下会直接修改文件,为了防止污染其他代码,每次让 AI 修改前尽量保证工作区干净,这样也能方便我们 Review 代码。

5)写 Prompt 尽量用明确的词汇,不要表意不清

比如在构建 ElementTree 时,我会明确告诉 AI 要支持哪些 Style,可以有效避免 AI 臆测

与之相反的反例是,在构建 LayoutTree 时,限定不足,导致 AI 自由发挥,最终实现出很多 Bug。

6)善用提示词:think < think hard < think harder < ultrathink

可以在 Prompt 中追加 think hard / think harder 等词汇,来让 AI 进入深度思考,这并不是什么黑魔法,而是 Claude AI 官方认证的,参见:www.anthropic.com/engineering…

实践下来,确实还是有效果的,如下是让 AI 修复文本对齐问题,加了 think hard AI 会更深入理解代码,找到问题原因;当然,这种方式也有弊端,就是会耗费更多的 token(money)👺

7)善用 /compact /clear命令,减少模型幻觉

如果不主动清除,Claude AI 会话中的上下文是会一直保存的,当一个会话中问答轮次过多,可能会导致 AI 理解不准确(幻觉)。

可以通过/compact/clear命令,来压缩/清除上下文。

一般我在修复有关联性的 Bug 时,会使用/compact命令,这样 AI 就不需要重新理解工程,理解 Bug 了,可以提高效率。

8)BugFix 尽量构造最小可复现 Demo

BugFix 其实也是一个子任务,最小可复现 Demo 减少 AI 的理解负担。

9)及时人工介入,避免在一个问题上死磕

有时候让 AI 修复 Bug 时,可能反复修改都解决不了,这时候大概率是 AI 没有真正理解问题,或者就是输入的 Prompt 有问题,这种情况下就没必要让 AI 死磕问题了,我们可以及时人工介入,避免浪费时间。

10)善用 Plan 模式

在任务拆分时,我们自己可能也没想明白应该怎么做,那可以切换到 Plan 模式,让 AI 和我们一起拆任务。

3.3 Vibe Coding 的一些弊端

1)付费,而且还挺贵!

这是一个挺现实的问题,一些好的模型都挺贵,而且还是消耗的刀乐,国内厂商的模型质量又不尽如人意。

2)编码风格问题 & 扩展性、易用性、鲁棒性不足

AI 写的代码还是挺容易看出来的,感觉很难带有程序员的个人风格,一个明显的表现是会用一些比较少见的 API,虽然,这可能也是 AI 的厉害之处。

另外,AI 在一些函数复用性、扩展性、使用方便性上有时候差强人意,比如 AI 生成代码如下:如果要配置 Element 的 Style,需要不断的调用text.style.xxx,但其实写成链式调用使用起来会更舒服,如下注释部分

let text = TextElement(text: "一、晨光初照")
text.style.color = .red
text.style.font = UIFont.systemFont(ofSize: 17)

// 更好的写法
// text.style.setColor(xxx).setFont(xxx)

鲁棒性方面,AI 不会主动考虑调用场景,比如我虽然告诉了 AI 我要支持子线程布局,但是 AI 生成的代码并不是线程安全的。

当然,上述这些,可以通过完善 Prompt 来部分弥补。

3)问题定位幻觉

有时候让 AI 排查一些 Bug,它无法找到真正的原因,反复修改后还是有问题。

这种情况下,就需要人工介入了,我们可以自己定位问题,再告诉 AI 怎么修改,而不要让 AI 死磕问题,避免浪费时间。

四、贴下源码 & Prompt

github.com/HusterYP/Ri…

内容首发在公众号「非专业程序员Ping」,觉得有用的话,三连再走吧~ (⁎˃ᴗ˂⁎)
富文本相关,你可能感兴趣:

刚刚,GPT-5.1 正式发布,OpenAI 这次有点「不对劲」

作者 张子豪
2025年11月13日 07:15

刚刚,OpenAI 正式发布了 GPT-5.1,但这次有点不一样。

翻完整篇官方博客,我发现了一个特别有意思的细节:一张跑分对比图都没有。没有 benchmark 数据,没有「性能提升 XX%」,甚至连「更快更强」这种常规话术都少得可怜。

这不太像 OpenAI 了,直到我看到这句话:

「我们从用户那里清楚听到,优秀的 AI 不仅要聪明,还要让人跟它聊天很愉快。」

新版 GPT-5.1 为了让我们聊天更愉快,提供了八种风格预设 

新版本确实更聪明了——推理更严谨,代码写得更漂亮,但最值得一提的是,它终于像个人了,并且首次允许我们细致地「调教」它的聊天风格。

和 AI 聊天不再是那种一问一答的工具感,而是变得有梗、懂氛围、会接话茬,甚至能陪你有的没的扯上半天。

看来上次 GPT-5 口碑崩塌后,OpenAI 终于听劝,也第一次捅破了窗户纸,承认光刷榜没用,用户要的是能好好说话的 AI,实用和情绪价值全都要。

直接放上具体的使用时间和方式:更新到 GPT-5.1 后,我们的 ChatGPT 会默认切换到最新模型,而不需要专门选择。

  • 付费用户 (Pro, Plus, Go, Business): 从今天(11月12日)开始逐步推送。
  • 免费和未登录用户: 将在付费用户推送完毕后跟进。
  • 企业和教育版: 拥有 7 天的早鸟期切换开关(默认关闭),之后将统一升级。
  • API 开发者: GPT-5.1 Instant 和 GPT-5.1 Thinking 将在本周晚些时候上线 API。

更强大的 AI 内核

这次更新的核心,是 GPT-5.1 Instant 和 GPT-5.1 Thinking 两大模型的全线升级。

GPT-5.1 Instant:最常用的模型,变「暖」了

GPT-5.1 Instant 是 ChatGPT 中最常被调用的模型。这次,它变得更「温暖」、更健谈了。根据 OpenAI 的早期测试,它甚至会不时展现出一些顽皮,同时保持回答的清晰和实用。

而更关键的升级来自底层:

  1. 更听话: 它现在能更可靠地遵循我们的指令,准确回答我们真正想问的那个问题。
  2. 自适应推理 (Adaptive Reasoning): 这是 Instant 模型第一次引入该功能。这意味着它在遇到难题时,会智能地决定先思考一下,从而给出更彻底、更准确的答案;而面对简单问题时,它依然保持极速响应。

OpenAI 提到,这种进化在数学(AIME 2025)和编程(Codeforces)等专业评估测试集上,也有了明显的提高。

GPT-5.1 Thinking:更强的大脑,也更易懂了

作为更高级的推理模型,GPT-5.1 Thinking 也迎来了关键优化,变得更高效、更易用。

▲ GPT-5.1 思考在简单任务上花费的时间更少,在困难任务上花费的时间更多

  1. 效率提升: 它现在能更精准地分配思考时间,在复杂问题上花费更多时间(答案更透彻),在简单问题上响应更快(等待时间更短)。
  2. 更易懂(用户福音!): 它的回答现在更清晰,使用了更少的行业术语和未定义的词汇。这让我们在用它处理复杂工作或解释技术概念时,能毫不费力地看懂。
  3. 同样温暖:Thinking 模型的默认基调也变得更温暖、更富同理心。

用 OpenAI 应用 CEO Fidji Simo 的话来说,这次升级的核心是将 IQ(智商)和 EQ(情商)更好地结合起来。

模型在保持高智商的同时,即继续使用与推理模型相同的技术栈;还大幅提升了情商,ChatGPT 有了更自然的对话和同理心。

 

这能满足用户在不同场景下,都能得到相对应的个性化需求,像是谈论健康时需要同理心,写文案时需要直接。

此外,对大多数用户来说,我们也不需要在 Instant 和 Thinking 之间纠结。因为还有 GPT-5.1-Auto 会自动为我们分配到最合适的模型,这也是 GPT-5 发布时的一大亮点,即智能路由。

总之,最直观的感受就是,答案更智能,语气更自然。

打造专属于你的 ChatGPT

如果说模型升级是硬实力,那个性化体验的飞跃就是软实力,而这正是本次更新的另一大亮点。

OpenAI 的目标是,是让我们毫不费力地将 ChatGPT 的语气和风格,调整到最舒服的状态。

在原有的默认、友好、高效基础上,新增了三种官方风格。

  • Professional (专业): 适用于工作、写作等正式场合。
  • Candid (坦诚): 更直接,不拐弯抹角。
  • Quirky (古灵精怪): 顾名思义,它会变得更有趣、更跳脱。

之前测试版中的「书呆子」和「愤世嫉俗」选项也依然保留在个性化设置中。

除了这种直接选择,更丰富的基本风格和语调,OpenAI 正在实验一项新功能,允许用户直接从设置中微调 ChatGPT 的特征

我们可以精确控制回答的简洁度、热情度(多热情)、回答是否易于浏览 (Scannable)、甚至是使用 emoji 的频率。

如果不想麻烦的手动设置,当我们试图在对话中引导某种特定语气时,ChatGPT 可能还会主动领悟到,然后询问我们,是否希望将这种偏好保存到永久设置中,省去了手动调整的麻烦。

▲ 这也是奥特曼喜欢的功能

在 Fidji Simo 分享的博客里,她提到过去的自定义指令,并不总尽如人意。比如我们可以在自定义设置里,让 ChatGPT 不要用某个词,但它还是会用。

GPT-5.1 在风格化的另一大改进是,自定义指令现在能更可靠地,在多轮对话中坚持住,ChatGPT 可以更稳定地,按照我们定义的个性来完成各项任务。

有网友直接一句话总结,GPT-5.1 这次的更新,就是更创造性地忽略我们的提示词。

当然,AI 的风格化、拟人化,也有它的代价。一个更温暖、情商更高的 AI,也必须更安全,这也是 OpenAI 在最近被卷入 16 岁少年自杀案,必须回应的事情。

在 GPT-5.1 的模型介绍 System Card 里,介绍了 OpenAI 在这方面的深入考量。OpenAI 首次在模型的安全评估中加入了两个全新的、更人性化的维度。

  1. 心理健康(Mental Health): 评估 AI 如何应对用户可能表现出的孤立、妄想或躁狂等迹象。
  2. 情感依赖(Emotional Reliance): 评估 AI 的回应是否会助长用户对 ChatGPT 产生不健康的依赖或情感依恋。

在传统的安全评估上,GPT-5.1 Instant 表现出色,在抵御越狱(Jailbreaks)方面,比其前代 gpt-5-instant-oct3 更强。

但 OpenAI 也坦诚地指出,GPT-5.1 Thinking 在处理骚扰、仇恨言论等内容的基准测试中,相比前代略有回退;Instant 模型在情感依赖的某些评估中,也显示了轻微的倒退。

OpenAI 当然是说正致力于改进这些方面,然后提到了,他们选择透明的公开这种回退的现象,在 AI 快速迭代的当下,比单纯的零失误宣传,更值得大家关注。

也有网友分享很乐意看到,OpenAI 愿意在让我们与模型的对话更愉快这方面,去做出一些努力。

如果你今天打开 ChatGPT 没看到更新,别急,未来几天内就会轮到你,OpenAI 正在逐步推送到所有用户。

此外,为了避免像之前 GPT-5 发布,网友们都在呼吁 GPT-4o 的回归,这种尴尬再次出现。

OpenAI 这次提供了后悔药,付费用户在 3 个月内,也就是 GPT-5 的淘汰期,依然可以在设置的下拉菜单中,选择使用旧的 GPT-5 模型,以便能从从容容地过渡到 GPT-5.1。

▲ 现在还能使用 4o 等模型

GPT-5.1 是一次能力与体验齐头并进的重大更新。OpenAI 显然在告诉我们,AI 的未来不仅是更强的参数,和更高的跑分,更是更懂你的体验,和更贴心的交互。

但一个完美的助手,又应该是什么样的?

OpenAI 应用 CEO Fidji Simo 在她的文章中,有一个挺有意思的比喻,她说「如果我能完全控制我丈夫的特质,我可能会让他永远同意我,但很明显,这不是个好主意。」

最好的 AI 应该像我们生活中最优秀的人一样,他们倾听、适应,但也在必要时挑战我们,帮助我们成长

从一个无所不知的万能工具,到一个能懂你聊天脾气,甚至能帮你成长的专属伙伴,这也许就是 GPT-5.1 真正想开启的未来。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


65 岁图灵奖得主终于不用向 28 岁辍学生汇报了,小扎是怎么把他气走的

作者 莫崇宇
2025年11月12日 16:12

那个站在 LLM 风口上唱反调的倔老头,可能要离开 Meta 了。

硅谷大佬出走创业,三天两头就有一桩,但要出走的 Yann LeCun 不一样,他是能让扎克伯格亲自登门的重量级人物,是深度学习三巨头之一,图灵奖得主,Meta AI Research 的开山祖师。

更重要的是,他这些年一直在干一件特别拧巴的事:站在全世界最热闹的 LLM 路线门口,举着牌子说「这帮人走错路了」。

现在《金融时报》传出他要离职,说他在筹备自己的初创公司,已经开始接触投资人了。注意,目前只是风声,言之凿凿地说 LeCun 已经离职创业,这显然是不严谨的。

只是,截至发稿前,面对铺天盖地的报道,Yann LeCun 本人还没吭声,这沉默本身,就很说明问题。

从三顾茅庐到分道扬镳,这十二年到底发生了什么?

2013 年那场豪赌,扎克伯格赌对了吗?

Lecun 与 Meta 故事得从 2013 年说起。

那段时间,正是深度学习蓬勃兴起的阶段。2012 年,Geoffrey Hinton 与其学生 Alex Krizhevsky、Ilya Sutskever 提交的 AlexNet 在 ILSVRC-2012 一骑绝尘,top-5 错误率约 15.3%,这个突破让整个学术界和工业界都看到了神经网络的潜力。

然后就是科技巨头们的抢人大战——谷歌花大价钱收购了 Hinton 所在的创业公司 DNNresearch,顺带把老爷子本人也挖走了;微软研究院也在疯狂扩张 AI 团队。

扎克伯格坐不住了。

Facebook(现为 Meta)当时正在从 PC 互联网往移动互联网转型,新闻推送算法、照片识别、内容审核,哪哪儿都需要技术。

但问题是,Facebook 的 AI 能力跟谷歌、微软根本不在一个量级。扎克伯格需要一个能撑起门面的人物,最好是那种在学术界有足够分量、能吸引顶尖人才加盟的大牛。

他盯上了 Yann LeCun。

LeCun 当时在纽约大学当教授,已经干了十多年。那时的 Lecun 自然不是什么新人,早在 1989 年,他就在贝尔实验室搞出了卷积神经网络 (CNN),用来识别手写数字,这后来也成了计算机视觉的基石。

但那个年代深度学习不受待见,LeCun 就这么冷板凳坐了许久,眼睁睁看着自己的研究被边缘化。直到 2012 年,Hinton 用深度学习拿下 ImageNet 冠军,证明了神经网络这条路走得通。

LeCun 憋了的那口气,终于能吐出来了。

后续,扎克伯格亲自登门拜访。具体谈了什么外人不知道,但最后开出的条件足够诱人:

第一,给钱,主打一个资源自由;第二,给自由,LeCun 可以保留纽约大学的教授身份,继续教书搞研究;第三,给权,让他参与建立 Facebook AI 研究院,怎么招人、做什么方向,全由他说了算。

这对一个憋屈了多年的学者来说,简直是梦寐以求的机会。

2013 年末,LeCun 正式加入 Facebook,出任新成立的 Facebook AI Research(FAIR) 实验室负责人。

他在纽约、门洛帕克和伦敦三地建起了 FAIR 实验室,自己常驻纽约办公室。

团队最初规模较小,但个个都是从顶尖高校和研究机构挖来的——LeCun 的号召力在这时候体现出来了,但凡是做深度学习的,没人不知道「卷积神经网络之父」这个名号。

扎克伯格给了资源,LeCun 也拿出了成果。

加入 Facebook 这些年,LeCun 干的事情可以分成三条线:一是把深度学习塞进 Facebook 的产品里,二是推动学术界的前沿研究,三是培养下一代 AI 人才。

产品线上,2014 年的 DeepFace 人脸识别系统达到 97.35% 准确率,深度学习优化的推送算法也提升了广告点击率。

与此同时,LeCun 自己继续在学术圈刷存在感:发论文、顶会 keynote、带学生办 workshop。直到和 Hinton、Bengio 一起拿图灵奖, 才算是熬出头了。

此外,在 LeCun 创建的 FAIR 实验室,Soumith Chintala 主导开发了 PyTorch 框架并于 2017 年开源,这也是 Meta 至今为数不多的形象招牌。

PyTorch 动态计算图、Python 原生接口, 调试方便, 学术圈迅速倒戈。这一招等于把全球 AI 研究者都拉进了 Facebook 生态。

不过,或许是冥冥中自有天意,Soumith 前几天也宣布离职 Meta,表示「不想一辈子做 PyTorch」。

而更重要的是人才培养。FAIR 有个规矩:研究员可以自由发表论文、跟学术界合作、指导外部学生。顶级资源加学术自由的组合,自然吸引了一批顶尖研究人员。

到 2020 年前后,FAIR 已是全球顶尖 AI 研究机构之一, 跟谷歌 DeepMind 并列第一梯队。扎克伯格的那场豪赌, 至少在前七八年就已经得到了不小的回报。

猫比 ChatGPT 聪明?这个图灵奖得主是认真的

在 ChatGPT 席卷世界初期,Yann Lecun 和扎克伯格也有过一段甜蜜期。

2023 年以来,Meta 陆续开源 LLaMA 系列模型,引发业界震动。

OpenAI、谷歌走的是封闭路线,靠 API 赚钱;Meta 却把模型权重直接扔出来,任人取用。这步棋背后的算盘其实挺清楚:与其让对手一家独大,不如用开源赢得开发者生态,让 LLaMA 成为 AI 界的 Android。

至少在明面上,身居 Meta 首席 AI 科学家一职的 LeCun,是这条路线最坚定的拥护者。

开源 LLaMA 让 Meta 在大模型竞赛中站稳了脚跟,也让 LeCun 的 AI 理想得到了一定程度的实现——尽管这个实现的方式,恰恰是通过他并不完全认同的 LLM 技术路线。

没错,LeCun 一直觉得 LLM 是条死胡同。这才是矛盾的核心。

LeCun 不止一次在公开场合炮轰 LLM 路线,在他看来,LLM 只会根据统计相关性预测下一个词,根本不理解世界。你问它常识问题,它能给你编出一本正经的瞎话——这叫「幻觉」(hallucination),说白了就是不懂装懂。

熟悉 LeCun 的人都知道,他最喜欢举的例子是猫和机器人:

「我们有了会考试聊天的语言模型,但家务机器人在哪里?哪怕像猫那样灵巧的机器人都没有出现。」

「你的猫肯定有一个比任何 AI 系统都更复杂的模型。动物拥有持久记忆的系统,这是目前的 LLM 所不具备的;能够规划复杂动作序列的系统,这在今天的 LLM 中是不可能的。」

他算过一笔账:一个 4 岁小孩通过视觉获取的信息量,几年下来就有 10 的 15 次方字节,远超 LLM 读遍互联网文本。但小孩已经掌握了基本的物理直觉和语言,LLM 耗费这么多数据,智能仍然很有限。

「光靠喂文本,不可能达到人类水平智能。这条路永远走不通。」他如此说道。

在当下最火的风口面前,这样唱反调的言论显然并不讨喜,有人批评他傲慢,有人说他故步自封。甚至 Meta 内部都有声音认为,正是 LeCun 对 LLM 路线的抵触,让公司在大模型竞赛中暂时落后。

但 LeCun 不在乎。

他有自己的路线图:世界模型 (World Model)、联合嵌入预测架构 (JEPA)等等。这些概念听起来学术味十足,核心思想其实很直观——

让 AI 通过观察世界来学习,而不是通过阅读文本来记忆。就像婴儿成长那样,先理解重力、因果关系这些物理常识,再逐步建立抽象认知。

他设想的 AI 架构是模块化的:感知模块、世界模型模块、记忆模块、行动模块,各司其职。不像 LLM 那样把所有知识和推理揉在一个巨型网络里,搞得像个什么都懂但其实什么都不懂的「书呆子」。

具体来说,世界模型就是让 AI 在内部学会一个对外部世界的预测模型。就像婴儿在成长过程中建立起对重力、物体恒存等常识那样,AI 应该通过观察世界,形成对物理规律、因果关系的理解。
有了世界模型,AI 就可以在脑海中模拟未来,从而具备计划行动的能力。

JEPA 则是实现这个世界模型的具体架构。

它采用自监督学习的方法,给 AI 两个相关的输入 (比如视频中相邻的两帧画面),模型将这两个输入分别编码到一个抽象的表示空间中,然后训练一个预测器,根据「上下文」表示去预测「目标」表示。

这种方式避免了直接生成所有细节,而是关注抽象的关键因素——更符合人类学习方式。LeCun 曾预言,如果团队的路线顺利推进,三到五年内就会有更好的范式出现,使得现在基于 LLM 的方法过时。

问题是,三到五年,Meta 等得起吗?

一场猝不及防的重组,FAIR 的黄金时代结束了

当初,LeCun 建立 FAIR 时的承诺是「做长期的、基础性的 AI 研究」,扎克伯格也同意了。

但这个「长期」到底有多长?「基础研究」到底能给公司带来多少直接收益?这些问题在早期不是问题,因为深度学习本身就是风口,FAIR 做什么都有望转化成产品优势。

可随着生成式 AI 开始爆发,竞争也日益激烈,形势开始发生了变化,尤其是 Llama 4 的失败也给了扎克伯格当头一棒。扎克伯格要的是现在就能用的技术,不是五年后可能有用的理念。

于是,一场猝不及防的重组出现了。

就在今年,Meta 搞了个大动作,成立「超级智能实验室」,把 FAIR、基础模型团队和各应用 AI 团队统统塞进一个筐里。表面上是整合资源,实际上是一场彻底的权力重组。

这场重组的核心逻辑很明确:让研究直接服务产品,让科学家为商业目标让路。

FAIR 团队原本「相对不受干扰地开展研究」,现在得跟着产品节奏走,研究方向要服务于个人 AI 助手。此外,Meta 对 FAIR 的研究发表制定了更严格的内部审核机制。

研究员在对外发布论文、开源代码之前,需要经过额外的内部交叉审阅和管理层审批,原因在于 Meta 担心自己砸钱搞出来的成果被竞争对手白嫖。

LeCun 对这些变化表现出强烈的抵触。

据多方报道,他在内部激烈反对新的论文审核制度,为维护研究自由据理力争。The Information 援引知情者的话称,LeCun 在今年 9 月一度「气到考虑辞职」以示抗议。

但或许更让他难以接受的是领导权的旁落。

扎克伯格在重组中做了一个大胆的人事任命:从外部挖来 Alexandr Wang,让他担任 Meta 的首席 AI 官,直接向 CEO 汇报。

Alexandr Wang 是谁?一个 28 岁的 MIT 辍学生,他创办的公司 Scale AI 专门做数据标注业务,给各大科技公司的 AI 模型提供训练数据。

扎克伯格看中的,恰恰是 Wang 的产品思维和商业嗅觉。在生成式 AI 的竞赛中,Meta 需要的不是象牙塔里的理想主义者,而是能快速把技术转化为产品的实干家。

这个任命的震撼在于:LeCun 这个图灵奖得主、深度学习三巨头之一、在 Meta 干了十二年的首席 AI 科学家,在新架构下的话语权被大幅削弱,甚至要向 Wang 汇报。

同时,今年 7 月,扎克伯格还任命了年轻有为的赵晟佳为超级智能实验室的首席 AI 科学家,负责制定新实验室的研究方向。

有趣的是,LeCun 当时发了个声明,说自己角色没变、使命没变,还期待跟新团队合作。这求生欲属实拉满。但他对于研究方向和领导层重组的分歧,显然是公开的秘密。

而真正可能成为压垮骆驼的最后一根稻草的,是最近的裁员。据报道,Meta 近期对 AI 团队进行了裁员,波及到 FAIR 研究部门以及与产品相关的 AI 团队,甚至华人大佬田渊栋也因此受到了波及。

裁员的信号很明确:Meta 不再愿意为「看不到短期回报」的基础研究买单了。那些不能直接转化为产品功能、不能立即提升用户增长或广告收入的研究方向,都成了被砍的对象。

FAIR 的黄金时代结束了。

种种因素之下,《金融时报》爆料他在筹备创业,倒也不算意外。

学术大佬出来单干,最近几年已经成了硅谷新常态。Hinton 退休后到处演讲呼吁 AI 监管,Bengio 也有自己的实验室和创业项目。LeCun 若是真出去创业,没准反而是好事。说到底,这事儿没有谁对谁错。

LeCun 能够在 Meta 之外继续他毕生的事业。

他带走了那个被 Meta「搁置」的愿景,可以放开手脚搞自己的世界模型,用自己的方式证明它是正确的,再也不用跟产品经理扯皮,不用向 28 岁的小老弟汇报。
成了,那就是「我早说过 LLM 是死路」;败了,顶多被人嘲笑几句「你看那个老顽固」。

而对于 Meta 来说,扎克伯格要给股东讲故事,要把最实用的生成式 AI 塞进旗下产品的各个角落,这确实是 CEO 该干的事。

只是,尽管少了 LeCun 也不会伤筋动骨,但可能会少点不一样的声音。等哪天大家发现 LLM 真的走到瓶颈了,回头看看当年那个举着反对牌子的倔老头说过什么,或许会觉得别有一番趣味。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


NodeJS创建流式接口&AI大模型接口流式调用记录

作者 前端君
2025年11月12日 12:03

什么是流式文本传输?

流式文本传输是一种在数据生成后立即传输,而不是等待所有数据生成完毕再一次性传输的技术。这种传输方式可以显著提升用户体验,特别是在处理大量数据或需要实时响应的场景中。

流式传输的主要优势:

  • 实时响应:用户可以立即看到部分结果,而不必等待整个过程完成
  • 减少内存占用:数据可以边生成边传输,避免在内存中缓存大量数据
  • 更好的用户体验:特别是在AI生成内容、长文本处理等场景下

NodeJS + Koa 流式接口示例

const Koa = require('koa');
const { Readable } = require('stream');

const app = new Koa();

// 流式响应中间件
app.use(async (ctx) => {
  // 设置响应头
  ctx.set('Content-Type', 'text/plain');
  ctx.set('Transfer-Encoding', 'chunked');
  
  // 创建一个可读流
  const readableStream = new Readable({
    read(size) {
      // 这个方法会在流需要更多数据时被调用
    }
  });
  
  // 模拟异步生成数据的过程
  async function generateData() {
    const data = '这是,第一部分,数据,\n这是,第二部分,数据\n这是,第三部分,数据,\n这是,第四部分,数据,\n这是最后一部分数据\n'.split(',');
    
    for (const chunk of data) {
      // 模拟处理延迟
      await new Promise(resolve => setTimeout(resolve, 100));
      // 将数据推入流中
      readableStream.push(chunk);
    }
    
    // 结束流
    readableStream.push(null);
  }
  
  // 启动数据生成
  generateData();
  
  // 将流管道到响应对象
  ctx.body = readableStream;
});

// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

NodeJS 发送流式请求

const https = require('https');
const http = require('http');

/**
 * Node.js版本的流式查询函数
 * @param {Object} options - 配置选项
 * @param {Object} options.params - 请求参数
 * @param {Function} [options.onStart] - 开始回调
 * @param {Function} [options.onChange] - 数据变化回调
 * @param {Function} [options.onFinish] - 完成回调
 * @param {Function} [options.onError] - 错误回调
 * @param {Function} [options.chunkTransform] - 数据块转换函数
 * @returns {Object} 返回包含abort方法的对象
 */
const streamRequest = ({
  params,
  onStart,
  onChange,
  onFinish,
  onError,
  chunkTransform,
}) => {
  const { url, method = 'GET', headers = {} } = params;
  const { searchParams } = params;
  const { body } = params;
  const parsedUrl = new URL(url);
  const isHttps = parsedUrl.protocol === 'https:';
  const requestModule = isHttps ? https : http;

  if (searchParams) {
    Object.entries(searchParams).forEach(([key, value]) => {
      parsedUrl.searchParams.set(key, value);
    });
  }

  const requestOptions = {
    hostname: parsedUrl.hostname,
    port: parsedUrl.port || (isHttps ? 443 : 80),
    path: parsedUrl.pathname + parsedUrl.search,
    method,
    headers: {
      'Content-Type': 'application/json',
      ...headers,
    },
  };

  let fullText = '';
  let aborted = false;

  const req = requestModule.request(requestOptions, (res) => {
    if (res.statusCode !== 200) {
      let errorData = '';
      res.on('data', (chunk) => {
        errorData += chunk.toString();
      });
      res.on('end', () => {
        const error = new Error(`${res.statusMessage}\n${errorData}`);
        onError?.(error);
      });
      return;
    }

    res.setEncoding('utf8');

    // 监听第一个数据块
    res.once('data', () => {
      onStart?.();
    });

    res.on('data', (chunk) => {
      if (aborted) return;
      const chunkStr = chunk?.toString();
      const transformedChunk = chunkTransform ? chunkTransform(chunkStr) : chunkStr;
      fullText += transformedChunk;
      onChange?.(transformedChunk, fullText);
    });

    res.on('end', () => {
      if (!aborted) {
        onFinish?.(fullText);
      }
    });

    res.on('error', (error) => {
      if (typeof onError !== 'function') {
          console.error(error);
          return;
      }
      if (!aborted) {
        console.error('Response error:', error);
        onError?.(error);
      }
    });
  });

  req.on('error', (error) => {
    if (!aborted) {
      console.error('Request error:', error);
      onError?.(error);
    }
  });

  // 发送请求体(如果是POST请求)
  if (method === 'POST' && body) {
    req.write(JSON.stringify(body));
  }

  req.end();

  return {
    abort: () => {
      aborted = true;
      req.destroy();
      console.log('Request aborted');
    },
  };
};

浏览器中发送流式请求

  • 基于 fetch 实现

/**
 * 浏览器版本的流式查询函数
 * @param options - 配置选项
 * @returns 返回包含abort方法的对象
 */
export const browserStreamRequest = ({
  params,
  onStart,
  onChange,
  onFinish,
  onError,
  chunkTransform,
}) => {
  const { url, method = 'GET', headers = {} } = params;
  const { searchParams } = params;
  const { body } = params;
  
  let controller = null;
  let fullText = '';
  let isStarted = false;
  let isFinished = false;
  
  const buildFetchUrl = () => {
    if (!searchParams) return url;
    
    const urlObj = new URL(url, window.location.origin);
    Object.entries(searchParams).forEach(([key, value]) => {
      urlObj.searchParams.set(key, value);
    });
    return urlObj.toString();
  };
  
  const fetchOptions = {
    method,
    headers: {
      'Content-Type': 'application/json',
      ...headers,
    },
    // 允许跨域凭证
    credentials: 'include',
  };
  
  // 设置信号用于中断请求
  controller = new AbortController();
  fetchOptions.signal = controller.signal;
  
  // 添加请求体(如果是POST请求)
  if (method === 'POST' && body) {
    fetchOptions.body = JSON.stringify(body);
  }
  
  const processStream = async () => {
    try {
      const response = await fetch(buildFetchUrl(), fetchOptions);
      
      if (!response.ok) {
        let errorData = '';
        try {
          errorData = await response.text();
        } catch (e) {
          // 忽略解析错误
        }
        const error = new Error(`${response.status} ${response.statusText}\n${errorData}`);
        onError?.(error);
        return;
      }
      
      // 检查响应是否支持流式处理
      if (!response.body) {
        const error = new Error('Response body is not streamable');
        onError?.(error);
        return;
      }
      
      // 创建TextDecoder用于解码二进制流
      const decoder = new TextDecoder('utf-8');
      const reader = response.body.getReader();
      
      // 流式处理数据
      const readStream = async () => {
        try {
          const { done, value } = await reader.read();
          
          if (done) {
            if (!isFinished) {
              isFinished = true;
              onFinish?.(fullText);
            }
            return;
          }
          
          // 首次收到数据时触发onStart
          if (!isStarted) {
            isStarted = true;
            onStart?.();
          }
          
          // 解码并处理数据块
          const chunkStr = decoder.decode(value, { stream: true });
          const transformedChunk = chunkTransform ? chunkTransform(chunkStr) : chunkStr;
          fullText += transformedChunk;
          onChange?.(transformedChunk, fullText);
          
          // 继续读取流
          readStream();
        } catch (error) {
          if (!isFinished && !controller?.signal.aborted) {
            console.error('Stream processing error:', error);
            onError?.(error instanceof Error ? error : new Error(String(error)));
          }
        }
      };
      
      readStream();
    } catch (error) {
      if (!isFinished && !controller?.signal.aborted) {
        console.error('Fetch request error:', error);
        onError?.(error instanceof Error ? error : new Error(String(error)));
      }
    }
  };
  
  // 立即开始处理流
  processStream();
  
  return {
    abort: () => {
      if (controller) {
        controller.abort();
        isFinished = true;
        console.log('Request aborted');
      }
    },
  };
};

  • 低版本浏览器(如 IE11)不支持 fetch 流式传输,需要使用 XHR 实现

/**
 * 浏览器兼容性检查
 * @returns 是否支持流式请求
 */
export const isStreamSupported = () => {
  return !!(window.fetch && 
    window.ReadableStream && 
    window.TextDecoder && 
    window.AbortController);
};

/**
 * 降级版本的请求函数(当浏览器不支持流式处理时使用)
 * @param options - 配置选项
 * @returns 返回包含abort方法的对象
 */
export const fallbackRequest = ({
  params,
  onStart,
  onChange,
  onFinish,
  onError,
  chunkTransform,
}) => {
  const { url, method = 'GET', headers = {} } = params;
  const { searchParams } = params;
  const { body } = params;
  
  let xhr = null;
  let fullText = '';
  
  const buildUrl = () => {
    if (!searchParams) return url;
    
    const urlObj = new URL(url, window.location.origin);
    Object.entries(searchParams).forEach(([key, value]) => {
      urlObj.searchParams.set(key, value);
    });
    return urlObj.toString();
  };
  
  try {
    xhr = new XMLHttpRequest();
    xhr.open(method, buildUrl(), true);
    
    // 设置响应类型为文本
    xhr.responseType = 'text';
    
    // 设置请求头
    Object.entries({
      'Content-Type': 'application/json',
      ...headers,
    }).forEach(([key, value]) => {
      xhr?.setRequestHeader(key, value);
    });
    
    // 监听进度事件(非标准流式处理)
    xhr.onprogress = (event) => {
      if (event.lengthComputable || xhr?.responseText) {
        const currentText = xhr?.responseText || '';
        const newChunk = currentText.substring(fullText.length);
        
        if (newChunk && fullText.length === 0) {
          // 首次收到数据
          onStart?.();
        }
        
        if (newChunk) {
          const transformedChunk = chunkTransform ? chunkTransform(newChunk) : newChunk;
          fullText += transformedChunk;
          onChange?.(transformedChunk, fullText);
        }
      }
    };
    
    xhr.onload = () => {
      if (xhr?.status === 200) {
        // 确保处理完所有数据
        const currentText = xhr.responseText || '';
        if (currentText !== fullText) {
          const newChunk = currentText.substring(fullText.length);
          const transformedChunk = chunkTransform ? chunkTransform(newChunk) : newChunk;
          fullText += transformedChunk;
          onChange?.(transformedChunk, fullText);
        }
        onFinish?.(fullText);
      } else {
        const error = new Error(`${xhr?.status} ${xhr?.statusText}\n${xhr?.responseText || ''}`);
        onError?.(error);
      }
    };
    
    xhr.onerror = () => {
      const error = new Error('Network error occurred');
      console.error('XHR error:', error);
      onError?.(error);
    };
    
    // 发送请求
    if (method === 'POST' && body) {
      xhr.send(JSON.stringify(body));
    } else {
      xhr.send();
    }
    
  } catch (error) {
    console.error('Fallback request initialization error:', error);
    onError?.(error instanceof Error ? error : new Error(String(error)));
  }
  
  return {
    abort: () => {
      if (xhr) {
        xhr.abort();
        console.log('Request aborted');
      }
    },
  };
};

/**
 * 智能流式请求函数 - 根据浏览器支持情况自动选择实现方式
 * @param options - 配置选项
 * @returns 返回包含abort方法的对象
 */
export const smartStreamRequest = (options) => {
  if (isStreamSupported()) {
    console.log('Using native fetch stream implementation');
    return browserStreamRequest(options);
  } else {
    console.log('Using fallback XHR implementation');
    return fallbackRequest(options);
  }
};

浏览器端流式请求使用示例

// 基本使用示例
const streamInstance = smartStreamRequest({
  params: {
    url: '/api/stream',
    method: 'POST',
    body: {
      prompt: 'Tell me a story',
      stream: true
    }
  },
  onStart: () => {
    console.log('Stream started');
    document.getElementById('status')?.textContent('Streaming...');
  },
  onChange: (chunk, fullText) => {
    console.log(`Received chunk: ${chunk}`);
    document.getElementById('output')?.textContent(fullText);
  },
  onFinish: (fullText) => {
    console.log(`Stream finished, total text: ${fullText.length} characters`);
    document.getElementById('status')?.textContent('Completed');
  },
  onError: (error) => {
    console.error('Stream error:', error);
    document.getElementById('status')?.textContent(`Error: ${error.message}`);
  },
  // 可选的数据转换函数
  chunkTransform: (chunk) => {
    // 例如处理特定格式的流式数据
    return chunk.replace(/^data: /, '');
  }
});

// 中断请求的示例
setTimeout(() => {
  streamInstance.abort();
}, 5000);

常见问题处理

  1. CORS 问题:确保服务器端正确配置了 CORS 响应头,特别是当需要流式传输时
  2. 超时处理:可以通过 setTimeoutabort() 方法实现请求超时控制
  3. 内存管理:对于长时间运行的流,注意监控内存使用情况,避免内存泄漏
  4. 错误恢复:可以实现重试逻辑,在 onError 回调中重新发起请求

案例: 流式调用免费的AI大模型接口(来自硅基流动)

备注:硅基流动(SiliconFlow)是一家提供免费AI大模型服务的公司,其API接口支持流式传输。其中token可以在 硅基流动API密钥管理 处创建。API介绍见:API手册

async function callSiliconFlowAPI() {
  const options = {
    url: 'https://api.siliconflow.cn/v1/chat/completions',
    method: 'POST',
    headers: { Authorization: 'Bearer <token>', 'Content-Type': 'application/json' },
    body: {
      model: 'Qwen/Qwen2.5-7B-Instruct',
      messages: [{ role: 'user', content: '你好' }],
      stream: true,
    },
  };

  try {
    streamRequest({
      params: options,
      onChange: (chunk, fullText) => {
        process.stdout.write(chunk);
      },
      chunkTransform: extractContentFromSiliconFlowChunk,
    });
  } catch (error) {
    console.error(error);
  }
}
/**
 * 从 SiliconFlow 模型的响应 chunk 中提取实际内容
 * @param {string} chunk - 包含 SiliconFlow 响应的 chunk 字符串
 * @returns {string} - 提取后的实际内容
 */
function extractContentFromSiliconFlowChunk (chunk) {
  return chunk?.trim()
    .split('data: ')
    .filter(item => item && !item.includes('[DONE]'))
    .map(item => {
      const json = JSON.parse(item.trim());
      const delta = json?.choices?.[0]?.message || json?.choices?.[0]?.delta;
      const res = delta?.content ?? delta?.reasoning_content ?? '';
      return res;
    })
    .filter(Boolean).join('');
}

iPhone Air 或许后继无人,但苹果会永远离经叛道

作者 马扶摇
2025年11月12日 08:58

虽然今年 iPhone 17 系列的销量很喜人,但残酷的商业现实再一次向我们证明:口碑好和卖得好完全是两码事——

当然,这里说的是 iPhone Air。

有媒体统计,截至 11 月 2 日,今年的四款新 iPhone 在国内的总激活量为 825 万台,接近一半都是价格最贵的 iPhone 17 Pro Max,数量达到了三百九十余万台。

而发布会前声量最大、开售后又引发全民对 eSIM 业务关注的 iPhone Air,则以 10.7 万台的数据虎踞最后一名,仅占总激活量约 1.2%

上海南京东路 Apple Store|Apple Newsroom

当然,这其中叠加了今年 Pro 机型改模具、标准版大升级、Air 自身延迟开售和国内 eSIM 争议等等多种因素,iPhone Air 在国内取得这样的销售数字并不奇怪。

但哪怕是在苹果主场作战、开售一切顺利的美国,根据 Counterpoint 的统计,iPhone Air 在总销量中也仅仅占了 2%~3% ——跟国内相比也就半斤八两。

图|Counterpoint

这样一来,iPhone Air 原本就不甚清晰的未来,变得更加惨淡了。

事实也的确如此,根据外媒 The Information 的报道:苹果已不再计划在明年秋季推出第二代 iPhone Air,并且「已经大幅缩减了第一代 iPhone Air 的产量」。

也就是说,明年秋季的 iPhone 发布会上,将只有 iPhone 18 Pro 与折叠 iPhone 两款新品。哪怕最乐观来看,新的 iPhone Air 至早也要和 2027 年春季 iPhone 18 标准版一起发布了,直接夭折也不无可能。

如果从产品线的角度看,iPhone Air 就像一个「局外人」一样游离在 iPhone 数字系列之外。

当用户的基础需要已经可以被 iPhone 17 满足、进阶需要已经被 iPhone 17 Pro Max 满足,加一千就能从单摄单扬升级到三摄双扬和大电池之后,iPhone Air 是非常缺乏站位的。

类似的情况也出现在可口可乐身上——以防你不知道,可口可乐除了经典之外,还有诸如零度、纤维+(健怡)、香草等等很多品种,但无一例外都没法和满足 99% 需求的经典口味竞争,最后要么减产要么停产。

换句话说,iPhone Air 就是那个长久以来「站不住的第四款 iPhone」——在数字版、Pro 和 Pro Max 之外,mini 最后被取消了、Plus 最后也被取消了,iPhone Air 虽然不属于数字系列,但同样岌岌可危。

到头来 iPhone Air 成了「价格锚点」本身|PhoneArena

但与此同时,iPhone Air 又不是那种常规意义上的「差劲产品」,恰恰相反,它身上聚集着很多我们已经很久没有在苹果公司的产品上看到的特质:探索极致工艺、敢于为了外观而舍弃功能,以及最重要的——

每一个人看了都说厉害,但就是卖不出去。

事实上,别看今天的苹果坐拥两万亿市值,有着数不清的试错机会可以用来分给这些剑走偏锋的产品,但在苹果过去 49 年历史里,「看着厉害,卖不出去」才是它的常态。

比如曾经被乔布斯寄予厚望、甚至亲自带队过一段时间的 LISA 个人电脑,在 1983 年推出时以它精美的外形和完善的 GUI(图形用户界面)给市场留下了深刻的印象:

图|MacStories

但 LISA 看着厉害,销量却是一次彻头彻尾的失败,原因包括但不限于定价过于高昂、软件开发困难、运行速度过低等等,最终导致机器硬件完全支撑不起设想中的使用场景,成为了典型的商业失败案例。

同样的情况也出现在 2015 年的 12 寸 Retina MacBook 上。从工业设计的角度来看,这台 A4 纸大小的全金属机身小笔记本可以说是 MacBook 有史以来最惊艳的一代,内部工艺更是货真价实的「tech porn」:

图|Apple Support

然而这样的「工业艺术品」依然没能收获一个好的销量。

它一方面面临着比自己更便宜的老模具 MacBook Air 的竞争,另一方面又轮流踩中了初代蝶式键盘、纯被动散热和英特尔酷睿 M3 几个大坑,最后变成了「远看好想要,打字吓一跳」的花架子。

图|9to5Mac

而在苹果的产品史上,类似的例子数不胜数:iPhone 5c、垃圾桶 Mac Pro、PowerMac G4 Cube,甚至 Vision Pro……不胜枚举。

但苹果很少认为自己犯了错——大部分时候,它更认为自己是在试错。

而且,每次试错之后,苹果总能拿出一些持久成功的产品。

图|Remi’s Classic Computers

比如 LISA 虽然是大失败,但一年之后推出的 Macintosh 麦金塔电脑则凭借革命性的 GUI 界面和鼠标逻辑,在 80 年代中后期的「桌面出版」(Desktop-publishing)革命里,证明了苹果在个人电脑领域的成功。

可以说,苹果花了很多年才逐渐摆脱乔布斯时代「设计影响功能」的色彩,开始以一种稳健但是无趣的方式按部就班地更新产品,变成了今年这幅「成熟的硅谷巨头」的样子。

但重点在于,正是因为有了那些功能为设计让路的产品,我们才逐渐将苹果看作那个用设计引领时代、带有叛逆艺术家特质的公司,而不是一个循规蹈矩的电脑手机厂。

图|Forbes

而这种极致的创新,也恰恰是当年苹果军国草创的时候,活跃于加州的那种真正的「硅谷海盗」的叛逆精神。

人们最近这些年对 iPhone 不满意,很多都是觉得苹果已经丢掉了曾经的叛逆精神、从「设计驱动」变成「会计驱动」了

但这也正是 iPhone Air 即便没人买、但话题度总是很高的原因:在经历了这么多代稳妥为主的更新之后,iPhone Air 是一次罕见的「冒险」,它不像是给市场的交代,更像是给苹果过去五十年叛逆海盗精神的一个交代

图|Apple Newsroom

而苹果虽然一直在赚钱,但是只要它仍然愿意做这种离经叛道的产品,那它就还是以前那个苹果。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


GPT 5.1 披「马甲」公测,免费可用,杀入年底大更新战场

作者 Selina
2025年11月11日 11:01

Gemini 3 还没影子,GPT 5.1 已经在路上。7 号深夜,OpenRouter 平台上线了一个全新的隐名模型。已经有眼尖动作快的网友尝鲜体验,并且认为这就是披着马甲的 GPT 5.1,暂名:Polaris Alpha。

目前提供 API 调用(包括 OpenRouter),知识库截止时间为 2024 年 10 月,不支持推理模式。最大 context 容量 256K,单次最大输出 128K。

开发商信息保密,但是在网友的不懈努力下,成功「越狱」,让 Polaris Alpha 自曝了家门。

▲ 图片来自 X 用户 @LarryAtherton1

这倒也算不上实锤,可能只是训练数据的问题——很多模型都会输出来自专有供应商的数据。除非系统提示中明确说明,或者通过指令调优反复灌输,否则这些模型实际上并不「知道」自己是什么。

无论如何,目前暂时没有官宣,暂且就还是叫它 Polaris 吧。APPSO 也第一时间简单试用了一下。由于是通过 API,部分功能比如处理语音素材暂时受限,除此之外,Polaris 的表现,让人相当期待 GPT 5.1。

案头工作:流畅,一如既往

首先是一些比较基础的简单任务:根据要求写邮件。这是一封道歉邮件,跟嘉宾通报活动改期。我特地强调了口吻要既饱含歉意,又表示亲近,让 Polaris 感受一下。

算是中规中矩吧,信息齐全,行文逻辑清楚,语气不会有强烈的「人机感」。比较神奇的是,在打开 Search 模式的前提下,Polaris 会去检索一些道歉信的写法,而它的引用信息里,居然有淘宝百科…… 看上去 Polaris 覆盖的信息源越来越多,也越来越冷门了。

然后是需要创意想法的文案写作任务,prompt 我都给得很模糊,只要求有网感,适合在小红书上传播。

Polaris 给出了三种不同的风格,给出的文案非常完整。后面还给出了活动具体执行的方案。从这里已经可以看出来,措辞上颇有 GPT 系会有的文字风格。也有网友专门做了相似性统计——马甲快要披不住了。

文字风格仍然是每一个大语言模型的立身之本,毕竟现在最主流的应用场景就是案头工作。2026 年都快要来了,还给出「人机味」的文字,是不能被原谅的。目前 Polaris 的文风,有相当典型的「GPT」风味,很多网友都有同样的感受。

同时,对于 chatbot、聊天、陪伴等应用场景里,文风能否快速适应用户节奏,并且灵活「习得」个性化的口吻,也将是 GPT5.1 面临的挑战——全球用户要求 4o 回来的盛况,OpenAI 应该不想再经历一次了吧。

由于不能直接处理音频文件,我上传了转录后的播客文字稿,让 Polaris 整理提炼信息点,适当调整口语化的地方,重点是:根据不同的主题维度,拉出一个层次明确的提纲,同时保留时间戳。

输入目前看来可以超过 1w 字(单条发送),受限于 OpenRouter 每个窗口只能保存八条记忆的限制,超长输入会一定程度的影响输出稳定性。不过自我纠正能力不错,第一次跑的时候生造了并不存在的时间戳,重抽一次之后自行纠正了。

自从 GPT 5 之后,ChatGPT 的单个窗口容量明显增大,从社交媒体上的反馈来看,最高的 token 总数可以去到 60 万-80 万才达到上限。这对于个性化用户信息而言是个非常好的信号,但不可避免的是,超长上下文额之后,会出现记忆调度的失序,以及输出稳定性的下降。

这留给了 GPT 5.1 全新的挑战,如何在进一步扩大窗口容量之后,依然保持灵活准确的记忆调度。对于用户而言,几乎是第一秒就能感受到的决定性体验。

编程:超简单,超顺滑

OpenRouter 提供几种基础的编程工作实现和预览。我快速用它测了一下 Polaris 写小游戏的本事。

最直观的感觉是时间稍微有点久,差不多要个五分钟了。不过倒是不需要我提供复杂的 prompt,「设计一个贪吃蛇游戏代码」,就足够生成一个可以上手的小游戏。

甚至还提供不同的模式、设置,在 preview 里的试玩都很顺畅。另外又让它跑了一个打地鼠游戏,也是顺利完成。

网页设计也很 OK,我把上面生成出来的咖啡馆文案,丢回给 Polaris,让它设计一个活动的落地页。

Polaris 研究了整个文案,并且加入了一些补充,最后出来的视觉效果也不错,我挺喜欢它给按钮设计的发光效果,这似乎是它的一个「独家特色」,在其他网友的测试中也出现了:

▲ 图片来自 X 用户

从网上的其它测试是来看,它的美学表现值得期待。

▲ 图片来自 X 用户 @HarshithLucky3

这些基础工作都没有太大的问题,但老实说,现在 AI 编程的赛道堪称白热化,而 GPT 系产品在编程上,竞争力一直不算很强。GPT 5.1 实装后,在编程上的表现能不能有大突破,只有继续等待才能知道。

前阵子 Sam Altman 明确发话表示,年底时 ChatGPT 将推行 NSFW 模式(成人模式),在目前的 Polaris 上,似乎已经看得到苗头了。

如果是这样,那 Polaris 是 GPT 5.1 的证据又多一条,尤其是考虑到最近 OpenAI 已经在小范围内做年龄验证,这并非全量行动,而是针对不确定实际年龄的用户做定向推送。

成人模式的争议很大,实际执行也并不如想象中简单,除了验证,还有隐私信息识别、储存等一系列麻烦。到底能不能有一个平衡多方诉求的解决方案,还得看真正的 GPT 5.1 如何应对。

眼瞧着年底又是一场血战,Gemini 3 早就放出风声(虽然一直跳票),Nano Banana 2 也突然冒头。更别提前阵子 Kimi K2 Thinking 的发布,收获了海内外一大波关注,训练成本仅为 460 万美元。

OpenAI 仍然有着惊人的支出,虽然也有着惊人的活跃用户群,但盈利还看不到苗头。在一系列又强又便宜的中国模型的狙击下,GPT 5.1 能达到期望吗?

快知道了,网传 11 月中就将发布,到时或许会有答案。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


李飞飞最新长文刷屏,AI 下一个十年最需要的不是大模型

作者 莫崇宇
2025年11月11日 09:48

当 ChatGPT 震惊世界时,我们以为 AI 已经足够聪明。但它依然做不到一件事:准确判断你伸手去够桌边咖啡杯时,手指距离杯沿还有多少厘米。

今天,知名 AI 学者李飞飞用一篇博客回答了这个问题:真正的智能不只是文字游戏,而是藏在一个我们每天都在使用却从未意识到的能力里:

空间智能。

这是一种比语言更古老的智能,改变人类文明的时刻,从来依靠的都不是语言,而是对空间的感知、想象与推理。
比如古希腊学者通过观察影子计算出地球周长,科学家摆弄金属丝拼出DNA的双螺旋结构,消防员在烟雾中凭直觉判断建筑是否会坍塌。

而现在,AI 即将获得它一直缺失的这种能力。

省流版如下:

1. 当前 AI(特别是大型语言模型 LLM)虽然那改变了我们运用抽象知识的方式,语言能力很强,却缺乏对物理世界的真实经验和理解。它们在机器人、科学发现、沉浸式创造力等领域仍有根本局限。空间智能是 AI 的下一个前沿。它将彻底改变我们创造和体验现实与虚拟世界的方式,并将在机器人、科学发现和创造力等领域引发变革。

空间智能是人类智能的基石,甚至先于语言存在。它不仅支撑着我们与物理世界的日常互动(如驾驶、接住钥匙),也是人类想象力、创造力和科学发现(如古希腊测量地球周长、DNA 双螺旋结构的发现)的核心。它是人类认知赖以构建的「脚手架」。

2. 尽管多模态模型(MLLM)有所进步,但 AI 在空间能力上与人类相差甚远。它们无法准确估计距离、方向,无法在脑中「旋转」物体,也无法预测基本的物理规律。AI 缺乏这种能力,就无法真正与物理现实建立联系。要实现空间智能,我们需要超越 LLM,构建更具雄心的「世界模型」。这是一种全新的生成式模型,其能力远远超越当今的 LLM。李飞飞与 World Labs 正致力于此。

3. 李飞飞定义了世界模型必须具备的三种能力:

  1. 生成性 (Generative):能够生成在感知、几何和物理规律上保持一致性的世界。
  2. 多模态性 (Multimodal):天生设计为多模态,能处理和输出多种形式的信息(如图像、视频、深度图、文字、动作)。
  3. 交互性 (Interactive):能够根据输入的「动作」,预测或输出世界的「下一个状态」,并最终可能预测「下一步应采取的行动」。

4. 李飞飞认为,构建世界模型远比构建语言模型困难,因为世界的维度远超语言。这需要克服三大挑战:

  • 新的训练任务:需要找到类似 LLM 中「下一个词预测」那样优雅的通用任务函数,但难度更高。
  • 大规模数据:需要能从海量的互联网图像和视频中提取深层空间信息,并辅以合成数据和多模态数据。
  • 新的模型架构:需要超越当前 1D/2D 序列范式,发展出具备 3D 或 4D 感知能力的新架构(比如 World Labs 的 RTFM 模型)。

5. AI 应当增强人类的能力,而非取而代之。AI 应始终尊重人的自主性与尊严。空间智能正是这一愿景的体现,它旨在赋能人类的创造力、关怀能力和科学发现。

6. 空间智能的应用将分阶段展开:

  • 近期(创造力):赋能故事讲述、电影、游戏和建筑设计。World Labs 已推出 Marble 平台,帮助创作者构建 3D 世界。
  • 中期(机器人学):实现「行动中的具身智能」。世界模型将通过模拟训练,使机器人成为人类的协作助手。
  • 长期(科学、医疗与教育):在药物研发、材料科学、辅助诊断、环境感知监护以及沉浸式教育等领域产生变革性影响。

7. 探索空间智能是李飞飞科研生涯的「北极星」。没有空间智能,「真正智能机器」的梦想就无法实现。她呼吁整个 AI 生态系统共同努力,将这项技术用来造福全世界。

从语言到世界:空间智能是人工智能的下一个前沿

1950 年,当计算机还只是自动化运算和简单逻辑的工具时,Alan Turing 提出了一个至今仍回荡的问题:机器能思考吗?他以非凡的想象力看到了一个大胆的可能——智能也许有一天不是天生的,而是被「创造」出来的。这个洞见后来开启了一场被称为「人工智能(AI)」的不懈科学探索。

在我从事人工智能研究的二十五年里,Turing 的远见依然激励着我。但我们离那个目标还有多远?这个问题并不容易回答。

如今,领先的人工智能技术——例如大型语言模型(LLM)——已经开始改变我们获取和运用抽象知识的方式。然而,它们依然像在黑暗中打磨文字的匠人:语言优美,却缺乏经验;知识丰富,却未真正立足于现实。空间智能(Spatial Intelligence)将重新定义我们创造和体验现实与虚拟世界的方式——它将彻底变革故事讲述、创造力、机器人学、科学发现等多个领域。这正是人工智能的下一个前沿。

自我进入这一领域以来,对视觉与空间智能的追求一直是我的北极星。这也是我花费多年时间创建 ImageNet 的原因——这是第一个大规模视觉学习与评测数据集,与神经网络算法以及现代计算(如 GPU 图形处理单元)一起,成为现代人工智能诞生的三大关键支柱之一。过去十年里,我在斯坦福大学的实验室致力于将计算机视觉与机器人学习相结合。而正因为这一信念,我与联合创始人 Justin Johnson、Christoph Lassner、Ben Mildenhall 于一年多前共同创立了 World Labs——希望第一次真正实现这一愿景。

在这篇文章中,我将解释什么是空间智能、它为何重要,以及我们如何构建能够解锁这种智能的「世界模型」——这种能力将重新塑造创造力、具身智能,以及人类的进步。

空间智能:人类认知的支架

人工智能正处在前所未有的激动人心的时刻。生成式 AI 模型(如大型语言模型,LLM)已经从研究实验室走进日常生活,成为数十亿人用于创造、工作和交流的工具。它们展现出了曾被认为不可能的能力——能够轻松生成连贯的文本、大量的代码、逼真的图像,甚至短视频片段。如今,我们已无需再问「AI 是否会改变世界」,因为无论从哪个角度来看,它已经在改变世界。

然而,仍有许多目标尚未实现。自主机器人的愿景依旧令人着迷,但仍停留在猜想阶段,距离未来学家所描绘的日常生活场景还有很远。AI 在疾病治愈、新材料发现、粒子物理等领域中实现研究飞速推进的梦想,也仍然大体未能实现。而能真正理解并赋能人类创造者的 AI——无论是学习分子化学复杂概念的学生、构思空间的建筑师、构建世界的电影制作人,还是追求沉浸式虚拟体验的任何人——仍未到来。

要理解这些能力为何仍难以实现,我们需要回溯空间智能的演化历程,并探究它如何塑造了我们对世界的理解。

视觉长期以来一直是人类智能的基石,但它的力量源于更为根本的机制。在动物能够筑巢、养育后代、使用语言交流或建立文明之前,最初那种感知的能力——哪怕只是捕捉到一束光、一种触感——就悄然点燃了通往智能的进化之路。

这种看似孤立的、从外部世界提取信息的能力,在感知与生存之间搭起了一座桥梁,而这座桥梁随着世代更迭不断加固、扩展。层层叠叠的神经元在这座桥上生长,形成了解读世界、协调有机体与环境互动的神经系统。因此,许多科学家推测:「感知—行动」这一循环正是智能进化的核心驱动力,也是自然界创造出我们——这种能感知、学习、思考并行动的物种——的根本基础。

空间智能在我们与物理世界的互动中起着至关重要的作用。每天,我们都在依赖它完成各种看似平凡的动作:停车时通过想象车头与路沿间逐渐缩小的距离来判断位置;接住从房间另一头扔来的钥匙;在人群密集的人行道上穿行而不相撞;或是半睡半醒时不用看就能把咖啡倒进杯子里。

在更极端的情境下,消防员在坍塌的建筑物中穿行,在烟雾弥漫的环境中凭直觉判断结构是否稳定、如何生存,并通过手势、身体语言以及一种难以言传的职业本能进行交流。而孩子们则在学会说话之前的几个月甚至几年里,通过与环境的游戏式互动来认识世界。所有这一切都发生得自然而然、毫不费力——这正是一种机器尚未掌握的「本能流畅」。

空间智能同样是我们想象力与创造力的基础。讲故事的人在脑海中构建出丰富的世界,并借助各种视觉媒介将其传达给他人——从远古的洞穴壁画,到现代电影,再到沉浸式电子游戏。无论是孩子们在沙滩上堆砌城堡,还是在电脑上玩《我的世界》(Minecraft),以空间为基础的想象力都是他们在现实或虚拟世界中进行互动体验的核心。

在众多行业中,对物体、场景以及动态交互环境的模拟也成为关键支撑——从工业设计到数字孪生,从机器人训练到各种业务应用,空间智能驱动着无数重要的实践场景。

纵观历史,空间智能在许多奠定文明走向的关键时刻都发挥了核心作用。

在古希腊,Eratosthenes 通过观察「影子」悟出了几何原理——他在亚历山大测量到阳光与地面形成的 7 度角,并在同一时间注意到赛恩城没有影子,由此计算出了地球的周长。

Hargreave 发明的「珍妮纺纱机」(Spinning Jenny)则通过一个空间布局的巧思彻底革新了纺织业:他将多个纺锤并排安装在同一架子上,让一个工人能够同时纺出多股纱线,从而将生产效率提高了八倍。

Watson 和 Crick 则通过亲手搭建三维分子模型,摆弄金属板与金属丝,最终拼出了 DNA 的空间结构,让碱基对的排列方式恰如其分地契合在一起。

在这些案例中,空间智能都是推动人类文明前进的关键力量——科学家和发明家必须操控物体、想象结构,并在物理空间中进行推理,而这些过程是文字所无法完全表达的。

空间智能,是人类认知赖以构建的「脚手架」。无论是被动观察还是主动创造,它都在发挥作用。它驱动着我们的推理与规划,即便面对最抽象的问题也是如此。它同样决定了我们与世界互动的方式——无论是语言交流还是身体行动,无论是与他人还是与环境本身的互动。

虽然我们大多数人并不会每天都像 Eratosthenes 那样揭示自然的奥秘,但我们依然以相同的方式思考——通过感官理解复杂的世界,并凭直觉掌握其中的物理与空间规律。

遗憾的是,如今的人工智能还无法像这样思考。

过去几年中,人工智能确实取得了巨大进步。多模态大型语言模型(Multimodal LLMs,简称 MLLM)在海量多媒体数据(除了文本外还包括图像、音频、视频等)的训练下,初步具备了某种「空间感知」能力。如今的 AI 已能分析图片、回答与图片相关的问题,并生成极其逼真的图像与短视频。同时,得益于传感器与触觉技术的突破,最先进的机器人也开始能够在高度受限的环境中操控物体和工具。

但坦率地说,AI 的空间能力仍远未达到人类水平,这种差距一眼便能看出。当前最先进的 MLLM 模型在估计距离、方向、大小等方面的表现往往不比随机猜测好多少——它们也无法像人类那样在脑海中「旋转」物体,从不同角度重新想象其形状。它们不能穿越迷宫、识别捷径,也无法预测最基本的物理规律。AI 生成的视频虽然令人惊叹,但往往在几秒后就失去连贯性。

目前的尖端 AI 虽然在阅读、写作、研究和数据模式识别等任务中表现出色,但在理解或与物理世界交互时,却存在根本性的局限。我们对世界的感知是整体性的——不仅看到「事物本身」,还理解它们在空间上的关系、意义以及重要性。而通过想象、推理、创造和互动来理解世界——而不仅仅是用语言去描述——这正是空间智能的力量所在。

缺乏这种能力,AI 就无法真正与它想要理解的物理现实建立联系。它无法安全高效地驾驶汽车,无法在家庭或医院中灵活地引导机器人,也难以为学习与娱乐提供全新的沉浸式互动体验,更无法大幅加速材料科学或医学领域的发现。

哲学家维特根斯坦(Wittgenstein)曾写道:「我的语言的界限意味着我的世界的界限。」

我并非哲学家,但至少我知道——对于人工智能而言,世界不止于「语言」。空间智能代表着超越语言的前沿——它是一种将想象、感知与行动连接起来的能力,使机器真正能够拓展人类的生活潜能,从医疗到创造力,从科学发现到日常助理,都因此焕发新可能。

人工智能的下一个十年:构建真正具备空间智能的机器

那么,我们该如何构建具有空间智能的 AI?怎样才能让模型既能像 Eratosthenes 那样进行空间推理,又能像工业设计师那样精确创造,像讲故事的人那样富有想象力,并像救援人员那样在复杂环境中灵活行动?

要实现这一点,我们需要的不只是大型语言模型(LLM),而是一种更具雄心的体系——世界模型(World Models)。这是一类全新的生成式模型,能够在语义、物理、几何与动态复杂的世界中——无论虚拟还是现实——进行理解、推理、生成与交互,其能力远远超越当今的 LLM。

这一研究领域尚处于萌芽阶段,目前的探索方法从抽象推理模型到视频生成系统皆有涉猎。World Labs 正是在这种信念下于 2024 年初创立的:我们认为,基础方法尚未确立,而这正是未来十年人工智能发展的决定性挑战。

在这个新兴领域中,最重要的是建立一套指导发展的核心原则。对于空间智能而言,我将「世界模型」定义为具备以下三种关键能力的系统:

1. 生成性(Generative):世界模型能够生成具备感知、几何与物理一致性的世界

要实现空间理解与推理,世界模型必须具备生成自身「模拟世界」的能力。它们应能根据语义或感知层面的指令,生成无穷多样的虚拟世界——这些世界无论在几何结构、物理规律还是动态变化上,都必须保持一致性,无论它们代表的是现实空间还是虚拟空间。

研究界正在积极探索这些世界应当以内在几何结构的隐式还是显式形式来表示。此外,除了具备强大的潜在表示能力,我认为一个通用的世界模型还必须能够输出清晰可观测的世界状态,以适应多种应用场景。尤其重要的是,模型对「当下世界」的理解必须与「过去世界」的状态相连贯——它要能理解世界从过去如何演变到现在。

2. 多模态性(Multimodal):世界模型从设计上就是多模态的

就像人类与动物一样,世界模型也应能处理多种形式的输入——在生成式 AI 领域,这些输入被称为「提示(prompt)」。面对不完整的信息(如图像、视频、深度图、文字指令、手势或动作),世界模型应能预测或生成尽可能完整的世界状态。

这要求它在处理视觉输入时具备接近真实视觉的精度,同时在理解语义指令时同样灵活。这样,智能体(agent)与人类都能通过多样化的输入与模型进行交流,并获得同样多样化的输出反馈。

3. 交互性(Interactive):世界模型能够根据输入的动作输出下一步的世界状态

最后,当「动作」或「目标」被作为输入提示的一部分时,世界模型的输出必须包含世界的下一状态,这种状态可以是隐式的,也可以是显式的。

当模型接收到一个动作(无论是否包含目标状态)作为输入时,它应能输出与世界先前状态、目标状态(若有)、语义含义、物理规律及动态行为一致的结果。

随着具备空间智能的世界模型在推理与生成能力上不断增强,可以想象——未来面对某个给定目标时,世界模型不仅能够预测世界的下一状态,还能基于这一新状态预测「下一步应采取的行动」。

这一挑战的规模,远超人工智能以往所面对的一切。

语言,是人类认知中一种纯粹的生成现象;而「世界」,却遵循着复杂得多的规律。以地球为例,引力决定了运动规律,原子结构影响了光线的色彩与亮度,无数的物理定律限制着每一次交互。即便是最天马行空的虚构世界,也依然由服从这些物理法则与动态行为的空间物体和智能体所构成。要让语义、几何、动态与物理这几种层面在同一模型中保持一致,需要全新的方法与思路。

世界的表示维度远比语言这种「一维、序列信号」复杂得多。要让世界模型具备人类所拥有的那种通用能力,我们必须跨越多个艰巨的技术障碍。而在 World Labs,我们的研究团队正致力于为实现这一目标奠定基础性突破。

以下是我们当前正在研究的一些课题示例:

· 一种新的通用训练任务函数
为世界模型定义一个像大型语言模型(LLM)中「下一个词预测」那样简单又优雅的通用任务函数,一直是该领域的核心目标。然而,由于世界模型的输入与输出空间更加复杂,这一函数的设计难度要高得多。尽管仍有许多未知需要探索,但这种目标函数及其对应的表示方式,必须能够体现几何与物理规律,忠实地反映世界模型作为「连接想象与现实的有根表示」的本质。

· 大规模训练数据
训练世界模型所需的数据复杂程度远超文本数据。好消息是——庞大的数据源已经存在。互联网级的图像与视频资源,为训练提供了丰富、可获取的素材。真正的挑战在于:如何开发算法,从这些基于二维图像或视频帧(即 RGB 信号)的数据中提取更深层次的空间信息。过去十年的研究表明,语言模型的性能提升遵循「数据量与模型规模的扩展规律」;而对于世界模型来说,关键突破在于构建能够在相似规模下充分利用视觉数据的模型架构。

此外,我们也不应低估高质量合成数据以及深度图、触觉等额外模态的价值。它们在训练过程的关键阶段能对互联网级数据起到补充作用。要让这一过程更高效,还依赖于更先进的传感系统、更稳健的信号提取算法,以及更强大的神经仿真技术。

· 新的模型架构与表征学习
世界模型的研究必然会推动模型架构与学习算法的革新,特别是超越当前多模态语言模型(MLLM)和视频扩散模型的范式。现有方法通常将数据「分词化」为一维或二维序列,这让一些简单的空间任务变得异常困难——例如在短视频中数清不同的椅子,或回忆一小时前房间的布局。

新的架构可能带来改进,比如在分词、上下文与记忆机制中引入三维或四维感知能力。举例来说,World Labs 最近开发的实时生成帧模型(RTFM)就是这种转变的体现。该模型利用「以空间为基础的帧」作为空间记忆单元,实现了高效的实时生成,同时在生成的世界中保持连续性与稳定性。

显然,我们距离通过「世界建模」彻底释放空间智能的潜能,还有许多艰巨的挑战要克服。

这项研究不仅仅是理论探索——它是推动新一代创造性与生产力工具的核心引擎。而在 World Labs,我们已经取得了一些令人振奋的进展。

最近,我们向少量用户展示了 Marble ——首个能够通过多模态输入进行提示(prompt),并生成、维持一致性三维环境的世界模型。它让用户与创作者能够在这些虚拟空间中探索、互动,并将其纳入创作流程中继续扩展。我们正在努力,让 Marble 尽快向公众开放!

Marble 只是我们迈向真正具备空间智能的世界模型的第一步。
随着研究的加速推进,科研人员、工程师、用户和商业领袖都开始认识到这项技术的非凡潜力。下一代世界模型将让机器实现一个全新的空间智能层次——这将解锁当今 AI 系统中仍大多缺失的关键能力。

用「世界模型」构建更美好的人类世界

推动 AI 发展的动机至关重要。

作为一名参与开启现代人工智能时代的科学家,我的初衷始终明确:AI 应当增强人类的能力,而非取而代之。

多年来,我一直致力于让 AI 的发展、应用与治理更好地契合人类需求。如今,关于科技乌托邦与末日论的极端叙事层出不穷,但我始终保持务实的信念:AI 由人创造,为人服务,并由人类治理。

它必须始终尊重人的自主性与尊严。AI 的真正魔力在于延展我们的能力——让我们变得更有创造力、更具连接性、更高效,也更充实。

空间智能正体现了这一愿景:

它是一种能赋能人类创作者、照护者、科学家与梦想家的人工智能,帮助我们实现曾经无法实现的目标。
正是这种信念,支撑着我将「空间智能」视为人工智能下一个伟大前沿领域的决心。

空间智能的应用将分阶段展开。

如今,创意类工具 已经开始出现——World Labs 的 Marble 已将这些能力交到创作者与故事讲述者手中。
机器人学 是中期目标,我们正在不断完善「感知—行动」循环,使机器能够在物理世界中灵活操作。
而最具变革性的 科学应用 可能需要更长时间,但它们的影响将深远,足以促进人类福祉的全面提升。

在这些不同的发展阶段中,有若干关键领域尤为突出——它们蕴含着重新定义人类能力的巨大潜力。

要实现这一目标,必然需要集体的努力——远非一个团队或一家公司所能独立完成。

这将需要整个 AI 生态系统的共同参与:研究者、创新者、企业家、公司乃至政策制定者,都应携手朝着共同的愿景前进。

而这个愿景,值得我们为之奋斗。

未来,将由此展开:

创造力:为讲故事与沉浸式体验赋予超级能力

「创造力就是智慧在玩耍。」这是我最喜欢的一句名言,出自我个人的英雄——爱因斯坦。在人类拥有文字之前,就已经在讲故事了——在洞穴的墙壁上作画、通过口口相传流传下来,并在共同的叙事中建立起整个文化。故事帮助我们理解世界,跨越时间与空间建立联系,探索「人类」意味着什么。更重要的是,它帮助我们在生命与爱中找到意义。

如今,空间智能有潜力彻底改变我们创作和体验故事的方式,不仅保留其根本的重要性,还将其影响力延伸至娱乐、教育、设计、建筑等多个领域。

World Labs 的 Marble 平台为电影制作人、游戏设计师、建筑师以及各类讲故事的人,提供了前所未有的空间能力和编辑控制权,让他们能够快速创建并反复迭代可自由探索的 3D 世界,而无需传统 3D 设计软件所需的大量投入。创造本身依旧是充满人性和活力的行为,AI 工具只是放大并加速了创作者的潜能。这包括:

  • 多维度叙事体验:电影人和游戏设计师正在利用 Marble 构建完整的虚拟世界,不再受限于预算或地理位置。他们能探索各种场景和视角,这在传统的制作流程中几乎是无法实现的。随着不同媒介和娱乐形式的界限逐渐模糊,我们正迈向全新的交互式体验形式,它融合了艺术、模拟和游戏——一个个个性化世界,不再仅属于大型工作室,而是任何人都可以创造和参与其中。随着更快速的方法将创意和分镜转化为完整体验,叙事将不再局限于某一种媒介,创作者可以在各种平台和界面上建立拥有共同线索的故事世界。
  • 通过设计实现空间叙事:几乎所有制造出来的物品或建造的空间,在实际成形之前都必须先进行虚拟 3D 设计。这个过程通常需要大量时间和金钱,且高度反复。而借助具备空间智能的模型,建筑师可以在投入数月设计前快速可视化结构,甚至可以「走进」还不存在的空间——讲述我们未来如何生活、工作和聚会的故事。工业设计师和时尚设计师也可以立即将想象转化为形体,探索物体如何与人体和空间互动。
  • 全新的沉浸式与互动体验:体验本身,是我们人类赋予事物意义的最深刻方式之一。在人类历史的绝大多数时间里,我们只拥有一个三维世界:我们共同生活的现实世界。直到近几十年,通过电子游戏和早期的虚拟现实(VR),我们才开始窥见由人类自己创造的另一个世界。而如今,空间智能结合虚拟现实(VR)、扩展现实(XR)头显以及沉浸式显示设备,使这些体验达到了前所未有的高度。我们正走向一个未来——进入完整构建的多维世界将像翻开一本书一样自然。空间智能让世界构建能力不仅属于拥有专业制作团队的工作室,也属于有故事、有想法的每一个人,包括独立创作者、教育者以及任何想要表达愿景的人。

机器人技术:行动中的具身智能

从昆虫到人类,动物依靠空间智能来理解、导航并与周围世界互动。机器人也不例外。具备空间感知能力的机器一直是机器人领域的梦想,我在斯坦福的研究实验室与学生和合作者们的工作,也正是围绕这一目标展开。这也是我对 World Labs 所构建的模型充满期待的原因之一——它们有望让这一梦想成真。

  • 通过世界模型扩展机器人学习能力:机器人学习的进步依赖于可扩展的高质量训练数据。考虑到机器人必须学会理解、推理、规划和互动的庞大状态空间,许多研究者认为要真正实现通用型机器人,必须结合互联网数据、合成仿真和现实世界中的人类示范数据。然而,与语言模型不同,机器人研究目前缺乏足够的训练数据。而世界模型将在其中发挥决定性作用。随着其感知逼真度和计算效率的提升,世界模型的输出可以迅速缩小仿真与现实之间的差距。这将有助于在无数种状态、互动和环境的模拟中训练机器人。
  • 成为伙伴与协作助手:机器人作为人类的协作伙伴,无论是在实验室中辅助科学家,还是在家中帮助独居老人,都能在劳动力紧缺和生产效率亟需提升的背景下,承担重要角色。但要实现这一点,机器人必须具备空间智能:能够感知、推理、规划和行动,而且——这一点最关键——要能与人类的目标和行为保持情感上的一致性。例如,实验室里的机器人可以操作仪器,让科学家专注于需要精细操作或逻辑推理的任务;而家用机器人可以协助老年人做饭,同时不剥夺他们的乐趣与自主性。真正具备空间智能的世界模型,能够预测环境的下一步状态,甚至预测符合人类预期的动作,对于实现这一目标至关重要。
  • 拓展具身形式的多样性:类人机器人确实适用于我们为自己打造的世界,但技术创新的全部潜力,将体现在更丰富多样的设计形式中:比如能够输送药物的纳米机器人、可在狭小空间中活动的软体机器人,以及为深海或外太空环境设计的专用机器。不论它们的外形如何,未来的空间智能模型都必须整合机器人所处的环境,以及其自身的感知与运动能力。但开发这些机器人面临的核心挑战之一,是缺乏适用于各种具身形式的训练数据。世界模型将在模拟数据生成、训练环境构建,以及任务基准测试等方面,发挥关键作用。

更长远的视野:科学、医疗与教育

除了在创意和机器人领域的应用,空间智能还将在其他领域产生深远影响,特别是在那些 AI 可以增强人类能力、挽救生命、加速发现的地方。我在下面重点介绍三个具有变革潜力的领域,当然,空间智能的应用远不止于此,还将在更多行业中大展拳脚。

在科学研究中,具备空间智能的系统可以模拟实验、并行测试假设,并探索人类难以抵达的环境——从深海到遥远的行星。这项技术将彻底改变气候科学、材料研究等领域的计算建模方式。通过将多维仿真与真实世界的数据采集相结合,这些工具可以降低计算门槛,扩展每一个实验室所能观察和理解的范围。

在医疗健康领域,空间智能将重塑从实验室到病床的各个环节。在斯坦福,我的学生和合作伙伴们多年来一直与医院、养老机构以及家庭中的病患合作。这些经验让我深信,空间智能在医疗中的变革潜力巨大。AI 可以通过建模分子之间的多维交互,加速药物研发;通过辅助放射科医生识别医学影像中的模式,提升诊断精度;还可以实现环境感知型的监护系统,为病患和护理人员提供支持,同时不替代医疗过程中至关重要的人际联系。更不用说机器人在协助医护人员和患者方面,在多种场景中也大有可为。

在教育方面,空间智能能够实现沉浸式学习,让抽象或复杂的概念变得具体可感,并创造出与人类大脑和身体学习方式高度契合的、可反复练习的学习体验。在 AI 时代,无论是对学龄儿童还是成年人成年人来说,更快、更有效的学习和技能再培训都尤为关键。学生可以「进入」细胞机制,或亲身「走过」历史事件;教师可以借助交互式环境实现个性化教学;而从外科医生到工程师等专业人士,也能在逼真的模拟中安全地练习复杂技能。

虽然这些领域的应用前景几乎没有边界,但我们的目标始终如一:用 AI 增强人类的专业能力、加速人类的发现、放大人类的关怀——而不是取代那些构成人类本质的判断力、创造力与同理心。

结语

过去十年,人工智能已成为全球现象,并在科技、经济乃至地缘政治领域引发重大转折。但作为一名研究者、教育者、如今也是一位创业者,最令我振奋的,仍然是图灵在 75 年前提出的那个问题背后的精神。我依然怀有与他相同的那份好奇与敬畏之心。正是这种探索空间智能的挑战,成为我每天的动力源泉。

在人类历史上,我们第一次有机会打造出与物理世界高度协调的机器,使它们成为我们应对重大挑战时真正的合作伙伴。无论是在实验室中加速对疾病的理解、彻底改变我们讲述故事的方式,还是在我们因疾病、受伤或衰老而处于最脆弱状态时给予支持,我们正站在这样一项技术的门槛前,它将提升那些我们最在乎的生活体验。这是一种更深刻、更丰富、更有力量的生活愿景。

在大约五亿年前,大自然首次赋予远古动物空间智能的萌芽。而今天,我们有幸成为这一代技术人中的一员,可能很快就能让机器也拥有这种能力——并有机会将这项能力用来造福全世界人民。没有空间智能,我们对「真正智能机器」的梦想就无法真正实现。

这个探索旅程,就是我心中的北极星。欢迎你与我一同追寻。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


❌
❌