谈复杂Agent策略框架的设计(2)【2023Q4】
本文共计约12400字。
0、前言
本系列的上一篇文章是 Multi Agent策略架构 基础(1)【2023Q4】 。虽然名字不同,但同属一个大主题。
上一篇的标题虽然叫做“基础”,但目前看来对很多人并不基础,建议没看过的读者先看下。(1)文主要讨论的是MultiAgent系的思路,同时少量讨论了一点XoT。本文则是同时讨论这两种思路,以及其他可能的思路。
本文主要讨论基于LLM的复杂Agent的实现,不讨论前LLM时代的内容。
0.1、术语定义
目前领域内各种叫法很多,为了消除歧义并方便读者准确理解本文意思,这里重新明确一下本文中一些术语的含义:
【复杂Agent的策略框架】
本文主要讨论策略框架,即项目的目标是让其他人更好的用其来做出一个具体功能的Agent。当然具体的Agent项目也可以直接采用类似的思路和设计来直接开发。
复杂Agent是相对于现在各种方案都要蹭一下Agent的热度来说的,这里期望实现的Agent能力要明显强于基座LLM的直接调用和各种工具的简单组合。复杂Agent的单次运行中可能涉及【至少】数十次、数百次的LLM API调用。在这个标准下,包括New Bing Chat在内的所有RAG方案都还不够“复杂”。
由于单次LLM API调用在该场景中重要性占比没有那么高,且这方面内容大家也熟悉,所以如何优化单次LLM API调用并非本文主题,本文并不会讨论,但并非说它不重要。
另外,具体的tools函数开发跟本文核心主题的耦合度较低,且大部读者知道该怎么做,所以这方面也不在本文讨论范围内。
1、导言
在(1)文写作之时,还看不到太多对于MultiAgent和其他复杂Agent框架的热潮的批评声音,我在当时说它“并非银弹”似乎还有点标新立异的感觉。但现在我已经看到了不少人独立调研后得到了类似的结论,甚至比我当时“并非银弹”的评价还要低,目前我听到的这方面声音的概括是:这些都是玩具,只能做一些很简单的应用,并不成熟。
我写作(1)文的时候,还并没有调研当时已有项目的具体能力,一方面是时间因素,另一方面是我觉得这里的很多想法很明确,自己根据自己场景的需要简单搭一个框架并不难,没必要依赖这些项目。而在本文的写作中,我为了更靠谱的喷这些项目,特地花了4天去调研了一下现在这些项目的能力。看完这些项目我也觉得:如果只是依靠这些项目,那么它们还都是玩具,没有任何一个项目是我推荐去使用的。大部分项目都是为了学术水论文目的而开源的结果,糟糕的文档和说明暂且不提,能力也很单一和不够完善。
如果是有自己构建策略框架的能力,那么做出来的东西还不至于这么鸡肋。我当时是基于说大家看了这些idea自然就能手搓一个适合自己场景的逻辑,所以才说这并非银弹,有一些用,但也有不少问题。而现在看来,似乎能手搓的人很少,这导致能发挥其价值的落地项目比想象得还要少。
我认为目前只有AutoGen可以推荐给【开发能力很弱】的人,例如会一点点代码的产品岗。只有AutoGen看起来有一定通用性、且文档和例子准备的比较好,这大概也是现在AutoGen在这方面大火的原因。但对于有一定开发能力和算法策略设计能力的人来说,我更建议自己根据自己的需求来做一个框架,可以参考它们的一些想法,但没必要拘泥。
2、论文与开源项目 考古
本节只是为了满足大家“这些论文/项目到底做了些什么”的好奇心,本文核心内容更多在后续章节,没兴趣的可以跳过本节。
2.1、CoT、AutoGPT 及 各种XoT
CoT(2022.1)很简单,且可以利用LLM的自回归特性在一次LLM调用内完成。从Self consistency with CoT(2022.3)开始,才有一些复杂Agent雏形的意思。
开启大家对复杂Agent领域关注的项目是AutoGPT(2023.4)。它的早期版本基本只有一个核心Prompt,支持tools,使用context和外部文件作为memory,麻雀虽小五脏俱全。
【BabyAGI】
BabyAGI(2023.4)几乎是开源项目中最早最接近目前复杂Agent设计思路的,它当时的策略流程图:(https://yoheinakajima.com/birth-of-babyagi/)
注意到子任务的重排序能力(prioritization)在当时就已经实现了,这个feature即使放在现在也不是那么标配。
【Tree of Thoughts】
ToT在2023.5提出,它的思路示意图:
从直观上来说,它已经接近于我们对于人思考过程的感觉,(但实际上人类的过程中细节要更加复杂的多)。ToT侧重于提出要并发探索多种可能的路径,对thought或者任务进行分解,并使用简单的BFS/DFS来探索整个树。但实际应用中需要的thought重复判定等等并未涉猎。
ToT的文章本身没有太多必要去看。
【后续的各种XoT】
ToT之后有一些继续完善的思路,例如Graph of Thoughts(GoT,2023.8)和最近的Everything of Thoughts(XoT,2023.11)。
GoT是在ToT上做了一些更符合人认知的完善,示意图:
GoT的思路很常规,但论文写作和测试的问题都很烂,这个文章并不值得去看。
XoT是进一步糅合了RL与GoT,增加了MCTS和Policy/Value-Network。考虑到在这种场景下还缺乏很鲁棒的RL AutoML方案,我觉得这在实际使用中有些过度了。不过倒是可以考虑把long-context LLM的in-context learning用法作为一个免学习的Network来用。
总的来说我认为ToT之后的文章都不值得看了,这些文章只是占坑,这些想法也并不难想,也并没有去认真解决一些困难的具体问题,对于实践没有太多参考性。
【XAgent】
近期的XAgent(2023.10)混在一众MultiAgent框架之中,容易被认为是MultiAgent框架,但它不是。
XAgent与上面的XoT类paper不同,本身还算做了一个完整的实现,是奔着解决任意问题去的。也许XAgent是BabyAGI之后第二个稍微好一点的XoT实现,但它的代码看起来太费劲了。(反而BabyAGI的代码易读性是所有项目中最好的。)
XAgent的策略流程是两层架构:上层进行整体规划和设计执行plan,根据反馈结果调整plan;下层是一个AutoGPT类似物,尝试求解子问题。但它的成功率仍然不高,且由于是强硬的两层分解,对于简单的问题容易出现过度细分任务不断循环反而最终迟迟无法输出结果的情况。
它的代码结构也并未为其他人调用而做太多考虑,没有代码复用价值,我个人也不建议花精力去看。XAgent的回放UI做的在这些项目里算好的,值得其他项目学习。
2.2、MultiAgent框架
【camel】
比较早的知名MultiAgent框架应该是camel(2023.3),这从它的主要设定也能看出来——它主要为2个Agent交互而设计,超过2个都不是它的主要目标,因此我更想把它称为“准MultiAgent框架”。
截至本文写作时,这个项目的文档几乎没有,整体上看是为了发论文而搞得项目,不建议依赖它进行开发。不过由于它比较早,它确实影响了一些后续项目。
【MetaGPT】
MetaGPT(2023.7)的着眼点在于预置一些Agent角色、tools等,而不是更复杂的上层的流程和策略框架设计。
它本身的代码接口设计灵活度也不高,基于截至本文写作时它的情况,并不建议去依赖它。但如果实在太懒想抄一些tools的实现,可以去翻翻,虽然我觉得里面的轮子也并不太多。
【AutoGen】
AutoGen其实是一个很早项目,代码库在2020.12月就有了,但之前一直都是非MultiAgent方向,看起来是NLP和AutoML的一些内容。直到2023.4月才开始开发目前的MultiAgent功能,可以说是旧项目废物利用了。项目在2023.9月的时候清掉了与MultiAgent无关的代码。这解释了为什么0.1.x有tune功能,因为这是历史遗留的能力,自然就用了。
AutoGen的主要特点:
设计还算简洁,易用性尚可,文档和example很丰富。
支持通过LLM来选择下一位发言者,降低了策略设计能力较弱的人的上手门槛,填一些Agent角色设定就可以用。也支持手工指定和由发言者的内容指定下一位发言者,适用范围还可以。
支持子Group,可以用来组装复杂的沟通架构,并能起到屏蔽子对话history的能力。但总体上感觉这条线能力还偏弱。
默认的2Agent设定中,虽然user agent承担了很多功能让人有点奇怪,但对于一些简单场景这是成本最低的方案。而且tools在user agent调用能够绕过OpenAI的function calling总是在回答开始的时候触发的限制。
AutoGen现在最火的原因就是:易用性好,构建简单应用的门槛低,且有一定的灵活度,没有太错误的设计。
不过AutoGen的RAG功能是外部贡献的,可用性还要低于AutoGen项目。还有一些TeachableAgent的小demo,但感觉还过于偏原型。
目前AutoGen还远远不是一个【好】的方案。
【ChatDev】
ChatDev(2023.9)容易被误认为是一个普通的MultiAgent框架在软件开发上的具体实现,但实际上它不是。ChatDev是基于Camel的,也就是说它内部流程都是2个Agent之间多次沟通,整体上的不同Agent角色的沟通关系和顺序都是由开发者配置死的,从这个角度上来说不太像是个全功能的MultiAgent框架的实现。
但似乎也很难说这就是使用Camel时候的削足适履,如果在多Agent的沟通路由层面没有做好的话,效果确实可能还不如这样的固定瀑布式两两沟通。ChatDev的作者也把这(每次是1-1沟通)作为一个feature来描述。
ChatDev项目本身的代码没有太多和复用性,依赖的旧版本Camel也是该抛弃的东西。这个项目本身更多是为了支撑论文的学术性原型,并不是为了让别人在上面开发而设计的。
在成功率方面,我的测试结果是很差,不值得费钱去测试。即使如此,相对于MetaGPT和XAgent的编程能力都已经算好了……
【其他MultiAgent项目亮点拾遗】
AgentVerse(2023.5)侧重于根据任务自动生成Agent角色和沟通关系图,做的也比较简单。这也是纯学术性发论文的项目,设计目标就不是给人用的。
OpenAgents(2023.10)侧重于如何使用各种公开tools。也是纯学术性发论文的项目。
aiwaves-cn/agents(2023.7)侧重于能够指定SOP抽象,但感觉其实现方式过于死板。接近于纯学术性发论文的项目。
AutoAgents(2023.8),没有特别明显独特的特性,功能完备性和文档比上面几个纯学术性项目好一点点,但也不推荐。
本节的项目知名度都已经较低了,其他还有一些项目的知名度更低就不再讨论了。
3、已有方案简评
虽然说上面这些方案没有我推荐的,但它们的实践确实带来了一些经验,在本节整合评论一下。
3.1、XoT的门槛
其实XoT类的方案是非常符合直觉的:它们是在模仿一个人认真的思考过程。但从我Q2的一些谈复杂策略的文章反馈来看,很多人并不能自然地应用这个思路。
我觉得在本文主题下,区分一个人是否有策略框架的设计能力,可以用是否觉得ToT/GoT的思路很“显然”为标准。当然,想不明白树结构上算法的人就很难想明白自己实现ToT的一些细节,想不明白图结构上简单算法的人也很难想明白GoT。如果在这方面感觉吃力,可能需要补一下数据结构和相关算法,让自己习惯在这类结构上构建算法的思路。
各种XoT的论文还经常忽略一个问题,就是如何判断两个thought是相同的。它们用的例子一般都是状态可以被简单结构化的问题,但在实际场景中一般都不是这样,这还是一个蛮麻烦的问题。
这些原因都导致虽然XoT的思路很“自然”,但很少有人给出实际可用的策略框架项目。直到XAgent才勉强给了一个例子,而XAgent在顶层其实也只相当于CoT的线性探索,还都没到ToT的复杂度。
3.2、MultiAgent的低门槛
在最简单的情况下,想要利用MultiAgent框架是比较容易的:就算想不明白自己的思考过程,总能明白自己身边的人类组织是怎么协作的,把这些角色和协作关系抄过来就可以了。
LLM的很多性质跟人的直觉思考很接近,prompt和对话历史不够小心处理的时候,也容易陷入到单视角误区。在这个角度上来说,抄人类组织的协作方式是MultiAgent方案设计的一个非常好的baseline获取方式,特别是考虑到LLM学习的技能和角色知识也是来源于人类社会,这个baseline就更加适合了。
不需要懂算法,不需要懂开发,就能够知道MultiAgent上的角色怎么设置、应该怎么让它们沟通,这明显地降低了开发门槛。而且Agent角色可以由人类自然的模拟,所以即使是非技术岗可以直接利用自己的领域知识来探索构建合适的Agent角色设置和协作流程。
但目前MultiAgent框架提供的能力也就到此为止了,毕竟它们一般连无损压缩对话历史、根据后续对话剔除前面的错误结论等等这样的秘书功能都没有。因为MultiAgent易上手而吸引来的初阶开发者用户们可没法自己解决这些问题。
想要在baseline的效果上进行提升,仍然需要跟上一节一样的策略设计能力,这些很大程度上是传统算法(前机器学习时代的算法)设计的能力。这也是现有MultiAgent框架鸡肋的地方:硬骨头还是需要用户开发者来啃,门槛还是不够低;而对于啃下这些硬骨头的开发者用户,这些框架又不够灵活。
3.3、过于依赖long context
前面提到的各种MultiAgent方案,太多在内部状态管理上都没有太多工作。有些还能把thought或者task粒度单独抽出来,而大部分项目都只是一股脑的把所有信息塞在对话历史(message history)中。
这导致现在的大部分项目都很依赖LLM的context window长度,一旦有些关键信息由于history超过LLM的context window而被截断时,整个框架的结果准确率就会在原本不高的水平上进一步快速滑坡。所以不少项目推荐使用的模型是gpt-4-32k,不光依赖gpt-4的推理能力和准确率,还依赖它的32k token的context window。
好在无论是国内还是国外,目前基座模型的long context刚有一波提升:gpt-4-turbo的128k,gpt-3.5-turbo-1106的16k,Claude 2.1的200k,国内的moonshot的128k。这些会明显地改善现有这些框架在稍微复杂一点的问题上的成功率,但也由于对于context window的暴力使用而让LLM调用费用也快速增长,实用性并没有改善太多。
虽然把内部状态管理都甩给LLM的context 来处理最简单,但这样的方式已经受限于基座LLM的能力,并导致成本上非常不经济。
3.4、没有优化LLM调用成本
前面提到的各种方案中,考虑单次运行的费用预算的项目都很少,更别提去优化LLM的调用成本了。
实际上LLM的调用有优化空间么?有,至少有以下方面可以优化:
只在需要更长的long-context能力的时候才调用更贵的long-context LLM
只在需要更高的推理、理解……能力时候才调用能力更强(也更贵)的LLM
而现在几乎所有框架都清一色地只能配置调用一个LLM服务,没有对不同类型请求配置对应的LLM的能力。
3.5、没有合适的压缩对话历史能力
仍然是针对MultiAgent框架:由于这类框架一般都只是依赖对话历史来保存中间状态,而对话历史中又经常包含一些错误的试错等“中间弯路”,这一方面需要LLM有更长的long-context能力,另一方面会增加LLM的调用成本。
虽然压缩对话历史在很多人觉得很容易,但在这个场景下要做的很好却不容易,因为:
何时触发压缩?已有的对话历史是否需要压缩,其中是否有足够的可压缩空间?
压缩过程是否能做到信息损失较小又不增加幻觉?
每一次调用LLM压缩历史在整体上的收益大于其成本?
这三个问题都颇难回答,以目前这些MultiAgent框架设计者能力,大概率一个都搞不定。
3.6、处理复杂度的能力较弱
目前我看到的所有Agent框架和具体Agent都有个问题:无法有效地处理超过一定程度的复杂度。这个复杂度可以简单理解为内部拆分出的子任务、子需求、子结论的数量这样的信息量。在这方面XoT类框架相对MultiAgent会好一些,但也没好太多。
举个例子:一个用户需求输入之后可能会拆分出一大堆子需求点和要求,这步的结果先无论是否有必要,但至少不算是太大的问题。但后续构建解决方案的时候往往就出问题了,拆分出3个要求可能能解决的问题,如果是拆分出10个要求可能就最终解决不了了。出现这种情况的时候,要么到达了step数限制吐出一个不完整的结果,要么整个过程就停不下来。这并不是说现在的Agent框架解决不了“高级的问题”,而是说一个问题如果被拆得“太零碎”,就可能组装不起来最后得出一个正确/可用的结果了,无论结果是否优秀。
我在测试XAgent的时候就遇到了这样的问题,有些问题似乎没必要拆分的太细,但就是拆分的很细,最后花费了大量的LLM调用还没有解决。更简单的AutoGPT在编码方面相对于XAgent还有30%的胜率大概就是这个问题。
如果简单的看这些问题,可能会觉得“先构建一个简单的方案,再逐步完善它”是一个更好的流程,这样至少在中间停止时能吐出一个可用的方案。但实际上确实有些因素必须要在前期考虑,否则在该需求增加时只能推翻前面的结果重新来做。一些因素需要在前期就进行涉及,一些细节复杂度可以推迟到后续步骤进行逐步添加。
但这种流程听起来就不好做,试问现在哪个项目能做出这样的能力呢?更何况区分是核心要求和非核心要求本身就需要调用LLM完成,做这件事的成本是否总能低于做它的收益呢?
复杂Agent策略框架的设计充满了两难问题。
3.7、对人类用户的意图感知不足
目前LLM应用开发者都已经知道:用户的输入经常是非常简略和信息不完备的。
响应时间较短的应用中,可以让用户根据每轮的反馈来进行干预、矫正、补充信息等等。但在复杂Agent场景下这种粗略的交互方式并不可行:如果是在用户等待很久之后再给用户一个不符合用户期望的结果:这是对Agent消耗的资源和用户时间的浪费。
如果是把所有中间过程展示给用户,期望用户进行反馈和干预:很多用户没有足够的耐心和细致程度逐个进行检查,很多系统的交互设计也没有控制展示给用户的信息量,导致用户淹没在大量日志中。从执行过程中提取进展概要是需要设计的,而且经常需要额外的LLM调用。
一些方案强制要求人类用户也在对话流程中,对于发给他的信息进行逐个确认,这种方式既不自动,用户体验也并不好。
人与人交互的时候,我们期望接受任务方在初期应该就一些细节要求进行明确,而着眼在这方面的框架很少。1是因为目前大多数LLM都没有做好主动提问,2是框架本身并不了解每个具体的问题,也很难对具体问题进行优化。
实际上“哪些细节应该向用户确认,确认到什么程度,哪些细节应该由LLM自己填默认值”是一个很难的问题,即使是人类秘书/下属,在不了解任务发出者时也很难把握尺度。
可以随着系统的执行来问用户一些问题,但这应该是涉及一些主要问题的时候,一般是涉及两种不同的执行路径时,但目前的基座LLM和Agent框架都没有很好的能力去判断一个问题是否是值得去暂停和打扰用户的问题。
我在测试的时候就遇到一种情况:我指定一个很模糊的简单编码任务“开发一个命令行版的四则运算计算器”,而框架的测试Agent却不断对于编码结果过度挑剔,远超过用户的要求,也超出了这个框架自己能处理的复杂度,导致一个简单的问题最终迟迟无法返回结果。
另外对于同样的case:其实我期待这个计算器应该支持括号,但反而所有的项目都没能发现或询问这一点。
3.A、总结
目前没法期望各种框架或者不是非常复杂的Agent能够解决什么稍微难一点的具体问题。例如小项目开发(不是toy任务),或者是解决调研解决一些问题。
最大的问题还是无人值守时的成功率过低,然后是LLM调用成本太高,再次是整个流程时间较长。
4、个人思考
4.1、LLM擅长的是经验,而不是规划
从使用感受上来说,LLM更多是记忆了一些解决方案,并能够把一些方案片段拼装在一起,并非去解决了这些问题,面对新问题的解决成功率也较差。
从原理上来说,LLM的训练目标只是记住训练集中的经验,甚至只是其中共性的部分,而非所有细节。从LLM的模型结构上来说,也没有任何更适合于规划的结构设计。
什么样的方式才算是规划呢?这里举两类例子:
最简单的规划问题抽象是应用数学中的各种规划问题(运筹问题),例如线性规划、混合整数线性规划、非线性规划。可以参考这类问题的求解算法。
实际应用场景中的规划方案可以参考我之前的文章 【2023H2】谈智能决策系统的策略如何设计(2)
简单来说,解决规划问题有几个要求:
给出的方案是可行的。对于序列规划问题来说,从起点到终点的每步都要是可行的。
将问题拆分为几个子问题时,应确保能从子问题的求解结果完整的得到原问题的解决方案。
解决方案的成本是合适的,最好能有某种最优性或近似最优性,至少也得是成本可接受得。
LLM与目前基于LLM的复杂Agent能够满足这些要求么?基本不行。
LLM在乎的是:
一定要能给出某种回答。
高概率的回答应该要符合训练数据中的分布。
而从规划的角度来说,总能给出一个靠谱或不靠谱的回答并不重要,回答是否要符合历史经验分布也不重要。重要的是要给出的回答要在该场景下“有用”,并能控制方案的总成本。规划能力的学习要么靠外部指导求解过程,要么需要类似RL的学习环境。
让LLM在虚拟环境中进行试错学习可以么?理论上是可以的,但目前RL的学习成本太高,也缺乏能让LLM基于少量样本就能够不断迭代改进的方式(RAG无法满足这个场景)。
4.2、外部规划经验的注入
主动学习规划能力目前在成本上是不可行的。那么想要提升规划能力,就需要外部注入求解过程的经验。
目前XoT类框架的流程和任务空间设计、MultiAgent的Agent设计与Agent协作流程设计都是外部注入的经验。这两者也并不冲突,可以联合使用。即使如此,现有的XoT和MultiAgent方案加起来我认为也远远不够。
一个思路是对于具体问题从外部注入该类问题的建议SOP(标准作业程序,Standard operating procedure),也就是一个完善的求解方案。但很多时候我们无法为每个问题、场景撰写SOP,这工作量相对于对于每类问题进行编码开发没有好太多,而且也有悖于有自主决策能力的Agent的设计目标。
反过来说,人就能不依赖SOP或外部经验在面对新问题时高概率解决么?其实也不行。
从外部注入某种不是很准确的求解经验似乎仍然是一个值得采用的方式,这种经验可以来自于:
从LLM自己的世界知识中提取
Agent框架本身的流程和机制设计
随任务一起输入的建议求解方式
Agent包含的历史解决方案库
4.3、半结构化内部状态
本文的半结构化是指类似Dict<Key, Text>这样的结构。其中Key类型也可以是String,但要能够被程序“理解”,即一般意味着其所有的值都被显式编码在程序中;而Text类型就是String,但可能包含任意语义内容,只能被LLM这样的语义处理工具进行处理。半结构化的类型并不仅限于字典,它可以是任意传统的数据结构并将其中的某些位置改成Text类型。
这种半结构化的方式是于MultiAgent对话直接得到的多轮message history相对的,这些对话历史相当于是Array<Text>类型,非LLM的上层逻辑很难对其进行任何处理。
光靠MultiAgent的Array<Text>内部状态和LLM的long context可以低成本的解决一些复杂度不高的问题,但其天花板很低,成本也很高。当开始试图压缩对话历史、提取其中的关键信息、甚至设计一些SubTask/Thought之类的概念的时候,就是在逐步朝着半结构化内部状态的方向不断深入了。
沿着半结构化的方向走到极限就变成了完全结构化的内部状态,这时候就变成了接近前LLM时代的程序设计的思路。这时候虽然仍然可以调用LLM,但传统的软件工程经验已经告诉我们:这条路不可行,光靠人类程序员进行开发的话成本无法接受。完全靠Agent开发复杂程序还很遥远,毕竟这就是本文讨论的复杂Agent想要解决的问题之一,自举都还没有完成。
半结构化内部状态的设计思路是比较简单的:流程设计者想要控制的部分应该结构化,不想控制的部分保留非结构化。例如设计者想要设定关于SubTask的处理方式,那么就需要提取每个SubTask。
目前LLM输出的内容要么是非结构化的Text,要么是function calling的参数,需要不断地从LLM的输出中提取结构化的信息,来更新Agent内部状态。
虽然可以把function calling作为自动让LLM输出结构化结果的方式,但现在function calling默认都是在LLM生成结果前就开始触发,这没有给LLM留下“思考”的空间。这导致经常需要用传统方式调用LLM一次,然后再专门让LLM从前一次回答结果中以function calling或json的形式提取信息。
我之前在 LLM native策略的内部状态是否应该结构化 【2023Q3】中就介绍过半结构化内部状态这个思路,但当时对于有自主能力的Agent是否应该这么干并没有结论。但目前我逐步倾向于复杂Agent现阶段也需要半结构化的内部状态设计,因为我发现有一些任务无法通过非结构化的方式可靠的完成。
【案例1】
举一个我研究过的例子,问题是:对某个query可靠且鲁棒的生成主题集合,用于自动化生成Agent人设。追求高完备性、要求结果对输入措辞不敏感。
我使用多轮Map-Reduce的方式进行投票汇总的方式,但发现这个问题很难解决,即使我最后使用的LLM总调用次数已经很高,也无法达到我的目标。主要问题在于Reduce阶段的LLM本身仍然很难解决“对输入措辞敏感”的问题,Map阶段的各种抽样对于主题的覆盖已经完备,但无法可靠的聚合出稳定的结果。
对此我目前认为“将主题和描述结构化提取出来,在框架的层面进行重叠判定和重新组织,然后结构化的投票”才是正确的思路。
【案例2】
考虑在message history中混有错误的尝试、答案等情况下的历史压缩问题。
一种思路是将其中所有的要点都提取为半结构化的信息,例如某个具体问题的答案。这样在后续提出了更正确的答案之后,就可以用新答案覆盖旧答案。只要确认它们都是对于同一个问题的答案,那么这个覆盖过程是很容易的。相对于让LLM模糊的在对话历史中剔除错误的回答又不引入新的幻觉且不丢失信息是容易的多的。
4.4、对抗伪随机性
肯定有些读者觉得上一节的案例1 “只要把temperature和随机数种子固定”就能解决了,但这个想法过于简单了。
虽然不少应用场景接受或者欢迎多样性,只要结果是可以接受的就行,不必稳定。但想象这样一个Agent:提问“对于某某问题,应该选方案A还是选方案B?”时候回答“选A”;但提问“对于某某问题,选方案A还是选方案B更好?”时候回答“选B”。这样的Agent你是否能够接受呢?普通用户是否能够接受呢?
很多时候用户可以接受Agent的结果准确率不够高,但不能接受它摇摆不定,这只会导致人们怀疑它给的每一次结果。即使我们消除了LLM推理和Agent流程中的所有随机性,仍然要面对LLM对于输入措辞敏感的问题。这时就好像用户的输入文本是一个随机数种子,Agent基于这个随机数种子产生了一个随机的答案。用户认为含义没有差别的两个问题,但没有随机性的Agent中却抽样到了语义完全不同的结果,我将这称为伪随机性问题。我之前的文章 被忽视的LLM伪随机性问题,贪心解码为什么无法消除它 【2023Q3】单独谈了这个问题。
对于复杂Agent来说,对抗伪随机性是必然要做的一个点,除非只想构建一些结果“不稳定”的Agent。
4.5、分场景的模型配置 与 对LLM模型的需求
考虑到前面的内容,复杂Agent中的LLM调用是可以按目的进行划分的:有些比较困难,例如提取知识、推理等等;有些比较简单,例如只是提取结构化信息、重复问题;还有些是中间的,例如决策需要更新半结构化内部状态中的哪些位置等。
很明显,对于一些难度不大的任务,可以调用更便宜的LLM来降低Agent的运行成本,并一般可以实现提速。
正经的Agent框架对于每种类型的LLM的调用都应该可以单独指定LLM配置。
目前的LLM一般可以分为几类:
昂贵、高质量、long-context的大参数LLM,例如gpt-4-turbo
便宜、能力有限、context window也有限的小参数量模型,例如各种开源的6B左右模型
应用开发者自己对于某些场景的微调模型
但考虑到复杂Agent的场景,还缺乏一些类型的模型:
便宜、long-context的小参数量模型,专门做一些类似秘书性工作的简单任务,但需要的上下文可能较长。
现有的开源拉长context的方案我并不看好。这类任务虽然不难,但如果准确率太低就没意义了。
针对复杂Agent中的一些常见子任务定制的小参数量模型,即上一条的更加特化、更加便宜的版本。
能够对于明显信息缺失进行主动提问的LLM
现在复杂Agent的调用LLM的总成本还太高,这限制了框架本身的复杂度,需要平均LLM调用成本降低1-2个数量级才行。
在目前MultiAgent框架中,还需要的一类功能是LLM在信息不完备时候的主动提问功能。虽然前面说了一个完善的主动提问功能很难做好,但目前的LLM在这上太差,我认为有合适成本的改进空间。如果前一级输入的信息不完备,就会导致后面的结果都有问题。这导致了现在的框架更加不能丢弃完整的message history,或贸然对其进行压缩。虽然可以通过一些流程设计来信息完备性检查和提问,但这个功能过于常用,构建包含此功能的模型来减少反复的检查环节在成本优化上是有必要的。
如何实现LLM的主动提问似乎还是开放问题,我之前文章 微调与RLHF在实际业务中的最佳实践思路【2023H2】 中的5.2节有描述一种思路。
4.6、内部状态的复杂度控制
前面3.6节已经说了Agent的策略框架能处理的复杂度限制了其能解决的问题的范围。通过半结构化和一些策略环节的设计,可以提升一些这方面的能力。但还有一个问题:在分析和解决一个问题的过程中,策略流程对于引入的复杂度的多少是否应该有某种偏好?这里说下我目前的看法。
人的思维和语言可以以一种很抽象和模糊的方式来讨论一些问题,例如讨论国家的教育体系该如何设计。在讨论这种很模糊/抽象的问题时,我们是否要把整个问题展开到讨论“高中的每节课是应该45min还是1h”呢?在复杂Agent的场景下这类问题的答案没有这么显然。
考虑到现在一般讨论的复杂Agent的用户还是人类用户,而人类用户提出问题时虽然会期待Agent能够考虑更多的细节,但这也不是无限的。即使算力是无限的,用户一般也不会希望随便问一个问题都返回一个100页的报告。
复杂Agent内部的复杂度展开应该还是惰性的,除非用户指定或者有其他明显的原因,否则不应该展开太多的复杂度和细节。可能需要在用户提问的层面往下展开一两层,但这不应该是无限的,这方面需要一种惰性展开细节的倾向。
4.7、与用户的交互
【问题输入阶段】
无论依赖的LLM模型是否有主动提问的能力,复杂Agent都应该在收到任务的早期就缺失的信息进行提问,因为:
一般用户输入都是懒惰的,这也是人类的沟通习惯之一
此时用户注意力还在这里,没有离开。
但用户补充信息的耐心是有限的,用户录入后续信息也仍然是懒惰的,我认为此时的交互需要:
要优先提重要的问题,次要的问题尽量作为之后的可选输入
对于每个不确定的维度,尽量列出所有常见可能性,让用户做选择,而非输入回答。只有在用户需求确实不常见时才进行输入描述。
对用户的提问问题应该一次性列在一个大盘上(可以不断追加)。把不确定的信息都放出来也有助于用户更好的思考清楚需求本身。不要每次只问一个问题,用户回答完马上给第二个,这让用户对要回答多少问题没底。
当用户失去耐心时,可以一键全默认,系统按它猜测的最大可能性配置进行,或者在“有限的复杂度内”对于常见情况进行分别讨论进行。
【运行中间的追问】
无论是人还是复杂Agent,都不能保证在收到任务的初期就能明确所有要确认的信息和选择。所以在运行过程中的追问是有必要的,特别是“当问题涉及到几种不同求解路径且选择错误时浪费的成本较高”时。
考虑到用户的耐心和目前复杂Agent系统的运行时间,可以假设当系统发出提问时,用户早就已经不再关注系统状态,甚至已经离开了。所以需要:
向用户提问时,重新组织一个最简化的问题,而不是简单让用户去看系统历史。
如果用户短时间内未反馈或已经离开,系统应该选择继续运行,并同时等待用户回来,该功能是可配置的。这样的目的是为了避免在用户离开很久的情况下浪费用户的时间。
对用户的确认线程也并非block在一个问题上,可以随着后台的运行不断新增问题。
当用户返回并提示新的信息后,系统应该基于用户的输入和后台继续运行的结果重新规划执行。
这个功能做起来并不简单,但从产品层面的分析来看,这是需要的。
【中间过程的展示】
当系统运行长时间运行时,应该给出“合适”的中间状态展示,即使没有问题需要询问用户。除了这是一个普通的交互涉及要求外,还可以方便用户检查系统运行的方向是否偏离了用户的预期,甚至可以:把优先探索方向的选择也做成非必答的问题向用户展示,用户可以去指定优先尝试的方向,也可以无视,放任系统按自己计划探索。
从交互的角度上来说,有两个主要的要求:
单位时间内展示给用户的信息量不应该过大
展示给用户的信息不应该过于细碎,这个细碎的标准是以用户是否关注为标准的。
最终效果就类似于:
系统总体上大约每分钟输出多少条信息(准确来说是近似地看总字数),这个速度要在用户“习惯”的范围内。
输出的信息应该是经过概括的,不能过于细碎,尽量维持在某个固定的抽象层面上。
同时有另一个侧栏窗口列了系统当前可供干预的一些维度和选项,包括:目前优先探索的方向、尚未经过用户确认的一些假设等。
【回放系统】
作为一个消耗很多资源和时间,内部状态很复杂的系统,应该有一个历史任务执行过程的回放系统。无论是给开发人员进行调试还是给用户进行探索都是需要的。
这个回放系统也应该可以逐步展开细节,而不是只能去看大量的log日志行。
【总结】
与用户交互是一个很重要的feature,值得为此去改变策略执行方式,并且为了优化展示、提问等去增加“额外”的LLM调用。
从成本优化的角度来说,这应该做成可选项,来方便无人值守的时候关掉来降低成本。
4.8、多方案并发探索
从规划的角度来说,Agent需要:1生成解决方案,2在潜在的解决方案中进行选择去探索。XoT类框架会在内部状态同时跟进多个不完全的方案。MultiAgent的对话方式是随机跳入某个看起来还行的方向不断探索和完善,在多方案管理上做得很弱。
针对于较为复杂问题求解的Agent几乎肯定需要多方案的探索和管理,每次选择最有可能的方案去进一步探索,这跟A*或者AlphaZero的思路没有本质的不同。
这要求能够在过程中产生多个方案并进行管理,在适当的时候挂起一个方案的探索,也可以在适当的时候恢复一个方案的探索。这是XoT框架的基本功。比较麻烦的是如何在不同的方案中发现重复的子任务,以及更加广义的在不同的方案探索过程中横向传递信息。
同时管理多个方案带来一些好处:
能够在一个方案的探索过程中发现其成本与最初计划不同的快速增加时,触发探索其他方案,并保持随时回来的能力。
可以同时并行探索多个方案,加速系统的求解过程
更自然地支持不耦合的子任务的并行探索。
更自然的支持与用户的异步交互
我认为这是肯定要做的功能。
4.9、方案评价模型
当有多方案并发探索能力之后,就有了如何选择方案(或评价方案相对好坏)的问题。这个能力在A*算法中叫做启发式函数h,在RL中叫做Q值函数、Value-Network等。
在Agent的场景下,最简单的方式是让LLM做zero-shot的打分/排序,选择它比较看好的方案。
当然一般期望这个方案评价模型可以根据本次session的信息来做快速更新。但很遗憾现在从实用的角度来说,LLM仅靠这些数据进行微调还无法达到稳定实用水平。另一个思路是把历史的尝试结果加入到context中,用类似few-shot的方式去预测新的方案的效果。随着样本的增多需要更大的long-context能力的LLM,而且叫few似乎也不太合适,也许得称作many-shot。当然这种方式也有不少细节问题,例如当样本数量过多时是否要做RAG进行筛选、如何避免引入shot之间不必要的相关性而影响结果的正确率。
在多次执行的session之间,大概率也有可复用的信息,当然这也跟Agent部署的场景有关。这方面的数据似乎多了一些,但在有足够的数据前我也并不看好仅通过微调来让LLM学习这部分历史经验。
5、总结
虽然前面罗列了很多问题,以及后续的一些可改进的思路,但我想对于写作(1)文的时候,对于自己是否已经看到了整个局面中的大部分问题感到怀疑。很可能这些只是一部分冰山,而还有很多问题在我们的视野之外。
从这个角度来说,我并不看好短期之内就有团队能做出一个较为完备的复杂Agent策略框架。像OpenAI这种在应用层策略架构能力明显弱于生态的(这是从它们做出的产品来推断)就更不要指望了,它未来一段时间在Agent框架上大概还是要抄生态的答卷。
但从探索的角度上来说,有必要去做一些探究性的原型框架来探索还有哪些我们所没预见到的问题。不做这些进一步的探索尝试大概很难发现哪些潜在的冰山。也许我们要重复“做一个新框架原型,发现问题,重新思考和认知”这个循环几次才能找到哪些冰山问题。
也可能我们永远无法达到“对看清这个问题有足够的自信”,也许人工构建策略框架这个事情在成本上就并不可行。
现在还没有哪个项目足够认真地探索了这个问题,信息不足。后续应该继续探索和实验新策略框架的设计。
交流与合作
如果希望和我交流讨论,或参与相关的讨论群,或者建立合作,请私信联系,见 联系方式。
希望留言可以到知乎对应文章下留言。
本文于2023.12.11首发于微信公众号与知乎。
知乎链接 https://zhuanlan.zhihu.com/p/671618601