技术栈:深度解析Agent实现,定制自己的Manus(01)

360影视 欧美动漫 2025-05-15 04:04 1

摘要:前一阶段Manus大火,被宣传为全球首款“真正意义上的通用AI Agent”,其核心能力就是基于LLM的自主任务分解与执行,根据官方测试数据,Manus 在 GAIA 基准测试中表现超越 OpenAI 同类产品,且完成任务的成本更低。虽然之后技术大咖们对齐技术

文章结合了理论分析与实践案例,旨在帮助读者系统地认识AI Agent的核心要素、设计模式以及未来发展方向。

背景

前一阶段Manus大火,被宣传为全球首款“真正意义上的通用AI Agent”,其核心能力就是基于LLM的自主任务分解与执行,根据官方测试数据,Manus 在 GAIA 基准测试中表现超越 OpenAI 同类产品,且完成任务的成本更低。虽然之后技术大咖们对齐技术深度表示不屑(嫉妒~), 认为其缺乏底层创新,依赖现有工具链组合。 但其工程化整合能力仍具有较高的价值,另外还有两个明显的特点,值得学习。

通过工程化能力(任务调度、工具整合)将 AI Agent 从概念落地为大众产品,满足了大家对 AI “自主解决问题” 的深层期待。准确抓住行业热点,成为2025年AI领域的现象级话题,精准抓住“国产AI”标签与行业热点,邀请码机制(限量发放)制造稀缺性,创始人通过简洁演示快速传递产品价值。

随后MetaGPT团队推出了OpenManus,宣称是Manus的开源复刻版,且在3小时内完成基础开发(看代码提交记录确实卷)。 OpenManus是一个简洁的实现方案,还在快速迭代中, 趁着项目还没有熟透,克隆下来学习了下。对比LangChain,代码结构还算比较容易理解,是一个很好的学习项目,也比较容易做二次开发。 周末花了两天时间基于OpenManus做了二次开发,希望构建属于自己的“Manus”,在这过程中对Agent有了更明确的实践认知,在这里分享下这个过程。

为了让读者对Agent能够有系统的认识,同时也可以对Agent的认识完成一次祛魅的过程。 本篇文章结构为:

1)认识, 对AI Agent相关的一些组成进行原理性解释

2)设计,基于OpenManus构建自己的MyManus,落地和验证认识中的一些概念和原理

3)实验,基于MyManus进行验证,对当前的Agent存在的问题有个直观的感受。

4)改进,结合前面的实验以及Agent当下的问题,探讨未来的方向和改进方案。

认识

结合Agent相关的几篇关键文章和论文,梳理下基于LLM的AI Agent的设计模式以及一些原理性的概念。ATA上关于LLM和Agent的原理性的优质文章太多,这里主要涉及Manus或OpenManus用到的部分。

AI Agent正在重新定义软件服务

你只需要告诉我你要什么,不要告诉我怎么做。

AI Agent将使软件架构的范式从面向过程迁移到面向目标,就像传统面向过程的工作流和K8S的声明式设计模式一样, 当然这两种解决的问题是不一样的, K8s通过定义期望状态而非具体步骤来管理集群,降低集群状态管理的复杂度,确保集群的稳定性和容错性。 Agent之前,传统的软件架构,只能解决有限范围的任务, 而基于Agent的架构,可以解决无限域的任务,真正意义上的个性化服务。

ProAgent: From Robotic Process Automation to Agentic Process Automation

我们可以从Manus的官方网站看到,Manus宣称可以做如下的事情,并提供了相当丰富的案例。

数据分析:比如分析店铺销售数据,制定改进策略等。教育:互动课程,学习资源收集,宇宙探索等生活:保险策略对比,租赁合同分析,旅行计划等效率:合同审查,简历筛选,网站SEO优化等。研究:财报分析,政策研究

AI Agent的核心要素

不同的机构或团队, 对Agent的架构模块的划分略有差异,但基本上包含了:感知,记忆,规划,行动四个核心的要素。 此外,部分团队还涉及定义(管理 Agent 角色特性)、学习(预训练、小样本学习)、认知与意识(思考、整体认知)等特色模块。

*内容来源:华泰证券研究所

Lilian Weng(翁荔)LLM Powered Autonomous Agents 这篇博客被广泛认为是 “AI Agent领域最权威的结构化综述之一” ,为开发者提供了技术框架参考, 大家应该对文中的图已经很熟悉了, 作者将 AI Agent 定义为 “规划 + 记忆 + 工具调用” 的组合,强调LLM作为系统的核心控制器

规划(Planning) :任务分解、子任务生成及自我反思记忆(Memory) :长期记忆存储与上下文管理,用于处理复杂任务工具使用(Tool Usage) :通过API或外部工具增强Agent的能力(如搜索、文件操作,代码执行等)

