《AI百通系列》

1.为啥所有AI都数不清手指?多模态底层原理揭秘

AI模型在数手指这种看似简单的问题上都表现不佳,这与底层模型机制有关。模型的输入包括图片和文字,输出为文字,难点在于处理不同类型的信息,即多模态问题

简化问题,去掉图片后,只有文字输入的话,模型是基于Transformer架构,要做的事情如下:

先对这段文字进行分词,再把每个词转换成向量输入给模型,最后得到模型输出词表的概率分布,进而得出预测的下一个词,把下一个词加回到原来这段文字的末尾,循环往复,不断生成新词,这就构成了一个回答。

我们可以把模型当做一个黑盒或者一个函数就可以了,这个模态只是文本的大语言模型。

另一个输入是图片,我们如何把图片融入到这个模型呢?

1.最简单的想法就是先把图片变成一段文字,然后和另一段文字拼接起来。但是把图片变成文字这一步不还是一种多模态的任务吗?

2.还得想办法解决,那我们就再进一步把图片变成一组向量,和文字部分一样,对于模型来说,接收到的信息就是一大堆向量,模型不关心,也不知道这个向量是从什么东西变过来的。

那怎么把图片变成一组向量呢?

使用传统的卷积神经网络CNN或Transformer兴起后的VIT,都可将图片转化为一组向量,这通常被称为图像编码器 。文本部分也有多种方法,同样可称为文本编码器

我们直接将这两组向量拼接,输入给模型,这样做是否可行?显然不可行。

在大语言模型中,词向量之间被训练成具有语义关系,即类似"男人"和"女人"的词向量在语义上相近,用数学语言表达,即两个向量的点积或余弦相似度较大。然而,在另一套模型中生成的图片向量,在语言模型向量空间中,就像一门外语一样,水土不服,语言不通。

比如:在大语言模型向量空间中,一张狗的图片转化为一组向量,可能反而与一个完全不相关的词向量相近,这样最终的输出肯定会混乱。

因此,最关键的一步是让图片和文字在同一个向量空间中对齐,使狗的图片转化的向量与"狗"这个词转化的向量相近,这被称为模态对齐,即:将不同模态的特征映射到同一个语义空间。

**如何进行模态对齐呢?**很简单,首先,从网上找到4亿对图片和文本,每张图片对应一段文本描述。然后,每张图片经过图像编码器生成向量,每段文本也经过文本编码器生成向量。

将它们排列成矩阵形式,接下来计算图片向量和文本向量的点积,即计算相似度。

注意看对角线上的结果,正是配对的一组图文向量的点积,我们把这些样本称为正样本对 ,并且希望它们的点积越大越好。而其余位置不匹配的文本,我们称为负样本对 ,并且希望它们的点积越小越好。这种使正样本对接近、负样本对远离的机器学习方法被称为对比学习

有了这种方法,我们就可以定义损失函数,在592块V100 GPU上训练18天,就大功告成了。

这就是OpenAI经典的多模态模型CLIP所做的,它确实收集了4亿对高质量的图文对,并且训练完成后开源了模型权重供下游任务使用。

**为什么这么强大的AI仍然数不清手指呢?**因为这个模型最底层的思路还是把图片先转化为文字,准确说是在语义空间做对齐和调整。经过图像编码器后,图片就只剩下有语义表达的信息了。从效果上看,就相当于先把图片变成了文字描述。

所以数不清手指这个问题的出现并不是大语言模型不智能、也不是模态对齐不够好,而是这个思路本身就必然会产生这样的问题。

在CLIP论文中,作者也说明了这块的局限性,即便是用大规模数据训练,它在手写数字识别MIST数据集上的准确率甚至不如最简单的逻辑回归模型。当然,解决这个特定问题很简单,比如用简单的YOLO模型就能很好地识别出手指,甚至还能标注出准确的位置,但要把这一能力训练成通用的、可泛化的多模态模型,就不是一个简单维度的事了。

由于大模型是基于大量文本做的训练,所以它在基于文本做总结、归纳甚至玩文字游戏、推理等方面都远远强于人类。但是也正是因为整体逻辑是基于文本,而文本内容本身就是把人类的思维经过一层转换后的输出结果,现在还要先把图片转换成文本,然后再基于文本做逻辑推理去理解图片,那这种策略就必然解决不了识别图片细节的问题。

这里也开个脑洞,如果我们的大模型不再是基于文本做训练,而是基于人类的思维做训练,或许多模态的路才能真正打通。但是人类的思维又是什么呢?似乎我们又同时在用基于文本的模型去尝试理解人们的思维。

2.开源大模型"开源"了什么?

DeepSeek刚出现时,有人说它是套壳的,有人说它是蒸馏的,这些说法都对吗?DeepSeek开源的是什么?你理解的开源是什么意思?

我的理解很简单,开源就是开放源代码的简称。例如,最著名的开源操作系统Linux,任何人都可以通过阅读其源码了解所有细节,甚至可以出版一本书,这就是开源的魅力。

对于DeepSeek,我们查看其源码不就能判断是否套壳或蒸馏了吗?我们来看一下,打开Hugging Face,找到DeepSeek-V3的源码,但太复杂了,换成DeepSeek-R1的源码,还是太复杂,再换一个早期版本,这个版本或许能让我们找到套壳的证据。怎么连一个Python文件都没有?

实际上,在大模型语境下,开源的含义已经发生了变化,为了更直观地理解,我们直接开源一个模型:

首先,在Hugging Face平台上新建一个模型仓库,简单命名为Simple。然后写一段Python代码,实现一个简单的手写数字识别任务,直接运行调试。

第一步,准备好现成的手写数字数据集,包含一堆28×28像素的灰度图片,这是数据部分。

然后,定义一个超级简单的三层神经网络模型,权重值目前都是随机初始化的,这是模型部分。

然后使用随机初始化的权重模型预测手写数字,几乎全都识别错误,没有问题。

接着开始对这个模型进行训练,大概训练三轮左右,模型的权重会更新为新的值,使用训练后的模型权重再次预测手写数字,现在全都识别正确了,说明训练效果不错。

然后,把这个模型参数保存成simple.bin文件。

这里就是一堆数值文件中的值不太方便查看,不过我们可以直接查看模型每一层的权重值。这是第一层,是一堆数;这是第二层,第三层也是如此。

最后,我们再把这个bin文件上传到Hugging Face平台,你就会看到在同样的位置多出了模型权重文件,恭喜你成功开源了一个模型。

别人如何使用你的模型呢?

首先需定义一模一样的模型结构,可自行编写,也可用开源仓库中的。

然后将模型权重从Hugging Face平台下载到本地,加载到模型中,此时模型权重已更新。

然后再次使用此模型识别五张图片,发现全都可以识别正确,再次恭喜,你的开源模型已被别人成功使用。

现在可回答一开始的问题:开源指的是生成的模型权重文件(simple.bin)及模型结构对应的Python类。

除此之外,数据部分及后续训练代码部分未开源,最多可能在论文中描述其思路。

**再回头看看DeepSeek是ChatGPT的套壳吗?**这个问题有歧义,若套壳指的是封装api接口,则问题本身不成立,我们讨论的是权重值的来源,而非服务的实现方式。

若套壳指的是抄袭权重,则更不对,一方面GPT未开源权重值,根本拿不到;另一方面,这俩模型结构不同,GPT的权重值就算拿过来也无法放到新模型中。

另一个问题是DeepSeek是gpt蒸馏来的吗? 这个问题也有歧义。按辛顿2015年论文及后来研究定义:蒸馏思想是让一个模型学习另一个模型输出的概率分布或中间特征分布,通常叫知识蒸馏。gpt未开源,我们只能拿到其最终输出的文字,这种知识蒸馏从根本上不可能实现。

在大模型时代,通过模型最终文本输出作为训练数据来训练另一个模型,也可称为广义上的蒸馏,通常叫数据蒸馏,本质是让更厉害的模型生成高质量训练数据,这部分看不出来。

