C

Colorful

Colorful - 一枚数字艺术家的自留地

《精读源码》- GoT

源码分析图 GoT Thoughts 统一结构后,Operation 实例的私有执行方法实际上就变成了前置 T 的转换函数,这个巧妙的设计是 GoT 框架的灵魂所在,比如可以用这个 T 生成新的 T’、也可以原地更新 T、或者其他操作,故名曰:思维变换。 GoT 在图数据结构上的算法简单高效(默认 Controller 执行策略为 BFS,最终实现在叶子节点上获得所有 Thoughts 的结果整合) Operation 核心是私有执行方法即 T 的变换函数,通过前置/后置指针最终连接在一起,因此支持分形 根节点是没有前置节点的节点;叶子节点是没有后置节点的节点 因为采用的是有向图的结构表达,因此天然支持降级为 IO / CoT / ToT Github

2024/8/7
articleCard.readMore

Javascript Sandbox

今天在整理以前的一些 Research,逐步搬运到博客上来,这篇是关于 Javascript Sandbox(Browser) Something works Iframe + sandbox attribute webworker + Prevent usage of dangerous objects + SCP Further Strict Sandbox (truely sandbox env) - Google Caja Lib flexible JS sandbox. Jailed is a small JavaScript library for running untrusted code in a sandbox. The library is written in vanilla-js and has no dependencies. Jailed — flexible JS sandbox https://github.com/asvd/jailed Caja is a tool for making third party HTML, CSS and JavaScript safe to embed in your website. It enables rich interaction between the embedding page and the embedded applications. Caja uses an object-capability security model to allow for a wide range of flexible security policies, so that your website can effectively control what embedded third party code can do with user data. Caja supports most HTML and CSS and the recently standardized “strict mode” JavaScript version of JavaScript – even on older browsers that do not support strict mode. It allows third party code to use new JavaScript features on older browsers that do not support them. https://github.com/google/caja Reference is-it-possible-to-sandbox-javascript-running-in-the-browser

2024/8/4
articleCard.readMore

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

前言 ChatGPT 是什么、它能做什么、甚至如果你想深入了解它的技术底层原理,那么,我相信此时此刻的你,在互联网的任何一个角落找到一篇 Paper 来快速了解它已经是一件非常容易的事情了,因为毫不夸张地说,它正在改变整个世界。 当然,最酷的方式一定是,你可以直接通过 ChatGPT 来了解 ChatGPT。今天铺天盖地的关于它的文章背后,体现的主流核心思想最多的关键字我认为是: 崇拜 。无数的人和事情都在跟进它、把玩它、研究它。直到此刻,它的热度依旧处于全网顶格状态,甚至在最近的一些来自硅谷的报道中,我隐约感觉到,硅谷好像只剩下 AI 了,几大巨头甚至不惜直接宣布解散元宇宙、Web 3.0 等命题组也要全身心押宝它。 国内的各大流量媒体更是不吝啬各种营销术语,对其各种浮夸表述,导致它的能力在坊间越传越离谱,几乎吹嘘它为无所不能。 如此神话、盲目崇拜之风的氛围下,作为一名十多年工作经验的程序员,同时又身处数字化转型的产业互联网赛道,我觉得当下很有必要理性、冷静地对它进行一番打量。 因此,在深度使用了一段时间并开发了一个集成 OpenAI API 的 Dingtalk 机器人服务后,我也终于自觉有底气来讲一讲自己对它的一些看法,另外一个补充背景是,笔者所在的产业目前对这块引起了高度的重视,本文也作为一篇偏向工程实践角度的调研报告来呈现,以上是我写下这篇文章的动力来源。 使用 OpenAI 提供的 Open API 服务,开发者可以方便地对其集成,图中是我开发的一个服务结合 Dingtalk 机器人 Outgoing 特性达成的最终效果,该服务托管在 Glitch 平台 目前,它拥有如下 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 会在每一轮计算生成之前,将之前的 输出 合并 输入 后再次给到 输入,如此反复,以达到 生生不息 的状态。每一次调用执行,一个新的上下文环境就自动生成,并和之前保留的上下文之间天然形成连接。熟悉编程语言的同学应该发现,该特性像极了主流编程语言中的 闭包 特性。 ChatGPT 工作方式的极简示意图 这个特性是 ChatGPT 在会话情境中给人类的第一印象达到震撼程度的核心原因,也是最为大家津津乐道的: 它好像真的在和人类 对话 ,而不是生硬的 你问我答 模式。 没有银弹 布鲁克斯的《No Silver Bullet — Essence and Accidents of Software Engineering》告诉我们,软件工程没有银弹,任何设计有优点就一定有缺点。 很显然,这里有几个应用架构侧的隐患在设计之初就已被埋下了,以此,我们可以从应用侧窥探到,即使背后的 AI 大脑再强大,到了应用侧依然会受到技术设计本身的限制,虽然我们有诸多曲线救国的办法,终究仍会受制于这个短板效应: 由于需要保持上下文环境的捕获特性,输出要不断继续给到输入,这就意味着,如果从系统架构上,不保留一个类似 tokens buffer 缓冲区的设计,整个 ChatGPT 可能会遭遇不可预期的风险(大量的算力消耗甚至会直接拖垮系统,导致崩溃),这也是为什么 OpenAI Open API 中关于 Model 能力调用的参数说明中明确指出了 4097 tokens 的长度限制: What are tokens and how to count them? / OpenAI Help Center。 应用侧需要使用会话 id 对每一次完整的会话进行隔离,因为即便是人类的对话,也不可能永远都处于一个全局唯一的上下文环境里:比如你今天和朋友聊的话题是吃饭、明天和家人聊的话题是电影,如果几个话题同时并行,很容易将会话带入到一个混乱不堪的状态,即俗称的:不在一个频道,跨服聊天。 ChatGPT 在实际运行场景中,始终在某个单会话中处于一个 生生不息 的状态,什么时候需要停止会话,即告知模型停止生成任务这个事情,需要给到用户主动权。 上述问题体现到 ChatGPT 应用侧的人机交互上,但凡使用过的同学应该很熟悉了,其中 2、3 完全就是通过纯产品维度的设计来解决的,比如你使用它的时候必须先新建一个会话,然后页面的底部会在生成任务进行到一定时间后,给到你一个停止按钮,让你可以随时停止这次生成。 chat.openai.com 主界面 截至此时此刻,你依旧可以找到很多讨论 ChatGPT token 限制的文章,里面的部分核心原因我们从软件工程的角度就可以分析出来,针对这个核心限制,目前 OpenAI 已经开放了 Embedding 的能力,这又是另外一个话题了,感兴趣的同学可以自行 Google。 再谈实时 Web 应用 我认为,这意味着,今天像 ChatGPT 这样强悍的 AI,如果要在应用侧设计一个足够好用的 AIGC 产品不是一件容易的事情。 它的诸多特性决定了我们在其配套的应用侧的设计上不可能像传统的应用一样。 我甚至斗胆预言,其会再一次推动 real-time web applications 即实时 Web 应用的技术演进,尤其 响应式编程范式 。如今,前端技术社区里关于 MeteorJS 特性的讨论仍旧不绝于耳。回头来看,其当年提出的 Full Stack Reactivity 的理念是何等的先进。 滚滚历史车轮一直是在争议中前行的,直至当下,在某些主流前端技术社区里,仍然可以看到这样的讨论:究竟什么样的场景要使用 RxJS 。 我想 ,现在属于它以及 Reactive 思想 家族的应用开发时代已经来了。 巨人的肩膀,站上去容易,站稳不容易 现在,让我们回到上面我提到的 Dingtalk 机器人遗留的那个 不支持 的问题。 由于如下所列的限制,导致该场景无法精确地集成 OpenAI 会话式感知能力 API,最终只能支持简单的 一问一答 ,体验效果大打折扣。 即使在 OpenAI 提供会话式特性 API 的情况下,Dingtalk 客户端只提供 Outgoing 能力的机器人是完全不够的,如果要在群场景中支持会话式 API,需要在服务端识别出一个唯一的会话标识,但是显然在 Dingtalk 的群会话等场景中再次定位出一个个会话是很难的,因为无法判定这个唯一标识,比如,群里的某个用户 AT 这个机器人的时候并不一定是某个会话的开始。 如上面提到的,在 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 的连接能力,正在赋能 生产力提升 的领域大放光彩。 n8n.io 集成 OpenAI 服务 重新审视它的 AI 能力会发现,其实它在玩文字接龙 从应用侧的视角阐述 ChatGPT 的工作方式是比较直观的,可以帮助我们理解 AIGC 类服务和集成它们的产品之间是怎样一种联系。 从上文的观点中,我们已经知道,想既做到让用户时刻感知到 AI 的强大,又保持相对不错的用户体验,还能通过某些精妙的产品端设计来规避掉技术侧的限制,是需要下苦功的。 而 ChatGPT 达到今天的成就,在 AI 能力的塑造上更是下足了苦工:距离初版 GPT-3 的发布都已经过去 2 年了,但事实上 2018 年 OpenAI 就已经开始推出这个自然语言处理模型(NLP) ,其拥有 1750 亿之多的模型参数,训练成本花费约 1200 万美元,是同类中参数数量最多、最昂贵的模型之一。 ChatGPT 进化史 我们对其能力的解剖,尚有一部分必须要留在 AI 的范畴进行阐述,那就是这个 ChatGPT 大脑的核心,它的生成能力: GPT.generate(inputTokens); 因为这个能力几乎决定了它的边界,也就是我们通过理解它能力的原理,从而判断出它能干什么,不能干什么。 如上文提及的一样,ChatGPT 和它的祖宗 GPT-3 以及它自己,本质都是一个 语言模型 ,而且是大模型。 我们先来讲讲大模型(Foundation Model),在此之前,有些前置概念的理解很重要: 人工智能、机器学习、深度学习,从左至右是包含的集合关系;为了帮助大家通俗易懂的理解,我在这里举例说明机器学习和深度学习的本质区别:比如机器学习,让它学习如何从一个图片中识别出一个猫,就要喂给它一堆的数据,并且由人类标注出来,清楚地告诉它这个是猫;深度学习,让它学习如何识别出一个猫,它就像自己会研究这个命题本身一样,它直接思考什么是猫,什么样的特征是猫,从细节到局部再到全局逐层特征提取和识别,最后达成目的,过程中只需要不停喂数据给它即可。 模型、算法、程序,这三个 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) {}; 从函数的形式上,我们就可以看出不够理想的原因: 两个函数分别只能处理特定类型 A 或 B 中的元素; 两个模型所需要的参数数量、含义等都不一致。 抽象代数赋予了程序语言类型系统强大的泛型能力,我们将 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); }, }; }; 这在大模型里叫 兼容下游任务 ,我们通过这两大步骤的处理,最终得到的这个函数,看起来已经接近有效了: 既能够提前先跑出来一个超大规模的 AI 模型(这就是 预训练 ),精准度上最起码可以基本涵盖 A, B 两个领域,当然,如果能够本身就做到精准就再完美不过了。 不过没关系,精准度如果不够,下游任务自己来处理,大模型这个大函数本身还支持扩展出更多可能的下游任务。 而今天我们的主角,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 达成的三大步骤: 李宏毅:ChatGPT 原理 - 预训练 至此,我们完整识别出来了它的核心能力,以及它的能力边界。 正确地使用它比神话它更重要 AlphaGo 已经打败地球上所有的围棋手了,还有多少人在当下能想起它? 屠龙刀也要有龙可以屠,你的场景是龙吗? 仅仅是 AIGC 这个人工智能子领域,在 ChatGPT 大火之前就已经有很多其他 AI 厂商在提供了: Futurepedia - The Largest AI Tools Directory / Home 的官网每天都更新各种 AI 服务厂商 很多深度实践的知识工作者,包括我本人在内,早就已经在使用类似的工具来尝试提升某些工作的生产力了。 从这个角度,恕我直言,有两种行为都不可取: 如果你平时都没有意识通过工具等来提升生产力,更不用说要用 AI 提升生产力了,也就更无从谈起你想用 ChatGPT 来做惊天动地的事情了。 滥用屠龙刀解决一切问题。 对于个人如此,对于企业,更是如此。至于有些媒体完全无脑鼓吹的言论,大家更应该理性地看待它们。 但是,当下 ChatGPT 的火热,让大家再次意识到生产力变革的影响是如此的巨大,这个本身是具有教育意义的。 毫无疑问,属于 ChatGPT 的时代才刚刚开始,更多有价值的研究和商业场景落地案例,都值得我们继续保持关注。

