OpenManus源码解析:智能体框架的设计与实现

一、项目概述

1.1 OpenManus的定位与设计目标

OpenManus是一个基于大语言模型(LLM)的智能体框架,它的设计目标是创建一个灵活、可扩展且功能强大的系统,使AI能够通过各种工具与外部世界交互,从而解决复杂的任务。

与传统的聊天机器人不同,OpenManus不仅能够理解和生成文本,还能够执行具体的操作,如搜索信息、浏览网页、执行代码和保存文件等。这种能力使其成为一个真正的"智能助手",而不仅仅是一个对话系统。

OpenManus的核心理念是"思考-行动"循环,即智能体先分析当前状态和任务需求(思考),然后选择并执行适当的工具(行动),接着基于执行结果进行下一轮思考。这种循环使智能体能够逐步解决复杂问题,同时保持对任务的连贯理解。

1.2 项目结构概览

OpenManus的项目结构清晰而模块化,主要包括以下几个部分:

app/
├── agent/                # **智能体实现
│   ├── base.py           # **基础智能体
│   ├── react.py          # **思考-行动智能体
│   ├── toolcall.py       # **工具调用智能体
│   └── manus.py          # **Manus智能体
├── tool/                 # **工具实现
│   ├── base.py           # **基础工具
│   ├── bash.py           # **命令行工具
│   ├── browser_use_tool.py # **浏览器工具
│   ├── file_saver.py     # **文件保存工具
│   ├── python_execute.py # **Python执行工具
│   ├── terminate.py      # **终止工具
│   └── tool_collection.py # **工具集合
├── flow/                 # **流程控制
│   ├── base.py           # **基础流程
│   ├── planning.py       # **规划流程
│   └── flow_factory.py   # **流程工厂
├── prompt/               # **提示模板
│   └── manus.py          # **Manus提示
├── llm.py                # **LLM接口
├── memory.py             # **记忆系统
└── message.py            # **消息定义
main.py                   # **主入口

这种结构使得各个组件之间的职责划分清晰,便于维护和扩展。

1.3 核心组件介绍

1) 智能体系统

智能体系统是OpenManus的核心,它采用了层次化的设计:

  • BaseAgent:提供基本的状态管理和执行循环
  • ReActAgent:实现思考-行动循环模式
  • ToolCallAgent:实现工具调用机制
  • Manus:集成多种工具的具体智能体实现

这种层次化设计使得代码更加模块化和可扩展,每个层次只需关注自己的职责。

2) 工具系统

工具系统为智能体提供了与外部世界交互的能力:

  • BaseTool:所有工具的抽象基类
  • ToolCollection:工具的集合和管理器
  • 具体工具:如PythonExecute、GoogleSearch、BrowserUseTool等

每个工具都有明确的名称、描述和参数规范,使LLM能够正确选择和使用它们。

3) 记忆系统

记忆系统使智能体能够在多个步骤中保持上下文连贯性:

  • Memory:存储交互历史的容器
  • Message:表示不同类型消息的结构

记忆系统记录了用户输入、LLM响应和工具执行结果,使智能体能够基于历史信息做出决策。

4) LLM接口

LLM接口负责与大语言模型(如OpenAI的GPT模型)通信:

  • LLM:封装了与LLM API的交互
  • ToolResponse:表示LLM响应的结构

LLM接口将智能体的记忆和工具信息传递给LLM,并解析LLM的响应。

5) 流程控制

流程控制组件管理不同类型的执行流程:

  • BaseFlow:所有流程的抽象基类
  • PlanningFlow:实现规划和执行的流程
  • FlowFactory:创建不同类型流程的工厂

流程控制使OpenManus能够支持不同的执行模式,如规划式执行。

1.4 技术特点

OpenManus具有几个显著的技术特点:

  1. 异步编程:广泛使用async/await进行异步操作,提高I/O效率
  2. 模块化设计:清晰的组件划分和接口定义,便于维护和扩展
  3. 错误处理:多层次的错误捕获和恢复机制,提高系统稳定性
  4. 工具抽象:统一的工具接口,便于添加新工具
  5. 记忆管理:完善的记忆系统,支持上下文连贯的多步骤任务

这些特点使OpenManus成为一个强大而灵活的智能体框架,能够应对各种复杂任务。

通过这个项目,可以看到AI智能体如何从简单的对话系统演变为能够执行具体操作的助手,这代表了AI应用的一个重要发展方向。在接下来的章节中,下文将深入探讨OpenManus的各个组件和机制,揭示其内部工作原理。


二、智能体的层次化设计

2.1 BaseAgent:基础智能体的实现

BaseAgent是所有智能体的基类,位于app/agent/base.py中。它提供了智能体的基本功能:

核心属性
  • name:智能体的名称
  • description:智能体的描述
  • system_prompt:系统级指令提示
  • next_step_prompt:决定下一步行动的提示
  • llm:语言模型实例
  • memory:智能体的记忆存储
  • state:当前智能体状态(IDLE、RUNNING、FINISHED、ERROR)
主要方法
  • run(request):执行智能体的主循环
  • step():执行单个步骤(抽象方法,需要子类实现)
  • update_memory():更新智能体的记忆
  • is_stuck():检测智能体是否陷入循环
  • handle_stuck_state():处理卡住状态

BaseAgent的run方法是智能体执行的核心,它实现了一个循环,在循环中不断调用step方法,直到任务完成或达到最大步骤数:

async def run(self, request: Optional[str] = None) -> str:
    if request:
        self.update_memory("user", request)

    results: List[str] = []
    async with self.state_context(AgentState.RUNNING):
        while (
            self.current_step < self.max_steps and self.state != AgentState.FINISHED
        ):
            self.current_step += 1
            step_result = await self.step()
            
            # **检查是否陷入循环
            if self.is_stuck():
                self.handle_stuck_state()
                
            results.append(f"Step {self.current_step}: {step_result}")

2.2 ReActAgent:思考-行动循环模式

ReActAgent继承自BaseAgent,位于app/agent/react.py中。它实现了思考-行动循环模式,这是一种强大的智能体决策框架。

核心方法
  • think():处理当前状态并决定下一步行动(抽象方法)
  • act():执行决定的行动(抽象方法)
  • step():执行单个步骤(实现了BaseAgent的抽象方法)

ReActAgent的step方法实现了思考-行动循环:

async def step(self) -> str:
    """执行单个步骤:思考和行动。"""
    should_act = await self.think()  # **先思考
    if not should_act:
        return "思考完成 - 无需行动"
    return await self.act()  # **再行动

这种思考-行动模式非常适合智能体的决策过程,它模拟了人类的思考方式:先分析情况,再采取行动。

2.3 ToolCallAgent:工具调用机制

ToolCallAgent继承自ReActAgent,位于app/agent/toolcall.py中。它实现了工具调用机制,使智能体能够使用各种工具来完成任务。

核心属性
  • available_tools:可用工具集合
  • tool_choices:工具选择模式(“none”、“auto”、“required”)
  • special_tool_names:特殊工具名称列表
  • tool_calls:工具调用列表
主要方法
  • think():实现了ReActAgent的抽象方法,使用LLM决定使用哪些工具
  • act():实现了ReActAgent的抽象方法,执行工具调用
  • execute_tool(command):执行单个工具调用
  • _handle_special_tool(name, result):处理特殊工具执行和状态变化

ToolCallAgent的think方法使用LLM来决定使用哪些工具:

