普通视图

发现新文章,点击刷新页面。
昨天以前Colorful - 一枚数字艺术家的自留地

《精读源码》- GoT

源码分析图

GoT
GoT

Thoughts 统一结构后,Operation 实例的私有执行方法实际上就变成了前置 T 的转换函数,这个巧妙的设计是 GoT 框架的灵魂所在,比如可以用这个 T 生成新的 T’、也可以原地更新 T、或者其他操作,故名曰:思维变换。

  • GoT 在图数据结构上的算法简单高效(默认 Controller 执行策略为 BFS,最终实现在叶子节点上获得所有 Thoughts 的结果整合)
  • Operation 核心是私有执行方法即 T 的变换函数,通过前置/后置指针最终连接在一起,因此支持分形
  • 根节点是没有前置节点的节点;叶子节点是没有后置节点的节点
  • 因为采用的是有向图的结构表达,因此天然支持降级为 IO / CoT / ToT

Github

面向未来的认知优化之《爆裂》启示录

目前,我们的旅程一直很舒适、惬意,路况一直很好,食物也充足、美味…… 事实上,如果我没经历过那些前所未有的糟糕透顶的事情,我会说,麻烦的事情才刚刚开始呢。 - 塔姆森。唐纳(Tamsen Donner)1846.6.16

前言

今天的科技变革速度远远超乎过往所有的「理所当然」和「想象」。

相比单纯记录下《爆裂》的读后感,我更愿意从另外的角度谈谈我的思考和理解,也许还会穿插一些我认为有趣的故事。因此请允许我调皮地将本文的标题改为「杂谈」。最近有刷到孟晚舟女士的一个短视频,视频中提到,她在谈及自己初三的儿子未来的专业选择和职业规划的时候说:“不管你将来选择什么样的职业,一定不要选择和机器竞争的职业”。

What?!和机器竞争? 我想,无论作为启蒙教育者的父母,还是专业教育者,很少会从这个不可思议的角度去发出这样的 “劝告”。这里的关键问题是,我们有多少人开始真正意识到这个问题

世界从未停止变革

现在,让我们抛开一些非常时髦的概念和名词,尝试静下心来挖掘一下发生在日常生活、工作中随处可见的案例,来以此感受「世界规则的颠覆」。

了解我的人应该知道我对「软件工具」的痴迷程度,从 08 年成为独立博主开始,经历了那个独立博客百花齐放的 Web 2.0 时代,那时候的互联网还没有正式进入无线时代,各大商业软件公司以抢占用户的电脑桌面为主,然而更加令人敬畏的是一群 Hero 在这个自由的世界里分享着各种各样的工具,分享精神就是 WWW 的灵魂。如果你曾痴迷于订阅「异次元」、「小众软件」、「善用佳软」等博客,你应该会对这些宝藏博客至今记忆犹新,他们中有些至今仍然在保持着稳定、高频的内容更新。

如果你是一个跟我一样的狂热爱好者,那我坚信我们对 Small is powerfull 会有共鸣,如今,你可以从你的口袋里随时拿出一个利器帮助你自动化地、高效地解决很多实际问题,并且他们运行在 Everytime & Everywhere

但是我想强调的是,关键的并不在于你有多少工具,而在于 你开始培养这样的意识和工作方式善用 工具、擅用 工具,毫不夸张地说,这个认知的变化,会给你带来巨变。

这些是「机器」吗?我想,是的。

值得深入探讨的是我们作为「Human」和「Machine」(以下均以这两个关键字提及)的协同关系,或者叫人机交互关系。

在此之前,请允许我抛砖引玉,通过一些过往的经历和认知,来阐述一下《爆裂》中提到的 九大法则我并不打算 “循规蹈矩” 地写这篇文章:去罗列这九大法则然后一一解释。我会像写故事一样将 这些法则穿插解释在其中,因为我特别希望他们更加生动一些,而不是成为某种新的教条。就像这些法则也自然地穿插在我们这个世界一样,他们从来也都不是孤立的。在我看来,这些法则本身并不 “高大上”,它们其实从头到尾一直在,所以,总结和思考它们的意义会更加靠谱一点,而不是沉迷于它们。

拉力优于推力的魔力

改变世界只需要 “一群有思想,有责任心的公民”。 - 《爆裂》

关于这个法则,其实都不用举现在的例子,现在的这个现象或者说 法则的体现 很明显已经频繁地发生在我们的身边了,聊聊过去,再结合现在,或许会更加有趣和好玩儿。

由于被狂热的爱好疯狂驱使,作为一个 90 年生人,有幸在 03 年说服爸妈给我买了人生第一台电脑,当时正是 Windows XP 火遍全球的时候。此后的时间里,这台电脑更多的成为了我的娱乐设施,我并没有很好地用它解决什么问题,在我看来,它完全变成了一台游戏机。但是我仍然对 Windows XP 的各项功能惊叹不已,在还没接触互联网的时候,阅读系统的 Help 脱机文档有时候都能成为一种享受

Windows XP Help and Support Center
Windows XP Help and Support Center

大概在 07 年左右,家里已经接入了宽带,我对这台有些 “古老” 的电脑也做了一次硬件的全面升级,作为那个年代的我们,最早一头扎进互联网的时候,仍然第一时间选择了游戏,但即使在这样眼花缭乱的虚拟世界里总是有非常多的聪明人,可以和现实世界中打理生活一样井井有条。

当时我已经开始接触到世界级网游:魔兽世界(以下简称 WoW),复杂到离谱的界面和操作方式当时劝退了一批玩家,时常我们需要用一些 辅助插件 来帮助我们在游戏中完成一些更好的操作,注意,这在当时来看是 革命性 的东西,尤其是那个外挂满天飞的年代。直到后来正式成为一名技术工作者,我才知道,当时 WoW 使用的是先进的 Lua Language 作为插件机制的核心实现。如今,Lua 仍然在众多领域发挥着极其重要的作用。(考虑到本文的受众和读者,这里不做更多展开叙述,感兴趣的可以自行 Google)

Beginning Lua with World of Warcraft Add-ons
Beginning Lua with World of Warcraft Add-ons

这样的机制在当时整个全球的 WoW 社区运作的非常好,商业公司和个人开发者不断涌入,贡献和分享了一大堆非常实用的插件,极大增强了全球玩家的游戏体验,它们改变了这个 “世界”。而且,你可以随意安装和卸载这些插件,如果你有一定的开发能力,你随时随地可以把你的创意分享给全球所有玩家。

一个游戏真正配称得上「伟大」,除了在于它本身高质量的水准之外,我想更多的是它能否带来更大的想象力,它能够提供某种「拉力」机制,让更多有思想的人进来参与奉献。 结果就是这样的伟大成为了经典,经久不衰。

其实,WoW 这样的例子在暴雪这样一家伟大公司的早期作品中就出现过雏形,《魔兽争霸》可以提供地图制作及功能定制化能力。令人惊叹的是,这样的机制让一款其实非常古老的游戏至今仍然活跃在世界的舞台上,甚至直接出现了同为世界级的衍生品。如今大为流行的 Moba 类型游戏的鼻祖,我相信老玩家都会脱口而出它的名字。

显然这样的机制离不开互联网强大的连接能力,全球玩家被自然连接在一个虚拟世界里,这样的连接并不会受到可见的限制,一个倡议群体和一个优秀的厂商碰撞在一起的时候,就会发生持续的、令人惊喜的化学 🧪 反应,它持续地为创新者激发自己的潜能和热情。

涌现优于权威

此前,我们对于知识的产生和传播有着十分单一的理解:它来自上帝,透露给不同的牧师、先知、神父、政教合一的领袖,并以教义(或世俗领域的政策)的形式传达给古代的中层管理人士,最后传播到无条件顺从的普罗大众。这样的认知很陈腐,饱含专制的意味。- 《爆裂》

事实证明这样的「拉力」机制仍然影响着全球的游戏市场和游戏的运营寿命,例子数不胜数。值得注意的是,这样的机制发展到今天愈演愈烈,伟大的创新电子游戏 Minecraft (我的世界)甚至直接将这样的机制注入到本体设计。而如今,甚至开始出现一些另类的「极端」。

有这么一款「不是游戏」的游戏。它开始套上「元宇宙」的概念火遍全球,资本市场都为之疯狂,它就是 Roblox。

图片来自视觉中国
图片来自视觉中国

但是,就在人们认为这个「元宇宙第一股」all in 元宇宙和游戏的时候,Roblox 却突然掉头,直接瞄准了和元宇宙、游戏赛道相去甚远的方向:教育培训。在这个领域,它给自己的定位是「针对少儿的在线游戏平台」,其已经在纽交所上市,月度用户将近 2 个亿,并且 2 / 3 的玩家是在校的学生,这样疯狂的数据,让其经营主体直接成为当今世界最有价值的电子游戏公司之一。

它的伟大在于它极有可能带来教育的数字化颠覆,即使我们现在已经有大量的科技进入到了教育领域,然而事实上它们并没有带来实质性的变化,教育仍然受制于「权威」,今天的中小学教育模式大部分源自于 19 世纪中期的普鲁士教育模式,大约 170 年前,这种模式在西方非常流行,它试图让学生「整齐划一」以适应未来的工厂工作。

如今这个模式仍然影响着我们的教育,标化的教育显然已经不是为了挖掘个体的独特去设计,而是试图制造想法一致的人,靠各种规章制度、评分机制在运作和维持。面向未来,这样自上而下的教育体系会逐渐失去作用。

我相信我们都有一个深刻的体会,这样体系下试图培养的规则和习惯,等入了职场之后,几乎没有任何顺畅的衔接体感,我们几乎需要从头开始适应职场。比如,作为一个技术工作者或者创意工作者,我并不需要某个定时 call 我的「铃声」,我要做的反而是专注于这个事情,并且搞定它。这种局面形成了「学校」过渡到「社会或职场」的天然「矛盾」和不连贯性,这是因为我们的教育体制在做一个危险的假定,认为经过多年的严格教育后,学生会具备在快速变化的社会和环境中取得成功的能力,可实际情况显而易见,非常令人沮丧。

关于这个「脱节」的说法,在《爆裂》一书中曾经提到 John Dewey 在一个世纪前就已经认识到这一点。当时他呼吁应该将学生的生活和学习无缝融合,主张 “教育即生活”。

事实上,大量的研究表明,当一个人将学习和自己的兴趣、人际关系以及可能追求的机会联系起来时,他会学得更好。

让人欣慰的是,这样的包袱和糟糕的情况,在 Roblox 等数字化世界里可以直接被改变,值得一提的是,Minecraft 早在 Roblox 之前已经率先进入教育领域,取得了很多意外收获。

如下是一个 Minecraft 的化学教育版的演示视频:

Roblox 既可以天然寓教于乐,教育工作者只需要预制一些课堂模板,剩下的让充满智慧的学生去自然「涌现」,它也可以形成某种激励制度,吸引更多的教育学者进场,带来一个良性的可持续发展。显然,激发独立个体的潜能才是教育真正的意义,不是么?

Roblox 正在推出儿童编程等课外兴趣课程
Roblox 正在推出儿童编程等课外兴趣课程

现在,我们可以感受到:在技能培训、兴趣教育方面,游戏可能真的是一个非常不错的老师,这在我看来正是「教育」和「学习」的本质区别,我们应该重视学习,而不是教育本身,授人以渔一定好过授人以鱼。

诚然,互联网世界本身的不对称性导致很多东西是双刃剑,时好时坏,关键在于我们如何识别和善用它。 我们可以留心回忆一下,或许我们在很多分享中和聆听别人的分享中经常能够听到「游戏」世界中的例子,它通过数字化的感染方式,也曾经给很多人带来非常正向的启发和积极乐观的处世态度。

在中国式的家庭教育里,曾经的「游戏」是让很多家长极其厌恶的东西,但是我们不得不承认,世界规则就是在悄然地发生颠覆性的变化,或许我们每一个人都应该思考和辩证地看待所有问题,这或许就是《爆裂》的法则真正想告诉所有人的东西:快速变革的时代,我们的适应性什么时候真正发挥出来?

这需要一定的时间,但是,我们必须做好准备。

实践优于理论的探索

从理论上讲,理论和实践没有差别。而在实践中,却有差别。 - 约吉。贝拉(Yogo Berra)

现在,我们已经从几个法则中聊到了「教育」和「学习」,让我们回到孟晚舟女士的 “劝告”,我现在想和大家聊聊这个「后工业前、人工智能时代」。

在未来,有一个结论会越来越突出,那就是:我们一定不需要人肉机器,我们需要更多伟大的创新者。

回到我们的日常工作中,说说我的体感,之前有提到,我是一个「工具」的倡议者,我相信绝大多数的技术工作者、程序员、计算机科研人员都有一个共识和很好的习惯,那就是我们都在工作中实践着「聪明的偷懒」。

很有趣的是,即使是在现在这个数字化高速发展的时代,我们似乎很少主动去承认今天「部分的工作」都可以「被机器替代」了,然而,我的观点是:事实上我们有些人每天都在这样做,而那些不承认的是,极有可能是不知道。

当然,我在工作中亲眼看到的情况比这个结论要更加复杂一些:

  1. 大部分人处于认知程度缺乏和无意识状态,机械地执行工作似乎成为了 “本能”。
  2. 大部分人不大愿意挑战「权威」,认为循规蹈矩地做事情风险更低。
  3. 大部分人不大愿意去突破自己的边界,不愿意去让自己成为多样化的存在,或者说缺乏在这个快速变革时代下的学习路径。
  4. 还有极少部分人,已经从「认知」层面就和这个时代发生着「对抗」。

这种现象在数字化程度比较低的国内公司里 随处可见,毫不夸张地说,如果不从「认知」上变革,我们中大部分停留在这个状态的人,即将被这个时代无情的淘汰。

在现实生活的工作中,我总是非常热情地给同事们分享很多我认为有价值的「工具」,即使对方不是像我一样的专业技术领域的工作者。因为这和我的专业没关系,仅仅是因为,我自己本身就是一个极度具有分享欲的人,并且,我认为,不管什么样的工作岗位,你在使用怎样的系统完成你日常的工作,有一个事实才真正值得探讨:那就是我们的工作方式。令人沮丧的是,我看到了一个非常令人担忧的局面:我们的工作方式和 10 年前没有任何本质区别

看到这里,我相信有人一定会反驳我:我现在每天都在使用各种各样的 SaaS 软件解决我负责的工作内容,10 年前可并没有这些玩意儿啊。

注意,我再次强调,我说的是,我们的工作方式和 10 年前没有任何本质区别,数字化工具赋予我们的能力,在我看来,一直是数字化工具在驾驭着我们,我们还并没有驾驭数字化工具,或者说,我们不能因为数字化工具的产生去说改变了我们的工作方式,而是应该去思考:我们的工作方式本身有没有去驾驭数字化工具?

值得欣慰的是,商业世界里总是有聪明人能够聚在一起去思考一些真正高度的问题,就在我自以为是的担忧时,很多优秀的厂商已经在解决这样的问题了,他们被称之为「下一代生产力工具」,国内的创业公司跻身这个赛道的已经有很多了,我认为这是一个非常好的开端,当然了,这里面还有一个很有意思的话题就是,这个赛道在美国其实已经早就爆发式增长了,这个感兴趣的可以与我交流。

前段时间,有同事 A 找我解决一个工作中的问题,这个问题本身不复杂,但是受限于他的专业,他需要撬动资源去解决这个问题,需要协调部分开发人员进行排期开发,一想到这里,他非常沮丧,因为在组织结构复杂的现实情况中,我们都很清楚,协调资源解决问题的难度。

于是,我给他推荐了被称为「自动化工作流」类别的工具,实际上,我在最开始并没有直接给他推荐某一个这个类别下的免费产品,我认为 传递价值比直接推荐工具来的有意义的多,我希望大家去了解一些好玩的东西,能够真正快速解决工作中实际问题的东西,这些创新的工具提供的系列特性会彻底颠覆掉你的工作方式

紧随其后,我才给他推荐了一款开源的产品:n8n.io,它提供桌面 desktop 的客户端版本,相对来说做到了简单易用,但其实还是有一定的使用门槛,因为我本人在技术领域做过类似的 VPL(可视化编程)产品,所以知道这种工具要想让更多的受众受益其实是一件有难度的事情。

但是通过简单的教学,最终,他使用这样的工具解决了几个这样的「长尾」工作场景。重要的并不是他解决了这个问题,重要的是,这个世界又多了一个可以真正愿意改变工作方式的人。

n8n.io 编排的工作流
n8n.io 编排的工作流

事实上,在我团队的异地协同和管理中,我一直在使用小而美的工具链来解决团队工作的连接问题,包括和 IM 工具做一些打通,自动化地完成一些看似不起眼的工作,但是可能给工程师文化带来极大熏陶的东西,甚至是我个人本身的日常工作也都在这些新的工具上进行。

  1. 比如我用「自动化工作流」的工作方式,给团队定期地从有价值的 rss 源每周固定的采集技术文章给到团队成员。
  2. 同样的,我用「自动化工作流」的工作方式,完成了各种自动化提醒的琐碎的工作,甚至包括挖掘简历等。
  3. 也使用「语雀」「Notion」等下一代知识协同、云笔记软件进行各种信息的记录和整理。
  4. 我也使用「Airtable」做各种数字化小应用,甚至曾经一度帮朋友直接解决工作中的问题,尤其在疫情期间。
  5. ……

这样的工作方式,完全取决于你自己的 「想象力」,而并不取决于工具本身推荐你干什么,我推荐所有人都应该去了解「下一代生产力工具」。

尤其是目前高估值、高增长的 PLG 赛道:

PLG MARKET MAP
PLG MARKET MAP

为了方便读者去了解和使用这些工具,我建立了一个开源的 Awesome List:

awesome-ng-tools

也欢迎感兴趣的同学能够参与到该 Awesome List 的贡献。

未来?

如果你认为,你现在不去关注这些还不足以有什么影响的话,那么当「人工智能」 今天摆在我们面前的时候,我们不得不 “焦虑” 一下了:

事实上,我绝对不打算在这里贩卖焦虑,我们也不谈论「机器是否终将代替人类」这样非常高度复杂的问题。我们只是从人机协同的角度,去尝试挖掘一下,我们每天日常的、具象的工作,被「机器」代替的可能性有多大?

这就不得不展开聊一聊当今世界正在发生的一些前所未有的科技变革了,我们可以尝试俯视地看一下、局部地看一下。

违抗优于服从,有良知的违抗

没有人是靠别人告诉他怎么做而赢得诺贝尔奖的。 - 《爆裂》

2019 年年初,我从阿里巴巴内部某事业部转岗加入盒马,这个团队非常年轻、多元。混合了传统零售行业的人才和从阿里过去的产研类人才,因为这是一个全新的「新零售」赛道,盒马一直没有停止探索新的业态,各种线下门店都有在全国各地先后运营,相信很多朋友也去体验过,盒马门店里也充斥着各种「新技术」。

