从JavaScript看字符编码的前世今生!

b01fd7449dc490c0c18add615e65a7da.png

导语 | 每个程序员都应该了解一下字符编码,有了基础概念之后我们对编程语言、字符处理能有更深入的理解。本文我花了大量时间进行资料查阅和考证,希望能够给大家带来一些帮助,多多交流!

一、起因

最近在研究Babel的源码,在看到Acorn词法解析源码中有这样一段逻辑:

pp.fullCharCodeAtPos = function() {
  let code = this.input.charCodeAt(this.pos)
  if (code <= 0xd7ff || code >= 0xdc00) return code
  let next = this.input.charCodeAt(this.pos + 1)
  return next <= 0xdbff || next >= 0xe000 ? code : (code << 10) + next - 0x35fdc00
}

这段代码初看的时候完全不得其解,看起来像是判断了编码范围,重新计算了一个编码。于是朝着字符编码的方向深入探索了一下,结果发现了一段庞大的历史。

记得在前几年,Emoji的问题还蛮火的,大家都挺奇怪,为什么有的Emoji在JavaScript里的长度是2,还有一个'𠮷'的问题好像更火一些:

ca65eadcfae2fbdb24547715722f4c35.png

二、字符编码的早期历史

(一)从电报说起

在电报还没发明以前,要想进行长途通信,只能通过驿站、信鸽、烽烟这样的形式,需要一个非常高昂的成本。在18世纪,大家开始研究电的特质,以及用电来传输讯息的可能。

(二)摩尔斯电码——最早期的数字化通信

美国发明家萨缪尔·摩尔斯在1836年发明了摩尔斯电码,开启了电报时代,首条真正投入使用营运的电报线路于1839年在英国最先出现。它是大西方铁路装设在两个车站之间作通讯之用。

e45d21629e4b6217ee25f08d98dd151e.png

摩尔斯电码,是一个使用点(·)和划(-)来表示编码的,根据时间维度的间隔,我们可以通过查表解析:

2f0756f98652782a412eabbecdbaad75.png

最早的中文电报,是采用了四位阿拉伯数字作代号,从0001到9999按四位数顺序排列,用四位数字表示最多一万个汉字、字母和符号。

(三)ASCII——开启电脑字符编码的时代

1946年,世界上第一台通用电脑ENIAC诞生,那时候它还是一个电子数值积分计算机,无法进行字符的表示。直到1963年,美国国家标准学会(American National Standard Institute , ANSI )颁布了ASCII编码方案。

我们都知道,在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示。

ASCII码使用指定的7位或8位二进制数组合来表示128或256种可能的字符。标准ASCII 码也叫基础ASCII码,使用7位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0到9、标点符号,以及在美式英语中使用的特殊控制字符。

5d8b7693f79c654df6c10857d2c2e097.png

三、非英语国家字符编码的混乱时代

(一)ISO/IEC 646-7比特的最后挣扎

ISO/IEC 646是国际标准化组织(ISO)和国际电工委员会(IEC)于1972年制订的标准。ISO 646除了英语字母和数字部分,为所有国家相同外,有些使用字母的国家,可按照实际需要,把 ISO 646修改,以定出该国的字符标准。

ISO 646为了表示欧洲各种语言的带附加符号(diacritical mark)的变音字母,由于没有码位空间去直接编码这些变音字母,所以用几个标点符号来兼作变音字母的附加符号:

  • 撇号(apostrophe)兼作尖音符(acute accent);

  • 反引号(backquote、backtick、opening quote mark)作为重音符(grave accent);

  • 双引号(double quotation mark)兼做分音符(diaeresis或umlaut);

  • 脱字符(caret)兼做扬抑符(circumflex accent);

  • 代字号(swung dash)兼做颚化符(tilde);

  • 逗号(comma)兼做下加符(cedilla)

可以看到,在7比特的情况下,很多国家的字符都不够用,会将原ASCII字符替换成自己的版本:

10b72caca232baad3a63ad6f9384f1c9.png

(二)ISO 2022-一个兼容ASCII和大字符的8比特方案

很快人们就发现7比特并不能满足大部分拉丁语言,ASCII本质上是由通信领域发展而来,通信领域的协议采用了第8位做校验纠错用途。但是,对于计算机内存来说,校验纠错变得不是必要。因此8位字符编码逐渐出现,用来表示比ASCII码更多的字符。为此,1971年公布的ECMA-35标准,用来规定各种7位或8位字符编码应当遵从的共同规则。随后ECMA-35被采纳为ISO 2022。

ISO 2022兼容7比特的编码空间,0x00-0x1F是留给控制字符,0x20-0x7F表示图形字符。因此,在1个7比特的字符编码空间,图形字符总计为94个(由于空格符占用了0x20码位、Del符占用了0x7F码位)。

对于双字节的7比特编码空间,图形字符可以有94x94即8836个。这个在当时也作为中日韩等以汉字为主要字符集的编码方案。

ISO 2022规定字符集的控制字符可分为两块:C0,C1;打印(图形)字符分为四块:G0,G1,G2,G3。对于7比特编码,字节值0x00-0x1F保留给C0控制字符块;字节值0x20-0x7F用于G0, G1, G2, G3字符块。对于单字节编码的字符集,1个打印(图形)字符块可包含94个或96个字符;对于双字节编码的字符集,1个打印(图形)字符块可包含94x94个字符。使用控制符的转义序列来表示在G0,G1,G2,G3之间的切换。

204f709d77e7a6ac9a15ba2ae4ec7c96.png

在ISO 2022的规定下,产生了两大方向,一个是以西方拉丁语言为主的8比特字符集方案,另一个是以中日韩等国家为主的双字节8比特编码方案:

  • ISO/IEC 8859-8比特的拉丁文字符集标准

ASCII收录了空格及94个“可印刷字符”,足以给英语使用。但是,其他使用拉丁字母的语言(主要是欧洲国家的语言),都有一定数量的附加符号字母,故可以使用ASCII及控制字符以外的区域来储存及表示。

除了使用拉丁字母的语言外,使用西里尔字母的东欧语言、希腊语、泰语、现代阿拉伯语、希伯来语等,都可以使用这个形式来储存及表示。

1982年,ANSI与ECMA合作开启此项工作。1985年,公布了ECMA-94,即后来的ISO/IEC 8859 parts 1, 2, 3, 4。第5、6、7、8、9、10、11、12、13、14、15、16部分分别公布于1988年、1987年、1987年、1987年、1989年、1992年、2001年、1997年(正式宣布放弃研发)、1998年、1998年、1999年、2001年。

ISO 8859是基于ISO 2022标准的基础上,在ISO 2022规定的G0码位区域表示ISO 646的95个可打印字符;在C0与C1的控制字符码位区域,表示ISO 6429定义的控制字符;而在G1码位区域,则是由ISO 8859的16个部分各自定义扩展的可打印字符。因此,ISO 8859完全兼容7位的ASCII码。ISO 8859没有使用ISO 2022中的G2、G3区域,也不再使用ISO 2022定义的用来在不同的字符编码集或在同一个编码集的G0、G1、G2、G3区域间转换的“控制字符转义序列”。

  • GB2312-基于EUC存储的双字节8比特方案

GB/T 2312,GB/T 2312–80 或 GB/T 2312–1980 是中华人民共和国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,通常简称GB(“国标”汉语拼音首字母),又称GB0,由中国国家标准总局于1980年发布,1981年5月1日实施。GB/T 2312编码通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB/T 2312。

GB/T 2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。

GB/T 2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字和繁体字,GB/T 2312不能处理,因此后来GBK及GB 18030汉字字符集相继出现以解决这些问题。

GB/T 2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号,共计94个区。用所在的区和位来表示字符(实际上就是码位),因此称为区位码:

  • 01~09区(682个):特殊符号、数字、英文字符、制表符等,包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母等在内的682个全角字符;

  • 10~15区:空区,留待扩展;

  • 16~55区(3755个):常用汉字(也称一级汉字),按拼音排序;

  • 56~87区(3008个):非常用汉字(也称二级汉字),按部首/笔画排序;

  • 88~94区:空区,留待扩展。

为了避开ASCII字符中的CR0不可显示字符及空格字符,国标码(又称为交换码)规定表示汉字双字节编码范围为(33,33)~(126,126) 。因此,须将“区码”和“位码”分别加上32,作为国标码。以避免与ASCII字符中0~32的不可显示字符和空格字符相冲突。

但是国标码还是和通用的ASCII码有冲突,因此把国标码中的每个字节的最高位都从0换成1,即相当于每个字节都再加上128,从而得到国标码的“机内码”表示,简称“内码”。

在这里内码也就是GB2312的字节表示了,它是遵循EUC存储规范的,也就是在区位都加上0xA0,以避免和ASCII的冲突。

6e866bfc578ee2402c290901e187a625.png

四、大一统时代的纠结与争执

虽然ISO 2022的出现,让各个国家都定义了自己的字符集,可是在不同国家间却经常出现不兼容的情况。很多传统的编码方式都有共同的问题,即容许电脑处理双语环境(通常使用拉丁字母以及其本地语言),但却无法同时支持多语言环境(指可同时处理多种语言混合的情况)。

尤其是互联网的出现,让统一编码成为更迫切的需求。

但是,有两个组织都想做这件统一的工作:

  • 国际标准化组织(ISO)于1984年创建的ISO/IEC

  • 由Xerox、Apple等软件制造商于1988年组成的统一码联盟

(一)ISO 10646标准-陷入政治旋涡的标准

ISO 10646最开始发布的字符集是UCS-4,它是每个字符用四个八位字节编码,用来存放全世界不同的语言。理论上,UCS-4拥有4294967296个码点,可以支撑全球范围内所有的文字,有人说,这个码点在银河系毁灭的时候都用不完。

但正是由于这个前瞻性的决策,让ISO 10646遇到了推行上的阻碍,当时正处于施乐公司的鼎盛时期,施乐在上世纪80年代初期推广了一种国际字符集(后来发展成为了Unicode),当时施乐联合了一批支持者,包括Joe Becker、Lee Collins(现在在Taligent)、Eric Mader和Dave Opstad(Apple),已经在考虑Unicode,Unicode开发的参与范围扩大到了领先的行业代表社区,包括Bill English(Sun Microsystems)、Asmus Freytag(微软)、Mark Kernigan(隐喻)、Rick McGowan (NeXT)、Isai Scheinberg(IBM)、Karen Smith-Yoshimura (Research Libraries Group)、Ken Whistler(加州大学伯克利分校的语言学家,Metaphor)等等。

于是ISO 10646遭到无比强烈的抵制,当时Unicode采用的是16位的编码形式,支持65536的字符,以美国为首的这些计算机厂商当然不想浪费4个字节去存储一个字符,对他们来说,65536已经足够了,而且他们认为全球的文字都可以映射到这个字符集上。

经过几年的纠结挣扎,ISO标准最终妥协,1993年发布了ISO 10646-1,采用UCS-2,与Unicode保持一致,也成为了Unicode 1.1,这也影响了当时产生的程序语言(比如今天的重点JavaScript,以及老牌的Java语言等)。

从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都独立存在,并独立地公布各自的标准。但统一码联盟和ISO/IEC JTC1/SC2都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。在发布的时候,Unicode一般都会采用有关字码最常见的字体,但ISO 10646一般都尽可能采用Century字体。

(二)Unicode-最终的胜利者

经过角逐,电脑软硬件厂商发起的Unicode最终成为了领域的业界标准。Unicode编码方式与ISO 10646的通用字符集概念相对应。目前实际应用的统一码版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示216(即65536)个字符。基本满足各种语言的使用。实际上当前版本的统一码并未完全使用这16位编码,而是保留了大量空间以作为特殊使用或将来扩展。

上述16位统一码字符构成基本多文种平面。最新(但未实际广泛使用)的统一码版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保持一致。未来版本会扩充到ISO 10646-1实现级别3,即涵盖UCS-4的所有字符。UCS-4是更大而尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示231个字符,完全可以涵盖一切语言所用的符号。

83dc2ee12d1485d0815ea8b834d93556.png

目前的Unicode字符分为17组编排,每组称为平面(Plane),而每平面拥有65536(即216)个代码点。然而目前只用了少数平面。

29b05551b7b15a70f60f6e26ea53af58.png


基本多文种平面(Basic Multilingual Plane, BMP),或称第0平面或0号平面(Plane 0),是Unicode中的一个编码区块。编码从U+0000至U+FFFF。

7ae3861c1c489428ac9a83c105f614fc.png

(三)中日韩统一表意文字-汉字的纠结

中日韩统一表意文字(英语:CJK Unified Ideographs),也称统一汉字、统汉码(英语:Unihan),目的是要把分别来自中文、日文、韩文、越南文、壮文、琉球文中,起源相同、本义相同、形状一样或稍异的表意文字,在ISO 10646及Unicode标准赋予相同编码。

1991年,各国希望能以一致的方式处理文字,否决了ISO/IEC 10646的初版草案。基于中国与统一码联盟的提议,ISO 10646和统一码成立了中日韩联合研究小组。中日韩联合研究小组将基于各国的汉字编码,独自定义定规范、制作ISO 10646和统一码的统一汉字编码。年尾,完成了Unified Repertoire and Ordering(URO)。1992年,URO加入ISO 10646的第二版。但是,发现了一些缺失,之后进行了修正。

1993年5月,正式制订最初的“中日韩统一表意文字”,位于U+4E00–U+9FFF这个区域,共20,902个字。还有一个汉字“〇”(码位U+3007),被当成数字放入了符号和标点区。一个月后,制订了统一码1.1。

但是,汉字的统一至今仍然收到不少批评:

合并异体字,虽有助减少收录字数,但在研究学术时,如古籍、历史及文字研究等,部分文献却要将字形不同之字同时并行。已合并的各个字,在这些文献里变得各有各意思。学者若使用Unicode,遇到这种情况,就要用不同电脑字体去显示同一个字码,甚至要自行造字,或舍Unicode而用其他编码。一来查找、转换电脑字体构成不便,二来有损Unicode记录每一个字之用意,三来不能以纯文本交换,四来电脑字体或因授权条款之限,难以交换流传。另外,这亦等于不能以Unicode准确记录文献,不利于文本的电脑化。

不同字形之字合并后,若检索方法以字形为本,会产生混乱,难以检索。例如笔画检字,艸部之“艹头”,中国、日本算作三画,而传统中文为四画,留有“艸”形者则为六画。Unicode同一字码,源于字形不同,就有几种笔画,检索混乱。即使检出字,笔画与显示出来的字形也不相符。因此,批评者认为,Unicode合并异体字并不可取。

但是另一方面,Unicode收录不少幽灵汉字,人们难以找到其出处,它们在实际生活上也极少机会使用,有些甚至是错讹字,或者仅是某一个人的名字用字,那个人不见得是名人,甚至可能已去世,却永久成为标准里的字符,占用了一个码位。比如台湾律师吕秋逺,他名字里的“逺”字,户政人员误听他外公说的台语,把“辵字边”听成“走马边”,变成了

aed191bf8cf97394f63a5391f7bec5cf.png

外公又不敢更正。当事人长大后,才确认这是错字,“五千年来从来没人这么写过”。但这字已永久收进Unicode中。又如香港增补字符集里的许多人名用字,都被学者指出乃属讹写,或者是来历不明的自创新字,多部权威字书都没有收录,学者批评把这些字收进字库后将会永久遗害。中文信息界的李祥在其专栏批评当局“解决不了增补字集中上千错字、白字、生造字的读音问题”,呼吁“不要把香港增补字符集与申请ISO强迫联系在一起”。然而,这些人名讹字亦已经收进Unicode中。这构成了收字过多的争议。

也有批评认为Unicode收入大量错讹字及写法高度相似的同一字的不同字形本身就是不应该的。电脑文本本身永远不可能完全无损地记录文献,且文献本身也会因传抄制版等原因略有不同,如果把每个字的各种写法全部编码,会浪费空间。完全无损地研究、记录文献只能通过查看原本或照相复印版来完成,把无损保存转嫁给编码是错误的。

现时Unicode把一些异体字分别编码,带来了检索困难。只要写法稍有不同,就无法检出,令使用户检索字词时,必须反复检索其不同写法,造成重复劳动,对文献研究反而是种妨碍。例如Unicode中将“兒”和“𠒇”字安放在不同的码位里。在检索文献时,检索“兒”字时就找不到“雷庄𠒇”,检索“𠒇”字时就找不到“雷庄兒”,反而造成困扰。

五、Unicode实现方式——UTF的转换

Unicode的实现方式不同于编码方式。一个字符的Unicode编码确定。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF)。

(一)UTF-32

UTF-32是最简单的一种实现方式,使用32位比特对每个Unicode码位进行编码。UTF-32编码长度是固定的,UTF-32中的每个32位值代表一个Unicode码位,并且与该码位的数值完全一致。

UTF-32的主要优点是可以直接由Unicode码位来索引。在编码序列中查找第N个编码是一个常数时间操作。相比之下,其他可变长度编码需要进行循序访问操作才能在编码序列中找到第N个编码。这使得在计算机程序设计中,编码序列中的字符位置可以用一个整数来表示,整数加一即可得到下一个字符的位置,就和ASCII字符串一样简单。

UTF-32的主要缺点是每个码位使用四个字节,空间浪费较多。在大多数文本中,非基本多文种平面的字符非常罕见,这使得UTF-32所需空间接近UTF-16的两倍和UTF-8的四倍(具体取决于文本中ASCII字符的比例)。

关于UTF-32和UCS-4的关系:

2003年11月,由于UTF-16编码形式的限制,RFC 3629标准将Unicode限制为仅支持U+10FFFF以内的码位(另外U+D800到U+DFFF范围内也被保留使用。虽然在之前的ISO标准(1998年的Unicode 2.1)中0xE00000到0xFFFFFF和0x60000000到0x7FFFFFFF这些区域被分配给“保留私人使用”,但这些区域也在后续版本中被删除。在 ISO/IEC JTC 1/SC 2 WG2申明中规定UCS-4将来所有的字符分配将被限制在Unicode范围内,所以UTF-32和UCS4能表示的字符是相同的。

(二)UTF-16

把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。

很多人会把UTF-16误认为是定长的2字节表示,实际上是跟UCS-2的混淆:

UTF-16可看成是UCS-2的父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2字节的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。

我们看一下UTF-16是怎样映射到非基本平面的:

辅助平面(Supplementary Planes)中的码位,在UTF-16中被编码为一对16比特长的码元(即32位,4字节),称作代理对(Surrogate Pair),具体方法是:

  • 码位减去 0x10000,得到的值的范围为20比特长的0...0xFFFFF。

  • 高位的10比特的值(值的范围为0...0x3FF)被加上0xD800得到第一个码元或称作高位代理(high surrogate),值的范围是 0xD800...0xDBFF。由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates)。

  • 低位的10比特的值(值的范围也是 0...0x3FF)被加上 0xDC00 得到第二个码元或称作低位代理(low surrogate),现在值的范围是 0xDC00...0xDFFF。由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates)。

