Skip to main content

多模态自主代理应用

多模态自主代理应用1. 概念介绍1.1 “自主代理”是什么?1.2 实现原理简述 2. 代码解析关键代码1. Agent核心工作流 (largemodel/utils/ai_agent.py)2. 任务规划与LLM交互 (largemodel/utils/ai_agent.py)3. 参数处理与数据流实现 (largemodel/utils/ai_agent.py)代码解析3. 实践操作3.1 配置离线大模型3.1.1 配置LLM平台 (yahboom.yaml)3.1.2 配置模型接口 (large_model_interface.yaml)3.2 启动并测试功能 (文本输入模式)4. 常见问题与解决方案4.1 代理行为异常问题1:代理陷入了无限循环,或者反复执行同一个工具。4.2 工具调用失败问题2:代理正确地规划了行动,但工具执行失败了。


1. 概念介绍

1.1 “自主代理”是什么?

largemodel项目中,多模态自主代理 是最高级的智能形态。它不再是简单地对用户的一个指令做出一次回应,而是能够为实现一个复杂目标,自主地思考、规划、并连续调用多个工具来完成任务

该功能的核心是 agent_call 工具或其背后的工具链管理器(ToolChainManager。当用户提出一个无法通过单次工具调用完成的复杂请求时,自主代理便会启动。

1.2 实现原理简述

largemodel中的自主代理实现,遵循了业界主流的ReAct (Reason + Act) 范式。其核心思想是模仿人类解决问题的过程,在“思考”和“行动”之间循环。

  1. 思考 (Reason) : 当代理接收到一个复杂目标时,它首先会调用一个强大的语言模型(LLM)来进行“思考”。它会自问:“为了完成这个目标,我的第一步应该做什么?我应该使用哪个工具?” LLM的输出不是最终答案,而是一个行动计划。
  2. 行动 (Act) : 代理根据LLM的思考结果,执行相应的行动——调用ToolsManager来运行指定的工具(如visual_positioning)。
  3. 观察 (Observe) : 代理获取上一步行动的结果(“观察”),例如 {"result": "找到了杯子,位置在[120, 300, 180, 360]"}
  4. 再次思考 : 代理将这次的观察结果,连同最初的目标,再次提交给LLM进行第二轮“思考”。它会自问:“我已经找到了杯子的位置,接下来为了知道它的颜色,我该做什么?” LLM可能会生成新的行动计划,例如 {"thought": "我需要分析杯子所在区域的图像来判断颜色", "action": "seewhat", "args": {"crop_area": [120, 300, 180, 360]}}

这个 思考 - > 行动 -> 观察 的循环会一直持续,直到最初的目标达成,代理才会生成并输出最终的答案。

2. 代码解析

关键代码

1. Agent核心工作流 (largemodel/utils/ai_agent.py)

_execute_agent_workflow函数是Agent的执行主循环,它定义了“规划 -> 执行”的核心流程。

​ x # From largemodel/utils/ai_agent.py ​ class AIAgent : # ... ​ def _execute_agent_workflow ( self , task_description : str ) -> Dict [ str , Any ]: """ Executes the agent workflow: Plan -> Execute. / 执行Agent工作流:规划 -> 执行。 """ try : # 第一步:任务规划 self . node . get_logger (). info ( "AI Agent starting task planning phase" ) plan_result = self . _plan_task ( task_description ) # ... (规划失败则提前返回) ​ self . task_steps = plan_result [ "steps" ] ​ # 第二步:按顺序执行所有步骤 execution_results = [] tool_outputs = [] ​ for i , step in enumerate ( self . task_steps ): # 2.1. 在执行前,处理参数中的数据引用 processed_parameters = self . _process_step_parameters ( step . get ( "parameters" , {}), tool_outputs ) step [ "parameters" ] = processed_parameters ​ # 2.2. 执行单个步骤 step_result = self . _execute_step ( step , tool_outputs ) execution_results . append ( step_result ) ​ # 2.3. 如果步骤成功,保存其输出以供后续步骤引用 if step_result . get ( "success" ) and step_result . get ( "tool_output" ): tool_outputs . append ( step_result [ "tool_output" ]) else : # 如果任一步骤失败,中止整个任务 return { "success" : False , "message" : f"Task terminated because step '{step['description']}' failed." } # ... 总结并返回最终结果 summary = self . _summarize_execution ( task_description , execution_results ) return { "success" : True , "message" : summary , "results" : execution_results } ​ # ... (异常处理)

2. 任务规划与LLM交互 (largemodel/utils/ai_agent.py)

_plan_task函数的核心是构建一个精密的Prompt,利用大模型自身的推理能力来生成结构化的执行计划。

xxxxxxxxxx # From largemodel/utils/ai_agent.py ​ class AIAgent : # ... def _plan_task ( self , task_description : str ) -> Dict [ str , Any ]: """ Uses the large model for task planning and decomposition. / 使用大模型进行任务规划和分解。 """ # 动态生成可用工具列表及其描述 tool_descriptions = [] for name , adapter in self . tools_manager . tool_chain_manager . tools . items (): # ... (从adapter.input_schema获取工具描述) tool_descriptions . append ( f"- {name}({params}): {description}" ) available_tools_str = "\\\n" . join ( tool_descriptions ) ​ # 构建高度结构化的规划Prompt planning_prompt = f""" 作为一个专业的任务规划Agent,请将用户任务分解为一系列具体的、可执行的JSON步骤。 ​ **# 可用工具:** {available_tools_str} ​ **# 核心规则:** 1\. **数据传递**: 当后续步骤需要使用之前步骤的输出时,**必须**使用 `{{{{steps.N.outputs.KEY}}}}` 格式进行引用。 \- `N` 是步骤的ID(从1开始)。 \- `KEY` 是之前步骤输出数据中的具体字段名。 2\. **JSON格式**: 必须严格返回JSON对象。 ​ **# 用户任务:** {task_description} """ # 调用大模型进行规划 messages_to_use = [{ "role" : "user" , "content" : planning_prompt }] # 注意这里调用的是通用的文本推理接口 result = self . node . model_client . infer_with_text ( "" , message = messages_to_use ) # ... (解析JSON响应并返回步骤列表)

3. 参数处理与数据流实现 (largemodel/utils/ai_agent.py)

_process_step_parameters函数负责解析占位符,实现步骤间的数据流动。

xxxxxxxxxx # From largemodel/utils/ai_agent.py ​ class AIAgent : # ... def _process_step_parameters ( self , parameters : Dict [ str , Any ], previous_outputs : List [ Any ]) -> Dict [ str , Any ]: """ Parses parameter dictionary, finds and replaces all {{...}} references. """ processed_params = parameters . copy () # 正则表达式用于匹配 {{steps.N.outputs.KEY}} 格式的占位符 pattern = re . compile ( r"\\\\{\\\\{steps\\\\.(\\\d+)\\\\.outputs\\\\.(.+?)\\\\}\\\\}" ) ​ for key , value in processed_params . items (): if isinstance ( value , str ) and pattern . search ( value ): # 使用 re.sub 和一个替换函数来处理所有找到的占位符 # 替换函数会从 previous_outputs 列表中查找并返回值 processed_params [ key ] = pattern . sub ( replacer_function , value ) return processed_params

代码解析

AI Agent是系统的“中枢大脑”,它将用户提出的高级、有时甚至是模糊的任务,转化为一系列精确、有序的工具调用。其实现不依赖于任何特定的模型平台,而是建立在通用的、可扩展的架构之上。

  1. 动态任务规划 : Agent的核心能力在于_plan_task函数。它不依赖硬编码的逻辑,而是通过与大模型交互来动态生成任务计划。

    • 自我认知与Prompt构建 :在规划开始时,Agent首先检查当前所有可用的工具及其描述。然后,它将这些工具信息、用户任务、以及严格的规则(如数据传递格式)打包成一个高度结构化的planning_prompt
    • 模型即规划器 :这个Prompt被发送给一个通用的文本大模型。模型根据提供的上下文进行推理,返回一个JSON格式的、包含多个步骤的行动计划。这种设计极具扩展性:当系统中新增或修改工具时,Agent的规划能力会自动更新,无需修改Agent自身代码。
  2. 工具链与数据流 : 现实世界的任务常常需要多个工具协作,例如“拍照并描述”需要将“拍照”工具的输出(图片路径)作为“描述”工具的输入。AI Agent通过_process_step_parameters函数优雅地实现了这一点。

    • 数据引用占位符 :在规划阶段,大模型会在需要传递数据的参数值中,嵌入特殊的占位符,如{{steps.1.outputs.data}}
    • 实时参数替换 :在_execute_agent_workflow的主循环中,执行每个步骤之前,_process_step_parameters都会被调用。它使用正则表达式扫描当前步骤的所有参数,一旦发现占位符,就会从之前步骤的输出列表中找到对应的数据,并进行实时替换。这个机制是实现复杂任务自动化的关键。
  3. 监督执行与容错 : _execute_agent_workflow构成了Agent的执行主循环。它严格按照规划好的步骤顺序,依次执行每个动作,并确保数据在步骤之间正确传递。

    • 原子化步骤 :每个步骤都被视为一个独立的“原子操作”。如果任何一个步骤执行失败,整个任务链会立即中止并报告错误。这确保了系统的稳定性和可预测性,避免了在错误状态下继续执行。

总结来说,AI Agent的通用实现展示了一种先进的软件架构:它不直接解决问题,而是构建一个框架,让一个外部的、通用的推理引擎(大模型)来解决问题。通过“动态规划”和“数据流管理”这两个核心机制,Agent能够将一系列独立的工具编排成复杂的、能够完成高级任务的工作流。

3. 实践操作

3.1 配置离线大模型

3.1.1 配置LLM平台 (yahboom.yaml)

此文件决定了 model_service 节点加载哪个大模型平台作为其主要的语言模型。

  1. 在终端打开文件 :

xxxxxxxxxx vim ~/yahboom_ws/src/largemodel/config/yahboom.yaml 2. 修改/确认llm_platform:

xxxxxxxxxx model_service : #模型服务器节点参数 ros__parameters : language : 'zh' #大模型接口语言 useolinetts : True #文字模式下此项无效,可忽略 ​ # 大模型配置 llm_platform : 'ollama' # 关键: 确保这里是 'ollama' regional_setting : "China"

3.1.2 配置模型接口 (large_model_interface.yaml)

此文件定义了当平台被选为 ollama 时,具体使用哪个视觉模型。

1.在终端打开文件

xxxxxxxxxx vim ~/yahboom_ws/src/largemodel/config/large_model_interface.yaml

2.找到ollama相关的配置

xxxxxxxxxx #..... ## 离线大模型 (Offline Large Language Models) # Ollama配置 ollama_host: "http://localhost:11434" # Ollama服务器地址 ollama_model: "llava" # 关键: 将这里改为你已下载的多模态模型,如 "llava" #.....

注意 : 请确保配置参数中指定的模型(如llava)能够处理多模态输入。

3.2 启动并测试功能 (文本输入模式)

注:Jetson Orin Nano 4GB 由于性能限制,效果较差差。如需体验此功能,请参考 <在线大模型(文字交互)>章节

  1. 启动largemodel 主程序 (文字模式): 打开一个终端,然后运行下面的指令:

xxxxxxxxxx ros2 launch largemodel largemodel_control.launch.py text_chat_mode: = true 2. 发送文本指令 : 再次打开另一个终端,运行下面的指令,

xxxxxxxxxx ros2 run text_chat text_chat

然后开始输入文本:“根据当前的环境,并把生成的环境描述保存成txt文档”。

  1. 观察结果 :

在第一个运行主程序的终端中,你将看到日志输出,显示系统接收到文本指令,调用aiagent工具,然后提供prompt给LLM,LLM会分析详细的调用工具的步骤。比如现在这个提问,就会调用seewhat工具获取画面,然后将画面提供给LLM解析,解析出来的文本会保存在~/yahboom_ws/src/largemodel/resources_file/documents文件夹下。


4. 常见问题与解决方案

4.1 代理行为异常

问题1:代理陷入了无限循环,或者反复执行同一个工具。

解决方案 :

  1. 增强Prompt : 在ReAct Prompt中加入更强的限制,例如:“不要重复执行相同的操作”,“如果连续两次观察结果相同,请尝试不同的工具或结束任务”。
  2. 增加迭代限制 : 如代码示例中的max_turns,设置一个最大循环次数,防止无限循环。
  3. 更换更强大的LLM : 代理的逻辑能力很大程度上取决于后端LLM的“智商”。一个更强大的模型能更好地理解任务并做出规划。

4.2 工具调用失败

问题2:代理正确地规划了行动,但工具执行失败了。

解决方案 :

  1. 检查单个工具 :这通常不是代理逻辑的问题,而是被调用的那个工具(如seewhatvisual_positioning)本身有问题。请按照对应工具的教程去排查问题。
  2. 错误处理 : 在ToolChainManager的循环中,需要有完善的try...except机制来捕获工具执行的异常,并将错误信息作为“观察结果”反馈给LLM,让它知道上一步失败了,并重新规划。