2023/3/11
articleCard.readMore

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

目前,我们的旅程一直很舒适、惬意,路况一直很好,食物也充足、美味…… 事实上,如果我没经历过那些前所未有的糟糕透顶的事情,我会说,麻烦的事情才刚刚开始呢。 - 塔姆森。唐纳(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 大概在 07 年左右,家里已经接入了宽带,我对这台有些 “古老” 的电脑也做了一次硬件的全面升级,作为那个年代的我们,最早一头扎进互联网的时候,仍然第一时间选择了游戏,但即使在这样眼花缭乱的虚拟世界里总是有非常多的聪明人,可以和现实世界中打理生活一样井井有条。 当时我已经开始接触到世界级网游:魔兽世界(以下简称 WoW),复杂到离谱的界面和操作方式当时劝退了一批玩家,时常我们需要用一些 辅助插件 来帮助我们在游戏中完成一些更好的操作,注意,这在当时来看是 革命性 的东西,尤其是那个外挂满天飞的年代。直到后来正式成为一名技术工作者,我才知道,当时 WoW 使用的是先进的 Lua Language 作为插件机制的核心实现。如今,Lua 仍然在众多领域发挥着极其重要的作用。(考虑到本文的受众和读者,这里不做更多展开叙述,感兴趣的可以自行 Google) 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 正在推出儿童编程等课外兴趣课程 现在,我们可以感受到:在技能培训、兴趣教育方面,游戏可能真的是一个非常不错的老师,这在我看来正是「教育」和「学习」的本质区别,我们应该重视学习,而不是教育本身,授人以渔一定好过授人以鱼。 诚然,互联网世界本身的不对称性导致很多东西是双刃剑,时好时坏,关键在于我们如何识别和善用它。 我们可以留心回忆一下,或许我们在很多分享中和聆听别人的分享中经常能够听到「游戏」世界中的例子,它通过数字化的感染方式,也曾经给很多人带来非常正向的启发和积极乐观的处世态度。 在中国式的家庭教育里,曾经的「游戏」是让很多家长极其厌恶的东西,但是我们不得不承认,世界规则就是在悄然地发生颠覆性的变化,或许我们每一个人都应该思考和辩证地看待所有问题,这或许就是《爆裂》的法则真正想告诉所有人的东西:快速变革的时代,我们的适应性什么时候真正发挥出来? 这需要一定的时间,但是,我们必须做好准备。 实践优于理论的探索 从理论上讲,理论和实践没有差别。而在实践中,却有差别。 - 约吉。贝拉(Yogo Berra) 现在,我们已经从几个法则中聊到了「教育」和「学习」,让我们回到孟晚舟女士的 “劝告”,我现在想和大家聊聊这个「后工业前、人工智能时代」。 在未来,有一个结论会越来越突出,那就是:我们一定不需要人肉机器,我们需要更多伟大的创新者。 回到我们的日常工作中,说说我的体感,之前有提到,我是一个「工具」的倡议者,我相信绝大多数的技术工作者、程序员、计算机科研人员都有一个共识和很好的习惯,那就是我们都在工作中实践着「聪明的偷懒」。 很有趣的是,即使是在现在这个数字化高速发展的时代,我们似乎很少主动去承认今天「部分的工作」都可以「被机器替代」了,然而,我的观点是:事实上我们有些人每天都在这样做,而那些不承认的是,极有可能是不知道。 当然,我在工作中亲眼看到的情况比这个结论要更加复杂一些: 大部分人处于认知程度缺乏和无意识状态,机械地执行工作似乎成为了 “本能”。 大部分人不大愿意挑战「权威」,认为循规蹈矩地做事情风险更低。 大部分人不大愿意去突破自己的边界,不愿意去让自己成为多样化的存在,或者说缺乏在这个快速变革时代下的学习路径。 还有极少部分人,已经从「认知」层面就和这个时代发生着「对抗」。 这种现象在数字化程度比较低的国内公司里 随处可见,毫不夸张地说,如果不从「认知」上变革,我们中大部分停留在这个状态的人,即将被这个时代无情的淘汰。 在现实生活的工作中,我总是非常热情地给同事们分享很多我认为有价值的「工具」,即使对方不是像我一样的专业技术领域的工作者。因为这和我的专业没关系,仅仅是因为,我自己本身就是一个极度具有分享欲的人,并且,我认为,不管什么样的工作岗位,你在使用怎样的系统完成你日常的工作,有一个事实才真正值得探讨:那就是我们的工作方式。令人沮丧的是,我看到了一个非常令人担忧的局面:我们的工作方式和 10 年前没有任何本质区别。 看到这里,我相信有人一定会反驳我:我现在每天都在使用各种各样的 SaaS 软件解决我负责的工作内容,10 年前可并没有这些玩意儿啊。 注意,我再次强调,我说的是,我们的工作方式和 10 年前没有任何本质区别,数字化工具赋予我们的能力,在我看来,一直是数字化工具在驾驭着我们,我们还并没有驾驭数字化工具,或者说,我们不能因为数字化工具的产生去说改变了我们的工作方式,而是应该去思考:我们的工作方式本身有没有去驾驭数字化工具? 值得欣慰的是,商业世界里总是有聪明人能够聚在一起去思考一些真正高度的问题,就在我自以为是的担忧时,很多优秀的厂商已经在解决这样的问题了,他们被称之为「下一代生产力工具」,国内的创业公司跻身这个赛道的已经有很多了,我认为这是一个非常好的开端,当然了,这里面还有一个很有意思的话题就是,这个赛道在美国其实已经早就爆发式增长了,这个感兴趣的可以与我交流。 前段时间,有同事 A 找我解决一个工作中的问题,这个问题本身不复杂,但是受限于他的专业,他需要撬动资源去解决这个问题,需要协调部分开发人员进行排期开发,一想到这里,他非常沮丧,因为在组织结构复杂的现实情况中,我们都很清楚,协调资源解决问题的难度。 于是,我给他推荐了被称为「自动化工作流」类别的工具,实际上,我在最开始并没有直接给他推荐某一个这个类别下的免费产品,我认为 传递价值比直接推荐工具来的有意义的多,我希望大家去了解一些好玩的东西,能够真正快速解决工作中实际问题的东西,这些创新的工具提供的系列特性会彻底颠覆掉你的工作方式。 紧随其后,我才给他推荐了一款开源的产品:n8n.io,它提供桌面 desktop 的客户端版本,相对来说做到了简单易用,但其实还是有一定的使用门槛,因为我本人在技术领域做过类似的 VPL(可视化编程)产品,所以知道这种工具要想让更多的受众受益其实是一件有难度的事情。 但是通过简单的教学,最终,他使用这样的工具解决了几个这样的「长尾」工作场景。重要的并不是他解决了这个问题,重要的是,这个世界又多了一个可以真正愿意改变工作方式的人。 n8n.io 编排的工作流 事实上,在我团队的异地协同和管理中,我一直在使用小而美的工具链来解决团队工作的连接问题,包括和 IM 工具做一些打通,自动化地完成一些看似不起眼的工作,但是可能给工程师文化带来极大熏陶的东西,甚至是我个人本身的日常工作也都在这些新的工具上进行。 比如我用「自动化工作流」的工作方式,给团队定期地从有价值的 rss 源每周固定的采集技术文章给到团队成员。 同样的,我用「自动化工作流」的工作方式,完成了各种自动化提醒的琐碎的工作,甚至包括挖掘简历等。 也使用「语雀」「Notion」等下一代知识协同、云笔记软件进行各种信息的记录和整理。 我也使用「Airtable」做各种数字化小应用,甚至曾经一度帮朋友直接解决工作中的问题,尤其在疫情期间。 …… 这样的工作方式,完全取决于你自己的 「想象力」,而并不取决于工具本身推荐你干什么,我推荐所有人都应该去了解「下一代生产力工具」。 尤其是目前高估值、高增长的 PLG 赛道: PLG MARKET MAP 为了方便读者去了解和使用这些工具,我建立了一个开源的 Awesome List: 也欢迎感兴趣的同学能够参与到该 Awesome List 的贡献。 未来? 如果你认为,你现在不去关注这些还不足以有什么影响的话,那么当「人工智能」 今天摆在我们面前的时候,我们不得不 “焦虑” 一下了: 事实上,我绝对不打算在这里贩卖焦虑,我们也不谈论「机器是否终将代替人类」这样非常高度复杂的问题。我们只是从人机协同的角度,去尝试挖掘一下,我们每天日常的、具象的工作,被「机器」代替的可能性有多大? 这就不得不展开聊一聊当今世界正在发生的一些前所未有的科技变革了,我们可以尝试俯视地看一下、局部地看一下。 违抗优于服从,有良知的违抗 没有人是靠别人告诉他怎么做而赢得诺贝尔奖的。 - 《爆裂》 2019 年年初,我从阿里巴巴内部某事业部转岗加入盒马,这个团队非常年轻、多元。混合了传统零售行业的人才和从阿里过去的产研类人才,因为这是一个全新的「新零售」赛道,盒马一直没有停止探索新的业态,各种线下门店都有在全国各地先后运营,相信很多朋友也去体验过,盒马门店里也充斥着各种「新技术」。 我很有幸能够带领一支战斗力强悍的研发队伍,这个队伍里有一些全栈的技术工程师(包括本人在内)、还有一些图形学领域的年轻人、还有一个设计和前端开发技术经验并存的多面手。更难能可贵的是,尤其是公司内部,我们和兄弟部门之间的合作,尤其和专业设计队伍的合作更像是大家在一起创业,没有这么多的束缚,公司在创新方面深度实践了「违抗优于服从」的法则,我们并没有因此受到创新上的阻碍。 我们几乎每天都很着迷地去深入研究和探讨「门店经营领域的数字化」,盒马有一个自有的科技品牌:Rex Tech(https://rextech.cn/)可以从官网浏览 Rex 在零售行业的创新。 其实对外透出的已经是成熟的商业化解决方案了,但是在成熟的路上非常艰辛和困难,其中也有很多创新是用低成本的方式试错验证以后倒在了半路上,有的现在仍然在艰难的探索中。 在此过程中,有一个大的命题激发了我们团队浓厚的兴趣,那就是在零售行业被称为「空间规划」的命题,其中又分为两个子命题:宏观空间规划、微观空间规划。 我们在最早期的时候,就已经通过团队内部的一个优秀工程师(此处 at 某人)沉淀了一套 「CAD 图纸 DXF 解析服务」并且进行门店室内地图渲染的技术,这套技术其实也依托了 Mapbox 开源的 Leaflet 地图渲染器,但就是这样的一个关键领域突破,给盒马的数字化经营带来了 无穷的想象力: HSET FY21 碰头会 可以基于室内空间的算法计算、路径规划实施精准作业调度 可以结合 AIoT 做智能设备的 Floor Plan、POI 标注,结合 AOI 计算(比如水果档口区域播放打折信息)做设备的智能调度 可以结合线下场景的实时大规模数据,提供 Spatial Analytics 的能力 …… 并且在后期,我们也顺利做到了 2D 到 3D 室内地图的延展: HSET FY21 碰头会 值得一提的是,我们在这个点上的初心和想法,纵观整个零售行业数字化,其实本身并不算特别创新,布局这个赛道的很多科技公司早就已经入场 Spatial Analytics 这个命题了,给我印象最深刻的是(https://carto.com/industries/retail/)公司出的解决方案,大家可以直观地感受到 空间数字化 的价值,尤其是室内地图的多种结合业务的生动场景。 除此之外,我们团队当时也在不断跟进 webGL 标准演进,尝试使用 webGL 结合微观空间规划命题来解决门店的个性化陈列以及业务数字化(量化坪效等) 线下门店的陈列是一个非常有意义的命题,没有人知道它的真正威力,一个陈列非常清晰、非常引入注目的门店能否带来直接营收的增长?这里面的深层原因和复杂因子之间的关系和规律,至今仍然是一个谜,即便我们已经通过技术手段可以拿到相应的数据,但是要 归因分析,找到一个增长密码,仍然非常困难。 门店陈列全景:图片素材来源于 36kr 风险优于安全 创新中的安全已经不再是优势,敢于冒险对于公司和经济的发展而言变得至关重要。- 《爆裂》 上述的微观空间规划命题是一个典型的案例,我们当初受到了多方的质疑,但是我们并不打算就此停下我们的步伐,里面的难度大到什么地步呢: 我们需要对所有的商品进行标准化采样和数字化建模,我们需要沉淀这样一个非常完备的模型库 我们需要对各个陈列方案做 3D 的还原和动态编辑,以及云端的烘焙渲染 我们需要能够结合陈列的有限空间位去结合宏观空间规划,进行点位的放置和检查等一系列业务 SOP 我们需要和业务沟通这个链路的 SOP 落地的可行性 …… 其中的任何一步要想做的很好,每一步都可能耗费「年」以上的时间,而我们团队当时能够专心在这个领域的,几乎只有 2 个同学。 不过我们还是决定先干出来,即便是下了这样的决心,前前后后我们在这个命题上的探索就花了近一年的时间。 早期,我们通过摸索业界的 case 来获得一些灵感,商业世界中的业务复杂度远远超过所有人的想象。所以,在技术和产品端并没有任何可以直接能够观测到的、或者挖掘到的低成本拿过来直接使用的解决方案。 03b-WebGL-Retail-Space-Planning-with-WebGL_Aug18.pdf MerchLogix 在 WebGL 领域的探索 当我看到 MerchLogix 的案例后非常兴奋,于是我们开始了在这个领域的有节奏的技术规划和攻坚,从内心上讲,我们认为创新是要付出一定的风险的,但是它是非常有意义的,需要一些有理想主义情结的人在一起去坚持。 最终,我们从粗糙的原型及 sop 链路验证顺利过渡到一年后的全链路产品化。而且,这个解决方案基本上解决了之前所有的难点,虽然其仍然有很大的提升空间,但是在创新上算是走出了领域内的第一步。(由于该解决方案目前还没有正式商业化,因此本文暂时不透露更多的素材和细节) 该陈列方案最终会通过消息渠道,推送到门店的陈列员手机上: 指南针优于地图,未来无法预测 只要你走的够远,就一定能到达那里。- 《爆裂》 我们在和「门店数字化」打交道的近几年中,发现商业世界的业务复杂度乃至规律都非常难以捕获,未来的形势判断,即使是最聪明的决策者,都给不出任何答案,但是在零售行业的数字化道路上,盒马已经给出了很多探索和更先进的智能,交给了这个时代一个我认为非常令人满意的样板。 调度效率极高的悬挂链系统,提升履约率 门店日常经营巡检机器人 门店 SOP 调度系统 自助 POS 机(大家最熟悉盒马的应该就是这个) 大规模数字化养殖解决方案 …… 这其中某一些,我们在 2020 年的 CCFA 上向来自零售行业的所有同行们及生态伙伴、客户进行了展示,在和友商的各种交流学习活动中,我本人也收获颇多,欢迎对零售行业感兴趣的读者与我探讨。 CCFA 2020 客户在我们的展台自行操作数字化场景编排器 + 数字孪生仿真 作为技术团队本身,也在一些少有人走的路上尝试了一些新的探索,并且逐步完善和沉淀成一套成熟的技术框架和上层平台能力。(考虑到本文受众,这里不展开技术讨论) 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 同样考虑到读者及相关受众,本文将不会展开讨论技术细节,我可以简单通俗地总结一下: MCTS 算法极大优化了策略的搜索效率(围棋的排列组合的最终数字恐怖到令人发指,靠暴力穷举遍历几乎是完全不可能做到的,上文已经提到),AlphaGo 的判定会非常快,而且可以选择出当前的最优策略(样本基于前置数据的学习)。 AlphaGo 不是万能的,它更多地就是沉淀了一套围棋的 AI,定位是一个非常聪明的下棋智能机器。 AlphaGo 至今仍然在发展和迭代,有些方法和模型已经沉淀到其他各个机器学习领域。 有意思的是,当时此事一出,国内各大科技媒体、著名企业家等人士均对 AI 表达了自己的观点,一时间,人们对 AI 的关注达到了一次空前的热度。当然,其中也不乏一些极具争议的观点。 而学术界,科研界从来就没有停止研究的步伐,截至到今天为止,机器学习领域的技术成熟度应该来讲已经迈过了里程碑节点,应用范围也已经具备了一定的规模,在某些特定垂直领域,甚至出现了杀手级应用场景(比如 NLP 结合 AI 的各种场景),在商业化世界中,它也已经深入结合到企业、产业的数字化转型中。 但我们要关注到一个事实:即使技术的先进性在不断发展,人工智能领域的技术应用要解决的问题还非常多。其本身基于大量「标注数据」进行训练的「特性短板」也在一些场景中开始逐渐暴露。 针对未来的发展,我有看到,一位学术界顶级的研究者、前辈(微软亚洲研究院刘铁岩博士)也给出了一些探索方向:(读者可以仅作了解) 对偶学习、精深学习、并行学习、符号学习、自主学习、(超)人类学习 更精准的分布式计算架构,使异步计算可以不受乱序的影响。 增强学习。 相信在不久的将来,人工智能将成为一门「普惠技术」走入我们的日常工作和生活,我们也将迈入真正的 AI First 时代。 数字化转型的不可逆趋势 听到了太多次的「数字化」,这一次我们选择正面刚。- Archer(本文作者) 如果你也跟我一样,在阅读《爆裂》的过程中有那么一瞬间真正发自内心的豁然开朗,尤其在你曾经也像我一样思考「数字化」对于个体的意义,那我相信你、我会对这样的认知有着极大的触动: 不管我们的性别、年龄、种族等等,地球上所有的人都能利用数字化的力量,来发挥自己的多样化的能力。 多样性优于能力 - 《爆裂》第九大法则 历史上每一次工业革命都会带来整个社会的进步,从而对就业、市场的变革带来极大的冲击,随之引起人们的某种可以预见的焦虑。但我想表达的是:这并不是「人工智能时代」才带来的影响,这是人类在变革发展进程中一直在发生的事情。 正因为如此才会让孟晚舟女士发出这样的 “劝告” 和感慨,如果说,我们这一代还没有显著影响的话,那么可以预见,我们的下一代将直接处于变革的浪潮中,历史告诉我们,与其过分焦虑这个事情还不如多多思考,因为我们在之前以及《爆裂》中反复提到,未来的不确定性是必然的。当然,每一次变革也会带来新的制度、新的市场机会、新的就业岗位。问题是,我们准备好了没有?真正值得焦虑的是,我们中的很多人并没有真正意识到这个问题,更不用说我们还需要扛起一个重大的责任:如何正确地影响和指引下一代。 回到我们个体,对于我们而言,有意义的探讨是:我们应该如何解决未来的生存问题。 回顾之前我提到的在盒马的经历,大家可以看到零售行业加速转型的冰山一角,当下,经营的效率问题和人工成本已经得到更为智能地解决,即使是原来的 信息化系统都不能 “幸免于难”,他们正在被更加智能、科学的数字化系统取代,这就是已经在发生的事情。 而事实上,大家留心观察,今天你去盒马逛线下门店的时候,少部分岗位已经被 用「机器」替代,比如柜台收银员、客服,巡检人员;甚至不排除就在未来短短几年内,导购员、面销、捡货打包等专职人员即将直接被代替。并且像类似无人 pos 机、巡检机器人等 “killer app” 应用正在向整个零售行业蔓延。 劳动力密集型产业 加速变革的背后,我们不能仅仅看到人工智能带来变革的表象,实际的本质是:产业本身就需要「数字化转型」才能得以在未来生存。 目前该命题已经被列为国家战略之一,未来 5 - 10 年,所有的本土企业,尤其中小企业,都要完成转型,否则势必面临淘汰的结局,这也就再次加快了这个命题反向推动产业变革、生产关系变革、劳动力结构性变革的速度。 这就需要每一个个体也都清晰地认识到这个趋势,为此,我个人有一些务实的建议: 了解「信息化」和「数字化」的区别 了解数字化的价值和意义 了解数字化在当下的主流手段、策略 作为企业,需要思考和重构企业生产关系,重新定义经营模型。 今天核心的商业模式本质,在我看来万变不离其宗。但是 Z 世代、下一代数字化原住民开始逐渐成为全球的中坚力量,消费者端的行为发生了巨大的变化,反向推进上游不得不做出变革,市场在激烈地反应,一个不注意便可以让上游措手不及。新消费、新制造、新零售……,回头看这些当时最开始被提出的「新」概念以及后续加入的「新」词汇,所有人几乎在我看来都是观望、迷茫状态,如今,在这个赛道里的各个巨头也好,中小企业也好,开始真正了解到:哦,原来不是在创造新的概念,是时代变了,是时代变了以后,我们所有的做生意的方式值得推翻重新做一遍。 因此,在我的意识里,我并没有从概念本身去理解这个新,这些概念一直在,「新」的起源是时代的变革。新的基础建设在飞速崛起,今天的时代已经全面进入算力时代,一项严谨的数据表明,近几年包括美国在内等国家的 GDP 增长数据却没有让能耗的数据大幅度增加,这跟算力的崛起有极大的关系。 前不久,在北京时间的凌晨,有幸随机 pick up 了一场远在美国湾区的技术圈在 twitter 线上举办的一个有声讨论,讨论的是非常有意思的 《数字游牧:Digital Nomad》 话题。 关于数字游牧,先给不了解的朋友简单描述下,这是一个很有趣的、令人一听就心生向往的「工作方式」。 我引用知乎某数字游牧伙伴 Jarod 的定义:数字游民的终极目标既不是单纯为了旅游,也不是为了发财致富,而更大程度上代表了一种对于更高生活品质和自由度的追求。 他说这个定义的上下文是认为,大家片面地把数字游牧定义为:一类采用「边旅游边工作工作方式」的人。 非常感性、充满自由气息的数字游牧,我相信每一个拥有理想主义情结的人,都向往这样的能够平衡生活和工作的奋斗方式,以让我们活的更加有质量。 数字游牧 我们可以关注到一个趋势,全球的疫情大背景催生了新的办公协同方式,近年来企业办公协同软件的使用量激增,因为我对钉钉非常了解,在阿里多年的职业生涯中几乎每天都和钉钉打交道,我也是最早期的一批 slack 用户、teambition 用户,国内外 PLG 赛道的办公协同软件也有过一些深度的使用,因此我可以从我个人的角度谈谈这类软件的价值。 其实这类软件存在已久,但是普及率在我看来,如果不是疫情等外界因素的刺激,尤其很多国内的传统企业,工作协同方式依旧是非常传统的。 我个人认为企业管理的基本定义可以高度抽象为:管人,管事。靠流程管事,靠制度管人。 这个定义不一定过于严谨,实际上企业核心关注的经营情况仍然需要很多核心关键职能部门的协同,但是从「管理」的角度,我认为这个基本逻辑是可以解释的。 在信息化为主的时代,其主要依赖一个核心链路:信息收集 -> 信息展示,它仍然没有进入严格的数字化领域范畴,更多的是将物理世界的协同活动进行刻画,将数据从物理世界收集到信息系统中,因此在这个阶段,诞生的很多大家熟悉的信息化系统基本上围绕的都是这个思路。 流程的信息化在业界的理论和实践已经非常成熟,OMG 组织一直在致力推动的 BPM 以及后续衍生的 CMM 等,无论是工具还是方法论,已经有大量的业界产品赋予了其新的定义和实践,但是在我看来仍然没有脱离信息化的范畴。 制度的信息化,如果我们在系统中非要给他一个实体定义的话,我认为可能就是侵入到各个流程中的规则,这是相对可以结构化的部分,例子有很多,比如制度要求员工晚上 9 点后打车才能够发起报销流程等。 但是制度的感性部分就比较有意思了,很难定义,很多公司甚至还有文化领域的建设范畴,因此,如果我们没有办法去定义的时候,我们可以从「目的」的出发点去思考,企业对于制度的实施本质是希望企业的经营协同活动在一个「有序、高效」的状态下进行。 这部分如果单纯使用信息化的方式会显得捉襟见肘、力不从心。因为信息的价值或者说数据的价值没有被充分挖掘,无法在制度实施的过程中体现闭环,也没有数据驱动决策的显现。 而在数字化的思路中,是把人或者说物理世界的事物往数字化世界中进行连接,这就让很多不可能成为了可能。比如,在协同命题中,我们认为的最佳实践就是人与人之间的连接足够直接,互联网的连接效率如此之高就是因为突破了物理空间的限制,因此,我们可以看到企业协同软件以 IM 能力作为基础能力的普遍性。 价值由解决问题的能力来凸显,当今流行的企业协同软件,包括钉钉、飞书、企业微信等,他们的核心都是基于数字化的理念在做,「连接」能力是他们的灵魂,但是却又各有侧重。 但毋庸置疑,他们的数字化基座能力提供的想象空间是巨大的: 打破企业协同壁垒和沟通壁垒,大幅度释放先进生产力 通过数字化的开放平台等能力,企业可以方便的获得大量 aPaaS 层的能力,通过这种能力尝试去定义新的管理理念和方式,比如,有很多公司通过这些能力开通类似 CEO 直通车,打通客户到高层的渠道,解决问题的效率提升非常明显。 工具整合,智能化驱动,场景化输出,可以大量覆盖企业的中长尾诉求。 …… 在这样一个先进的生产力方式下,企业员工的工作方式就会发生很大的变化,尤其在 2B 的业务背景下,我们要做的 是和「客户」共赢。 因此,我们不仅要习惯这样的工作方式,更要 能够善用数字化的能力去做一些创造,这个建议我在此文中的其他地方也在反复提及,这是一种特别宝贵的能力:「自驱力」。 如果说以上的例子还不够性感,那么今天诸多的 KOL、在各大网络媒体平台上的自媒体博主就是另外一些生动的例子了。 这就是我认为回归到我们个体,应该关注的「转型」。 建立多样化的能力模型,在数字化世界里游刃有余,可以极大降低创新的门槛,今天只要我们有一个 idea,就可以快速地做起一个 mvp 来。它看来像是在强调个体的作用,实际上是我们与数字化的连接紧密了,成为了一个更强大的个体。 劳动力密集型的可替代性,我必须强调,并没有一种转型的目的本身就是为了替代掉「人」,而是因为这类工作方式的特性决定了它可以被「机器」替代,比如 大量的可被模式化的重复劳动。 那在这里也推荐读者可以关注下 Gartner 提出的超自动化。 这里的「机器」如我本文开头所言,概念上它是广义的。 我们可以梦想一下成为「数字游牧」的一天,但当下,我们应该了解这种态度背后的时代意义和深远的价值。 特别有趣,「知识就是力量」变得越来越具像化了。 人机共生,科技向善 科幻电影的画面总是给我们太强烈的视觉冲击,可如今,当科幻电影的种种逐渐变成现实的时候,我们应该思考科技到底应该给人类带来什么?这也是我个人认为《爆裂》中「系统优于个体」法则想要探讨的问题 - Archer(本文作者) 在早前时候,我在关注和留意未来的人机交互创新领域,在知乎上也是看到了一个很有意思的讨论: 下一个革命性的人机交互方式会是什么? BMW Intelligent Personal Assistant communications system 人机共生是一个跨学科领域,由来已久,并且在学术领域仍然在大力研究,其中这个问题下有提到一些关键字 「多模态交互」、「宁静技术」 等比较有趣。 人类作为一个高等智慧生命种族,我们的结构应该来讲是非常复杂的,我们的大脑每天都在非常协调地进行着我们的工作和活动,如果我们把问题简单化一下,我们大致可以认为,我们日常的工作主要是偏接收指令执行的工作和需要大脑去决策的工作。 很显然,人类是擅长下达指令的,这是一种本能,所以在过去的我们认知的人机交互中,我们更多的是在向机器下达指令,随后机器定向地跟我们形成某种反馈。 未来的人机交互发展到人机共生阶段,人和机器交互会无限接近于人与人的交互,界限会相对模糊化,「宁静化技术」的例子就更生动一些: 你可以想象一下,你回到家以后,你突然感觉一阵燥热,想要打开空调,这个时候你身上的传感器已经自动地将你的身体状况信号发送给了一个物联网家庭网关,空调随即启动,这个时候你准备洗漱一下,来到洗漱镜面前,镜子是一个智能化的屏幕,已经在给你推送最新的资讯。 关键是,这一切行云流水,无比自然,我们和机器共生在了一个活动圈里。 当然,总是有人会担心「奇点」论,我个人反而比较认同另外一个观点:我们始终确认的一点是,我们生而为人,千万不能像机器一样活着。 我觉得我们不用过于担心,「科技向善」 已经成为全球诸多科技企业的共识,很多人可能不明白这个所谓口号的意义,我们在过去发展科技领域的这么多年,商业世界里的很多决策仍然是更多地受到个人利益、企业利益的驱动,但是在未来,我们要去思考「科技」的每一次变革对于人类社会的意义、对于生态等的影响,这是非常重大的一个思维转变。 包括现在我们一直在讲的「低碳」,核心的意义也是如此。 作为一个个体,尤其在科技行业的工作者,我们深知「科技向善」就出现在我们每一次产品细节的打磨过程中,这是我们能够为之做的一点浅薄的贡献。 这给「创新」又赋予了更高层次的含义,我觉得我们可以做的更有社会责任感,更有侠义精神。 结束语 在疫情出差北京期间,我花了点时间读完了《爆裂》全书,结合我工作中的一些经历,我的一些认知,断断续续写下了这篇杂谈,我想,评论区可以留给大家,我们可以深度展开探讨其中涉及的任何话题。 该书在读者中的评价褒贬不一,我认为可能的原因是因为群体认知的两级分化严重,太多面向未来的不可预见性容易导致人们对一些观点持有保留意见甚至是质疑。 我觉得这都不重要,重要的是,我们总是还在思考中前行。 令人唏嘘的是,2019 年,本书的核心作者,麻省理工学院媒体实验室主任伊藤穰一(Joichi Ito)因与杰弗里・爱泼斯坦(Jeffrey Epstein)充满争议的往来关系而辞职。 这位美国金融家当年七月因性交易指控被捕,随后在拘留期间自杀身亡。 我想,这又是另外一个话题了。 以上,感谢耐心的读者。

2022/12/31
articleCard.readMore

How to clean email for gmail via app script ?

分享一枚非常实用的 google app script,需要的同学可以将其安装于 https://script.google.com/; 它支持全自动调度清理任务,清理任务主要根据 gmail 中的指定搜索条件。 Feature 指定 gmail 搜索条件 错误 / 边界条件等自动触发新调度 trigger 唯一 Content of App Script 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 //search string to delete var SEARCH_STRING = "category:forums"; //tigger function name var TRIGGER_NAME = "cleanWithScheduler"; //first time job delay (min) var FIRST_TIME_DELAY_MIN = 1; // FREQUENCY of scheduler (min) var RESUME_FREQUENCY = 10; //batch size var BATCH_SIZE = 500; //intialize function intialize() { return; } //install function install() { ScriptApp.newTrigger(TRIGGER_NAME) .timeBased() .at(new Date(new Date().getTime() + 1000 * 60 * FIRST_TIME_DELAY_MIN)) .create(); } //clean triggers function cleanTriggers() { var triggers = ScriptApp.getProjectTriggers(); for (var i = 0; i < triggers.length; i++) { ScriptApp.deleteTrigger(triggers[i]); } } //clean immediately function cleanImmediately() { var NOW = new Date(); Logger.log("SEARCH: " + SEARCH_STRING + " BATCH_SIZE: " + BATCH_SIZE); try { var threads = GmailApp.search(SEARCH_STRING, 0, BATCH_SIZE); Logger.log("Processing " + threads.length + " threads..."); for (var i = 0; i < threads.length; i++) { var thread = threads[i]; var lastMessageDate = thread.getLastMessageDate(); if (lastMessageDate < NOW) { Logger.log("lastMessageDate " + lastMessageDate); thread.moveToTrash(); } else { var messages = GmailApp.getMessagesForThread(threads[i]); for (var j = 0; j < messages.length; j++) { var email = messages[j]; if (email.getDate() < NOW) { Logger.log("Start to move this mail to trash."); email.moveToTrash(); Logger.log("Mail has been moved to trash."); } } } } } catch (e) { Logger.log("error: " + e.message); } } //schedule new job function scheduleNewJob() { //before new job, we clear all triggers first Logger.log("clean triggers..."); cleanTriggers(); Logger.log("Scheduling one new job..."); ScriptApp.newTrigger(TRIGGER_NAME) .timeBased() .at(new Date(new Date().getTime() + 1000 * 60 * RESUME_FREQUENCY)) .create(); } //clean with scheduler function cleanWithScheduler() { var NOW = new Date(); Logger.log("SEARCH: " + SEARCH_STRING + " BATCH_SIZE: " + BATCH_SIZE); try { var threads = GmailApp.search(SEARCH_STRING, 0, BATCH_SIZE); if (threads.length == BATCH_SIZE) { scheduleNewJob(); } Logger.log("Processing " + threads.length + " threads..."); for (var i = 0; i < threads.length; i++) { var thread = threads[i]; var lastMessageDate = thread.getLastMessageDate(); if (lastMessageDate < NOW) { Logger.log("lastMessageDate " + lastMessageDate); thread.moveToTrash(); } else { var messages = GmailApp.getMessagesForThread(threads[i]); for (var j = 0; j < messages.length; j++) { var email = messages[j]; if (email.getDate() < NOW) { Logger.log("Start to move this mail to trash."); email.moveToTrash(); Logger.log("Mail has been moved to trash."); } } } } } catch (e) { Logger.log("error: " + e.message); scheduleNewJob(); } }

2022/12/29
articleCard.readMore

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 The past, present, and future of Dark Mode

2022/5/31
articleCard.readMore

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 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

2016/4/28
articleCard.readMore

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 有本质上的区别

2016/4/10
articleCard.readMore

前端工程师的工具包

前两天看到 SegmentFault 运营专栏最新的工具控撰写活动,挺有意思的。我觉得整理这样一份自己工作中常用的工具清单还是有好处的,一来是检验自己有没有必先利其器,二来是通过这种分享的方式获得更多的神器推荐。 工具跟使用者的属性是有必然联系的,本文将从前端开发从业者的角度整理一份我自己的清单,大部分工具的使用平台为 OSX 我的工具格言 具不在多,够用就好。 编辑器及插件 名称 推荐理由 备注 ATOM / 编辑器本身 atom-beautify 编辑器代码美化 / atomic-chrome 使用 atom 编辑页面中的输入框内容 / atomic-emacs emacs 快捷键绑定 Ctrl+x 3 分屏的赶脚爽多了 autocomplete 自动完成 必备 autoprefixer 自动前缀 css3 必备 colorpicker 颜色拾取 在编辑器中使用频率不高 codeif 命名神器 听奏写的变量命名服务 emmet 前端开发神器 https://docs.emmet.io/cheat-sheet/ terminal-plus 编辑器集成终端 打造 ide 的前奏 file-icons 文件图标显示 不装逼,很实用 OSX 工具集 名称 推荐理由 备注 Alfred 快启神器 不多说了 Tmux 终端分屏 神器属性爆表 oh-my-zsh zsh 增强版 神器属性爆表 Gitbook Editor 写书用 / Spectacle 窗口按指定位置摆放 / 微信 Web 开发者工具 微信开发必备 / Dash 手册仓库 神器属性爆表 AssitorPS 切图辅助 神器属性爆表 Macdown Markdown 编写 体验佳 Chrome 插件 名称 推荐理由 备注 singleman 必备神器 / Avatars for Github Github 的动态上显示头像 / DevTools Author 在 chrome 的 Sources, console 等面板美化代码显示 / Google 绘图 画 UML 咯 / Isometric Contributions 以 3d 柱状图显示 Github Contributions / JSON-handle json 编辑神器 / JSONView json 查看神器 / Lastpass 密码管理 / Momentum 壁纸,TODO / Octotree 在 Github 左侧显示项目的 Tree 阅读和学习开源项目的神器 OneTab 不多说了,节省资源神器   React Developer Tools React 开发必备   Vimium vim 快捷键操作浏览器 神器属性爆表 WEB 前端助手 (FeHelper) 常用工具箱 国产优秀插件 划词翻译 很方便   稀土掘金 掘金社区 Co-Editor 必备   Xmarks 浏览器收藏夹同步 必备良品 CLI 增强 名称 推荐理由 备注 caniuse-cmd caniuse 命令行版 实用

2016/3/12
articleCard.readMore

Github 上那些炙手可热的技术手册

Github 上有一些手册类型的开源项目,具有很高的务实价值,因此,本文将保持小碎步更新,请叫我收集狂。 清单 等等,有敬业福吗?:smile: name description repo CSS 参考手册 国内最好的 CSS 参考手册,持续小步更新 :link: bash-handbook This document was written for those who want to learn Bash without diving in too deeply. :link: stream-handbook This document covers the basics of how to write node.js programs with streams. :link: babel-handbook A guided handbook on how to use Babel and how to create plugins for Babel. :link: browserify-handbook This document covers how to use browserify to build modular applications. :link:

2016/2/3
articleCard.readMore