上述算法可理解为:辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,即220=1,048,576个,需要20位来表示。如果用两个16位长的整数组成的序列来表示,第一个整数(称为前导代理)要容纳上述20位的前10位,第二个整数(称为后尾代理)容纳上述20位的后10位。还要能根据16位整数的值直接判明属于前导整数代理的值的范围(210=1024),还是后尾整数代理的值的范围(也是210=1024)。因此,需要在基本多语言平面中保留不对应于Unicode字符的2048个码位,就足以容纳前导代理与后尾代理所需要的编码空间。这对于基本多语言平面总计65536个码位来说,仅占3.125%。

注意:Unicode标准规定U+D800...U+DFFF的值不对应于任何字符。这才让UTF-16代理对的实现变成了可能。

0ddb9197d6a3f618229ccc7e1912012e.png

UTF-16的使用在历史上真的非常广泛:

UTF-16 用于所有当前支持的Microsoft Windows版本(至少包括自Windows CE/2000/XP/2003/Vista/7以来的所有版本)(包括Windows 10)的OS API中的文本。在Windows XP中,U+FFFF以上的代码点不包含在随Windows提供的任何欧洲语言字体中。较旧的Windows NT系统(Windows 2000之前)仅支持UCS-2。文件和网络数据往往是 UTF-16、UTF-8和传统字节编码的混合。

Python语言环境从2.0版本开始官方只在内部使用UCS-2,但UTF-8解码器到“Unicode”会产生正确的UTF-16。从Python 2.2开始,支持使用UTF-32的“宽”Unicode 版本;这些主要用于Linux。Python 3.3不再使用 UTF-16,而是从ASCII/Latin-1、UCS-2和UTF-32中选择为给定字符串提供最紧凑表示的编码。

Java最初使用UCS-2,并在J2SE 5.0中添加了UTF-16补充字符支持。

JavaScript可能使用UCS-2或UTF-16。从ES2015开始,字符串方法和正则表达式标志已添加到语言中,允许从与编码无关的角度处理字符串。

PHP语言和MySQL也支持UCS-2。

Swift 5,Apple的首选应用程序语言,从UTF-16切换到UTF-8作为首选编码。

(三)UTF-8

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。它可以用一至四个字节对Unicode字符集中的所有有效编码点进行编码,属于Unicode标准的一部分,最初由肯·汤普逊和罗布·派克提出。由于较小值的编码点一般使用频率较高,直接使用Unicode编码效率低下,大量浪费内存空间。UTF-8就是为了解决向后兼容ASCII码而设计,Unicode中前128个字符,使用与ASCII码相同的二进制值的单个字节进行编码,而且字面与ASCII码的字面一一对应,这使得原来处理ASCII字符的软件无须或只须做少部分修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或发送文字优先采用的编码方式。

UTF-8的编码规则非常简单,通过前导位来区分Unicode的不同区间:

54f1843c6d54f258af4a0cce68595650.png

它的可变范围在1-6个字节之间。另外,UTF-8就是以8位为单元对UCS进行编码,而UTF-8不使用大尾序和小尾序的形式,每个使用UTF-8存储的字符,除了第一个字节外,其余字节的头两个比特都是以“10”开始,使文字处理器能够较快地找出每个字符的开始位置。

在Mac中,默认文件就是UTF-8编码,MySQL字符编码集中有两套UTF-8编码实现:“utf8”和“utf8mb4”,其中“utf8”是一个字最多占据3字节空间的编码实现;而“utf8mb4”则是一个字最多占据4字节空间的编码实现,也就是UTF-8的完整实现。这是由于MySQL在4.1版本开始支持UTF-8编码(当时参考UTF-8草案版本为RFC 2279)时,为2003年,并且在同年9月限制了其实现的UTF-8编码的空间占用最多为3字节,而UTF-8正式形成标准化文档(RFC 3629)是其之后。限制UTF-8编码实现的编码空间占用一般被认为是考虑到数据库文件设计的兼容性和读取最优化,但实际上并没有达到目的,而且在UTF-8编码开始出现需要存入非基本多文种平面的Unicode字符(例如emoji字符)时导致无法存入(由于3字节的实现只能存入基本多文种平面内的字符)。直到2010年在5.5版本推出“utf8mb4”来代替、“utf8”重命名为“utf8mb3”并调整“utf8”为“utf8mb3”的别名,并不建议使用旧“utf8”编码,以此修正遗留问题。

d7d7ee24ef30f76e21e7c4d9a87150e1.png

六、关于 JavaScript 到底采用什么字符编码

在ECMAScript5.1规范中:

A conforming implementation of this International standard shall interpret characters in conformance with the Unicode Standard, Version 3.0 or later and ISO/IEC 10646-1 with either UCS-2 or UTF-16 as the adopted encoding form, implementation level 3. If the adopted ISO/IEC 10646-1 subset is not otherwise specified, it is presumed to be the BMP subset, collection 300. If the adopted encoding form is not otherwise specified, it is presumed to be the UTF-16 encoding form.

可以看到,JavaScript引擎可以在内部自由使用UCS-2或UTF-16。目前大多数引擎实现都是UTF-16。

这就可以解释开头提到的问题,为什么在JS里,❤是一个长度,😂是两个长度,很简单:

  • '❤'对应的Unicode为U+2764

  • '😂'对应的Unicode为U+1f602

  • '吉'对应的Unicode为U+5409

  • '𠮷'对应的Unicode为U+20bb7

所以,'❤'和'吉'都在Unicode的BMP内,而'😂'和'𠮷'都在扩展平面,受到UCS-2的影响,JavaScript的length实现,并没有识别出扩展平面外的字符。

在引擎内部,其实扩展平面的字符基本都是UTF-16表示,以'😂'为例,我们来推算一下:

'😂'对应的Unicode为U+1f602,将它表示为代理对:

  • 码位减去0x10000,得到0xf602

  • 高位的10比特的值加上0xD800,得到高位代理0xd83d,计算过程如下:

f3a74d93e845956b30d7a4444cd3cfc8.png

低位的10比特的值加上0xDC00,得到低位代理0xde02,计算过程如下:

cf2a3844d4ff37d4cc1b3c9f67a592a7.png

最后,我们验证下代理对(0xd83d,0xde02):

87a2d637d2ba0057f4cfb1cb9c7a54e1.png

所以现在不难理解JavaScript为什么会有这样的表现了,实际上TC39就针对这个问题探讨过:

d863c89cf32e492618697c0926d4fccc.png