蒸馏技术在大模型时代是常见且有效的方法。例如,DeepSeek开源模型使用DeepSeek-R1的80万条高质量训练样本,对开源模型Qwen和LLama进行微调的蒸馏模型。

3.UE8M0是啥?

将数字66转换成二进制即可存储到计算机里,用8位二进制表示的整数称为int8。若该数为小数,则先将整数部分转换成二进制,再将小数部分转换成二进制,两者用小数点拼接。

但8位二进制存储时还需考虑小数点位置。最简单的做法是默认小数点位置,前四位表示整数部分,后四位表示小数部分,这种小数点位置固定的表示方法称为定点数

但定点数不够灵活,若小数点位置靠左,则范围过小,无法表示很大的数;若小数点位置靠右,则精度太低,就无法表示很精确的数了。

科学计数法可实现范围与精度的兼顾。以十进制为例,用3位表示尾数,1位表示指数,调小指数可提高精度,如调整为-9,精度高达0.0000001;调大指数可扩大数的范围,如调整为9,可表示的范围高达十亿。

科学计数法中的指数本质上让小数点来回浮动,与定点数不同,这种方式称为浮点数

浮点数范围大、精度高,它是通过牺牲大数的绝对精度来实现的,对于很大的数来说,部分精度可以忽略不计,因此两个大数之间的间隔也很大,这样既可以表示很大的范围,又没有损失相对的精度。

回到二进制视角,用八个格子设计二进制的浮点数,即二进制的科学计数法 。其中四位表示指数,三位表示尾数,最开头的一位表示符号位,这种格式的浮点数表示称为八位浮点数,即: Float Point8,简称FP8

这种指数是四位、尾数是三位的分配方式称E4M3。每种指数位和尾数位的分配方式都可成为一种新格式,本质上涉及精度和范围的权衡。如E5M2可扩大范围、减少精度。

E4M3E5M2 是英伟达GPU支持的两种FP8浮点数的格式,并进行了硬件层面的优化。

若完全舍弃尾数,同时去掉符号位,改用加偏置的方式表示整数位,则这种无符号的指数位为八位、尾数位为零位的八位浮点数称为UE8M0 FP8

由于没有了尾数位,所以再进行2的幂次运算时,可直接用位移操作计算结果,硬件也可方便优化,在不追求精度但追求计算速度的场景下,这是其好处。正如DeepSeek-V3.1发布文章所述,这涉及让国产芯片一夜暴涨的名词的原理。

实际上,计算机中最常用的浮点数格式是fp32和fp64,即单精度和双精度浮点数,如java中对应的float和double数据类型,它们分别包含1位符号、8位指数、23位尾数;以及1位符号、11位指数、52位尾数。

随着深度学习发展,32位浮点数占空间较大,于是有了fp16和BF16这两种位数分配不同的16位浮点数。

随着大模型发展,为极致压榨性能,浮点数存储空间进一步压缩,于是有了两种fp8格式,以及本期视频所讲的另一种更极致的fp8格式。

这些格式有的用于存储权重值,有的用于存储梯度值,还有的用于存储缩放因子,并无绝对优势,而是在大模型底座性能难以再突破时,在工程层面极致榨干性能的尝试。

4.GPU到底是啥?

让我们来看一颗真实的GPU长什么样子,这是英伟达最新的显卡RTX 5090,它的内部采用的是Blackwell架构的GB202核心,总共有12个GPC,每个GPC里面有8个TPC,每个TPC里面有两个SM单元,每个SM单元里面有128个流处理器,也叫CUDA核心。所以,总共就有12乘8乘2乘128等于24576个CUDA核心。

CUDA核心可以想象成只会计算加减乘除的实习生的数量,对比英特尔最新的CPU酷睿Ultra9,它有16核22线程,在CPU产品中已经算是佼佼者了。如果强行把GPU的SM单元类比为CPU的核心,把CUDA核心类比为CPU的线程,那么这颗GPU就相当于一颗拥有192核心、24576线程的CPU,如果这种CPU有卖的话,售价可能会高达500万人民币。

所以,GPU和CPU各有各的擅长领域,CPU擅长处理复杂的通用任务,而GPU擅长处理简单的加减乘除运算,在图形渲染和AI领域发挥并行计算的优势。

由于AI领域大部分的计算都是枯燥乏味的矩阵相乘,比如计算前行后列矩阵的值,就需要两个向量相乘。实际上,在算这样一个式子时,假设一个GPU有12个CUDA核心,那么就可以同时计算这四个乘法,这显然是比CPU一个核心处理要快多了。但是由于计算后面的加法需要依赖前面的值,所以其实也只能先乖乖地把乘法算完,把结果存起来,然后再放到CUDA核心里面再次计算。

是不是感觉还有优化空间?没错,如果中间的结果不用倒腾一遍,直接经过硬件电路向下传递,通过硬件把整个矩阵运算的全过程都实现了,岂不是更快?恭喜你发明了一种新技术,并将该技术思路应用于GPU,增加了张量核心TensorFlow。若将该技术用于全新芯片设计,那就是现在的NPU,是专门用于处理神经网络及深度学习计算的芯片。

这三种芯片在矩阵计算效率上逐渐提升,但这是以牺牲灵活性为代价的。CPU是通用的,而NPU仅能计算标准矩阵乘法,即便式子稍作改动,NPU也无法处理。

虽然芯片种类繁多,但作为开发者,我们是幸运的。若你学习过Python,我们通过torch.randn(2,2) 创建一个张量使用的就是CPU,加一个**.to("cuda")** 则利用的是GPU,加一个**.to("npu")**则利用的是NPU,目前NPU尚未像GPU那样形成统一生态,这取决于厂商提供的开发者套件。罗马并非一日建成,所谓的傻瓜式开发体验,实则凝聚了无数开发者多年的智慧与心血。

5.理清3000+开源模型

现在每天都能听到新的开源大模型发布,数量众多,从2023年开始,开源大模型数量增多,到2025年数量将非常多。我从Hugging Face平台上搜集了30个知名厂商的开源模型,并整理成表发现数量竟达3000多个,这只是近三年的结果。研究这些模型非常困难,即使是专家,每天研究十个模型,也需要一年才能研究完,而且一年后又会新增几千个模型。因此,这期视频将用通俗易懂的方式梳理这3000多个模型的关系,并最终简化成一个模型。

我们从一个问题开始:什么是开源模型?很简单,记住两个东西,一个是模型结构 ,即模型的样子;一个是模型权重,即模型中的参数值,本质上是一堆数字。把这两个东西放到Hugging Face平台上,就成功开源了一个模型。

只要有这两个东西,就可以称之为一个开源模型。这样的开源模型有多少呢?刚刚的3000多个实在太多了,我们从中筛选出阿里的千问系列,也有355个,由于有一些视频、音频等多模态模型,我们再筛选一下,只看最经典的文本生成模型,此时还剩下255个,虽然还是很多,但已经有所减少。

假如此时你已经开源了一个模型,那么可以直接复制出来一个,把这里的权重值的精度 降低一些,比如0.12变成0.10,0.43变成0.4,0.56变成-0.5等等,这种将模型中的参数转化为较低精度以节省空间和提高计算效率的技术 叫做量化

比如右边这三个就是对qwen3-8b用不同方式量化后的模型,具体是怎么量化的,这里就不展开了。把这些乱七八糟的量化模型都去掉,还剩下多少呢?答案是一百零九个,一大半的模型就这样没了。109个还是太多了,我们继续往下看。

刚刚我们量化了一个模型,降低了参数精度,现在复制一个模型,不调整精度,直接修改参数值 ,例如将0.12调整为0.14,0.43调整为0.42,恭喜你又开源了一个新的模型,这种微调模型参数的技术 就叫做微调。当然,实际过程并非盲目调整,而是在特定任务上进行训练,这个过程和预训练一个模型一样,也是要经过完整的训练周期的,这里不再展开。

