代理循环:三重循环的风衣
代理循环常常被过于简化。它们被呈现为一个单一的循环,实际上是三个循环的结合,构成了客户的“代理”体验。我在这里写(是的,我写了这篇,疯狂吧?)另一篇关于代理循环的博客。示例代码块也是伪代码,用于说明这些想法。另外,我省略了流媒体,这使得这篇文章复杂化,但这些循环的形状保持不变。我甚至制作了这张图片!解释大型语言模型的最简化方式是,它们接收文本,并预测下一个字符……呃……标记。与您的前任不同,它们确实能完成您的句子。这是通过推理循环实现的。您的推理循环有三个职责:进行聊天完成API调用(推断下一个词)将工具使用请求传递给您的工具循环(稍后介绍)管理聊天历史的持久性(工具结果或更多用户消息)当您构建代理时,第一个“外部”循环就是推理循环。这是将系统提示、用户和助手消息以及可用工具发送到LLM的“聊天完成”端点的循环。Anthropic有一个。OpenAI有一个。OpenRouter使所有这些变得易于使用。一旦LLM返回其消息,您就有责任将其附加到“聊天历史”中,以便能继续对话。这可以是一个数组、数据库表、Redis键等等。随您决定。 chatMessages = [ { role: "system", content: "您是一名情侣治疗师。您帮助人们重新建立关系,或者让他们意识到这段关系永远无法修复。您正在与之交谈的用户名叫汤姆。" }, { role: "user", content: "我有点想念Laney,我应该给她写什么?" } ] continueInferenceLoop = true while continueInferenceLoop inferred = aiClient.completeChat(messages: chatMessages) chatMessages.push({ role: "assistant", content: inferred.message.content, toolCalls: inferred.toolCalls, }) if inferred.toolCalls.length == 0 continueInferenceLoop = false else # 处理工具(稍后更多) end end finalResult = chatMessages.last.content puts "助手的回应是:#{finalResult}" (大部分)大型语言模型提供商的API设计是无状态设计。这意味着模型提供商无法知道您与它的之前对话内容。您每次都需要提供整个对话。这就是为什么您会看到“较大的对话会更快地使用令牌”的警告——是的,我输入了一个破折号——因为您正在发送关于是否应该联系您前任的大量消息。汤姆,别这样。大型语言模型就是瓶子里的大脑。它们本身没有任何功能价值。您给LLM的工具才使它成为一个代理。当您在外部推理循环中告诉模型“这是您拥有的工具”时,模型可能会尝试在其推理(响应)中“使用”它们。这就像大脑发送电信号,告诉您的食指在您迫切想发送给Laney的电子邮件的回车键上悬停一样。汤姆,我们需要设定界限。您在API请求中包含的单独工具定义通常被序列化到模型处理的令牌流的系统提示字段中。它可能在一轮推理中推断出使用多个工具。(因此:工具循环)。 chatMessages = [ { role: "system", content: "您是一名情侣治疗师。您帮助人们重新建立关系,或者让他们意识到这段关系永远无法修复。您正在与之交谈的用户名叫汤姆。" }, { role: "user", content: "我有点想念Laney,我应该给她写什么?" } ] tools = [ { type: "function", function: { name: "send_ex_girlfriend_an_email", parameters: { type: "object", properties: { email_message: {type: "string"} }, required: ["email_message"] } } } ] continueInferenceLoop = true while continueInferenceLoop inferred = aiClient.completeChat( messages: chatMessages, tools: tools ) chatMessages.push({ role: "assistant", content: inferred.message.content, toolCalls: inferred.toolCalls, }) if inferred.toolCalls.length == 0 continueInferenceLoop = false else # 看吧……工具循环! while tool = inferred.toolCalls.shift # 在这里处理工具操作,比如发送电子邮件 # 您可能会有一个开关/案例语句来控制 # 根据名称选择工具。 # 工具调用也有一个ID toolResult = "...请看下一部分..." # 工具结果形状因提供者的API而异 chatMessages.push({ role: "user", content: [{ type: "tool_result", toolCallId: tool.toolCallId, content: toolResult }] }) end end end 您的工具循环必须查找模型推断应该使用的工具,并使用模型提供的参数调用您自己的函数。但请记住一些事情:因为工具调用也是推测文本——这意味着工具名称或函数参数可能会被虚构。您应该对这些错误的工具调用保持警惕,类似于工具未找到:“call_my_ex_girlfriend”API响应将返回一个tool_call_id(如果您使用的是Anthropic则为tool_use_id)。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