async def think(self) -> bool:
    if self.next_step_prompt:
        user_msg = Message.user_message(self.next_step_prompt)
        self.messages += [user_msg]

    # **获取带工具选项的响应
    response = await self.llm.ask_tool(
        messages=self.messages,
        system_msgs=[Message.system_message(self.system_prompt)]
        if self.system_prompt
        else None,
        tools=self.available_tools.to_params(),
        tool_choice=self.tool_choices,
    )
    self.tool_calls = response.tool_calls
    
    # **记录响应信息
    logger.info(f"✨ {self.name}'s thoughts: {response.content}")
    logger.info(
        f"🛠️ {self.name} selected {len(response.tool_calls) if response.tool_calls else 0} tools to use"
    )

ToolCallAgent的act方法执行工具调用并处理结果:

async def act(self) -> str:
    if not self.tool_calls:
        if self.tool_choices == "required":
            raise ValueError(TOOL_CALL_REQUIRED)
        return self.messages[-1].content or "No content or commands to execute"

    results = []
    for command in self.tool_calls:
        result = await self.execute_tool(command)
        logger.info(
            f"🎯 Tool '{command.function.name}' completed its mission! Result: {result}"
        )

        # **将工具响应添加到记忆中
        tool_msg = Message.tool_message(
            content=result, tool_call_id=command.id, name=command.function.name
        )
        self.memory.add_message(tool_msg)
        results.append(result)

    return "\n\n".join(results)

2.4 Manus:最终智能体的集成

Manus继承自ToolCallAgent,位于app/agent/manus.py中。它是用户直接交互的主要智能体,集成了多种工具。

核心属性
  • name:“Manus”
  • description:“一个可以使用多种工具解决各种任务的多功能智能体”
  • system_prompt:来自app/prompt/manus.py的系统提示
  • next_step_prompt:来自app/prompt/manus.py的下一步提示
  • available_tools:包含PythonExecute、GoogleSearch、BrowserUseTool、FileSaver和Terminate的工具集合
  • max_steps:20(最大步骤数)
class Manus(ToolCallAgent):
    name: str = "Manus"
    description: str = (
        "A versatile agent that can solve various tasks using multiple tools"
    )

    system_prompt: str = SYSTEM_PROMPT
    next_step_prompt: str = NEXT_STEP_PROMPT

    # **添加通用工具到工具集合
    available_tools: ToolCollection = Field(
        default_factory=lambda: ToolCollection(
            PythonExecute(), GoogleSearch(), BrowserUseTool(), FileSaver(), Terminate()
        )
    )

    max_steps: int = 20

2.5 总结

层次结构的优势
  1. 代码重用:每个层次只需实现自己特有的功能,其他功能可以从父类继承。
  2. 关注点分离
    • BaseAgent处理基本的状态管理和执行循环
    • ReActAgent实现思考-行动模式
    • ToolCallAgent处理工具调用
    • Manus集成特定工具和提示
  3. 灵活性和可扩展性:可以通过继承现有智能体类来创建新的智能体类型,而不需要修改现有代码。
  4. 维护性:每个层次的代码都相对简单和专注,使得代码更容易理解和维护。
  5. 测试性:可以独立测试每个层次的功能,简化测试过程。
智能体执行流程

当用户输入一个请求时,执行流程如下:

  1. 请求被传递给Manus智能体的run方法(继承自BaseAgent)
  2. run方法进入一个循环,重复调用step方法(继承自ReActAgent)
  3. step方法首先调用think方法(ToolCallAgent实现)来决定使用哪些工具
  4. 然后调用act方法(ToolCallAgent实现)来执行工具调用
  5. 工具执行结果被添加到记忆中,用于后续决策
  6. 重复这个过程,直到任务完成或达到最大步骤数

这种层次化设计使得OpenManus项目能够以一种模块化、可维护的方式实现复杂的智能体行为。


三、工具系统的实现

OpenManus中的工具是经过精心设计的软件组件,它们主要是规范了输入输出的功能模块,而不是完全智能化的应用。下文来详细解析这些工具的实现机制。

3.1 BaseTool:工具的基础抽象

所有工具都继承自BaseTool抽象基类,这个基类定义了工具的基本结构:

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

    async def __call__(self, **kwargs) -> Any:
        """执行工具"""
        return await self.execute(**kwargs)

    @abstractmethod
    async def execute(self, **kwargs) -> Any:
        """执行工具的具体逻辑"""
        pass

    def to_param(self) -> Dict:
        """转换为函数调用格式"""
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": self.parameters,
            },
        }

这个架构确保了所有工具都有统一的接口,方便智能体调用和管理。

3.2 主要工具详解

PythonExecute:代码执行工具

PythonExecute工具允许执行Python代码:

class PythonExecute(BaseTool):
    name: str = "python_execute"
    description: str = "执行Python代码并返回结果"
    parameters: dict = {
        "type": "object",
        "properties": {
            "code": {
                "type": "string",
                "description": "要执行的Python代码"
            },
            "timeout": {
                "type": "integer",
                "description": "执行超时时间(秒)"
            }
        },
        "required": ["code"]
    }

    async def execute(self, code: str, timeout: int = 5) -> Dict:
        """执行Python代码"""
        try:
            # **创建一个安全的执行环境
            locals_dict = {}
            
            # **使用asyncio.wait_for实现超时控制
            await asyncio.wait_for(
                self._execute_code(code, locals_dict),
                timeout=timeout
            )
            
            # **提取执行结果
            result = locals_dict.get("result", None)
            return {"result": result}
        except asyncio.TimeoutError:
            return {"error": f"代码执行超时({timeout}秒)"}
        except Exception as e:
            return {"error": str(e)}
    
    async def _execute_code(self, code: str, locals_dict: Dict):
        """在隔离环境中执行代码"""
        # **添加一些安全限制
        restricted_globals = {
            "__builtins__": {
                name: getattr(__builtins__, name)
                for name in ["print", "range", "len", "dict", "list", "set", "int", "float", "str"]
            }
        }
        
        # **执行代码
        exec(code, restricted_globals, locals_dict)

这个工具主要是一个代码执行器,它创建了一个受限的执行环境,并使用Python的exec函数来执行代码。它不是智能化的应用,而是一个规范了输入输出的功能模块。

GoogleSearch:搜索工具

GoogleSearch工具允许执行网络搜索:

class GoogleSearch(BaseTool):
    name: str = "google_search"
    description: str = "使用Google搜索信息"
    parameters: dict = {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "搜索查询"
            },
            "num_results": {
                "type": "integer",
                "description": "返回结果数量"
            }
        },
        "required": ["query"]
    }
    
    search_client: GoogleSearchClient = Field(default_factory=GoogleSearchClient)

    async def execute(self, query: str, num_results: int = 10) -> List[str]:
        """执行Google搜索"""
        try:
            # **调用搜索客户端
            search_results = await self.search_client.search(query, num_results=num_results)
            
            # **格式化结果
            formatted_results = []
            for i, result in enumerate(search_results, 1):
                title = result.get("title", "无标题")
                link = result.get("link", "无链接")
                snippet = result.get("snippet", "无摘要")
                formatted_results.append(f"{i}. {title}\n   URL: {link}\n   {snippet}\n")
            
            return "\n".join(formatted_results)
        except Exception as e:
            return f"搜索出错: {str(e)}"

这个工具是一个搜索接口封装,它调用GoogleSearchClient来执行实际的搜索操作。GoogleSearchClient可能是一个API客户端,用于调用Google搜索API或其他搜索服务。