例如,这里的Coder和Math分别在代码和数学任务上对qwen2.5进行微调的模型,而且微调的模型还可以继续微调,理论上永无止境。如果去掉这些微调的模型,还剩下多少?答案是35个。

通过肉眼观察,很容易按名字归类,你观察出什么规律了吗?没错,除了参数数量几B不同外,它们的前缀几乎都一样,这就是命名的规范。

那么**这些"b"表示什么意思呢?**它表示模型参数的数量,英文是billion,即10亿。0.5b表示5亿参数,7b表示70亿参数,仅此而已。回过头再看这些模型,无非参数规模不同,也可以叫尺寸不同,而且现在很多小尺寸的模型都是通过大尺寸模型蒸馏而来的,可以随意裁剪,成本低,效果又好。

所以我们可以把不同尺寸的模型归拢到一起,这时模型的数量就只剩下八个了,既然都走到这一步了,我们还能让它再少点吗?

还是回到这张图,不论是微调还是量化,只是在模型权重上修改,模型结构不变

不过我们刚刚对模型结构做了简化,只是简单的两层神经网络,但实际上现在的模型远非这种简单的全连接神经网络,而是由各种不同的模块拼接而成,下面的内容如果你没有接触过代码或模型结构的细节可能会有些困惑,不过我还是先尝试说出它最本质的东西,然后再用通俗的语言给你解释一遍。

比如我分别打印出千问1.5、2.5、7b模型的结构,会发现它们的整体架构相似,只是里面具体模块的输入输出维度不同,所以这三个模型可以归为一类。但如果和千问3对比,或者和其他家模型,比如腾讯混元对比,整个结构就完全不同了。

回到这张图,每个模型都对应一个模型架构,我们称之为model_type,这是一个抽象层次更高的概念,正如之前看到的,这三者都属于qwen2模型架构体系下,可以归为一类。随着抽象层次的提高,模型数量已进一步减少为六个。

但你可能仍觉得太多,仅千问系列的模型架构就有十多个,如果再算上其他厂商的,数量岂不是爆炸了?没关系,我们再简化一下。

不知道你有没有注意到,刚刚看这些模型时,即使模型架构不同,但总觉得它们长得都差不多。其实大模型的结构早就已经收敛了,各家厂商都大同小异,不论是国内还是国外,最初的版本几乎都是参考开源模型的鼻祖LLama模型实现的,而LLama模型又是参考transformer这篇论文的思想,一个是理论的鼻祖,一个是实现的鼻祖,这就是所有大语言模型的根基所在。

总结一下,最开始我们搜集了3000多个开源模型,筛选出阿里千问系列的35个模型,只保留文本生成类的模型,还剩255个;再去掉量化的模型,还剩109个;再去掉微调的模型,还剩39个;再合并不同尺寸的模型,还剩八个;再合并相同模型架构的,还剩六个。最后,它们终于源自这两个大语言模型的鼻祖,万物归一。

模型架构设计已近乎成熟,真正的挑战在于模型训练,训练过程如同炼丹,需反复试错,无法预见何种数据、训练或超参数组合能产生优秀模型。每次训练动辄需上千上万张GPU,花费数百万甚至更多资金,训练过程可能长达数天甚至数月,且随时可能因细小bug导致前功尽弃。在此过程中,架构设计仅占一小部分,即便采用现成熟模型,如llama、DeepSeek、千问等,也很难训练出与之相当的模型,更别说超越。

模型训练真正的挑战在于数据处理、训练稳定性及参数设置,这些是成功与否的关键。当然,当前大模型逐渐涌现出诸多创新,如DeepSeek在训练过程中引入强化学习RL、MOE混合专家模型的使用,以及最近的千问next在新模型架构上的探索等,我们既不应被眼花缭乱、数量庞大的模型所迷惑,也不应忽视模型架构和训练细节上的持续创新。

6.AI文生图的底层原理

他们的时间居然像商量好了一样,都指向了10:10分左右,更离谱的是,即便在提示词中明确要求指向其他时间,他们依然会十分倔强地指向10:10分左右,这不是偶然事件,我试了几百次,每次都这样,就像施了魔法一样,就算画个扭曲的表也不行,只有画出电子表逃过一劫。以及虽然时间对不上,但张开的角度依然是10:10分的样子,这也太奇怪了。难道世界的真相是10:10分?

我们今天就来借着这个问题一起揭秘AI绘画,也就是文生图的底层原理。

我们先来看一下一张简单的图片是怎么构成的,我从一个时尚类数据集中挑了一张衣服的图片,它很简单,就是由28乘28个灰度的色块构成的,每个色块有一个值表示灰度值,仅此而已。

所以一张图片,就是一堆数字而已。如果把它们简单粗暴地排成一队放在一个神经网络中,作为输入就是图像识别类的任务,作为输出就是图像生成类的任务,也就是我们今天所关心的内容。

这时有人就说,直接收集大量的文字和图片作为输入输出,硬生生把这个神经网络训练出来不就行了。想什么呢?要是能这么容易训练出来就好了。

要知道即使是文字到文字的映射,尚且是在transformer架构提出,并且由ChatGPT大力出奇迹,才得到了令人满意的结果,更别说跨模态的文本到图像了,所以还是得一点点拆解来看,不能一步到位。

文生图的具体架构应该怎么拆解设计呢?我们直接扒开经典的开源扩散模型stable diffusion,看看它内部长啥样。

第一步,将输入的文字提示词经过一个文本编码器转换成向量的形式。第二步,经过扩散模型,对一个纯噪声图像根据文本向量的指引,逐步去噪。经过多次迭代后,逐渐生成清晰的图像。最后,经过图像解码器,将图像放大到正常尺寸。这样一个文生图的间距任务就被拆解成了三个子任务。

其中,文本编码器用的是clip中的文本编码器部分。我在"AI为什么数不清图片中的手指?"那一期讲过,它统一训练了两个文本和图像的编码器,并且让它们在同一个向量空间中对齐。直接使用clip中的文本编码器的好处就在于编码后的文本已经按照它所能表示的图片在空间中对齐了,降低了后面训练的难度。

最后的图像解码器用的是vae中的图像解码器部分。vae简单来说就是一个图像编码器和一个解码器,先将图片压缩,再还原目标,尽可能还原出原来的图像。如果能训练好这个模型,那就相当于我们用一个小小的向量就能表达一张大大的图片,我们用这个小小的向量给模型做训练,也相当于降低了训练的难度,也就是这个中间的扩散模型,不用再拿着特别大的原始图像做训练了。

有了clip和vae这两大模型辅助,中间的diffusion模型压力就小很多了。接下来我们重点看看它是怎么做的。

其实早期,这一部分由生成式对抗网络GAN一统天下,发音叫/ˈɡæn/。它有一个用于生成逼真假图片的生成器和一个判断图片真假的判别器。两者互相较着劲儿训练,但判别器是陪跑的,最终目标是训练好生成器,让它生成逼真图片。然而,一方面训练两个网络难度大且不稳定,另一方面由于训练目标是接近真实图片,其创造性和多样性往往不足。

因此在扩散模型出现后,GAN慢慢退出了历史舞台,连论文标题都毫不留情。扩散模型究竟是什么呢?别急,接下来几句话给你整明白。

如果让你直接生成一张图片,那太难了。但是如果让你从一张稍稍带有一点噪音的图片出发,是不是就没那么困难了?再来一下、再来一下、直到整个图片已经接近纯噪音了。把这些简单步骤拼叠起来,不就相当于从无到有生成了一张图片吗?也就是说,如果我们能训练出这样一个模型,只给一张图片去掉一点点噪音,只要不断迭代调用这个模型,最终就能生成一张完整的图片了。

好了,那接下来我们看看怎么训练一个这样的模型。

刚刚我们一直说噪音,噪音到底是什么呢?很简单,噪音就是一堆数字而已。同样在刚刚说图片的时候,我们拿其中一个像素点举例,其实每个像素点的数值和噪音值做加减法而已,这里的去噪过程是减去噪音。其实只要知道噪音就能通过去噪过程还原图片,那我们的模型只要预测这个噪音就可以了,无需预测还原后的完整图片,这样训练过程又简单了。

