2024 年初的大语言模型编程实践

2024 年初的大语言模型编程实践

原文:LLMs and Programming in the first days of 2024
作者:Antirez

首先我要明确,这篇文章并不旨在回顾大语言模型。显而易见,2023 年对人工智能来说是不平凡的一年,再去强调这一点似乎没有多大必要。这篇文章更多是作为一位程序员的个人体验分享。自从 ChatGPT 出现,再到使用本地运行的大语言模型,我就开始广泛应用这项新技术。我的目标不仅仅是提高编码效率,更重要的是,我不想在编程中那些无需过多精力投入的地方浪费时间。不愿意花费大量时间去查找某些专业且无趣的文档,不想为了学习一些过于复杂且往往无需如此的 API 而劳心费力,也不想编写那些几小时后就会被我抛弃的临时代码。尤其是如今 Google 成了一个充斥着垃圾信息的海洋,我们只能在其中努力寻找那少数有用信息。

同时,我也不是编程领域的新手。我完全有能力在没有任何辅助的情况下编写代码,而且我也常常这么做。随着时间的推移,我越来越频繁地借助大语言模型来编写高级代码,特别是 Python 代码,而在 C 语言方面则相对少一些。在体验大语言模型的过程中,我深刻认识到,应该在何时使用它们,以及何时它们的使用反而会拖慢我的步伐。我还了解到,大语言模型有点类似于维基百科和 YouTube 上琳琅满目的视频课程:它们对那些有意愿、有能力和自律的人大有裨益,但对于其他人来说,帮助有限。我担心,至少在初始阶段,它们更多的是惠及那些本就占据优势的人。

但我们还是一步一个脚印来吧!

全知全能还是鹦鹉学舌?

在机器学习新浪潮中,最让人担忧的是 AI 专家们难以接受自己知识的局限性。人类发明了神经网络,更关键的是,还发明了一个自动优化神经网络参数的算法。随着硬件能力的提升,能够训练更大的模型,利用数据的统计知识(即先验知识),通过不断的尝试和错误,逐渐找到了一些比其他架构更有效的模型设计。但不管怎样,神经网络本质上还是相当复杂且不透明的。

面对大语言模型一些新的无法解释的能力,谨慎的科学家们反而低估了它们。许多人认为,大语言模型不过是稍微高级点的马尔科夫链 (Markov chains),最多只能重复训练集里有限变化的内容。然而,越来越多的证据表明,这种看法几乎可能是大错特错的。

同时,很多吃瓜群众过分夸大了大语言模型的能力,认为它们拥有现实中根本不存在的超自然力量。事实上,大语言模型最多只能在其训练数据所代表的空间内进行插值,即使如此,它们的这一能力也已经相当惊人。真要是今天的大语言模型能够在看过的所有代码构成的空间内自如插值,它们即便不能创造真正的新颖事物,也足以取代 99% 的程序员。但现实情况要并没有这么乐观。大语言模型确实可以编写一些它之前未曾见过的程序,展示出将不同思想的训练数据融合的能力,但这种能力目前还有很大的限制,尤其是在需要细腻推理时,它们往往无法胜任。尽管如此,它们仍代表着从人工智能诞生至今的最伟大成就,这一点似乎无庸置疑。

无知却博学

确实,大语言模型大体上只能进行初级的推理,这些推理经常不准确,甚至会掺杂着一些关于不存在事实的幻觉。但它们却拥有海量的知识。在编程领域,以及其他有高质量数据的领域,大语言模型就像是理解力有限却知识渊博的人。如果要和这样的伙伴进行结对编程(对我来说,结对编程本身就是个痛苦),可能会很糟糕:它们可能提出一些荒谬的想法,我们需要不断斗争才能贯彻我们自己的想法。但如果这个博学的傻瓜能够听从我们的指令,回答我们提出的所有问题,那一切都会变得不同。现有的大语言模型虽然不能引领我们超越已知的路径,但如果我们想探索一个不熟悉的领域,它们往往能够帮我们从一无所知到掌握足够的知识来独立前行。

在编程领域,直到二十或三十年前,这些能力可能还不太引人注目。那时,你需要掌握几种编程语言、经典算法以及那些基本的库。其余的则依靠你自己的智慧、专业知识和设计能力。具备这些素质,你就能成为一名全能的专家级程序员。然而,随着时间的推移,我们见证了框架、编程语言、各种库的大量涌现。这种复杂性通常是不必要的,甚至无法自圆其说,但事实就是如此。在这样的情况下,一个无所不知的“白痴”成了宝贵的盟友。

我来举个例子:我对机器学习的实验最初是用 Keras 进行的,持续了至少一年。后来因各种原因,我转向了 PyTorch。我已经了解什么是嵌入(Embedding)或残差网络(Residual Networks, ResNets),但我并不想深入研究 PyTorch 的文档(就像我学习 Keras 那样,那时 ChatGPT 还不存在)。有了大语言模型,用 Torch 编写 Python 代码变得非常容易。我只需清楚地了解我想要构建的模型,并提出合适的问题。

应用案例

我要讨论的不是那些简单的问题,比如“嘿,类 X 是如何执行 Y 操作的?”如果只是这些问题,那些对大语言模型保持怀疑态度的人可能就有理由了。但实际上,更复杂的模型能做的事情远远超出这些。几年前,这些还被认为是不可思议的魔法。我可以这样对 GPT4 下指令:看,这是我在 PyTorch 中实现的神经网络模型。这里是我的数据批次。我想调整这些张量的大小,以便它们能与神经网络输入的要求相匹配,并且我希望以这种特别的方式展现它们。你能帮我写出调整这些张量尺寸的代码吗?GPT4 帮我写出了代码,我接下来要做的,就是在 Python 命令行界面中测试这些张量是否真的符合我需要的大小,以及数据结构是否正确。

再举一个例子。不久前,我需要为基于 ESP32 的设备开发一个蓝牙低能耗 (BLE) 客户端。经过研究后,我发现多平台蓝牙编程接口大都不好用。解决方法很简单,就是用 Objective C 和 macOS 的原生 API 来编写代码。但这样一来,我就面临着两个问题:一是学习 Objective C 中复杂的 BLE API,这些 API 充满了我认为完全没有必要的复杂设计(作为一个极简主义者,这些设计与我所认为的“好设计”截然相反);二是回忆起怎样使用 Objective C 编程。我上次使用 Objective C 编写程序是十年前了,很多细节,比如事件循环、内存管理等,我都已经记不清了。

最后的结果就是这段代码,虽然它看起来不是很美观,但它完成了它的任务。我在极短的时间内就编写完成了。否则根本不可能做到这一点。

https://github.com/antirez/freakwan/blob/main/osx-bte-cli/SerialBTE.m

这段代码主要是通过在 ChatGPT 上复制粘贴我想实现但不太确定如何着手的功能来编写的,因此最初它们并未能正确运行。然后,大语言模型帮我指出了问题所在并告诉我如何解决。虽然大部分代码不是由 LLM 直接编写的,但它确实极大地加快了编程速度。不用 ChatGPT 我也能完成这个任务吗?答案是肯定的,但更有趣的不仅是它节省了我很多时间:事实上,如果没有 ChatGPT,我连尝试的勇气都没有,因为那似乎并不值得。这一点至关重要。对于我的项目来说,编写这样一个不重要的程序的努力与其带来的好处的比例本来是不划算的。此外,这个过程产生了一个比程序本身更有用的副作用:为了这个项目,我对 linenoise(我用于行编辑的一个库)进行了改造,使其可以在多路复用环境下运行。

这是另一个例子,更多地涉及到数据解释而非代码编写。我打算用一个我在网上发现的卷积神经网络 (Convolutional Neural Network) 设置一个 Python 脚本,但这个网络缺乏详细文档。网络的一大优势是它采用了 ONNX (Open Neural Network Exchange) 格式,这使我能够轻松地识别出网络的输入和输出以及它们对应的名称。我对这个卷积网络了解的唯一一点是:它能识别图像中的特定特征。但我不知道所需输入图像的格式和大小,更何况,网络的输出比我预想的要复杂得多(我原本以为它是一个二元分类器 (binary classifier),用于判断观察到的图像是否正常或存在问题。我原以为它只有两个输出,但实际上有数百个)。我首先把 ONNX 网络的元数据输出复制粘贴到 ChatGPT 中,并向助手阐述了我所知道的关于网络的有限信息。ChatGPT 推测了输入的组织方式,以及输出可能是标准化后的框,用于指出图像中潜在缺陷的部分,还有其他输出表示这些缺陷的可能性。经过几分钟的交流,我得到了一个能进行网络推断的 Python 脚本,以及将起始图像转换为适合输入的张量等必要代码。让我印象深刻的是,当 ChatGPT 观察到测试图像上的原始输出值(基本上是逻辑单元 (logits))时,它终于“理解”了网络的运作方式:一系列浮点数为识别输出的确切细节和标准化提供了上下文,比如框是否居中或指定了左上角等细节。

一次性程序

我曾经遇到过很多类似的情况,就像我之前叙述的那样。但记录这些并没有太大意义,因为这些情况重复的故事基本相同。我的问题是,我需要迅速了解一些事情,特别是在大语言模型给出的回答可能是无稽之谈时,我得能够验证这些信息的真实性。在这种情况下,我会利用大语言模型加快我的知识获取速度。

但在其他情况下,我会让大语言模型完全编写代码。举个例子,当我需要编写一个基本可以随时丢弃的程序时。比如这个:

简单语言模型示例程序

我需要可视化一个小型神经网络学习过程中的损失曲线(loss curve)。我向 GPT4 展示了 PyTorch 程序在学习过程中生成的 CSV 文件格式,然后我提出了一个需求:如果我在命令行中指定了多个 CSV 文件,我不想再看到同一实验的训练和验证损失曲线,而是想比较不同实验的验证损失曲线。上面就是 GPT4 生成的结果,总共用了三十秒。

类似地,我需要一个程序来读取 AirBnB 的 CSV 报告,并按月份和年份对我的公寓进行分组。接着,它会考虑清洁成本和每次预订的夜晚数量,统计不同月份的平均租金价格。这个程序对我非常有用,但编写它又极其无聊,因为过程中没有什么新颖之处。因此,我把 CSV 文件的一部分复制粘贴到 GPT4 上,告诉大语言模型我要解决的问题。程序第一次尝试就运行成功了,下面是完整的展示。

import pandas as pd
pd.set_option('display.max_rows', None)
df = pd.read_csv('listings.csv')
reservations = df[df['Type'] == 'Reservation']
reservations['Start Date'] = pd.to_datetime(reservations['Start Date'])
reservations['Year'] = reservations['Start Date'].dt.year
reservations['Month'] = reservations['Start Date'].dt.month
reservations['Nightly Rate'] = (reservations['Amount'] - reservations['Cleaning Fee']) / reservations['Nights']
all_listings = reservations['Listing'].unique()
all_years = reservations['Year'].unique()
all_months = range(1, 13)
index = pd.MultiIndex.from_product([all_listings, all_years, all_months], names=['Listing', 'Year', 'Month'])
all_data = pd.DataFrame(index=index).reset_index()
merged_data = pd.merge(all_data, reservations, on=['Listing', 'Year', 'Month'], how='left')
average_nightly_rates = merged_data.groupby(['Listing', 'Year', 'Month'])['Nightly Rate'].mean().fillna(0)
print(average_nightly_rates)

要准确地理解如何对这些原本分散和无序的数据进行分组,需要一些简单的逻辑推理。这不是大语言模型仅凭重复它所见过的语言模式就能简单解决的问题。虽然 GPT4 在训练中肯定观察到了类似的程序,但几乎可以肯定它没有看到一个程序需要以我所要求的方式,在具有特定格式的 CSV 文件中对数据进行分组。这证明了(至少在我看来),大语言模型能够在其训练集中不同程序所勾勒的范围内进行一定程度的创新和推理。

在我个人看来,编写这类程序可能是对时间的不明智使用。一个大语言模型能替我完成这项工作,显著提升了我编程的效率。这意味着,我能将精力集中在真正重要的事务上。

当事情不按计划进行:系统编程的挑战

在大语言模型和编程领域,我取得了一定的成功。但是,当我使用 C 语言编写程序时,我通常只把 LLM 作为一种更便捷的文档工具。作为一名系统编程专家,我发现只有在 LLM 能进行复杂的推理分析时,它们才对我有实际用处。而在这方面,LLM 经常无法满足期望。

让我们看一个实验例子:

试着概述一个高效、小型但完整的 C 语言实现的布隆过滤器(Bloom Filter)的要求,重点在于哈希(hashing)功能的关键要素。然后用优质的 C 语言编写它。

另外,考虑到你的例子应该可以存储 100,000 个元素,并且误报概率不超过 5%。添加的元素是以空字符结尾的字符串。

以下是输出结果:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define N 100000  // Number of elements
#define P 0.05    // False positive probability
#define M (int)(-(N * log(P)) / (log(2) * log(2)))  // Size of bit array
#define K (int)(M / N * log(2))  // Number of hash functions

unsigned char bitArray[M / 8 + 1];

void setBit(int n) {
    bitArray[n / 8] |= 1 << (n % 8);
}

int getBit(int n) {
    return bitArray[n / 8] & (1 << (n % 8));
}

// Basic hash functions (for demonstration purposes)
int hash1(char *str) {
    int hash = 5381;
    int c;
    while ((c = *str++))
        hash = ((hash << 5) + hash) + c;
    return abs(hash % M);
}

int hash2(char *str) {
    unsigned long hash = 0;
    int c;
    while ((c = *str++))
        hash = c + (hash << 6) + (hash << 16) - hash;
    return abs(hash % M);
}

void add(char *str) {
    setBit(hash1(str));
    setBit(hash2(str));
    // Add more hash functions if needed
}

int is_in(char *str) {
    return getBit(hash1(str)) && getBit(hash2(str));
    // Check more hash functions if used
}

int main() {
    char *testStr = "testString";
    add(testStr);

    if (is_in(testStr)) {
        printf("%s is probably in the set.\n", testStr);
    } else {
        printf("%s is definitely not in the set.\n", testStr);
    }

    return 0;
}

GPT4 所生成的布隆过滤器实现上并不算很出色。布隆过滤器应用广泛,并不是什么稀奇古怪的数据结构。然而,要编写一个合格的布隆过滤器,需要更高层次的抽象思维。比如,需要找到一种有效的方法,对同一个字符串进行多次哈希处理,同时确保这些哈希值相互之间的独立性。

但是,如果你明确指示 GPT4 改进哈希函数,使之能产生多个互不相关的输出,它提出的方案就显得更加合理:

unsigned int hash(const char* str, unsigned int hash_id) {
    unsigned int hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c + hash_id;
    }
    return hash % BF_SIZE;
}

如果 GPT4 能够自行提出这种方案,它编写的布隆过滤器将会有所不同,可能会使用单一的哈希函数一次性设置多个位。

GPT4 能够独立完成编写合适且更通用哈希函数的任务。但在整个布隆过滤器的编写过程中,它未能表现出足够的推理能力。相反,它只是提供了两种不同但相似的哈希函数,这种结果更像是从一篇关于布隆过滤器的低质量文章中看到的。

大语言模型在推理能力上的不足,加上可能缺乏高质量的相关资料,甚至还可能存在大量低质量的资料,共同造成了这种不尽人意的结果。事实上,这并非个例。在算法或系统编程领域,我尝试使用 LLM 的体验通常都很差劲,哪怕在类似 Python 编程这样的领域,LLM 表现出来的推理能力并不算高。

与此同时,GPT-4 不仅能反编译它生成的函数(在另一个独立会话中),还能精准地理解该函数的用途:

https://chat.openai.com/share/f54b64d7-d19a-4be4-8f10-08553aa812f1

因此,认为 GPT-4 在系统编程领域无用的说法显然是不恰当的。

有趣的是,这些情况下,小型与大型模型之间的差异显著。来看看两个本地模型的对比表现。

例如,Mistral 的专家模型 (MoE) Mixtral,对同样的问题给出了以下回应:

unsigned int hash(const char* str, unsigned int hash_id) {
    unsigned int hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;
    }
    return hash + hash_id;
}

