Fork me on GitHub
COLORFUL 一枚数字艺术家的自留地
2023 - 03 - 11

(图片来自 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 的时代才刚刚开始,更多有价值的研究和商业场景落地案例,都值得我们继续保持关注。