BrowserUseTool:浏览器控制工具

BrowserUseTool工具允许控制浏览器:

class BrowserUseTool(BaseTool):
    name: str = "browser_use"
    description: str = "控制浏览器执行各种操作"
    parameters: dict = {
        "type": "object",
        "properties": {
            "action": {
                "type": "string",
                "enum": [
                    "navigate", "click", "input_text", "screenshot",
                    "get_html", "get_text", "execute_js", "scroll",
                    "switch_tab", "new_tab", "close_tab", "refresh"
                ],
                "description": "浏览器操作类型"
            },
            "url": {
                "type": "string",
                "description": "用于'navigate'或'new_tab'操作的URL"
            },
            # **其他参数...
        },
        "required": ["action"]
    }
    
    browser: Optional[BrowserUseBrowser] = Field(default=None, exclude=True)
    
    async def execute(self, action: str, url: Optional[str] = None, ...) -> ToolResult:
        """执行浏览器操作"""
        async with self.lock:
            try:
                # **确保浏览器已初始化
                context = await self._ensure_browser_initialized()
                
                # **根据操作类型执行不同的浏览器操作
                if action == "navigate":
                    if not url:
                        return ToolResult(error="URL is required for 'navigate' action")
                    await context.navigate(url)
                    return ToolResult(output=f"Navigated to {url}")
                
                elif action == "click":
                    # **点击操作实现...
                
                # **其他操作实现...
                
            except Exception as e:
                return ToolResult(error=f"Browser action '{action}' failed: {str(e)}")

这个工具是一个浏览器自动化接口,它使用BrowserUseBrowser类(可能基于Playwright或Selenium)来控制浏览器。它提供了一组标准化的操作(如导航、点击、输入文本等),但本身并不包含智能化的逻辑。

FileSaver:文件保存工具

FileSaver工具允许保存内容到文件:

class FileSaver(BaseTool):
    name: str = "file_saver"
    description: str = "保存内容到本地文件"
    parameters: dict = {
        "type": "object",
        "properties": {
            "content": {
                "type": "string",
                "description": "要保存的内容"
            },
            "file_path": {
                "type": "string",
                "description": "文件保存路径"
            },
            "mode": {
                "type": "string",
                "description": "文件打开模式",
                "enum": ["w", "a"],
                "default": "w"
            }
        },
        "required": ["content", "file_path"]
    }

    async def execute(self, content: str, file_path: str, mode: str = "w") -> str:
        """保存内容到文件"""
        try:
            # **确保目录存在
            directory = os.path.dirname(file_path)
            if directory and not os.path.exists(directory):
                os.makedirs(directory)

            # **写入文件
            async with aiofiles.open(file_path, mode, encoding="utf-8") as file:
                await file.write(content)

            return f"内容已成功保存到 {file_path}"
        except Exception as e:
            return f"保存文件出错: {str(e)}"

这个工具是一个简单的文件操作接口,它使用aiofiles库(异步文件I/O)来保存内容到文件。它是一个非常基础的功能模块,没有智能化的逻辑。

Terminate:终止工具

Terminate工具用于结束智能体的执行:

class Terminate(BaseTool):
    name: str = "terminate"
    description: str = "当任务完成或无法继续时终止交互"
    parameters: dict = {
        "type": "object",
        "properties": {
            "status": {
                "type": "string",
                "description": "交互的完成状态",
                "enum": ["success", "failure"]
            }
        },
        "required": ["status"]
    }

    async def execute(self, status: str) -> str:
        """终止当前执行"""
        return f"交互已完成,状态: {status}"

这个工具非常简单,它只是返回一个状态消息。实际的终止逻辑是在ToolCallAgent_handle_special_tool方法中实现的,它会将智能体的状态设置为FINISHED

3.3 工具与智能体的协作模式

在OpenManus框架中,工具与智能体之间形成了一种独特而高效的协作模式,这种模式可以概括为"智能体决策,工具执行"。这种协作充分发挥了大语言模型的推理能力和专用工具的执行能力,形成了一个强大的组合。

决策与执行的分离

OpenManus中最核心的协作理念是将决策与执行明确分离:

  • 智能体负责决策:利用LLM的强大理解和推理能力,分析任务需求,选择合适的工具,确定工具参数,解释工具执行结果。
  • 工具负责执行:接收智能体提供的参数,执行特定功能,返回执行结果,不参与决策过程。
    这种分离使系统既有LLM的灵活性和创造性,又有专用工具的可靠性和确定性。
协作流程

工具与智能体的协作流程可以分为以下几个步骤:

  1. 任务分析:智能体分析用户请求,理解任务需求。
  2. 工具选择:智能体从可用工具列表中选择合适的工具。
  3. 参数准备:智能体确定工具所需的参数。
  4. 工具调用:智能体通过工具调用机制执行工具。
  5. 结果处理:智能体接收工具执行结果,进行分析和解释。
  6. 下一步决策:智能体根据结果决定下一步行动。
接口标准化

为了使协作顺畅,OpenManus对工具接口进行了标准化:

  • 统一的工具描述:每个工具都有名称、描述和参数规范,使LLM能够理解工具的功能和用法。
  • 统一的调用方式:所有工具都通过execute方法调用,参数通过关键字参数传递。
  • 统一的结果格式:工具执行结果统一为字符串或特定的结果对象,便于智能体处理。

这种标准化使得添加新工具变得简单,只需实现标准接口即可。

错误处理与恢复

协作模式中的一个重要方面是错误处理与恢复:

  • 工具执行错误:工具捕获执行过程中的异常,返回错误信息而不是抛出异常。
  • 智能体处理错误:智能体接收错误信息,分析原因,尝试其他方法或工具。
  • 多次尝试:如果一种方法失败,智能体可以尝试其他方法,直到任务完成或达到最大尝试次数。

这种错误处理机制使得系统在面对各种异常情况时仍能保持稳定运行。


四、工具选择与使用的机制

4.1 工具选择的实现

在OpenManus中,工具选择主要在ToolCallAgent类的think方法中实现。

工具选择的核心代码

ToolCallAgentthink方法是工具选择的核心:

async def think(self) -> bool:
    # **添加下一步提示到消息列表
    if self.next_step_prompt:
        user_msg = Message.user_message(self.next_step_prompt)
        self.messages += [user_msg]

    # **调用LLM获取带工具选择的响应
    response = await self.llm.ask_tool(
        messages=self.messages,
        system_msgs=[Message.system_message(self.system_prompt)]
        if self.system_prompt
        else None,
        tools=self.available_tools.to_params(),
        tool_choice=self.tool_choices,
    )
    
    # **保存工具调用信息
    self.tool_calls = response.tool_calls
    
    # **记录思考过程
    logger.info(f"✨ {self.name}'s thoughts: {response.content}")
    logger.info(
        f"🛠️ {self.name} selected {len(response.tool_calls) if response.tool_calls else 0} tools to use"
    )
    
    # **将LLM响应添加到记忆中
    self.memory.add_message(Message.assistant_message(response.content, response.tool_calls))
    
    # **如果有工具调用,返回True表示需要执行行动
    return bool(self.tool_calls)
工具选择的关键步骤
  • 1) 准备工具列表

首先,智能体需要准备可用工具的列表。这在Manus类中通过available_tools属性设置:

available_tools: ToolCollection = Field(
    default_factory=lambda: ToolCollection(
        PythonExecute(), GoogleSearch(), BrowserUseTool(), FileSaver(), Terminate()
    )
)

