一次AI辅助编程实践和分析

这里也来蹭一个AGI的热点。

最近笔者在编写一些跟文件,网络服务相关的程序,中间涉及到一些关于数据流编程和操作的方式。出于参考、学习和调优的想法,笔者使用了AI技术来辅助完成这个需求,但实际操作下来,包括使用搜索看到好像网络上相关(靠谱)的内容都比较少,包括claude和ChatGPT提供的代码,都有一些问题,遂著文记录之,也当一个开发笔记使用吧。

笔者使用的是nodejs系统,涉及到的相关类,方法和技术限于这个技术体系。

基本需求

基本需求非常简单,就是有很多文件,需要将这些文件按照纯二进制的方式进行合并,最终输出单一的二进制文件。所以,输入就是一个文件名称列表和对应的文件,输出就是一个单一文件。文件的格式和内容不重要,在这个场景中都使用纯二进制的方式来处理。

解决方案构想

下面是一些可以很快想得到的解决方案。

shell方式

如果不考虑应用的集成,只考虑文件操作的话,最简单和直观的方式,其实就是操作系统自己提供的文件合并命令。比如cat和重定向符号,它的示例如下。

bash 复制代码
cat *.txt > combined.txt

上面的cat命令,可以遍历并合并当前文件夹中所有的.txt的内容,并将合并后的内容,输出到另一个txt文件中。当然我们一般的应用场景是在程序中执行这些操作,另外也需要考虑不同的操作系统环境的兼容性。所以这个方法不是本文讨论的主要内容。

编程

可以使用编程的方式来进行处理。不考虑具体的实现和代码,基本的操作流程就是,先按照文件列表,依次使用二进制的方式读取文件的内容,并将这些二进制数据进行合并,最终用二进制的方式,在写入到目标文件中。

基本思路非常简单和清晰,但实际在工程上的实现,却可能会遇到很多问题。比如中间的文件访问的错误处理、计算资源占用和消耗等等。不考虑其他的问题,最主要的问题,就是如果需要一次性处理的文件数量很多或者数据量比较大,很有可能超过计算机系统的容量,使程序无法正常运行,通常就会报出如OutOfMemory或者StackOverflow这种错误。

对于这类的问题,在计算机技术上有一个通用的处理思路,就是将复杂问题或者大问题拆分成小问题来处理,最后再将结果合并起来,就可以用相对比较少的计算资源,来处理更复杂庞大的问题。具体到本文的需求中,就可以考虑使用数据流的方式,将文件拆分成为小型的数据包,来进行处理。其实所有编程语言中,相关于数据流的功能和实现,遵循的都是这个基本思路。

实现

下面让我们来看看具体的实现过程。这里笔者有了一些思路,但不是很确定可行的方案和代码。就带着相关的问题,先去请教了一些AI系统。在这个过程中,也发现了一些有趣的内容,值得分享和讨论。

针对这个需求,结合笔者的基本开发环境,所使用的提示词如下:

如何使用nodejs stream 合并多个文件到一个文件

下面就是chatGPT3.5和Claude3提供的响应结果。

chatGPT的方案

chatGPT的响应大致如下:

js 复制代码
你可以使用 Node.js 中的可读流(Readable Stream)和可写流(Writable Stream)来合并多个文件到一个文件。下面是一个简单的例子:

const { Transform } = require('stream');

// 要合并的文件列表
const filesToMerge = ['file1.txt', 'file2.txt', 'file3.txt'];

// 创建可写流,指定要写入的文件路径
const outputStream = fs.createWriteStream('mergedFile.txt');

// 遍历文件列表
filesToMerge.forEach(fileName => {
  // 创建一个可读流
  const readStream = fs.createReadStream(fileName);

  // 使用管道将可读流连接到可写流中
  readStream.pipe(outputStream, { end: false });

  // 在每个文件的末尾添加换行符
  readStream.on('end', () => {
    outputStream.write('\n');
  });
});

// 在所有文件合并完毕后,结束可写流
outputStream.end();

可以看到,chatGPT确实能够正确的理解这个问题。它在解决问题方案方面的基本思路是,使用for each来遍历文件数组,然后为每个文件创建一个读取流,然后使用pipe方法,将这个流导输出到目标写入流中。

这段代码看起来不错,但实际上是不能运行的。这中间的主要问题应该是,pipe是一个同步执行机制,所以实际上这些读取流是并行运行的,显然和业务需求冲突,并且在逻辑上,也会造成混乱。

此外,有趣的是,虽然代码声明了tranform流,但实际并没有使用它,所以,chatGTP只是不知道从哪里找来了这段代码,但实际上它并不能更深刻的理解代码的执行方式,并确保能够重执行原理推导出可以真实正确运行的程序。

claude的方案

下面是claude提供的方案的核心代码:

js 复制代码
const fs = require('fs');
const path = require('path');

