先来看效果
输入需求:查询北京天气,并绘制为折线图。
智能体根据输入的需求,首先打开浏览器访问相关的网页,当网页无法访问时,还会自动切换网页,最后,智能体将会把浏览器中收集的数据保存整理为文件,并通过编程的方式,通过Python脚本绘制折线图。
任务规划:
浏览网页:
编写脚本:
Manus复刻思路
前端 (UI - HTML/CSS/JS): 用户交互的界面。我们设计了一个双栏布局:
l 左栏: 核心对话区域,展示用户与 Manus 的交流历史,以及 Manus 调用工具的摘要信息(可点击交互)。
l 右栏: 右栏是对Manus的电脑的模拟,可以展示Manus生成的文件、浏览网页等。
l 实时通信: 通过 WebSocket 与后端保持长连接,实现流式响应和状态更新。
后端 (API - FastAPI): 负责处理前端请求,管理 WebSocket 连接,并作为前端与智能体核心的桥梁。它使用异步框架以支持并发连接和流式处理。
LLM:使用DeepSeekV3作为核心的决策模型。并结合MCP实现工具的调用。
后端
MCP服务端
整个过程中一共需要两个MCP服务:
{"mcpServers":{"playwright":{"command":"npx","args":["@playwright/mcp@latest"]},"manus_server":{"command":"python","args":["manus_server.py"]}}}Playwright:
Playwright是一个由微软开发的现代化端到端测试框架,专为Web应用测试而设计。它支持多种浏览器(包括Chromium、Firefox和WebKit),提供跨浏览器的一致性测试能力,确保应用在不同环境下表现一致。其核心优势在于高可靠性和快速执行,通过直接与浏览器引擎交互,避免了传统测试工具的不稳定性问题。
Playwright 在 LLM(大语言模型)工具调用中的应用主要体现在通过其强大的浏览器自动化能力,为 LLM 提供了与网页交互的接口。Playwright MCP 是一个基于 Model Context Protocol(MCP)的服务器,它利用 Playwright 的浏览器自动化能力,使 LLM 能够直接控制浏览器,完成打开网页、点击元素、输入文字等操作。这种工具的核心优势在于快速、轻量,能生成结构化数据,无需依赖截图或视觉模型。
Playwright MCP 支持多种浏览器,如 Chromium、Firefox 和 WebKit,并且兼容多种编程语言,包括 TypeScript、JavaScript、Python、.NET 和 Java。它提供了两种模式:默认的快照模式(Snapshot Mode)和视觉模式(Vision Mode),其中快照模式更适合快速、高效的结构化数据交互。
manus_server:
由于Playwright中提供的工具并不能完全满足智能体的需求,因此需要构建一些额外的MCP服务,满足智能体的功能,因此,我额外的构建了一个名为manus_server的MCP服务,manus_server提供了下面几个工具:
google_search:搜索相关连接
make_todo_md:创建计划文件
write_to_file:创建文件
execute_command:执行指令
MCP客户端
客户端通过下面的函数实现核心的处理逻辑:
asyncdefprocess_user_message(message:str,websocket:WebSocket):"""这是核心处理函数,你需要在这里集成你的Agent交互逻辑。接收用户消息,调用Agent,并将结果通过WebSocket发回前端。"""try:manus_agent.add_message(role='user',content=message)awaitSendStatus("Manus正在处理...",websocket).send()whileTrue:last_message_chunk=awaitgenerate_and_send_message_chunk(websocket,manus_agent.generate)tool_executed=last_message_chunk.include_toolifnottool_executed:breaksend_tool=SendTool(last_message_chunk,websocket)awaitsend_tool.send_tool()result=awaitmanus_agent.judge_and_execute(last_message_chunk)awaitasyncio.sleep(0.5)awaitsend_tool.send_result()manus_agent.add_message(role='system',content=result)#---结束---logging.info("Finishedprocessingusermessage.")exceptExceptionase:logging.error(f"Errorinagentlogic:{e}",exc_info=True)awaitwebsocket.send_text(json.dumps({"type":"error","content":f"Agent处理出错:{e}"}))Manus电脑的模拟
Manus电脑的模拟主要通过docker实现。
我使用docker构建了一个docker容器,这个容器中包含了一些Python运行的环境,以便能运行智能体编写的Python代码。
docker容器中需要有一个路径与本机中的某个路径相绑定,两个路径中的文件是同步的,这样的设定能够方便后端获取智能体在docker中生成的文件,从而展示文件的内容。
defget_or_create_docker_container(container_name:str=CONTAINER_NAME,image_name:str='python:3.12.10',local_workspace_dir=DEFAULT_TASK_DIRECTORY,container_workspace_dir:str|None=CONTAINER_WORKSPACE_DIR,docker_client:docker.DockerClient|None=client,keep_alive_command:str=DEFAULT_KEEP_ALIVE_COMMAND,auto_remove:bool=False#SettoTruetoautomaticallyremovecontaineronexit(usefulfortemptasks)):client=docker_clientcontainer=Nonetry:#1.Trytogettheexistingcontainerprint(f"Checkingforexistingcontainer'{container_name}'...")container=client.containers.get(container_name)print(f"Foundexistingcontainer:{container.name}(ID:{container.short_id})")#2.Ensurethefoundcontainerisrunningifcontainer.status!='running':print(f"Container'{container_name}'isnotrunning(status:{container.status}).Starting...")try:container.start()#Waitamomentforthecontainertofullystarttime.sleep(2)container.reload()#Refreshcontainerstateifcontainer.status!='running':#Ifstartfailed,raiseanerrorlogs=container.logs(tail=10).decode('utf-8',errors='ignore')raiseRuntimeError(f"Failedtostartexistingcontainer'{container_name}'."f"Currentstatus:{container.status}.\nLastlogs:\n{logs}")print(f"Container'{container_name}'startedsuccessfully.")exceptExceptionase:print(f"ERROR:Unexpectederrorwhilestartingcontainer'{container_name}':{e}")raiseelse:print(f"Container'{container_name}'isalreadyrunning.")exceptNotFound:#3.IfcontainerNotFound,createitprint(f"Container'{container_name}'notfound.Creatingnewcontainer...")#Preparevolumesifspecifiedvolumes={}iflocal_workspace_dirandcontainer_workspace_dir:host_path=Path(local_workspace_dir).resolve()#Useabsolutepathhost_path.mkdir(parents=True,exist_ok=True)#Ensurehostdirexistsprint(f"Ensuringlocaldirectoryexists:{host_path}")volumes[str(host_path)]={'bind':container_workspace_dir,'mode':'rw'}print(f"Mappinglocal'{host_path}'tocontainer'{container_workspace_dir}'")#Check/PullImageprint(f"Checking/pullingimage:{image_name}...")client.images.get(image_name)#Createandstartthecontainerprint(f"Creatingandstartingcontainer'{container_name}'fromimage'{image_name}'...")container_config={"image":image_name,"name":container_name,"command":keep_alive_command,"volumes":volumesifvolumeselseNone,"working_dir":container_workspace_dirifcontainer_workspace_direlseNone,"detach":True,"tty":True,"stdin_open":True,"auto_remove":auto_remove,#Addrestartpolicyifdesired,e.g.,restart_policy={"Name":"unless-stopped"}}#RemoveNonevaluesfromconfigcontainer_config={k:vfork,vincontainer_config.items()ifvisnotNone}container=client.containers.run(**container_config)#Waitamomentandverifytime.sleep(2)container.reload()ifcontainer.status=='running':print(f"Successfullycreatedandstartedcontainer:{container.name}(ID:{container.short_id})")else:logs=container.logs(tail=10).decode('utf-8',errors='ignore')raiseRuntimeError(f"Createdcontainer'{container_name}'failedtostart."f"Currentstatus:{container.status}.\nLastlogs:\n{logs}")#Finalverification(shouldberedundantiflogicaboveiscorrect,butsafe)ifnotcontainerorcontainer.status!='running':#ThispathshouldideallynotbereachediferrorswereraisedcorrectlyaboveraiseRuntimeError(f"Failedtoobtainarunningcontainerinstancefor'{container_name}'.")print(f"Container'{container.name}'isreadyandrunning.")print("-"*30)returncontainer智能体可以直接通过命令行对docker容器进行操作,这段代码在execute_command工具的调用中实现。execute_command工具将直接对docker容器进行操作。
@mcp.tool()defexecute_command(command:str)->str:"""执行命令并返回结果"""workdir=CONTAINER_WORKSPACE_DIRexit_code,output=container.exec_run(["/bin/sh","-c",command],workdir=workdir,stream=False,demux=False)output_str=output.decode('utf-8').strip()ifoutputelse""returnf"{output_str}"构建Manus这样一个智能体绝非易事。Prompt 的健壮性、复杂任务的规划能力、工具执行错误的处理、多工具协同、以及安全性都是需要持续投入的方向。
未来,我们设想 Manus 能集成更多类型的工具,拥有更强的长期记忆和规划能力,并在更复杂的场景中为用户提供端到端的解决方案。
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |