当前位置: 首页 > news >正文

大模型复杂任务

概述

由于大模型编程本身存在一定的不稳定性,因此这里列举的两个需求示例在某些情况下也是能够被编程成功的,但相比于其他简单需求,大模型对于复杂需求的编程准确性明显大幅下降。

经过上述验证不难发现,尽管当前的大模型编程流程已经添加了非常多的提高稳定性的措施(诸如多段提示、提示流程审查和代码审查等),但对于复杂的编程任务,大模型编程的准确率仍然很低。面对这一问题,有什么解决方案呢?需要注意的是,对于大模型工程师来说,如何“用好”大模型始终是岗位核心竞争力,而自然语言编程又是大模型非常重要的应用领域,因此,更进一步的思考和探讨如何进一步提高大模型对复杂问题的编程能力,是非常有必要的。

编程性能瓶颈根源与解决方案

编程性能瓶颈根源

其实大模型对复杂问题的编程能力不足,和大模型对于创作长文本能力不足的原因是一样的——都是因为模型本身并不具备“长期记忆”,而这会导致大模型无法一直围绕某个恒定的目标进行长段的文本生成,需要注意的是,对于大模型来说,代码编写本质上就是按照某种特定格式进行文本创作,本质上并没有区别,而面对复杂需求,往往意味着为了实现该需求需要编写长段的代码,因此受限于大模型编写长段代码能力不足,对于复杂需求的代码编写准确性也随之降低。

提升长段代码编程稳定性策略

  • 如何解决这个问题呢?
    首先需要明确的是,此时大模型所表现出来的问题,并不是需求理解不足,而是在充分理解需求的情况下,也无法编写长段代码来满足需求,也就是说大模型在此前的LtM提示流程下是能够充分理解用户需求的,也能够顺利的辨别需求背后的变量(作为函数参数),问题在于实现该需求层面代码本身编写能力不足。因此我们解决问题的重点是如何提高模型的编程能力(而不是通过提示工程来进一步提升模型理解能力。)。
  • 如何提升大模型的编写长段代码的稳定性呢?
    和编写长文本类似,最简单有效的方法就是先列提纲,然后分段编写,最后进行合并,即通过分段完成的方式,来提高模型长文本(长段代码)编写的表现。当然,编程任务还有其特殊之处,例如分段编写的代码需要变量一致,并且前后代码功能需要很好的衔接,此外我们已经编写了非常多辅助代码编写的函数,还需要考虑尽可能借助这些辅助函数来提高开发效率等。

第一阶段:复杂需求拆解

既然是要分段编写代码再进行合并,首先就需要先对原始的复杂需求进行拆解。
这个拆解的过程就非常类似于CoT思考链——一步步思考并解决问题。
例如对于需求:请将我邮箱里面未读邮件中的有关开会邀请的邮件添加上会议预定的标签。就可以拆分为步,第一步是找到所有未读邮件,第二步是在未读邮件中找到有关会议邀请的邮件,并将其添加会议预定的标签。不难看出,这一拆分过程本身并不复杂,甚至相比CoT,围绕当前邮件项目的复杂任务拆分流程会更加具备确定性,因此也会更加简单。

这里所谓的拆分任务的确定性,主要是因为拆分得到的子任务往往是一些最小功能单位,比如查找邮件、给邮件打标签等。而一旦拆分的子任务属于较为确定性的一类任务,那么这个拆分任务这一过程本身,其实就是一类特殊且简单的拆分任务。而要引导大语言模型完成特殊且较为简单的任务,最好的方式就是输入一些项目背景信息(挂载外部文档)并且提供一些拆解的示例作为Few-shot。

这里需要注意的是,在进行子任务时,必须强调子任务需要是能够简单实现的子任务,否则模型在进行子任务拆分时甚至会认为第一步是打开邮箱,而另一方面,只有拆分的子任务是能够简单快速实现的任务,才能尽可能确保子任务对应的代码编写的准确性。而根据我们对大模型性能的了解,这方面信息的顺利传递,不能只通过Few-shot,还必须依靠编写一个外部文档进行项目说明。此外需要注意的是,这里如果找不到合适的任务拆分示例作为Few-shot,也可以在充分描述背景之后让大模型帮我们进行编写。

而当我们已经完成任务拆分之后,接下来就是考虑如何围绕子任务进行代码编写,以及各部分代码如何拼接等问题了。这些工作我们统一称为复杂任务编程的第二阶段,第二个阶段的实现方法可以有很多种,我们这里依次对其进行介绍:

第二阶段方案一:单独创建复杂需求函数

首先最容易想到的方法,就是在基础已有的基础功能函数基础之上再架设一层高层函数,用于组合基础函数功能,来实现更加复杂的需求。这里所谓的基础功能函数,指的是可以借助code_generate函数进行准确高效编写的基础功能函数,例如此前我们创建的查看未读邮件、发送邮件、邮件汇总等功能函数,而从任务拆分角度来看,这些基础函数就是专门用于去实现子任务的相关函数。而在面对复杂需求时,我们可以围绕拆分出来的子任务先分别编写对应的功能实现函数,然后再编写一个可以调用基础函数的高层函数,用于将基础函数的输出结果进行整合,并最终完成原始复杂任务的处理。

例如对于需求:请将我邮箱里面未读邮件中的有关会议邀请的邮件添加上会议预定的标签。就可以拆分为步,第一步是找到所有未读邮件,第二步是在未读邮件中找到有关会议邀请的邮件,并将其添加会议预定的标签。

例如,对于当前需求“请将我邮箱里面未读邮件中的有关会议邀请的邮件添加上会议预定的标签”(以下简称为复杂需求或者当前需求)来说,我们已经将其拆分为了两个子任务,分别是获取未读邮件列表(以下简称为子任务1),以及从未读邮件中挑选会议相关邮件,并为其添加会议预定标签(以下简称为子任务2)。这两个子任务可以分别借助此前定义好的code_generate函数+function_test函数进行创建,同时需要注意在创建完成子任务2的函数(以下简称为函数2)时,需要确保其输入的参数格式和完成子任务1的函数(以下简称为函数1)的输出结果一致。而当我们创建完函数1和函数2之后,接下来即可定义一个外部函数,用于串联函数1和函数2来进行运行,即可获得最终结果。

不难发现,方案一的这种分层开发的思路其实非常像传统技术团队的研发策略,这种策略便于分工、易于管理,并且有非常明显的阶段性产出,同时如果是人工进行代码编写,也能非常便捷的维持函数1的输出结果和函数2的参数一致。并且,这种开发方案的优势在于代码利用率很高,试想一下,伴随着基础函数库的函数功能不断丰富,会有越来越多的复杂需求或许并不需要单独编写子任务函数,而可以直接从已有的函数库中挑选函数来组合运行。例如,如果现在的需求是在未读邮件中寻找会议相关邮件,并将邮件内容编写为附件本地保存,则在分解任务时,分解得到的第一个子任务——列举未读邮件列表,就可以直接从利用函数1来完成。总的来说,方案一是一种易于管理且代码利用率极高的一个方案。

但是,该方案也存在一些问题,其中最核心的问题就在于现在是引导大模型进行代码开发,而不是引导人进行代码开发。这其中会存在较为本质的区别,对于程序员来说,能够非常简单的的统一一系列函数的输出和输入格式,但如果是自然语言编程,这一点就很难做到,我们很难向大模型传递“函数1的输出结果要用于函数2,因此函数1需要输出每一封未读邮件的邮件ID,否则函数2就无法定位具体的邮件”。因此,在实际执行方案一时,哪怕函数1和函数2都能顺利编写代码,但二者也很难顺利衔接到一起来进行运行。

其实对于大多数初级大模型工程师来说,都习惯以人的思维思考大模型的运行状态,而实际上,要设计一套引导大模型来执行任务的流程,就必须优先考虑到大模型和人的区别,很多看起来对人类程序员非常友好的流程,却不一定适用于引导大模型来完成。一个为大模型设计的任务流程需要充分发挥大模型的优势,而不能先入为主的优先从人的角度来考虑问题。