每个工具都有一个to_param方法,将工具转换为LLM可以理解的函数调用格式:

def to_param(self) -> Dict:
    """Convert tool to function call format."""
    return {
        "type": "function",
        "function": {
            "name": self.name,
            "description": self.description,
            "parameters": self.parameters,
        },
    }
  • 2) 调用LLM进行工具选择
    关键步骤是调用llm.ask_tool方法,这个方法会将所有可用工具的信息传递给LLM,让LLM决定使用哪些工具:
response = await self.llm.ask_tool(
    messages=self.messages,
    system_msgs=[Message.system_message(self.system_prompt)],
    tools=self.available_tools.to_params(),
    tool_choice=self.tool_choices,
)
  • 3) LLM如何选择工具
    LLM类的ask_tool方法中,会构建一个请求发送给OpenAI API:
async def ask_tool(
    self,
    messages: List[Message],
    system_msgs: Optional[List[Message]] = None,
    tools: Optional[List[Dict]] = None,
    tool_choice: Optional[str] = None,
) -> ToolResponse:
    """Ask the LLM with tool calling capability."""
    
    # **构建请求参数
    params = {
        "model": self.model,
        "messages": self._prepare_messages(messages, system_msgs),
    }
    
    # **添加工具信息
    if tools:
        params["tools"] = tools
        
    # **设置工具选择模式
    if tool_choice:
        if tool_choice == "auto":
            params["tool_choice"] = "auto"
        elif tool_choice == "required":
            params["tool_choice"] = {"type": "function"}
    
    # **发送请求给OpenAI API
    response = await self.client.chat.completions.create(**params)
    
    # **解析响应
    return self._parse_tool_response(response)

LLM会根据当前任务和上下文,选择最合适的工具来完成任务。这个选择过程是由LLM的模型能力决定的,它会分析任务需求并选择合适的工具。

工具选择的具体例子

假设用户输入:“帮我搜索关于Python异步编程的信息”

  1. 这个请求被传递给Manus智能体
  2. Manus调用think方法,将请求和可用工具信息传递给LLM
  3. LLM分析请求,发现需要搜索信息,因此选择GoogleSearch工具
  4. LLM返回一个包含工具调用信息的响应:
{
  "content": "我将使用Google搜索来查找关于Python异步编程的信息。",
  "tool_calls": [
    {
      "id": "call_123",
      "function": {
        "name": "google_search",
        "arguments": {
          "query": "Python异步编程 async await tutorial",
          "num_results": 5
        }
      }
    }
  ]
}
  1. think方法将这个工具调用信息保存在self.tool_calls中,并返回True表示需要执行行动

4.2 工具使用的实现

一旦工具被选择,下一步就是使用工具。这主要在ToolCallAgent类的act方法中实现。

工具使用的核心代码
async def act(self) -> str:
    # **如果没有工具调用,直接返回
    if not self.tool_calls:
        if self.tool_choices == "required":
            raise ValueError(TOOL_CALL_REQUIRED)
        return self.messages[-1].content or "No content or commands to execute"

    # **执行每个工具调用
    results = []
    for command in self.tool_calls:
        # **执行工具
        result = await self.execute_tool(command)
        logger.info(
            f"🎯 Tool '{command.function.name}' completed its mission! Result: {result}"
        )

        # **将工具响应添加到记忆中
        tool_msg = Message.tool_message(
            content=result, tool_call_id=command.id, name=command.function.name
        )
        self.memory.add_message(tool_msg)
        results.append(result)

    # **返回所有工具执行结果
    return "\n\n".join(results)
工具使用的关键步骤
  • 1) 执行工具调用

execute_tool方法负责执行单个工具调用:

async def execute_tool(self, command: ToolCall) -> str:
    """Execute a single tool call."""
    name = command.function.name
    args = command.function.arguments
    
    # **处理特殊工具
    if name in self.special_tool_names:
        return await self._handle_special_tool(name, args)
    
    # **执行普通工具
    try:
        result = await self.available_tools.execute(name=name, tool_input=args)
        return result
    except Exception as e:
        error_msg = f"Error executing tool {name}: {str(e)}"
        logger.error(error_msg)
        return error_msg
  • 2) 工具集合的执行
    ToolCollection类的execute方法负责找到并执行指定的工具:
async def execute(self, *, name: str, tool_input: Dict[str, Any] = None) -> ToolResult:
    """Execute a tool by name with given input."""
    # **查找工具
    tool = self.tool_map.get(name)
    if not tool:
        return ToolFailure(error=f"Tool {name} is invalid")
    
    # **执行工具
    try:
        result = await tool(**tool_input)
        return result
    except ToolError as e:
        return ToolFailure(error=e.message)
  • 3) 具体工具的执行
    每个工具都有一个execute方法,实现具体的功能。以GoogleSearch工具为例:
async def execute(self, query: str, num_results: int = 10) -> List[str]:
    """Execute a Google search with the given query."""
    try:
        # **调用Google搜索API
        search_results = await self.search_client.search(query, num_results=num_results)
        
        # **格式化结果
        formatted_results = []
        for i, result in enumerate(search_results, 1):
            title = result.get("title", "No title")
            link = result.get("link", "No link")
            snippet = result.get("snippet", "No snippet")
            formatted_results.append(f"{i}. {title}\n   URL: {link}\n   {snippet}\n")
        
        return "\n".join(formatted_results)
    except Exception as e:
        return f"Error performing Google search: {str(e)}"
工具使用的具体例子

继续上面的例子,用户要求搜索Python异步编程的信息:

  1. think方法已经选择了GoogleSearch工具,并保存了工具调用信息
  2. 接下来,act方法被调用,它会遍历self.tool_calls中的每个工具调用
  3. 对于GoogleSearch工具调用,execute_tool方法被调用:
result = await self.execute_tool(command)  # **command是GoogleSearch工具调用
  1. execute_tool方法找到GoogleSearch工具并执行:
result = await self.available_tools.execute(
    name="google_search", 
    tool_input={"query": "Python异步编程 async await tutorial", "num_results": 5}
)
  1. ToolCollection.execute方法找到GoogleSearch工具实例并调用它的execute方法:
result = await google_search_tool.execute(
    query="Python异步编程 async await tutorial", 
    num_results=5
)
  1. GoogleSearch.execute方法执行实际的搜索操作,并返回格式化的搜索结果:
1. Python异步编程入门:理解async和await - 掘金
   URL: https://juejin.cn/post/6844904088201584654
   这篇文章详细介绍了Python中的异步编程概念,包括async/await语法和使用方法...

2. Python异步编程指南 - 廖雪峰的官方网站
   URL: https://www.liaoxuefeng.com/wiki/1016959663602400/1048430311929344
   本教程介绍了Python中的协程和异步IO,以及如何使用async/await关键字...

...
  1. 这个结果被添加到智能体的记忆中,并返回给用户
特殊工具的处理

OpenManus还有一些特殊工具,如Terminate工具,它们需要特殊处理:

async def _handle_special_tool(self, name: str, args: Dict[str, Any]) -> str:
    """Handle special tools that affect agent state."""
    if name == "terminate":
        status = args.get("status", "unknown")
        self.state = AgentState.FINISHED
        return f"Agent terminated with status: {status}"
    
    # **其他特殊工具的处理...
    
    return f"Special tool {name} executed with args: {args}"

Terminate工具被调用时,智能体的状态会被设置为FINISHED,这会导致run方法中的循环终止,从而结束智能体的执行。


五、信息的保存、传递与使用

在OpenManus中,信息在智能体的多次迭代中通过精心设计的记忆系统进行保存、传递和使用。这个系统确保了智能体能够在多个步骤中保持上下文连贯性,并基于历史信息做出决策。下文将详细解析这个过程。

5.1 信息的保存机制

记忆系统的实现

OpenManus使用Memory类来保存信息。这个类在app/memory.py中定义:

class Memory:
    """存储智能体交互历史的记忆系统"""
    
    def __init__(self):
        self.messages: List[Message] = []
    
    def add_message(self, message: Message):
        """添加消息到记忆中"""
        self.messages.append(message)
    
    def get_messages(self) -> List[Message]:
        """获取所有记忆中的消息"""
        return self.messages
    
    def clear(self):
        """清空记忆"""
        self.messages = []

Memory类非常简单,它主要是一个消息列表的包装器,提供了添加、获取和清空消息的方法。

消息的结构

每个消息都是Message类的实例,这个类定义了消息的结构:

class Message:
    """表示一条消息的类"""
    
    def __init__(
        self,
        role: str,
        content: Optional[str] = None,
        tool_calls: Optional[List[ToolCall]] = None,
        tool_call_id: Optional[str] = None,
        name: Optional[str] = None,
    ):
        self.role = role
        self.content = content
        self.tool_calls = tool_calls
        self.tool_call_id = tool_call_id
        self.name = name
    
    @classmethod
    def system_message(cls, content: str) -> "Message":
        """创建系统消息"""
        return cls(role="system", content=content)
    
    @classmethod
    def user_message(cls, content: str) -> "Message":
        """创建用户消息"""
        return cls(role="user", content=content)
    
    @classmethod
    def assistant_message(
        cls, content: str, tool_calls: Optional[List[ToolCall]] = None
    ) -> "Message":
        """创建助手消息"""
        return cls(role="assistant", content=content, tool_calls=tool_calls)
    
    @classmethod
    def tool_message(
        cls, content: str, tool_call_id: str, name: str
    ) -> "Message":
        """创建工具消息"""
        return cls(
            role="tool", content=content, tool_call_id=tool_call_id, name=name
        )

每个消息都有一个角色(系统、用户、助手或工具)和内容。助手消息可能还包含工具调用信息,工具消息包含工具调用ID和工具名称。

信息保存的时机

信息在多个地方被保存到记忆中:

  • 用户输入保存

当用户提供输入时,它会被保存到记忆中:

# **BaseAgent.run方法
if request:
    self.update_memory("user", request)

# **BaseAgent.update_memory方法
def update_memory(self, role: str, content: str):
    """更新记忆"""
    if role == "user":
        self.memory.add_message(Message.user_message(content))
    elif role == "system":
        self.memory.add_message(Message.system_message(content))
    elif role == "assistant":
        self.memory.add_message(Message.assistant_message(content))
  • LLM响应保存

当LLM生成响应时,它会被保存到记忆中:

# **ToolCallAgent.think方法
self.memory.add_message(Message.assistant_message(response.content, response.tool_calls))
  • 工具执行结果保存

当工具执行完成时,结果会被保存到记忆中:

# **ToolCallAgent.act方法
tool_msg = Message.tool_message(
    content=result, tool_call_id=command.id, name=command.function.name
)
self.memory.add_message(tool_msg)

5.2 信息的传递机制

消息传递给LLM

在每次调用LLM时,记忆中的消息会被传递给LLM,使其能够了解历史上下文:

# **ToolCallAgent.think方法
response = await self.llm.ask_tool(
    messages=self.messages,  # **传递记忆中的所有消息
    system_msgs=[Message.system_message(self.system_prompt)]
    if self.system_prompt
    else None,
    tools=self.available_tools.to_params(),
    tool_choice=self.tool_choices,
)

这里的self.messages就是记忆中的消息列表。

消息格式转换

在传递给LLM之前,消息需要转换为LLM能够理解的格式:

# **LLM._prepare_messages方法
def _prepare_messages(
    self, messages: List[Message], system_msgs: Optional[List[Message]] = None
) -> List[Dict]:
    """准备发送给LLM的消息"""
    prepared_messages = []
    
    # **添加系统消息
    if system_msgs:
        for msg in system_msgs:
            prepared_messages.append({"role": msg.role, "content": msg.content})
    
    # **添加历史消息
    for msg in messages:
        message_dict = {"role": msg.role, "content": msg.content}
        
        # **添加工具调用信息
        if msg.role == "assistant" and msg.tool_calls:
            message_dict["tool_calls"] = [
                {
                    "id": tc.id,
                    "type": "function",
                    "function": {
                        "name": tc.function.name,
                        "arguments": json.dumps(tc.function.arguments),
                    },
                }
                for tc in msg.tool_calls
            ]
        
        # **添加工具响应信息
        if msg.role == "tool":
            message_dict["tool_call_id"] = msg.tool_call_id
            message_dict["name"] = msg.name
        
        prepared_messages.append(message_dict)
    
    return prepared_messages

这个方法将Message对象转换为包含适当字段的字典,这些字典符合OpenAI API的消息格式要求。

5.3 信息的使用机制

LLM使用历史信息

LLM会使用传递给它的所有历史消息来生成响应。这使得它能够:

  • 理解当前任务的上下文
  • 记住之前的交互
  • 基于之前的工具执行结果做出决策
  • 避免重复之前的错误

例如,如果用户要求搜索信息,智能体使用GoogleSearch工具获取结果,然后用户要求总结这些结果,LLM会使用记忆中的搜索结果来生成总结,而不需要重新搜索。

记忆长度管理

由于LLM的上下文窗口有限,记忆中的消息数量可能需要限制。OpenManus可能使用以下策略来管理记忆长度:

  • 保留最新的N条消息
  • 保留重要的消息(如系统提示、用户请求)
  • 压缩或摘要化旧消息

虽然代码中没有明确实现这些策略,但这是处理长期交互的常见做法。

记忆持久化

当前的实现中,记忆只存在于内存中,当智能体实例被销毁时记忆也会丢失。为了支持长期记忆,可能需要将记忆持久化到数据库或文件中。

具体例子:多步骤任务执行

下文将通过一个具体例子来说明信息如何在多次迭代中流动:

假设用户要求:“帮我找到关于Python异步编程的信息,然后创建一个简单的异步程序”

步骤1:理解请求

  1. 用户请求被添加到记忆中:Message.user_message("帮我找到关于Python异步编程的信息,然后创建一个简单的异步程序")
  2. LLM接收这个消息,并决定首先需要搜索信息
  3. LLM的响应被添加到记忆中:Message.assistant_message("我将搜索Python异步编程的信息", [ToolCall(google_search)])

步骤2:执行搜索

  1. GoogleSearch工具被执行,搜索"Python异步编程"
  2. 搜索结果被添加到记忆中:Message.tool_message(搜索结果, tool_call_id, "google_search")
  3. LLM接收所有历史消息,包括搜索结果
  4. LLM分析搜索结果,决定创建一个异步程序
  5. LLM的响应被添加到记忆中:Message.assistant_message("根据搜索结果,我将创建一个简单的异步程序", [ToolCall(python_execute)])

