什么是Agent?智能体(Agent)是一个能够感知环境、进行推理、制定计划、做出决策并自主采取行动以实现特定目标的 A 系统。它以大语言模型为核心,集成 记忆、知识库和工具 等能力为一体,构造了完整的决策能力、执行能力和记忆能力,就像一个有主观能动性的人类一样。 与普通的 AI 大模型不同,智能体能够:
- 1.感知环境:通过各种输入渠道获取信息(多模态),理解用户需求和环境状态
- 2.自主规划任务步骤:将复杂任务分解为可执行的子任务,并设计执行顺序
- 3.主动调用工具完成任务:根据需要选择并使用各种外部工具和 API,扩展自身能力边界
- 4.进行多步推理:通过思维链(Chain ofThought)逐步分析问题并推导解决方案
- 5.持续学习和记忆过去的交互:保持上下文连贯性,利用历史交互改进决策
- 6.根据环境反馈调整行为:根据执行结果动态调整策略,实现闭环优化
大多数同学第一次感受到智能体应该是“深度思考"功能,这是 A逐步智能化的体现。 智能体的分类跟人的生长阶段一样,智能体也是可以不断进化的。按照自主性和规划能力,智能体可以分为几个层次:
- 1)反应式智能体:仅根据当前输入和固定规则做出反应,类似简单的聊天机器人,没有真正的规划能力。23 年时的大多数 Al聊天机器人应用,几乎都是反应式智能体。
- 2)有限规划智能体:能进行简单地多步骤执行,但执行路径通常是预设的或有严格限制的。鉴定为“能干事、但干不了复杂的大事”。24 年流行的很多可联网搜索内容、调用知识库和工具的 AI 应用,都属于这类智能体。比如ChatGPT + Plugins:
- 3)自主规划智能体:也叫目标导向智能体,能够根据任务目标自主分解任务、制定计划、选择工具并一步步执行,直到完成任务。这类智能体通过“思考-行动-观察” 的循环模式工作,能够持续推进任务直至完成目标。
智能体实现关键技术在自主开发智能体前,我们要先了解一下智能体的关键实现技术,也就是方案设计阶段做的事情。1、CoT 思维链CoT(Chain of Thought)思维链是一种让 A|像人类一样“思考”的技术,帮助 AI 在处理复杂问题时能够按步骤思考。对于复杂的推理类问题,先思考后执行,效果往往更好。而且还可以让模型在生成答案时展示推理过程,便于我们理解和优化 Al。CoT的实现方式其实很简单,可以在输入Prompt 时,给模型提供额外的提示或引导,比如“让我们一步一步思考这个问题”,让模型以逐步推理的方式生成回答。还可以运用 Prompt的优化技巧few shot(少样本学习),给模型提供包含思维链的示例问题和答案,让模型学习如何构建自己的思维链。在 OpenManus 早期版本中,可以看到实现 CoT的系统提示词:- You are an assistant focused on Chain of Thought reasoning. For each question, please follow these steps:
-
- 1. Break down the problem: Divide complex problems into smaller, more manageable parts
- 2. Think step by step: Think through each part in detail, showing your reasoning process
- 3. Synthesize conclusions: Integrate the thinking from each part into a complete solution
- 4. Provide an answer: Give a final concise answer
-
- Your response should follow this format:
- Thinking: [Detailed thought process, including problem decomposition, reasoning for each step, and analysis]
- Answer: [Final answer based on the thought process, clear and concise]
-
- Remember, the thinking process is more important than the final answer, as it demonstrates how you reached your conclusion.
复制代码
2、Agent Loop 执行循环Agent Loop 是智能体最核心的工作机制,指智能体在没有用户输入的情况下,自主重复执行推理和工具调用的过程。 在传统的聊天模型中,每次用户提问后,AI回复一次就结束了。但在智能体中,A回复后可能会继续自主执行后续动作(如调用工具、处理结果、继续推理),形成一个自主执行的循环,直到任务完成(或者超出预设的最大步骤数)。Agent Loop 的实现很简单,参考代码如下:- public String execute() {
- List<String> results = new ArrayList<>();
- while (currentStep < MAX_STEPS && !isFinished) {
- currentStep++;
- // 这里实现具体的步骤逻辑
- String stepResult = executeStep();
- results.add("步骤 " + currentStep + ": " + stepResult);
- }
- if (currentStep >= MAX_STEPS) {
- results.add("达到最大步骤数: " + MAX_STEPS);
- }
- return String.join("\n", results);
- }
复制代码
3、ReAct 模式ReAct(Reasoning+ Acting)是一种结合推理和行动的智能体架构,它模仿人类解决问题时"思考-行动-观察"的循环,目的是通过交互式决策解决复杂任务,是目前最常用的智能体工作式之一。核心思想:
- 1.推理(Reason):将原始问题拆分为多步骤任务,明确当前要执行的步骤,比如“第一步需要打开编程导航网站”。
- 2.行动(Act):调用外部工具执行动作,比如调用搜索引擎、打开浏览器访问网页等。
- 3.观察(Observe):获取工具返回的结果,反馈给智能体进行下一步决策。比如将打开的网页代码输入给 Al。
- 4.循环迭代:不断重复上述3个过程,直到任务完成或达到终止条件。
ReAct 流程如图: 示例实现代码:- void executeReAct(String task) {
- String state = "开始";
-
- while (!state.equals("完成")) {
- // 1. 推理 (Reason)
- String thought = "思考下一步行动";
- System.out.println("推理: " + thought);
-
- // 2. 行动 (Act)
- String action = "执行具体操作";
- System.out.println("行动: " + action);
-
- // 3. 观察 (Observe)
- String observation = "观察执行结果";
- System.out.println("观察: " + observation);
-
- // 更新状态
- state = "完成";
- }
- }
复制代码 手撕开源框架 OpenManus 的源码1、整体文件夹组织形式 2、agent 目录agent 目录是 OpenManus 实现的核心,采用了分层的代理架构,不同层次的代理负责不同的功能,这样更利于系统的扩展。OpenManus 的代理架构主要包含以下几层:
- BaseAgent:最基础的代理抽象类,定义了所有代理的基本状态管理和执行循环
- ReActAgent:实现 ReAct 模式的代理,具有思考(Think)和行动(Act)两个主要步骤
- ToolCallAgent:能够调用工具的代理,继承自 ReActAgent 并扩展了工具调用能力
- Manus:具体实现的智能体实例,集成了所有能力并添加了更多专业工具
还有更多适用于特定领域的智能体实例,比如 DataAnalysis 数据分析 Agent、SWE 软件开发工程师 Agent、MCP 服务交互 Agent、Browser 浏览器操作 Agent,它们都继承了 ToolCallAgent。 3、tool 目录tool 目录定义了各种各样的工具,比如网页搜索、文件操作、询求用户帮助、代码执行器等等:4、prompt 目录prompt 目录定义了整个项目中可能会用到的提示词。从下图中我们可以看到提示词写的比较专业,这块也是比较值得学习的。5、其他支持为了实现完整的智能体功能,OpenManus 依赖以下关键组件:
- 记忆系统:使用 Memory 类存储对话历史和中间状态
- LLM 大模型:通过 LLM 类提供思考和决策能力
- 工具系统:提供 BaseTool和 ToolCollection 类扩展智能体的能力边界
- 流程控制:通过 AgentState 和执行循环管理状态转换和任务流程
在 OpenManus 中,这些都是自主实现的: AI 智能体核心实现了解整体架构后,我们重点学习 Agent 分层代理架构。1、BaseAgentBaseAgent 是所有代理的基础,定义了代理状态管理和执行循环的核心逻辑。查看 base.py 文件,关键代码就是 Agent Loop 的实现,通过 while 实现循环,并且定义了死循环检查机制:- class BaseAgent(BaseModel, ABC):
- async def run(self, request: Optional[str] = None) -> str:
- """执行代理的主循环"""
- if self.state != AgentState.IDLE:
- raise RuntimeError(f"Cannot run agent from state: {self.state}")
-
- 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}")
-
- if self.current_step >= self.max_steps:
- self.current_step = 0
- self.state = AgentState.IDLE
- results.append(f"Terminated: Reached max steps ({self.max_steps})")
-
- return "\n".join(results) if results else "No steps executed"
-
- @abstractmethod
- async def step(self) -> str:
- """执行单步操作,必须由子类实现"""
复制代码 这里其实使用了模板方法设计模式,父类定义执行流程,具体的执行方法(step)交给子类实现。 2、ReActAgentReActAgent 实现了 ReAct 模式,将代理的执行过程分为思考(Think)和行动(Act)两个关键步骤。查看react.py 文件:- class ReActAgent(BaseAgent, ABC):
- @abstractmethod
- async def think(self) -> bool:
- """处理当前状态并决定下一步行动"""
-
- @abstractmethod
- async def act(self) -> str:
- """执行决定的行动"""
-
- async def step(self) -> str:
- """执行单步:思考和行动"""
- should_act = await self.think()
- if not should_act:
- return "Thinking complete - no action needed"
- return await self.act()
复制代码 上述代码同样运用了模板方法设计模式,这种设计体现了 ReAct 模式的核心思想,也就是“程思考-行动-观察”的循环过。但是具体怎么思考、怎么行动,交给子类去实现。 3、ToolCallAgentToolCallAgent 在 ReAct 模式的基础上增加了工具调用能力,是 OpenManus 最重要的一个层次。查看 toolcall.py文件,虽然代码比较复杂,但原理很简单,就是工具调用机制的具体实现:
- 1.think:和 AI 交互思考使用什么工具
- 2.act:程序执行工具
- 3.observe:将结果返回给 AI
- class ToolCallAgent(ReActAgent):
- """能够执行工具调用的代理类"""
-
- available_tools: ToolCollection = ToolCollection(
- CreateChatCompletion(), Terminate()
- )
- tool_choices: TOOL_CHOICE_TYPE = ToolChoice.AUTO
- special_tool_names: List[str] = Field(default_factory=lambda: [Terminate().name])
-
- 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 = tool_calls = (
- response.tool_calls if response and response.tool_calls else []
- )
- content = response.content if response and response.content else ""
-
- # 添加助手消息到记忆
- assistant_msg = (
- Message.from_tool_calls(content=content, tool_calls=self.tool_calls)
- if self.tool_calls
- else Message.assistant_message(content)
- )
- self.memory.add_message(assistant_msg)
-
- # 决定是否应该执行行动
- return bool(self.tool_calls or content)
-
- async def act(self) -> str:
- """执行工具调用并处理结果"""
- if not self.tool_calls:
- # 返回最后一条消息内容,如果没有工具调用
- 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)
-
- # 记录工具响应到记忆
- tool_msg = Message.tool_message(
- content=result,
- tool_call_id=command.id,
- name=command.function.name,
- base64_image=self._current_base64_image,
- )
- self.memory.add_message(tool_msg)
- results.append(result)
-
- return "\n\n".join(results)
复制代码
4、ManusManus 类是 OpenManus 的核心智能体实例,集成了各种工具和能力。查看 manus.p文件:- class Manus(ToolCallAgent):
- """多功能通用智能体,支持本地和 MCP 工具"""
-
- name: str = "Manus"
- description: str = "A versatile agent that can solve various tasks using multiple tools"
-
- # 添加各种通用工具到工具集合
- available_tools: ToolCollection = Field(
- default_factory=lambda: ToolCollection(
- PythonExecute(),
- BrowserUseTool(),
- StrReplaceEditor(),
- AskHuman(),
- Terminate(),
- )
- )
复制代码 关键实现细节学完了超级智能体的核心实现后,我们再学习一些项目中比较微妙的实现细节,对我们自己开发项目也会很有帮助。1、工具系统设计1)工具抽象层 BaseTool所有工具均继承自 BaseToo1 抽象基类,提供统一的接口和行为:- 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:
- """执行工具的具体逻辑,由子类实现"""
-
- def to_param(self) -> Dict:
- """将工具转换为函数调用格式"""
- return {
- "type": "function",
- "function": {
- "name": self.name,
- "description": self.description,
- "parameters": self.parameters,
- },
- }
复制代码 这种设计使得每个工具都有统一的调用方式,同时具有规范化的参数描述,便于LLM理解工具的使用方法。 2)终止工具 TerminateTerminate 工具是一个特殊的工具,允许智能体通过 A 大模型自主决定何时结束任务避免无限循环或者过早结束。- class Terminate(BaseTool):
- name: str = "terminate"
- description: str = """Terminate the interaction when the request is met OR
- if the assistant cannot proceed further with the task.
- When you have finished all the tasks, call this tool to end the work."""
-
- parameters: dict = {
- "type": "object",
- "properties": {
- "status": {
- "type": "string",
- "description": "The finish status of the interaction.",
- "enum": ["success", "failure"],
- }
- },
- "required": ["status"],
- }
-
- async def execute(self, status: str) -> str:
- """完成当前执行"""
- return f"The interaction has been completed with status: {status}"
复制代码 在 agent 源码中,有一个 special tool names 变量,用于指定终止工具等特殊工具 3)询问工具 AskHumanAskHuman 工具允许智能体在遇到无法自主解决的问题时向人类寻求帮助,也就是给用户一个输入框,让我们能够更好地干预智能体完成任务的过程。- class AskHuman(BaseTool):
- """Add a tool to ask human for help."""
-
- name: str = "ask_human"
- description: str = "Use this tool to ask human for help."
- parameters: str = {
- "type": "object",
- "properties": {
- "inquire": {
- "type": "string",
- "description": "The question you want to ask human.",
- }
- },
- "required": ["inquire"],
- }
-
- async def execute(self, inquire: str) -> str:
- return input(f"""Bot: {inquire}\n\nYou: """).strip()
复制代码 这个工具实现虽然简单,但极大地提升了智能体的实用性和安全性。 4)工具集合 ToolCollectionOpenManus 设计了 ToolCollection 类来管理多个工具实例,提供统一的工具注册和执行接口:- class ToolCollection:
- """A collection of defined tools."""
-
- def __init__(self, *tools: BaseTool):
- self.tools = tools
- self.tool_map = {tool.name: tool for tool in tools}
-
- def to_params(self) -> List[Dict[str, Any]]:
- return [tool.to_param() for tool in self.tools]
-
- async def execute(self, *, name: str, tool_input: Dict[str, Any] = None) -> ToolResult:
- 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)
-
- def add_tools(self, *tools: BaseTool):
- """Add multiple tools to the collection."""
- for tool in tools:
- self.add_tool(tool)
- return self
复制代码
智能体工作流当我们面对复杂任务时,单一智能体可能无法满足需求。因此智能体工作流(Agent Workflow)应运而生,通过简单的编排,允许多个专业智能体协同工作,各司其职。智能体工作流编排的精髓在于 将复杂任务分解为连贯的节点链,每个节点由最适合的智能体处理,节点间通过条件路由灵活连接,形成一个高效、可靠的执行网络。Anthropic 曾经在一篇 研究报告《Building effective agents》 中提到了多种不同的智能体工作模式,大家需要了解每种模式的特点和适用场景。智能体工作模式1)Prompt Chaining 提示链工作流Prompt Chaining 是最常见的智能体工作流模式之一。它的核心思想是将一个复杂任务拆解为一系列有序的子任务,每一步由 LLM 处理前一步的输出,逐步推进任务完成。比如在内容生成场景中,可以先让模型生成大纲,再根据大纲生成详细内容,最后进行润色和校对。每一步都可以插入校验和中间检查,确保流程正确、输出更精准。这种模式结构清晰,易于调试,非常适合任务可以被自然分解为多个阶段的场景。2)Routing 路由分流工作流Routing 工作流模式则更像是一个智能的路由器。系统会根据输入内容的类型或特征,将任务分发给最合适的下游智能体或处理流程。非常适合多样化输入和多种处理策略的场景。比如在客服系统中,可以将常见问题、退款请求、技术支持等分流到不同的处理模块;在多模型系统中,可以将简单问题分配给小模型,复杂问题交给大模型。这样既提高了处理效率,也保证了每类问题都能得到最优解答。3)Parallelization 并行化工作流在 Parallelization 并行化模式下,任务会被拆分为多个可以并行处理的子任务,最后聚合各自的结果。比如在代码安全审查场景中,可以让多个智能体分别对同一段代码进行安全审查,最后“投票”决定是否有问题。又比如在处理长文档时,可以将文档分段,每段由不同智能体并行总结。这种模式可以显著提升处理速度,并通过“投票”机制提升结果的准确度。4)Orchestrator-Workers 协调器-执行者工作流对于复杂的任务、参与任务的智能体增多时,我们可以引入一位“管理者”,会根据任务动态拆解出多个子任务,并将这些子任务分配给多个"工人"智能体,最后再整合所有工人的结果。这种中央协调机制提高了复杂系统的整体效率,适合任务结构不确定、需要动态分解的复杂场景。5)Evaluator-Optimizer 评估-优化循环工作流Evaluator-Optimizer 模式模拟了人类“写 =>评 =>改”的过程。一个智能体负责生成初步结果,另一个智能体负责评估和反馈,二者循环迭代优化输出。举个例子,在机器翻译场景中,先由翻译智能体输出,再由评审智能体给出改进建议,反复迭代直到达到满意的质量。这种模式特别适合需要多轮打磨和质量提升的任务。 A2A 协议什么是 A2A 协议?A2A(Agentto Agent)也是最近很热门的一个概念,简单来说,A2A 协议 就是为"智能体之间如何直接交流和协作”制定的一套标准。A2A 协议的核心,是让每个智能体都能像“网络节点“ 一样,拥有自己的身份、能力描述和通信接口。它不仅规定了消息的格式和传递方式,还包括了身份认证、能力发现、任务委托、结果回传等机制。这样一来,智能体之间就可以像人类团队一样,互相打招呼、询问对方能做什么、请求协助。可以把 A2A 类比为智能体世界里的 HTTP协议,HTTP 协议让全球不同服务器和电脑之间能够交换数据,A2A 协议则是让不同厂商、不同平台、不同能力的智能体能够像团队成员一样互相理解、协作和分工。如果说 HTTP 协议让互联网成为了一个开放、互联的世界,那么 A2A 协议则让智能体世界变得开放、协作和高效。 A2A 协议的应用场景A2A 协议的应用非常广泛,总结下来4个字就是 开放互联。比如在自动驾驶领域,不同车辆的智能体可以实时交换路况信息,协同避障和规划路线;在制造车间,生产线上的各类机器人智能体可以根据任务动态分工,互相补位;在金融风控、智能客服等场景,不同的智能体可以根据自身专长协作处理复杂业务流程。我们还可以大胆想象,未来开发者可以像调用云服务一样,按需租用或组合不同的智能体服务,甚至实现智能体之间的自动交易和结算。目前其实就有很多智能体平台,只不过智能体之间的连接协作甚少。 和 MCP 协议的区别虽然 A2A 和 MCP 都算是协议(或者标准),但二者存在本质上的区别。MCP 协议是 智能体和外部工具之间的标准,它规定了智能体如何安全、规范地调用外部的数据库、搜索引擎、代码执行等工具资源。你可以把 MCP 理解为“智能体-工具"的 HTTP 协议。而 A2A 协议则是 智能体之间的通信协议。它更像是让不同的 A 角色之间可以直接对话、协作和分工。从安全角度看,MCP 和 A2A 处理的是不同层面的安全问题:
- MCP 的安全关注点:主要集中在单个智能体与工具之间的安全交互,主要防范的是工具滥用和提示词注入攻击。
- A2A 的安全关注点:更关注智能体网络中的身份认证、授权和信任链。A2A 需要解决“我怎么知道我在和谁通信”、“这个智能体有权限请求这项任务吗”、“如何防止恶意智能体窃取或篡改任务数据”等问题。(这些问题也是 HTTP 协议需要考虑的)
显然,A2A 面临的安全挑战更加复杂,因为它处理的是跨网络、跨平台、多方协作的场景。对于一个成熟的智能体系统,可能会同时运用 MCP 和 A2A,MCP 负责某个智能体内部调用工具完成任务,A2A负责智能体之间协同完成任务。 1)、给智能体添加循环检测和处理机制,防止智能体陷入无限循环。可以参考 OpenManus 源码实现,示例代码如下:- private int duplicateThreshold = 2;
-
- /**
- * 处理陷入循环的状态
- */
- protected void handleStuckState() {
- String stuckPrompt = "观察到重复响应。考虑新策略,避免重复已尝试过的无效路径。";
- this.nextStepPrompt = stuckPrompt + "\n" + (this.nextStepPrompt != null ? this.nextStepPrompt : "");
- System.out.println("Agent detected stuck state. Added prompt: " + stuckPrompt);
- }
-
- /**
- * 检查代理是否陷入循环
- *
- * @return 是否陷入循环
- */
- protected boolean isStuck() {
- List<Message> messages = this.memory.getMessages();
- if (messages.size() < 2) {
- return false;
- }
-
- Message lastMessage = messages.get(messages.size() - 1);
- if (lastMessage.getContent() == null || lastMessage.getContent().isEmpty()) {
- return false;
- }
-
- // 计算重复内容出现次数
- int duplicateCount = 0;
- for (int i = messages.size() - 2; i >= 0; i--) {
- Message msg = messages.get(i);
- if (msg.getRole() == Role.ASSISTANT &&
- lastMessage.getContent().equals(msg.getContent())) {
- duplicateCount++;
- }
- }
-
- return duplicateCount >= this.duplicateThreshold;
- }
-
- // 每一步 step 执行完都要检查是否陷入循环
- if (isStuck()) {
- handleStuckState();
- }
复制代码
2)、智能体支持交互式执行,可以向用户询问信息或获取反馈,从而优化任务的完成效果。实现思路可以参考 OpenManus,专门定义一个 AskHuman 工具,让 A| 自主决定什么时候需要寻求人类帮助:当然也可以通过编写 Prompt 实现,比如 Prompt 中提到“如果你认为需要向人类寻求帮助,输出结果中需要包含[ASK USER](寻求帮助的具体问题}”,并且检查每一条 A1给出的消息,如果包含了 ASK USER 标记,就通过系统控制台和用户交互。示例代码如下:- public boolean think() {
- boolean shouldAct = super.think();
-
- // 获取最新的助手消息
- Message lastMessage = getMessageList().get(getMessageList().size() - 1);
- if (lastMessage instanceof AssistantMessage) {
- String content = lastMessage.getContent();
-
- // 检查是否包含向用户询问的标记
- if (content.contains("[ASK_USER]")) {
- // 提取问题
- String question = content.substring(content.indexOf("[ASK_USER]") + 10);
-
- // 向用户输出问题
- System.out.println("智能体需要你的帮助: " + question);
-
- // 获取用户输入
- Scanner scanner = new Scanner(System.in);
- String userAnswer = scanner.nextLine();
-
- // 添加用户回答到消息列表
- UserMessage userResponse = new UserMessage("用户回答: " + userAnswer);
- getMessageList().add(userResponse);
-
- // 需要继续思考
- return true;
- }
- }
- return shouldAct;
- }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |