Windows 3.0的简体中文化

这些年来,给收到的古董设备安装中文系统,基本算是我整备过程中的必选项目。无论是PC还是Mac,又或是各种PDA和智能手机,虽然寻找安装介质和驱动的过程未必轻松,特别是简体中文资源因为诸多原因留存甚少,但或多或少还能找到前人留下的成果。几年下来,手头完全没有中文解决方案的设备只剩两台,一台是HP 200LX,另一台则是Apple Newton MessagePad 2000。

这两台设备的中文支持问题一直在考虑之中。只是与普通PC相比,它们的平台都比较特殊,现有的中文软件资源也远没有Windows那么丰富。对于主要从事Java开发的我而言,从头研究显示驱动和古早操作系统的文字处理机制,时间和精力都不允许。不过近两年来AI辅助编程能力的进步,让这种探索逐渐有了实际操作的可能。既然之前已经用AI解决过旧设备兼容的Blog和同步问题,自然可以尝试把目标再往底层推进一些。

其实严格来讲,HP 200LX并非完全没有中文解决方案。毕竟本质上讲这台设备就是一个标准的PC/XT系统,因此早期支持8086和CGA的中文DOS环境无需改动即可正常运行。但既然是x86,能不能在上面运行中文Windows就是一个很值得探索的问题。

一、为什么是Windows 3.0

如前文所述,HP 200LX虽然有着近似电子词典的外形,本质上就是一个能够运行MS-DOS的PC/XT兼容环境。与后来采用Windows CE、Palm OS的PDA相比,这种设计让它可以直接使用相当一部分DOS时代的软件。当然,这样的兼容性也有边界,80186处理器、640K常规内存和CGA显示能力,基本决定了它能使用怎样的软件。

Windows 3.0仍然保留实模式,可以在这类设备上启动。实际上我也确实尝试过启动英文Windows,使用完全正常。Windows 3.0存在繁体中文版,但当我继续尝试安装时问题出现了,繁体中文版中没有CGA驱动。这就形成了一个有些尴尬的组合。英文Windows 3.0支持CGA,却没有中文处理能力;繁体中文版已经有了中文显示、输入及双字节处理机制,却缺少可以直接使用的CGA中文显示驱动。如果用英文版CGA驱动替换,窗口可以出现,但中文毫无意外的出现了乱码。

由此,想要让中文Windows在HP 200LX上跑起来的第一步,便是先解决繁体版的CGA驱动问题。

二、给繁体中文版添加CGA驱动

从DOS绘字到Windows驱动

1990s时代,显示汉字的基本过程并不复杂:识别编码,从字库中找到对应点阵,再把点阵画到屏幕上。但在正式修改Windows驱动之前,还有必要先确认字库格式和CGA显存操作本身没有问题。因此,最初实现的是一个8086实模式的DOS测试程序,读取磁盘字库后,直接在CGA图形模式下绘制中文。

CGA的显存并非按照屏幕扫描线连续排列,偶数行和奇数行分别位于不同区域。将实际汇编中的地址计算整理成C形式,大致如下:

/* CGA 640×200单色模式的像素地址计算 */
unsigned offset = (y >> 1) * 80 + (x >> 3);

if (y & 1)
    offset += 0x2000;

unsigned char mask = 0x80 >> (x & 7);

完成这一层验证后,我们即可以开始处理Windows显示驱动。探索中曾考虑过利用原中文字体体系中的DISPATCH.BIN转发调用,但检查接口后发现,它实际上是打印驱动使用的分发模板,缺少显示驱动需要的接口,无法直接拿来包装CGA驱动。

最终采用的方案,是以Windows 3.0 DDK中的CGA驱动为基础,在STRBLT.ASM的文字绘制路径中加入中文处理。原驱动继续负责既有的显示工作,新增代码则保存绘制参数、识别双字节、读取中文字形,并根据目标区域完成绘制。主要接入点如下,中间省略了原驱动代码:

draw_char_string:
    call chinese_save_context
    call get_clip
    ; ……原驱动处理……

strblt_clean_up:
    call chinese_overlay
    ; ……原驱动收尾……

exit_strblt:
    call chinese_finish

