验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

与ChatGPT结对编程怎么实现

阅读:486 来源:乙速云 作者:代码code

与ChatGPT结对编程怎么实现

      如何编写一个节点

      编写主干逻辑

      我们以dsl_translator这个节点为例,一开始,我只是这么提问(并没有,我只是演示一个错误示范):

      我想用nodeJS用下面的yaml描绘的数据结构得到一个新的数组:

      base:
        steps: 10
        batch_size: 1
      poly:
        - template_prompt:
            template: >
                a cat,
                ${ chara }
                ${ facial_expressions }
            meta:
              view_angle: 
                - side_view,
                - front_view,
              faal_expressions:
                - (smile:1.5),  
                - (smile:1.2),  
                - smile,

      果然,ChatGPT也不惯着我,给出了我下面的代码:

      //......
      const newArray = [];
      data.poly.forEach(item => {
      const obj = {
          template: item.template_prompt.template,
          meta: item.template_prompt.meta
      };
      newArray.push(obj);
      });
      console.log(newArray);

      我仿佛听到AI在说:“你就说是不是数组吧,你要数组,我给你数组,没毛病~”

      遇到这种问题,只能反思是自己没说清楚,那么该怎么说清楚呢?

      我们这个程序是服务于AI画图的,其实我们也可以从AI画图中借鉴不少经验,比如说在AI画图中,有一个control net里的open pose技术,我可以画个骨架,就像下面这样:

      与ChatGPT结对编程怎么实现

      我就可以用这个骨架图去画个穿靴子的猫,拿着个刺剑什么的。这个技术告诉我们什么呢?我们可以通过描述一个骨架来控制AI生成的内容。那么图片可以,编程是不是也可以?

      于是经过一段时间的摸索,最后我写出了下面的描述:

      我想用nodeJS用下面的yaml描绘的数据结构得到一个新的数组:

      base:
        steps: 10
        batch_size: 1
      poly:
        - template_prompt:
            template: >
                a cat,
                ${ chara }
                ${ facial_expressions }
            meta:
              chara: 
                - Abyssinian,
                - cat_in_boots,
              facial_expressions:
                - (smile:1.5),  
                - (smile:1.2),  
                - smile,

      要求:

      假设上面的yaml转成json的转换代码我已经写完了

      我需要遍历poly下的所有的顶层元素

      遍历过程中,要处理template_prompt元素的子元素:

      从template中读取作为模版。

      读取meta中的属性,因为属性可能每次都不一样,是不确定的,所以不能硬编码。

      然后基于meta中的属性,把template作为 string literal 解析,这个解析代码我已经有了,假设名为render_string_template,可以不实现,留一个函数接口即可。

      要遍历组合meta中的属性形成一个数组,比如chara 有两个值,facial_expressions 有三个值,那么应该生成2*3也就是六组属性放入这个数组中,这个数组和template会被传入render_string_template函数,最后会获得两个prompt字符串

      将生成的prompt字符串数组和template_prompt元素之外的其他元素合并成一个对象,要求在同一级别。prompt字符串数组有几个元素,就会合并成几个对象,并放入一个新数组中,我们称之为ploys。

      继续遍历,直到遍历完所有顶层元素,所有元素都放入了polys中。polys是一个一维数组。

      将ploys中的每一个元素与base中的属性合成一个新的对象,base的属性展开与prompt属性同级,当ploys中的每一个元素的属性名与base中的属性名相同时,覆盖base中的属性。这些新对象组合出的数组就是我要的数组

      可以看到,我在之前的prompt的后面加了很多的描述,其中的2-6就是用自然语言讲了我期望这个函数实现整个过程的大概逻辑。这个手法就很像Open Pose。通过这个操作,我得到了我想要的代码,毕竟这个逻辑不复杂。

      边界划分

      上面prompt的要求里除了表达了主干逻辑,其实也用了一些其他的技巧。比如“1. 假设上面的yaml转成json的转换代码我已经写完了”,“把template作为 string literal 解析,这个解析代码我已经有了,假设名为render_string_template,可以不实现,留一个函数接口即可。”

      这个呢有点像control net里的seg技术,他就是把图片上的东西都分区域,就像下面这样:

      与ChatGPT结对编程怎么实现

      然后再生成的就靠谱很多,甚至你还可以对其中特定部分进行针对性的描述。这个技术就很适合用来控制AI生成的注意力焦点。

      那么在我们这个例子,上面提到的那些描述就起到了seg的作用。他可以让我们不用在一个prompt描述里编写所有的细节,这对于复杂的逻辑很有帮助。因为即便是一个小节点,其实逻辑也可能有点复杂的,就像这里面,我们可以把meta数据与tempalte合并的功能延迟实现,只描述与他的交互,使生成的程序先把参数扔给它。有的时候,我们想要使用特定的库,也可以用这个方式,比如我在另外一个场景下是这么实现的这个效果:

      1. 读取文件的fs,要使用const fs = require('fs/promise')引入。

      2. 用js-yaml库解析yaml。

      通过这样的技巧,我们就可以把代码进行进一步的分解,让我们更容易描述清楚我们想要部分的业务逻辑,而且也可以给我们节省点算力,毕竟ChatGPT生成的时候如果太长也会中断,你让他继续的话,有时候也续不上。这个技巧就可以让他专注于你希望他专注的地方,从而提高表现力。

      功能迭代

      上面的写完之后呢,我发现一个问题,这个DSL还不是我想要的最终形态,对于同一套模板,我可能需要多种meta,因为有些属性的组合是没有意义的,我也不想浪费我宝贵的GPU。那么我们就不能太暴力的让它穷举所有的组合,我要针对同一个tempalte给他不同的属性组合,所以meta的值就必须是个列表,大概如下所示:

      base:
        steps: 10
        batch_size: 1
      poly:
        - template_prompt:
            template: >
                a cat,
                ${ chara }
                ${ facial_expressions }
            meta:
              - chara: #  这里改成了数组
                  - Abyssinian,
                  - cat_in_boots,
                facial_expressions:
                  - (smile:1.5),  
                  - (smile:1.2),  
                  - smile,

      于是现在我们就面临一个问题:改代码。而这个时候就体现出我们之前拆任务的价值了,因为各个模块都是隔离的,那就没有什么改代码,重新生成一份就好了,所以我改了改要求:

      要求:

      假设上面的yaml转成json的转换代码我已经写完了

      我需要遍历poly下的所有的顶层元素

      遍历过程中,要处理template_prompt元素的子元素:

      从template中读取作为模版。

      读取meta中的属性,因为属性可能每次都不一样,是不确定的,所以不能硬编码。

      然后基于meta中的属性,把template作为 string literal 解析,这个解析代码我已经有了,假设名为render_string_template,可以不实现,留一个函数接口即可。

      要遍历组合meta中的每一个属性组形成一个数组,

      每一个属性组可能只需要看做一个对象,当且仅当每一个属性值都为单值

      每一个属性组可能也需要展开,当且仅当任何一个属性值有多值,比如 chara 有两个值,facial_expressions 有三个值,那么应该生成2*3也就是六组属性放入这个数组中,这个数组和template会被传入render_string_template函数,最后会获得两个prompt字符串

      将生成的prompt字符串数组和template_prompt元素之外的其他元素合并成一个对象,要求在同一级别。prompt字符串数组有几个元素,就会合并成几个对象,并放入一个新数组中,我们称之为ploys。

      继续遍历,直到遍历完所有顶层元素,所有元素都放入了polys中。polys是一个一维数组。

      将ploys中的每一个元素与base中的属性合成一个新的对象,base的属性展开与prompt属性同级,当ploys中的每一个元素的属性名与base中的属性名相同时,覆盖base中的属性。这些新对象组合出的数组就是我要的数组

      改完要求,配上上面的DSL直接扔给了它,得到了我们想要的代码。

      所以在今天这个时刻,有些重构工作突然变得没有那么大价值了,因为在划定好的边界里,重写比重构更快,尽管两次输出的代码并不一样,但是他们的功能是一样的。

    分享到:
    *特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
    相关文章
    {{ v.title }}
    {{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
    你可能感兴趣
    推荐阅读 更多>