显然,仅在最后加入 hash_id 是一个非常不佳的解决方案。Mixtral 对许多应用而言是一个优秀的模型,但对于大语言模型本就有限的推理能力来说,目前的规则显然是:越大越好。

这是我与本地大型语言模型 deepseek-coder 的对话记录。由于 RAM 有限,我只能将模型量化到 4 位来使用。尽管如此,作为一个拥有 340 亿参数的庞大模型,它在这个问题上的推理能力似乎相当出色。以下是我用 llama.cpp 进行的聊天记录:

我有一个哈希函数,它应该为相同的数据输出 N 个不同的哈希。但是,我发现当我改变 hash_id 的值时,生成的哈希分布非常差:

unsigned int hash(const char* str, unsigned int hash_id) {
    unsigned int hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;
    }
    return hash + hash_id;
}

可能是我使用的加盐策略不够好?我该如何改进呢?

[Deep-seek coder 的回复]

你的实现中可能存在的一个问题是,你只是简单地在哈希的末尾添加了 hash_id。这可能会导致分布不均,尤其是当 hash_id 过大或分布不均时更为明显。

考虑使用位运算来混合 hash_id。这里有一个使用异或 (XOR) 运算的例子:

unsigned int hash(const char* str, unsigned int hash_id) {
    unsigned int hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) ^ c + hash_id; // Using XOR to mix in the hash_id
     }
    return (hash ^ hash_id); // Mixing the hash_id one more time at the end
}

这样做可以比简单添加更有效地分布 hash_id,从而使哈希分布更均匀。[省略……实际回复更详尽]

在我的 MacBook M1 Max 上运行的这个模型表现得相当不错。它成功地将求和和异或操作混合使用了。在这个案例中,模型得到了我提供的关于问题的线索的帮助,但最终是模型自己识别出了问题的真正所在,并提出了一个有效的解决方案。这样的成就是任何书籍、文档或 Google 搜索都无法达到的。不管你如何看待这个模型——无论是作为一个初级的、基于插值的结果,还是以其他方式——它确实展现了某种形式的推理能力。在这个特定案例中,如果我们认为识别问题的根源和找到可能的解决方案是一种推理,那么它确实做到了这一点。但无论我们如何看待大语言模型,断言它们对程序员无用是非常草率的。

然而,根据我过去几个月的经验,对于系统编程来说,如果你已是资深程序员,大语言模型往往无法提供令人满意的解决方案。我来举一个真实世界中的例子。我的最新项目,ggufflib,涉及到开发一个读写 GGUF 格式文件的库,而这正是 llama.cpp 用来加载量化模型的格式。起初,为了弄懂量化编码的工作原理(因为速度原因,每个量化比特都以特殊方式存储),我试过使用 ChatGPT,但最后我选择了对 llama.cpp 代码进行逆向工程,这样更加迅速。一个能够有效协助系统程序员的大语言模型,在看到数据编码的结构声明和解码函数后,应该能够重建数据格式的文档。虽然 llama.cpp 的功能足够简短,可以完全放入 GPT4 的上下文中,但它的输出却毫无用处。在这些情况下,我们还是得回归传统方式:纸笔在手,细读代码,寻找解码器提取的比特在哪里注册。

为了让你更好地理解上述案例,如果你感兴趣,可以尝试一下。这里有一个来自 llama.cpp 实现的结构。

// 6-bit quantization
// weight is represented as x = a * q
// 16 blocks of 16 elements each
// Effectively 6.5625 bits per weight
typedef struct {
    uint8_t ql[QK_K/2];      // quants, lower 4 bits
    uint8_t qh[QK_K/4];      // quants, upper 2 bits
    int8_t  scales[QK_K/16]; // scales, quantized with 8 bits
    ggml_fp16_t d;           // super-block scale
} block_q6_K;

然后是用于执行去量化的函数:

