OpenManus 是一个基于 LLM 的任务规划和执行框架,其核心功能之一是通过 LLM 智能选择并调用各种工具来完成复杂任务。本文详细解析 OpenManus 中 LLM 工具调用的完整机制。

一、工具定义与结构

1.1 工具基类

所有工具都继承自 BaseTool 基类,该类定义了工具的基本接口:

class BaseTool(ABC, BaseModel):
    name: str
    description: str
    parameters: Optional[dict] = None

    async def __call__(self, **kwargs) -> Any:
        """Execute the tool with given parameters."""
        return await self.execute(**kwargs)

    @abstractmethod
    async def execute(self, **kwargs) -> Any:
        """Execute the tool with given parameters."""

    def to_param(self) -> Dict:
        """Convert tool to function call format."""
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": self.parameters,
            },
        }

1.2 工具参数定义

每个工具都通过 parameters 字典定义其接受的参数,遵循 JSON Schema 格式:

parameters: dict = {
    "type": "object",
    "properties": {
        "command": {
            "description": "The command to execute...",
            "enum": ["create", "update", "list", "get", "set_active", "mark_step", "delete"],
            "type": "string",
        },
        # 其他参数...
    },
    "required": ["command"],
    "additionalProperties": False,
}

1.3 OpenManus 中的核心工具

OpenManus 定义了多种工具,包括:

  • PlanningTool:任务规划工具,用于创建和管理计划
  • BrowserUseTool:浏览器自动化工具,用于网页交互
  • WebSearch:网络搜索工具,支持多种搜索引擎
  • Bash/Terminal:命令行执行工具
  • FileSaver:文件保存工具
  • PythonExecute:Python 代码执行工具
  • 等等

二、工具调用流程

2.1 工具定义传递给 LLM

系统将工具定义传递给 LLM,这通常在 ask_tool 方法中完成:

response = await self.llm.ask_tool(
    messages=messages,
    system_msgs=[Message.system_message(self.system_prompt)],
    tools=self.available_tools.to_params(),  # 将所有可用工具传递给 LLM
    tool_choice=ToolChoice.AUTO,  # 让 LLM 自动选择合适的工具
)

to_params() 方法将工具转换为 OpenAI API 可接受的格式:

def to_params(self) -> List[Dict[str, Any]]:
    """Convert all tools to function call format."""
    return [tool.to_param() for tool in self.tools]

2.2 LLM 分析请求并选择工具

LLM 收到用户请求和工具定义后,执行以下步骤:

  1. 理解用户意图:分析用户请求,理解任务需求
  2. 匹配合适工具:从提供的工具列表中选择最合适的工具
  3. 构造参数:为选定的工具构造必要的参数
  4. 生成工具调用:生成包含工具名称和参数的工具调用

2.3 工具调用返回格式

LLM 生成的工具调用会在 response.tool_calls 中返回,格式如下:

[
    {
        "id": "call_abc123",
        "type": "function",
        "function": {
            "name": "planning",
            "arguments": "{\"command\":\"create\",\"plan_id\":\"plan_1234\",\"title\":\"比特币价值分析\",\"steps\":[\"收集市场数据\",\"分析历史趋势\",\"评估当前价值\"]}"
        }
    }
]

2.4 解析和执行工具调用

系统解析 LLM 返回的工具调用,并执行相应操作:

if response.tool_calls:
    for tool_call in response.tool_calls:
        if tool_call.function.name == "planning":
            # 解析参数
            args = json.loads(tool_call.function.arguments)
            # 执行工具
            await self.planning_tool.execute(**args)

在 PlanningAgent 中,系统使用 Message.from_tool_calls 将工具调用转换为消息格式:

assistant_msg = Message.from_tool_calls(
    content=response.content, 
    tool_calls=response.tool_calls
)
self.memory.add_message(assistant_msg)

三、工具执行机制

3.1 工具执行流程

工具执行通常包括以下步骤:

  1. 获取工具实例:根据工具名称获取对应的工具实例
  2. 解析参数:将 JSON 格式的参数解析为 Python 对象
  3. 执行工具:调用工具的 execute 方法,传入解析后的参数
  4. 处理结果:获取工具执行结果,并根据需要更新系统状态

3.2 执行结果处理

工具执行结果通常包装在 ToolResult 对象中:

class ToolResult(BaseModel):
    """Represents the result of a tool execution."""
    output: Any = Field(default=None)
    error: Optional[str] = Field(default=None)
    base64_image: Optional[str] = Field(default=None)
    system: Optional[str] = Field(default=None)

系统会根据执行结果更新状态,并可能将结果反馈给 LLM 进行下一步决策。

四、具体示例:创建计划

以创建比特币价值分析报告为例,完整流程如下:

4.1 用户请求

用户输入:"请做一份btc价值分析报告"

4.2 系统处理

# 1. 创建消息
messages = [Message.user_message("请做一份btc价值分析报告")]

# 2. 调用 LLM
response = await self.llm.ask(
    messages=messages,
    system_msgs=[Message.system_message(self.system_prompt)],
    tools=self.available_tools.to_params(),
    tool_choice=ToolChoice.AUTO,
)

4.3 LLM 响应

LLM 分析请求,选择 planning 工具,并生成工具调用:

{
  "id": "call_abc123",
  "type": "function",
  "function": {
    "name": "planning",
    "arguments": "{\"command\":\"create\",\"plan_id\":\"plan_1234\",\"title\":\"比特币价值分析报告\",\"steps\":[\"收集当前市场数据\",\"分析历史价格趋势\",\"评估基本面因素\",\"考虑宏观经济影响\",\"总结价值评估\"]}"
  }
}

4.4 系统解析

系统解析工具调用,获取参数:

args = {
  "command": "create",
  "plan_id": "plan_1234",
  "title": "比特币价值分析报告",
  "steps": ["收集当前市场数据", "分析历史价格趋势", "评估基本面因素", "考虑宏观经济影响", "总结价值评估"]
}

4.5 执行工具

系统调用 planning_tool.execute 方法,创建计划:

result = await planning_tool.execute(**args)

4.6 更新状态

系统更新计划状态,准备执行计划中的步骤。

五、多工具调用处理

LLM 可能会在一次响应中生成多个工具调用,系统会依次处理每个调用:

if response.tool_calls:
    for tool_call in response.tool_calls:
        tool_name = tool_call.function.name
        args = json.loads(tool_call.function.arguments)
        
        # 获取工具实例
        tool = self.available_tools.get_tool(tool_name)
        if tool:
            # 执行工具
            result = await tool.execute(**args)

六、工具调用的优势

基于工具调用的设计有以下优势:

  1. 结构化输出:LLM 生成的是结构化的工具调用,而不是自由文本,减少了解析错误
  2. 参数验证:工具定义中包含参数验证规则,确保 LLM 提供有效参数
  3. 功能扩展:可以轻松添加新工具,扩展系统功能
  4. 错误处理:工具执行失败时,可以返回详细的错误信息,帮助 LLM 调整策略

七、总结

OpenManus 通过精心设计的工具调用机制,实现了 LLM 与各种工具的无缝集成,使 LLM 能够执行复杂的任务规划和执行流程。这种设计不仅提高了系统的灵活性和可扩展性,还充分利用了 LLM 的智能决策能力,为自动化任务执行提供了强大的支持。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