这样做的好处是,不必重新实现整套CGA显示驱动。但文字绘制涉及的情况远比最初的DOS程序复杂,除了直接画到屏幕,还要考虑裁剪区域、前景与背景颜色、透明绘制、字符间距以及内存位图。比如中文与英文混排时,英文驱动可能先画出两个错误的西文字符;而后续英文字符串的不透明背景,又可能擦掉刚刚补画的汉字。这些问题都需要在原有绘制流程中分别处理。

字库与内存

字库的存放方式同样需要取舍。对如今的电脑而言,几百KB的内存占用几乎可以忽略,但对实模式Windows而言,在640K的常规内存中塞进完整中文字库几乎可以把内存占满。因此,字库采用磁盘文件形式,运行时按需读取当前字形,只在驱动内部保留较小的缓冲和少量备用字形。

字体大小也经历了几轮调整。640×200的屏幕纵向空间十分有限,字号稍大,菜单和对话框就容易放不下;缩得太小,汉字笔画又会粘连或缺失。最终的原生方案保留了12、10、8像素三档点阵,分别用于不同的界面及正文场景。即便如此,CGA下的可读性仍然远逊于VGA,这也是后来需要单独调整菜单和对话框布局的原因。

到这一步,繁体中文版已经有了一条可靠的CGA中文绘制路径,具备了理论上的使用可能。但这一步显然不是终点,一是因为繁体中文与我的语言习惯相距甚远,二是繁体中文的文字结构天然与CGA有限的显示空间存在冲突。怎样能在CGA屏幕上让汉字更具备可读性,同时更贴合中国大陆用户的使用习惯——几乎是自然而然的,对简体中文的支持进入了下一步的视野中。

三、简单尝试——保留Big5,只更换简体字形

最初考虑的简体化方式,是保留系统的Big5编码,仅将字库中的繁体字形替换为对应简体字形。这样不必立即修改系统和应用的双字节处理逻辑,也方便通过更换文件在两种字形之间切换。

实现时使用OpenCC提供的单字繁简映射,逐个读取Big5码位,将对应的简体字形写回原字库位置。省略字库格式检查后,核心逻辑可以概括为:

# 示意:编码及字库槽位不变,仅替换其中的字形
traditional = raw_big5.decode("cp950")
simplified = mapping.get(traditional, traditional)

slot = big5_slot(*raw_big5)
font[slot] = glyphs[simplified]

除了CGA,期间也对VGA、EGA使用的字体路径做过验证。不同字体组件的内部排列并不一致,尤其是位图字库,不能假定其中的字形就是按照Big5码值简单顺排。直接套用这种假设,会出现字库文件能够加载,但显示成其他汉字的情况。

从显示结果看,这条路线是可行的。但继续深入就会发现,字形替换所能解决的问题相当有限。

首先是两岸间的计算机术语问题。不同的语言背景衍生了不同的语言习惯,简单的字形替换,把“檔案”逐字变成“档案”,并不会得到简体Windows中习惯使用的“文件”;“程式管理員”也不会自动替换为“程序管理器”。此外,字体只能根据单个码位替换字形,无法根据上下文判断词义,也不能实现传统意义上的繁简转换工作。

更重要的是,保存下来的文字仍然是Big5。屏幕上虽然出现了简体字形,文件中的字节却没有变化,与GB2312文件之间仍然需要编码转换。如果我们只局限于UI界面的简体化,这样或许能满足部分需求;但如果要输入中文、保存文档,并与其他简体中文环境交换文件,单纯的UI简体化并不能够解决问题。

因此,这个方案最终保留为一次字库实验,没有成为后续系统的基础。

四、英文Windows的外挂中文尝试

既然英文Windows本来就支持CGA,另一条值得尝试的路线,就是在英文系统上增加一套外挂中文环境,由驻留程序负责GB2312输入、绘制和界面翻译。实际上早期的简体中文环境,如中文之星等,大多数都基于这条路线尝试。而我最初的目标,也是希望能产出一份对标中文之星基本功能的介质。

在这条路线的尝试耗费了我近一周的时间和多次Codex额度重置。对于标准EDIT控件,可以保存原窗口过程,通过子类化处理输入、移动、删除和重绘;对于菜单,则可以调整原菜单宽度,使用owner-draw绘制中文下拉项,并保留原命令和快捷键。

以标准编辑控件为例,显示部分需要让原控件先完成背景等绘制,再补画中文字形。将处理过程简化后,可以理解为:

/* 示意代码,省略重绘调度、裁剪和窗口销毁处理 */
result = CallWindowProc(old_proc, hwnd, message, wp, lp);

if (message == WM_PAINT)
    DrawGb2312Text(hwnd);

return result;

实际实现当然要复杂许多。删空最后一个汉字之后,需要及时清掉原字形;菜单翻译成中文后,需要同步调整显示宽度与鼠标区域;标题栏激活和失焦时,也要跟随原系统颜色重新绘制。否则很容易出现文字已经变成中文,但鼠标仍然只能点击原英文位置,或者切换一次窗口又露出英文的情况。

中文文件名则暴露了另一个问题。英文系统会在ANSI与OEM之间进行西文代码页转换,GB2312文件名的高位字节经过转换后,便可能与磁盘上的原始名称不一致。为此,外挂方案还生成了专用键盘驱动副本,修改相关转换表,让这些字节原样往返。实际构建脚本中的主要修改如下:

data[ansi - 2:ansi] = struct.pack("<H", 936)

data[ansi:ansi + 96] = bytes(range(0xA0, 0x100))
data[oem:oem + 128] = bytes(range(0x80, 0x100))

不过,这仍然不能解决所有问题。文件管理器使用自己的目录列表,写字板使用自己的文档窗口,画图里的文字最终又要进入位图。标准控件上的处理方法,并不能直接套到这些程序上。所以,外挂方案需要为不同程序分别增加识别和适配逻辑,有时还要记录文字位置,或在打开文件之前预读正文。

实机的测试进一步暴露了模拟器中不易察觉的问题。图标名称的位置、选中后的重画顺序,以及频繁扫描目录造成的使用影响都需要重新优化。图中的程序管理器便是这一阶段的结果,虽然中文已经出现在真实屏幕上,但布局与操作体验距离正常使用还有相当一段距离。特别是对于实模式Windows而言,外挂方案还需要在内存常驻一个汉化程序,让本就不宽裕的常规内存雪上加霜。或许也正是因为此,当年几乎所有的外挂中文方案,均要求Windows 3.x的标准或增强模式支持。

当然,外挂路线也并非一无所获。证明了Windows 3.0上实现GB2312支持的可能性,但它的适配成本也逐渐清晰:越接近实际使用,越需要介入每个程序自己的文字处理过程。与其继续在外部补齐这些行为,重新利用繁体中文版已有的双字节机制,反而成为了更合适的选择。

五、基于繁体版机制的原生GB2312环境

最终采用的方案,是保留繁体中文版已有的中文处理基础,将编码、字库、资源和输入法等相关环节改为GB2312编码。

这里所谓的“原生”,是指运行时使用系统原有的中文处理路径,不再依赖前述通用驻留外挂。系统仍然建立在原有Windows及字体组件之上,其中部分模块通过二进制补丁修改,另一些组件则重新编译。

字形索引与字库重建

首先要处理的是编码与字形的对应关系。Big5与GB2312虽然都使用双字节,但不能将同一组字节对应到相同汉字。在VGA的WIFE字体路径中,需要修改CFFONT.FOD原有的索引函数,并按新的规则重新生成字库。

GB2312的区位索引计算如下:

def gb_ordinal(code: int) -> int:
    return (
        ((code >> 8) - 0xA1) * 94
        + ((code & 0xFF) - 0xA1)
    )

这套字体容器中的位图索引使用275 + ordinal,轮廓字形使用5000 + ordinal。构建时放置字形的位置,必须与运行时补丁完全一致,否则仍然会出现编码正确、字形错误的情况。补丁同时保留原NE模块的段布局与重定位结构,并在写入前检查原文件身份和目标字节。

CGA则继续使用前面建立的磁盘点阵容器,其内部仍沿用与繁体中文相同的157列槽位布局,但写入其中的字形及运行时识别规则改为GB2312。因此,CGA和VGA的字库内部组织方式不同,对同一组GB2312字节的解释却必须一致。最终字库覆盖7,445个有效GB2312双字节码位,其中包括6,763个汉字。

这一阶段还解决了一个比较典型的乱码问题:程序管理器显示正常,从中打开文件管理器后,却出现大量错误字形。