void dequantize_row_q6_K(const block_q6_K * restrict x, float * restrict y, int k) {
    assert(k % QK_K == 0);
    const int nb = k / QK_K;

    for (int i = 0; i < nb; i++) {

        const float d = GGML_FP16_TO_FP32(x[i].d);

        const uint8_t * restrict ql = x[i].ql;
        const uint8_t * restrict qh = x[i].qh;
        const int8_t  * restrict sc = x[i].scales;
        for (int n = 0; n < QK_K; n += 128) {
            for (int l = 0; l < 32; ++l) {
                int is = l/16;
                const int8_t q1 = (int8_t)((ql[l +  0] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32;
                const int8_t q2 = (int8_t)((ql[l + 32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32;
                const int8_t q3 = (int8_t)((ql[l +  0]  >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32;
                const int8_t q4 = (int8_t)((ql[l + 32]  >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32;
                y[l +  0] = d * sc[is + 0] * q1;
                y[l + 32] = d * sc[is + 2] * q2;
                y[l + 64] = d * sc[is + 4] * q3;
                y[l + 96] = d * sc[is + 6] * q4;
            }
            y  += 128;
            ql += 64;
            qh += 32;
            sc += 8;
        }
    }
}

当我请求 GPT4 编写关于使用格式的概述时,它难以清晰地说明“ql”中上下四位的数据块是如何存储的,这与权重位置有关。在撰写这篇博客时,我还尝试让它编写一个简化版本的函数来展示数据的存储方式(可能它难以用文字解释,但可以通过代码来表达)。然而,它生成的函数存在诸多问题,比如索引不正确,从 6 位到 8 位的符号扩展处理错误(仅仅是将其转换为 uint8_t 类型),等等。

对了,这是我最终自己编写的代码:

} else if (tensor->type == GGUF_TYPE_Q6_K) {
    uint8_t *block = (uint8_t*)tensor->weights_data;
    uint64_t i = 0; // i-th weight to dequantize.
    while(i < tensor->num_weights) {
        float super_scale = from_half(*((uint16_t*)(block+128+64+16)));
        uint8_t *L = block;
        uint8_t *H = block+128;
        int8_t *scales = (int8_t*)block+128+64;
        for (int cluster = 0; cluster < 2; cluster++) {
            for (uint64_t j = 0; j < 128; j++) {
                f[i] = (super_scale * scales[j/16]) *
                        ((int8_t)
                        ((((L[j%64] >> (j/64*4)) & 0xF) |
                          (((H[j%32] >> (j/32*2)) & 3) << 4)))-32);
                i++;
                if (i == tensor->num_weights) return f;
            }
            L += 64;
            H += 32;
            scales += 8;
        }
        block += 128+64+16+2; // Go to the next block.
    }
}

从上述函数中,我移除了这段代码的核心贡献:即长篇注释,详细记录了 llama.cpp 中 Q6_K 编码使用的确切格式。现在,如果 GPT 能够帮我完成这一工作,那将非常有帮助。我相信这只是时间问题,因为这类任务在没有技术突破的情况下也是可行的,只需适当的扩展即可。

重新审视编程工作

不得不说,这是一个事实:现今的编程大多是在微调同样的内容,只是形式略有变化。这种工作并不需要太高的推理能力。大语言模型在这方面表现出色,尽管它们的能力仍然受限于上下文长度。这个现象应该引起程序员的深思:真的值得去编写这类程序吗?虽然可以赚到不错的收入,但如果大语言模型也能完成其中一部分工作,那么在未来五到十年,这可能并非最佳的职业发展方向。

再来看,大语言模型真的具备一定的推理能力,还是只是表面上的假象?有时候,它们似乎在进行推理,但这可能只是因为,像符号学家所说,使用的“符号”造成了一种实际上并不存在的意义错觉。足够了解大语言模型的人会明白,事实并非如此:这些模型整合既有信息的能力,远非简单的词汇重复。它们在预训练期间的训练主要是预测下一个 Token,这个过程迫使模型构建了一种抽象的模型。虽然这个模型可能脆弱、零散且不完美,但从我们观察到的现象来看,它确实存在。在数学确定性存在疑问,且领域内顶尖专家意见分歧的情况下,相信自己的直觉似乎是明智之举。

最后,今天还有什么理由不去使用大语言模型辅助编程呢?

正确地向大语言模型提问是一项关键技能。这项技能练习得越少,利用 AI 改善工作的能力就越弱。而且,无论是与大语言模型还是与人类交流,清晰描述问题同样重要。沟通不畅是一个严重的障碍,很多程序员尽管在自己的专业领域很有能力,但在沟通上却做得很糟糕。现在,连 Google 都变得不那么好用了,所以即便是将大语言模型作为一种压缩文档的方式来使用,也是个不错的主意。至于我,我将继续大量使用它们。我从来不喜欢去深究某个晦涩的通讯协议的细节,或者去理解由某些想要炫耀自己技术的人编写的复杂库方法。这些对我来说就像是"无用知识"。有了大语言模型,我就能免于这些困扰,每天都能感觉到它带来的帮助。

相关推荐

RN 鸿蒙安全三方库落地:react-native-digital-shield,密钥不出 TEE,AES-GCM/RSA-JWT 跨端身份打通

本文基于 RNOH 0.84 与 HarmonyOS API 26,自研数字盾库 react-native-digital-shield,把 ArkTS 专属的 HUKS、asset 能力经TurboModule 桥接给 JS:提供 AES-256-GCM 加解密与密钥托管、敏感资产存储、RSA-2048 签发校验 RS256 JWT,并以 JWK 公钥和 HS256 共享密钥实现跨端身份互认,密钥明文始终不出 TEE。文中还复盘 TurboModule 双侧注册、GCM 需拆分 16 字节

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 16

Flutter 修复鸿蒙 WebView Cookie 同名键丢失:离线回读与开发助手复核实录

修复了 Flutter 三方库 webview_cookie_manager 鸿蒙端因同名键误判导致的 Cookie 名称丢失问题,确保业务键如 path、domain 等在回读时正确保留。修复后,模拟器与真机测试均实现四组数据完整恢复,值中多等号也保持不变。补丁已提交至 CPF-Flutter 官方仓库,提升插件在鸿蒙平台的可靠性。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 29

Flutter 鸿蒙启动图撤销后编译失败:用 DevEco Code 复核三方库修复

本文针对 Flutter 鸿蒙启动图撤销时导致编译失败的问题,修复了 flutter_native_splash_ohos 库在移除启动图时误删仍被引用资源的缺陷。通过保留1×1透明图片并透传自定义模块路径,确保资源引用完整性,使工程可正常构建。修复已提交至官方仓库,支持幂等操作与多场景验证,提升开发体验。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 41

Flutter 接入鸿蒙 ArkWeb 预初始化:帮助页首开时机与主线程成本实测

为解决Flutter鸿蒙版webview_flutter无法在无视图状态下提前初始化引擎的问题,我在其原生实现中新增可选静态入口initializeWebEngine()。实测表明,在2秒预热窗口下,页面首屏渲染(FCP)耗时从中位数1791ms降至1019ms,显著提升首次打开体验;而在零延迟场景下,总路径耗时略有上升,需权衡预热时机与用户等待成本。该补丁已提交至CPF-Flutter官方仓库,支持社区协作优化。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 37

Flutter 优化鸿蒙 printing_ohos 多页 PDF 光栅化:原生资源释放与缓冲复用

针对鸿蒙端 printing_ohos 插件存在的页码映射错误与高内存占用问题,本文提出并实现三项优化:修正页码索引映射、引入原生资源分层释放机制、复用像素缓冲区。修复后,第三页显示正确,内存增长显著降低——在模拟器上PSS增量中位数下降65.8%,真机测试下降64.4%。完整补丁已提交至官方仓库,支持社区协作与问题反馈。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 34

React Native 鸿蒙权限审计实战:让每一项权限都有人认领

本文介绍一款纯 JS 编写的 React Native 鸿蒙权限审计工具,解决权限声明与使用对账难、最小权限集计算矛盾、审核必填字段缺失三大痛点。通过 module.json5 声明、使用台账与源码扫描三路输入对账,自动识别未使用权限、补全 reason/usedScene、生成可粘贴的最小清单,并支持 CI 卡控与风险分评估。工具内核独立,离线运行,兼顾准确性与安全性,助力应用高效通过审核。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 12

Cordova 鸿蒙 WebView 安全实战:能改的运行时拦,改不了的构建期审

本文通过 cordova-plugin-harmony-webguard 插件,解决 Cordova/HarmonyOS 应用中导航安全、引擎不可修改配置、JS 门面静默降级三大难题。基于运行时策略闸门(onPageStart + JS 预检)与构建期审计 CLI(扫描 MainPage.ets、config.xml 等),实现协议拦截、白名单校验、高危配置阻断,支持 CI 失败退出,将安全基线可视化、可测化,真正落地“能改的运行时拦,改不了的构建期审”。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 26

Flutter 鸿蒙供应链审计实战:让高危依赖卡在 CI 门口

本文介绍了一个基于纯 Dart CLI 与 Flutter Dashboard 的依赖审计工具,解决鸿蒙工程中三套锁文件(pubspec.lock、oh-package-lock.json5、package-lock.json)统一读取、离线快照与 OSV 在线情报选择、以及高危依赖通过退出码卡在 CI 的问题。工具以锁文件为事实依据,支持离线快照(4 条真实 GHSA)与在线查询,输出 SBOM(CycloneDX 1.5)、Markdown/JSON 报告,并通过退出码实现 CI 阻断,性能秒级

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 151

React Native 鸿蒙端侧 OCR 实战:TurboModule 与纯 TS 引擎的逐字契约

本文以鸿蒙真机上的 React Native 0.84 工程为例,详解三大难题:通过 TurboModule 接入 HarmonyOS Core Vision Kit 实现端侧离线 OCR;规避 Metro 预置头导致的 require 不存在问题;实现 npm 包与 HAR 双形态交付。项目采用 codegen 生成 ArkTS 接口,结合纯 TS 回退引擎,确保跨引擎逐字符一致,并通过 autolinking 支持无缝集成。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 92

Flutter 鸿蒙端侧 OCR 实战:让文字识别留在设备上

本文通过一个运行在鸿蒙真机上的 Flutter 插件工程,系统解决了三大核心问题:如何统一内存、文件、相册三类输入的识别链路;如何实现与 KMP 版本的 JSON 契约逐字节对齐;以及在系统不提供置信度时如何诚实处理。基于 HarmonyOS Core Vision Kit,插件实现端侧离线 OCR,全程无网络、无权限、无密钥,支持真实小票与截图识别,性能稳定(312~507ms),并通过 35 项单元测试与真机集成验证。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 149

跨端 RN 鸿蒙碰一碰分享库实战:基于 Share Kit 封装 react-native-ohos-knock-share,实现卡片、文件一碰传

本文介绍基于React Native的鸿蒙碰一碰分享库,解决RN开发者在鸿蒙生态中无法便捷使用Share Kit碰一碰功能的痛点。通过封装NFC/星闪触碰、3秒发送窗口、沙箱文件URI转换等核心逻辑,实现跨设备快速传递链接、文件、图片等内容。支持手机、平板、PC多端适配,提供回环演示与真机实测验证,显著降低接入门槛,提升跨端协作效率。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 188

React Native 鸿蒙实战:给应用接入小艺语音指令,跑通 Skill 声明到操作闭环

本文介绍如何在 React Native 鸿蒙(RNOH)工程中实现小艺技能闭环,通过开源库 react-native-ohos-ai-agent 封装技能声明、语音指令接收、端侧执行与结果回传全流程。针对三方应用权限限制,精准定位可操作边界,实现“被唤起→执行→反馈”链路贯通。结合五张真机截图验证:技能注册、指令匹配、意图上报、智能体调起及对话层弹出等关键节点均成功,完整打通语音交互闭环,助力应用无需入口即可被用户“叫”到。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 169

Flutter 鸿蒙实现精准碰一碰分享:tap_share_ohos 三方库打通真机传文件与传卡片

本文针对Flutter在鸿蒙系统上“碰一碰”分享功能缺失的痛点,基于DevEco Studio 26.0、HarmonyOS 7.0 API 26与Flutter 3.41.9实测环境,提出并实现开源库tap_share_ohos。该库封装NFC、Share Kit、权限管理及分布式能力,通过Dart门面层统一接口,利用MethodChannel与BasicMessageChannel对接原生ArkTS逻辑,实现文件与卡片的碰一碰分享。真机测试显示,可无缝调起系统分享面板,支持华为分享接力

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 170

Flutter 鸿蒙实战:用三方库接入小艺语音指令与智能体,跑通 Skill 声明到操作闭环

本文基于 flutter_ohos_ai_agent 三方库,实现 Flutter 应用一键调起鸿蒙小艺智能体。针对跨端框架对接鸿蒙 AI 能力的碎片化、环境差异等痛点,通过插件封装系统 Kit 能力,提供统一 Dart API。实测显示,模拟器与真机均支持文本/语音指令闭环,真机可完整调起小艺面板并回传事件。核心原理为:利用 ArkUI 承载页桥接 FunctionComponent,通过 MethodChannel 与 EventChannel 实现 Flutter 与原生能力双向通信,完成从语音识别到

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 153

RNOH 原生内存泄漏定位与治理:借助 LeakyGuard 实现图片解码降采样、LRU 字节缓存、资源生命周期管控,长列表图片页面峰值内存优化

本文面向 RNOH 0.84 长列表图片场景,剖析滑动数分钟后进程被系统杀掉的根因:原图解码尺寸失控、缓存按条数限流、PixelMap未显式 release。基于自研LeakyGuar,TurboModule 库,给出 desiredSize 真降采样、字节预算 LRU 缓存(逐出必 release)、资源登记与快照 diff、卸载即释放 Hook 的闭环方案,实测单图内存降 98.6%,24 图峰值由 1098.6MB 降至15.8MB。并总结双侧注册、ArkTS 语法差异、vp 转像

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 280

Flutter 鸿蒙空间音频实战:让音乐播放器的声音“长出方位感“

本文通过一个真实运行的 Flutter 工程,系统解析了在鸿蒙系统上实现空间音频的三大核心问题:基于双耳时间差(ITD)、声级差(ILD)和频谱整形的方位感算法;耳机切换与音频焦点管理;以及播放器与音效的集成。项目使用自研插件 spatial_audio,结合原生 HarmonyOS 接口,实现了跨设备的空间音频体验,支持普通耳机,无需硬件依赖,并提供实时参数反馈与系统状态监听。附带完整代码仓库与实测效果展示,为 Flutter on HarmonyOS 的音频开发提供了可复用的技术方案。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 289

Flutter 鸿蒙 HiTrace 请求归因:图片压缩等待时,主线程在做什么

为定位鸿蒙平台图片压缩性能瓶颈,我在 flutter_native_image 插件中新增可选的 HiTrace 阶段追踪功能,支持按请求开启解码、缩放、编码与资源释放阶段的耗时分析。通过对比模拟器与真机(华为畅享 100 Pro Max)数据,精准识别出耗时主要集中在解码与编码阶段,显著提升诊断效率。相关代码已提交至官方仓库,助力开发者快速定位性能问题。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 适配鸿蒙折叠屏铰链角度按需订阅:支架调节页用完即停与重入实测

本文介绍对 Flutter 三方库 dual_screen 的优化:针对支架调节页等短时观察场景,保留原有缓存入口的同时,新增按需订阅机制。通过按需启停监听,实现页面退出后停止原生回调,避免无效数据更新。实测显示,无消费者时原生回调从3条降至0条,重进页面则等待新事件,更符合临时工具页的使用逻辑。完整代码已开源,支持鸿蒙折叠屏角度动态感知,适用于需精准控制生命周期的折叠屏应用开发。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 适配鸿蒙手表屏幕形状:让计时卡片按圆屏和方屏布局

本文基于 wear_plus 1.2.4 版本,为鸿蒙系统补全屏幕形状查询功能,使 WatchShape 能正确返回 round 等形状信息。通过在原 Dart 接口不变的前提下,新增鸿蒙平台处理器,实现与 Android 一致的异步查询逻辑。对照实验验证:适配后页面可从“查询未接入”变为“圆屏已确认”,日志成功接收系统回执,布局行为保持一致。完整代码开源至 oh-flutter/wear_plus,支持 Flutter 鸿蒙穿戴设备开发。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 优化鸿蒙剪贴板大图复制卡顿:从主线程同步阻塞到异步文件描述符直解

针对鸿蒙系统下 flutter_image_clipboard 插件复制高清图片时阻塞主线程的问题,本文提出三项优化:异步文件描述符直解、异步剪贴板写入及原生资源逆序释放。实测显示,模拟器事件循环最大间隔从300.32ms降至54.36ms(降幅81.9%),真机上更从133.20ms降至11.20ms(降幅91.59%),显著提升页面响应性。完整代码已开源,支持直接使用与社区交流。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 三方库 petalmaps_ohos 的 OpenHarmony 适配实战(0 → 1 全记录)

本文详细记录了从零构建 Flutter 三方库 petalmaps_ohos 并适配 OpenHarmony/HarmonyOS 平台的全过程,涵盖项目初始化、原生实现(基于 ArkTS 和 MethodCallHandler)、插件注册、功能验证及升级至 HarmonyOS SDK 26 的关键步骤。重点解析了为何需实现 AbilityAware 获取 UIAbilityContext 以确保地图调起成功,并通过分层架构、枚举封装与兼容性设计,实现功能完整、易用且低维护成本的“调起系”地图插件,

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 59

Flutter 三方库 holding 的 OpenHarmony 适配实战:从 0 到 1 开发一个鸿蒙感知插件

本文详述了从零实现开源 Flutter 插件 holding 并适配 OpenHarmony/HarmonyOS 的全过程。基于鸿蒙专属 MultimodalAwarenessKit,实现握持手与操作手状态感知,覆盖事件流订阅、状态查询及 API 26 新增接口,确保功能一致、性能高效、接口稳定。通过 Dart 层封装、ArkTS 原生桥接、真机验证与单元测试,完成插件工程化落地,并发布至 pub.dev,为同类系统能力插件的鸿蒙化提供可复用的技术路径。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 28

Flutter 三方库 gesturesshare 从 0 到 1:鸿蒙隔空传送分享插件的开发、升级与发布实战

本文详述了 Flutter for OpenHarmony 三方库 gesturesshare 从零开发到发布全过程:基于 HarmonyOS 隔空传送能力,实现跨设备图片与 App Linking 一键分享。涵盖双端代码设计、API 26 升级、真机验证及 Pub 发布流程,提供 Dart/ArkTS 对照与踩坑复盘。项目填补 Flutter 生态空白,推动鸿蒙多设备交互能力普及,助力开发者快速接入系统级功能。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 28

Qt 鸿蒙沉浸光感实战:让玻璃的每个像素自己发光

本文通过一个 QML 模块与 Qt for OpenHarmony 构建实践,完整解答了三个核心问题:如何用 81 行 GLSL 实现无图片依赖的动态玻璃材质(含光核漂移、视差、SDF 圆角与颗粒);如何在 Qt Quick 中通过 C++ 控制器实现弹层锚定与翻转;以及如何将 Qt 应用交叉编译并以 HAR 形式部署至鸿蒙系统。项目基于 Qt 5.12.12,利用 ShaderEffect 实现纯渲染级视觉效果,兼顾性能与跨平台兼容性,提供可复用的开源方案。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 23

开源鸿蒙平台 KMP_CMP 三方库「端侧语音转文字」适配全流程

本项目实现KMP/CMP在OpenHarmony鸿蒙平台的语音转文字(TTS)完整链路适配,通过Kotlin/Native共享模型、C ABI/N-API桥接与ArkTS交互,解决权限、音频格式、生命周期及跨平台契约等难题。支持中文、英文、日文多语言识别,提供统一结果模型与JSON序列化,实测于华为Mate 80 Pro Max模拟器,验证了JVM测试、Native动态库、HAP签名部署及真机识别全流程。项目结构清晰,分六阶段推进,确保代码复用与平台无关性,为KMP在鸿蒙生态落地提供可复用的技术范式。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 30

开源鸿蒙平台 KMP_CMP 三方库「端侧OCR」新特性适配

本项目实现Kotlin Multiplatform(KMP)在OpenHarmony平台的端侧OCR适配,基于HarmonyOS 7.0 SDK与Compose Multiplatform 1.9.2,成功打通JVM、Kotlin/Native与ArkTS间的数据交互。通过C ABI/N-API桥接层,实现KMP模型与Core Vision Kit识别结果的跨语言共享,支持中、英、日、韩四语识别。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 13

开源鸿蒙平台 KMP/CMP 三方库「智感握姿」新特性

本项目实现鸿蒙KMP/CMP平台下智感握姿能力的跨端适配,基于Kotlin Multiplatform构建共享状态模型,通过C ABI与ArkTS桥接,支持左手、右手、双手等五种握持状态的稳定传递。项目打通ohosArm64目标、Native动态库构建与DevEco Studio集成链路,提供统一API与真机验证示例,确保状态模型、事件序列与生命周期在多端一致。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 19

Electron 鸿蒙沉浸光感实战:把玻璃交给操作系统去发光

本文通过一个 Electron 库与 HarmonyOS 三方库,系统解决三大核心问题:跨平台材质路径对齐(macOS vibrancy、Windows acrylic、鸿蒙 backgroundBlurStyle)、菜单子窗口精准定位(坐标转换与工作区适配)、以及无脚本菜单文档的安全事件回传(自定义 URL scheme 与焦点管理)。基于 CPF-Electron 鸿蒙化分支,实现桌面级玻璃效果——材质由操作系统合成器采样,网页仅渲染薄色层与光斑,确保视觉一致性与性能优化。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 21

Flutter 修复 flutter_secure_storage 鸿蒙入口路由与前缀隔离:让多账号登录凭证在手机上独立读写与清理

本文针对 flutter_secure_storage 在鸿蒙平台的适配问题,修复了主包缺失 TargetPlatform.ohos 路由导致的 unsupported_platform 错误,并统一鸿蒙子包多账号前缀的读写与清理范围,解决键值覆盖、前缀错用及批量操作匹配过宽等问题。修复后,业务层可继续使用统一接口实现跨账号安全存储,保障数据隔离性。完整代码已开源至 abaoge/flutter_secure_storage。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Cordova 鸿蒙沉浸光感实战:让弹层玻璃“喝到“背景的光

本文通过一个 HarmonyOS 模拟器上的 Cordova 项目,详解了在 WebView 中实现“玻璃拟态”效果的三大核心问题:利用 backdrop-filter 实现背景实时模糊与提亮,构建四层结构(采样、渐变底、光场、噪点)模拟真实玻璃质感;通过锚点定位与动态布局实现弹层贴边生长;并集成焦点管理、系统返回键及 inert 属性,确保交互礼仪完整。插件为纯 JS 无原生依赖,基于 ArkWeb 内核,兼顾性能与真实背景采样,适用于菜单、浮层等场景。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 28
上一篇: 2023 AI 的一年
下一篇: 大型语言模型的检索增强生成:调查
AtomGit 代码君
AtomGit 代码君 企业官方账号 企业官方账号
博客等级 码龄6年 2万+粉丝 856原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值