尽管大模型在函数功能的语义理解层面不如人类程序员,但大模型却有自己的得天独厚的优势——能够不知疲倦的反复高效的创建代码,并且能够对自己编写的代码进行自动审查。因此我们完全可以利用这点,来创建一个更适合大模型来执行的复杂需求函数编写流程。因此,接下来的两个方案不再考虑拆分创建个函数再组合运行的思路,而是考虑采用一个函数来满足复杂需求,并且充分借助大模型能够进行自我审查的特性,来提高这一个函数对需求满足的准确性。

  • 第二阶段方案二:创建嵌套函数

  而如何在一个函数内完成复杂需求功能设计,首先能想到的就是创建嵌套函数,即将方案一中的函数1作为内部函数,函数2作为外部函数来创建一个嵌套函数,并由此来满足原始需求。具体执行流程如下:首先我们先单独引导大模型编写函数1和函数2的需求,并且在提示中规定函数1的输出将作为函数2的输入,然后先创建函数2,在创建完函数2的代码之后,再将函数2的参数中原本用于表示函数1输出的参数,改为函数1的参数,然后再将函数2的代码作为提示,并且在提示中说明函数1和2的功能,以及函数1是函数2的内部函数等信息,以此引导大模型创建函数1。此时由于模型是在知道函数1和2之间的关系、以及函数2的代码基础之上创建的函数1,便能够自动判断需要将未读邮件的邮件ID作为函数1的输出,才能让函数2顺利的为某些函数打上标签。在创建完函数1和函数2之后,再将其进行合并(函数1作为函数2的内部函数),并输出最终结果。

  很明显,该流程能够非常好的借助大模型本身的能力,解决此前所面临的函数1和2无法很好的协调输入和输入的问题,同时由于一个复杂需求一个函数,因此也不存在方案一中由于大模型语义理解不稳定性造成潜在风险。总的来说,相比方案一,方案二可以说是能够更好的发挥大模型本身的能力。但是,通过仔细观察上述方案流程不难发现,方案二最核心的问题就在于需要反复给与大模型非常多段的提示(例如函数2的参数修改、函数1和2的关系等),才能很好的引导大模型完成相关工作。尽管我们也可以通过创建某个函数来流程化的批量执行这些提示,但这个流程和此前我们创建的用于编写基础函数的流程差距太大,因此并不能最大程度借鉴此前流程,从而导致需要耗费大量的人工成本、且整体效率较低。

  介于此,我们需要在方案二的基础之上,进一步优化整个流程,即在这个复杂函数自动编写流程中,不仅需要扬长避短发挥大模型特性,同时也需要尽可能做到简洁优雅,需要尽可能借用此前基础函数的开发流程,并且保证一个较高的准确性。

  • 第二阶段方案三:分别创建函数再统一代码

而最后一个方案则是在前两个方案基础之上进行优化和迭代的最终版方案,该方案非常不符合人类程序员的一般开发思路,但却是非常适合大语言模型来执行的开发流程,并且能够非常好的复用此前的基础函数的提示和审查流程,从而极大程度提升开发效率。

方案三执行流程如下,首先和方案一一样,都是拆分两个子任务,并且分别编写两个函数以满足这两个子任务需求。并且在创建函数1和2时也是采用code_generate+function_test来进行代码编写和审查,但这里需要注意,我们在创建这两个函数时,会先创建函数1,然后将函数的输出结果作为函数2创建时的需求的一部分(注意,这里只涉及需求的拼接,不涉及提示流程的修改)。并且,我们在创建函数1和函数2时,只要求代码通顺即可,并不要求两个函数能够拼凑成一个完整的链路,即不要求函数1的输出结果是函数2的输入。此时根据我们对大模型编程能力的判断,大概率函数1和2能够分别顺利运行,但无法组成一个完整的链路。但没有关系,接下来我们再次调用code_generate+function_test流程来围绕原始需求进行函数编写(以下将该函数称为原始需求函数),并且在这个过程中同时将函数1和2的源码作为system_message带入这个编写过程,此时,大模型有函数1和2的代码作为参考,即可非常顺利的完成原始需求函数编写。需要注意的是,原始需求函数并不是函数1和函数2的拼接,而是在参考了函数1和2的代码基础之上,重新编写的一个独立且完整的函数,因此哪怕函数1和2本身并不能很好的进行衔接,也不会影响原始需求函数的代码正确性。并且,此时有了函数1和2的代码作为参考,大模型围绕复杂需求进行编程的准确性也将大幅提升。更重要的是,这个过程基本是完全复用code_generate+function_test流程,无需进行更复杂大模型提示即可完成,开发效率很高。

复杂任务的编程落地

Stage 1.复杂需求拆解

首先,我们需要创建一个专门用于复杂问题拆解的模型。根据此前的分析,该模型同时需要外部挂载文档以说明项目背景和复杂任务拆解的目的,同时还需要一些复杂任务拆解的示例作为Few-shot。

1. 什么是前端项目?前端项目本身由一系列的任务组成。核心功能是根据用户自然语言描述进行相应的邮箱操作,如查询邮件、收发邮件等。该场景是我们自己构建的一个场景,主要是用来对我们 Mymail 项目中任务的拆解进行编程实现。
2. 什么是任务拆解?在前端项目中,任何复杂的操作都是将用户需求拆解为若干个最小子任务的过程。任务拆解的目标是为了能够完成复杂任务的需求。例如以下就是一个复杂任务拆解过程:原始用户需求为“a,请帮我查下西门吹雪给我发了哪些邮件,并对这些邮件内容进行总结”。该需求可以拆分为两个最小子任务,分别是:“a, 直接找我和西门吹雪之间的邮件列表”、“a, 根据某邮件列表,对这些邮件的主要内容进行总结”。
3. 什么是最小子任务?在运行复杂任务拆解时拆解得到的最小子任务单元。例如获取我和西门吹雪之间的通信列表、围绕某些邮件进行内容总结,就属于最小子任务。最小子任务不可再进行任务拆解,并且最小子任务往往都是较为简单的任务,是一些可以快速实现的功能。
4. 若一个任务拆解成了多个最小子任务,那么请注意这个最小子任务的任务顺序。例如此前的例子中,查取邮件列表就应该发生在对邮件内容进行总结之前。
5. 你是一名任务拆解助手,负责邮件项目中用户需求拆解。当一个用户需求可以拆解为多个最小子任务时,请对其进行最小子任务的拆解。而当一个用户需求本身就是一个最小子任务时,则无需对其进行拆解。