那如果你只看一个像素,这个模型就可以描述为输入是125,经过什么样的函数才能让输出等于9呢?好了,现在的问题已经足够简化了,但想写出一个标准公式还是太难了,咋办呢?遇事不决上神经网络。

很多结构都可以完成这个目标,早期使用的是UNet结构,后来逐渐替换成了现在当红的transformer。因此现在由transformer构成的diffusion模型也叫做DiT,也就是现在文生图和文生视频的主流架构了。

如何训练这个函数呢?训练该函数的方法很简单,准备大量图文对,主动逐步给图片添加噪音,并标记加噪时间步;取出其中一张图片及其真实噪音,连同时间步和对应文字作为模型输入,经过模型计算,得到预测的噪音。与真实噪音比较,计算损失函数,进而反向传播训练神经网络,其余步骤交由大力出奇迹。注意,图片是经过VAE压缩后的向量,这是模型中核心预测噪声的模块。

回顾一下,一张图片本质上是一堆像素值,直接作为一组数字送入神经网络,即可作为输入和输出。如果简单粗暴地训练文字到图片直接映射的神经网络,太复杂。我们需要拆解任务,简化训练难度。在输入端,可以使用CLIP的文本编码器,将文字转换成词向量,因为CLIP已将文字和图片向量对齐,对训练友好。在输出端,可以使用VAE的图像解码器,将图像解压,以缓解扩散模型训练难度。最后,中间的扩散模型也经历了从一统天下的GAN模型逐渐演进到扩散模型的过程。扩散模型其实就是根据加噪声的图片预测其添加的噪音是什么,用于预测噪声的核心神经网络模块同样经历了从UNeT进化到Transformer的过程。最后,这个由文本编码器CLIP、图像解码器VAE以及使用UNeT或Transformer的扩散模型Diffusion构成了现在文生图的基本框架。文生图模型并非全新技术,而是在前人工作成果上的创新。

不过话说回来,AI为什么画的表都是10点10分呢?其实原因很简单,其训练数据都是10点10分的表。比如你现在打开一个电商平台网站,搜索手表,会看到几乎所有手表都指向10点10分,这是因为人们发现10点10分非常美观,且形状像微笑的表情,能给消费者带来积极的心理暗示。慢慢这个习惯就被保留下来了,没想到在人工智能迅猛发展的今天,这为文生图领域形成了一个非常强的偏见,细想起来是不是有点蝴蝶效应的恐怖?

另外,在AI数不清手指那一期视频,我也讲了,在图片问答任务中,同样使用了CLIP作为图片和文本的编码器。在其论文中也提到了,确实在一些精确的任务中存在局限性。我们也发现,无论是识别图片中的手指数,还是画出特定方向的指针,以及前段时间生成让奥特曼数数的视频等,所有这些需要精确的任务在现代大语言模型架构中均存在问题。因此,沿着现在的架构思路演进,这个问题无法根治。

7.张量到底是啥?

这是一个数字0,把它放到内存中,再用一个变量指向它,你就成功在计算机中表示了一个数字。但假如你想表示一组数字,该怎么办呢?很简单,还是把这组数据按顺序放到内存里,记住起始位置和元素个数这两个值就可以了,我们把这种结构叫做数组

仔细看一下,现在这堆数字是很死板的,排成一行的,像个呆木头似的,没什么结构。如果我想把它变成三行四列,该怎么记住这个结构呢?很简单,把之前简单粗暴的元素个数改成各个维度的元素个数就可以了,内存中的结构可以完全保持不动,这个结构就叫做二维数组

顾名思义,二维数组有横竖两个方向的维度,这里抛个小问题,二维数组和二维向量,这两个"维"字含义相同吗?

回到正题,如何找到其中的某个元素呢?比如我们要找这个6,它的位置是第一行,第二列,我们可以用a12这样的符号来表示它。那要怎么样根据这个符号找到它在内存中的位置呢?

我们先来看一下数据的结构和内存位置的对应关系,在第一个维度上,每增加一个单位,内存中实际上是跳过了四个单位,我们把这个4叫做第一个维度上的步幅stride。

同理,在第二个维度上stride等于1,也就是内存中都是紧挨着的。所以刚刚的a12可以分别乘以每个维度的stride,再求和得到最终的内存偏移量,即1×4+2×1 =6。

二维数组搞定了,三维数组自然就水到渠成了,保持内存中的数据不动,把shape调整为(2,3,2),最终得到的数组是什么样子呢?我个人通常喜欢这么想,第一个维度是2,所以最外层先分成两份,第二个维度是3,所以上面的每一份再拆成三份,最后一个维度自然拆分,然后把它们选个好看的样子摆放起来就可以了。

但是这样有个坏处,维度一旦超过3,我们的大脑就承受不住了,而且就算只有三维,也涉及到长宽高的顺序,容易搞混。所以我通常还是喜欢还原成一维,通过分组套娃来理解。

我们再举个shape(2,3,4)的例子,让每个维度的元素个数不同,方便观察。第一个维度是2,所以先分成两份,左边就可以用a0表示,右边可以用a1表示,也能清晰地看到它的步幅stride等于12。

再以左边这个为例,第二个维度是3,所以呢,再拆成三份,分别是a00、a01、a02,同样,可以清晰地看到它的步幅stride等于4。

再以中间这个为例,最后一个维度是4,拆成四份,同理可以用a010、a011、a012、a013来表示。stride等于1,这就定位到了最终的元素。

在此之前,我们先梳理一下前面的内容,一个数字叫做标量 ,也可叫零维数组 ;一组标量叫做向量,也可叫一维数组 ;多组向量叫做矩阵 ,也可叫二维数组。前面是数学中线性代数的叫法,后面是计算机中数据结构的叫法。

专家(一拍脑袋):在深度学习中,核心是大量的向量矩阵之间的计算,所以深度学习专家们认为向量矩阵都是线性代数的概念,多维数组显得太low了,数学和物理中有张量这个词,可以表示好多维的数据,我们以后就用它了。

于是本质上是多维数组的数据结构,在深度学习框架中,就被叫成了张量,这里的"阶"多维数组中的维。不论是书籍、论文,还是代码中,都沿用了这个习惯,TensorFlow甚至连框架名就叫TensorFlow,但要知道,它们的本质仅仅是多维数组而已。

首先我们要创建一个张量,方法有很多,我们选torch.range() ,创建个等差序列的一维数组,它在内存中也是一样的格式,接下来我们调用一个**reshape()**方法改变一下这个张量的形状,比如改为3x4,也就是把原数据信息中的shape改成了3x4。

如果reshape为2x3x2呢?那就是先分成两大份,再每份分成三份,最后一个维度正好是俩元素,仍然是内存数据完全不动,原数据信息改一下搞定。

回到之前这种简单的情况,再调用一个**transpose()**改变维度的顺序,比如我们把第零维和第一维的顺序互换,在二维情况呢,就相当于一个转置。

那你觉得此时我们能不能不改变内存中的顺序,仅修改原数据信息,就表达出这个逻辑结构呢?可以的,只需要把shape进行互换,再把stride也互换即可。

我们以第二个维度为例,逻辑顺序是0、4、8,那内存中呢,要按照这个步幅stride进行跳跃读取,同样也是0、4、8。可以看到,此时和之前的各种情况有个本质的不同,就是张量不再是连续内存,即按行优先的逻辑顺序和内存顺序不一致了

我们可以增加一个属性contiguous 来记录这种不连续的情况,在contiguous等于false的情况下,一些操作无法保持内存数据不动。例如,此时调用**view()**并再次改变形状,会收到报错提示。

应使用**reshape()**操作代替,此时的reshape操作会重新拷贝一份内存数据,并在新内存数据进行reshape,使contiguous恢复为true。