const outputFilePath = path.join(__dirname, 'merged.txt');
const outputFileStream = fs.createWriteStream(outputFilePath);

const filePathsToMerge = [
  path.join(__dirname, 'file1.txt'),
  path.join(__dirname, 'file2.txt'),
  path.join(__dirname, 'file3.txt')
];

const readStreams = filePathsToMerge.map(filePath => fs.createReadStream(filePath));

const { pipeline } = require('stream');

pipeline(
  ...readStreams,
  outputFileStream,
  (err) => {
    if (err) {
      console.error('Error merging files:', err);
    } else {
      console.log('Files merged successfully.');
    }
  }
);

笔者感觉,claude提供的代码和思路,看起来比chatGPT更好一点。简单而言,就是基于文件列表,创建多个相关的文件读取流,然后使用pipeline方法,将这些流连接起来。这个方案非常简洁优雅,但可惜是,它也是不能运行的,因为pipeline方法,不是这样用的。

笔者的理解,pipeline是一个流水线式的处理方式,其精髓就是,上一个步骤的输出,就是下一个步骤的输入。所以,它参数中的每个元素,作为流的类型,其实是有限制的。简单而言就是第一个流必须是一个读取流,中间的流必须是转换流(有输入和输出),最后一个流是一个写入流,来承载最后的处理结果。所以claude这样用,中间都是读取流,这个流水线,是无法工作的。

其实,虽然claude虽然提供了错误的代码和方法,但启发了笔者思考文件数组和流水线处理的方案。下面就是基于它的启发,笔者构想并且实现的,可以真正运行并且比较完美的满足业务需求的解决方案。

人工的解决方案

上面我们已经提到,claude提供的pipeline方案,理论上可以进行一些改造,将中间的读取流,全部改造成为传输流的方式,就可以正常工作了。但在实践上,笔者并没有实施这个改造pipeline的方法,因为觉得过于复杂。笔者的解决方案,借鉴了数组处理的思路,简单而言就是将读取流数组使用Promise同步并且队列化,一次只处理一个流。核心示例代码如下:

js 复制代码
// write file stream
const wstream = fs.createWriteStream(RECV.PATH + finfo.fileName);

for (const block of blocks) {
    await new Promise((rv,rj)=>{
        const rstream = fs.createReadStream(RECV.PATH + block);
        console.log("merge:", block);

        rstream
        .on("data", chunk=> wstream.write(chunk))
        .on("end", ()=>{  rv(); rstream.close(); });
    });
};
// close stream
wstream.emit("end");

这里的要点包括:

  • 使用结果文件名创建一个写入流
  • 使用for of 同步遍历文件名数组
  • 为每个文件创建对应的读取流
  • 便利时,使用promise进行同步化
  • 侦听读取流的data方法,并将数据写入写入流
  • 在end事件中返回和结束promise,进入下一循环
  • 全部文件处理完成后,关闭写入流

这个方法,可以将文件处理队列化,一次只专注于处理一个文件的读取,并且还进一步使用读取流的方式,实际处理的数据更小(数据流读取操作的范围),再加上写入流方式,使整个处理流程,在理论上,可以处理任意规模的信息,而不会造成系统资源的过分占用,而且相关的处理时间只会随信息量线性增长,不会造成恶化的情况。

小结

简单的总结一下。起码在现在这个阶段,针对于一些不是特别常规,但有代表性的,需要一些简单的创意和综合能力的编程文件,AGI系统还是无法提供一个比较理想的方案。包括对于原理的理解性、提供代码的可执行性方面,还是有一定不足的。这需要开发者在其基础上进行调整和改进,才能最终实现可执行的代码和功能。

相关推荐
子兮曰1 小时前
Node.js v26.1.0 深度解读:FFI、后量子密码与调试器的进化
前端·后端·node.js
Wy_编程2 小时前
go语言中的结构体
开发语言·后端·golang
我没胡说八道3 小时前
2026论文工具选购指南:降重、降AI率、排版一站式筛选
人工智能·经验分享·深度学习·考研·aigc·学习方法
凌波粒4 小时前
什么是 MCP(模型上下文协议)
人工智能·网络协议·aigc
独自归家的兔4 小时前
OCPP 1.6 协议详解:GetLocalListVersion 获取本地列表版本指令
java·后端·物联网·spring·ocpp1.6
小溪彼岸4 小时前
初识Qoder CLI
aigc
大家的林语冰4 小时前
Node 2026 发布,JS 三大新功能上线,最后一个奇偶版本
前端·javascript·node.js
墨风如雪5 小时前
GitHub 狂揽 10.7k Star!这款飞书神器配合 AI Agent,工作流彻底起飞了
aigc
小溪彼岸5 小时前
OpenSepc新版本新提升
aigc
无人不xiao6 小时前
springBoot 实现 接口进度条
java·spring boot·后端