排查后发现,问题来自为了减少磁盘操作而缓存的DOS字库句柄。这个句柄与当前任务的DOS进程环境有关。DOS/Windows 3.x时代,应用程序间的内存不存在隔离(所以当时经常发生一个程序错误拖垮整个系统的情况,这个问题直到Windows NT内核成为主流后才真正被解决),因此当跨Windows任务保留后,并不能保证原有内存指针仍然指向原字库。后续读取可能落到其他文件上,于是文件内容便被当作点阵画了出来。

修复方式是将句柄的生命周期限制在一次所属绘字调用内,结束时关闭,下一次调用重新打开。虽然增加了文件打开操作,却避免了跨任务复用句柄造成的内存数据读取错误。

系统资源与输入法

系统界面的转换则通过解析NE资源完成,分别处理菜单、对话框和字符串表。早期转换尽量保持字段字节长度,避免改变资源偏移及其他二进制结构,再按模块处理术语和布局。

考虑到前文中提到了两岸术语问题,这一步同样不能只做机械的繁简转换。比如写字板中的File与Document,如果都翻译为“文件”,便会形成两个同名菜单,因此需要根据上下文分别处理为“文件”和“文档”。CGA下还要压缩过高的对话框、为超长菜单增加分页,并调整程序组布局。

输入法则接入原繁体中文系统WINNLS机制,通过PHON.IME入口实现全拼组字和GB2312提交。与字库类似,拼音词典也放在磁盘上按需查询,避免整体占用常规内存。

实现过程中,消息提交方式是一个关键问题。简单地同步调用两次SendMessage(WM_CHAR),并不一定能经过原系统的双字节处理路径;字符需要进入目标任务的真实消息队列,而队列容量又很有限。因此,输入法内部增加了固定大小的待发送队列,投递失败时等待目标任务运行后再试。实际提交代码的核心如下:

for (offset = 0; offset < candidate_lengths[choice]; offset += 2) {
    QueueMessage(destination, WM_KEYDOWN, 0x280, 0L);

    QueueMessage(destination, WM_CHAR,
                 candidates[choice][offset], 1L);
    QueueMessage(destination, WM_CHAR,
                 candidates[choice][offset + 1], 1L);

    QueueMessage(destination, WM_KEYUP, 0x280, 0L);
}

其中QueueMessage是自行实现的排队函数,内部通过PostMessage投递;0x280配合原系统的中文输入路径使用。后续输入法扩充到了6,763个带读音汉字和24,678个不同词组,可以满足基本的单字与词组全拼输入需求。

VGA环境下的系统界面

除了面向HP 200LX的CGA路径,原生GB2312方案也保留了VGA支持。相较于CGA有限的纵向空间,VGA能够更完整地呈现原有界面,也便于对照检查中文资源和字体。两种显示环境使用相同的GB2312文字,但由于底层字库及绘制路径不同,因此仍然需要分别验证。

六、Word与Excel的适配

经过前序的验证,我基本完成了原生简体中文Windows 3.0版的构建。但完成一个裸操作系统并不是任务的重点,如果想真正用这台设备处理一些“生产力”任务(其实只有吃饱了撑的才会用这玩意干活),应用软件的进一步适配便自然成为了下一步的焦点。

当年绝大多数的应用软件并没有考虑过双字节字符的输入和显示问题,因此即便操作系统能够显示汉字,并不意味着应用的排版、光标和删除操作会自动遵守汉字边界。为了满足“生产力”软件的要求,考虑到Windows 3.0实模式本来支持的应用软件就极为有限,本次选择了典型的微软自己的Word 1.1a和Excel 2.1d。而两者的中文支持也选择了不同的实现方式。

Word的中文处理

Word的有利条件在于,计算机历史博物馆已在微软许可下公开其1.1a版本源码,供非商业用途研究。因此,可以直接修改文档编辑和排版逻辑。不过,恢复旧构建流程本身也花费了不少工作,源码能够阅读,与能够重新生成可运行程序仍然是两回事。

Word需要处理的核心,是文档字符流中的双字节边界。基本判断如下:

#define FWordGbLead(ch) \
    ((unsigned char)(ch) >= 0xA1 && \
     (unsigned char)(ch) <= 0xF7)

#define FWordGbTrail(ch) \
    ((unsigned char)(ch) >= 0xA1 && \
     (unsigned char)(ch) <= 0xFE)

#define FWordGbPair(lead, trail) \
    (FWordGbLead(lead) && FWordGbTrail(trail))

但文档并非一块连续字符串,汉字的两个字节可能恰好落在不同的分片或属性片段中。因此,取字和排版时还需要检查相邻文档位置,保证整个汉字共同参与宽度计算、换行和编辑。否则,即使屏幕已经能显示中文,行尾仍然可能拆开汉字,退格也可能只删除其中一半。

Word的长文档与表格

Word的适配也经历了从正文到界面的过程。早期先处理双字节输入、排版和编辑,因此会出现中文正文已经能够显示,而菜单仍然保留英文的阶段。下面的长文档测试便属于这一时期,主要用于观察连续中文、换行和多段落显示。

正文之外,表格等功能还需要继续检查。它们既涉及应用自己的文档模型,也包含独立的对话框和控件,不能因为普通段落可以输入中文,就认为其他编辑功能也已经完成。

Excel的编辑器与图表

Excel由于没有Word那样的源码,汉化过程选择了保留其原有计算和文件处理核心,完成界面资源本地化,再通过应用专用辅助程序处理公式编辑框的处理路线。安装后的EXCEL.EXE启动配套的XLCORE.EXE,并在该实例运行期间维护双字节编辑行为。

这里除了需要处理整字移动、选择和删除,还遇到了重入问题。在Excel正在处理按键时,立即同步查询公式栏文本,可能打乱其内部处理流程。因此,窗口过程先将操作排队并返回,随后再查询文本、确定字符边界并执行编辑。主要代码如下:

if (window == formula_window &&
    message == WM_KEYDOWN &&
    (key == VK_DELETE || key == VK_BACK ||
     key == VK_LEFT || key == VK_RIGHT) &&
    flags != INPUT_SENTINEL) {

    QueueInput(key, 0);
    return 0L;
}

确定边界时需要从字符串起点逐字解析,不能只检查光标前一个字节是否位于高位范围,因为GB2312首字节和尾字节的取值范围存在重叠。

Excel的适配进一步延伸到了独立图表。旧版Excel将工作表和图表分别保存为XLS、XLC,图表引用的工作表名称和路径也需要保持一致。因此,除了中文标题和图例,还要检查数据引用、格式修改,以及保存后的重新打开。

打印界面同样需要单独处理。这里既有翻译后的固定标签,也有来自驱动和应用的动态内容;进入打印对话框之后,还要继续验证预览和输出,并考虑实模式下的内存限制。

后续适配还涉及Word表格、页眉页脚、脚注、域和图片,以及Excel工作表、独立图表、批注和多窗口等功能。验证时除了检查画面,还需要经过原生保存、关闭和重新打开,确认内容与引用关系能够保留。

同时,旧应用的原有语义也需要继续遵守。例如这版Excel的字符串函数仍按字节工作,LEN(“中文”)得到4,LEFT(“中文”,2)才得到“中”。中文界面和双字节编辑支持,并不会自动改变其全部计算行为。

七、真机验证

开发目录中的系统能够运行之后,还需要将它整理成真正可以安装的介质。最终介质仍然使用原版Setup,并加入CGA选项、GB2312组件和安装收尾过程。

接下来就是真机的测试过程。得益于Dosbox-X在最新版本中对MCP的完整支持,使得Codex有条件直接操作模拟器对整个构建过程实现了极为完整的测试,真机的验证过程反而显得十分顺利。安装时的最大问题是安装程序的SHA-256校验,在80186上跑如此复杂的校验过程耗时极长。在调整了安装包的实现后问题得以解决。

在HP 200LX上,程序管理器、文件管理器等界面的简体中文界面均有较好的显示效果。自带的写字板、画图等功能也一切正常,并可以支持中文全拼词组输入。

Word和Excel的真机验证也无问题,由于内存有限,两者的启动过程都较为缓慢。但启动后的使用基本正常。当然,由于较小的屏幕尺寸和CGA的限制,二者的实用性仍然有限。

八、当前里程碑

最终整理出的里程碑介质包括原生GB2312的Windows 3.0a、CGA和VGA组件、全拼输入法,以及配套的Word 1.1a和Excel 2.1d。CGA使用实模式,通过WIN /R启动;VGA则覆盖实模式、标准模式和386增强模式。中文文件名仍然遵守DOS 8.3规则,文件主名最多容纳四个GB2312汉字。

从探索过程看,最初补齐的CGA绘制能力最终得到了保留;Big5简体字形实验帮助理清了编码和字库的关系;外挂方案则让标准控件、自绘窗口、文件路径和输入消息中的问题逐步暴露出来。最终采用繁体版机制实现GB2312,也正是建立在这些尝试的基础上。

相关代码见:。由于版权与许可证限制,具体的构建过程

后记

回头来看最近的几篇博客,似乎在无意中留下了一份AI辅助编程的使用记录。从第一篇到现在不过两年多,AI能够参与的工作范围,却已经有了相当明显的变化。

在2024年的《奇怪的需求1》中,我需要批量校正照片的Exif时间。当时计算方法已经想得很清楚,缺少的是读取和写入Exif的具体手段。因此,ChatGPT主要承担了查找相关库、解释API和提供例程的工作,我再将代码放进IDE,结合自己的需求完成修改。从使用方式上看,它更像是一个能够直接给出代码的技术助手,帮助我省去了翻阅大量文档的时间。

到了《奇怪的需求2》,问题已经扩展到了完整的内容转换流程。为了让古董设备访问现有博客,需要配合WordPress内容读取、HTML模板、图片格式转换、字符编码处理和Shell脚本编排。这里已经很难用一个函数或一个库解决全部问题,需要将多种工具组织起来,让它们共同完成一个目标。AI辅助实现的范围,也随之从局部代码扩展到了多个环节之间的配合。

而在《奇怪的需求3》中,为了让旧设备继续同步联系人、日历和待办,项目涉及了Microsoft Graph、EWS、认证、数据存储以及前后端界面。借助命令行AI编程工具,开发过程已经能够直接围绕项目展开,很多过去需要手动复制、调整和衔接的工作,都可以交给AI连续完成。对主要从事后端开发的我而言,能够把前端等不熟悉的部分也一并实现,其意义已经超过了单纯提高写代码的速度。甚至于文章本身,主要也是AI生成。

而在这一次,探索又进入了一个完全不同的领域。Windows 3.0的显示驱动、16位汇编、NE资源、双字节文字处理,以及旧版Word和Excel的内部行为,都不属于我平时工作的范围。资料零散、工具古老,很多问题也没有现成答案。AI需要参与的工作因此不再局限于实现既定方案,还包括阅读源码和二进制结构、编写探针、运行模拟器、检查截图及保存文件,再根据结果继续修改。虽然期间仍然有误判和反复,但与第一篇中通过对话取得一段例程相比,已经是很不一样的工作方式了。

应当说,AI对个人能力的放大,在这几个项目中体现得愈发直接。过去一个人能够完成什么,除了取决于想法,也受限于自己熟悉的语言、平台和工具,以及能够投入多少时间。对于工作之外的个人项目,时间往往又是最难解决的问题。即使愿意从头学习,也未必有精力把所有陌生领域都研究到足以独立实现的程度。AI补上了其中相当一部分实现和资料处理能力,使一些原本只能长期搁置的念头,进入了可以实际尝试的范围。当然,这种放大也并不意味着人可以完全退出过程。只从本次构建来看,简体字形是否足够,文件是否必须使用GB2312,外挂方案的适配成本是否值得继续承担,以及模拟器结果能够说明多少实机问题,都需要在过程中不断判断。如果目标没有定义清楚,AI同样可以沿着一条并不符合实际需求的路线,写出大量代码。人的工作会越来越多地落在问题定义、技术取舍和结果判断上,而这些工作仍然需要经验和对实际使用场景的理解。

不过,在我看来,人的核心竞争力仍然是想法,以及想法背后那些具体的生活经验。校正写真照片的拍摄时间,是因为我希望NAS里的照片能够按真实时间排列;让旧设备访问博客,是因为已经花费时间把它们连上了网络;同步日历和联系人,是希望这些设备仍然能够参与自己的日常生活;给HP 200LX添加简体中文,则是因为这台机器就在手边,而我始终觉得它还可以再完善一些。这些需求未必有多大的市场,也未必值得一个商业团队投入,但对提出需求的人而言,它们自有其价值。

从这个角度看,AI的进步让我更愿意去保留那些看起来有些奇怪的想法。过去觉得没有时间、没有相关知识,或者一个人很难完成的事情,如今已经可以换一种方式开始尝试。至于最终做出来是否有用,至少可以在亲手试过之后再下判断。

当然,AI确实也容易让人变懒。比如这篇文章,你猜是谁写的?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注