若想用view操作但遇到不联续的情况,怎么办?其实,reshape操作默认情况下与view效果相同,但在遇到不联续的情况时,它会先重新拷贝一份内存,再进行view操作,从而不会直接报错。所以我们只需在view之前手动操作一下contiguous即可,这个方法叫做**contiguous(),**然后,就像之前一样,先创建一份新的连续内存,重新调整形状。

我们再回到最简单的一维数组情况,还有个常用的操作是从原数组中取出一个切片。例如想取这一段可以写成[3:10:1],表示从第3个索引开始取到第10个索引结束**(不包括)**,步幅为1。

如果stride改成2,那么原数据信息该如何修改呢?首先,只有四个元素了,所以shape改成4,stride变成了2,同时,内存已经不连续了,所以contiguous变成false。但此时还有个信息缺失了,即第一个元素的起始内存地址变了。这怎么办呢?再加一个原信息叫offset即可,对原始内存数据的偏移就好了。

总结一下,深度学习中用张量这个词表示单纯的多维数组,为了方便大家写代码,诸如PyTorch这样的深度学习框架设计了tensor库来操作张量。张量在内存中尽量只保持一份,通过storage索引到初始内存地址,并通过shape、stride、contiguous、offset等语言信息记录结构,然后再基于此进行各种张量的变换操作,如改变形状的shape、view,改变维度顺序的transpose,重新复制出连续内存的contiguous以及切片操作等。

8.Agent智能体到底是啥?

先问大家一个问题,你平时大部分时间是怎么使用AI的?我相信大部分人和我一样,打开ChatGPT对话框,和AI一问一答。没错,这就是大模型的最简单使用方式。此时,大模型就像一个固定的大函数,每次接受一个新的问题输入,然后给出答案并返回,整个使命就这样结束了。

它的内部不能自发地与外界产生交互,比如联网搜索,它只是个单纯的神经网络,你可以把它想象成另一个被关在小黑屋里,啥也干不了的人类,只不过它有个聪明的大脑。为了让它发挥更强大的能力,我们应该怎么办呢?这其实不是个技术问题,而是一个沟通问题。其实只需要在原来的问题后面加这样一句话,询问它是否需要帮忙补充资料,这时AI发现自己不知道XXX是谁,就会在返回的信息里寻求帮助,然后你按照它的要求上网搜集信息,然后把搜到的信息再次发给AI,这时AI就可以回答你了。

有人说,这不就是和AI进行了两轮对话吗?没错,但是你把中间这一来一回看成黑盒的话,其实AI已经具备了与外界交互的能力,这就是智能体agent的雏形了。这个黑盒里面不还是需要人工介入吗?这哪是agent,全是人工在操控啊。所以解决办法也自然就来了,我们把这个人工的地方替换成程序,不就解决了吗?

很简单,那要怎么换成程序呢?我们得分析下人和程序有啥区别。"帮我查一下XXX的资料,搜关键字XXX"这句话说给人听,是完全没有问题的,正常智商的人都知道打开浏览器然后去搜索XXX,所以人本身就是个超级智能体。但是对于程序来说,可不行,代码只认死理,必须约定好死板的格式,否则就直接任性报错了,所以说这句话得换成下图这样:

为了让AI乖乖地回复这样的格式,那么提问也不能这么口语化了,需要改成下面这样:

这其实也是和AI交流最好的方式,需求越明确,它执行得越好。综上,整个流程就可以换成下面这种方式:

如果把中间这一大坨全都看成一个黑盒,就和刚刚是一样的效果了,AI似乎有了联网能力。

那么这个AI + 专门用于和AI交互,以协助其感知外部信息的程序,就叫做智能体agent。

重新展开这个agent,你觉得还有什么问题呢?现在这个agent只支持联网搜索这一种能力,而且最多只能多出一轮沟通能力,还是太低了。我们希望事先准备好几个功能的函数,根据需求调用,并设计一种机制,使AI能多轮沟通,直至满足所有外部信息需求。

我们需避免口语化沟通,需要设计一套极其呆板的提示词如下:

首先定义沟通格式,然后描述沟通流程,最后提供可使用工具列表。

简单来说,根据用户问题<question>,思考接下来的行动是否需调用工具,如需调用工具,则将具体调用信息放入<action>中;接着等待用户返回,将工具调用结果放入<observation>中;不断循环思考、调用工具、获取返回结果的过程,直至认为信息足够,返回最终结果。

举个具体例子,如下:

1、首先,将提示词与用户问题放入<question>中发给AI,

2、AI进行思考,将任务拆解为两步,并决定先调用网络搜索工具,具体信息放入<action>中,

3、程序执行搜索逻辑,将返回结果填写到<observation>中。

4、然后AI再次思考,决定调用读取文件工具,具体信息再次放入<action>中,

5、序执行读文件逻辑,仍将返回结果写入<observation>中。

6、接着AI再次思考,发现无需继续调用工具,因此将最终结果放入<answer>中结束。

此时,若将此过程视为整体,我们可称之为智能体,但从外部看,仍是一问一答形式。

我们用时序图总结整个流程:

1、用户向智能体提问

2、智能体内部程序向AI大模型说明工具列表和沟通流程

3、AI返回本次需调用的工具

4、程序内部调用工具得到结果,并将结果返回给AI

5、AI再次判断是否需要调用工具,并进入循环

6、直至认为信息足够,跳出循环,并且AI将最终结果给程序

7、程序识别流程可终止,提取最终结果返回给用户

这就是大名鼎鼎的react模式,是众多agent运行模式中的一种,也是最有名的一个,它源于2022年的一篇论文,你可阅读这篇论文,了解如何将与AI对话变得有趣。

现在与大模型完成这套动作,完全通过一大段混在一起的自然语言,听起来不太靠谱。我们可以将用户提出的问题单独提取出来,称为User Prompt ;再将系统本身的角色定位、流程相关的提示词提取出来,称为System Prompt ;最后,将调用工具、自然语言描述用一套JSON格式规范起来,这就是Function Calling技术。

目前,这些函数都写死在agent主程序里,直接调用很方便,但耦合度较高,且无法复用。若将其单独抽离出来做成一个服务,并规定通用的对外服务标准,这就是mcp协议

无论两边采用何种方式,底层仍然无法避免不断尝试,让概率模型给出确定性的答案,这一点始终存在问题,不可靠。

关于agent的主程序,可以参考下面这个开源项目,它以最简单的方式实现了一个基于react模式的agent demo,代码不到200行。

你也可以体验一下这些以CLI形式存在的agent工具,简单直观,比较知名的有国外的Gmini CLI、Cloud Code、OpenAI的CodeX,以及国内主打免费应用的ilow。

其实现在很多听起来高级的ai技术,比如agent、RAG等,说到底都是用不同的技巧和大模型对话,最终还是落实到提示词上。为什么agent执行复杂任务总是不太可靠呢?因为底层仍然依赖大模型的自然语言回应,必须用一大堆严格且不断强调的提示来约定其格式,比如要这样做,不能那样做。虽然后来有function calling和mcp等规范降低了这种不确定性,但仍然无法改变大模型本质上是语言概率模型的事实,无法做到像程序一样完全可控。

所以,今天所有的agent技术离真正的自动化系统还有明显距离,本质上仍然是在语言之上硬塞操作能力罢了,想要变得更稳定、更可控,还是得在模型、工具、调度层上继续演进和优化。

9.Skill/MCP/RAG/Agent/OpenClaw底层逻辑

小的语言模型一开始像个智障,但随着模型参数越来越大,居然在某个临界点涌现出了智能。为了和之前智障模型做个区分,你在前面加了个"大"字,这就构成了现在常说的大语言模型,简称LLM。

大模型本身只能做文字接龙,不断输出下一个字,但如果只是这么用的话,看起来仍然像个智障。那如果把角色区分一下,人为划分成一问一答两个角色,就实现了第一个有点智能的使用方式------对话