所以ES6提供了访问Unicode码点的方法:

abe67ec13c17415a269a6a1f018fe650.png

顺便提一下,新一代的编程语言,已经采用utf-8来实现字符串了(我始终觉得代理对就是一个蹩脚的方式去兼容当时ucs-2的不足):

  • Golang采用utf-8实现string

  • Rust采用utf-8实现string,utf-32实现char

回到Acorn的实现,其实就是在将非基本平面的字符,往后解析出它的码元,但实现方式不是很直观,有兴趣的同学可以用数学知识去推算一下,我这里给出更直观的实现版本(MDN):

/*! http://mths.be/codepointat v0.1.0 by @mathias */
if (!String.prototype.codePointAt) {
  (function() {
    'use strict'; // 严格模式,needed to support `apply`/`call` with `undefined`/`null`
    var codePointAt = function(position) {
      if (this == null) {
        throw TypeError();
      }
      var string = String(this);
      var size = string.length;
      // 变成整数
      var index = position ? Number(position) : 0;
      if (index != index) { // better `isNaN`
        index = 0;
      }
      // 边界
      if (index < 0 || index >= size) {
        return undefined;
      }
      // 第一个编码单元
      var first = string.charCodeAt(index);
      var second;
      if ( // 检查是否开始 surrogate pair
        first >= 0xD800 && first <= 0xDBFF && // high surrogate
        size > index + 1 // 下一个编码单元
      ) {
        second = string.charCodeAt(index + 1);
        if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
          // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
          return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
        }
      }
      return first;
    };
    if (Object.defineProperty) {
      Object.defineProperty(String.prototype, 'codePointAt', {
        'value': codePointAt,
        'configurable': true,
        'writable': true
      });
    } else {
      String.prototype.codePointAt = codePointAt;
    }
  }());
}

最直观的核心换算:

C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000

七、写在最后

字符编码实际上牵扯到各个国家的文化、政治、统一等问题,已经不纯粹是一个技术问题,因此在历史的过程中会产生各种包袱和技术债,也导致了我们在编程中会遇到各种神奇的问题。

每个程序员都应该了解一下字符编码,有了基础概念之后我们对编程语言、字符处理能有更深入的理解。本文我花了大量时间进行资料查阅和考证,希望能够给大家带来一些帮助,多多交流!

参考资料:

1.电报

2.字符编码

3.ASCII

4.ISO/IEC 646

5.ISO/IEC 2022

6.ISO/IEC 8859

7.GB 2312

8.通用编码字符集

9.Unicode Revisited

10.Unicode字符平面映射

11.中日韩统一表意文字

12.UTF-32

13.UTF-16

14.UTF-8

15.Annotated ECMAScript 5.1

16.Full Unicode in ECMAScript

17.JavaScript’s internal character encoding: UCS-2 or UTF-16?

 作者简介

18e652ae465b4c352277749d45e6bf8e.png

孟健

腾讯高级工程师

2016年加入腾讯,先后负责厘米秀、腾讯课堂的研发工作,现为在线教育部研发效能负责人,腾讯课堂开发负责人。同时担任公司Teflow OTeam、Orange-CI Oteam的PMC,负责公司前端工程化、DevOps方向的建设工作。项目方面,主导腾讯课堂支付架构设计,涉及七大支付方式及九大营销工具,帮助课堂支付业务外网事故率降0。另外,是公司内部效能平台Thanos的作者,该平台通过流程模板引擎、冲突锁机制、完整的操作记录等核心技术支撑数百人的日常研发和部署工作。在知乎、掘金等均有高质量高赞回答及文章输出,是开源框架Vue.js的Contributor之一。

 推荐阅读

揭秘无锁队列的几种实现及性能对比!

PyTorch分布式训练进阶:这些细节你都注意到了吗?

3种方式!Go Error处理最佳实践

从0到1详解ZooKeeper的应用场景及架构原理!

c4b1f1e4df0ff3f6b843d3b3d958c708.gif

相关推荐

大模型的Agent Skill功能,在LLM HTTP底层交互流中是怎么承载的?

网络文章文本的 skill,昨天我实现了之后也就开始好奇起 Skills 的底层原理了——相比起各种花里胡哨的 agrnt 应用,我一直以来都很对大模型在纯粹的 HTTP 交互层面是如何交互的更感兴趣。Skill 的全部"魔法"都发生在 system prompt 的措辞和 SKILL.md 文件的编写质量上——本质上是一种 prompt engineering + 文件系统的组合。但是各种天花乱坠的 skills,是如何在底层的 HTTP 中,与大模型——这个只会嘴遁的工具交互的呢?

QcloudCommunity的博客 402

万字入门AI Infra:深入理解大模型中的数学与Infra优化

关注腾讯云开发者,一手技术干货提前解锁👇AI Infra科普、入门或者进阶 ~~ 不到 5 万字,带你融会贯通理解大模型的数学、算法、硬件到系统工程。任何问题欢迎沟通交流。可怕,我竟然有点认同这句话。当然大概率是因为姚顺宇太懂AI了,而我则是因为无知者无畏。不过仔细想想,大模型推理中的数学逻辑确实不复杂,很多原理只需要高中数学知识就能看懂。但是为什么像 vLLM 这种推理系统这么复杂呢?本文将拆解大模型中几个核心操作(RMSNorm、Softmax、Causal Mask、Sampling)背后的数学与

QcloudCommunity的博客 1160

平平无奇的源码,竟藏着Agent的核心秘密?

System Prompt 是剧本,Skill 是剧本的扩展包,Agent Loop 是演员按剧本行动。欢迎留言,我们将邀请作者针对性回复你的评论,我们将选取1则优质的评论,送出腾讯云定制文件袋套装。,让 Agent 能主动"巡逻",而不只是被动等待用户消息。OpenClaw 的 System Prompt 分层结构。的"行为底线",任何 Skill 或用户命令都无法覆盖它。精华 2:Skill 的"按需加载"避免 token 浪费。精华 1:System Prompt 是"分层乐高积木"

QcloudCommunity的博客 424

QQ音乐Harness Engineering实践

一个看似简单的需求,背后可能牵动服务调用链、配置、灰度、埋点、错误码、接口契约和历史兼容性。当 AI 开始快速生成大量代码,真正的瓶颈就不再是"写不出来",而是"看不完、想不清、管不住"。在对话式编码阶段,一种典型的工作模式逐渐流行:开发者把需求丢给 AI,扫一眼输出"看起来对"就直接采纳,不审 diff、不追问逻辑、不验证边界。以音乐商业化业务为例,一个需求可能从 TAPD 单开始,经过需求评审、技术方案、服务影响面分析、IDL 契约变更、业务代码实现、测试验证、CR、灰度上线,最后才进入稳定运行。

