内容提要
文章提出“编码代理三明治”模式来架构LLM系统:先进行功能分解,将问题拆分为不同职责,再逐一判断用普通代码还是LLM实现。能精确计算的任务优先用代码,因为可重复、易调试、快速且低成本;仅在需要判断或处理模糊输入时使用LLM。编排也属于职责,可由代码或LLM承担。LLM应尽量窄化,工具调用等确定性操作交给代码。
延伸解读
何时用代码,何时用LLM:判断标准
文章提出明确标准:当你能精确指定如何计算答案时,用普通代码(POC);当只能指定好答案的样子而无法指定计算过程时,才用LLM。POC具有可重复、易调试、快速、低成本的优势;LLM则适合处理模糊输入、自然语言理解或需要判断的任务。这一标准帮助避免“黄金锤”反模式,即盲目用LLM解决所有问题。
LLM应窄化:避免大包大揽
文章强调,将职责分配给LLM时,应保持职责狭窄。例如,在实现计划工作流中,为每个任务调用一次编码代理,而不是用一次代理运行实现整个计划。大范围的LLM调用或代理运行可靠性更低,可能中途停止,且浪费昂贵资源去做POC能廉价完成的工作。窄化LLM职责有助于提升系统整体可靠性和成本效益。
编排职责:代码还是LLM?
编排本身也是一种职责,同样适用POC或LLM的选择。文章给出两种选项:用POC编排整体工作流,或用LLM代理决定下一步动作。实现计划工作流采用POC编排,形成“编码代理三明治”的顶层。当LLM根据上一步结果决定下一步时,该职责由代理实现,代理包含LLM、执行循环的POC harness和工具。选择取决于是否需要动态判断。
LLM常位于系统边界,但并非绝对
应用POC或LLM规则后,LLM往往位于系统边界,负责解释非结构化、模糊的真实世界输入,将其转化为结构化状态,后续操作由POC执行。例如,运维支持请求由LLM解析为结构化工单,POC验证并路由。但文章指出,并非所有LLM职责都在边界,如实现计划工作流中,LLM还负责编写代码等需要判断的工作。
Q&A
什么是“编码代理三明治”模式?
“编码代理三明治”是一种用于构建编码代理框架的设计模式,其结构是在两层普通代码(POC)之间夹着一层LLM调用。其核心规则是:能用普通代码时就用普通代码,必须用LLM时才用LLM。
在LLM系统架构中,什么时候应该使用普通代码(POC)而不是LLM?
当你能明确指定如何计算答案时,应使用普通代码(POC)。这适用于规则已知且正确性可以精确定义的情况。POC具有可重复性、易于调试、运行速度更快且成本更低的优点。
在LLM系统架构中,什么时候应该使用LLM而不是普通代码?
当你能指定一个好的答案是什么样子,但无法合理指定如何计算它时,应使用LLM。这适用于输入模糊或非结构化、需要理解自然语言、或需要判断和解释的情况。
如何对使用LLM的系统进行功能分解?
首先进行递归功能分解,识别解决问题所需的责任,包括编排(决定下一步执行哪个责任的控制流)。分解是分层的,一个责任可以进一步分解为子责任和一个协调器。然后对每个责任决定使用POC还是LLM实现。
在LLM系统中,编排责任应该由代码还是LLM承担?
编排本身也是一个责任,因此同样适用POC或LLM的决策规则。有两种选择:用POC编排(如实现计划工作流)或用LLM编排(即代理模式,由LLM决定下一步行动)。
为什么应该将LLM责任尽量窄化?
窄化LLM责任可以提高可靠性,因为处理多个责任的LLM调用或代理运行更容易出错(例如中途停止),并且更浪费,因为使用了昂贵的LLM来完成POC可以更廉价完成的工作。
在LLM系统中,工具调用等确定性操作应该由谁处理?
工具调用等确定性操作应实现为POC工具,由LLM调用。这些工具具有明确定义的行为,形成“三明治”的底层。例如,编码代理调用Gradle运行测试、Git提交更改。
LLM在系统架构中通常位于什么位置?
应用POC或LLM规则后,LLM通常位于系统的边界。在边界处,系统接收来自现实世界的非结构化、模糊的输入,需要解释。一旦输入被解释,就变成结构化状态,由POC实现的操作处理。
实现一个责任时,最初选择LLM还是POC的决定可以改变吗?
可以。最初可能因为问题理解不够而使用LLM,随着理解加深,可能发现可以指定计算方式,从而改用POC,使其更快、更便宜、可重复。反之,如果POC需要不断添加规则,可能改用LLM更简单。切换前应比较两种实现。