步骤3:创建程序

  1. PythonExecute工具被执行,创建并运行一个异步程序
  2. 执行结果被添加到记忆中:Message.tool_message(执行结果, tool_call_id, "python_execute")
  3. LLM接收所有历史消息,包括程序执行结果
  4. LLM生成最终响应,解释程序并总结任务完成情况
  5. LLM的响应被添加到记忆中:Message.assistant_message("我已经创建了一个简单的异步程序...", None)

在这个过程中,信息不断被添加到记忆中,并在每次LLM调用时传递给LLM,使其能够基于完整的历史上下文做出决策。

5.4 总结

OpenManus中的信息通过记忆系统在多次迭代中流动。用户输入、LLM响应和工具执行结果都被保存到记忆中,并在每次LLM调用时传递给LLM。这使得智能体能够保持上下文连贯性,累积知识,分解任务,并从错误中恢复。

这种信息保存、传递和使用机制有几个重要优势:

  • 上下文连贯性 :智能体能够保持对话的连贯性,理解之前的交互并基于它们做出决策。
  • 累积知识:智能体可以累积知识,将之前步骤获取的信息用于后续步骤。
  • 任务分解:复杂任务可以分解为多个步骤,每个步骤都基于之前步骤的结果。
  • 错误恢复:如果某个步骤失败,智能体可以看到错误信息并尝试不同的方法。

这种设计使得OpenManus能够处理复杂的多步骤任务,每个步骤都能利用之前步骤获取的信息,创造出超越单个步骤能力的解决方案。


六、错误处理与容错机制

在OpenManus中,确保LLM给出正确响应以及处理错误响应是一个重要的环节

6.1 确保LLM给出正确响应的机制

系统提示和指令

OpenManus通过精心设计的系统提示来引导LLM生成正确的响应。在Manus类中,系统提示定义如下:

system_prompt: str = SYSTEM_PROMPT

这个SYSTEM_PROMPT包含了详细的指导,告诉LLM如何分析任务、选择工具和格式化响应。例如:

SYSTEM_PROMPT = """SETTING: You are an autonomous programmer, and you're working directly in the command line with a special interface.

  

The special interface consists of a file editor that shows you {{WINDOW}} lines of a file at a time.

In addition to typical bash commands, you can also use specific commands to help you navigate and edit files.

To call a command, you need to invoke it with a function call/tool call.

  

Please note that THE EDIT COMMAND REQUIRES PROPER INDENTATION.

If you'd like to add the line ' print(x)' you must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.

  

RESPONSE FORMAT:

Your shell prompt is formatted as follows:

(Open file: <path>)

(Current directory: <cwd>)

bash-$

  

First, you should _always_ include a general thought about what you're going to do next.

Then, for every response, you must include exactly _ONE_ tool call/function call.

  

Remember, you should always include a _SINGLE_ tool call/function call and then wait for a response from the shell before continuing with more discussion and commands. Everything you include in the DISCUSSION section will be saved for future reference.

If you'd like to issue two commands at once, PLEASE DO NOT DO THAT! Please instead first submit just the first tool call, and then after receiving a response you'll be able to issue the second tool call.

Note that the environment does NOT support interactive session commands (e.g. python, vim), so please do not invoke them.

"""
工具参数验证

每个工具都定义了明确的参数规范,这些规范会在调用LLM时传递给它:

parameters: dict = {
    "type": "object",
    "properties": {
        "query": {
            "type": "string",
            "description": "搜索查询"
        },
        "num_results": {
            "type": "integer",
            "description": "返回结果数量"
        }
    },
    "required": ["query"]
}

这些参数规范告诉LLM每个工具需要哪些参数,哪些是必需的,以及它们的类型和描述。

工具选择模式

ToolCallAgent类提供了不同的工具选择模式:

tool_choices: str = "auto"  # **可以是"none"、"auto"或"required"
  • "none":不要求LLM使用工具
  • "auto":LLM可以自行决定是否使用工具
  • "required":要求LLM必须使用工具

这个设置会影响发送给OpenAI API的tool_choice参数:

if tool_choice:
    if tool_choice == "auto":
        params["tool_choice"] = "auto"
    elif tool_choice == "required":
        params["tool_choice"] = {"type": "function"}

6.2 处理不正确响应的机制

尽管有上述机制,LLM仍可能给出不正确的响应。OpenManus通过多层错误处理来应对这种情况:

响应解析和验证

LLM类的_parse_tool_response方法中,会解析和验证LLM的响应:

def _parse_tool_response(self, response: ChatCompletion) -> ToolResponse:
    """Parse the response from the LLM with tool calls."""
    choice = response.choices[0]
    message = choice.message
    
    # **解析工具调用
    tool_calls = []
    if message.tool_calls:
        for tc in message.tool_calls:
            try:
                # **解析工具调用参数
                args = json.loads(tc.function.arguments)
                tool_calls.append(
                    ToolCall(
                        id=tc.id,
                        function=FunctionCall(
                            name=tc.function.name,
                            arguments=args,
                        ),
                    )
                )
            except json.JSONDecodeError:
                logger.error(f"Failed to parse tool call arguments: {tc.function.arguments}")
    
    # **返回解析后的响应
    return ToolResponse(
        content=message.content or "",
        tool_calls=tool_calls,
    )

这个方法会尝试解析LLM返回的工具调用参数,如果解析失败(例如,参数不是有效的JSON),会记录错误但不会中断执行。

工具执行错误处理

ToolCallAgentexecute_tool方法中,会捕获工具执行过程中的任何异常:

async def execute_tool(self, command: ToolCall) -> str:
    """Execute a single tool call."""
    name = command.function.name
    args = command.function.arguments
    
    # **处理特殊工具
    if name in self.special_tool_names:
        return await self._handle_special_tool(name, args)
    
    # **执行普通工具
    try:
        result = await self.available_tools.execute(name=name, tool_input=args)
        return result
    except Exception as e:
        error_msg = f"Error executing tool {name}: {str(e)}"
        logger.error(error_msg)
        return error_msg  # **返回错误信息而不是抛出异常

如果工具执行失败,会返回一个错误消息,而不是中断整个执行流程。

工具参数验证

ToolCollectionexecute方法中,会检查工具是否存在:

async def execute(self, *, name: str, tool_input: Dict[str, Any] = None) -> ToolResult:
    """Execute a tool by name with given input."""
    # **查找工具
    tool = self.tool_map.get(name)
    if not tool:
        return ToolFailure(error=f"Tool {name} is invalid")  # **返回错误结果
    
    # **执行工具
    try:
        result = await tool(**tool_input)
        return result
    except ToolError as e:
        return ToolFailure(error=e.message)  # **返回错误结果

如果指定的工具不存在,或者执行过程中抛出ToolError异常,会返回一个ToolFailure对象,而不是中断执行。

具体工具的参数验证

每个工具都可以实现自己的参数验证逻辑。例如,GoogleSearch工具会检查查询参数是否有效:

async def execute(self, query: str, num_results: int = 10) -> List[str]:
    """Execute a Google search with the given query."""
    if not query or not isinstance(query, str):
        return "Error: Invalid query parameter"
    
    try:
        # **执行搜索...
    except Exception as e:
        return f"Error performing Google search: {str(e)}"
卡住状态检测

BaseAgent类实现了卡住状态检测机制,用于处理LLM陷入循环的情况:

def is_stuck(self) -> bool:
    """Check if the agent is stuck in a loop."""
    if len(self.memory.messages) < 4:
        return False
    
    # **检查最近的消息是否重复
    recent_messages = self.memory.messages[-4:]
    content_set = set(msg.content for msg in recent_messages)
    return len(content_set) <= 1  # **如果最近4条消息内容相同,认为卡住了