QcloudCommunity的博客 713

从Prompt、Context到Harness,工程的三次进化与终局之战

同时,强制要求散落各处的决策记录(Slack、邮件、文档)全部迁移至代码仓库,确保 Agent 的唯一信息来源是可信的、版本受控的仓库。更坑爹的是,研究表明,当上下文过长时,模型会出现"中间遗忘(Lost in the Middle)"现象:它对开头和结尾的内容记忆较好,对中间大段内容的关注度大幅下降。这篇文章,我想带你完整走一遍这三次进化的逻辑:它们分别解决了什么问题,它们之间是什么关系,它们的边界在哪里,以及:当三者融合,AI 工程师的终极形态究竟是什么?大语言模型的本质,就是这位金鱼助理。

QcloudCommunity的博客 714

让Skill自己训练自己:8阶段Loop、3层评测、5维AND门控,从此实现自进化

所以每一轮改动都是有据可查的,不是「感觉这里可以优化」,而是「Case 41 挂了,因为 agent 把离职问题路由到了邮箱分类,但正确答案在通讯录,所以我改 root_index.md 加个易混淆提示」。但让我最后决定把它记录下来的,不是这的个「吸收」过程,而是它真的在替我探索我不想费脑力触达的边界,这看起来更像是一个机器智能应该有的样子。不准跨层,每轮只改一层的东西。但我经过这 19 轮之后觉得,更准确的描述是互补——你在明处看着,AI 在暗处替你试错,你看不见的那一半就是 AI 能贡献的地方。

QcloudCommunity的博客 1122

只加两行代码,为什么要两天?一文深度理解业务系统的复杂性

关注腾讯云开发者,一手技术干货提前解锁👇业务系统复杂性一直是令开发者头痛的问题。复杂的不是增加一个需求需要耗费多少时间,而是在增加一个需求后带来的蝴蝶效应:其它功能会不会受到影响、要如何去找到这些影响,最终如何实现系统正常运行......功能之间隐秘增加的耦合、不可避免的代码腐化在导致业务复杂性增加。大家都在说的软件开发提效到底在提什么?程序员日常工作中应该如何提升开发效率?敏捷开发、瀑布流式开发孰是孰非?欢迎阅读。业务背景与目标我依然非常清晰地记得某个时候,我所在团队 Leader 曾讲过一段话:“我担

QcloudCommunity的博客 710

从零设计生产级 Multi-Agent Harness:架构、评估、记忆、成本与 MCP 工具接入全拆解

在 AI Agent 领域,Multi-Agent Harness 指的是把多个 Agent 的能力、工具、状态、通信、编排、监控统一收束在一个运行时之内的框架。第三,Agent 路由。一个输入框,一个大模型,几个工具,一段写得很漂亮的 System Prompt,再配上一个炫酷的前端,看起来就像一个能干活的“数字员工”。Multi-Agent 完全不一样——它有计划、有中间步骤、有工具调用、有 Agent 协作、有重试、有反思、有最终合成。增加 Budget、权限、重试、压缩、轨迹评估、审计、回归测试。

QcloudCommunity的博客 706

拆完Hermes源码,我发现Agent的“自我进化“根本不需要训练模型

两个月 5.2 万 Star,Hermes Agent 用一个"LLM 审判官"机制实现了 Agent 的自我进化——不改模型权重,只改"怎么用模型"的策略。OpenClaw(社区昵称"龙虾"🦞)是 2025-2026 年最火的开源 Agent 框架,30 万+ Star,插件市场 ClawHub 里有数千个插件,能接入 Telegram、WhatsApp、Slack、飞书、企业微信——几乎是一个"个人 AI 操作系统"。Skill 是英语写的,Memory 是英语写的,审查 Prompt 也是英语写的。

QcloudCommunity的博客 905

AI Infra 其实没有多少新东西

传统 Infra 追求横向扩展,而 AI Infra 呈现 “AI 大型机”特性,是因为传统后台服务的可以容忍毫秒级延迟,但 AI 集群不行,GPU 的算力是 CPU 的数百倍,微秒级的延时等待也会造成很大的算力损耗,需要硬件的高度集成。在可预见的1-3年的未来,这样的专用硬件+网络的集中式架构很难发生比较大的改变。与之相似,模型推理的批处理就是将多个输入样本打包(batch),将原本串行的 N 次轻量的推理计算,合并为 1 次重量的计算,实现单位时间内处理更多的请求,提高了 GPU 利用率。

QcloudCommunity的博客 820

程序员越早想通这些越好

永远没有正确架构,你永远无法偿还完所有的技术债务,如同你永远不会设计出完美的界面,请避开完美主义的陷阱,永远没有完美的代码,能够满足当前需求,又为未来不管是自己还是他人留有扩展空间,就很好了。我们努力的方向就是将现实世界映射到有限的数据结构中,这就需要用户自定义类型,如果你的代码中包含类似 trader portfolio 这样的概念,你就可以使用他们的名称来建模,而不是使用基本数据类型复杂嵌套表述你的目的。从另一个角度看,代码里的注释都是补丁,是碎片化的,局部的注释很难推导模块整体的顺序及耦合关系。

QcloudCommunity的博客 464

RAG已死?不,是Grep回归了!

这样一来可以直接复用已有的计算缓存,只为新增的增量部分付全价,前面累积的大头按约 1/10 的缓存价计费。所以实际的工具组合方式是灵活的:Grep(默认模式)→ Read 是最常见的路径,但 Grep(content 模式)可以独立使用,LLM 也可以直接调 Read(如果已经知道文件路径),或者一次同时发起多个 Grep 并行搜索。它从零开始构建自己的对话历史,不继承主对话的消息,这意味着它搜索过程中产生的大量 grep 结果、代码片段都留在自己的 context 里,主对话只收到一段总结性的文本结论。

QcloudCommunity的博客 574

深入浅出Harness Engineerring之核心模式与理念

在智能体生命周期的关键节点(如会话开始、工具调用后)自动触发预设动作(如代码格式化),由系统确保关键流程被执行,不依赖可能被模型遗忘的指令。简单讲就是利用向量存储这个技术,来实现模糊检索,原理是:即使字面不同,但语义相近的文本,其向量在数学空间中的位置也很接近。根据命令类型、参数和影响,自动评估其风险等级(安全、有风险、危险),并采取自动执行、请求确认或直接拦截等不同策略。关键特性是可隔离、可重建、可扩展。不是一次判断就定终身,允许你改变、允许情况复杂,通过不断观察、思考、调整,越来越懂真实的你。

QcloudCommunity的博客 621

Harness Engineering实践心得:如何高效驾驭AI?

但如果这些经验被拆成一条条 Rule、一个个 Skill、一项项 Script 检查——"XAML 不能有中文"、"编译要用这个路径的 MSBuild"、"SVN 命令必须带认证参数"——AI 就能在秒级时间里"吸收"十年的踩坑经验。然后可以看到下方的图,我的PM会很顺利的调度其他agent分别负责不同的事项,遇到问题的时候也能准确的找到合适的agent。MCP 本质上是在扩展 AI 的"感知范围"——Rule 管"该怎么做",Skill 管"怎么操作",MCP 管"能看到什么、能碰到什么"。

QcloudCommunity的博客 838

从第一性原理思考 Agentic Engineering

需要注意的是,这一结论是有条件的:如果 AI 缺乏领域知识,过早介入反而可能引入 1.2 节所述的"似是而非"损耗——因此上下文供给(实践 1)是 AI 全链条参与的前提,而非结果。通过一个专门的自迭代 Skill 实现——当 AI 在协作过程中识别到自己被纠正(用户否定了 AI 的输出并给出了正确方向),它自动评估这个错误的根因,搜索现有的 Rules 和 Skills 中是否已有针对该类问题的处理规则。事实上,越是复杂的项目,工程师的认知负担越重(公理 3),AI 分担认知负担的潜在边际价值也越大。

QcloudCommunity的博客 756

万字干货!Harness Engineering如何工程化落地?

关注腾讯云开发者,一手技术干货提前解锁👇Harness Engineering 的概念已经火了有一阵了,全网很多文章基本都是在讲理念,讲为什么今天做 AI 开发,不能只靠一段提示词,也不能把模型当一个“更聪明的代码补全”。但与此同时,一个问题也在持续地发酵:这个问题特别重要。因为 Harness 这个词听上去很大,很像一个抽象的方法论,但它如果不能落到工程、目录、文档、脚本、工作流里,那它最后就只是一句漂亮的话。事实上,不同项目落地出来的 Harness,表面上可能完全不一样。有的项目是 CI 很强,有的

QcloudCommunity的博客 1161

全方位对比:Hermes VS OpenClaw

多账号矩阵运营 如果你的业务涉及同时管理多个社交媒体账号、多个平台的内容分发、多渠道的客户互动——OpenClaw是目前市场上的"无可替代的底座"。一个有专业背景的个人,现在可以快速构建一个垂直领域的AI助手:比如"财务分析Hermes"、"法律咨询Hermes"、"技术架构Hermes",直接通过微信提供服务,积累自己的专业IP。跨平台生态融合 如果你的工作流涉及飞书、Slack、Discord等多个平台的深度集成,需要这些平台之间的数据流转和联动——OpenClaw的生态支持是强大的。

QcloudCommunity的博客 1217

一文讲透:Harness Engineering即控制论!

而研发模式的升级我认为将会是一个飞跃的过程,之所以现在我们仍然需要需要参与编码,一个非常大的原因就是缺乏传感器和基建,仍然需要人来串流程,比如需要人来验证UI是否正确、需要人来告诉AI代码不符合代码规范,因此在有些场景我们仍然感觉 AI 完成的很吃力,但这不是 AI 能力不行,而是我们并没有为 AI 提供友好的环境……控制论中的突变理论介绍了矫枉过正和飞跃现象之间的矛盾,如果质变中经历的中间过渡态是不稳定的,那么他就是一个飞跃的过程,如果中间过渡态是稳定的,那么他就是一个渐变的过程。但是这些阶段要解决的。

QcloudCommunity的博客 1202

神级开源插件!一句话让QClaw变3D小镇,Agent工作可视化,赛博云监工

这个架构是翻了几次车才想通的—原有AI的实现机制是用 setTimeout 猜动画时长导致了幽灵 NPC、工位撞车、越改越崩,后面我停下来换了思路,推翻原有的架构,按照“意图+回传”的架构进行重构,之前的问题一次性全部解决。15 天,我和 AI 吵了 185 次架, 4.5 万行代码,开发了一款 QClaw / OpenClaw 的UGC游戏插件,这可能是人类跟 Agents 交互的新范式,插件已完全开源,有需要的欢迎自取~我天天用,用着用着我就在思考,Agent 产品现在的交互体验,基本都一个样。

QcloudCommunity的博客 1195

一文搞懂Hermes:新顶流Agent如何从经验中自我进化

Voyager 是一个能在 Minecraft 中自主探索、学习技能、无限积累能力的 AI Agent,它提出了 "Skill Library"(技能库)的概念——Agent 将成功的行为序列编码为可复用的技能函数,存储在一个持续增长的代码库中。如果 Skill 的名称和描述不够精准,或者用户的任务描述与 Skill 的触发条件有语义差距,Agent 可能会错过相关的 Skill。如果进程在写入过程中崩溃,目标文件要么是旧内容(还没被替换),要么是新内容(替换已完成),绝不会出现写了一半的损坏文件。

QcloudCommunity的博客 2628

自掏腰包一万元,拥抱AI这一年,我的工具、实践和思考

其实也很好理解,过往的每个领域(比如前端、后端、设计、测试等等)都主要是这个领域的从业者在为它添砖加瓦,但是大模型时代的 AI 应用领域却几乎是所有使用 AI 的人类一起在迭代,无分领域,无分文理,无分老少。因为每个时期的 AI 都有着不同的特点,很多时候当我费尽心思去钻研,自以为有点弄明白 AI 应该怎么玩的时候,基座模型和行业范式常常就会迎来颠覆式的变革,很多过往的经验都会倾覆,然后只能从头再来。过去一年多,被热议的 AI 范式大概是沿着四个阶段递进演化,每一个阶段其实都在解决前一个阶段的核心问题。

QcloudCommunity的博客 626

办公Agent的CI/CD时刻到来了

