支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


我复刻了一个Manus

发布日期:2025-04-23 17:32:39 浏览次数: 1532 作者:根根AI
推荐语

复刻Manus智能体,探索前端后端交互与LLM工具调用的前沿技术。

核心内容:
1. Manus智能体执行任务的详细过程:从查询天气到绘制折线图
2. 前端设计:双栏布局、实时通信与用户交互界面
3. 后端架构:FastAPI处理请求、LLM决策模型及MCP服务的应用

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家


先来看效果


输入需求:查询北京天气,并绘制为折线图。


智能体根据输入的需求,首先打开浏览器访问相关的网页,当网页无法访问时,还会自动切换网页,最后,智能体将会把浏览器中收集的数据保存整理为文件,并通过编程的方式,通过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客户端

客户端通过下面的函数实现核心的处理逻辑:

async def process_user_message(message: str, websocket: WebSocket):    """    这是核心处理函数,你需要在这里集成你的 Agent 交互逻辑。    接收用户消息,调用 Agent,并将结果通过 WebSocket 发回前端。    """    try:        manus_agent.add_message(role='user', content=message)        await SendStatus("Manus 正在处理...", websocket).send()        while True:            last_message_chunk = await generate_and_send_message_chunk(websocket, manus_agent.generate)            tool_executed = last_message_chunk.include_tool            if not tool_executed:                break            send_tool = SendTool(last_message_chunk, websocket)            await send_tool.send_tool()            result = await manus_agent.judge_and_execute(last_message_chunk)            await asyncio.sleep(0.5)            await send_tool.send_result()            manus_agent.add_message(role='system', content=result)        # --- 结束 ---        logging.info("Finished processing user message.")    except Exception as e:        logging.error(f"Error in agent logic: {e}", exc_info=True)        await websocket.send_text(json.dumps({"type""error""content"f"Agent 处理出错: {e}"}))




Manus电脑的模拟


Manus电脑的模拟主要通过docker实现。

我使用docker构建了一个docker容器,这个容器中包含了一些Python运行的环境,以便能运行智能体编写的Python代码。

docker容器中需要有一个路径与本机中的某个路径相绑定,两个路径中的文件是同步的,这样的设定能够方便后端获取智能体在docker中生成的文件,从而展示文件的内容。

def get_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  # Set to True to automatically remove container on exit (useful for temp tasks)):    client = docker_client    container = None    try:        # 1. Try to get the existing container        print(f"Checking for existing container '{container_name}'...")        container = client.containers.get(container_name)        print(f"Found existing container: {container.name} (ID: {container.short_id})")        # 2. Ensure the found container is running        if container.status != 'running':            print(f"Container '{container_name}' is not running (status: {container.status}). Starting...")            try:                container.start()                # Wait a moment for the container to fully start                time.sleep(2)                container.reload()  # Refresh container state                if container.status != 'running':                    # If start failed, raise an error                    logs = container.logs(tail=10).decode('utf-8', errors='ignore')                    raise RuntimeError(                        f"Failed to start existing container '{container_name}'. "                        f"Current status: {container.status}.\nLast logs:\n{logs}"                    )                print(f"Container '{container_name}' started successfully.")            except Exception as e:                print(f"ERROR: Unexpected error while starting container '{container_name}': {e}")                raise        else:            print(f"Container '{container_name}' is already running.")    except NotFound:        # 3. If container Not Found, create it        print(f"Container '{container_name}' not found. Creating new container...")        # Prepare volumes if specified        volumes = {}        if local_workspace_dir and container_workspace_dir:            host_path = Path(local_workspace_dir).resolve()  # Use absolute path            host_path.mkdir(parents=True, exist_ok=True)  # Ensure host dir exists            print(f"Ensuring local directory exists: {host_path}")            volumes[str(host_path)] = {'bind': container_workspace_dir, 'mode''rw'}            print(f"Mapping local '{host_path}' to container '{container_workspace_dir}'")        # Check/Pull Image        print(f"Checking/pulling image: {image_name}...")        client.images.get(image_name)        # Create and start the container        print(f"Creating and starting container '{container_name}' from image '{image_name}'...")        container_config = {            "image": image_name,            "name": container_name,            "command": keep_alive_command,            "volumes": volumes if volumes else None,            "working_dir": container_workspace_dir if container_workspace_dir else None,            "detach"True,            "tty"True,            "stdin_open"True,            "auto_remove": auto_remove,            # Add restart policy if desired, e.g., restart_policy={"Name": "unless-stopped"}        }        # Remove None values from config        container_config = {k: v for k, v in container_config.items() if v is not None}        container = client.containers.run(**container_config)        # Wait a moment and verify        time.sleep(2)        container.reload()        if container.status == 'running':            print(f"Successfully created and started container: {container.name} (ID: {container.short_id})")        else:            logs = container.logs(tail=10).decode('utf-8', errors='ignore')            raise RuntimeError(                f"Created container '{container_name}' failed to start. "                f"Current status: {container.status}.\nLast logs:\n{logs}"            )    # Final verification (should be redundant if logic above is correct, but safe)    if not container or container.status != 'running':        # This path should ideally not be reached if errors were raised correctly above        raise RuntimeError(f"Failed to obtain a running container instance for '{container_name}'.")    print(f"Container '{container.name}' is ready and running.")    print("-" * 30)    return container


智能体可以直接通过命令行对docker容器进行操作,这段代码在execute_command工具的调用中实现。execute_command工具将直接对docker容器进行操作。

@mcp.tool()def execute_command(command: str) -> str:    """执行命令并返回结果"""    workdir = CONTAINER_WORKSPACE_DIR    exit_code, output = container.exec_run(["/bin/sh""-c", command], workdir=workdir, stream=False, demux=False)    output_str = output.decode('utf-8').strip() if output else ""    return f"{output_str}"


构建Manus这样一个智能体绝非易事。Prompt 的健壮性、复杂任务的规划能力、工具执行错误的处理、多工具协同、以及安全性都是需要持续投入的方向。

未来,我们设想 Manus 能集成更多类型的工具,拥有更强的长期记忆和规划能力,并在更复杂的场景中为用户提供端到端的解决方案。


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询