def handle_stuck_state(self):
    """Handle the case when agent is stuck in a loop."""
    logger.warning("Agent appears to be stuck in a loop. Adding intervention...")
    
    # **添加干预消息
    intervention_msg = Message.system_message(
        "You appear to be stuck in a loop. Please try a different approach."
    )
    self.memory.add_message(intervention_msg)

如果检测到智能体陷入循环(最近几条消息内容相同),会添加一条干预消息,提示LLM尝试不同的方法。

6.3 具体例子

例子1:工具参数不正确

假设LLM选择了GoogleSearch工具,但提供了错误的参数格式:

{
  "function": {
    "name": "google_search",
    "arguments": "Python异步编程"  // 错误:应该是一个JSON对象,而不是字符串
  }
}

处理流程:

  1. _parse_tool_response方法尝试解析参数,但会失败
  2. 错误会被记录,但不会中断执行
  3. execute_tool方法会收到一个空的参数字典
  4. GoogleSearch.execute方法会检测到无效参数并返回错误消息
  5. 错误消息会被添加到智能体的记忆中
  6. 在下一个步骤中,LLM会看到这个错误消息,并可能尝试修正参数格式
例子2:工具不存在

假设LLM选择了一个不存在的工具:

{
  "function": {
    "name": "nonexistent_tool",
    "arguments": {}
  }
}

处理流程:

  1. execute_tool方法会调用ToolCollection.execute方法
  2. ToolCollection.execute方法会检查工具是否存在
  3. 由于工具不存在,会返回一个错误消息:“Tool nonexistent_tool is invalid”
  4. 这个错误消息会被添加到智能体的记忆中
  5. 在下一个步骤中,LLM会看到这个错误消息,并可能选择一个存在的工具
例子3:工具执行失败

假设LLM选择了GoogleSearch工具,参数正确,但搜索API暂时不可用:

{
  "function": {
    "name": "google_search",
    "arguments": {
      "query": "Python异步编程"
    }
  }
}

处理流程:

  1. execute_tool方法会调用GoogleSearch.execute方法
  2. GoogleSearch.execute方法会尝试调用搜索API
  3. 由于API不可用,会抛出异常
  4. 异常会被捕获,并返回一个错误消息:“Error performing Google search: API not available”
  5. 这个错误消息会被添加到智能体的记忆中
  6. 在下一个步骤中,LLM会看到这个错误消息,并可能尝试其他方法或工具

6.4 总结

OpenManus通过多层错误处理机制来确保即使LLM给出不正确的响应,系统也能继续运行:

  1. 预防措施

    • 精心设计的系统提示
    • 明确的工具参数规范
    • 灵活的工具选择模式
  2. 错误处理

    • 响应解析和验证
    • 工具执行错误捕获
    • 工具参数验证
    • 卡住状态检测和干预

这种设计使得OpenManus能够在各种情况下保持稳定运行,即使LLM偶尔给出不正确的响应。


七、实际应用场景与扩展可能

7.1 OpenManus的应用场景

OpenManus作为一个功能强大的智能体框架,可以应用于多种场景:

自动化助手

OpenManus可以作为个人或团队的自动化助手,执行各种任务:

  • 信息收集与整理:自动搜索、浏览网页并整理信息
  • 代码生成与测试:根据需求生成代码,并自动执行测试
  • 文档处理:自动生成、修改和整理文档
  • 数据分析:执行数据处理脚本,生成分析报告
研究辅助工具

研究人员可以使用OpenManus来加速研究过程:

  • 文献调研:自动搜索和总结相关文献
  • 数据处理:执行数据清洗和转换脚本
  • 实验自动化:设计、执行和记录实验
  • 结果可视化:生成图表和可视化展示
教育与学习工具

OpenManus可以作为教育和学习的辅助工具:

  • 交互式教程:创建能够执行代码示例的教程
  • 问题解答:回答学习问题并提供实际示例
  • 编程练习:生成编程练习并评估解答
  • 知识探索:帮助学习者探索新领域
开发辅助系统

软件开发人员可以使用OpenManus来提高开发效率:

  • 代码生成:根据需求生成代码框架或完整实现
  • 调试辅助:分析错误信息并提供修复建议
  • 文档生成:自动生成代码文档和API说明
  • 测试自动化:生成测试用例并执行测试

7.2 添加新工具的方法

OpenManus的模块化设计使得添加新工具变得简单直接。以下是添加新工具的步骤:

1) 创建工具类

首先,创建一个继承自BaseTool的新类:

from app.tool.base import BaseTool

class MyNewTool(BaseTool):
    name: str = "my_new_tool"
    description: str = "这个工具的功能描述"
    parameters: dict = {
        "type": "object",
        "properties": {
            "param1": {
                "type": "string",
                "description": "参数1的描述"
            },
            "param2": {
                "type": "integer",
                "description": "参数2的描述"
            }
        },
        "required": ["param1"]
    }
    
    async def execute(self, param1: str, param2: int = 0) -> str:
        """执行工具的具体逻辑"""
        try:
            # **实现工具功能
            result = f"处理 {param1}{param2} 的结果"
            return result
        except Exception as e:
            return f"工具执行出错: {str(e)}"
2) 实现工具功能

execute方法中实现工具的具体功能。确保:

  • 处理所有可能的异常
  • 返回清晰的结果或错误信息
  • 使用异步编程(async/await)处理I/O操作
3) 添加工具到智能体

将新工具添加到智能体的可用工具列表中:

from app.agent.toolcall import ToolCallAgent
from app.tool.tool_collection import ToolCollection
from my_new_tool import MyNewTool

class MyCustomAgent(ToolCallAgent):
    available_tools: ToolCollection = Field(
        default_factory=lambda: ToolCollection(
            PythonExecute(), GoogleSearch(), MyNewTool()
        )
    )

或者修改现有的Manus智能体:

from app.agent.manus import Manus
from app.tool.tool_collection import ToolCollection
from my_new_tool import MyNewTool

# **创建包含新工具的Manus实例
agent = Manus()
agent.available_tools = ToolCollection(
    *agent.available_tools.tools,  # **保留原有工具
    MyNewTool()  # **添加新工具
)
4) 更新系统提示(可选)

如果新工具需要特殊的使用说明,可以更新系统提示:

agent.system_prompt += "\n你现在可以使用my_new_tool工具来处理特定任务。"

7.3 定制智能体的方法

OpenManus的层次化设计使得定制智能体变得灵活多样:

1) 继承现有智能体

最简单的方法是继承现有的智能体类:

from app.agent.toolcall import ToolCallAgent

class MySpecializedAgent(ToolCallAgent):
    name: str = "MySpecializedAgent"
    description: str = "专门用于特定任务的智能体"
    
    system_prompt: str = "你是一个专门用于特定任务的智能体..."
    next_step_prompt: str = "分析当前状态,决定下一步行动..."
    
    # **自定义属性和方法
    max_steps: int = 30
    
    async def think(self) -> bool:
        # **自定义思考逻辑
        return await super().think()
2) 自定义工具集

为特定任务定制工具集:

from app.tool.tool_collection import ToolCollection
from app.tool.python_execute import PythonExecute
from app.tool.file_saver import FileSaver
from my_special_tools import ToolA, ToolB

class DataAnalysisAgent(ToolCallAgent):
    available_tools: ToolCollection = Field(
        default_factory=lambda: ToolCollection(
            PythonExecute(), FileSaver(), ToolA(), ToolB()
        )
    )
