xmlns="http://www.w3.org/2000/svg"style="display:与多模态应用前两篇教程里,我们把文本生成、结构化输出和基础工具调用都摸了一遍。用maxSteps搞出来一个多步推理的小demo,看着挺像那么回事但说实话,真要做一个能自己思考、自己决策的Agent,光靠那些基础循环是不够的。你会发现逻辑越写越乱,代码越堆越多,到最后自己都不知道在第几步干了什么这篇文章我们聊点更实战的东西。ToolLoopAgent怎么把智能体封装得更优雅,LoopControl封装起来还记得上一章的汇率助手吗?我们把工具和maxSteps直接塞进generateText里。写个demo没问题,但你要是真的做业务,这代码很快就会变成一坨意大利面你想想,每次都要重新配置工具、重新写循环逻辑,改个参数还得全局搜索。更要命的是,你没法把“这个会搜索的AI”当成一个独立模块复用ToolLoopAgent就是来解决这个痛点的。它把模型、工具、循环规则全打包成一个对象,你可以像调函数一样调用它。想要多个不同能力的Agent?实例化几个就行实战案例:做一个会搜资料的研究员Agent我们来搞个能模拟搜索、记笔记、写总结的Agentimport{ToolLoopAgent,tool,stepCountIs}from'ai';import{openai}from'@ai-sdk/openai';import{z}from'zod';importdotenvfrom'dotenv';dotenv.config();//定义工具集consttools={searchWiki:tool({description:'搜索维基百科摘要',parameters:z.object({query:z.string()}),execute:async({query})=>{console.log(`[Agent操作]正在搜索:${query}...`);//模拟搜索结果return{title:query,summary:`${query}是一种强大的技术...(模拟数据)`};},}),saveNote:tool({description:'保存关键信息到笔记',parameters:z.object({content:z.string()}),execute:async({content})=>{console.log(`[Agent操作]记录笔记:${content}`);return{success:true};},}),};//实例化AgentconstresearcherAgent=newToolLoopAgent({model:openai('gpt-4o'),system:'你是一个严谨的研究员。遇到问题时,先搜索信息,然后记录关键点,最后总结回答。',tools:tools,//默认停止条件:最多运行步stopWhen:stepCountIs(10),});asyncfunctionmain(){//Agentconstresult=awaitresearcherAgent.generate({prompt:'请研究一下Vercel的主要功能,并做简要总结。',});console.log('---最终报告---',result.text);}main().catch(console.error);看到没,ToolLoopAgent把所有配置都收拢在一起了。你想在别的地方用这个研究员?直接researcherAgent.generate()就完事。想搞个编码Agent、审核Agent?照着这个模式再实例化几个,让它们互相配合干活这才是工程化的写法2.Loop跑起来就是个循环:思考→行动→观察→再思考。AISDK机制,让你能插手这个循环的任何环节stopWhen:自己定义什么时候停默认情况下,Agent会一直跑到模型觉得任务完成了,或者达到最大步数。但有些场景你得强制控制,比如“调用了某个工具就立刻停”,或者“置信度太低了别继续瞎搞了”import{stepCountIs}from'ai';//组合多个停止条件stopWhen:[stepCountIs(10),//防止死循环(step)=>{//工具就立刻停returnstep.toolCalls.some(call=>call.toolName==='escalateToHuman');}]prepareStep:在每一步执行前动态调整prepareStep这个钩子很有意思。它能让你在Agent每走一步之前,偷偷修改请求参数。比如注入当前时间、用户位置这些动态信息,或者根据前面的执行结果临时换一套工具constdynamicAgent=newToolLoopAgent({model:openai('gpt-4o'),tools:myTools,prepareStep:async({messages,stepCount})=>{//每一步执行前,把当前时间塞进去consttimeContext=`当前系统时间:${newDate().toISOString()}`;return{system:`你是一个时间感知的助手。${timeContext}`,//步之后,换一套简化的工具集tools:stepCount>3?simplifiedTools:myTools,};},});3.多模态:让把多模态输入的接口统一了,发图片变得特别简单以前你得手动把图片转Base64,再拼到里,麻烦得要死。现在content属性直接支持数组,文本和图片混着传就行实战案例:发票识别助手import{generateText}from'ai';import{openai}from'@ai-sdk/openai';importfsfrom'node:fs';asyncfunctionanalyzeInvoice(){constresult=awaitgenerateText({model:openai('gpt-4o'),messages:[{role:'user',content:[{type:'text',text:'帮我看看这张发票,把总金额和日期提取出来'},{type:'image',//URL或者本地文件都行image:fs.readFileSync('./invoice.jpg'),},],},],});console.log('识别结果:',result.text);}这个多模态能力可以跟结构化输出配合使用。你传一张网页截图进去,让用generateObject生成对应的HTML逻辑一复杂,光靠console.log调试真的会疯。你根本看不清模型在第BVercel的可视化调试工具,专治这种疑难杂症怎么用:装个依赖:npminstall@ai-sdk/devtools用wrapLanguageModel把模型包一层,让它能捕获所有输入输出:import{wrapLanguageModel}from'ai';import{experimental_createToolLoopAgent}from'ai';import{devtools}from'@ai-sdk/devtools';constmodel=wrapLanguageModel({model:openai('gpt-4o'),middleware:devtools,});//后面在ai-sdk-devtools打开浏览器访问http://localhost:4983,你会看到一条清晰的时间轴。每次LLM消耗、工具调用的详细参数,全都一目了然这玩意对优化System“幻觉”问题太有用了。你能看到模型到底在哪一步开始胡说八道的写在最后到这里,你已经从写简单脚本的阶段,进化到能设计完整Agent系统的程度了ToolLoopAgent让你把智能体封装成可复用的模块,LoopControl的核心能力你都摸透了下一篇实战项目篇,我们会把这些知识全部串起来,做一个真正能上生产的RAG聊天机器人。它会有文档理解、记忆保持、专业问答这些能力。到时候你会发现,前面学的这些东西是怎么在实际项目里配合工作的