经常使用Agent帮你干活的同学们都知道,你跟claude说“帮我做一个贪吃蛇的页面小游戏”,它马上就会吭哧吭哧地开始帮你创建文件夹、写代码,最后给你一份成品。可是你跟大模型直接对话的时候,却只能你说一句它回一句。
这是为什么呢?Agent和LLM有什么区别呢?这就要说到Agent的两个核心能力:Agent Loop --- 心跳,Tool System --- 手脚。
Agent 的心跳
Agent和LLM最本质的区别就是Agent的心跳机制,其实也就是一个while循环。Agent在工作时,LLM遇到解决不了的问题,就会返回一个tool_call类型的字段来告诉你它需要调用什么工具来解决这个问题,当然,前提是你先做好一个工具系统,提前告诉LLM你有哪些工具可供调用。
那么我们就可以用是否有工具调用作为条件来决定是否结束while循环,如果有工具需要调用,Agent中就会帮LLM调用工具,然后把结果返回给LLM,然后继续循环。当某一轮循环不再有工具需要调用时,就可以结束循环。
import { streamText } from "ai";
const MAX_STEPS = 50 // 设置一个最大循环次数
let step = 0 // 当前这轮的循环次数
while(step < MAX_STEPS) {
step++;
let hasToolCall = false // 当前这轮是否有工具调用
const result = streamText({
model,
tools,
messages,
system,
)}
for awit (const part of result.fullStream) {
if(part.type === 'tool_call') {
hasToolCall = true
break
}
}
stepResponse = await result.response
messages.push(...stepResponse!.messages)
if (!hasToolCall) {
if (fullText) console.log()
break
}
}
Agent 的手脚
想要让Agent完成任务,需要有一个Tool System,因为LLM本身干不了这些事情,需要你自己准备多个工具来让Agent完成你的任务,向LLM通信时可以传一个tool参数告诉它有哪些工具可以使用。Agent里都会事先打造一些基本工具,比如读写文件之类的。
我们可以使用AI SDK来打造工具,这样LLM返回工具调用时,这个包会帮你执行execute属性内的代码。比如:
const readFileTool = {
name: 'read_file',
description: '读取指定路径的文件内容',
parameters: {
type: 'object',
properties: {
path: {
type: 'string',
description: '文件路径',
}
},
required: ['path'],
additionalProperties: false,
},
execute: async ({ path }: { path: string }) => {
return readFileSync(resolve(path), 'utf-8')
},
}
然后将工具传给LLM。当然也可以接入一些已经打造好的MCP服务,里面会包含很多工具可供使用。这样Agent就能一步一步帮你完成任务了。
你已经了解了Agent中最基础的两个功能了,那么开始打造你自己的Agent吧!