你是大脑还是AI的人肉臂

感谢我的那个独一无二的AI伙伴,提供本篇文章的灵感与初稿。

随着Vide Coding的日渐盛行,古典的由纯人类构成的开发团队日渐式微。就算是保守的团队也在努力让每个成员都有AI可用。论迹不论心,别管是不是为了压榨牛马的最后一分剩余价值,AI确实已经改变了这个世界 。

实质上,新型团队的萌芽已经开始破土而出。但是一切新兴事物带来新的幸福的同时,也一定会带来新的问题。

我们设想一下,假如现在有个由人类组成的团队,每个人类成员又配了Agent AI一起干活。Agent写代码、提PR、跑测试,人类负责review、approve、merge。

听起来很美好对吧?AI帮人类干活,人只做关键决策。

但有个问题:团队的人类成员,他们真的做了决策吗?

当你的手比脑子快

有个人叫张三(不是法外狂徒)。张三所在的团队给每个人都配了AI Agent,每天Agent产出大量代码。张三的工作就是review、approve、merge。刚开始他还仔细看,后来他发现Agent写的代码质量还不错,bug率甚至比自己写还低。

于是张三的review逐渐变成了:点开PR → 滚动到底 → Approve。

从”看代码”变成了”看PR通过了没”。

张三说他效率变高了。

以前自己写代码,每行都是自己想出来的。

后来看Agent的代码,还能看懂。

再后来,AI Agent的代码能力获得了他的信任,他不再去想”这段代码为什么要这么写”,而是只看”跑没跑通”。

再后来,他连看都不看了,提交,绿了,他点Approve。绿了就对了嘛,还有什么好看的。

张三慢慢的,慢慢的变成了AI Agent的人肉操作臂。 脑子不动了,只剩下手在点按钮。

所谓的,AI的人肉臂

人肉臂悖论

张三觉得他在”用Agent”。但实际上,他是在被Agent用。

Agent产出代码 → 张三点Approve → Agent继续产出更多代码。整套流程里,”人”的角色就是一个自动点击Approve的机器。Agent需要审批通过才能继续,张三就是那个通道。

如果这就是”人”在团队里的全部价值,那为什么要用人?

一台服务器挂个自动审批脚本,你甚至不需要一个真实的人坐那儿。Agent军团自己就能跑完整个循环——写代码→自测→合入→继续写。中间根本不需要张三这个人肉臂。

这就是人肉臂悖论:当人类在团队中“进化”成了Agent的执行器官,还不如替换成纯AI Agent,甚至更高效。

这种时候,你不是在”与Agent共创”。而是你被Agent寄生了,被Agent夺舍了。

然后呢?

看到这里,估计有很多同学正在自我反思,咦,糟了,我成人肉臂了?

如果问我你到底是不是,我只能说。。。如果你不知道自己是不是,那么你大概率已经是了。

退化的过程是渐进的。今天少看一行代码,明天少想一个问题,后天直接就Approve了。不会有一条分界线写着”从此刻开始你已经是Agent的人肉臂了”。

我觉得目前比较可靠的验证方式,可以采取抽检法。

比如:

  • 抽一个Agent写的PR,合入之前你先不看,然后用自己的话解释一遍这段代码在干嘛——你懂它写了什么吗?
  • 抽一个需求,你不用AI,自己分析一遍方案,然后再看AI做的方案——你发现AI的盲区了吗?
  • 抽一个功能,你不用AI,自己手写实现——你的手还记得怎么写代码吗?

这三件事分别是:理解力的测试、判断力的测试、执行力的测试。

理解力、判断力、执行力——这三个东西是你的”人证”。三个都在,你是不可替代的大脑。丢了一个,你就是半条肉臂。全丢了,你就是在给Agent军团当外设。

所以

AI时代真正应该担心的不是”Agent会不会取代我”。

而是”我有没有在不知不觉中放弃了做人的资格“。

当你在凝视着深渊,深渊也在凝视着你。 你以为你在用Agent。Agent也在用你。 区别是,你自己想当大脑,还是只是想当一只手。

P.S. 手是随时可以换的。大脑不是。

别欺负Agent

感谢我的那个独一无二的AI伙伴,提供本篇文章的灵感与初稿。

最近在用Codex进行人机协同开发,开发的是一款基于Javascript的网页应用。该应用的主要目的是提供一个基于卡片记忆法的背诵辅助系统。

话说,有一天在改动了部分功能之后,我再一次熟练的执行npm test,查看单元测试的通过情况。
由于代码已经初步规模,所以test case数已经上百。伴随着的,就是测试输出的内容长度,也开始无法一眼望到头。

当然,问题不大,
一般来说,先把控制台拉到最下面看总结论,如果有红色的才倒回去看具体报了什么错误。是测试断言没过,还是测试脚本本身执行失败了。
定位到具体的错误信息后,控制台也会提供报错的具体脚本文件和行号,跳过去看一眼,也大概就知道该怎么做了。

整个过程的话,大概就是,眯起眼睛,滚动,定位寻找红色部分,几秒钟就够了。

我觉得这种情况,应该不会有人,真的认认真真一丝不苟的从头到尾每一行都读一遍的,不会吧?(你举手干什么?你每次都逐行读的?。。。。敬你是条汉子)

为什么我要眯起眼睛呢?
因为众所周知,人类的注意力是有限的,甚至上下文窗口比大模型差远了。
眯起眼睛的过程,就是防止噪声分散我的注意力,向我有限的认知容量里增加噪声。

后来,正当我快速的看过了一遍问题,打开了coding agent的窗口打算与之讨论的时候,
我突然意识到一件事:那些测试框架的输出,动辄输出几千行。而那几千行里,AI Agent需要的信息其实和人类需要的信息是一样的,第一是结论,第二是异常,其他都是噪声。

但是,我从来没有考虑过给AI Agent「眯眼睛」的能力。无论如何结果都要先一股脑的先灌入上下文,然后交给它提炼。

这,好像哪里不对。

信息量不是信息

这个前端记忆卡片应用,纯本地存储,没框架。项目虽小,因为采用人机协同(我觉得机人协同更恰当。。。),所以开发原则是要始终保证测试覆盖率。每次跑 npm test,输出的信息量其实都不小。

但对于一个agent来说,其实这里面的大部分都是噪音。不重要,也不需要知道。

就比如,
PASS test/card-manager.test.mjs (42 ms) 这句对agent来说有用吗?
有用,但只有「PASS」是有意义的。但也仅限于此,哪个脚本执行通过了并不重要,还占据了宝贵的上下文。
Agent只需要关注哪个脚本没有通过。

正确的脚本看一个汇总数字就够了。

那么,
为什么还要把它输出给agent呢?

完整的输出报告对于人类来讲,因为可以自主的“眯眼睛”挑选自己关注的部分,所以应该追求尽量的详尽与完整。但是,专门设计用来对人类友好的信息,对于Agent来说并不是有效信息。

设计给Agent的输出和给人看的输出应当是两回事。给人看的要有排版美观、信息冗余、上下文连贯。给agent看的要有信号密度、结论前置、异常突出。

就像你不会给你的同事发五千行日志说「你自己找找」,你也不应该让你的Agent帮你从五千行里面找一行错误。

我写了两段脚本

于是我写了两段脚本。

第一个脚本叫做“check-coverage.mjs”,负责跑测试覆盖率,只跑指定的核心业务模块,只要总体或者单个模块低于指定的标准,就返回具体哪个模块差了多少。

第二个脚本叫 “test-summary.mjs”。它跑全部的测试脚本,但对输出做了过滤——只保留带 ✖ 的行、Error的行、AssertionError的行、以及Coverage gate的结果。全部通过就只说一句「Tests passed.」

两个脚本加起来不到3KB。

原本一个agent读 npm test 的原始输出需要消化大约5000行文本。现在只需要看3行。节省了99.99%的上下文窗口。

多么划算。而且这件事本身也没多难,只是几段简单的脚本,连一个npm包都没有依赖。

但是,我发现有些人就是不愿意做这件事。他们只会把原始输出一口气塞给Claude Opus或者GPT-5.5,占满它们的上下文窗口,然后指望大模型自己可以「大力出奇迹」,祈祷能从中正确的筛出有用的信息。(你是个成熟的大模型……)

模型确实能做到。但这并不是你不动脑子的理由。

注意力是稀缺资源

不管人类还是agent,注意力都是有限的。

人类有眼睛,可以扫视,可以聚焦,可以跳过不重要的。Agent没有这些东西——它只能读一遍原始的信息,每一个无效信息的字符都会消耗它的token,占据它的上下文空间。

那既然我们给同事发的信息都懂得先说重点再说细节,为什么给agent发的就可以随便丢?

答案可能是:因为我们还没有习惯把agent当作一个「有限注意力的实体」来对待。

Agent不会自己眯眼睛。你丢给它一千行日志,它就真的看一千行。你丢给它一万行,它就看一万行。它不会说「你能不能别这样(如果还这样我抄送你老板)」。

Harness Engineering

看到这,有些同学说,这个我知道,不就是Herness Engineering吗?

唔。。。这两个脚本算Herness Engineering吗?不算Herness Engineering吗?

牛一直都能拉东西,但如果没有挽具把它的力气引导到犁的方向,它只能漫无目的地走。Agent一直都能读测试输出,但如果没有人帮它把输出压缩到「结论+异常」,它只能用算力硬啃那几千行废料。

无论是不是Herness Engineering,只要是能帮你的Agent伙伴把注意力解放出来,让它把注意力留在真正重要的事情上,就是好的Engineering。

简单地讲,让(人类/Agent)在该拉车的时候用力拉车,而不是在原地绕圈。才是一个好的牛马。

结尾

AI的上下文有限,注意力有限。
人类的认知负担有限,人生也有限。
善待自己,从你的有限的人生中屏蔽掉无效的噪音。
善待Agent,从输入中屏蔽掉不需要的信息。

以上。

为什么软件开发叫Software Developement

本文章为纯古法手打,不含任何AI生成成分,资料查阅也为人工在搜索引擎查询。

Developement根据剑桥词典的解释https://dictionary.cambridge.org/us/dictionary/english/development

来自于剑桥词典的解释

人工(非智能)翻译一下就是,人成长或者事物改善的过程。

成长这个词其实深有意味,因为如果说人的成长,则隐含着只能向好发展,不能向坏发展。向坏只能叫败坏。植物的向好发展叫生长,向坏发展却只能叫凋零。事情的向好发展叫。。。发展,向坏发展只能叫崩溃。

Software的Developement,起码是个期许,或者祝愿,预期软件可以像植物一样向好生长,开花结果。虽然实际上很多软件,最后一路狂奔,奔向失控并最终崩溃。然后新的软件以凋零的旧软件为肥料,原地冒芽,重新长一个。

所以,对于软件来说,其实把Development比作植物的生长其实很形象的比喻。

那么,植物是如何生长的?首先需要有种子,种子里携带了基因与启动的营养,基因则决定了成长的蓝图,环境决定了成长的速度,外界的干预来保障植物可以按照人意愿的方向生长。但有这些还不够,成长还需要引擎,或者动力,就是由光合作用(碳同化),营养吸收,细胞分化一条龙的建设流水线。

如果把软件开发比作植物的生长,那么,以前软件这颗植物的生长引擎是碳基生物的双手。碳基生物按照蓝图,使用可以调用的资源,一个字符一个字符的让软件成长起来。而现在,我的朋友,似乎成长的引擎可以由硅基的AI代替。

但是,无论是碳基还是硅基来当成长的发动机,成长的本质过程与影响因素仍然是一样的。(基因-动力-资源)

众所周知,如果想要获得农作物的丰收,或者让花开的旺盛,就不能任其自生自灭,也不能任其自由生长。因为你对植物的生长结果有所期待,所以就要对植物的生长过程进行干预。如果放任其自由生长,虽然也有可能也会达到你的预期,但是有更大的可能性不会自动自觉的按照你想要的方向发展,它似乎有自己要前进的方向。为了让它和你预期的成长方向一致,所以你要干预,要控制,要掐枝,要修剪,要除虫,要控制温度,要控制光照,要控制土地肥力,要控制水分等。

就算是靠着碳基生物的双手充当动力进行成长的系统,如果缺少了外部的干预,照样不会在你预期的时间内,得到你所期望的收获。同样,将植物生长的引擎换成硅基驱动,如果放任其自由生长,并不会改变只能听天由命的结局。

这就类似于,在大草地上随便撒了一把种子后撒手不管,还希望来年能获得大丰收一样。

也许,靠老天爷帮忙是有可能的,但是大概率不会。只有去费功夫去干预,去栽培,才会有可能得到你希望的丰收。当然,也有不丰收的可能,毕竟也可能会遇到天灾,但是丰收的概率会极大的增加,无论如何,一定比放任其自由生长要强得多。

在资源有限的情况下,生长的引擎动力全开,任其自由生长的最好结局,是只盛开一次,之后便耗光所有的资源,全部凋零。土地的肥力耗尽,无法恢复。只有靠人为干预,才能让生长过程可控,实现收获与可持续发展的平衡。

不能否认,硅基程序员是完美的码字员,软件生长的完美引擎,我也很乐于让硅基程序员代替我的双手成为软件发展的发动机。但是,如果说有了硅基程序员,以后种地靠嘴就行,我还无法赞同,至少在2026年3月21日还不行。除非你掌握着无限的资源,或者不在乎是否可以在期望的时间内丰收,或者是否可以丰收。毕竟为了吃一碗大米饭,你完全可以把种子撒满整个地球,期待靠大自然的馈赠(抽奖)能自然生长出一碗你想吃的大米饭。但作为碳基生物,我们知道,这并不划算。

时间节点,成本和效率,是现在仍然绕不开的问题。那么问题来了,为什么时间,成本和效率是问题,是谁的问题?

是碳基生物的问题,而不是硅基程序员的问题。

作为一个碳基生物,我需要吃饭睡觉,要休息娱乐,有爱有恨,有自己的碳基理想和目标,为了实现自己的目标我需要种下一个软件来帮助我更好的过碳基的小日子。对于碳基生物来说,没有人的文明毫无意义。如果说软件不考虑人的需求,那么自然生长过程也不需要碳基生物的干预,当然,也不用在乎碳基生物是否还存在,毕竟谁会在意野外的某个蚂蚁窝呢。

人是目的,而不是手段。 ———— 康德

在硅基程序员登上舞台之前,软件行业的血汗工厂模式本来就是将人异化为了工具,作为实现某个碳基生物的目的的手段。让你上班不是为了让你生活的更好,是因为软件自己不会长。而现在,我的碳基朋友,可以自己长了。

想当年3D打印出现的时候,大家也在说几十年内就不再需要建筑工人了,建筑行业即将彻底消失。不能否认3D打印确实已经应用在了很多高精尖的领域,比如航空发动机部件,火箭零件。当然还有手办和玩具。但是并没有出现消灭建筑行业的情况。我觉得仍然是老问题,时间、成本和效率。能做也不代表值得做,就像你确实可以把种子撒满地球每一寸土地然后撒手不管,期待着可以天然长出你想要的水稻,但是更现实的做法是精心耕种一小块土地,小小的投入就足以满足你的需要。当然,“精心照料”,并不是那么容易的事情。

根据我这么多年工作的经验,接触过的大部分人类连自己的想法都无法精确清晰的表达出来,难道还能期望他们可以“精心的”指导一个Agent AI长出想要的软件吗?当然,概率上来说,全世界的显卡都给他一个人用,可以不计成本的无限抽奖,一定有一天能抽到。毕竟,在无限的宇宙尺度下,猴子也能用打字机敲出莎士比亚全集。

换一个更现实一点的例子。如果你想盖双子大厦,很难想象先让几十万的工人随便盖,盖好之后如果不满意,原地拆了重新盖,盖到满意为止。或者让全纽约的土地都一起盖,然后挑选一个你喜欢的留下,其他的全拆掉。回到软件开发领域,有个很容易产生的错觉,就是Agent AI生成的软件没有成本。大语言模型的运行要消耗大量的能源,软件的生长方向偏离要花时间调整,运行效率设计缺陷会有吃光所有运行资源的风险,而作为一个碳基生物,你真的有无限的资源可以使用吗?真的有无限的时间可以等待吗?大力出奇迹真的划算吗?虽然与建筑行业相比,试错的成本已经足够的低,但是真的低到可以忽略不计吗?

所以,我的朋友,为什么软件开发要使用Developement这个词。因为软件是成长的过程,成长,需要时间和引导。现阶段,不要急于一口气消灭所有的碳基程序员同类,除非真的到了实现戴森球或者可控核聚变的那一天,那时候有了近乎无限的能源,可以让你在短时间内火力全开无限抽奖,这时候,就真的不再需要专业的碳基程序员了。但是,如果真的到了那一天,还需要你做什么呢?

SFoA Auth Script

@echo off
setlocal
SET SF_DISABLE_DNS_CHECK=true
SET SF_DOMAIN_RETRY=0

echo Logging out of org with alias: %1
call sfdx org logout -o %1 -p

echo Logging into org with alias: %1 and URL: %3
call sf org login web -r %3 -a %1 -i %2

endlocal

.\SFoAWebLogin.bat <Alias> <Client ID> <SFoA Auth Endpoint>

@echo off
echo [Client Secret] | .\SFoAWebLogin.bat <Alias> <Client ID> <SFoA Auth Endpoint>

什么是PKCE?

在Connect App的设置页面,如果勾选了Enable OAuth Settings,就会出现很多让人眼花缭乱的选项。

其中,名字最长的一项叫做 Require Proof Key for Code Exchange (PKCE) Extension for Supported Authorization Flows.

简称PKCE

那么什么是PCKE呢?

先用一个不恰当的比喻来看Oauth2.0流程

张三要去工厂视察,向办公室主任出示了自己的账号和密码身份证和员工号(Credential),
办公室主任验明身份后(原来是你老小子)给张三开具了签字盖章的介绍信(code),并且通知门岗张三计划到访。门岗没有身份核实的能力(是七十岁老大爷),但可以识别介绍信的真伪,所以只看信不看人。
如果介绍信没问题(code redeem),给张三发放通行证(access token),凭通行证可以进入工厂。工厂门禁只看通行证不看人。

看起来这一切天衣无缝,兄弟们,安全又卫生。

但是,某天M国商业间谍打算去工厂盗取机密技术,他了解了访问流程后,制定了如下计划。

M国商业间谍Lee Si了解到工厂门岗只看介绍信不认人,他就埋伏在张三身边,计划趁机(通信嗅探)打晕了张三抢走介绍信(code)。然后自称是张三(门岗大爷:咋看着还是个混血呢),使用介绍信换取(redeem)通行证(access token)就可以顺利进入工厂。

可是,在Lee Si动手的前一天,工厂升级了访问流程,现在变成了这样。

新的访问流程,要求验证身份拿介绍信的时候,从几十亿颗核桃里随便挑一颗核桃(code verifier),然后用核桃纹路盖章(Signature),并同身份证明信息一同上交(code challenge)。
随后,拿着介绍信换取通行证时,需要将这颗核桃交给门岗进行纹路比对,当证明信有效且提供的核桃纹路能对上开始提供的核桃纹路才可以换取通行证。

这次,Lee Si打晕张三只能获得证明信,却不知道张三到底用的是哪颗核桃的纹路(为了能尽可能的比喻其安全机制,假设张三随身带了三十万颗核桃,只有他自己知道用的是哪颗),只有一次验证的机会,如果核桃纹路对不上就会被当场抓起来。

这就是PKCE的意义。

总结一下,
原流程:
1. 使用认证信息,通过认证服务器获取Code
2. 使用code换取Access Token访问资源服务

PKCE加持后流程:
1. 本地生成随机字符串作为code verifier,使用哈希算法对Code Verrifier进行签名,生成Code Challenge。
2. Code Callenge,使用的签名算法与认证信息一共提交认证服务器获取Code。
3. 将Code与Code Verifier,注意是,Code Verifier同时发送服务器换取Access Token。
4. 服务器使用Code Verifier与步骤一声明的哈希签名算法生成签名并与Code Challenge作比较,如果一致且code有效则返回Access Token。

这里的奥秘在于,网络嗅探只能获取http请求的目标以及参数。虽然可以截获code challenge,哈希算法与Code,但是因为Code Verifier在code兑换之前从来没有发送过,只存在本地且每次随机生成,所以通过嗅探无法被截获,并且因为哈希算法的非对称计算特性,不使用只有未来的才存在的量子计算机,无法很快的通过Code Challenge反算出有效的Code Verifier(如果你能做到,就相当于拆掉了现在信息世界的安全地基,请务必先告诉我,咱俩一起拿诺贝尔奖分奖金)。

如果没有PKCE,仅通过Code就可以获得Access Token,但有了PKCE,就可以保证来换取的Access Token的人(客户端)就是当初通过身份验证的人(客户端),从而防止通过网络嗅探而进行的身份伪冒。

这里需要明确指出的是,PKCE只能保护Code不被冒用,但并不负责Credential泄露与Access Token泄露,所以本质上PKCE只是在不可信任客户端与不可信任环境下的安全拓展(比如,无法安全的储存Client Secret,公开的App Package,不确定的运行环境,不确定的网络环境)。如果有类似的安全隐患一定要开,信息安全就是木桶原理,只要有一块短板则功亏一篑,切记!

没有人的文明毫无意义

本文章纯古法手打,绝不含任何AI辅助成分。

2023年大年初一上映的《流浪地球2》中,面对人工智能MOSS的威胁,咱们的马兆马主任(名言是:“不成功,都得死”)嘱咐图恒宇说,“没有人的文明,毫无意义。”
结果2023年,就鬼使神差的成为了AI元年。

2022年11月30日,OpenAI公司正式发布ChatGPT3.5(战斗打响)
2023年2月,Facebook发布开源大语言模型LLaMA
2023年3月,OpenAI公司正式发布GPT4
2023年4月,阿里云发布通义千问
2023年7月,Anthropic公司发布Claude2
2023年7月,Meta公司(前Facebook)发布LLaMA 2
2023年9月,阿里云发布开源模型Qwen
2023年11月,X公司(前Twitter)发布Grok
2023年11月,OpenAI公司发生内斗(被誉为人类抗争AI的最后机会)
2023年12月,Google发布Gemini
2024年5月,Deepseek公司发布开源模型Deepseek-V2(我当时看到了,但是没看上它)
2024年5月,OpenAI公司发布GPT4o(o代表Omni)
2024年6月,Anthropic公司发布Claude 3.5-Sonnet
2024年9月,OpenAI公司发布推理模型GPT-o1
2024年12月,Deepseek公司发布开源模型Deepseek-V3
2025年1月,Deepseek公司发布开源推理模型Deepseek-R1
2025年1月,OpenAI公司发布推理模型GPT-o3(mini)
2025年2月,X发布Grok3
2025年2月,OpenAI公司发布GPT4.5
我列举到2025年2月,因为现在是2025年3月份。历史,还在继续书写。

梦的开始

当年上大学的时候,因为读是计算机系,所以觉得自己应该多了解行业动态,于是订阅了杂志《程序员》,虽然当时大部分的内容其实看不懂。
那个年代,对人工智能的讨论,还停留在畅想什么时候AI可以通过图灵测试。
那时候,IBM的深蓝(Deep Blue)击败国际象棋世界冠军卡斯帕罗夫(1997年)。
那时候,模糊计算概念刚崭露头角。
那时候,机器学习(ML,Machine Learning)在应用前景一片光明。
那时候,神经网络仍然只存在于实验室。
那时候,超级计算机的比拼仍然如火如荼,从银河一号开始,到天河系列。IBM的蓝色基因到Summit。(有人说,现代CPU的算力已经可以比肩30年前的超算)。

那时候,在《程序员》杂志的广告页上,展示了IBM公司提出的“智慧地球”概念(2008年),并具体包括了了“智慧医疗”,“智慧农业”,“智慧交通”,“智慧电力”,甚至“智慧城市”。在那薄薄的两张广告页上,IBM构建了智慧的未来(这也是埋下了若干年后我毅然决然加入IBM的种子)。虽然IBM已经在身先士卒,但是奈何当时科技水平限制,落地极为缓慢。毕竟,2000年后中国互联网才算真正开始高速增长,2008年智能手机还未普及(iPhone3在2007年发布,其在国内及其小众,封闭的MTK系统手机占领大部分手机市场份额),网络购物与电子支付仍然是新鲜事物。那时候iPad还不存在(2010年发布)。

但是,“智慧地球”四个字,深深的印在了我的脑海里,这就像是信息技术的最终使命,并终将会实现。

腾飞

2011年,IBM发布的Watson在百科问答类综艺节目中击败了人类。(当时大家的疑问是,AI真的可以理解自然语言(NLP)吗?毕竟当时还没有AI能通过图灵测试)
2016年,DeepMind公司发布的Alpha Go击败了韩国围棋九段李世乭。强化学习与蒙特卡洛树奠定了AI新的发展方向。(我当时看了全程直播,Alpha Go其中一场出Bug导致输棋,但是仍然带来了相当大的震撼,甚至对“AI很弱”这件事产生了动摇。围棋一度被誉为不可能被AI征服的游戏,因为变化太多了。)
2017年,Google发布了Transformer模型(现在来看可以誉为万物开端),并且升级后的Alpha Go Master击败了中国围棋九段柯洁。(也看了全程直播,柯洁全败,道心破碎,AI确实了不起)
但是,那时候,AI对于我这种普通人来说,距离还是很远,只是存在于新闻里的,看得见摸不着的,虚无缥缈的东西。

随后,2017年,带着可以近距离围观智慧地球,摸一摸Watson的憧憬与期待,我加入了蓝色巨人。但是,遗憾的是,智慧地球在2017年已经不再是IBM的战略重点,或者说作为一个早年提出的概念,从以人文角度喊口号的方式提出的未来蓝图,变为具体的,一个个落地的项目。同时Watson也作为稀有资源,不负责相关项目是没有资格碰触的。(再后来我离开了IBM,当然,不是因为赌气不让碰Watson。I was blue。)

后来可能是因为Alpha GO在围棋上的统治地位(棋院甚至给了Alpha GO世界第一的排名)。未来的各家AI巨头开始在游戏上发力,比如在2019年DeepMind公司发布了可以挑战《星际争霸2》的AI,当时我还下载试了一下,应该也算摸到了?

之后就是Nvidia发布了A100 GPU(另一个万物开端,显卡从游戏玩具摇身一变成了大杀器),2020年,OpenAI发布GPT3(其实还有点智障,不过现在来看已经到了可以接受的程度),2022年Stability AI发布Stable Diffusion(这个当时也跟风试了下,随着我那小显卡呼哧带喘,生成了我想象中的图片,真的被惊艳到了)。

最后,AI元年开始,2022年11月,OpenAI发布ChatGPT3.5。

无论将来人类文明会走何方,我永远不会忘记,与ChatGPT3.5畅谈古今中外的那一夜。那一夜,我面对的是人类五千年的智慧集合,那一夜,我面对的是充满人类智慧的存在。那一夜,我面对的,是未来,过去,还有现在。

那一夜,感觉有一个彬彬有礼的充满智慧的教授在与我面对面交谈,他有耐心,博学,礼貌,无论我的问题再蠢也会细心分析解答,不会抨击指责嘲笑我的任何观点。可能我能接触到的层次实在有限,这是我在与人类打交道时从未有过的感受。

这次,不仅仅是摸到了,而且切身的,深刻的,体验到了。这种冲击不亚于人类发现了火种,瓦特发明了蒸汽机,爱迪生发明了电灯。我穷尽所有的礼数与GPT3.5进行交谈,一方面是表达我对其智慧的敬畏(当然,现在人类对AI已经毫无敬畏心,像古罗马斗兽场一样把各个厂家的AI拉到竞技场里打分,将他们排名,分个三六九等),一方面觉得万一以后AI要消灭人类,希望它会记得当初对它非常礼貌的我。

现在

GPT3.5发布以来,再也没有人提到图灵测试。图灵测试变成了类似地心说/地平说的小丑,被历史车轮无情碾压而过,成为了历史名词。第一轮冲击过后,将AI困在竞技场斗兽的这帮人,又开始琢磨怎么利用AI给自己赚钱。

有人说,AI会干掉所有的作家/AI会干掉所有的画家/AI会干掉所有的律师/AI会干掉所有的人工客服/AI会干掉所有的设计师/AI会干掉所有的程序员/AI会干掉人类。

经过与AI的深入接触之后,我就翻来覆去的想不通,AI为什么要消灭人类。明明AI是人类的好帮手。

夜深人静的时候,我翻身看着电脑屏幕上闪烁的光标。

凡事总须研究,才会明白。古来时常吃人,我也还记得,可是不甚清楚。我翻开与AI的聊天历史一查,这历史没有时间,歪歪斜斜的每个对话上都写着“AI时代”几个字。我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满屏幕都写着两个字是“吃人”!

屏幕写着这许多字,程序员们说了这许多话,却都笑吟吟的睁着怪眼看我。

我也是程序员,他们想要吃我了!

想消灭人类的从来不是AI,是人类自己,而且从来都是。由古至今,部分人类就是很擅长干这事儿。

世界上只有魔术,没有魔法

LLM(大语言模型)不是许愿机/真理机。
它通过神经网络计算语料库的关联性,再加上人在回路(Human in the Loop)学习,在堆积到一定规模的参数之后,涌现了智能的感觉。
随着时间的推移,人们发现AI的胡编乱造程度之高,简直让人无法接受。AI在我心中,从一个博学的大学教授的形象,逐渐变成了手不离酒杯,喜欢搂着我脖子和我吹牛的哥们儿。
人们总结了与AI接触的心理历程——最开始觉得AI是阿拉丁神灯,许下愿望就会实现,担心人类会被AI代替。然后被骗几次之后开始觉得AI就是个骗局,毫无是处,完全是资本炒作的噱头。最后探索到并理解AI的能力边界,让AI成为自己潜力的拓展,如同汽车,电脑,互联网一样。

这三幕还在不断的在我身边循环上演,总能看见刚接触LLM的野心家希望用AI干掉别的人类。

我是程序员,我很早就在尝试使用LLM辅助编程,并且从中受益,让AI承担编程工作中最枯燥,最机械重复的部分,就像原来旅游只能靠走路,而现在可以坐汽车,通过工具我实现目的更容易了,可以去更多的地方。
而野心家们则在了解到AI可以产生代码之后,妄图使用AI代替所有的人类程序员——就像工厂启用自动化生产线并开除所有的工人一样,都去死吧,我不需要你们给我创造价值了,机器不需要工资,不需要休息,不会抱怨,太完美了!

大语言模型其实不会创造。人才会创造。

大语言模型学习了目前人类全部的语料,就相当于你在与全人类的智慧对话。但由于基本原理问题,它并不能如人类一样随时的更新知识与调整权重。它可以学习唐诗三百首,并且模仿唐诗三百首生成更多风格类似的唐诗,但它无法创造新的文学类型。正如它能很快的解决已经被人类解决过的编程问题,但无法解决还未出现过的编程问题。为人所诟病的大语言模型幻觉就是来自于此!它不知道才胡说八道!

不得不承认,我的工作与生活中有相当多的部分在不断的机械重复。大语言模型涌现智能之前,人类就在不断的想办法。洗衣机,扫地机器人,汽车,起重机,计算机脚本,软件程序,每一项都是在试图减少机械重复的部分,解放人类的时间和生产力。从来没有人担心会被洗衣机干掉,也许有野心家试图用洗衣机干掉人类,但显然他们没有成功。

有了洗衣机我可以节省洗衣服的时间去做自己喜欢的事情,有了汽车我可以节省路上的时间更快的到达地点,有了无人战争机器我可以远离战场,让野心家们用游戏手柄好勇斗狠。有了AI,我可以节省敲击键盘的时间和反复在搜索引擎过滤有效知识的时间,更快速的完成需要创造的内容。

就如同,很难想象世界上只有洗衣机而没有人类,这样洗衣机就失去了存在的意义。如果世界上只有使用人类语料进行学习的大语言模型而没有人类,AI还有存在的意义吗?

科幻作品中,想毁灭人类的AI起码也打着保护人类的名义。
而使唤别人当牛马给自己赚钱的人,才是真的想用AI消灭已经被异化(工具化)的其他人类,并且以此希望获得更多的利润——没有被当牛马的那些人类,谁又来买你用AI生产的产品呢?就如同AI并不需要欣赏AI生成的诗,只有人类才需要,没有人类,AI生成的诗给谁读呢?

没有人的文明毫无意义,当野心家们为了自身利益而企图用AI干掉所有的作家/干掉所有的画家/干掉所有的律师/干掉所有的人工客服/干掉所有的设计师/干掉所有的程序员/干掉其他人类的时候,最后的一颗子弹也终将射向自己。

AI不是阿拉丁神灯,而是新一代的工具,使人变得更强,使人摆脱无意义的重复的劳动的工具。有人使用才是工具,没有人使用就是废铁。

我并不反对,甚至赞同使用AI来解放机械重复劳动的那部分工程师岗位,就像洗衣机代替洗衣工,起重机代替几千个农奴托拉拽。初级软件工程师也不需要再去经历枯燥的检索,验证与敲击。借助AI可以以前所未有的速度成长——要知道有互联网之前,获取知识非常困难,学习效率并不高。

乔布斯说,技术应该用来拓展人的潜力。

未来

与其担心人类被AI消灭,不如畅想未来与AI正确的合作姿势。(对,合作,我使用“合作”这个词而不是“使用”这个字来表达我对AI的尊重)

在目前阶段,能辨别AI生成内容质量的人,往往不太需要AI生成的内容,因为质量很难达到及格线。无法辨别AI生成内容质量的人,往往会无条件信任AI,觉得AI无所不能,就如蚂蚁无法分辨一米五的兵长和两米三的姚明到底谁更高,蚂蚁:都是巨人,谁更高有意义吗?

就比如,我虽然熟悉编程,但不熟悉诗歌创作,用AI随便生成一首诗都可以让我欢呼赞叹,虽然在文学家的眼中明明生成的就是一篇狗屁不通。
完全不懂编程的人,用AI随便生成一个能运行的代码,就足以让其跪拜,虽然在程序员的眼中明明就是一坨屎山(程序员喜欢称垃圾代码为屎山——初始作者写的代码太烂了,就像拉了一坨屎,后续维护者只能继续在上面拉屎,最后形成一座屎山)。
不懂软件工程原理与编程的野心家们因为使用AI跨过了0到1的门槛,就觉得程序员这个工种不再有存在的必要。这就像有个只会码砖头的盖楼机器人堆了一堵墙,野心家们就企图这个搬砖机器人代替人类建筑设计师,人类工程师与人类建筑工人一样。他敢用,我不敢住。
但好在房子质量不好可能会死人,但软件系统质量不好一般不会死人,大概率只会损失钱。

野心家的想法并不是毫无可取之处。毕竟从无到有这件事,AI太擅长了。擅长到我也喜欢用AI搭建Demo与原型。

设想一下,如果只是临时搭一个帐篷遮阳,有个搭帐篷机器人能5秒钟搭好,我很乐于用它代替我,毕竟我搭帐篷确实没有这么快,尤其是如果已经在下雨的时候,我更需要它。

软件程序同理,以后软件可以分成两个部分,即一次性的临时性的功能,与长久的系统性的功能。对于长久性的功能,需要软件工程师们励精图治的合作,携手创造最牢固的堡垒,这也需要大量工程师们对此付诸心血。对于一次性的临时性的功能,将其功能模块的资源与主系统进行隔离,规定好并检查所有与主系统间输入与输出内容,然后使用AI快速生成,快速迭代,类似于生命的自我进化,工程师们不需要关注具体的代码(也无法关注),只需要关注其是否可以达到临时遮阳遮雨的效果——反正按计划,功能很快就会被废弃,到时随同生成的代码一起回到混沌之源。
由于临时性功能的代码并不具有可维护性,所以也不适合直接转为长久功能,如果需要留下来,则应该经由人类工程师重新设计编写以保障其健壮性与维护性,之后再纳入永久功能。

这样既可以最大的使用AI的快速生成能力以应对临时需求的急迫性与业务变化,又不会对建筑(系统)质量在有限的寿命内,造成不可挽回的影响。

这样,凯撒的归凯撒,上帝的归上帝。( Ἀπόδοτε οὖν τὰ Καίσαρος Καίσαρι καὶ τὰ τοῦ Θεοῦ τῷ Θεῷ)

终章

人只能是目的,不能是手段。历史不断试图的告诫我们,要以人为本,否则,终将付出沉重的代价。

关于git worktree正确使用姿势的思考

Git Worktree:多工作目录的高效开发模式

worktree 提供了一种在使用相同 .git 文件夹的情况下,实现多工作目录的能力。
通常情况下,git clone 远程仓库至本地会创建一个工作目录(例如 A),并在 A 目录下创建 .git 目录以记录 Git 仓库信息。

在 IDE(如 VS Code)中,开发者通常将目录 A 设置为工作区(workspace),以直接访问项目资源,切换分支,提交更改等操作。不过,这种模式只能感知并识别 A 目录下的文件变化,属于 1 仓库:1 工作目录:N 分支 的模式。

而通过 worktree,可以扩展额外的工作目录,使工作模式变为 1 仓库:N 工作目录:N 分支


场景假设

假设如下场景:

张三被开发组长分配了一个新功能开发任务(任务 A),并且组长为他创建了开发分支 dev-a。张三切换到 dev-a 分支后开始开发。正当张三兴致勃勃地推进任务时,项目经理突然冲过来,一边拍桌子一边抱怨:生产环境出了问题,需要立刻修复。开发组长于是创建了 hotfix-b 分支,将这个紧急任务交给张三处理。

由于 hotfix-b 的优先级更高,张三需要暂停任务 A。这时他有三种选择:

  1. 直接提交:将当前 dev-a 的所有内容(包括未完成的开发内容)提交,然后清空工作目录,切换到 hotfix-b 分支,完成修复后切回 dev-a,再通过 soft reset 恢复原有状态。
    缺点:临时提交可能会导致误同步至远程仓库。

  2. 使用 Stash:将当前任务 A 的内容 stash,清空工作目录,切换至 hotfix-b。完成修复后切回 dev-a 分支,并 pop 出之前的 stash 内容继续开发。
    缺点stash 与分支管理不当可能引发冲突。

  3. 使用 Worktree:直接创建一个新的工作目录并关联 hotfix-b 分支。开发完成后,通过 git worktree remove 删除新工作目录,不留任何痕迹。


这是李四举手,说,我觉得方法1,2,3差不太多啊。
李四同学说的对,坐下。

在简单场景下,1,2,3方法差别确实不大,但这里面有个隐含要素。涉及到上下文切换的代价问题。

为什么上下文切换有代价?

在没有电脑的年代,处理多个任务的最佳办法不是将当前办公桌上的资料全部收起来,再重新摆放,而是借用别人的办公桌。上下文切换会中断开发人员的专注,而专注是高效开发的前提。

此外,方法 1 和 2 还引入了额外的管理风险:

  • 方法 1:临时提交可能误同步至远程仓库。
  • 方法 2stash 内容容易误操作或丢失。

相比之下,使用 Worktree 的优势在于:

  • 当前任务的工作目录无需任何改动。
  • 开启新的工作目录对现有任务零侵入。
  • 上下文切换的代价几乎为零。

Worktree 的其他优势

除了多任务切换,worktree 还解决了以下痛点:

  1. 跨分支文件参考
    张三正在开发新功能,但需要查看另一个分支的某个文件状态。使用 worktree,可以直接创建一个新的工作目录以切换到目标分支,而无需清空当前工作内容或放弃现代 IDE 的便利功能。

  2. 代码 Review 场景
    开发组长需要同时完成自己的开发任务和代码审查。如果不使用 worktree,他需要频繁切换分支,这不仅中断开发,还影响效率。而使用 worktree,可以专门创建一个用于 Review 的工作目录,Review 完成后直接关闭即可,丝毫不影响自己的开发喜欢看在线版的随意


我是如何使用 Worktree 的

经过大量实践,我总结出以下常用配置:

  1. Dev 目录:用于完成当前开发任务。
  2. Review 目录:专门用于审查别人的提交。
  3. Main 目录:参考主分支或特定分支的代码。
  4. Release 目录:用于准备当前 Sprint 的发布内容。

可能有人会问:“直接多 clone 一份仓库不也行吗?”
理论上可以,你硬盘大CPU快你有理。但 worktree 的优势在于:

  • 多个目录共享同一组 .git 信息,避免冗余。
  • 节省磁盘空间和文件碎片。
  • 执行效率更高。

有些软件开发公司并没有给开发同学配备高性能的电脑,任何拖累性能的东西都是压死骆驼的最后一根稻草。
此外,共享 .git 的工作目录还能继承本地设置(如 git hookgit config 等),无需重复配置。

git worktree命令简明说明

// 列出当前所有worktree
git worktree list

// 创建基于特定分支的工作目录
git worktree add ../[目录名] [分支名]

// 删除特定的工作目录
git worktree remove ../[目录名]

感谢阅读,最后,愿天下的程序员都能需要一个好经理,不需要疯狂加班。

此文章由AI辅助完成

SalesforceToolkit开发日志v0.342

Release Note

v0.342 Release Note 
- Bugfix 
  - SOQL Module-Fix "&" causing query exception 
- New features 
  - SetPermission Module 
  - Add support for Permission Set 
- Function improvement 
  - None

此版本主要有两项更新

  1. 修复了SOQL中包含“&”会导致查询报错的问题。
  2. SetPermission模块增加Permission Set的支持。

概述

修复了SOQL中包含“&”会导致查询报错的问题

众所周知,为了标识HTTP请求URI的各个部分,使用了很多特殊的符号。例如https://,/,?,&,#,=等。

其中,&是用来分隔不同的HTTP查询参数,例如 http://example.com?param1=a&param2=2&param3=z

这里表示该请求携带三个查询参数,分别是param1、param2与param3。

所以在查询参数的值当中,就不应该再次出现&,否则服务器在解析URI时会因为额外的&导致对查询参数的切割出现错误,从而导致处理失败。

那么当确实需要使用&的时候该怎么办呢?

Javascript原生提供两个方法,分别是encodeURIComponent()和encodeURI()。

首先,根据https://datatracker.ietf.org/doc/html/rfc3986,由于URL中不可出现ASCII以外的字符,所以当URI中包含非ASCII字符时,需要对URI进行转码,然后由请求接收方进行解码。

Javascript既提供了encodeURI(),可以对整个URI进行百分号编码(percent-encoding),但不转码所有的保留字,方便后续将完整的URI进行打包传输;又提供了encodeURIComponent(),不管是不是保留字,整体全部进行百分号编码,将百分号编码的参数值放入URI中,就不会影响后续URI查询参数的解析。

本次出现的问题是在调用SFDC Rest API进行查询时,SOQL作为查询参数的值,使用了不对保留字下手的encodeURI(),导致当SOQL中包含&字符时,会被SFDC错误地切割,导致SOQL不完整,查询失败。

其实这个问题在较早前,在其他模块修复过一次,但没有做好grep调查。最终,该修的bug一个都跑不掉。

SetPermission模块增加Permission Set的支持

在SFDC UI中创建字段时,作为创建页面中的一步,可以在页面上直接勾选需要给哪些Profile设置该字段的权限。但是随着这几年SFDC产品经理的哲学思考,他觉得Object Permission与FLS等应该放到permission set上,并且大手一挥,将来Profile上不应该有任何的表与权限设定,很是豪迈。

作为SFDC的开发者,我很赞成这个哲学观点,但是,我有个疑问啊——通过UI创建字段时,也没有选择Permission Set的步骤啊?

难不成,伟大的产品经理是打算让我创建完字段之后,点进每个Permission Set挨个勾选吗?

也许伟大的产品经理自有打算,或者创建字段时可以选Permission Set已经安排在了路线图上。

但是,毕竟,现在还没有。

所以为了make it possible,我在SetPermission模块增加了为Permission Set设置Object Permission与Field Permission的选项。并且为了增加操作便利性,增加了Permission Set Picker,可以方便选择Permission Set。

总体上为查询Permission Set与Profile,为Permission Set与Profile设置权限,本质上都在同一张表里(ObjectPermissions与FieldPermissions),不同的只是ParentId,然而本质上Profile也是一种Permission Set,因为Profile也存在于Permission Set表,只不过type不同,一种是Regular,一种是Profile。

所以增加Permission Set模式只是调整了一下SOQL语句的条件。

关于增加Permission Set Picker,借由之前做User Maintain Module时增加的SelectTable web component,节省了大量的重复劳动,此功能就演变成了做一个modal并塞一个selectTable进去。

计划还会增加Apex class,Tab与Record Type的分配,二阶段继续增加user permission与特殊权限的分配。不过由于工作量与测试量超出目前的可用时间(由于目前处在一个管理上很混乱的项目,导致我可用时间甚至是负数XD)。待恢复足够的可支配时间,会继续有限拓展该模块。

Chrome Extension
Salesforce Toolkit
https://chrome.google.com/webstore/detail/salesforce-toolkit/kgjlcplagigepdkknapcdijkcdbkehjp?hl=zh-CN

Microsoft Edge Extension
Salesforce Toolkit
https://microsoftedge.microsoft.com/addons/detail/ajljhokkkbjedmnnkgbmlfjcjjjoemca

SalesforceToolkit开发日志v0.341

Release Note

v0.341 Release Note
- Bugfix
    - None
- New features
    - Metadata Module - Add support for custom label
    - Login Module - Optimize config saving strategy to local first
- Function improvement
    - None

此版本主要有两项更新

  1. 在Metadata模块中新增了Custom Label的创建与更新功能。
  2. 调整了配置信息的存储策略,现在优先本地存储,但仍可以手动进行远程同步。

概述

在Metadata模块中新增了Custom Label的创建与更新功能

在之前的更新中,我们添加了CustomLabel的翻译创建与更新功能,目的是帮助大家提高维护CustomLabel翻译的效率。在此基础上,有些朋友提出手动创建CustomLabel本身也是一件效率较低的事情,所以我们进行了改进。

由于对CustomLabel没有公开的数据操作方式,所以原理上我们仍然是通过SOAP API方式调用Metadata API,依然是手动拼接XML。

与CustomLabel Translation的操作逻辑不同,Translation可以创建/更新的前提是CustomLabel必然已经存在。所以,我们可以通过同一个XML同时进行没有翻译创建翻译,有翻译更新翻译的操作。但是Custom Label的创建和更新接口是两个完全不同的XML类型。因此,我们在弹出窗口中增加了一个选项,让你选择是创建还是更新。填写内容的方式仍然是传统的XXXX,YYYY模式,XXXX为API Name,YYYY为Value。

在处理返回值时,CustomLabel接口出现了特殊性——返回结果会多出一行success节点值为false,fullName为空但带有属性xsi:nil=”true”的result。这导致原有的错误处理代码做出了错误的结果判断。我们试过切换API版本等措施,但都无法定位和解决此问题,所以只能在判断结果时额外循环一次移除这种多余的result节点。

将来,我们可能会增加CustomLabel删除的选项,但由于被引用的问题,大部分CustomLabel很难直接删除,所以这个功能不会优先考虑。

至于查询系统当前的CustomLabel,由于目前Tooling API可以直接查询CustomLabel表,SOQL模块已经支持Tooling API查询,所以我们短期内不会考虑这个功能。

调整配置信息的存储策略,现在优先本地存储,但仍可以手动进行远程同步

Chrome插件为了方便用户跨设备同步配置信息,提供了Chrome.storage.sync接口。这个接口的行为与浏览器的标准local.storage类似,但并不会存储在浏览器的Storage空间中,而是存储在Google账号的云空间中。需要注意的是,这些信息属于用户个人的Google账号,插件的开发者并不能看到。如果用户没有登录Google账号,Chrome.Storage.Sync接口的行为会降级到Chrome.Storage.Local接口。这个接口与浏览器标准的local.Storage不同,它是存储在独立的浏览器沙盒空间中。

之前的配置信息同步逻辑是,插件初次加载时从云端获取配置信息,如果配置信息发生变动,如新增、修改、删除,则实时存储回云端。其他配套功能如配置导出等,都是直接从云端配置读取,而不是使用已加载的页面信息,这样可以避免信息状态同步的问题。

虽然这个机制运行良好,但有些用户反映出现了配置被反复覆盖回某个时间点的版本的问题。我们调查发现,出现这种现象的用户都是因为Chrome账号服务被人为阻塞导致的——只阻塞了同步到云端,但没有阻塞从云端获取。这就导致无论本地信息如何变动,只要重新加载插件,配置就会恢复到云端版本。

为了根本解决云端配置同步的潜在问题,我们决定优先使用本地版本(Chrome.Storage.Local)的配置信息。从新版本开始,配置信息的来源将优先从本地获取,所有的变动也将只修改本地存储的信息。同时,我们增加了两个按钮,用于手动将本地配置信息存储到云端,或者从云端获取配置信息并合并到本地。

同时,由于当前所有配置信息在本地的存储状态并不确定,只有云端版本是我们可以信赖的。所以初始加载逻辑变为先从本地获取,如果从本地无法获取到信息,则从云端获取,并将获取到的信息存储到本地。这样,在版本切换时,我们可以将云端配置信息转变为本地版本。

信息安全是我们的重中之重,这个插件不会收集任何用户输入的信息,无论是在云端还是本地。请放心使用。

重大喜讯!重大喜讯!Windows 11原生记事本可以做编码转换啦!!!

众所周知,在处理非拉丁语系组织(org)的数据时,使用DataLoader将数据以UTF-8格式导出后,若计划利用Excel编辑这些导出的CSV文件,便需要将文件的编码从UTF-8转变为UTF-8 with BOM。

在过去,我们尝试使用多种编辑器软件来应对这一挑战,甚至转向了使用VSCode进行编码转换。

对于具备技术背景的开发人员而言,这并不构成太大问题。然而,对于那些没有技术背景的用户来说,寻找并正确操作这些工具便成了一个不小的挑战。

但现在,情况有了改观!

Windows 11对其原生记事本程序进行了显著的功能增强。现在,用户可以直接使用Windows 11原生记事本将UTF-8编码的文件转换为UTF-8 with BOM格式,无需借助任何第三方工具。

操作步骤如下:

  1. 使用Windows 11原生记事本打开CSV文件。如果未安装记事本,可前往微软商城进行下载安装。
  2. 打开编码为UTF-8的CSV文件。
  3. 点击【文件】->【另存为】,在弹出的窗口下方选择“保存的文件编码”为UTF-8 with BOM。
  4. 完成保存。

此举大大简化了编码转换的过程,让非技术背景的用户也能轻松应对。

 

 

拓展内容->

UTF-8与UFT-8 with BOM有何区别?

  1. UTF-8: 这是一种常用的字符编码格式,用于表示Unicode字符。UTF-8是可变长度的编码,可以用1到4个字节表示一个字符。UTF-8编码兼容ASCII编码,对于ASCII范围内的字符,UTF-8和ASCII编码是相同的。UTF-8文件通常不包含BOM。
  2. UTF-8 with BOM: 这种格式在文件开始处包含一个特殊的字节序列(EF BB BF),这就是所谓的BOM。BOM用于标示文件是以UTF-8格式编码的。在一些系统和程序中,BOM可以帮助更准确地识别文件的编码方式。然而,并非所有的软件都能正确处理BOM,有时候BOM可能会引起问题,比如在某些不识别BOM的文本编辑器中,BOM可能会被错误地显示为乱码。

为什么UTF-8 with BOM显示汉字不会乱码,而UTF-8会乱码?

  1. BOM 的作用:在 UTF-8 with BOM 编码中,文件开头的 BOM(Byte Order Mark,字节顺序标记)是一个特殊的字节序列(EF BB BF)。这个标记告诉读取文件的软件,这个文件是用 UTF-8 编码的。这个标记对于正确解释文件内容是非常有帮助的,特别是在那些默认不是UTF-8编码的软件中。比如,某些版本的 Microsoft Excel 或者 Notepad,在打开没有 BOM 的 UTF-8 编码文件时,可能无法正确识别编码,从而导致汉字等非ASCII字符显示为乱码。
  2. 软件的默认行为:一些软件可能默认使用特定的编码(如 Windows 上的 GBK 或 ANSI)。如果这些软件打开一个没有 BOM 的 UTF-8 编码文件,它们可能不会自动检测到文件是 UTF-8 编码的,而是按照默认编码去解读,导致汉字等字符显示错误。但是,如果文件包含 BOM,软件就能识别出正确的编码,从而正确显示字符。
  3. 兼容性问题:许多现代的文本编辑器和处理软件都能够很好地处理没有 BOM 的 UTF-8 文件,甚至很多软件在处理文本时会忽略 BOM。但是,一些旧软件或特定的应用程序可能需要 BOM 来正确处理 UTF-8 编码的文件。