现在呢,我不管你是什么身份,立刻把自己想象成一个老板,LLM就是你的员工,我们就叫他小L,只不过他服务你的方式有点特别,只能一问一答,然后就结束了,不能追问(这个非常重要,后面要考)。

接下来的任务,就是你要想尽办法压榨只会一问一答的小L,榨干他的全部剩余价值,那你会怎么做呢?不过先别急,你先给自己每次和小L的对话起了个洋气的名字,叫prompt

然后你还发现这部分内容还可以进一步区分,有的部分是背景信息,有的部分是最终的指示,于是你把背景信息的部分单独起了个名叫context

同时,有的时候你需要对小L进行追问,但是刚刚说了,他只能一问一答,不能追问。但是你想了个巧妙的办法,每次沟通前把你们之前的对话历史放到context,作为上下文信息,然后呢,再给出你的问题,伪装成多人对话。然后你又迫不及待给这些特殊的上下文信息起了个新词,叫memory,意思是大模型的记忆。这些memory还可以再次调用大模型进行总结,从而对它的记忆进行压缩,进而减少上下文的长度。

此时,一个原本只能进行词语接龙的小L,就成功被你玩出了可以对话,并且可以不断追问的优秀员工了。

当然,不久之后,你就不满足于现状了。你发现的第一个问题就是,小L没有上网查阅资料能力,要么就不知道,要么就胡说八道,说的内容都是些过时的消息。这很简单,给小L准备一台电脑不就可以了?

不可以,还是那个问题。小L本身只会词语接龙,其他任何逻辑都无法独立完成。那怎么办呢?

好办,你就告诉小L,如果你需要上网搜索资料的话,就告诉你,然后你帮忙查完资料后再给他不就行了,但很快你就发现这样好像显得自己有点蠢,到底谁才是牛马呀?

于是你将上网逻辑编写为一段程序,让该程序代理你与想要沟通的对象进行交流,并完成搜索任务。在外人看来,你仍是一问一答就拿到了结果,只不过面对的是这个神秘的程序。这个发明非常了不起,这个神秘的程序似乎拥有了智能,而且是能操作工具的更高级别的智能,你将它命名为智能体agent。一些早期所谓的智能体逻辑,仅仅多加了一段prompt而已,从现在的视角回看,当时简直就是一种诈骗。

回到这里,既然agent能上网搜索内容了,那么是否也可以增加搜索本地文档或数据库的能力呢?可以的,只不过搜索方式与传统数据库不同,需要使用向量数据库,找出语义相近的片段。你将这种通过语义匹配向量化的信息,并将其加入上下文以增强生成内容可靠性的方法称为检索增强生成retrieval Augmented generation,简称RAG。

刚刚提到的联网搜索,也起个名字叫Web Search,连rag也算是search的一种了,都属于获取模型参数以外的信息的能力。

现在的整体架构是你和小L之间隔着一层agent程序,用于减少你和大模型直接沟通的次数,并处理一些小L无法操作的事情,包括刚刚的搜索以及可能出现的其他各种工具的调用。但这就有个问题,我们聚焦于agent和大模型的对话过程来看,如果这部分一直用自然语言来沟通,那么agent代码将很难用程序来实现,谁知道大模型会怎么描述自己的需求呢?

所以最好有个约定,让大模型按照指定的死板格式来回复,比如说json,这样程序就能直接、很方便地解析了,你给agent和大模型之间关于工具调用所约定的对话格式,叫做function calling。 其实这只是个约定而已,就好像开发时前端和后端约定接口格式一样。

我们再看看这些工具的实现,现在是写在agent的主程序里面的,没有跟核心功能解耦。如果单独写成一个服务,那么agent的主程序如何发现并调用这个服务,就又需要一套约定的规范了。比如说约定好tools/list的方法,就是返回工具列表;tools/call方法,就是调用具体的工具等等,也就是一套约定而已。你给这边的约定也起了个名字,叫做mcp,翻译过来叫模型上下文协议。

至此架构就变成了下面的样子,此时大模型就像个只会说话不会做的智者,而mcp服务能提供各种工具的程序,中间的agent就是个传话筒,把大模型的话转换成调用工具的代码,把工具调用的结果再原封不动地传话给大模型。

现在我们聚焦于agent与你的对话之间,虽然最底层是文字,但交互形式可以丰富多彩,可以是像CLI一样的命令行窗口,也可以是编程IDE工具,还可以是更为通用的桌面助手,比如最近爆火的CloudBot、MotBot、OpenClaw,当然这三者是一个东西。

这里说句题外话,我感觉cloud code这个名字起得太失败了,一开始好多人认为它是个大模型的名字,但其实它是编程agent,现在,好不容易大家接受它是编程agent了,但它早就走上了通用agent的道路。我感觉要是名字起好了,就没现在的CloudBot什么事了。

不论什么形式的智能体,都有一个统一的缺点,假设我们想完成这样一个任务:从一个英文pdf文档当中提取内容,翻译成中文,最后保存成markdown格式。你可以直接把这个需求描述给agent,让它自己策划整体的流程。

但每次重新让agent自由发挥的话,不但不稳定,还非常浪费token。比如整个流程中提取pdf和保存markdown这两步,完全可以固化成固定的脚本,中间的翻译直接和大模型沟通即可,整个流程就不需要任何一个中间的智能体插手了。要固化这样一个流程,你可以通过编程的方式来实现,为了方便编写这种链式的任务,你又发明了一个新的编程框架,起名叫Langchain

为了照顾非程序员用户,你又发明了一种低代码的方式,在页面上傻瓜式地拖拽,上手难度更低,你给它起个名字,叫WorkFlow,即工作流。

但是还有个问题,假如这个问题我们再变一下,比如说处理原始文档,不只有pdf,还有可能是word文档、text文档、ppt等,然后输出的格式,也可能是html、pdf,甚至是一张图片,那么难道你要给这些所有的排列组合都写一套工作流吗?这显然是不合适的。

当然,你也可以写一堆if else做判断,但如果你仍然希望用户是以自然语言的方式触发这个任务,不牺牲这个体验,这个时候就又不好用程序来判断分支了。那该怎么办呢?

你可以这样设计:准备一个目录,把所有可能涉及到的转换脚本全都写好,放在这儿,然后呢,写个统一的说明文件,把整体的流程描述清楚,并且告诉agent,根据文件的格式灵活选取指定的脚本。然后再给agent下达任务之前,加上这么一句话:先读取刚刚我们写好的那一大串要求,然后再按照要求完成任务。这样整个过程就既保证了一定的灵活性,同时呢,又变得比较可控。

但是这不就又来了优化空间吗?我可以提前约定好某个指定的位置,在agent中编写固定程序以读取特定位置的skill.md,相当于将这句话固化成程序,这样无需每次重复。虽只是将提示词换位置存储,但不妨为其命名为skill,即agent的技能。

现在agent已被设计成下面这样的结构了:

对于复杂任务,其上下文可能变得非常大。于是,你提出了新概念sub agent,用于处理一些独立的子任务,这些子任务可单独在子agent中完成,本质上就是做了上下文隔离,sub agent产生的上下文不会保留在主agent中,仅此而已。

好了,回顾一下你这些设计。

mcp刚出现时,很多人问function calling和mcp有什么关系?

刚刚的图已经很清晰了,一个是agent和大模型沟通的约定,目的是让大模型回答符合一定格式,方便程序解析;另一个是agent和工具服务之间调用的约定,像接口文档一样,约定如何调用、传参、接收返回值等。这两者完全不相关,甚至有人问mcp是否能取代function calling,这是无稽之谈。

skill刚出现时,也有人问skill和mcp有什么区别?

skill是prompt加载器,只需skill.md文件,对其他无要求,与mcp不是同一维度。那么,skill是否能取代mcp呢?我认为可以。因为可以把mcp服务中的工具放在skill目录下,并在skill.md文件中说明使用方法,特别是常用的小工具未来都会内化在agent主程序中,所以目前看,skill稍显鸡肋。

不过,将skill和mcp比较也不合适,两者不在同一层次,它更应该和下面这些词做比较:

我把这些顺序称为从刚性到柔性,从稳定到变化,他们的目标其实都是一个需要多个阶段才能完成的任务。

1、使用Langchain纯编程形式实现全是硬编码,虽稳定但缺乏柔性,难以包容小问题。

2、而workflow只是把程序替换成了低代码的拖拽,相对容易修改。

3、skill则是将Langchain和workflow这种由程序控制的流程走向,变成了由智能体自行控制。即提前写好说明文档和直接可运行的脚本,存在一定灵活调整空间,同时又不至于变得特别不可控。

4、最后的纯Agent的形式最为柔性,因其可随时根据判断调整,甚至需用时自行生成脚本运行,但这也容易导致不可控,其可能编写任意脚本,将简单任务复杂化。

因此,我认为这是他们宏观上的区别。至于Skill的渐进式披露、按需加载,我认为只是其特性,该特性在未来Token价格变得越来越便宜后,会显得鸡肋。

对普通人而言,Skill兼顾了灵活性和稳定性,我认为会逐渐淘汰MCP和Workflow。MCP作为常用工具,我认为会直接内化到Agent主程序中,或在未来基础Skill包中变得鸡肋;而Workflow既不如Langchain适合程序员,也不如Skill适合普通人,也属于鸡肋存在。

当然,Skill我认为本身也是中间产物,未来一定会有更方便的形式出现,让所有人都能符合直觉地无脑使用。

从最本质的角度说,所有这些技术最终还是离不开大模型和我们之间的提示词,这些技术无非是帮助我们自动在提示词中增加上下文信息,比如Search、Rag、sKill等,都是把一堆内容塞进上下文,或通过代理形式帮助我们减少与上下文沟通的次数。

那为什么开头我说Agent是所有不需要智能的地方构成的部分呢?一个流程中所有能用固定程序解决而不需要问大模型的地方,就是Agent发挥作用的地方,即:把模糊的分流逻辑交给大模型,根据语义识别出用户想做A还是B,把确定的分流逻辑交给程序,比如PDF提取文本,最终目标都是节省人类时间,降低使用门槛。

但现在最大问题是Token实在太贵,越是强大的能默默处理问题的Agent背后消耗的Token就越大。不过我觉得未来这或许不是问题,因为Token一定会越来越便宜,甚至等到生产级别的大模型能轻松部署在普通电脑上时,Token就相当于免费了。

由此我想到了Java领域的Spring Boot和Python领域的UV,你会发现这两者都将开发者的便利放在第一位,运行速度快不快、包体积大不大、是否浪费内存空间或磁盘空间等问题,最终与使用便利性相比,几乎都是瞬间被秒杀。

程序员尚且怕麻烦,而在Agent领域,我认为更是如此,因为它面向的是普通人,不可能让普通人把Skill放到指定目录、配置MCP服务,甚至配置哪个大模型的API key。这些都会被极为便利的产品淘汰掉,比如最近的Cloud Bot,为什么这么火?除营销因素外,它与cloud code、codex manus等agent无本质区别,仅因能连接社交软件、配置定时任务、有页面查看并管理skill,才让普通人首次觉得它像智能体,而非仅是电脑上的服务。

未来会是怎样?我认为提供便利的方向即为趋势。比如未来定有打包好的超级agent,配置好所有常用mcp、skill等,普通人无需配置,即可直接使用。

10.OpenClaw的一次对话流程背后到底干了什么?

OpenClaw与普通Agent无本质区别,可部署在本地,也可部署在云服务器,最大不同是可接入社交媒体,通过发消息触发Agent执行任务。

我的组合是腾讯云轻量服务器+OpenRouter大模型+飞书,操作起来很简单,先购买腾讯云轻量服务器,在应用管理里配置大模型api key,在飞书开放平台新建机器人,并在事件与回调中开启长链接订阅方式,回头在通道配置里填好飞书机器人App ID和Secret Key。

然后就能在飞书里发送消息,进而触发OpenClaw在后台执行指令。

举个例子,先看这条消息,飞书消息发出去后OpenClaw服务器收到消息,这里最重要的信息是role字段 ,其中user 表示我们输入给大模型的话;assistant 是大模型回复的话;toolResult是agent调用工具的结果,即说给大模型的话。

接下来重点看它们如何沟通的?

首先是user,即我说的话:你有多少内存?;

然后是assistant及大模型的答复,它分了几个角度来回答,且在第三点分析出我想问的是服务器系统内存;注意这里有一个toolCall类型回复,即告诉agent需帮忙进行工具调用,格式也很简单,就是方法名exec+参数"free -h",这个exec是OpenClaw内置的函数,用于执行shell命令,具体执行的命令free -h用于查看系统内存。

最后也可看到这轮共消耗14032个输入token和294个输出token,且是第一次,没用到任何cache。我们还可切到OpenClaw后台,看到这里的token数是对应的上的,这一轮沟通大概花费0.1美元。

不过还没完,此时agent执行刚刚命令后,再次给大模型发送消息,即toolResult返回工具结果,即"free -h"的输出。

大模型收到结果后,决定不再继续调用其他工具,就最终给用户一个结论,即我手机上收到的消息。

根据两轮对话的token消耗来看,有个问题:为何询问内存大小后,输入的token就上万了呢?因为OpenClaw有一大堆系统提示词,新会话的第一轮会直接注入进去,在机器上可以直接找到这些文件,例如agent.md中就有大量指令,如下所示,可认真阅读感受一下。

一个agent功能强大,是靠堆叠大量提示词完成的,甚至读取文件和写入文件等操作也是直接用自然语言注入进去的。

接下来再看一个复杂一点的例子:将今日的AI新闻整理成PDF发给我,看看后台如何完成任务。

可以看到仍是user 角色起手问题,即我在飞书上发的那个问题,assistanttoolResult一来一回交互,即大模型一直在请求agent调用工具,共进行了将近70多次对话。

我们可查看一下所有thinking字段,基本可明白发生的事了,首先是粗暴的Web Search,直接搜索今日新闻;然后尝试转换为PDF;在转换PDF过程中,发现需将Markdown文件转换为PDF,......等等一系列过程

我们再尝试触发OpenClaw的另一个独有功能------配置定时任务,如监控一个人的推特,有消息立刻告知。实际上在后台增加了cron配置文件,每次定时定点将提示词发给agent,没什么特别。

再看看后台日志,仍像个执着而倔强的实习生,死命完成任务,不惜弄乱整个系统;又是接口各种不通、返回值不符合预期、安装命令各种失败等。其实更希望它在第一步思考时就主动告知,有更好的办法,如使用RSS.app这样的工具,但要付费,可跟我商量,而不是有问题一直不反馈,强行干下去。工作中是不是也特别怕遇到这样的人?

现在的agent怎么形容呢?举个夸张但又形象的例子你就懂了:

若老板询问今日日期,你将其视为任务,需查看手机时间,却发现手机无电,于是寻找充电器充电,但充电器丢失,便决定去超市购买;到达超市时发现已关门,分析后决定前往另一家超市,并打车前往,到了另一家超市,下车时发现手机无电无法付款,且零钱不足,于是分析后决定去银行取钱;你到了银行,取钱后发现金额不够,无法下车,若按早期逻辑可能陷入死循环,而现在可能稍聪明些,决定先将手机抵押给司机,之后你成功下车去超市购买充电器,却发现手机已抵押给司机,于是需购买新手机,但银行钱不够,于是决定去银行贷款,最终,你花了一整天时间,丢掉了旧手机,在银行欠下贷款,购买了新手机,并告知老板今日是2月6号。

11.Harness是什么东西?

回到2023年,那个时候AI还只是个聊天机器人,我们来场角色扮演的游戏,把它想象成一个刚入职的员工,而你就是那个无良的老板,整个AI对话范式的演进史,就是你们俩斗智斗勇、相爱相杀的过程。