设计

接下来围绕Agent几个核心要素进行设计,会先简单介绍一些基本的原理,然后基于OpenManus做二次设计或者展示其原来的实现。

LLM

在这之前概括下LLM的一些基本的原理,关于LLM的原理性文章已经很多了,这里只对LLM的推理原理做个简单回顾。

当下的LLM的核心依然是基于神经网络, 相比传统的神经网络CNN/RNN等,Transformer架构通过自注意力机制允许模型直接计算任意两个位置之间的关系,从而更高效捕捉长距离依赖,使得LLM有更强大的多层叠加能力。Transformer的并行计算能力(如自注意力的矩阵运算)同时加速了训练速度,支持更大的模型规模。另外通过多层非线性激活函数(如 ReLU、GeLU)可学习更加复杂模式, 随着参数规模和训练数据增加,LLM 会突现传统神经网络难以实现的能力(如逻辑推理、代码生成),LLM具备了涌现能力!( 也许这就是量变产生了质变吧)

LLM的推理简单理解为是一个 “模式匹配 + 概率计算” 的过程,通过 Transformer 架构和自回归生成,基于海量文本学习到的统计规律,逐步生成符合语言习惯的输出。其效果依赖于训练数据的质量、模型规模以及生成策略的选择。 每一步生成都依赖于已生成的上下文,即通过已知的前序词预测下一个词,当前LLM具备上下文学习(In-Context Learning)能力,LLM通过Prompt中提供的示例或指令,无需参数更新即可适应新任务,依然是在于利用模型的模式匹配能力和统计规律,Transformer 架构中,Prompt的token位置靠前,权重更高,对后续生成的影响更大。

LLM的涌现能力使其具备复杂任务处理能力,但同时也因训练数据的噪声和统计生成特性,容易产生幻觉。RAG通过引入外部知识减少幻觉,SFT微调通过高质量数据优化输出,Agent通过工具调用增强交互能力。这些方法可在一定程度上缓解幻觉,增强可解释性, 但可解释性仍受限于LLM的黑箱特性。Agent设计中,Prompt是核心组件之一,所有的设计都是围绕构建和管理Prompt,需结合工具调用、记忆管理等模块设计。为适应不同任务,Prompt需精细化设计并管理多个版本。

Memory

前面了解到,LLM本质上就是一个具有数十亿到千亿级别参数的无状态函数, 比如可以这样:

LLM自身不具备主动记忆功能,但可通过外部技术(如提示工程、记忆模块)模拟记忆效果,以增强任务表现和交互连贯性。所以别想着LLM会有人类的情感啦,LLM的“连贯性”是概率驱动的,而非真正的意图或情感。

LLM就像一个失忆的绝世高手一样,虽然没有记忆,但可以武功还在, LLM回复完全基于输入的提示(prompt)和训练数据中的模式,通常我们所说的记忆是指的是用户输入提示, 分为短期记忆和长期记忆。

LLM的“短期记忆”通常依赖于其上下文窗口(最大Token限制),通过在输入中包含当前对话或任务的相关信息来维持连贯性。LLM的一些生产参数比如Temperature,Top-P可以影响其基于上下文生成文本内容。 Temperature主要是影响生成的确定性, 是通过控制Temperature实现生成内容的准确性与创造性二者之间的权衡。Top-P主要是发散程度,在聚焦和多样性之间的平衡。长期记忆依赖于外部组件,虽然LLM的预训练参数“存储”了知识,通常已经过时,特定领域的知识,需外部技术(如RAG)补充长期依赖关系。短期记忆也可以转变为长期记忆, 这个对Agent的自主进化很有帮助。

我们设计一个简单的短期记忆模块, 记录推理和外部感知的结果信息, 每次调用LLM时都会带上(受限于LLM的最大Token), 这样LLM就会在每次推理时“记住”之前的推理过程,以及外部工具感知的结论。相当于:每次你问问题,都要讲一遍前面的交流内容。 定义Memory类,记录每次的Message内容。

class Memory(BaseModel): messages: List[Message] = Field(default_factory=list) max_messages: int = Field(default=100)

以下是Memory内容例子

{ "messages": [ { "role": "user", "content": "\n CURRENT PLAN STATUS:\n Plan: QuickSort Implementation Plan (ID: plan_1742277313)\nn\nSteps:\n1. [→] {'step_name': 'Research and understand the QuickSort algorithm', 'status': 'in_progress', 'notes': ''}\n2. ...", "tool_calls": null, "name": null, "tool_call_id": null }, { "role": "user", "content": "You can interact with the computer using PythonExecute, save important content and information files through FileSaver, open browsers with BrowserUseTool, and retrieve information using GoogleSearch.\n\nPythonExecute: ...", "tool_calls": null, "name": null, "tool_call_id": null }, { "role": "assistant", "content": "", "tool_calls": [ { "id": "call_294a4ff7396f4c91813990", "type": "function", "function": { "name": "google_search", "arguments": "{\"query\": \"QuickSort algorithm explained\"}" } } ], "name": null, "tool_call_id": null }, { "role": "tool", "content": "Observed output of cmd `google_search` executed:\n['https://www.w3schools.com/dsa/dsa_algo_quicksort.php']...", "tool_calls": null, "name": "google_search", "tool_call_id": "call_294a4ff7396f4c91813990" }, { "role": "user", "content": "You can interact with the computer using PythonExecute...", "tool_calls": null, "name": null, "tool_call_id": null }, { "role": "assistant", "content": "I have researched the QuickSort algorithm using a Google search. Here are some resources that can help us understand how it works...", "tool_calls": null, "name": null, "tool_call_id": null } ]}

Tools

人类创造、修改和利用外部物体来做超出人类身体和认知极限的事情,为 LLM 配备外部工具可以显著扩展模型功能和应用场景。

Agent的工具调用,实际上是借助LLM的能力, LLM需要先理解工具的功能和参数格式。

Tool Learning with Large Language Models: A Survey

上图可以简单概括为:

1. 你有一系列工具,同时有比较详细的说明书, 你拿着说明书找LLM问一个LLM自身推理无法(准确)完成的问题, 比如:今天几号,天气怎么样, 当前数据库内容是什么, 这些预训练的LLM是无法感知到的, 需要借助外部工具。

2. LLM会从你的工具说明书列表中选工具,准确地告诉你你应该用哪个工具,同时把输入参数也给你(基于说明书描述)

3. 你拿到后调用工具,获取感知信息, 然后再把结果信息给到LLM。

4. LLM判断输出结果,并继续回答你之前的问题。

举一个基于LangChain的简单例子, 加了些HTTP请求埋点, 方便跟踪LangChain与LLM的IO交互。

import osimport httpxfrom dotenv import load_dotenvload_dotenv('../.env.production')os.environ["OPENAI_API_KEY"] = os.getenv("DASHSCOPE_API_KEY")os.environ["OPENAI_API_BASE"] = os.getenv("DASHSCOPE_API_BASE_URL")print(os.getenv("DASHSCOPE_API_BASE_URL"))import jsonimport loggingfrom langchain_core.tools import toolfrom langgraph.prebuilt import create_react_agentfrom langchain_openai import ChatOpenAIlogging.basicConfig(level=logging.INFO)logger = logging.getLogger("httpx")logger.setLevel(logging.INFO)@tooldef string_length(text: str) -> int:"""Calculate text length"""return len(text)def print_request(request, *args, **kwargs):logger.info(f"HTTP Request: {request.method} {request.url}")logger.info(f"Request Headers: {request.headers}")logger.info(json.dumps(json.loads(request.content),indent=1))def print_response(response, *args, **kwargs):logger.info(f"HTTP Response: {response.status_code} {response.reason_phrase}")logger.info(f"Response Headers: {response.headers}")response.readlogger.info(json.dumps(json.loads(response.text),indent=1))custom_client = httpx.Client(base_url=os.getenv("DASHSCOPE_API_BASE_URL"),headers={"X-Custom-Header": "value"},event_hooks={"request": [print_request],"response": [print_response]})llm = ChatOpenAI(model_name="qwen-max-latest", verbose=True, http_client=custom_client )agent = create_react_agent(llm, [string_length],)response = agent.invoke({"messages": [("human", "How long is 'Hello World'")]},config={"callbacks": })# 打印输出for message in response["messages"]:message.pretty_print# Step1 带着工具说明,问LLM{"messages": [{"content": "How long is 'Hello World'","role": "user"}],"model": "qwen-max-latest","stream": false,"tools": [{"type": "function","function": {"name": "string_length","description": "Calculate text length","parameters": {"properties": {"text": {"type": "string"}},"required": ["text"],"type": "object"}}}]}# Step2 LLM根据用户请求和工具信息给出工具调用指令,包含参数{"choices": [{"message": {"content": "","role": "assistant","tool_calls": [{"function": {"name": "string_length","arguments": "{\"text\": \"Hello World\"}"},"index": 0,"id": "call_816c14bce4124280b61eb3","type": "function"}]},"finish_reason": "tool_calls","index": 0,"logprobs": null}],"object": "chat.completion","usage": {"prompt_tokens": 162,"completion_tokens": 18,"total_tokens": 180},"created": 1742270364,"system_fingerprint": null,"model": "qwen-max-latest","id": "chatcmpl-18a82215-d19c-92ff-8bb1-7d0684f4abac"}# Step3: Agent本地调用工具,并把结果给LLM"messages": [{"content": "How long is 'Hello World'","role": "user"},{"content": null,"role": "assistant","tool_calls": [{"type": "function","id": "call_816c14bce4124280b61eb3","function": {"name": "string_length","arguments": "{\"text\": \"Hello World\"}"}}]},{"content": "11","role": "tool","tool_call_id": "call_816c14bce4124280b61eb3"}],"model": "qwen-max-latest","stream": false,"tools": [{"type": "function","function": {"name": "string_length","description": "Calculate text length","parameters": {"properties": {"text": {"type": "string"}},"required": ["text"],"type": "object"}}}]}# Step 4 LLM返回最终结果"choices": [{"message": {"content": "The length of the text 'Hello World' is 11 characters.","role": "assistant"},"finish_reason": "stop","index": 0,"logprobs": null}],"object": "chat.completion","usage": {"prompt_tokens": 191,"completion_tokens": 16,"total_tokens": 207},"created": 1742270365,"system_fingerprint": null,"model": "qwen-max-latest","id": "chatcmpl-1452e622-cf30-9cf8-9971-4530065952f6"}