3) 自定义提示模板

为智能体定制专门的提示模板:

# **在app/prompt/my_agent.py中定义
SYSTEM_PROMPT = """
你是一个专门用于数据分析的智能体。你可以使用以下工具:
1. python_execute - 执行Python代码,特别是数据分析代码
2. file_saver - 保存分析结果
3. tool_a - 特殊功能A
4. tool_b - 特殊功能B

分析任务时,请遵循以下步骤:
1. 理解数据结构
2. 执行初步分析
3. 可视化关键指标
4. 生成分析报告
"""

# **在智能体中使用
from app.prompt.my_agent import SYSTEM_PROMPT

class DataAnalysisAgent(ToolCallAgent):
    system_prompt: str = SYSTEM_PROMPT
4) 自定义执行流程

通过重写stepthinkact方法来自定义执行流程:

async def step(self) -> str:
    """自定义步骤执行逻辑"""
    # **记录步骤开始时间
    start_time = time.time()
    
    # **执行标准的思考-行动循环
    should_act = await self.think()
    if not should_act:
        return "思考完成 - 无需行动"
    
    result = await self.act()
    
    # **记录步骤执行时间
    execution_time = time.time() - start_time
    logger.info(f"步骤执行时间: {execution_time:.2f}秒")
    
    return result

7.4 集成到其他系统的可能性

OpenManus的设计使其易于集成到各种系统中:

1) Web应用集成

将OpenManus集成到Web应用中:

from fastapi import FastAPI, Request
from app.agent.manus import Manus

app = FastAPI()
agents = {}  # **存储用户会话的智能体实例

@app.post("/chat/{session_id}")
async def chat(session_id: str, request: Request):
    data = await request.json()
    user_input = data.get("message", "")
    
    # **获取或创建智能体实例
    if session_id not in agents:
        agents[session_id] = Manus()
    
    # **运行智能体
    result = await agents[session_id].run(user_input)
    
    return {"response": result}
2) 桌面应用集成

将OpenManus集成到桌面应用中:

import tkinter as tk
from app.agent.manus import Manus

class ManusApp:
    def __init__(self, root):
        self.root = root
        self.agent = Manus()
        # **设置UI组件
        
    async def send_message(self):
        user_input = self.input_field.get()
        self.input_field.delete(0, tk.END)
        
        # **在后台线程中运行智能体
        result = await self.agent.run(user_input)
        
        # **更新UI显示结果
        self.display_area.insert(tk.END, f"结果: {result}\n")
3) API服务集成

将OpenManus作为API服务提供:

from fastapi import FastAPI
from pydantic import BaseModel
from app.agent.manus import Manus

app = FastAPI()
agent = Manus()

class QueryRequest(BaseModel):
    query: str

@app.post("/api/query")
async def query(request: QueryRequest):
    result = await agent.run(request.query)
    return {"result": result}
4) 自动化工作流集成

将OpenManus集成到自动化工作流中:

from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
from app.agent.manus import Manus

def run_manus_task(task_description):
    agent = Manus()
    result = asyncio.run(agent.run(task_description))
    return result

with DAG('manus_workflow', start_date=datetime(2023, 1, 1), schedule_interval='@daily') as dag:
    task1 = PythonOperator(
        task_id='data_collection',
        python_callable=run_manus_task,
        op_kwargs={'task_description': '收集今天的市场数据'}
    )
    
    task2 = PythonOperator(
        task_id='data_analysis',
        python_callable=run_manus_task,
        op_kwargs={'task_description': '分析收集到的市场数据'}
    )
    
    task1 >> task2

7.5 未来发展方向

OpenManus作为一个开源项目,有多个潜在的发展方向:

多智能体协作

实现多个智能体之间的协作,每个智能体专注于特定领域:

  • 专家智能体:不同领域的专家智能体
  • 协调智能体:负责分配任务和整合结果
  • 评估智能体:评估其他智能体的输出质量
  • 对抗智能体:提供批判性思考和挑战
增强学习能力

为智能体添加更强的学习能力:

  • 长期记忆:持久化存储重要信息
  • 知识库集成:连接到专门的知识库
  • 示例学习:从用户示例中学习新技能
  • 反馈学习:根据用户反馈调整行为
自主性增强

增强智能体的自主性:

  • 自主规划:智能体自主规划解决问题的步骤
  • 自主探索:主动探索未知领域
  • 自主学习:识别知识缺口并主动学习
  • 自主决策:在不确定情况下做出决策
工具生态扩展

扩展工具生态系统:

  • 多模态工具:处理图像、音频、视频的工具
  • 专业领域工具:针对特定领域的专业工具
  • 工具市场:允许社区贡献和分享工具
  • 工具组合:自动组合多个工具创建复杂功能
安全与隐私增强

加强安全性和隐私保护:

  • 沙箱执行:更安全的代码执行环境
  • 权限控制:细粒度的工具访问权限
  • 隐私保护:敏感信息的处理机制
  • 审计日志:详细记录智能体的所有操作
用户体验优化

改善用户与智能体的交互体验:

  • 自然对话:更自然的对话界面
  • 多模态交互:支持语音、图像等交互方式
  • 进度反馈:实时显示任务进度
  • 可解释性:解释决策过程和推理逻辑

OpenManus作为一个灵活且强大的智能体框架,有着广阔的应用前景和发展空间。随着技术的进步和社区的贡献,它有潜力成为构建下一代AI应用的重要基础设施。

八、拙见

关于大模型的可靠性
大模型的输出仍然需要大量后期校验来修正,这一过程既可以依赖人工编写的规则,也可以借助大模型自身进行自纠自查。或许,随着技术的发展,模型自我优化的能力会进一步增强,使得人工干预的成本逐步降低。

关于 Agent 开发
当前,大模型的主流交互方式仍然较为低效,Agent 的引入可以在一定程度上提升交互效率。然而,这种形态仍让我联想到清朝末年“马拉火车”的景象——新旧技术的混搭或许只是过渡阶段,最终更高效、更自然的交互方式将会取代它。

关于 Prompt 工程
Prompt 工程从最初的火热讨论,到如今的相对沉寂,反映了技术的演进趋势:交互方式正变得越来越自然,复杂性逐渐被底层封装,最终回归到最朴素的形态。然而,在 Agent 开发中,Prompt 设计仍然是重要环节,或许未来这一过程也会被进一步抽象化,让开发者无需直接接触底层逻辑,而是通过更直观的方式操控大模型。

关于大模型之间的交互
曾经看到一个视频,内容是两个大模型通过语音聊天,在确认对方也是 AI 后,切换为一种奇特的电子音交互模式。这是否真实尚未求证,但这一思路令人遐想——或许未来的大模型之间需要一种全新的交互协议,使得它们能够高效、准确地沟通,而非模拟人类语言的方式。

关于软件开发
从单机到联网,从物理机到云,每一次基础设施的变革都推动了软件开发范式的演进。如今,大模型正在成为新一代的基础设施,这意味着未来的软件开发可能需要与大模型深度适配,甚至直接基于大模型构建。也许,Agent 只是过渡形态,更激进的方式是——模型即软件。

关于端侧智能
大模型的横空出世,使得在联网环境下,边缘计算和端侧智能的意义似乎变得模糊。当计算能力可以按需获取,设备本地的智能化或许不再是核心问题,而是如何高效地利用模型资源,真正实现智能无处不在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值