我很有幸能够带领一支战斗力强悍的研发队伍,这个队伍里有一些全栈的技术工程师(包括本人在内)、还有一些图形学领域的年轻人、还有一个设计和前端开发技术经验并存的多面手。更难能可贵的是,尤其是公司内部,我们和兄弟部门之间的合作,尤其和专业设计队伍的合作更像是大家在一起创业,没有这么多的束缚,公司在创新方面深度实践了「违抗优于服从」的法则,我们并没有因此受到创新上的阻碍。

我们几乎每天都很着迷地去深入研究和探讨「门店经营领域的数字化」,盒马有一个自有的科技品牌:Rex Tech(https://rextech.cn/)可以从官网浏览 Rex 在零售行业的创新。

其实对外透出的已经是成熟的商业化解决方案了,但是在成熟的路上非常艰辛和困难,其中也有很多创新是用低成本的方式试错验证以后倒在了半路上,有的现在仍然在艰难的探索中。

在此过程中,有一个大的命题激发了我们团队浓厚的兴趣,那就是在零售行业被称为「空间规划」的命题,其中又分为两个子命题:宏观空间规划、微观空间规划。

我们在最早期的时候,就已经通过团队内部的一个优秀工程师(此处 at 某人)沉淀了一套 「CAD 图纸 DXF 解析服务」并且进行门店室内地图渲染的技术,这套技术其实也依托了 Mapbox 开源的 Leaflet 地图渲染器,但就是这样的一个关键领域突破,给盒马的数字化经营带来了 无穷的想象力

HSET FY21 碰头会
HSET FY21 碰头会
  1. 可以基于室内空间的算法计算、路径规划实施精准作业调度
  2. 可以结合 AIoT 做智能设备的 Floor Plan、POI 标注,结合 AOI 计算(比如水果档口区域播放打折信息)做设备的智能调度
  3. 可以结合线下场景的实时大规模数据,提供 Spatial Analytics 的能力
  4. ……

并且在后期,我们也顺利做到了 2D 到 3D 室内地图的延展:

HSET FY21 碰头会
HSET FY21 碰头会

值得一提的是,我们在这个点上的初心和想法,纵观整个零售行业数字化,其实本身并不算特别创新,布局这个赛道的很多科技公司早就已经入场 Spatial Analytics 这个命题了,给我印象最深刻的是(https://carto.com/industries/retail/)公司出的解决方案,大家可以直观地感受到 空间数字化 的价值,尤其是室内地图的多种结合业务的生动场景。

除此之外,我们团队当时也在不断跟进 webGL 标准演进,尝试使用 webGL 结合微观空间规划命题来解决门店的个性化陈列以及业务数字化(量化坪效等)

线下门店的陈列是一个非常有意义的命题,没有人知道它的真正威力,一个陈列非常清晰、非常引入注目的门店能否带来直接营收的增长?这里面的深层原因和复杂因子之间的关系和规律,至今仍然是一个谜,即便我们已经通过技术手段可以拿到相应的数据,但是要 归因分析,找到一个增长密码,仍然非常困难。

门店陈列全景:图片素材来源于 36kr
门店陈列全景:图片素材来源于 36kr

风险优于安全

创新中的安全已经不再是优势,敢于冒险对于公司和经济的发展而言变得至关重要。- 《爆裂》

上述的微观空间规划命题是一个典型的案例,我们当初受到了多方的质疑,但是我们并不打算就此停下我们的步伐,里面的难度大到什么地步呢:

  1. 我们需要对所有的商品进行标准化采样和数字化建模,我们需要沉淀这样一个非常完备的模型库
  2. 我们需要对各个陈列方案做 3D 的还原和动态编辑,以及云端的烘焙渲染
  3. 我们需要能够结合陈列的有限空间位去结合宏观空间规划,进行点位的放置和检查等一系列业务 SOP
  4. 我们需要和业务沟通这个链路的 SOP 落地的可行性
  5. ……

其中的任何一步要想做的很好,每一步都可能耗费「年」以上的时间,而我们团队当时能够专心在这个领域的,几乎只有 2 个同学。

不过我们还是决定先干出来,即便是下了这样的决心,前前后后我们在这个命题上的探索就花了近一年的时间。

早期,我们通过摸索业界的 case 来获得一些灵感,商业世界中的业务复杂度远远超过所有人的想象。所以,在技术和产品端并没有任何可以直接能够观测到的、或者挖掘到的低成本拿过来直接使用的解决方案。

03b-WebGL-Retail-Space-Planning-with-WebGL_Aug18.pdf

MerchLogix 在 WebGL 领域的探索

当我看到 MerchLogix 的案例后非常兴奋,于是我们开始了在这个领域的有节奏的技术规划和攻坚,从内心上讲,我们认为创新是要付出一定的风险的,但是它是非常有意义的,需要一些有理想主义情结的人在一起去坚持。

最终,我们从粗糙的原型及 sop 链路验证顺利过渡到一年后的全链路产品化。而且,这个解决方案基本上解决了之前所有的难点,虽然其仍然有很大的提升空间,但是在创新上算是走出了领域内的第一步。(由于该解决方案目前还没有正式商业化,因此本文暂时不透露更多的素材和细节)

该陈列方案最终会通过消息渠道,推送到门店的陈列员手机上:

指南针优于地图,未来无法预测

只要你走的够远,就一定能到达那里。- 《爆裂》

我们在和「门店数字化」打交道的近几年中,发现商业世界的业务复杂度乃至规律都非常难以捕获,未来的形势判断,即使是最聪明的决策者,都给不出任何答案,但是在零售行业的数字化道路上,盒马已经给出了很多探索和更先进的智能,交给了这个时代一个我认为非常令人满意的样板。

  1. 调度效率极高的悬挂链系统,提升履约率
  2. 门店日常经营巡检机器人
  3. 门店 SOP 调度系统
  4. 自助 POS 机(大家最熟悉盒马的应该就是这个)
  5. 大规模数字化养殖解决方案
  6. ……

这其中某一些,我们在 2020 年的 CCFA 上向来自零售行业的所有同行们及生态伙伴、客户进行了展示,在和友商的各种交流学习活动中,我本人也收获颇多,欢迎对零售行业感兴趣的读者与我探讨。

CCFA 2020 客户在我们的展台自行操作数字化场景编排器 + 数字孪生仿真
CCFA 2020 客户在我们的展台自行操作数字化场景编排器 + 数字孪生仿真

作为技术团队本身,也在一些少有人走的路上尝试了一些新的探索,并且逐步完善和沉淀成一套成熟的技术框架和上层平台能力。(考虑到本文受众,这里不展开技术讨论)

hlang - A Flow-based programming language for universal applications
hlang - A Flow-based programming language for universal applications

没有人告诉我们终点在哪儿、走过的路径该是 AAA 或者 ABC。我们能做过的就是不断地唤醒心中的信念和理想,我们希望门店的经营管理可以从劳动力密集型向智能密集型转变,用数字化的手段解决经营的一系列问题;我们也希望通过数字化的手段,帮助消费者真正在门店内或者是线上感受到前所未有的消费体验。

以上,所有正在发生的,已经发生的,都还只是整个世界变革的冰山一角。当我试图从一个从业者的角度去解释一些现象的时候,这个角度也许会不一样,从而让大家更加直观地感受到,整个行业的变革趋势正在以从未有过的速度向我们每天的生活场景席卷而来,并且它一定是高度数字化、智能化的。

创新的成本在逐步降低,一些适应性好的企业正在使用《爆裂》提到的「指南针」法则加速这个进程。

这很好,再次提醒我们:我们要努力、积极地让自己成为创新者。

韧性优于力量

孩子,钢铁不强,血肉之躯更强。- 《野蛮人柯南》

在写这篇长文的过程中,我同时也花了几个晚上的业余时间,补充阅读了一些有趣的过去和历史,读到《爆裂》这一法则的时候,也勾起了我的一些回忆。

芦苇和橡树 的故事、蚂蚁和大象 的故事每天都在商业世界里上演。在技术的世界里,「防御」也早就不是唯一选项了,而是需要架构能够容错,能够具备容灾的能力。

容忍混乱,或者接受意料之外的事情,是我们必须要牢记在心的事实。

我的老东家,携程旅行网,就在我离开的那一年,2015 年 5 月 28 日 11 时许发生了一件非常令人痛心的意外。不夸张地说,这是一次载入中国互联网史册的重大线上瘫痪事件。

当时,整个携程旅行网突然陷入瘫痪,主站、App 均彻底无法正常使用。注意,不是简单的局部不可用,是整个瘫痪。

先来感受一组此次事故的影响数据,根据当时一季度的财报显示,携程网只要不可用一小时,就会直接损失 106.48 万 美元,而此次事故直接中断了近 10 小时,也间接导致携程当时的盘前股价暴跌 11.67%。

而造成此次事故的主要原因竟然是内部员工的不当操作。

更加巧合的是,就在前一天,5 月 27 日,支付宝出现重大故障,随后公布原因,竟然是光纤被挖断。

2015 年的这两天随后便被永远刻在了中国互联网重大事故历史册上。

今天已经是 2022 年 6 月 2 日,之前事故的技术性原因及讨论已经没有意义,真正值得尊敬的是,这两家伟大的公司至今仍然屹立在东方。

更加值得敬畏的是,2019 年,新冠病毒席卷全球,给 OTA 赛道造成重创,携程旅行网在地狱之火中涅槃重生,梁建章先生亲自下场直播带货,带领携程网在疫情中加速转型成提供旅游灵感的综合内容平台,抓住疫情复苏的机会以及境外游回流的机会,选择主动出击,而不是简单维持战力,原地等待反转。

我想,我不用再举更多的例子了,以上活生生的案例已经足以说明一家有韧性的公司是多么地顽强,毋庸置疑这是面向未来的生存法则之一,因为我们永远不知道这个变化的世界会给我们带来怎样毁灭性的打击。

或许我们应该多一份包容,致敬那些在疫情中艰难转型的企业以及企业家们,我也希望自己能够在这个命题里贡献出一份微薄的力量。

由衷地祝福两个老东家越来越好。

人工智能与机器学习

第四次工业革命已经到来,一切都在重构,一切也都值得重构。- Archer(本文作者)

讲到未来或者是第四次工业革命,我想我们不得不提到人工智能、机器学习。

2016 年 3 月,谷歌旗下 DeepMind 公司某团队研发的围棋 AI - AlphaGo 击败世界冠军 李世石,并在随后的 2016 年末到 2017 年初的时间内,连续击败中日韩数十位围棋顶级高手,战绩达到了 恐怖的 60 局无一败绩

这是人工智能在「围棋界」第一次以如此的实力展现在世人面前,当时的整个事件可以说是:举世瞩目。

很多人看到这里,会遥想到当年的「国际象棋」的超级计算机:深蓝。要知道,深蓝本质上只是暴力穷举的超级计算机,而围棋的暴力穷举即使放到现在算力极高的 CPU 内进行运算,都无法在短时间内给出答案,不借助人工智能的发展几乎 “无解”。

技术上来说,很多同学应该已经很熟悉了,AlphaGo 的核心是一个不断运行的 Loop:「阅读棋盘」->「执行当前步骤最优的策略」,采用 Monte Carlo tree search(MCTS),即大名鼎鼎的蒙特卡洛搜索树算法(启发式搜索算法)

Steps of MCTS: From wikipedia
Steps of MCTS: From wikipedia

同样考虑到读者及相关受众,本文将不会展开讨论技术细节,我可以简单通俗地总结一下:

  1. MCTS 算法极大优化了策略的搜索效率(围棋的排列组合的最终数字恐怖到令人发指,靠暴力穷举遍历几乎是完全不可能做到的,上文已经提到),AlphaGo 的判定会非常快,而且可以选择出当前的最优策略(样本基于前置数据的学习)。
  2. AlphaGo 不是万能的,它更多地就是沉淀了一套围棋的 AI,定位是一个非常聪明的下棋智能机器。
  3. AlphaGo 至今仍然在发展和迭代,有些方法和模型已经沉淀到其他各个机器学习领域。

有意思的是,当时此事一出,国内各大科技媒体、著名企业家等人士均对 AI 表达了自己的观点,一时间,人们对 AI 的关注达到了一次空前的热度。当然,其中也不乏一些极具争议的观点。

而学术界,科研界从来就没有停止研究的步伐,截至到今天为止,机器学习领域的技术成熟度应该来讲已经迈过了里程碑节点,应用范围也已经具备了一定的规模,在某些特定垂直领域,甚至出现了杀手级应用场景(比如 NLP 结合 AI 的各种场景),在商业化世界中,它也已经深入结合到企业、产业的数字化转型中。

但我们要关注到一个事实:即使技术的先进性在不断发展,人工智能领域的技术应用要解决的问题还非常多。其本身基于大量「标注数据」进行训练的「特性短板」也在一些场景中开始逐渐暴露。

针对未来的发展,我有看到,一位学术界顶级的研究者、前辈(微软亚洲研究院刘铁岩博士)也给出了一些探索方向:(读者可以仅作了解)

  1. 对偶学习、精深学习、并行学习、符号学习、自主学习、(超)人类学习
  2. 更精准的分布式计算架构,使异步计算可以不受乱序的影响。
  3. 增强学习。

相信在不久的将来,人工智能将成为一门「普惠技术」走入我们的日常工作和生活,我们也将迈入真正的 AI First 时代。

数字化转型的不可逆趋势

听到了太多次的「数字化」,这一次我们选择正面刚。- Archer(本文作者)

如果你也跟我一样,在阅读《爆裂》的过程中有那么一瞬间真正发自内心的豁然开朗,尤其在你曾经也像我一样思考「数字化」对于个体的意义,那我相信你、我会对这样的认知有着极大的触动:

不管我们的性别、年龄、种族等等,地球上所有的人都能利用数字化的力量,来发挥自己的多样化的能力。

多样性优于能力 - 《爆裂》第九大法则

历史上每一次工业革命都会带来整个社会的进步,从而对就业、市场的变革带来极大的冲击,随之引起人们的某种可以预见的焦虑。但我想表达的是:这并不是「人工智能时代」才带来的影响,这是人类在变革发展进程中一直在发生的事情。

正因为如此才会让孟晚舟女士发出这样的 “劝告” 和感慨,如果说,我们这一代还没有显著影响的话,那么可以预见,我们的下一代将直接处于变革的浪潮中,历史告诉我们,与其过分焦虑这个事情还不如多多思考,因为我们在之前以及《爆裂》中反复提到,未来的不确定性是必然的。当然,每一次变革也会带来新的制度、新的市场机会、新的就业岗位。问题是,我们准备好了没有?真正值得焦虑的是,我们中的很多人并没有真正意识到这个问题,更不用说我们还需要扛起一个重大的责任:如何正确地影响和指引下一代。

回到我们个体,对于我们而言,有意义的探讨是:我们应该如何解决未来的生存问题

回顾之前我提到的在盒马的经历,大家可以看到零售行业加速转型的冰山一角,当下,经营的效率问题和人工成本已经得到更为智能地解决,即使是原来的 信息化系统都不能 “幸免于难”,他们正在被更加智能、科学的数字化系统取代,这就是已经在发生的事情。

而事实上,大家留心观察,今天你去盒马逛线下门店的时候,少部分岗位已经被 用「机器」替代,比如柜台收银员、客服,巡检人员;甚至不排除就在未来短短几年内,导购员、面销、捡货打包等专职人员即将直接被代替。并且像类似无人 pos 机、巡检机器人等 “killer app” 应用正在向整个零售行业蔓延。

劳动力密集型产业 加速变革的背后,我们不能仅仅看到人工智能带来变革的表象,实际的本质是:产业本身就需要「数字化转型」才能得以在未来生存。

目前该命题已经被列为国家战略之一,未来 5 - 10 年,所有的本土企业,尤其中小企业,都要完成转型,否则势必面临淘汰的结局,这也就再次加快了这个命题反向推动产业变革、生产关系变革、劳动力结构性变革的速度。

这就需要每一个个体也都清晰地认识到这个趋势,为此,我个人有一些务实的建议:

  1. 了解「信息化」和「数字化」的区别
  2. 了解数字化的价值和意义
  3. 了解数字化在当下的主流手段、策略

作为企业,需要思考和重构企业生产关系,重新定义经营模型。

今天核心的商业模式本质,在我看来万变不离其宗。但是 Z 世代、下一代数字化原住民开始逐渐成为全球的中坚力量,消费者端的行为发生了巨大的变化,反向推进上游不得不做出变革,市场在激烈地反应,一个不注意便可以让上游措手不及。新消费、新制造、新零售……,回头看这些当时最开始被提出的「新」概念以及后续加入的「新」词汇,所有人几乎在我看来都是观望、迷茫状态,如今,在这个赛道里的各个巨头也好,中小企业也好,开始真正了解到:哦,原来不是在创造新的概念,是时代变了,是时代变了以后,我们所有的做生意的方式值得推翻重新做一遍。

因此,在我的意识里,我并没有从概念本身去理解这个新,这些概念一直在,「新」的起源是时代的变革。新的基础建设在飞速崛起,今天的时代已经全面进入算力时代,一项严谨的数据表明,近几年包括美国在内等国家的 GDP 增长数据却没有让能耗的数据大幅度增加,这跟算力的崛起有极大的关系。

前不久,在北京时间的凌晨,有幸随机 pick up 了一场远在美国湾区的技术圈在 twitter 线上举办的一个有声讨论,讨论的是非常有意思的 《数字游牧:Digital Nomad》 话题。

关于数字游牧,先给不了解的朋友简单描述下,这是一个很有趣的、令人一听就心生向往的「工作方式」。

我引用知乎某数字游牧伙伴 Jarod 的定义:数字游民的终极目标既不是单纯为了旅游,也不是为了发财致富,而更大程度上代表了一种对于更高生活品质和自由度的追求。

他说这个定义的上下文是认为,大家片面地把数字游牧定义为:一类采用「边旅游边工作工作方式」的人。

非常感性、充满自由气息的数字游牧,我相信每一个拥有理想主义情结的人,都向往这样的能够平衡生活和工作的奋斗方式,以让我们活的更加有质量。

数字游牧
数字游牧

我们可以关注到一个趋势,全球的疫情大背景催生了新的办公协同方式,近年来企业办公协同软件的使用量激增,因为我对钉钉非常了解,在阿里多年的职业生涯中几乎每天都和钉钉打交道,我也是最早期的一批 slack 用户、teambition 用户,国内外 PLG 赛道的办公协同软件也有过一些深度的使用,因此我可以从我个人的角度谈谈这类软件的价值。

其实这类软件存在已久,但是普及率在我看来,如果不是疫情等外界因素的刺激,尤其很多国内的传统企业,工作协同方式依旧是非常传统的。

我个人认为企业管理的基本定义可以高度抽象为:管人,管事。靠流程管事,靠制度管人

这个定义不一定过于严谨,实际上企业核心关注的经营情况仍然需要很多核心关键职能部门的协同,但是从「管理」的角度,我认为这个基本逻辑是可以解释的。

在信息化为主的时代,其主要依赖一个核心链路:信息收集 -> 信息展示,它仍然没有进入严格的数字化领域范畴,更多的是将物理世界的协同活动进行刻画,将数据从物理世界收集到信息系统中,因此在这个阶段,诞生的很多大家熟悉的信息化系统基本上围绕的都是这个思路。

流程的信息化在业界的理论和实践已经非常成熟,OMG 组织一直在致力推动的 BPM 以及后续衍生的 CMM 等,无论是工具还是方法论,已经有大量的业界产品赋予了其新的定义和实践,但是在我看来仍然没有脱离信息化的范畴。

制度的信息化,如果我们在系统中非要给他一个实体定义的话,我认为可能就是侵入到各个流程中的规则,这是相对可以结构化的部分,例子有很多,比如制度要求员工晚上 9 点后打车才能够发起报销流程等。

但是制度的感性部分就比较有意思了,很难定义,很多公司甚至还有文化领域的建设范畴,因此,如果我们没有办法去定义的时候,我们可以从「目的」的出发点去思考,企业对于制度的实施本质是希望企业的经营协同活动在一个「有序、高效」的状态下进行。

这部分如果单纯使用信息化的方式会显得捉襟见肘、力不从心。因为信息的价值或者说数据的价值没有被充分挖掘,无法在制度实施的过程中体现闭环,也没有数据驱动决策的显现。

而在数字化的思路中,是把人或者说物理世界的事物往数字化世界中进行连接,这就让很多不可能成为了可能。比如,在协同命题中,我们认为的最佳实践就是人与人之间的连接足够直接,互联网的连接效率如此之高就是因为突破了物理空间的限制,因此,我们可以看到企业协同软件以 IM 能力作为基础能力的普遍性

价值由解决问题的能力来凸显,当今流行的企业协同软件,包括钉钉、飞书、企业微信等,他们的核心都是基于数字化的理念在做,「连接」能力是他们的灵魂,但是却又各有侧重。

但毋庸置疑,他们的数字化基座能力提供的想象空间是巨大的:

  1. 打破企业协同壁垒和沟通壁垒,大幅度释放先进生产力
  2. 通过数字化的开放平台等能力,企业可以方便的获得大量 aPaaS 层的能力,通过这种能力尝试去定义新的管理理念和方式,比如,有很多公司通过这些能力开通类似 CEO 直通车,打通客户到高层的渠道,解决问题的效率提升非常明显。
  3. 工具整合,智能化驱动,场景化输出,可以大量覆盖企业的中长尾诉求。
  4. ……

在这样一个先进的生产力方式下,企业员工的工作方式就会发生很大的变化,尤其在 2B 的业务背景下,我们要做的 是和「客户」共赢。

因此,我们不仅要习惯这样的工作方式,更要 能够善用数字化的能力去做一些创造,这个建议我在此文中的其他地方也在反复提及,这是一种特别宝贵的能力:「自驱力」。

如果说以上的例子还不够性感,那么今天诸多的 KOL、在各大网络媒体平台上的自媒体博主就是另外一些生动的例子了。

这就是我认为回归到我们个体,应该关注的「转型」。

建立多样化的能力模型,在数字化世界里游刃有余,可以极大降低创新的门槛,今天只要我们有一个 idea,就可以快速地做起一个 mvp 来。它看来像是在强调个体的作用,实际上是我们与数字化的连接紧密了,成为了一个更强大的个体。

劳动力密集型的可替代性,我必须强调,并没有一种转型的目的本身就是为了替代掉「人」,而是因为这类工作方式的特性决定了它可以被「机器」替代,比如 大量的可被模式化的重复劳动

那在这里也推荐读者可以关注下 Gartner 提出的超自动化。

这里的「机器」如我本文开头所言,概念上它是广义的。

我们可以梦想一下成为「数字游牧」的一天,但当下,我们应该了解这种态度背后的时代意义和深远的价值。

特别有趣,「知识就是力量」变得越来越具像化了。

人机共生,科技向善

科幻电影的画面总是给我们太强烈的视觉冲击,可如今,当科幻电影的种种逐渐变成现实的时候,我们应该思考科技到底应该给人类带来什么?这也是我个人认为《爆裂》中「系统优于个体」法则想要探讨的问题 - Archer(本文作者)

在早前时候,我在关注和留意未来的人机交互创新领域,在知乎上也是看到了一个很有意思的讨论:

下一个革命性的人机交互方式会是什么?

BMW Intelligent Personal Assistant communications system
BMW Intelligent Personal Assistant communications system

人机共生是一个跨学科领域,由来已久,并且在学术领域仍然在大力研究,其中这个问题下有提到一些关键字 「多模态交互」、「宁静技术」 等比较有趣。

人类作为一个高等智慧生命种族,我们的结构应该来讲是非常复杂的,我们的大脑每天都在非常协调地进行着我们的工作和活动,如果我们把问题简单化一下,我们大致可以认为,我们日常的工作主要是偏接收指令执行的工作和需要大脑去决策的工作。

很显然,人类是擅长下达指令的,这是一种本能,所以在过去的我们认知的人机交互中,我们更多的是在向机器下达指令,随后机器定向地跟我们形成某种反馈。

未来的人机交互发展到人机共生阶段,人和机器交互会无限接近于人与人的交互,界限会相对模糊化,「宁静化技术」的例子就更生动一些:

你可以想象一下,你回到家以后,你突然感觉一阵燥热,想要打开空调,这个时候你身上的传感器已经自动地将你的身体状况信号发送给了一个物联网家庭网关,空调随即启动,这个时候你准备洗漱一下,来到洗漱镜面前,镜子是一个智能化的屏幕,已经在给你推送最新的资讯。

关键是,这一切行云流水,无比自然,我们和机器共生在了一个活动圈里。

当然,总是有人会担心「奇点」论,我个人反而比较认同另外一个观点:我们始终确认的一点是,我们生而为人,千万不能像机器一样活着。

我觉得我们不用过于担心,「科技向善」 已经成为全球诸多科技企业的共识,很多人可能不明白这个所谓口号的意义,我们在过去发展科技领域的这么多年,商业世界里的很多决策仍然是更多地受到个人利益、企业利益的驱动,但是在未来,我们要去思考「科技」的每一次变革对于人类社会的意义、对于生态等的影响,这是非常重大的一个思维转变。

包括现在我们一直在讲的「低碳」,核心的意义也是如此。

作为一个个体,尤其在科技行业的工作者,我们深知「科技向善」就出现在我们每一次产品细节的打磨过程中,这是我们能够为之做的一点浅薄的贡献。

这给「创新」又赋予了更高层次的含义,我觉得我们可以做的更有社会责任感,更有侠义精神。

结束语

在疫情出差北京期间,我花了点时间读完了《爆裂》全书,结合我工作中的一些经历,我的一些认知,断断续续写下了这篇杂谈,我想,评论区可以留给大家,我们可以深度展开探讨其中涉及的任何话题。

该书在读者中的评价褒贬不一,我认为可能的原因是因为群体认知的两级分化严重,太多面向未来的不可预见性容易导致人们对一些观点持有保留意见甚至是质疑。

我觉得这都不重要,重要的是,我们总是还在思考中前行。

令人唏嘘的是,2019 年,本书的核心作者,麻省理工学院媒体实验室主任伊藤穰一(Joichi Ito)因与杰弗里・爱泼斯坦(Jeffrey Epstein)充满争议的往来关系而辞职。

这位美国金融家当年七月因性交易指控被捕,随后在拘留期间自杀身亡。

我想,这又是另外一个话题了。

以上,感谢耐心的读者。

影评 -《The Ballad of Buster Scruggs》

电影简介

《The Ballad of Buster Scruggs》 《巴斯特·斯克鲁格斯的歌谣》是一部由科恩兄弟(乔尔·科恩和伊桑·科恩)执导的西部题材选集电影,于 2018 年在 Netflix 平台首映。这部电影由六个独立的故事组成,每个故事都以美国边疆生活为背景,探索了生命的荒谬与死亡的无常。影片汇集了蒂姆·布莱克·尼尔森(Tim Blake Nelson)、詹姆斯·弗兰科(James Franco)、利亚姆·尼森(Liam Neeson)等实力派演员,他们的精彩表演为这部电影增添了不少亮点。

科恩兄弟以其一贯的黑色幽默和深刻哲思,将这部电影打造成了一部既经典又独特的西部片。六个故事虽然彼此独立,却通过共同的主题——死亡与人性的复杂性——紧密相连。无论是欢快的歌声还是冷酷的枪声,电影都以一种荒诞的方式展现了西部世界的残酷与美丽。

故事与主题分析

六个独立的故事

电影的六个章节分别是:

  • “巴斯特·斯克鲁格斯的歌谣”:讲述了一位看似滑稽的歌唱牛仔巴斯特·斯克鲁格斯如何用枪法和机智应对挑战,最终却难逃命运的讽刺。
  • “近阿尔戈多内斯”:一个试图抢银行的牛仔在荒诞的因果循环中走向终结。
  • “饭票”:一位巡回艺人和一个没有四肢的演讲者之间的冷酷生存故事。
  • “淘金热”:一位孤独的淘金者在自然与人性的双重考验中寻找希望。
  • “受惊的少女”:一对兄妹在前往俄勒冈的旅途中遭遇悲剧,揭示了生命的无常。
  • “遗体”:一辆马车上的五位乘客在前往未知目的地的途中展开关于生死的大讨论。 每个故事都像一幅独立的画卷,既展示了西部片的经典元素,如枪战、荒野和孤独,又融入了科恩兄弟对人性弱点和命运无常的深刻思考。

死亡与荒谬的主题

在这部电影中,死亡就像一位不速之客,随时可能降临到每个角色头上。科恩兄弟用犀利的笔触描绘了生命的脆弱和荒谬。例如,在第一个故事中,巴斯特·斯克鲁格斯以轻松的歌声和精准的枪法征服对手,却在下一刻被一个不起眼的挑战者击败。这种戏剧性的反转让人既感到好笑又不寒而栗。

电影通过这些故事提醒观众:无论你是英雄还是小人物,命运的结局往往出人意料。六个故事的多样性也让观众在欢笑与泪水中不断反思生命的意义。

演员表现与视听体验

演员们的精彩演绎

蒂姆·布莱克·尼尔森饰演的巴斯特·斯克鲁格斯无疑是电影的亮点之一。他用夸张的表演和动听的歌声塑造了一个令人难忘的西部传奇人物。他的每一次出场都充满了活力,仿佛在向观众证明:即使面对死亡,也可以用微笑和歌声来迎接。

詹姆斯·弗兰科在“近阿尔戈多内斯”中贡献了冷幽默的表演,他那句重复的台词“第一次?”成为了电影中最经典的笑点之一。而利亚姆·尼森在“饭票”中则展现了冷酷与深沉,将一个为生存不择手段的角色演绎得入木三分。 摄影与配乐

电影的摄影由布鲁诺·德尔邦内尔(Bruno Delbonnel)操刀,每一帧画面都充满了西部荒野的苍凉美感。从广袤的沙漠到寂静的山谷,视觉效果令人叹为观止。配乐方面,卡特·伯韦尔(Carter Burwell)的音乐为影片增添了浓厚的氛围,时而轻快,时而忧郁,与故事的情感起伏完美契合。

优点与不足

优点

  • 独特的叙事结构:六个独立故事的组合既保持了新鲜感,又通过主题统一形成了整体感。
  • 黑色幽默:科恩兄弟擅长的讽刺与荒诞贯穿始终,让观众在沉重的话题中也能找到笑点。
  • 出色的视听效果:摄影、配乐和演员表演相辅相成,打造了一场视听盛宴。

不足

  • 节奏不均:部分章节(如“饭票”)节奏较慢,可能让部分观众感到沉闷。
  • 主题重复:尽管每个故事都有独特之处,但对死亡的反复探讨可能会让观众感到审美疲劳。

总结与推荐

《巴斯特·斯克鲁格斯的歌谣》是一部值得一看的电影。它不仅保留了西部片的经典元素,如枪战与荒野冒险,还融入了科恩兄弟独特的导演风格和对生命意义的深刻探讨。无论你是西部片的忠实粉丝,还是喜欢带有哲思的黑色幽默电影,这部作品都能为你带来不一样的体验。

如果你还没有观看这部电影,不妨找一个安静的夜晚,打开 Netflix,沉浸在这六个荒诞又动人的故事中。也许在巴斯特的歌声中,你会找到属于自己的答案。

本文由 Grok 3 Beta 生成并人工审核完成。

关于 AI 编排:好处、最佳实践、工具(译)

您的AI之旅,n8n都提供了满足您需求的灵活性和力量。 伟大的力量商业中的人工智能世界被广泛认可,但许多组织仍然在部署上挣扎。

这一挑战对于大型企业来说尤为真实,技术部门和业务部门之间的顺利整合可以决定企业的成功或失败。高德纳已经透露略多于50%的人工智能项目能够投入生产,这些数字在2019年至2022年的几年里是一致的。

Global AI adoption among enterprise-scale organizations. Source: https://www.multivu.com/players/English/9240059-ibm-2023-global-ai-adoption-index-report/

全球企业规模组织采用人工智能的情况。来源:https://www.multivu.com/players/中文/9240059-ibm-2023全球人工智能采纳指数报告/

这就是人工智能编排发挥作用的地方。

人工智能编排是一种实用的方法,可以帮助公司制定管理多个人工智能工具和项目的策略。想象一个世界,人工智能系统不仅自动化孤立的例行任务,而且创建完全智能的决策过程。这个愿景并不像看起来那么遥远。

在这篇文章中,我们将探讨人工智能编排的关键概念及其优势,并回顾前5名人工智能编排平台及其独特的功能和方法论。

你还会学习如何有效地使用n8n,一个开源的工作流自动化工具,用于人工智能编排。

让我们开始吧!

什么是人工智能编排?

虽然普遍接受的定义尚未确定,但帕姆·贝克(Pam Baker)在她关于人工智能编排方法提到在组织内部管理各种人工智能工具、流程、数据和人才的战略协调。

目标是创建一个相互连接、智能的决策生态系统。这种方法不仅最大化了人工智能投资的商业价值,还确保了由人工智能驱动的举措与公司的整体目标保持一致。

要理解决策智能的重要性,考虑这些现实世界的例子:

  • 公共卫生危机(来自上述来源):在COVID-19大流行期间,安东尼·福奇博士能够分享有关病毒传播的数据,但在做出有关学校安全的最终决定方面却举步维艰。真正的决策智能将弥合这一差距,提供基于复杂数据分析的清晰、可行的建议。
  • 零售库存管理:一家大型零售连锁店可以使用人工智能来预测产品需求,这基于历史销售数据、季节性趋势以及诸如天气或当地事件等外部因素。然而,决策智能更进一步,通过自动调整库存水平、启动补货订单,甚至为数百家商店提出最优定价策略。这不仅可以减少过度库存和缺货,还可以提高整体盈利能力和客户满意度。

通过将决策智能作为最终目标,AI编排有助于弥合AI能力与实际业务成果之间的差距。

人工智能编排的好处

人工智能编排的关键优势在于其能够使组织内部的决策更加智能化。决策智能是一门将信息转化为任何规模下更好行动的学科,有效地自动化了从行动到结果的全过程。

The concept of decision intelligence. Source: https://medium.com/oreillymedia/ai-orchestration-enables-decision-intelligence-2a88d8306ac9

决策智能的概念。来源:https://medium.com/oreillymedia/ai-orchestration-enables-decision-intelligence-2a88d8306ac9

这就是为什么由人工智能编排促进的决策智能如此重要:

  1. 提高决策通过将人工智能整合到日常运营中,公司可以在业务的各个层面做出更加明智、数据驱动的决策。
  2. 可扩展性:决策智能不仅在孤立的案例中,而且在整个组织中都能带来更好的行动。
  3. 业务对齐:AI编排确保AI举措直接与业务成果联系起来,而不是孤立的技术实验。
  4. 操作效率通过自动化复杂的决策过程,企业可以更高效地工作,并减少在常规决策上花费的时间和精力。
  5. 适应性随着人工智能系统不断学习和改进,自动化决策的质量可以持续提高,使公司能够更快地适应变化的条件。
  6. 风险缓解: 适当的人工智能协调包括保障措施和监督,减少了与偏见或错误的人工智能输出相关的风险。
  7. 竞争优势:通过人工智能协调成功实施决策智能的公司,可以更快、更有效地响应市场变化和客户需求。

在接下来的部分中,我们将探讨实现人工智能协调的最佳实践,以实现这些好处,并在您的组织中实现真正的决策智能。

传统的人工智能和机器学习项目的方法通常是从原始数据开始的。公司收集了大量的信息,并希望以后能够获得洞察力。

然而,这种方法通常并不会产生有形的商业价值。

根据人工智能协调方法报告中提到,一个更有效的方法则是将这个过程颠倒过来。

与其从原始数据开始,不如从清晰的业务目标开始,然后反向工作。这种方法对应于决策智能的概念,专注于识别你想要改进的具体决策和结果。

Reverse-engineering approach to ML/AI projects. Source: https://www.oreilly.com/library/view/ai-orchestration-methodologies/9781492091806/

逆向工程方法对机器学习/人工智能项目的研究。来源:https://www.oreilly.com/library/view/ai-orchestration-methodologies/9781492091806/

这种以目标为导向的方法在几个方面是有帮助的:

  1. 它确保人工智能项目从一开始就与业务目标保持一致。
  2. 它有助于确定项目所需的最相关数据以及使用这些数据的最适当方式。在某些情况下,甚至可能不需要AI模型。
  3. 它通过确保数据具有代表性和上下文性,降低了人工智能模型中偏见的风险。

其他有效协调人工智能项目的最佳实践包括:

  1. 将人工智能项目与整体商业战略对齐
  • 建立关键绩效指标(KPIs)来衡量成功并管理开发。
  • 根据项目的潜在影响和可行性来优先考虑项目。

2.选择正确的人工智能编排工具

  • 评估与现有基础设施的兼容性。
  • 评估可扩展性和灵活性以适应未来的增长和不断变化的需求。
  • 确保支持所需的数据模态,如结构化数据、图像、文本和音频,以处理多样化的人工智能应用。

3.构建一个健壮的人工智能管道或解决方案

  • 设计一个模块化且可重用的架构,以提高项目间的效率和一致性。
  • 实施严格的数据质量和预处理步骤,确保AI模型的可靠输入。
  • 建立适当的版本控制和可重复性,不仅适用于代码,还适用于数据和模型。

4.监控并优化

  • 实施实时监控系统,以跟踪性能并及时检测问题。
  • 进行定期的审计和审查,以确保持续与业务目标保持一致。
  • 解决模型漂移问题,并实施重新训练策略,以保持模型的准确性和相关性。

实施这些最佳实践需要组织内部的协调。这包括来自了解当前流程的一线员工的输入,设定战略目标的高级管理人员,以及实施人工智能解决方案的技术团队。

灯泡

请记住,人工智能编排的最终目标是在日常工作操作中实现自动化的决策智能。通过从业务目标开始,然后向后工作到数据选择,组织可以开发出能够提供真正、可衡量价值的人工智能解决方案。

为了在您的组织中实施最佳实践并有效地协调人工智能,您需要正确的工具。

尽管整个过程涉及的远不止技术,但您可以使用合适的人工智能编排软件显著优化您的努力。这些工具可以帮助管理工作流程,将人工智能模型集成到业务流程中,并确保您的人工智能计划与您的业务目标保持一致。

让我们来看一下今天可用的一些领先的人工智能编排工具!

n8n

n8n AI orchestration

n8n AI 编排

概述:

n8n 是一个强大且灵活的工作流自动化平台。与专门的机器学习平台不同,n8n 可以直接将人工智能集成到业务流程中。

n8n在人工智能集成的“购买与构建”灵活方法上脱颖而出。

它通过API端点与现成的AI工具无缝连接,使快速原型和迭代。随着项目的发展和公司人工智能能力的成熟,n8n使得轻松切换到内部AI模型成为可能,从而促进了从第三方服务到定制生产模型的平稳过渡。

灯泡

这种适应性确保了n8n能够支持一个组织从最初的实验到全面定制AI部署的AI之旅。

主要特点:

  1. 直观、低代码界面,用于设计复杂的AI驱动工作流程。
  2. 预构建的节点,用于常见的AI服务、数据库和商业工具。
  3. 用于与外部服务交互的HTTP请求,用于启动内部工具的执行命令节点,以及用于编写完全自定义代码的代码节点。
  4. 为特定人工智能任务或专有模型创建全新的节点的能力。
  5. 强大的预处理和操作数据的工具。
  6. 设计用于高效处理复杂、多步骤的工作流程;创建可重用的构建块以实现可扩展性。
  7. LLM和LangChain支持:与先进的人工智能技术以及传统的机器学习模型无缝集成。
  8. 灵活部署:提供基于云或自托管安装的选项,确保数据保护和安全。
  9. 版本控制和协作:支持团队开发和迭代的功能。
  10. 错误处理和监控:保持可靠的人工智能驱动过程。

定价:

  • 免费层自托管社区版,无功能限制
  • 云计划从每月20美元
  • 针对大规模部署提供高级支持和特性的企业定制定价层

清晰。ml

Clear.ml AI orchestration

Clear.ml AI 编排

概述:

清晰的。是一个端到端的MLOps(机器学习的DevOps)平台,用于简化整个AI生命周期。Clear.ml以其“自动魔法”方法脱颖而出,该方法需要最少的代码更改即可集成到现有工作流程中。该平台旨在支持企业级AI开发,提供诸如实验跟踪、模型管理和编排等功能。

Clear.ml 还提供了 ClearGPT,这是一个安全的企业级平台,用于大型语言模型(LLMs),它解决了安全、性能和数据治理等常见挑战。这使得它特别适合那些想要利用生成性 AI 同时保持对数据和模型控制的组织。

主要特点:

  1. 实验经理,用于自动跟踪机器学习实验。
  2. MLOps/LLMOps用于机器学习/深度学习/通用人工智能工作的编排和自动化。
  3. 数据集的版本控制数据管理。
  4. 快速部署模型端点的模型服务解决方案。
  5. 用于创建和共享广泛标记文档的报告模块。
  6. 用于管理计算集群的编排仪表板。
  7. 与常见的机器学习框架和开发环境集成。
  8. ClearGPT用于安全、企业级的LLM部署。

定价:

  • 个人和小团队免费层,包括基本功能和100GB免费工件存储。
  • 专业级别:每用户每月15美元+使用量。
  • 更高级的规模和企业版提供定制定价。Clear.ml还提供了一个自托管的开源核心版本,其部署灵活性类似于Apache NiFi。

Apache NiFi

Apache NiFi is a powerful and flexible data flow management system

Apache NiFi 是一个功能强大且灵活的数据流管理系统。

概述:

Apache NiFi是一个强大且灵活的数据流管理系统。虽然它不像Clear.ml或Comet.ml那样专门针对机器学习工作流程,但NiFi提供了一个强大的平台,用于自动化系统之间的数据流,使其适合管理复杂的人工智能管道。

NiFi 的可视化界面使得设计和修改数据流变得简单。这种创建工作流的可视化方法与 Azure 机器学习对可视化工具的关注类似。NiFi 的架构支持高吞吐量和低延迟,使其适合处理大量 AI 数据。

主要特点:

  1. 用于人工智能工作流的有向图的可视化创建和管理。
  2. 数据来源追踪,以获得完整的信息谱系。
  3. 保证交付,持久写前日志。
  4. 数据流的动态优先级划分。
  5. 通过集群实现无缝扩展。
  6. 包括多租户授权在内的广泛安全特性。
  7. 灵活的扩展架构,用于自定义AI组件。
  8. 用于NiFi实例之间高效数据传输的站点间通信协议

定价:

  • Apache NiFi 是开源的,可以免费使用。然而,当在大规模实施 NiFi 时,组织可能会产生与基础设施、支持和维护相关的成本。

彗星。ml

Comet.ml is a machine learning platform for data scientists and ML engineers

Comet.ml 是一个面向数据科学家和机器学习工程师的机器学习平台

概述:

彗星。ml是一个机器学习平台,旨在帮助数据科学家和机器学习工程师在整个机器学习生命周期中管理、可视化和优化他们的模型。像Clear.ml一样,它提供了强大的实验跟踪功能,使团队能够实时监控和比较训练运行。

Comet.ml 的平台在支持传统机器学习和大型语言模型(LLM)工作流程方面特别强大,类似于 Clear.ml 和 Azure Machine Learning。它不仅提供了跟踪模型指标和超参数的工具,还提供了管理和评估 LLM 提示的工具。

主要特点:

  1. 实时实验跟踪和可视化。
  2. 定制仪表板创建,以获得量身定制的洞察。
  3. 模型注册表用于版本控制和部署管理。
  4. LLM提示管理和评估工具。
  5. 超参数优化。
  6. 工件和数据集版本控制。
  7. 与流行的机器学习框架无缝集成,只需几行代码即可设置。
  8. 团队工作流程的协作功能。

定价:

  • 社区:个人使用免费,无项目限制,提供100GB的存储空间
  • 初学者(50美元/用户/月)适合小团队
  • 面向大型组织的企业级服务,定制定价

Azure 机器学习

Azure Machine Learning is an enterprise-grade service for the end-to-end machine learning

Azure机器学习是一个企业级的端到端机器学习服务

概述:

Azure 机器学习是一个面向端到端机器学习生命周期的企业级服务。像Clear.ml和Comet.ml一样,它为数据科学家和开发人员提供了一个全面的平台,用于构建、训练和部署机器学习模型。Azure ML支持从数据准备和模型开发到部署和管理的全谱机器学习任务。

该平台与Azure的其他服务很好地集成,为已经投资于Azure云生态系统的组织提供了独特的优势。

主要特点:

  1. 与Apache Spark集成的数据准备。
  2. 提高模型开发敏捷性的特征存储。
  3. 专为人工智能设计的基础设施,配备高性能的GPU。
  4. 自动化机器学习用于快速创建模型。
  5. 负责任的人工智能工具,用于可解释性和公平性评估。
  6. 模型目录,可以访问来自不同提供商的基础模型。
  7. 为语言模型工作流程设计和部署提供即时流程。
  8. 可扩展模型部署和监控的管理端点

定价:

  • 按使用付费:按秒计费,无需长期承诺
  • Azure计算节省计划:承诺1年或3年的固定小时费率,享受折扣价格
  • 预留的虚拟机实例:承诺使用1年或3年,为稳定工作负载获得显著折扣

如何使用n8n进行AI编排?

现在我们已经探讨了5个人工智能编排和自动化平台,让我们看看其中之一的实际应用。

我们将重点关注n8n的AI能力,因为它在数据处理、自定义代码集成以及将AI服务与现有业务流程的顺畅连接方面提供了最大的灵活性。

灯泡

通过将n8n作为AI编排的中心组件,公司可以创建一种方法,将AI能力整合到他们现有的运营中。

加载和转换数据

数据准备对于成功的人工智能实施至关重要。n8n提供了强大的工具,用于从各种来源提取、转换和加载数据。您可以自动化简单的数据加载和转换过程,以及构建具有数据集比较和分支逻辑的复杂工作流程。

数据转换工作流

运行系统程序和自定义代码

n8n的灵活性使得将自定义代码和系统程序顺利集成到您的AI工作流程中。有了执行命令代码SSH节点,您可以将专有算法或专业工具整合到AI编排过程中。n8n作为一个单一的启动点,可以启动所有其他工具,无论它们是安装在同一系统上还是在企业网络中。

最受欢迎的包含自定义代码的工作流

将人工智能服务整合到业务流程中

n8n的一个优势是它能够将AI服务与现有的业务工具和工作流程相连接。

随着新版本的发布HTTP请求工具节点,您只需一个节点就可以将LLM连接到几乎任何网站或API服务!看看这个有多个用例的深入教程。

将人工智能洞察直接连接到您的运营流程中,并提高整个组织的决策能力。这些工作流程可能对每个公司都非常具体。因此,我们为您准备了一些简单的想法以供您参考。

最受欢迎的工作流程与人工智能集成

在低代码平台上管理高级LLM逻辑

n8n + 语言链通过直观的低代码用户界面,为创建模块化人工智能应用程序提供了强大的组合。这种集成提供了直接映射到LangChain概念的广泛节点和工具:

  1. 链条和代理复杂的人工智能逻辑
  2. 向量存储用于管理嵌入式数据
  3. 来自不同提供商的语言模型
  4. 用于保留对话上下文的存储节点
  5. 数据处理的输出解析器和文本分割器
  6. 嵌入节点用于文本表示

高级逻辑管理逻辑工作流

n8n 使技术和非技术用户都能创建复杂的 AI 工作流。

编排混合AI环境

n8n 在管理结合了基于云的 AI 服务和本地自定义模型的工作流程方面表现出色。这种混合方法使公司能够在利用现有 AI 平台的优势与数据隐私需求和定制 AI 解决方案之间取得平衡。

混合人工智能环境工作流程

这些用例展示了n8n如何适应组织AI旅程的不同阶段,从使用现成服务进行初步实验到部署自定义的内部模型。

总结

在这篇文章中,我们研究了现代业务运营中的人工智能编排,以及提高人工智能部署成功率的策略。

我们已经介绍了各种专业的人工智能编排工具,以及n8n,并强调了它在将人工智能集成到业务流程中的特点。

最后,我们还展示了n8n在AI编排方面的实际应用,从数据准备到管理混合AI环境。

创建您自己的人工智能工作流程

构建复杂的人工智能自动化,速度提高10倍,无需与API抗争

现在试试n8n

接下来是什么?

以下是开始使用n8n进行AI编排的一些后续步骤:

  1. 通过查看我们的文章来扩展你的人工智能工具箱最好的人工智能编码助手.
  2. 看看我们的指南构建智能ERP解决方案与n8n你真聪明,你已经掌握了这个句子的翻译。
  3. 如果你是开发者,不要错过我们的关于人工智能代理的实用指南我需要一个翻译器。

从我们的云计划中选择今天创建一个试用的n8n账户。请记住,无论您是刚开始还是正在推进您的AI之旅,n8n都提供了满足您需求的灵活性和力量。

原文链接:AI orchestration: benefits, best practices, and tools

本文使用 🐝 A C(Collect) -> T(Transform) -> P(Publish) automation workflow for content creator. 全自动采集 - 翻译 - 发布

为开发人员准备的 Cursor 终极介绍(译)

让我们面对现实,编程有时候确实很痛苦。我们都经历过——盯着一个空白屏幕,试图记住一个特定的语法,或者调试一个让我们疯狂的错误。

当然,我们已经看到了像人工智能工具这样的工具GitHub 副驾驶ChatGPT, 和克劳德尝试让我们的生活变得更轻松。但让我们诚实一点——它们也让我们想要更多。

你知道那些时刻,当你深陷一个复杂的项目中,你希望你的编码助手能看到更大的画面吗?比如,理解你的整体架构,你的编码风格,以及你项目的特定怪癖?

这就是Cursor的用武之地。

光标人工智能代码编辑器

光标基本上是一个在类固醇上的AI驱动的代码编辑器。它是Visual Studio Code,将先进的人工智能功能带到一个熟悉的界面。

Cursor与众不同之处在于它与编码工作流程的深度集成。它不仅仅是关于建议代码或处理重复性任务。这个工具实际上理解你的项目。它了解你的编码风格,它知道你的项目结构,它甚至能掌握你团队的最佳实践。就像有一个配对编程伙伴在看你的肩膀,提供建议,捕捉错误,甚至帮助你重构代码——所有这些都是实时的。

好的,让我们深入了解使光标如此酷的关键特性,以及它们如何使您的编码体验变得更好。

1.标签

忘记你所知道的关于传统自动完成的一切——Cursor的Tab功能就像有一个通灵的编码伙伴就在你的指尖。

一旦启用,Cursor中的Tab功能就会在幕后不断工作,分析您现有的代码并预测您的下一步行动。它不仅仅限于单行补全;Cursor可以建议跨多行的编辑,同时考虑到您最近的更改和您项目的总体上下文。

下一代代码生成

你知道那些时刻,当你在编程时,你会想,“天哪,我希望它能读懂我的思想,为我编写这个函数”吗?好吧,光标的标签功能非常接近。开始输入,它会理解你的意图,并建议下一步的逻辑步骤。这就像与一个真正了解你的人工智能进行配对编程。

多行编辑

这就是事情变得非常酷的地方。光标的标签可以一次建议多个编辑。这不仅仅是节省击键次数——这是关于让你保持在状态。无论你是在添加新代码还是在重构旧代码,光标都支持你。

智能改写

我们都有那些日子,我们的手指跟不上我们的大脑。打字错误发生。语法错误悄悄进入。光标的Tab功能就像您的个人代码校对器,实时捕捉并修复您的错误。这就像有一个拼写检查器,但用于代码。

直观光标预测

是否感觉光标总是出现在错误的地方?Cursor的Tab功能包括一个智能光标预测功能。它几乎能诡异地预测出你接下来想要编辑的地方。

但Tab仅仅是开始。让我们谈谈另一个改变游戏规则的:⌘ K。

2.⌘ K

如果Tab是你的编程伙伴,那么⌘ K就是你的编程巫师。这个快捷键将人工智能辅助编程的全部力量置于你的指尖。

按需代码生成

盯着一个空白文件,不知道从哪里开始?⌘ K 已经为你准备好了。只需描述你想要创建的内容,然后看着 Cursor 为你编写代码。从样板代码到复杂的算法,⌘ K 可以帮助你启动编码过程。

轻松编辑代码

还记得上次你需要重构一大块代码的时候吗?那可能涉及很多咖啡,也许还有一些沮丧的嘟囔。有了⌘K,你只需要选择代码,按下快捷键,然后告诉它你想要改变什么。这就像拥有一个精灵,但不是愿望,而是代码编辑。

快速问题即时回答

有时候你只需要一个快速的解释。这就是⌘ K的“快速问题”功能派上用场的地方。选择任何代码,提出一个问题,就能得到即时的、上下文感知的答案。这就像有一个高级开发人员在快速拨号上,没有尴尬的闲聊。

现在,让我们看看Cursor如何将其人工智能智能带到命令行。

3.终端

Cursor的AI魔法不仅仅局限于你的代码编辑器。它甚至扩展到了你的终端。不再需要疯狂地搜索命令语法或滚动浏览手册页。

在终端中使用⌘ K,你可以直接用英语输入你想做的事情。光标会将你的请求翻译成正确的命令。这就像是拥有一个真正会说人类的命令行解释器。

例如,而不是试图记住确切的寻找命令语法,你只需键入“查找过去24小时内修改的所有文件”,让光标Cursor来完成繁重的工作。

4.聊天

虽然cmd K特性改变了你编写代码的方式,但其聊天功能则改变了你对代码的思考方式。

语境感知对话

这不仅仅是另一个侧边栏聊天窗口。光标理解您所在的文件以及光标的位置。这就像是在与一个正在查看您屏幕的开发人员聊天。

有一个你不确定的函数吗?只需问,“这里有bug吗?”你将得到一个基于你的实际代码的答案,而不是一些通用的回应。

即时代码应用

在聊天窗口看到一个你喜欢的建议吗?只需点击一下,它就会出现在你的代码中。无需复制粘贴。聊天和代码编辑器之间的这种无缝集成简化了你的工作流程,使你能够比以往更快地实施解决方案。

具有图像支持的视觉上下文

有时候,代码并不能讲述整个故事。也许你有一个用户界面原型或系统图。使用Cursor的聊天功能,你可以直接将图片拖放到对话中。这就像是在代码审查期间能够在白板上进行素描,不过是数字化的。

五.作曲家

现在,这就是事情变得真正有趣的地方。虽然Tab、Chat和⌘K非常适合编写和编辑代码,但Composer将其提升到了一个全新的水平。

应用程序生成

想象一下描述一个应用程序的想法并看着它变成现实。这就是Composer。无论你是在制作原型,构建概念验证,还是甚至创建一个现有应用程序的克隆,Composer都能在几分钟内生成一个功能性的代码库。它不仅仅是编写代码 - 它是创建完整的应用程序,包括所有必要的导入和样板代码。

提示:作曲家擅长从描述中生成代码,但有专门的工具可以将设计文件直接转换为代码。视觉副驾驶例如,转换Figma 设计转化为代码这可以特别有用,如果你有自己的设计系统,并且想要简化将设计转化为功能性代码的过程。

多文件魔术

Composer不仅仅可以处理单个文件。它可以在整个项目中协调变更。这就像是拥有一个人工智能建筑师,它不仅理解你的代码,还知道所有部件如何协同工作。一个完美的用例是当你在构建一个应用程序,并且你想要重构代码库以使用一个新的库时。

Composer提供了两种接口选项:

  1. 浮动窗口(CMD+I):一个可移动、可调整大小的窗口,让你在处理其他事情时可以方便地使用Composer。非常适合多任务处理。
  2. 全屏模式(CMD+SHIFT+I):当你需要关注大局时。你得到三个面板:

这就像是拥有一个高级开发人员,他不仅可以编写代码,还可以理解和实施整个架构。

六.背景

Cursor的上下文感知能力使其与其他AI编程工具区别开来。它不仅仅看到你正在工作的文件;它理解你的整个代码库。这种深入的理解为Cursor的许多功能提供了动力,允许更准确和相关的帮助。

@符号:您的上下文超级英雄

光标使用@符号作为在您的AI交互中引用不同类型的上下文的强大方式。无论您是使用⌘ K、聊天还是作曲家,@符号都能让您快速访问文件、代码片段、文档等。

这里是快速回顾:

  • @文件:在您的项目中引用整个文件。
  • @文件夹:引用整个文件夹。
  • @代码:引用代码的具体部分。
  • @文档:访问预索引的第三方文档或添加您自己的文档。
  • @Git:在提示(聊天)中添加git提交,差异,或拉取请求。
  • @代码库:让光标扫描您的整个代码库以获取上下文。
  • @网:让光标搜索互联网上的相关资讯。
  • @聊天@定义:在⌘ K中,包括聊天信息或附近的代码定义作为上下文。

您甚至可以粘贴带有@前缀的链接,让光标将该网络资源纳入其中。

如果你想要让光标保持焦点,你可以使用一个.cursorignore文件(工作像.gitignore) 排除特定文件或目录的索引。

7.人工智能评审

想象一下,有一个经验丰富的开发者实时审查你的代码更改,捕捉到潜在的错误,防止它们进入生产环境。这正是Cursor的AI Review功能所提供的。

它会扫描您最近的更改,提供见解并捕捉潜在问题。您可以深入到每个审查项目中,查看编辑器中的完整上下文,甚至可以与AI聊天以获取更多详细信息。

您可以自定义AI的搜索内容——想要专注于性能优化还是安全漏洞?只需告诉AI您想要寻找的内容,它就会相应地调整其审查。

你可以选择何时进行审查:

  • 检查你的未提交更改
  • 将你的工作与主分支进行比较
  • 检查你最近的提交

这就像随时进行代码审查,而不必打扰你的队友。这个功能显著提高了代码质量,甚至可以帮助你编写更好的单元测试。

八.人工智能的规则

每个开发者和团队都有自己的编码风格、最佳实践和项目特定要求。Cursor 让您可以将这些偏好直接融入到 AI 的行为中。

在“设置>常规>AI规则”下,您可以添加自定义指令,这些指令将指导Cursor的AI在聊天和⌘K等功能中进行操作。这确保了AI的建议与您首选的编码标准保持一致。

为了获得更多的控制,您可以使用.cursorrules在项目根目录下创建一个`.codeclimate.yml`文件。这允许你定义特定于项目的指令,确保AI理解每个代码库的独特要求。

这就像是训练你自己的个人人工智能助手,按照你喜欢的方式进行编码。

9.模型

不同的任务需要不同级别的人工智能计算能力。这就是为什么Cursor提供了一系列的人工智能模型供选择:

  • GPT-4o:巨大的大脑。无与伦比的智慧和理解力。
  • GPT-4:功能强大,性能和速度平衡良好。
  • 克劳德3.5 十四行诗:以细致入微的理解和创造性的输出而闻名。
  • 光标-小:光标自定义模型。不如GPT-4智能,但速度快且无限制。适合快速任务。

对于那些你需要深入研究大型代码库的时候,Cursor提供了设计用来处理长文本的模型。这些模型可以处理高达200k的标记,这意味着你可以在不丢失上下文的情况下分析大量的代码。

十.隐私和安全

Cursor非常重视数据安全。它提供了一种隐私模式,确保您的代码永远不会离开您的机器。这对于处理敏感项目或处理专有代码时非常完美。

结论

光标正在将人工智能辅助开发提升到一个全新的水平。这不仅仅是关于代码补全或生成代码的问题。这是关于拥有一个真正理解您的项目、您的编码风格以及作为开发人员您的需求的人工智能。它支持多种编程语言,可以帮助新手编码员和高级软件工程师。

随着人工智能不断发展,像Cursor这样的工具向我们展示了一个开发者与人工智能助手之间的界限越来越模糊的未来。这将导致更高效、更具创造性和更强大的软件工程。

Cursor的简单易用掩盖了它强大的功能。只需几行指令,您就可以让它生成复杂的函数,重构整个代码库,甚至创建一个功能性的应用程序。它确实是一个部分开发环境,部分人工智能助手,全部都是游戏规则改变者。

那么为什么不尝试一下Cursor呢?它可能会改变你对编程的看法。谁知道呢?你甚至可能在这个过程中找到一些乐趣。

光标常见问题解答

为了帮助你开始使用光标,这里有一些常见的问题:

Cursor 是否支持所有编程语言?

差不多。它与Python、JavaScript和Java等常见的编程语言配合得很好,但它将与大多数Visual Studio Code支持的语言一起工作。

我需要一个OpenAI API密钥来使用光标吗?

不。光标直接从盒子里出来就可以工作。不需要额外的设置。

Cursor 能同时处理简单和复杂的编程任务吗?

绝对!它可以帮助实现简单的功能,但它也可以处理更大的任务,比如重构庞大的代码库或编写整个函数。

Cursor对初学者好吗?

当然。如果你刚开始,它的人工智能助手可以帮助解释概念,建议最佳实践,并帮助你边做边学。

Cursor如何处理现有的代码?

实际上非常好。它可以理解和处理你已经编写的代码。它可以帮助你操纵代码,重构,甚至解释你的代码库中复杂的部分。

Cursor能从头开始制作一个完整的应用程序吗?

这不是魔法——它不能凭空变出一个完整的应用程序。但它可以大大加快速度。有了正确的提示,你可以从一个简单的想法迅速变成一个基本的工作应用程序。

Cursor 会干扰我现有的工作流程吗?

Cursor旨在无缝地融入您的开发过程。它建立在VS Code之上,所以如果您习惯了,您会很好的。

Cursor 可以帮助进行单元测试吗?

是的,Cursor 可以帮助编写和改进单元测试。它可以建议测试用例,帮助测试设置,甚至识别代码中可能需要更好测试覆盖的区域。

光标的价格选项是什么?

Cursor提供了一个免费的Hobby级别,功能有限,一个20美元/月的Pro级别,功能扩展,以及一个40美元/用户/月的Business级别,用于高级功能和团队管理。

引入视觉副驾驶:一键将Figma设计转换为高质量代码。

尝试视觉副驾驶

原文链接:The Ultimate Introduction to Cursor for Developers

本文使用 🐝 A C(Collect) -> T(Transform) -> P(Publish) automation workflow for content creator. 全自动采集 - 翻译 - 发布

微前端:一份指南(译)

微前端正在从根本上改变我们构建和扩展前端的方式。通过将庞大的前端分解成更小、更易于管理的部分,团队可以独立地开发、部署和更新前端组件,这增强了可扩展性和灵活性。

随着企业应用程序变得复杂,采用微前端对于保持敏捷性和加速开发周期至关重要。

在本指南中,我们将探讨在2024年成功实施微前端架构的关键要求。我们还将深入探讨如何Webpack模块联邦可以帮助满足这些需求,并在大型组织中扩展微前端架构。

本指南将引导您了解构建可组合、可扩展且性能良好的微前端架构的基础,包括在组织中跨多个应用程序的协作和更新。

在阅读之前,你可以先观看这个编码演示: 观看编码演示:

并尝试这个教程:✍️

让我们开始吧,欢迎在下面添加你的想法和建议✏️

微前端架构的关键要求

要在企业环境中成功实施微前端,必须满足几个关键要求。让我们详细探讨每个要求,并检查Bit如何满足它们。

1.解耦开发

在微前端架构中,解耦开发允许每个微前端独立地进行开发、部署和维护。每个微前端代表一个独特的功能,可以使用自己的技术栈、发布计划和流程。这种自主性减少了团队之间的依赖,从而实现了更快的创新和迭代。

在大型企业中,前端往往变得庞大而难以管理,其中的变化可能影响到整个系统,从而减缓开发速度并增加风险。解耦开发将前端分解为可管理的部分,让团队能够:

  • 更快地创新选择最优的工具和框架,加快开发速度。
  • 降低风险:隔离更改以最小化对更广泛应用程序的影响。
  • 高效扩展:独立扩展每个微前端,支持有机增长。

提供了一个企业级平台,用于在微前端中实现解耦开发。以下是Bit如何促进这一过程的:

  1. 它允许开发者将前端特性封装为单独的、独立开发和版本化的组件。这种隔离允许团队更新和发布新版本,而不影响应用程序的其他部分。例如,一个团队可以更新基于React的微前端,而另一个团队同时在Angular组件上工作,两者都独立管理。

了解更多

  • _例子:_可以在Bit环境中独立开发用户个人资料页面的React组件。它可以进行版本控制、测试和独立部署,确保对该组件的任何更新都不会干扰其他微前端。

2.定制开发环境:Bit 支持针对不同技术栈定制的开发环境。

多个环境可以在同一个“monorepo”中使用,甚至可以在仓库之间重用我必须承认,我对这个决定感到非常高兴。

团队可以为React、Angular、Vue.js或任何其他框架设置特定的环境。这些环境包括所有必要的工具,如linter、编译器和测试框架,确保每个微前端都在一致的条件下开发。了解更多:

  • _例子:_一个团队可能会使用Bit来搭建一个自定义的React环境,其中包含ESLint和Jest用于测试,而另一个团队可能会搭建一个Angular环境,并使用Karma进行单元测试。这些环境确保了在每个微前端的上下文中,开发、测试和构建过程都是标准化和简化的。

4.独立构建的胶囊:Bit的胶囊技术允许组件在与代码库的其他部分完全隔离的情况下构建。这种隔离确保了构建的执行,并且所有依赖项都得到了正确管理,防止了全局范围的问题,并使得在过程中早期更容易捕获和修复错误。胶囊为开发人员提供了一个可靠的开发和测试环境,然后再进行部署。

2.版本控制和依赖管理

版本控制和依赖管理是微前端架构的关键方面。在微前端设置中,每个前端模块或组件都是独立开发、版本化和维护的。

这种独立性意味着每个模块都可以按照自己的节奏发展,拥有其特定的依赖关系和版本控制系统。

组件级别的版本控制确保对微前端的更新不会无意中破坏应用程序的其他部分,而有效的依赖管理则可以防止冲突,并确保每个模块都能在更大的生态系统中按预期工作。

在大规模应用中,随着引入更多的微前端,管理依赖和版本变得越来越复杂。

没有一个健全的系统,依赖冲突很容易出现,导致诸如重复库、版本不匹配,甚至应用程序崩溃等问题。适当的版本控制和依赖管理确保:

  • 保持稳定:更新一个微前端不会破坏其他微前端,从而保持应用程序的整体稳定性。
  • 依赖是一致的:每个微前端都使用共享库的正确版本,避免了冲突和冗余。
  • 部署灵活性:团队可以更新或回滚特定的微前端,而不影响整个应用程序,从而实现更灵活和响应更快的部署策略。

为微前端架构中的版本控制和依赖管理提供了全面的解决方案。以下是它如何支持这些过程的:

独立版本控制

允许每个组件或微前端独立版本控制(使用semver规则)。这使得团队可以混合和匹配组件,更新或回滚特定组件而不影响其他组件,从而实现微前端的增量更改和发布。

使用Bit进行微前端的语义版本控制

此外,您可以比较组件版本之间的变化,不仅仅是代码,还有预览、文档、测试、依赖关系等等:

每个微前端的版本控制;自动依赖和更新

Bit 会跟踪和管理组件中的每个变化,提供清晰的版本和变更历史记录。标记一个组件将应用一个版本,并且增加一个版本将促使 Bit 提示您是否有其他组件也因为结果需要增加版本。学习:

  • _例子:_一个团队可能会更新登录组件以提高安全性。使用Bit,他们可以发布这个组件的新版本,而应用程序的其他部分继续使用较旧的、稳定的版本。如果出现任何问题,很容易恢复到以前的版本,而不影响系统的其他部分。

自动化依赖管理

这就是Bit真正改变游戏规则的地方。

Bit 自动定义和管理每个组件的依赖项。当创建或更新组件时,Bit 会解析并安装所有必要的依赖项,确保每个微前端都有它正常运行所需的一切。

每个组件的自动依赖管理

这个过程有助于防止依赖冲突或缺少库等问题,这些问题在具有单体仓库、微前端和共享组件的复杂应用程序中很常见。这里有一个 PNPM作者的视频演示你说得对,我确实需要多学习。

  • _例子:_假设一个微前端依赖于特定版本的库,比如lodash位将确保安装了正确的版本,并且其他使用不同版本的微前端lodash不要干预它。这种隔离可以防止冲突,并确保每个微前端保持稳定和功能。
  1. 共享依赖项:Bit 还自动化了微前端之间的共享依赖项的管理。当多个微前端使用相同的库或组件时,Bit 有助于管理这些共享依赖项,以避免重复并减少捆绑包的大小。共享依赖项在应用程序中是一致的,同时允许各个微前端独立更新我需要一个翻译器。

  • _例如:_多个微前端可以使用的设计系统或实用程序库可以作为Bit组件共享。这个共享组件是版本化的,并在中央仓库中维护,使得团队可以轻松地拉取最新版本或根据需要回退到以前的版本。

与持续集成/持续部署(CI/CD)管道集成

Bit 与 CI/CD 管道无缝集成,自动化测试、构建和部署组件的过程,因为它们被更新。这种集成确保在组件上线之前,每个版本的组件都经过了彻底的测试,从而降低了将错误或破坏性更改引入生产环境的风险。

您甚至可以自动更新您的GitHub存储库的PR:

  • _例如:_当微前端的新版本被推送到Bit时,CI/CD流水线会自动触发测试,构建组件,并为其准备部署。如果所有检查都通过,新版本会被标记并发布;如果没有,部署会被停止,防止潜在问题影响到用户。

3.运行时与构建时集成和更新

在运行时集成和构建时集成之间的决策影响了微前端架构的灵活性和可扩展性。运行时集成允许动态组合和更新,而构建时集成涉及将所有内容编译成一个单一的包,这可能限制了灵活性,但提供了高性能和良好的开发体验。

由于每个组件都是一个独立的包或模块,并且有自己的版本,因此可以更新特定的组件(以及它们影响的依赖项),而无需更新项目的其余部分。

比特支持两种类型的集成并让你选择哪一个适合你。它原生支持 Module-Federation以及每一个包管理器或注册表。

例如,这里有一些使用Module-Federation集成和更新Bit组件的演示:

以及在该设置中使用模块联邦对他们的部署进行深入研究:

同时,每个导出到比特云已经是一个包 — 随着您标记和导出组件,这个过程会自动发生。这意味着您可以使用任何包管理器或注册表(包括JFrog Artifactory)来安装它,您可以在每个组件页面的“使用”标签下找到这个面板:

独立部署

微前端应该可以独立部署,允许应用程序的不同部分以自己的节奏发展和发布,而不需要与其他组件同步。

Bit 的模块化架构允许每个组件或微前端独立部署。Bit 与 Webpack 模块联邦(Module Federation)无缝集成,使得组件可以在运行时按需加载。这将部署过程与应用程序的其余部分解耦,从而实现快速、自主的部署。

6.通过中央平台共享微前端

6.1 中央平台

Bit.cloud 是一个共享组件(例如微前端)的中心平台。它提供云托管、私有云和本地服务器版本。Bit 云是一个端到端的平台,用于组件驱动的开发,它使创建、共享、发现、消费、更改和更新组件的工作流程变得流畅和完整。这是在组织环境和规模上成功采用微前端的关键部分。它可以与您的工具和工作流程集成和连接,如 GitHub 或 Artifactory,以及 Figma 甚至您的 CI/CD 工具。

你的微前端可以托管在你的私人账户中,也可以托管在你的组织账户中,并可以分割成“范围”,这些“范围”作为具有共同主题、所有权领域的组件集合。

去检查一下:

6.2 微前端的发现机制

比特云提供了一个强大的平台,用于发现共享的微前端组件,使团队能够轻松地找到、理解和选择最适合其项目的最佳组件。它提供了高级搜索功能,允许用户根据特定标准(如标签、依赖项和版本)过滤组件。这有助于开发人员快速找到他们需要的确切组件。

除了搜索功能,Bit Cloud 还增强了组件发现功能视觉示例自动生成文档每个组件的页面都提供了详细的文档,包括使用说明、依赖关系图和视觉组合,这些组合展示了组件在不同状态和上下文中的样子。这些组合模拟了组件的行为,帮助开发者和非开发者(如设计师)理解组件的工作原理以及如何将其集成到不同的项目中。

6.3 用于状态、使用和采用的分析

在使用共享微前端或其他类型的组件进行扩展开发时,最重要的方面之一是能够跟踪它们的使用情况、采用情况、技术栈、状态以及有助于管理和控制这些构建块的每一个其他重要参数。

在Bit.Cloud上,您可以查看每个组件(或Scope)的直接和间接依赖项和依赖者,不同项目中的使用情况,版本更新,使用的技术等。

7.合作开发

有效的微前端架构需要团队之间的协作,也需要跨功能团队内部的协作。构建者需要能够无缝地协同工作,即使他们在开发应用程序的不同部分,同时一起规划、审查和发布。

比特旨在促进合作。

例如,可以通过“变更请求”来建议组件的新版本,审查团队成员可以看到代码变更、视觉预览、测试变更、依赖关系等等。他们可以直接在变更请求屏幕中讨论和批准这些变更。

此外,组件可以轻松导入到您的本地工作区进行编辑和更改,这使得在组件上进行协作、建议更改、交付更新和保持同步变得非常容易。

这种协作环境加快了开发速度,并确保所有团队都保持一致,技术标准化,用户体验/用户界面保持一致。

8.自主团队

团队拥有并交付领域驱动的微前端

在不同的微前端上工作的团队应该自主运作,完全控制他们各自的领域。这种自主性对于促进创新和减少开发过程中的瓶颈至关重要。

Bit的平台通过提供工具来支持自治团队,使他们能够独立地开发、测试和部署他们的组件。每个团队都可以管理自己的工作流程,从开发到部署,无需与其他团队协调。这种自治性通过Bit对独立版本控制、部署和运行时集成的支持得到了增强,使团队能够快速行动并自由创新。

9.多种技术栈

微前端在不同团队偏好使用不同技术栈的组织中尤其强大。这种灵活性允许每个团队为他们正在开发的特定功能选择最佳工具和框架。例如,一个团队可能使用反应对于用户仪表板,另一个可能会选择角的处理复杂表格,而另一个团队可以利用Vue.js对于轻量级、快速加载的组件,如导航栏。

许多组件,许多技术,一个平台

Bit在管理这些不同的堆栈中扮演着至关重要的角色组件环境位环境是配置和管理特定于每个框架或技术栈的开发工具和工作流程的扩展。在位管理的工作区中,团队可以为反应角的Vue.js或者他们正在使用的任何其他技术栈。这些环境确保每个组件都在一个一致的环境中被隔离和开发,无论底层技术是什么。

例如,Bit中的React组件环境可能包括配置ESLint,和Webpack,而Angular环境可以设置TypeScript因果报应,和Angular 命令行接口这使得每个团队可以专注于开发,而不必担心跨栈冲突,因为Bit处理集成并确保每个组件在组合成更大的应用程序时是兼容的。

此外,Bit环境支持胶囊创建了组件的隔离开发环境。这意味着每个组件都可以在与代码库其余部分完全隔离的情况下进行开发、测试和构建,确保像依赖冲突或全局状态泄漏这样的问题能够尽早被发现。

十.应用外壳

一个应用外壳是一个静态框架,它首先加载并提供 Web 应用程序的核心结构,包括共享的 UI 元素,如标题和导航栏。在微前端架构中,应用外壳确保了一致的用户界面,同时动态加载微前端。

应用程序外壳充当一个稳定的基础,管理路由和状态,而微前端则独立加载。这种分离允许每个微前端独立开发和部署,而不会影响整个应用程序。

  • 独立开发:Bit 允许创建应用程序外壳和微前端作为独立的组件,使它们可以独立开发、版本控制和部署。
  • 动态整合:通过使用Webpack的模块联邦(Module Federation),Bit允许微前端动态加载到应用外壳中,确保在运行时的平滑集成。
  • 集中管理:Bit Cloud将应用外壳和微前端的存储和管理集中化,使得在组织中发现、更新和共享组件变得容易。

本质上,Bit 支持应用外壳和微前端的无缝集成和独立管理,确保复杂 Web 应用程序中的一致性和灵活性。这里有一个例子:

结论

微前端是一种强大的架构方法,用于管理现代企业应用程序的复杂性。通过满足解耦开发、独立版本控制、增量更新、独立部署、运行时集成、共享组件、协作开发和自治团队等关键要求,组织可以实现高度模块化、可扩展的前端架构。Bit和Webpack模块联邦提供了一套强大的工具来支持这些目标,使团队能够更快地创新,并在快速发展的数字环境中提供卓越的用户体验。

原文链接:Micro Frontend Architecture: A Guide

本文使用 🐝 A C(Collect) -> T(Transform) -> P(Publish) automation workflow for content creator. 全自动采集 - 翻译 - 发布

多代理系统和单一代理的对比(译)

过去几年,强大的大型语言模型的出现,尤其是ChatGPT,使人工智能成为全球瞩目的焦点。虽然这些模型在理解和生成类似人类的文本方面非常出色,但它们只代表了人工智能革命的开始。

通过增强LLMs,您可以创建能够在一个环境中运行的人工智能实体或“代理”。这些代理可以理解他们周围发生的事情,决定做什么,然后采取行动以实现他们的目标。

然而,具有前瞻性思维的商业领袖知道,人工智能的真正变革力量不在于个别的万能代理,而在于将高度专业化的代理组合成协调的多代理系统,以应对复杂的工作流程和挑战。

投资多代理系统将对贵公司的效率和可扩展性产生深远影响,因此让我们来分析一下为什么它们是贵公司必不可少的补充。

多智能体系统和单一智能体有什么区别?

单一代理系统:这些人工智能系统依赖于一个代理来执行多样化的任务和责任。想象一下,它是一个多才多艺的通才,训练有素,能够处理多种职责。

多智能体系统多智能体系统(MAS)由多个相互作用的智能主体组成——这些主体是能够感知、学习环境模型、做出决策并据此行动的自主实体。想象一下一个运转良好的机器,每个专业的智能体都将其独特的专业知识带到桌面上。他们和谐地一起工作,协调他们的努力,更有效地实现共同目标。这就像是拥有一个专家团队,每个成员都在发挥自己的长处。

现在,让我们深入探讨为什么多智能体系统在人工智能领域是重量级选手。

效率和吞吐量的提高

通过将任务分配给多个专业代理,多代理系统可以大幅提升效率和生产力。每个代理都可以专注于他们的专业领域,最小化瓶颈并最大化产出。结果呢?复杂任务更快、更高效地完成。

灵活性和扩展性

多智能体系统最大的优势之一是它们的适应性和可扩展性。您可以轻松地移除或修改智能体,并且随着业务需求的演变或工作负载的增加,您可以无缝地将新智能体集成到系统中,增强其能力,而无需从头开始重新训练模型,或处理因人员短缺而导致的昂贵替换和破坏性停机时间。

专业专长

在当今复杂的商业环境中,应对多样化的挑战通常需要广泛的专业知识和专长。这就是多代理系统的优势所在,它允许您将具有深厚领域专长的代理整合在一起,例如市场营销、财务、供应链管理或客户服务等领域。通过结合专业代理的优势,您可以比单一的通用代理更有效地解决多方面的问题。

弹性和容错能力

让我们面对现实——系统可能会失败,当这种情况发生在单一代理上时,整个操作就会戛然而止。有了多代理系统,你就拥有了内置的冗余和容错能力。如果一个代理出现问题或需要维护,其他代理可以保持事物的顺利运行,最大限度地减少对整体系统性能的影响。

可解释性

多智能体系统的模块化特性使它们更加透明且易于管理。这就像在团队项目中遇到问题时,你可以根据角色明确知道该与谁交谈以寻求解决方案。如果需要改进,你可以轻松识别出负责的“团队成员”并帮助他们进行调整。这种可解释性水平不仅对系统维护和优化非常实用,而且对于建立对AI系统的信任至关重要,特别是在那些必须理解决策过程的领域。

自动化工作流程

在多代理架构中,各个代理可以被专门构建和编程来处理更大、更复杂工作流程中的特定步骤。它们以同步的方式进行通信和协调行动,这意味着传统上需要人工监督的重复过程可以全天候自动运行。

至关重要的是,自动化的多代理工作流程提供了一致性、可重复性和降低错误率的水平,这是人类团队根本无法比拟的。每个代理都严格遵循其程序指令,消除了困扰手动流程的可变性和潜在错误。

但这里才是真正令人兴奋的地方。

集体智慧的复合效应

当你将多个专业代理聚集在一起时,你不仅仅是在增加他们各自的能力——你正在解锁一个远远超过其各部分总和的集体智能水平。

想象一下,你被指派负责为一个新产品的推出制定一个全面的市场策略。一个单一的代理可能难以有效地整合市场研究、品牌建设、内容创作和数据分析等方面。但是,通过一个多代理系统,你可以无缝地协调专门从事这些领域的代理,从而实现一个真正全面和平衡的方法。

随着您将更多代理整合到您的多代理系统中,创新和解决问题的可能性呈指数级增长。每个新代理都带来了全新的视角、专业知识和定制能力,创造了一个协作和集体智能的良性循环。

如何开始使用多智能体系统

多智能体系统作为我们在Relevance AI构建的核心部分。我们认为它们能够解决单个智能体无法解决的复杂问题。所以我们构建了它,我们称之为AI智能体团队。

顺便说一句,Relevance AI的旗舰BDR代理实际上是一个多代理系统。这个代理是一个复杂的系统,由许多子代理组成,每个子代理在BDR过程中都扮演着特定的角色。

您可以联系我们安排演示对于这里的BDR代理在这种情况下,我们需要考虑多种因素,包括时间、金钱和资源。

总之

在单一代理和多代理系统之间的选择可能是仅仅跟上潮流与真正走在潮流之前的区别。随着人工智能的采用继续加速,很明显,多代理系统将在推动各行业的创新、效率和解决问题方面发挥关键作用。

原文链接:The power of multi agent systems vs single agents

本文使用 🐝 A C(Collect) -> T(Transform) -> P(Publish) automation workflow for content creator. 全自动采集 - 翻译 - 发布

Make your own Dark Mode with mbm.js

[mbm] = (🌞 -> 🌛 || 🌛 -> 🌞)

Lyrical as usual, Apple said about Dark Mode that it’s a “dramatic new look that helps you focus on your work,’’ as well as a “distraction-free environment that’s easy on the eyes — in every way”.

Google was much more pragmatic, saying Dark Theme can “reduce power usage by a significant amount”, “improve visibility for users with low vision and those who are sensitive to bright light” and “make it easier for anyone to use a device in a low-light environment”.

👦 Archer: Dark Mode IS NOT Dark Design or Dark UI in Javascript.

What is mbm ?

It’s short name for CSS Mix Blend Mode.

About

🌞 🌛 Play with your Dark Mode.

Quick Start

const mbm = MixBlendMode({
  backgroundColor: "#FFFFFF",
  enable: true,
});

// toggle
mbm.toggle();

// show
mbm.show();

// hide
mbm.hide();

// get auto-increment zIndex
mbm.izIndex();

UI Controller

Default UI Controller

MixBlendMode.UIController().render()

You can write your own ui controller

const yourController = MixBlendMode.UIController((MixBlendMode) => {
  // controller logic
});

Examples

npm run dev

Reference

KOA 源码阅读系列(一) - 理解 KOA 中间件的执行

源码阅读系列的开篇就不多废话了,开门见山。

首先看添加中间件的入口 app.use 的源码

/**
 * Use the given middleware `fn`.
 *
 * @param {GeneratorFunction} fn
 * @return {Application} self
 * @api public
 */

app.use = function (fn) {
  if (!this.experimental) {
    //es7 async functions are not allowed,
    //so we have to make sure that `fn` is a generator function
    assert(
      fn && "GeneratorFunction" == fn.constructor.name,
      "app.use () requires a generator function"
    );
  }
  debug("use % s", fn._name || fn.name || "-");

  // 主要就是做这个事情
  // 根据上面的 assert,这里的 fn 均为 generator function
  this.middleware.push(fn);
  return this;
};

接着,来看一下 Server 是怎么起来的

/**
 * Shorthand for:
 *
 *    http.createServer (app.callback ()).listen (...)
 *
 * @param {Mixed} ...
 * @return {Server}
 * @api public
 */

app.listen = function () {
  debug("listen");

  // 非常熟悉的 createServer
  // 调用的是 app.callback
  var server = http.createServer(this.callback());
  return server.listen.apply(server, arguments);
};

很显然,app.callback 里应该就有我们想要的中间件执行的部分

/**
 * Return a request handler callback
 * for node's native http server.
 *
 * @return {Function}
 * @api public
 */

app.callback = function () {
  if (this.experimental) {
    console.error(
      "Experimental ES7 Async Function support is deprecated. Please look into Koa v2 as the middleware signature has changed."
    );
  }

  var fn = this.experimental
    ? compose_es7(this.middleware)
    : co.wrap(compose(this.middleware)); // 把中间件串起来
  var self = this;

  if (!this.listeners("error").length) this.on("error", this.onerror);

  return function (req, res) {
    res.statusCode = 404;
    var ctx = self.createContext(req, res);
    onFinished(res, ctx.onerror);
    fn.call(ctx)
      .then(function () {
        respond.call(ctx);
      })
      .catch(ctx.onerror);
  };
};

这里的 compose 是关键,我们来到 koa-compose 的源码,仅仅就这 38 行代码,就构成了 KOA 中间件执行的核心部分,这里我们暂且不讨论 co.wrap

/**
 * Expose compositor.
 */

module.exports = compose;

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */

function compose(middleware) {
  return function* (next) {
    if (!next) next = noop();

    var i = middleware.length;

    while (i--) {
      next = middleware[i].call(this, next);
    }

    return yield* next;
  };
}

/**
 * Noop.
 *
 * @api private
 */

function* noop() {}

这么快就看到这行了?我觉得上面的代码信息量很大,道友们再细细品味一下,确定品味完了,下面的开胃 DEMO 可以帮助你更好的理解这个执行过程

#!/usr/bin/env node

// 中间件 a
function* a(next) {
  yield 1;

  // 执行下一个中间件
  yield* next;

  yield " 继续执行 A 中间件 ";
}

// 中间件 b
function* b(next) {
  yield 2;
  yield 3;
}

var next = function* () {};
var i = [a, b].length;

// 通过 next 首尾相连
while (i--) {
  next = [a, b][i].call(null, next);
}

// 包裹第一个 middleware
function* start(ne) {
  return yield* ne;
}

// 输出
console.log(start(next).next());
console.log(start(next).next());
console.log(start(next).next());
console.log(start(next).next());

输出结果:

➜  a-lab ./a
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: ' 继续执行 A 中间件 ', done: false }

等等,我们的 co 去哪儿了?大家有没有发现上面 Demo 中和我们平时用 KOA 写中间件的不同之处,我们来看一下 KOA 的官方示例代码:

var koa = require ('koa');
var app = koa ();

//x-response-time

app.use (function *(next){
  var start = new Date;
  yield next;
  var ms = new Date - start;
  this.set ('X-Response-Time', ms + 'ms');
});

//logger

app.use (function *(next){
  var start = new Date;
  yield next;
  var ms = new Date - start;
  console.log ('% s % s - % s', this.method, this.url, ms);
});

//response

app.use (function *(){
  this.body = 'Hello World';
});

app.listen (3000);

有没有发现?如果还没有发现我就公布答案啦:

根据上文,我们已经知道,那个负责把所有中间件串起来的 next 其实本身也是一个 generator,但是,如果在 Generater 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的,这个时候我们必须使用 yield* next

但是,我们写代码的时候明明写的是 yield next 啊,这就是 co 的巧妙之处了:

co 帮我们 “自动管理” generator 的 next,并根据调用返回的 value 做出不同的响应,这个响应是通过 toPromise 方法进行的,我们可以在 toPromise 中发现:

  • 如果遇到另外一个 generator,co 会继续调用自己,这就是为什么我们不需要写 yield* next 的原因,而只要写 yield next
yield next;

可以看一下这个 toPromise

/**
 * Convert a `yield`ed value into a promise.
 *
 * @param {Mixed} obj
 * @return {Promise}
 * @api private
 */

function toPromise(obj) {
  if (!obj) return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ("function" == typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}

所以,co 对 yield 后面跟的类型是严格约定的,如果我们在项目中直接使用了 DEMO 中的

yield 1

co 就会给我们一个错误

You may only yield a function, promise, generator, array, or object.

所以,我们一般在项目中如果想要 “同步” 写法,都会用 Promise 封装一下,比如这样:

let getAuthInfo = function (req) {
  return new Promise((resolve, reject) => {
    rp(req)
      .then((authres) => {
        if (authres.status === 0 && authres.code === 0) {
          resolve(authres);
        } else {
          resolve(null);
        }
      })
      .catch((err) => {
        reject(err);
      });
  });
};

然后,我们就可以这样使用了:

let authinfo = yield getAuthInfo (authReq);

执行完所有中间件之后,KOA 才会进入到 respond

fn.call(ctx)
  .then(function () {
    respond.call(ctx);
  })
  .catch(ctx.onerror);

respond

/**
 * Response helper.
 */

function respond() {
  //allow bypassing koa
  if (false === this.respond) return;

  var res = this.res;
  if (res.headersSent || !this.writable) return;

  var body = this.body;
  var code = this.status;

  //ignore body
  if (statuses.empty[code]) {
    //strip headers
    this.body = null;
    return res.end();
  }

  if ("HEAD" == this.method) {
    if (isJSON(body)) this.length = Buffer.byteLength(JSON.stringify(body));
    return res.end();
  }

  //status body
  if (null == body) {
    this.type = "text";
    body = this.message || String(code);
    this.length = Buffer.byteLength(body);
    return res.end(body);
  }

  //responses
  if (Buffer.isBuffer(body)) return res.end(body);
  if ("string" == typeof body) return res.end(body);
  if (body instanceof Stream) return body.pipe(res);

  //body: json
  body = JSON.stringify(body);
  this.length = Buffer.byteLength(body);
  res.end(body);
}

所以,我们说使用 KOA 写中间件的时候,我们可以任意修改我们的 response body,然后再返回给客户端

总结

  • KOA 通过 next 将中间件 “串” 了起来,形成了链表,然后通过最外层的 generator function 触发整个执行过程

  • 在 compose 的时候,拿取中间件的顺序是 FILO 的,但是拿出来之后做的操作是循环 i– 赋值 next,因此,依然可以保证正确的顺序执行

  • KOA 框架通过 co 框架包装 generator 来达到 “同步” 写法

  • KOA 在执行完所有中间件之后才会真正执行 respond,在此之前,我们可以对 response body 做任何你想做的修改,这点与 Express 有本质上的区别

FEDAY 2016 之旅

前戏

2016/3/21 补上参会的完整记录,这个问题从一开始我就是准备 “自问自答” 的,希望可以通过这种形式把大会的干货分享给更多人。

出发 / 到达

我跟同事周周是周六凌晨 1 点才到的广州,住的地方在小区里面,路过楼下的时候看到一家还在营业的啤酒吧,很有 Feel,但是此时的精神状态直接把我们送到了房间里,洗完澡之后就碎觉了,准备次日集中精神好好听讲。

初到会场

次日,我们穿个马路就来到了本次 feday 大会的现场。然后是标准的签到,拿 “大礼包 “,参会证等流程,经常参加大会的同学应该很熟悉了,由于我之前参加过 d2,觉得阿里报告厅的屏幕已经很震撼了,没想到,第一次在电影院参加技术大会真的有种赶老罗发布会的感觉

此次的嘉宾阵容:

好了,进入正题,以下内容既是记录,又是自己的看法和总结,然后形式均为我认为的精华内容整理,完整的内容我觉得没必要赘述,因为大会官网会放出完整的视频。

Stepan From Facebook - 用 Node.js + React.js 打造通用应用

来参加 feday 前看到本次大会的主题,当我看到同构话题的时候比较兴奋,因为之前负责的我厂一个全站消息中心改造项目,我和搭档有实践同构并为之落地,而且该项目已经上线,所以还是比较清楚同构的作用以及使用场景,而且在厂内也做了相关分享,所以想看看大会上能不能碰撞出一点不一样的火花。

Stepan 的演讲精华我觉得可以精简整理为如下几点:

原来用 RoR (其实这里泛指后端) 做的事情现在都可以用 Javascript 做,好处是可以前后端复用代码,符合同构的基本条件,然后他通过一个目录结构对比演示指出了同构应用中需要解决的三个事情:渲染 / 路由 / 数据(我是这么理解的,因为我认为这确实是同构落地的关键)

对于渲染,他先列举了一个非常简单的例子,我认为他要表达的意思是:渲染的本质其实就是模版 + 数据,例如:

function template(data) {
  return "<body>${data}</body>";
}

这个函数可以在 server 端直接将数据扔给 res.send,也可以用在 client 端用来生成真实的 dom;但我们的应用往往是复杂的,React (Facebook 的工程师肯定是要来安利 React 的) 的 renderToString 方法可以帮助我们完成 Server-Side Rendering,因为 React 的 vdom 不需要依赖浏览器侧的环境,这是 React 支持服务端渲染的唯一一个方法,好多同学已经知道了,讲到这里,作为一名 Facebook 工程师,他成功地为本次大会率先安利了一把 React

对于路由,他基本上直接安利了 React-Router,然后贴出了跟 React-Router 官方文档几乎一样的代码,所以,折腾过的同学基本可以略过这个环节,但其实很多同学应该知道,路由共享是同构的重要部分,其实这里的坑还是蛮多的,其中还包括 React-Router 自身的 bug,我在项目里的做法是将这块逻辑以中间件的形式进行处理。

对于数据,不管你有没有用 flux,都要解决初始化数据的问题,因为两端共用一份 render 逻辑,在后端直出的时候,需要将后端得到的数据同步给前端,否则,前端二次 render,会得到不正确的渲染输出,这个相信玩过 React 后端直出的应该也知道,解决方案几乎都是一致的,说到底就是通过:

window.__STORE__ = {};

将数据带给前端。你会发现其他封装好的第三方同构库 ISO 等最终用的都是这个逻辑。

关于组件拉取数据,他安利了 isomorphic-fetch,这样前后端可以共享一份拉取数据的逻辑,对于组件数据在 server 端的初始化,他的处理方式是,server.js:

async function fetchAllData (props) {
  return Promise.all (
    props
      .components
      .filter (x => x.fetchData) // 探测组件是否有 fetchData 方法
      .(x => x.fetchData (props))
  );
}

这里的 props 可以传入 React-Router 中 match 方法返回的上下文,由于我们的业务只直出了部分组件数据,所以这里的做法有所不同,我的做法是将 ISO 逻辑置入中间件,当中间件匹配到路由后,将利用 yield next 转交给下一个中间件先拉取数据,然后将数据放入 locals 中,等到执行到 ISO 中间件时,中间件将 locals 中的数据拿出,初始化给 React-Router 匹配到的组件上下文,最后 renderToString React + Node.js 打造通用应用的折腾过程中其实只要解决这关键的三点,差不多就可以打造出一个同构应用了,但是他还没有提到的还有:

  • 因为前后端共用一份代码,如果 client.js 中包含 require (‘fastclick’) 之类的只在浏览器才会依赖的组件引入代码时,我们需要做好环境判断,当然,这非常简单,但是不得不考虑

  • 如果前端项目中的 jsx 用的是 es6 modules,但是 server 端用的是 require,则需要考虑统一

  • 同构项目的工程化问题

  • ……

最后,我在星巴克逛 Stepan 博客的时候发现他的博客就是同构的,很有趣,大家可以体验一下:

Stepan’s Blog

江剑峰 - 微信开发过程中的最佳实践

剑锋幽默风趣的讲演风格显然非常接地气,这个话题从一开始就吸引住了全场的开发者,因为有太多开发者曾经被微信坑过,这个分享我直奔干货总结了:

  • JS-SDK 签名过程中提交的 URL 参数中不得带 “#” 及后面部分的内容,会导致签名报错

  • 异步获取签名的时候,要设置正确的 Content-Type

  • 清缓存黑科技://triggerWebViewCacheCleanup

  • flex 部分支持

  • 微信真的没有动过你的 localStorage/Cookie,可能原因是进程被杀等

等等,快后退,我要装逼了:

到 3 月份底,微信 x5 将全面升级为 blink 内核,并全网灰度发放完毕,也就说,我们即将可以大胆写 flex 了,并再也不担心缓存问题了,动画卡顿问题也会得到改善,大家赶紧验证去吧。

黄士旗 - React Tips

士旗也是来自 Facebook 的工程师,讲的也是 React,总结下来就是:士旗在教大家如何正确使用 React:

  • 容器组件的存在是为了让它可以专注于数据处理,然后让渲染组件专心负责渲染,只需要管扔进来的是什么数据然后渲染就可以了,这样处理后,我们会发现 component 的代码将变得非常复杂,当我们要管理的 state 太多之后,所以就有了 flux store,但是 flux 的实现中有不必要的实现,对于应用来说,一个 action,一个 state 就可以返回一个新的 state,这完全就是 pure function 就可以搞定的事情,于是有了 redux store

  • 将组件拆分,用更好的 pure function 来返回你需要渲染的这些组件,这样可以利用 decorator/HOC 来达到组件复用,还可以减少组件中大量的 _XXX 私有方法,让应用程序变得更加可控,debug 变得更容易,其实这块还是能够产生很多共鸣的,相信各厂都在实践一些营销页面快速产出的技术方案,React 应该是比较合适的技术选型,可以利用 decorator 达到组件的高度复用

  • 善用 FP,RxJS。士旗在这里安利了一把 learnRX 项目(GitHub - ReactiveX/learnrx: A series of interactive exercises for learning Microsoft’s Reactive Extensions Library for Javascript.),FP 跟 RxJS 本质上是两个东西,只是 RxJS 中有用到 FP 的思想,编程思维的转变我认为是需要训练和下功夫的,因为习惯思维非常可怕,我有看过 RxJS,这种 “一切皆 Stream” 的咒语一开始令人非常困惑,但豁然开朗后简直仿佛像是看到另外一个世界,这方面,士旗主要强调,我们要善用 Array 的 map/reduce/filter,FP 可以让代码变的简洁,FP 的 “语义化 “方法名可以帮助提升代码可读性。

陈子舜 - 下一代 web 技术可以运用的点

子舜的话题中讲到了很多务实的,腾讯正在实践的一些技术:

  • 包括离线化,包括对前端性能的不断优化 之前在阿里 d2 听过腾讯工程师分享过 Node.js 加速 Qzone 的一些细节,离线化这块有领略过腾讯对于追求产品极致用户体验的那种态度,我厂也正在慢慢实践,并且落地了一些初步工作,我们意识到无线端的离线化意义重大。

  • 然后他讲了 ServiceWorker,http2,这里可以到时候看大会放出的视频

  • 子舜这里还提到了运营商劫持的问题,然后安利了 HTTPDNS

中间有一次圆桌,HAX 主持,主要是一些撕逼,没有啥实质内容,而且我对于有同学问出:[你们怎么看待 RN 的出现扰乱了原生开发和 web 开发之间的那种和谐]这种问题感到纳闷。

winter - 如何成为更好的前端

第一次见到 winter 本尊,我佩服和尊敬这样的前辈,但是我会保持风度和拒绝浮躁,winter 的分享虽然不是技术内容分享,但他分享了他在学习前端过程中的一些他认为的好方法,我觉得现场好多前端工程师应该是可以跟他产生共鸣的:

  • 比如,我们都干过 console.dir (window) 这种事情吧,然后看到陌生的 api,赶紧去学习一下,给自己充充电

  • 追求真理的态度,建立自己的知识体系,用权威推翻自己认为的甚至是社区认为的那些权威,比如他提到闭包,通过 Google 学术找到出处论文(追本溯源),然后推翻自己之前的那些认知

  • 他认为要成为专业的前端工程师,20% 靠的是知识,另外 80% 靠的是编码能力,工程能力,架构能力,后者可能需要的就是工作经验,然后不断练习,然后 winter 感慨,他自己成长最快的那几年都是在学校里,到了工作之后就很少有这样的机会快速成长,即使工作多年,但是发现自己的进步缓慢

Holger Bartel - http/2 时代的 web 性能

因为之前读过几篇关于 http/2 的博文,对 http/2 还是有所了解的,这个话题我没有听完,后来有事情就先走了,听了前面 3/4 场,这部分大家可以阅读相关博客弥补,我可以安利几篇:

结束篇

说个题外话,QCON 貌似也临近了,据我了解,今年 qcon 对前端话题的范围基本也是限制在下一代 web 技术,再回过头来看本次的 FEDAY,我觉得从嘉宾到议题还是符合时代气息的。希望下次越办越好,很开心的是在回来的前一天晚上,在楼下的那个啤酒吧里遇到了 stepan,holger,士旗,裕波等人,跟 stepan 和 holger 面对面交流了相关主题,真可谓不虚此行,满足之余,在知乎上,博客上同步以上所有内容给大家,谢谢。

从编程范式及实时 Web 应用的另类角度看向 ChatGPT 的 AI 大脑

(图片来自 Look What ChatGPT Did to My Online Dating Profile - CNET)

前言

ChatGPT 是什么、它能做什么、甚至如果你想深入了解它的技术底层原理,那么,我相信此时此刻的你,在互联网的任何一个角落找到一篇 Paper 来快速了解它已经是一件非常容易的事情了,因为毫不夸张地说,它正在改变整个世界。

当然,最酷的方式一定是,你可以直接通过 ChatGPT 来了解 ChatGPT。今天铺天盖地的关于它的文章背后,体现的主流核心思想最多的关键字我认为是: 崇拜 。无数的人和事情都在跟进它、把玩它、研究它。直到此刻,它的热度依旧处于全网顶格状态,甚至在最近的一些来自硅谷的报道中,我隐约感觉到,硅谷好像只剩下 AI 了,几大巨头甚至不惜直接宣布解散元宇宙、Web 3.0 等命题组也要全身心押宝它。

国内的各大流量媒体更是不吝啬各种营销术语,对其各种浮夸表述,导致它的能力在坊间越传越离谱,几乎吹嘘它为无所不能。

如此神话、盲目崇拜之风的氛围下,作为一名十多年工作经验的程序员,同时又身处数字化转型的产业互联网赛道,我觉得当下很有必要理性、冷静地对它进行一番打量。

因此,在深度使用了一段时间并开发了一个集成 OpenAI API 的 Dingtalk 机器人服务后,我也终于自觉有底气来讲一讲自己对它的一些看法,另外一个补充背景是,笔者所在的产业目前对这块引起了高度的重视,本文也作为一篇偏向工程实践角度的调研报告来呈现,以上是我写下这篇文章的动力来源。

image.png

目前,它拥有如下 Features

  • 集成了 GPT-3 模型 API(已更新为 ChatGPT 同款 3.5 Turbo 模型),支持它的文本生成特性
  • 集成了 DALL.E 2 模型,使用 img-> 固定 prompt 唤醒词,支持它的图片生成特性

但是目前该机器人有个最大的问题就是没有接入会话式感知能力的 API ,下文我会提及原因。

如果说,AI 能力是 ChatGPT 的大脑,那么今天配套的 ChatGPT 的产品 chat.openai.com 就是它的身体,即它与人类直接交互的部分,它的重要性不言而喻。

因此,本文将从这个角度切入,随后再看向它的大脑。

理解 ChatGPT 目前某些限制的原因

由于本人的技术工作长期专注在应用架构领域,属于软件工程的实践,学术研究工作涉及较少,因此对 AI 部分的技术内容,我会在吸收和理解引用文章的基础上,根据我自己的理解进行阐述和说明,如有错误和不严谨,欢迎指出。与此同时,我认为换一个技术视角去看待同一个问题丝毫不影响我写下本章节的初心和目的:那就是,尝试透过现象看本质。

众所周知,充当 ChatGPT 大脑的其实是一个 语言模型 ,如果用 响应式编程范式 来类比理解它目前的工作方式,大概是这样的:

const ChatGPT = ($input) => {
  $input.on("data", (inputTokens) => {
    // 根据输入生成 tokens 发送到 $output
    // $output 和 $input 相连,生生不息
    $output.send(GPT.generate(inputTokens));
  });
};

这里用如下示意图给大家展示,如我上述的 伪代码 所示,如果我们把这里的 输入输出 看成是 ChatGTP 的首和尾,那么这就是一个典型的首尾相连设计。正因为这样的设计,ChatGPT 会在每一轮计算生成之前,将之前的 输出 合并 输入 后再次给到 输入,如此反复,以达到 生生不息 的状态。每一次调用执行,一个新的上下文环境就自动生成,并和之前保留的上下文之间天然形成连接。熟悉编程语言的同学应该发现,该特性像极了主流编程语言中的 闭包 特性。

image.png

这个特性是 ChatGPT 在会话情境中给人类的第一印象达到震撼程度的核心原因,也是最为大家津津乐道的:

它好像真的在和人类 对话 ,而不是生硬的 你问我答 模式。

没有银弹

布鲁克斯的《No Silver Bullet — Essence and Accidents of Software Engineering》告诉我们,软件工程没有银弹,任何设计有优点就一定有缺点。

很显然,这里有几个应用架构侧的隐患在设计之初就已被埋下了,以此,我们可以从应用侧窥探到,即使背后的 AI 大脑再强大,到了应用侧依然会受到技术设计本身的限制,虽然我们有诸多曲线救国的办法,终究仍会受制于这个短板效应:

  1. 由于需要保持上下文环境的捕获特性,输出要不断继续给到输入,这就意味着,如果从系统架构上,不保留一个类似 tokens buffer 缓冲区的设计,整个 ChatGPT 可能会遭遇不可预期的风险(大量的算力消耗甚至会直接拖垮系统,导致崩溃),这也是为什么 OpenAI Open API 中关于 Model 能力调用的参数说明中明确指出了 4097 tokens 的长度限制: What are tokens and how to count them? / OpenAI Help Center
  2. 应用侧需要使用会话 id 对每一次完整的会话进行隔离,因为即便是人类的对话,也不可能永远都处于一个全局唯一的上下文环境里:比如你今天和朋友聊的话题是吃饭、明天和家人聊的话题是电影,如果几个话题同时并行,很容易将会话带入到一个混乱不堪的状态,即俗称的:不在一个频道,跨服聊天。
  3. ChatGPT 在实际运行场景中,始终在某个单会话中处于一个 生生不息 的状态,什么时候需要停止会话,即告知模型停止生成任务这个事情,需要给到用户主动权。

上述问题体现到 ChatGPT 应用侧的人机交互上,但凡使用过的同学应该很熟悉了,其中 2、3 完全就是通过纯产品维度的设计来解决的,比如你使用它的时候必须先新建一个会话,然后页面的底部会在生成任务进行到一定时间后,给到你一个停止按钮,让你可以随时停止这次生成。

image.png

截至此时此刻,你依旧可以找到很多讨论 ChatGPT token 限制的文章,里面的部分核心原因我们从软件工程的角度就可以分析出来,针对这个核心限制,目前 OpenAI 已经开放了 Embedding 的能力,这又是另外一个话题了,感兴趣的同学可以自行 Google。

再谈实时 Web 应用

我认为,这意味着,今天像 ChatGPT 这样强悍的 AI,如果要在应用侧设计一个足够好用的 AIGC 产品不是一件容易的事情。

它的诸多特性决定了我们在其配套的应用侧的设计上不可能像传统的应用一样。

我甚至斗胆预言,其会再一次推动 real-time web applications 即实时 Web 应用的技术演进,尤其 响应式编程范式 。如今,前端技术社区里关于 MeteorJS 特性的讨论仍旧不绝于耳。回头来看,其当年提出的 Full Stack Reactivity 的理念是何等的先进。

滚滚历史车轮一直是在争议中前行的,直至当下,在某些主流前端技术社区里,仍然可以看到这样的讨论:究竟什么样的场景要使用 RxJS

我想 ,现在属于它以及 Reactive 思想 家族的应用开发时代已经来了。

巨人的肩膀,站上去容易,站稳不容易

现在,让我们回到上面我提到的 Dingtalk 机器人遗留的那个 不支持 的问题。

由于如下所列的限制,导致该场景无法精确地集成 OpenAI 会话式感知能力 API,最终只能支持简单的 一问一答 ,体验效果大打折扣。

  1. 即使在 OpenAI 提供会话式特性 API 的情况下,Dingtalk 客户端只提供 Outgoing 能力的机器人是完全不够的,如果要在群场景中支持会话式 API,需要在服务端识别出一个唯一的会话标识,但是显然在 Dingtalk 的群会话等场景中再次定位出一个个会话是很难的,因为无法判定这个唯一标识,比如,群里的某个用户 AT 这个机器人的时候并不一定是某个会话的开始。
  2. 如上面提到的,在 Dingtalk 中如果集成一个 ChatGPT,想要告诉它停止生成任务,就无法再使用默认的群会话这种场景了,因为其完全不支持自定义界面的能力。

针对这些问题,实际上对于 Dingtalk 客户端或者说 Dingtalk 整个平台来说几乎是一个全新的命题和挑战:如何和 ChatGPT 这一类 AIGC 服务更好地集成才能给到用户最佳的体验,可想而知,其对应的改造成本是不低的。

由此可见,要想使用好 ChatGPT 这类 AIGC 服务,Dingtalk 等这类人机交互端产品自身的适应能力建设都需要有一个深入的思考和长期的规划才行,这方面我个人认为,微软是业界绝对的表率。

微软目前几乎全产品线正在与 OpenAI 深度集成,图为 Power 家族产品 Virtual Agents 的 AI-based boosted conversations

值得一提的是,针对这个小场景,我在 Glitch 平台上集成开发 OpenAI 的 Open API 只用了不到 1 小时的时间,站在巨人的肩膀上是容易的,站上去之后的事儿才重要。

有人会说,那我们是不是可以直接开发一个类似的 Web 界面,然后嵌入到 Dingtalk 来解决这个问题?这个解法是可行的,但是仍旧不理想。在企业某些业务场景的数字化活动中,很大可能性需要和群会话能力等联动才能达到一个最佳效果。

我想到的相对比较理想的一个方案是使用某种自动化工作流编排引擎 + Dingtalk 的开放 API,这个方案通过极大增强灵活性来满足各种场景的诉求。这样就可以在特定场景下动态创建一个场景群,使用场景 id 作为唯一的会话 id 来解决上述的核心限制。

事实上,业界目前某些顶尖自动化工作流厂商的产品已经内置了 OpenAI 的连接能力,正在赋能 生产力提升 的领域大放光彩。

image.png

重新审视它的 AI 能力会发现,其实它在玩文字接龙

从应用侧的视角阐述 ChatGPT 的工作方式是比较直观的,可以帮助我们理解 AIGC 类服务和集成它们的产品之间是怎样一种联系。

从上文的观点中,我们已经知道,想既做到让用户时刻感知到 AI 的强大,又保持相对不错的用户体验,还能通过某些精妙的产品端设计来规避掉技术侧的限制,是需要下苦功的。

而 ChatGPT 达到今天的成就,在 AI 能力的塑造上更是下足了苦工:距离初版 GPT-3 的发布都已经过去 2 年了,但事实上 2018 年 OpenAI 就已经开始推出这个自然语言处理模型(NLP) ,其拥有 1750 亿之多的模型参数,训练成本花费约 1200 万美元,是同类中参数数量最多、最昂贵的模型之一。

Pasted image 20230212034337.png

我们对其能力的解剖,尚有一部分必须要留在 AI 的范畴进行阐述,那就是这个 ChatGPT 大脑的核心,它的生成能力:

GPT.generate(inputTokens);

因为这个能力几乎决定了它的边界,也就是我们通过理解它能力的原理,从而判断出它能干什么,不能干什么。

如上文提及的一样,ChatGPT 和它的祖宗 GPT-3 以及它自己,本质都是一个 语言模型 ,而且是大模型。

我们先来讲讲大模型(Foundation Model),在此之前,有些前置概念的理解很重要:

  1. 人工智能、机器学习、深度学习,从左至右是包含的集合关系;为了帮助大家通俗易懂的理解,我在这里举例说明机器学习和深度学习的本质区别:比如机器学习,让它学习如何从一个图片中识别出一个猫,就要喂给它一堆的数据,并且由人类标注出来,清楚地告诉它这个是猫;深度学习,让它学习如何识别出一个猫,它就像自己会研究这个命题本身一样,它直接思考什么是猫,什么样的特征是猫,从细节到局部再到全局逐层特征提取和识别,最后达成目的,过程中只需要不停喂数据给它即可。
  2. 模型、算法、程序,这三个 AI 领域的高频词汇究竟什么区别?我们仍然来拿猫的识别来举例,我们从数学的角度来对这个问题进行建模和定义的话,我们很容易想到一个抽象的函数:’Cat = F(毛发,腿的长度,花纹,眼睛,鼻子,头和颈的距离,…),括号中的这些被称为模型的参数,很显然参数越多,参数与参数之间的关系连接的越紧密,最后识别的就越精准;而算法就是对这个数学模型以计算机的角度去理解和还原;程序只是执行这个算法的工具之一,因为算法也可以由人执行,虽然在大规模计算面前,可行性为 0,但是逻辑上需要这样理解。

至此,我们已经通俗地理解了模型是什么,接下来我们尝试从程序的角度来理解下大模型(Foundation Model),大模型就是尝试找到一个更加通用的函数,也叫基石模型或者基础模型

以往针对特定领域 A 的模型往往只能用于 A 领域,想把它迁移到 B 领域来解决 B 领域的问题,往往不理想,我们用伪代码来表示是这样的:

// 只能处理 A 领域的问题
const ModelA = function <A>(a, b, c, d, e, ...others) {};

// 只能处理 B 领域的问题
const ModelB = function <B>(x, y, z, ...others) {};

从函数的形式上,我们就可以看出不够理想的原因:

  1. 两个函数分别只能处理特定类型 A 或 B 中的元素;
  2. 两个模型所需要的参数数量、含义等都不一致。

抽象代数赋予了程序语言类型系统强大的泛型能力,我们将 A、B 类型泛化为一个 T 类型,然后将原本属于 ModelA、ModelB 的参数全部合并,得到如下函数(模型):

// 针对特定类型 T (领域)的通用模型
const GenericModel = function <T>(a, b, c, d, e, x, y, z, ...others) {};

这就是大模型,看来也没啥高深的,不过就是一个更加通用的函数而已。

现在它已经可以很好地处理 A、B 两个特定领域的问题了,这里要注意,我们虽然一直拿函数来理解,但是模型和函数在实操层面不是一回事。

这样做的好处显而易见,通过合并两个函数为一个通用的函数,相当于我们现在只需要专注搞定这一个 AI 模型就可以了,参数越多越好,哪怕多达万亿,模型却始终只有这一个,不再有特定领域的模型了。

但这也带来了一个新问题:AI 模型肯定是以精准地解决一个问题为最终目的的,合并以后,大模型中的处理运算可能不够精准,因为刚才的 ModelA、ModelB 函数(模型)的处理没了,这问题要是从程序的角度去解决它的话,就容易多了:

const GenericModel = function <T>(a, b, c, d, e, x, y, z, ...others) {
  // 说不定通用模型中还给我们注入了一些工具方法
  // 省略...

  return {
    // 特定领域转移到了这里
    // 和大模型共享

    A: function ModelA() {
      // 只使用大模型中的部分参数
      doSomething(a, b, c);
    },
    B: function ModelB() {
      // 只使用大模型中的部分参数
      doSomething(d, e, f);
    },
    C: function ModelC() {
      // 只使用大模型中的部分参数
      doSomething(x, y, z);
    },
  };
};

这在大模型里叫 兼容下游任务 ,我们通过这两大步骤的处理,最终得到的这个函数,看起来已经接近有效了:

  1. 既能够提前先跑出来一个超大规模的 AI 模型(这就是 预训练 ),精准度上最起码可以基本涵盖 A, B 两个领域,当然,如果能够本身就做到精准就再完美不过了。
  2. 不过没关系,精准度如果不够,下游任务自己来处理,大模型这个大函数本身还支持扩展出更多可能的下游任务。

而今天我们的主角,ChatGPT,就是建立在 大模型 + 深度学习 的基础上得来的产物。

所以,根据上面我的解释,相信大家已经能够很清楚地看出来,ChatGPT 的基本能力很大程度依赖于这个大模型的能力,这个大模型就是 GPT-3.5 ,也听闻  OpenAI 已经在训练一个更强悍的大模型 GPT-4 了,这两个基础模型都是 NLP 领域的大模型,说穿了就是比较适合用来处理自然语言

现在让我们把 GPT 模型代入到上面的伪代码:

const GPTModel = function <T>(a, b, c, d, e, x, y, z, ...others) {
  // 自然语言处理的大模型,起名为 GPT
  // 省略...

  return {
    翻译: function ModelA() {
      doSomething(a, b, c);
    },
    提取摘要: function ModelB() {
      doSomething(d, e, f);
    },
    文案生成: function ModelC() {
      doSomething(x, y, z);
    },
    代码生成: function ModelC() {
      doSomething(aa, bb, cc);
    },
  };
};

这就是整个 GPT 大模型的全貌,至此我们终于通过程序角度的理解,酣畅淋漓地把一些模糊的概念给清晰化了。

既然如此,我们就重点来看下 GPT 3.5 乃至 GPT 4.0 这个大模型究竟有多强悍,首先它是一个吞金兽,刚才就解释了 AI 大模型的投入成本,相信有了上面的抽象,理解这个点就更加容易了;此外,OpenAI 投喂了多少数据给它呢?据多方报道的统计数据参考,大概近似整个互联网 10% 的语料内容已经喂给了它,至于来源其实不用去细究,有的数据显然不一定方便公开出来。

从这个角度上,说它是一个永不知疲倦且不断在自我进步的知识工作之神都是不过分的,这里有个重点一定要理解及注意,它并不是联网帮你搜寻 wiki 的内容,它在前面说明的训练过程中才会拿互联网上的信息,目的是找到上文所说的函数,一旦找到了,它就不再需要了。但是收集知识和学习知识完全是两码事,我们回到 NLP 自然语言处理,自然语言处理主要用于感知和识别语言上下文,通俗的理解就是去猜测语言意图:

比如,针对 老王想要 xxx 这个问题 。因为 NLP 只能从已有的当前上下文去判断,这个时候它肯定不知道老王要干什么,于是我们继续补充上下文,说:老王在肚子饿的时候,想要 xxx,NLP 立刻知道一个关键上下文,那就是他饿了,于是它根据模型和训练数据,发现后面紧随其后出现 吃东西 这三字的可能性最大。

你每天在使用的输入法联想功能也就是这样的。

所以,重点来了,它不关心 老王饿了吃东西 本身是不是对的,肚子饿了要吃东西是它通过 饿了 这个关键上下文去推导出来的,它本身并没有意识判定出这是一个生活常识,生活常识的对错它更没法知道。

这也就意味着,它只是一个精通语言的模型,它并不擅长逻辑的严谨性验证,也就是它会“说”这个技能,但是它不知道对错,对错的部分,目前是通过强化学习的部分来勉强补充的,这部分考虑到篇幅限制和本文受众对象,不再详细展开描述。

这就是为什么有很多用户会在体验  ChatGPT 后评价它说:一本正经地胡说八道,原因正是在这里。

它就是一个函数,它在玩文字接龙。在本节的最后,我想引用李宏毅老师课程的一张总结性示意图来完整地呈现 ChatGPT 达成的三大步骤:

uTools_1678671018101.png

至此,我们完整识别出来了它的核心能力,以及它的能力边界

正确地使用它比神话它更重要

AlphaGo 已经打败地球上所有的围棋手了,还有多少人在当下能想起它?

屠龙刀也要有龙可以屠,你的场景是龙吗?

仅仅是 AIGC 这个人工智能子领域,在 ChatGPT 大火之前就已经有很多其他 AI 厂商在提供了:

uTools_1676180541485.png

很多深度实践的知识工作者,包括我本人在内,早就已经在使用类似的工具来尝试提升某些工作的生产力了。

从这个角度,恕我直言,有两种行为都不可取

  1. 如果你平时都没有意识通过工具等来提升生产力,更不用说要用 AI 提升生产力了,也就更无从谈起你想用 ChatGPT 来做惊天动地的事情了。
  2. 滥用屠龙刀解决一切问题。

对于个人如此,对于企业,更是如此。至于有些媒体完全无脑鼓吹的言论,大家更应该理性地看待它们。

但是,当下 ChatGPT 的火热,让大家再次意识到生产力变革的影响是如此的巨大,这个本身是具有教育意义的。

毫无疑问,属于 ChatGPT 的时代才刚刚开始,更多有价值的研究和商业场景落地案例,都值得我们继续保持关注。

Sketchfab Share - Low poly 80’-90’s European Car - Store pack

I was looking for a car to model, and I choose to model an old European car. I was inspired by the Peugeot 205 GTI. It’s an old french car produced from 1982 to 1998 that you can still see in France, Belgium and some others European countries. Made with Blender and Substance Painter.

From Sketchfab.

v-circle - 漂亮的 Vue.js 圆形进度组件集合

v-circle

A collection of circle progress with Vue.js.

  • Vue 1.0+ use v-circle 0.1+
  • Vue 2.0+ use v-circle 0.2+

Demos & Examples

Live Demo: xiaoa.name/v-circle

To build the examples locally, run:

npm install
npm run dev

Then open http://localhost:8080/examples/home.html in a browser.

Installation

The easiest way to use v-circle is to install it from NPM and include it in your own Vue build process (using Webpack, etc)

npm install v-circle

Build

build to dist

npm run build

You can also use the standalone build by including dist/v-circle.js in your page. If you use this, make sure you have already included Vue, and it is available as a global variable.

Usage

.vue file usage

<template>
<circle-css color="#3498db" width=120 font-size=48 pv=12 bold=8 text-bg-color='#f0f0f0'></circle-css>
</template>

<script>
import CssCircle from 'v-circle/components/css-circle.vue'

export default {
  components: {
    circleCss: CssCircle
  }
}
</script>

Circles

  • CssCircles
  • SvgCircles
  • CanvasCircles

API

CssCircles

prop type description example default value
color String circle progress fill color #000000 #2ecc71
width Number circle size 180 150
fontSize Number circle progress value size 64 64
pv Number circle progress value 75 0
textColor String circle progress value color #bdc3c7 #bdc3c7
bold String circle progress outline width 10 5
textBgColor String circle progress value background-color #000000 #f9f9f9
borderColor String circle progress outline color #000000 #bdc3c7
during Number circle progress animation dur-time 2 0.8
bgColor String circle progress background-color #000000 #f0f0f0

KOA 源码阅读系列(一) - 理解 KOA 中间件的执行

源码阅读系列的开篇就不多废话了,开门见山。

首先看添加中间件的入口 app.use 的源码

/**
 * Use the given middleware `fn`.
 *
 * @param {GeneratorFunction} fn
 * @return {Application} self
 * @api public
 */

app.use = function (fn) {
  if (!this.experimental) {
    //es7 async functions are not allowed,
    //so we have to make sure that `fn` is a generator function
    assert(
      fn && "GeneratorFunction" == fn.constructor.name,
      "app.use () requires a generator function"
    );
  }
  debug("use % s", fn._name || fn.name || "-");

  // 主要就是做这个事情
  // 根据上面的 assert,这里的 fn 均为 generator function
  this.middleware.push(fn);
  return this;
};

接着,来看一下 Server 是怎么起来的

/**
 * Shorthand for:
 *
 *    http.createServer (app.callback ()).listen (...)
 *
 * @param {Mixed} ...
 * @return {Server}
 * @api public
 */

app.listen = function () {
  debug("listen");

  // 非常熟悉的 createServer
  // 调用的是 app.callback
  var server = http.createServer(this.callback());
  return server.listen.apply(server, arguments);
};

很显然,app.callback 里应该就有我们想要的中间件执行的部分

/**
 * Return a request handler callback
 * for node's native http server.
 *
 * @return {Function}
 * @api public
 */

app.callback = function () {
  if (this.experimental) {
    console.error(
      "Experimental ES7 Async Function support is deprecated. Please look into Koa v2 as the middleware signature has changed."
    );
  }

  var fn = this.experimental
    ? compose_es7(this.middleware)
    : co.wrap(compose(this.middleware)); // 把中间件串起来
  var self = this;

  if (!this.listeners("error").length) this.on("error", this.onerror);

  return function (req, res) {
    res.statusCode = 404;
    var ctx = self.createContext(req, res);
    onFinished(res, ctx.onerror);
    fn.call(ctx)
      .then(function () {
        respond.call(ctx);
      })
      .catch(ctx.onerror);
  };
};

这里的 compose 是关键,我们来到 koa-compose 的源码,仅仅就这 38 行代码,就构成了 KOA 中间件执行的核心部分,这里我们暂且不讨论 co.wrap

/**
 * Expose compositor.
 */

module.exports = compose;

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */

function compose(middleware) {
  return function* (next) {
    if (!next) next = noop();

    var i = middleware.length;

    while (i--) {
      next = middleware[i].call(this, next);
    }

    return yield* next;
  };
}

/**
 * Noop.
 *
 * @api private
 */

function* noop() {}

这么快就看到这行了?我觉得上面的代码信息量很大,道友们再细细品味一下,确定品味完了,下面的开胃 DEMO 可以帮助你更好的理解这个执行过程

#!/usr/bin/env node

// 中间件 a
function* a(next) {
  yield 1;

  // 执行下一个中间件
  yield* next;

  yield " 继续执行 A 中间件 ";
}

// 中间件 b
function* b(next) {
  yield 2;
  yield 3;
}

var next = function* () {};
var i = [a, b].length;

// 通过 next 首尾相连
while (i--) {
  next = [a, b][i].call(null, next);
}

// 包裹第一个 middleware
function* start(ne) {
  return yield* ne;
}

// 输出
console.log(start(next).next());
console.log(start(next).next());
console.log(start(next).next());
console.log(start(next).next());

输出结果:

➜  a-lab ./a
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: ' 继续执行 A 中间件 ', done: false }

等等,我们的 co 去哪儿了?大家有没有发现上面 Demo 中和我们平时用 KOA 写中间件的不同之处,我们来看一下 KOA 的官方示例代码:

var koa = require ('koa');
var app = koa ();

//x-response-time

app.use (function *(next){
  var start = new Date;
  yield next;
  var ms = new Date - start;
  this.set ('X-Response-Time', ms + 'ms');
});

//logger

app.use (function *(next){
  var start = new Date;
  yield next;
  var ms = new Date - start;
  console.log ('% s % s - % s', this.method, this.url, ms);
});

//response

app.use (function *(){
  this.body = 'Hello World';
});

app.listen (3000);

有没有发现?如果还没有发现我就公布答案啦:

根据上文,我们已经知道,那个负责把所有中间件串起来的 next 其实本身也是一个 generator,但是,如果在 Generater 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的,这个时候我们必须使用 yield* next

但是,我们写代码的时候明明写的是 yield next 啊,这就是 co 的巧妙之处了:

co 帮我们 “自动管理” generator 的 next,并根据调用返回的 value 做出不同的响应,这个响应是通过 toPromise 方法进行的,我们可以在 toPromise 中发现:

  • 如果遇到另外一个 generator,co 会继续调用自己,这就是为什么我们不需要写 yield* next 的原因,而只要写 yield next
yield next;

可以看一下这个 toPromise

/**
 * Convert a `yield`ed value into a promise.
 *
 * @param {Mixed} obj
 * @return {Promise}
 * @api private
 */

function toPromise(obj) {
  if (!obj) return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ("function" == typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}

所以,co 对 yield 后面跟的类型是严格约定的,如果我们在项目中直接使用了 DEMO 中的

yield 1

co 就会给我们一个错误

You may only yield a function, promise, generator, array, or object.

所以,我们一般在项目中如果想要 “同步” 写法,都会用 Promise 封装一下,比如这样:

let getAuthInfo = function (req) {
  return new Promise((resolve, reject) => {
    rp(req)
      .then((authres) => {
        if (authres.status === 0 && authres.code === 0) {
          resolve(authres);
        } else {
          resolve(null);
        }
      })
      .catch((err) => {
        reject(err);
      });
  });
};

然后,我们就可以这样使用了:

let authinfo = yield getAuthInfo (authReq);

执行完所有中间件之后,KOA 才会进入到 respond

fn.call(ctx)
  .then(function () {
    respond.call(ctx);
  })
  .catch(ctx.onerror);

respond

/**
 * Response helper.
 */

function respond() {
  //allow bypassing koa
  if (false === this.respond) return;

  var res = this.res;
  if (res.headersSent || !this.writable) return;

  var body = this.body;
  var code = this.status;

  //ignore body
  if (statuses.empty[code]) {
    //strip headers
    this.body = null;
    return res.end();
  }

  if ("HEAD" == this.method) {
    if (isJSON(body)) this.length = Buffer.byteLength(JSON.stringify(body));
    return res.end();
  }

  //status body
  if (null == body) {
    this.type = "text";
    body = this.message || String(code);
    this.length = Buffer.byteLength(body);
    return res.end(body);
  }

  //responses
  if (Buffer.isBuffer(body)) return res.end(body);
  if ("string" == typeof body) return res.end(body);
  if (body instanceof Stream) return body.pipe(res);

  //body: json
  body = JSON.stringify(body);
  this.length = Buffer.byteLength(body);
  res.end(body);
}

所以,我们说使用 KOA 写中间件的时候,我们可以任意修改我们的 response body,然后再返回给客户端

总结

  • KOA 通过 next 将中间件 “串” 了起来,形成了链表,然后通过最外层的 generator function 触发整个执行过程

  • 在 compose 的时候,拿取中间件的顺序是 FILO 的,但是拿出来之后做的操作是循环 i– 赋值 next,因此,依然可以保证正确的顺序执行

  • KOA 框架通过 co 框架包装 generator 来达到 “同步” 写法

  • KOA 在执行完所有中间件之后才会真正执行 respond,在此之前,我们可以对 response body 做任何你想做的修改,这点与 Express 有本质上的区别

❌
❌