Planning

Lilian Weng博文里Planning包含四个子模块,Reflection(反思)、Self-critics(自我批评)、Chain of thoughts(思维链)、Subgoal decomposition(子目标分解), Planning将任务分解为可执行的步骤,并根据反馈不断优化计划。例如,通过Subgoal decomposition将大任务拆解为小任务,通过Reflection评估结果并调整策略。

我们可以通过 Reason + Act(ReAct)Plan-and-Solve(PS)来初步实现Agent的Planning能力。还有一些其他的模式。 比如ToT 模式( 提示词:“生成3种不同预算方案,总预算不超过10万元,并列出每个方案的子任务”), 基于角色的多Agent(MetaGPT)

Reason + Act 推理-行动-反馈

ReAct通过LLM交替执行推理和行动,增强了推理和外界感知(通过行动获得)之间的协同作用:推理帮助模型推断、跟踪和更新行动计划,以及处理异常,而行动则允许它与外部资源(如知识库或环境)交互,以获取额外信息。

ReAct: Synergizing Reasoning and Acting in Language Models

设计ReActAgent流程, 如下图,实现重复的Thought- Action-Observation

退出机制有2种方式,一种是当超过最大步数, 另一是当LLM提示要调用一个终止工具时,退出循环。

终止工具描述为:When you have finished all the tasks, call this tool to end the work。

设计ReAct抽象类,由主体的Agent继承, 包含llm和memory,同时有think和act两个阶段的操作,think通过调用LLM进行推理, act主要是调用工具获取额外的信息。 通过step串联, step由Agent重复调用,每次都会通过LLM的推理判断是否需要调用工具。

class ReActAgent(BaseModel, ABC):name: str = Field(..., description="Unique name of the agent")description: Optional[str] = Field(None, description="Optional agent description")# Promptssystem_prompt: Optional[str] = Field(None, description="System-level instruction prompt")next_step_prompt: Optional[str] = Field(None, description="Prompt for determining next action")# Dependenciesllm: LLM = Field(default_factory=LLM, description="Language model instance")memory: Memory = Field(default_factory=Memory, description="Agent's memory store")@abstractmethodasync def think(self) -> bool:"""Process current state and decide next action"""@abstractmethodasync def act(self) -> str:"""Execute decided actions"""async def step(self) -> str:"""Execute a single step: think and act.Thought: ...Action: ...Observation: ...... (Repeated many times)"""should_act = await self.think# 判断是否使用工具ifnot should_act:return"Thinking complete - no action needed"return await self.act

Plan-and-Solve

Chain-of-Thought(CoT)使LLMs能够明确地生成推理步骤并提高它们在推理任务中的准确性。比如Zero-short-CoT将目标问题陈述结合"让我们逐步思考"这样的提示词, 作为LLM的输入。这是一种隐式生成推理链,具有一定的模糊性,存在语义理解错误, 无法生成合理的计划等问题

Plan-and-Solve是一种改进的CoT提示方法 ,通过显式分解任务为子目标并分步执行,提升复杂任务的推理能力。Plan-and-Solve将传统的“黑箱”推理操作,进一步转化为结构化流程,使得Agent的可解释性增强, 而且还可以动态调整和反思。 是Agent实现Planning能力的很好的技术支撑。

Plan-and-Solve Prompting: Improving Zero-Shot Chain-of-Thought Reasoning by Large Language Models

参考Plan-and-Solve论文,设计一个Planner

--------各位老铁,下棋继续!!!!

来源:心平氣和

相关推荐