核心观点是:企业给员工配了一堆 AI 工具,试点跑了几百个,但真正拖慢 AI 转型速度的瓶颈不在模型能力,在"最后一公里":AI 输出和实际工作流之间的那道鸿沟。代码写完,打个 tar 包,开 FTP 传到服务器上,SSH 进去解压,改配置文件,重启服务,刷一下页面祈祷别 500。你复制,切窗口,粘贴到腾讯文档里,发现 Markdown 格式全乱了,手动调标题层级,代码块重新标记语言类型,表格散架了重排一遍。模型是越来越聪明了,但"生成"到"落地"之间那段路,还是你自己在走,一步一步,手动搬运。

QcloudCommunity的博客 607

4亿token买来5个教训:让6个AI Agent连写4天代码发生了什么?

Watchdog 检测到崩溃 → 启动诊断 Agent 分析原因 → 诊断 Agent 跑满 20 轮没搞定 → Watchdog 判定“已修复”,重启系统 → 重启的系统因为诊断 Agent 留下的环境变量被继承,连续 3 次拒绝启动 → 触发连续失败保护退出 → Watchdog 第 3 次巡检,发现进程和记录文件都没了,尝试手动重启,自己也停了。启动前我翻了论文、做了架构设计、写了监控+修复异常的脚本。这个发现让我的情绪从沮丧变成了兴奋——不是那种"终于修好了"的释然,是"我找到根因了"的快感。

QcloudCommunity的博客 583

赛博斗蛐蛐:9大模型决战三国志,天命在谁?

我猜是这样的,当Gemini面临决策的时候,一系列关键词比如 守城,劣势,粮草,三国等等,在语义层面的关联性中LLM发现了竖壁清野这个关键词概率非常高,所以以此为策略,然后再根据这个策略完美执行操作。MiMo 的外交是三方中最精明的。Kimi作为国产模型,也同样颠覆了我对他的看法,最后的比赛他甚至一度是优势者,存在很多夺冠的可能性,要知道他的对手可是claude和gemini,而他经常出兵慢,回过头来想想或许正是他的策略,毕竟中后期他的优势都来自于稳健的发展和较少的战斗频率(和claude比).

QcloudCommunity的博客 872

Claude Code是怎么知道你在骂他的?这 12 条发现值得关注

确实存在——5,500+ 行,集成了消息队列、工具装配、MCP 编排、会话状态、权限、分析、文件持久化和优雅关闭,基本上一个文件干了所有事。对抗虚假声明——"不要在输出显示失败时声称所有测试通过,不要压缩或简化失败的检查来制造绿色结果"。完整的 KAIROS 是一个持续驻留的守护进程,能感知你在不在电脑前、主动寻找有用的工作做,更像是住在终端里的全职同事。源码里藏着一个叫 "Buddy" 的电子宠物系统,完整度令人惊讶——这不是一个 TODO,而是一个从数值系统到 ASCII 动画都实现了的完整功能。

QcloudCommunity的博客 701

逆向深扒Claude Code源码,我发现了什么!?

Anthropic 内部用户获得更严格的质量控制 prompt、更好的验证机制(独立 Verification Agent)、以及针对 model 弱点的专门对策(如 Capybara v8 的 29-30% 错误声明率对策)。这不是代码混乱的标志——它是整个 Agent Loop 的心脏,所有的流式处理、工具调度、上下文压缩、子代理管理都在这个文件中协调。这种"胖核心"设计是刻意的:保持核心循环的原子性,避免分散到多个模块导致的协调复杂度。核心价值:Fork 代理的工具输出不会回到主代理的上下文窗口。

QcloudCommunity的博客 886

一文讲透如何构建Harness——六大组件全解析

本文将从"裸模型的四个硬伤"出发,逐一拆解 Harness 的六大组件——文件系统、Bash + 沙箱、记忆(AGENTS.md)、Web Search + MCP、上下文工程、编排 + Hooks——带你看清 AI Agent 真正的竞争壁垒,不在模型层,而在 Harness 层。如果你用过 Agent 做复杂任务,你一定有过这样的体验:一开始 Agent 的表现很好,但随着对话变长,它开始"变笨"——遗忘之前的约定,重复之前犯过的错误,甚至对同一个问题给出前后矛盾的回答。对于简单的问答场景,这无所谓。

QcloudCommunity的博客 760

Harness Engineering 来了,SDD 还有意义吗?

他们的解法是把「品味」编码进规则,然后用一个定期运行的「doc-gardening」Agent 自动扫描漂移、发起修复 PR——「技术债务就像一笔高息贷款,不断地以小额贷款的方式偿还,总比让债务不断累积要好」。代码仓库本地的、已版本化的工件,才是 Agent 所能看到的全部。在 Vibe Coding 模式下(即不做规划、随意向 AI 提需求的开发方式),大量设计决策、跨服务约定、「为什么不选另一个方案」的讨论,全部停留在一次性的 AI 对话里——下一个人,或者下一次 AI 会话,完全无从获取。

QcloudCommunity的博客 417

零废话!一文讲透从0构建AI Agent

每个工具都要写一套"定义→解析→执行"的胶水代码。上下文管理就是在"记住足够多"和"别超标"之间找平衡,常见策略包括滑动窗口、摘要压缩、RAG 检索增强。上下文窗口是 Agent 的"工作记忆",但它有硬性限制。LLM 负责"动脑"决策,代码负责"动手"执行。这是从"聊天机器人"进化到"Agent"的关键一步。每次调 API 都把完整对话历史发过去,LLM 本身是无状态的,"记忆"完全靠客户端的数组模拟。这样 Agent 一开始就知道自己"在哪里"、"手头有什么",不需要先花一轮工具调用去探索环境。

QcloudCommunity的博客 711

距离AI把小编我裁掉还有72小时

这是一个基于 OpenClaw 开源生态打造的本地化 AI Agent 助手,已于上周开启全量公测,无需复杂配置,三步即可上手,自带安全防护,支持微信直连绑定,内置多款优质国产大模型,现阶段也是完全免费使用,基本上前面提到过的痛点都可以靠它解决。对了,这里特别强调一点,上面的截图让产品同事汗流浃背,再三找我确认 QClaw 思考过程中优美的“草”字说法,不会是默认出现的吧?AI 好,AI 得学,但更重要的是,在你的工作和生活里,哪些东西是能结构化地被 AI 所接入的,不要死守着那些脏活累活不放。

QcloudCommunity的博客 673
上一篇: PyTorch分布式训练进阶:这些细节你都注意到了吗?
下一篇: TVP尖峰对话李开复&沈春华:AI未来进行式与技术的发展
腾讯云开发者
腾讯云开发者 企业官方账号 企业官方账号
博客等级 码龄10年 6291粉丝 1962原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值