第 11 章《AgentRunner LLM 循环》· nanobot AgentRunner 源码深度解析:_request_model + _execute_tools 多轮工具调用

发布时间:2026/10/1 3:59:34

第 11 章《AgentRunner LLM 循环》· nanobot AgentRunner 源码深度解析:_request_model + _execute_tools 多轮工具调用 本文回答什么问题:AgentRunner.run() 内部怎么循环?_request_model怎么调 LLM?_execute_tools怎么跑工具?迭代上限 / 流式 / 推理块怎么协调?目标读者:LLM Agent 开发者 / Provider 适配者预计阅读时间:16 分钟源码版本:GitHub HKUDS/nanobotmain 分支主线代码(仓库相对路径)AgentRunner(nanobot/agent/runner.py,约 1505 行)是 nanobot 模型层多循环——把单次 LLM 调用扩展为多轮 LLM 工具执行的迭代过程。1. 整体定位:为什么 AgentRunner 单独成模块如果让 AgentLoop 直接调 LLM,AgentLoop 就要处理 200 轮迭代 工具调用 流式——膨胀到 5000 行。AgentRunner 抽出模型层关注,AgentLoop 只负责调度。核心要点速查(建议收藏)核心文件:nanobot/agent/runner.py(约 1505 行)主入口:AgentRunner.run(spec, hook)跑单回合(可能 200 轮)3 个核心子方法:_request_model(messages)/_execute_tools(tool_calls)/_finalize(result)迭代上限:默认 200 次(Config.max_iterations)4 类 stop_reason:tool_calls/completed/max_iterations/error2. AgentRunner.run() 主循环asyncdefrun(self,spec:AgentRunSpec,hook:AgentTurnHook|NoneNone)-AgentRunResult:messageslist(spec.messages)outbound:list[OutboundMessage][]iteration0whileiterationspec.max_iterations:iteration1# 1. 调 LLMresponseawaitself._request_model(messages)outbound.extend(response.stream_events)# 流式片段# 2. 决定是否继续ifresponse.stop_reasoncompleted:# 已生成最终回复,退出循环outbound.append(self._finalize(response))returnAgentRunResult(messagesmessages,outbound_messagesoutbound,iterationiteration)ifresponse.stop_reasontool_calls:# 3. 跑工具tool_resultsawaitself._execute_tools(response.tool_calls)messages.extend(tool_results)continue# 下一轮# 4. 达到 max_iterationsreturnAgentRunResult(messagesmessages,outbound_messagesoutbound,iterationiteration,stop_reasonmax_iterations)3. 3 个核心子方法3.1_request_model(messages)→ LLMResponseasyncdef_request_model(self,messages:list[Message])-LLMResponse:responseawaitself._provider.chat(messagesmessages,toolsself._spec.tools.to_openai_schema(),# 工具清单转 OpenAI 格式streamTrue,# 流式)# 流式累积contenttool_calls:list[ToolCallRequest][]stream_events:list[OutboundEvent][]asyncforchunkinresponse:ifchunk.typecontent_delta:contentchunk.text stream_events.append(StreamDeltaEvent(contentchunk.text))elifchunk.typetool_call_delta:tool_calls.append(chunk.tool_call)elifchunk.typereasoning_delta:stream_events.append(StreamDeltaEvent(contentchunk.text,kindreasoning))returnLLMResponse(contentcontent,tool_callstool_calls,stream_eventsstream_events,stop_reasontool_callsiftool_callselsecompleted,)关键:所有 8 个 Provider 都通过LLMProvider.chat()统一接口(详见第 17 章)流式片段直接转OutboundEvent让 AgentLoop 投递给通道工具调用转ToolCallRequest(详见第 17 章)3.2_execute_tools(tool_calls)→ list[ToolResult]asyncdef_execute_tools(self,tool_calls:list[ToolCallRequest])-list[Message]:results:list[Message][]forcallintool_calls:toolself._spec.tools.get(call.name)iftoolisNone:results.append(Message(roletool,namecall.name,contentfError: tool {call.name} not found))continue# scope 检查(workspace / network / pairing,详见第 29 章)ifnotself._tool_scope.allows(tool,call):results.append(Message(roletool,namecall.name,contentError: permission denied))continue# 调工具resultawaittool.execute(**call.arguments)results.append(Message(roletool,namecall.name,contentresult.content))returnresults关键:工具调用按call.name查 ToolRegistryscope 校验失败的工具返回错误消息(不抛异常)工具结果作为roletool消息反馈给 LLM3.3_finalize(response)→ OutboundMessagedef_finalize(self,response:LLMResponse)-OutboundMessage:returnOutboundMessage(channelself._spec.channel,chat_idself._spec.chat_id,contentresponse.content,eventNone,# 纯文本)4. 4 类 stop_reasonstop_reason含义后续动作tool_callsLLM 想调工具_execute_tools→ 下一轮completedLLM 已生成最终回复退出循环max_iterations达到 200 轮强制结束(返回当前结果)errorLLM 调用失败返回错误消息(详见 §5)5. 错误处理asyncdef_request_model(self,messages):try:responseawaitself._provider.chat(...)exceptProviderErrorase:# fallback provider(详见第 20 章)ifself._fallback:responseawaitself._fallback.chat(...)else:returnLLMResponse(contentf⚠{e},stop_reasonerror)returnresponse3 层容错:单 Provider 失败 → FallbackProvider 兜底Fallback 失败 → 返回错误消息给通道AgentLoop 收到错误消息 → 通知用户6. 实战:增加轮次上限# config.yamlmaxIterations:50# 默认 200,降为 50# Pythonfromnanobot.configimportload_config configload_config()print(config.max_iterations)# 507. 常见问题 / 避坑Q:LLM 流式输出什么时候结束?A:Provider 的stream返回的 chunk 序列结束 →_request_model完成。AgentLoop 把累积的stream_events投递给通道渲染。Q:max_iterations太小会怎样?A:LLM 多轮任务未完成就强制结束,可能产生半成品回复。建议设 50-200。Q:工具调用超时怎么办?A:每个 Tool.execute 有自己的timeout(默认 60s);超时会抛ToolTimeoutError,被_execute_tools捕获后转roletool错误消息。8. 小结主循环:while iteration max_iterations: _request_model → 决定 stop_reason → _execute_tools / 退出关键模块:3 个核心子方法:_request_model/_execute_tools/_finalize设计要点:4 类 stop_reason:tool_calls/completed/max_iterations/error常见坑:3 层容错:Provider 错误 → FallbackProvider → 返回错误消息本文要点速查run()主循环默认 200 轮,见 §23 个核心子方法见 §34 类 stop_reason见 §4下一步:第 12 章《ContextBuilder 系统 Prompt》—— 阶段 ③ 的build_messages()详细展开按角色推荐LLM Agent 开发者:必读(模型层核心)LLM Provider 适配者:必读(Provider 接口契约)系统架构师:选读(知道 AgentRunner.run 即可)聊天通道开发者:选读Tool / MCP 工具开发者:选读(知道 Tool 怎么被调用即可)下一步第 12 章《ContextBuilder 系统 Prompt》——_request_model之前的 messages 怎么构造(主题群Agent 核心,第 3 周)第 17 章《LLMProvider 抽象》——chat()接口完整契约(主题群LLM Provider,第 4 周)第 20 章《FallbackProvider 兜底》——_request_model错误的兜底路由(主题群LLM Provider,第 4 周)tags:#nanobot#AI Agent#LLM#Python#源码解析#AgentRunner#工具调用
延伸阅读

更多相关文章

2026/9/27 4:51:50

长期合作供应商考察:珠三角热镀锌无缝管售后服务对比

珠三角热镀锌无缝管供应商考察:从售后响应看长期合作价值在珠三角地区的工程建设与制造业供应链中,珠三角热镀锌无缝管因兼具优异的防腐性能与高强度承压能力,成为许多项目选材的重点关注对象。面对市场上众多的钢材批发商与加工服务商&#…

2026/10/1 3:46:08

AI绘画提示词工程:从Seedance 2.5学习高效提示词结构与实战应用

1. 先搞清楚 Seedance 2.5 是什么,以及它能帮你解决什么问题如果你正在找一套能直接上手、效果稳定的 AI 绘画提示词,特别是想生成那种有强烈动感、光影氛围和电影感的图像,那么 Seedance 2.5 这个提示词精选集就值得你花时间研究一下。它不是…

2026/9/29 3:22:09

基于Tauri+React+Rust构建一体化多项目开发桌面应用实践

1. 项目概述:从“多开”到“一体化”的研发协作新范式最近在折腾一个挺有意思的玩意儿,我把它叫做“TalkCozy”。这个名字听起来有点玄乎,但核心想法其实特别简单:能不能像我们平时聊微信一样,同时、流畅地处理多个开发…

2026/10/1 3:56:28

hindsight 记忆分层架构:MCP 协议接入与 Docker 化部署实战

1. 从“hindsight”这个词说起:为什么它值得单独拿出来聊第一次看到“hindsight”作为项目名,我脑子里蹦出来的不是词典释义,而是一个很具体的场景:你在跟一个 LLM Agent 对话,它前面已经帮你查过三次数据库、改过两版…

2026/10/1 3:56:28

基于MCP与Docker构建LLM Agent记忆系统实战

1. 从“hindsight”说起:为什么我们需要给Agent装上“后视镜”“hindsight”这个词本身很有意思,字面意思是“事后的洞察力”,也就是我们常说的“后见之明”。放在LLM Agent的语境里,它指向一个非常具体且棘手的问题:A…

2026/10/1 3:56:28

条件期望与全期望公式:数值笔试高频考点完全拆解

数值笔试系列走到第二篇,这次要啃的是三个高频考点:条件期望、全期望公式、重复独立试验。如果你在准备算法岗、量化岗,或者任何要考概率统计数值题的岗位,这三个名字几乎每次都会出现在试卷上。我见过不少同学拿着厚厚的讲义反复…

2026/10/1 3:51:28

VC6老工程数字识别救星:tesseract-2.01源码包编译与调优实战

简介:tesseract-2.01.rar 是一份面向数字与英文 OCR 识别场景的开源工具源码包,基于 Google 维护的 Tesseract 引擎早期版本,并针对 VC6.0 编译环境做了适配,适合需要在老项目或旧系统中集成文字识别的开发者,以及希望…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