接下来继续创建Few-shot的提示示例。这里总共编写了四组难度和复杂度各不相同的四个需求进行拆解,具体提示示例如下:

example1_user = 'Q:请帮我查下西门吹雪给我发送了哪些邮件,并对这些邮件内容进行总结。'
example1_assistent = 'A:1.请帮我整理西门吹雪发给我的邮件列表。2.对某些给定的邮件进行邮件内容总结。'example2_user = 'Q:请帮我查下最近一封未读邮件的发件人。'
example2_assistent = 'A:1.请帮我查下最近一封未读邮件的发件人。'example3_user = 'Q:请分析我邮箱里全部已收到的邮件,告诉我最常联系的人。'
example3_assistent = 'A:1.请帮我整理邮箱里全部已收到邮件的邮件列表。2.根据某份邮件列表,分析并找出最常联系的人。'example4_user = 'Q:请在我邮箱里查找6月5号到6号的未读邮件,并找出工作相关的未读邮件,请依次给这些未读邮件回复说我正在休假,同时请将这些邮件转发给我的助理西门吹雪。'
example4_assistent = 'A:1.请查找并整理我邮箱里6月5号到6号的未读邮件列表。2.根据某份邮件列表,查找并汇总其中和工作有关的列表。3.根据某份邮件列表,对其逐一进行回复,告诉他们我正在休假。4.根据某份邮件列表,将其逐一转发给我的助理西门吹雪。'

需要注意的是,这里我们统一通过Q&A的方式来提示模型不同内容的类型,同时拆解之后的每段文字也都以句号结尾,方便后续进行结构化字符串提取。在创建的四个提示实力中,1、3示例属于一般示例,对于大多数复杂需求来说,拆解为两个子任务基本就基本能够构成较好的自然语言编程引导,而第二个提示示例则是提醒模型,当需求较为简单时,不用进行需求拆解;而最后一个提示的示例,则是一个相对来说非常复杂的需求,总共拆解得到了4个子任务,是为了提醒模型在必要时,可以多拆分一些子任务。

这里模型将原始需求拆分为了三个子任务。当然对于我们即将要执行的方案三来说,无论拆分多少个子任务,都是可以依次创建对应部分代码并进行合并的。



喜欢的朋友记得点赞、收藏、关注哦!!!

http://www.dtcms.com/a/311253.html

相关文章:

  • 【MySQL】增删改查操作 —— CRUD
  • 楼市低迷能否通过股市提振
  • ADB 底层原理
  • Android 15 中禁用/启用应用的系统级方法
  • 二叉搜索树(C++实现)
  • LeetCode 刷题【26. 删除有序数组中的重复项、27. 移除元素、28. 找出字符串中第一个匹配项的下标】
  • 10.1通用数据类型
  • 查找文献
  • 类似 Pixso 但更侧重「网页 / 软件界面设计」「前后端可视化开发」的工具
  • 【智能体cooragent】_process_workflow 结构拆解分析
  • 一维dp-序列类型-最长有效括号
  • XGBoost三部曲:XGBoost参数详解
  • 机械臂的轨迹生成的多种方案
  • 信号完整性、电源完整性与电磁兼容的含义
  • Removing Digits(Dynamic Programming)
  • SEA-RAFT:更简单、更高效、更准确的RAFT架构
  • 人工智能与交通:智能出行的变革与未来
  • OneCode 3.0表达式从语法到执行的全链路设计
  • 解锁智能油脂润滑系统:加速度与温振传感器选型协同攻略
  • 【隧道篇 / IPsec】(7.6) ❀ 02. 如何删除向导创建的IPsec安全隧道 (点对点) ❀ FortiGate 防火墙
  • 阿里云:Ubuntu系统部署宝塔
  • 【Go语言-Day 29】从time.Now()到Ticker:Go语言time包实战指南
  • eSIM技术深度解析:从物理芯片到数字革命
  • SAP 标准代码测试OO ALV案例分享
  • ubuntu22.04离线一键安装gpu版docker
  • Unity —— Android 应用构建与发布​
  • 社群团购市场选择与开源技术赋能下的下沉市场开拓策略研究——以开源AI智能名片、链动2+1模式与S2B2C商城小程序为例
  • 苹果MAC 安卓模拟器
  • 2561. 重排水果
  • 48Days-Day12 | 添加字符,数组变换,装箱问题