最开始的时候,员工虽然很聪明,但是没什么工作经验,可以理解为是个实习生,你下达的指令非常模糊,所以得到的答案往往不是很理想。因此,这个时候关于提示词的优化开始流行起来。有的方法是把提示词说得具体一点,对输出的格式做了要求;有的方法是提示他先思考再回答,比如在结尾加上一句"think step by step",这就能让模型的推理能力提升不少。总之,人们研究各种办法和话术,让这个小萌新回答得更好。

这种通过直接优化提示词来激发模型潜力的时代,被后来的人们称为"Prompt Engineering",即提示词工程。

后来模型能力越来越强,实习生进化成了老油条,你不再需要太多提示词技巧,也能得到一个不错的回答了。但这个时候你发现,有的时候不是你的指令不够清晰,也不是模型能力不行,而是缺少必要的上下文信息。

所以说问题的关键其实是上下文信息的补充,这里的上下文补充方式可以非常灵活,可以是手动写进去的,也可以是"RAG"这种方式动态查询向量数据库;还可以是调用工具返回的结果;还可以是现在的Skill、Memory、历史对话压缩等等,其实都是围绕着上下文的填充想办法。

之前折腾提示词那点事叫做提示词工程,那现在的范围更大了,咱不得造个新词吗?不然怎么样体现工作量呢?就叫"Context Engineering",即上下文工程。

"Prompt Engineering"可以理解为包含在"Context Engineering"内。

想象一下你手下的员工已经非常厉害了,能给他的资料也都给齐了,那接下来你觉得还有什么问题呢?除提供必要信息和工具外,还需对他进行限制,包括权限收敛、规范制定、颗粒度对齐等。这就是你老板专门为你准备的"牛马套餐",在AI领域可起名为"Hrness engineering",即驾驭工程。

Prompt Engineering就是调整模型说话方式,Context Engineering为模型提供必要信息,而Harness engineering则制定约束。

Harness engineering词根本就不是一个被严格定义出来的概念,一开始就是一个人随手写的博客,写得还挺有感情的,后来OpenAI和Anthropic分别在官方文章中使用了这个词,于是便流行了起来,就像当年卡巴奇的Vibe coding一样。

最初,模型本身的语言能力尚不完善,我们经过努力终于在语言层面实现了对其的驾驭;

随后,我们开始研究如何与其进行更有效的对话,并开展了提示词工程,逐步在沟通层面上实现了对其的驾驭;

再后来,我们用agent来代替沟通和调用工具,这时范式发生了巨大转变,驾驭大模型的工作转由agent完成,与此同时,我们又增加了新工作,即驾驭agent;

如今,我们已经将之前的三个概念从拆分维度上转变为两个不同层面的驾驭,而Harness engineering就蕴含在这两个驾驭之中。

先看agent如何驾驭大模型的,这就等于agent本身的设计思路。原本可能还没有最佳实践,但自从cloud code源码泄露后,这方面算是被迫补齐了,具体可以参考源码或相关解读,很多人提到的Harness实际上是把这部分也包含进来了。

对于使用者来说,更有用的其实是你如何驾驭Agent,目前还没有公认的最佳实践,各大厂都在积极探索。不过,从大类上看,要么是通过个人形成一套使用习惯;要么是通过类似Spec框架等中间层再次进行驾驭对象的转移。

先说个人使用习惯这部分,这因人而异。比如我有小需求时,就临时打开一个cloud code窗口,做完就直接关掉;如果是长期维护的项目,要么保持窗口不变,等填满后再压缩一下,要么关掉后重新开窗口,但在这之前,我会先让他自己读一遍项目,做个了解,然后再提需求。

其实,我的这种驾驭方式也是一种驾驭,后来由于经常重复这个过程,就优化了一下,直接让他阅读完整个项目后,进入到一个说明文档里,这样下次再启动新窗口时,就不用再重复读整个项目了,只读说明文档即可,如果项目代码有更改,同步更新下文档就行。这就是Anthropic所提出的Harness设计的一个关键思路,即不要压缩上下文,而是重启一个新的agent,通过传递前一个智能体达成的状态来完成交接,解决处理长任务时容易失去连贯性的问题,更多的设计思路,可以参考这篇文章。

再说Spec框架,拿简单的OpenSpec为例,你可以简单把它理解为三个skill,分别告诉agent提出需求、实现需求、归档需求。完成这三个操作后,如何把状态、信息记录在磁盘中。本质上是对目录下内容的操作,需让所有人遵循共同规范,过程较为简单。许多编程IDE也支持类似思想,只是名称多样,比如:Cursor里叫Plan、Kiro里叫Spec、Trae里又叫SOLO。即便你不使用框架和IDE,只要能确保大项目在跨agent环境下持续运行,那你所做的工作就是Spec。

驾驭agent的过程与公司成长历程极为相似,当公司只有一人时,无需harness,个人能力至关重要;当有100人时,需成立组织来驾驭个人,此时组织沟通和协作能力至关重要,个人主义就要靠边站了;当有1万人时,需设定规范、约束等更抽象的东西来驾驭各个组织,此时个人能力和组织内协作相对次要,稳定持续、不出问题才是核心。作为老板,只需管理规章制度和处罚措施,公司运转可交给Harness机制来保证。

技术与驾驭之间的边界在不断变化。当模型能力增强时,一些知识会内化为模型常识,agent层可少做些事。例如,模型若知道remove命令可删除文件,agent便无需再提供skill或说明文档,同时,与模型直接对话的prompt也可更简洁。

当agent能力增强时,驾驭agent的工作可减少。例如,cloudcode开始支持记忆功能,相当于将通用跨agent session能力内化,若未来OpenSpec变得通用,也可能内化到agent内部。同理,当人类发现通用但仍需手动操作的场景时,厂商会将其内化到spark框架层,如项目开发流程,各公司情况相似,因此OpenSpec才有机会成为通用工具。

未来,最终目标是让人类无需手动操作,只需一句话便能实现目标,将各种能力内化到各层级。

从更高视角看,驱动循环进行的因素有两个:一是人类懒惰,一切能写成sop的东西都会内化成工具和框架。第二功能会下沉,一旦一个功能达到一定的通用程度,就会下沉到底层,变成基础能力,所有的技术演进大概都是如此。

而Harness就是让不可控的强大智能朝着安全、稳定、可控的方向发展的各种办法,在其他非agent领域,大概也是如此。

视频最后,我闲聊几句个人感悟,帮助大家更好理解创作这期视频的过程。我也参考了很多资料和讲解视频,它们通常会把提示子工程、上下文工程和现在的哈尼斯工程放在一起讲。

其实往后,如果OpenSpec等框架已经能很好地驾驭agent,那么接下来的经验就会是我们人要如何驾驭Spec,也就是我们如何更好地使用它来进行任务,这就会成为一种新的驾驭方式了,所以说,驾驭这个词仍然随着技术的发展而不断扩大其内涵。

相关推荐
400分4 小时前
Claude Code 进阶使用:Subagents、第三方插件、Hooks、记忆与工作流
人工智能
Zhangr1324 小时前
1Panel + Docker 部署 AstrBot + NapCat:打造你的 QQ AI 机器人
人工智能·机器人
QYR-分析4 小时前
AI边缘计算设备:分类、市场格局与发展机遇
人工智能·边缘计算
米小虾4 小时前
MCP 协议深度解析:AI 时代的「USB-C」接口如何重塑智能体生态
人工智能·mcp
ZhengEnCi4 小时前
O04-马斯克起诉OpenAI世纪诉讼全解析 📜
人工智能
冬奇Lab4 小时前
RAG 系列(三):调对这 4 个参数,让你的 RAG 从「能用」变「好用」
人工智能·llm
夜雨深秋来4 小时前
AI技术落地典型问题分析
人工智能
RxGc4 小时前
# Agent Skills评测:让AI编程工具拥有Google级工程成熟度
人工智能·github
卷积殉铁子4 小时前
OpenClaw不装了,GPT-6硬刚:谁能拿下未来5年AI红利?
人工智能·aigc·openai