1. 从 PEV 循环到多智能体沙盒:为什么配置骨架比模型本身更关键
Manus 这类通用 AI Agent 之所以能把一句模糊指令跑成一份可交付成果,核心不在某个单点模型,而在于 Planning-Execution-Verification 三层闭环加上多智能体沙盒协作。很多开发者第一次接触时会把注意力全放在“用哪个大模型”上,结果复现时发现任务跑两步就断、工具调用报错、上下文爆炸。我实测下来,真正卡住复现进度的往往是配置骨架没搭对:规划层拿不到工具清单、执行层沙盒没隔离、验证层没有回环入口。
这篇面向想复现 Manus 运行链路的开发者,把 PEV 循环拆成可复制的config.toml与settings.json骨架,并演示用统一 Key/API 通道接入沙盒执行器的验证动作。适合已经写过基础 Agent demo、想进一步理解多智能体协作与沙盒执行器如何串起来的人。读完后你能拿到一套能跑通的最小配置,知道每个字段对应 PEV 的哪一层,以及出错时先查哪里。
Manus 的 PEV 架构可以这样理解:Planning 层是“大脑”,负责意图识别、任务分解、依赖图构建;Execution 层是“双手”,通过工具注册中心调用 300+ 工具,全部跑在 Docker 沙盒里;Verification 层是“质检员”,做事实检查、逻辑一致性、任务完成度评估,不达标就生成修正计划回灌给规划层。多智能体则是在这三层之上做专业化分工——搜索 Agent、代码 Agent、浏览器 Agent、文档 Agent 各管一摊,通过协调者 Agent 用角色扮演机制推进任务。
复现时最容易忽略的是:PEV 不是三个独立模块,而是一个带反馈边的有向图。验证层的输出必须能回到规划层,否则就退化成一次性流水线。下面的配置骨架会把这个回环显式写出来。
2. TaoToken 前置:统一 Key/API 通道在 PEV 里的位置
在复现 Manus 链路时,模型调用会散落在三个地方:规划层做意图分类和任务分解、执行层的代码 Agent 生成脚本、验证层做事实与逻辑检查。如果每个 Agent 各自维护一套 Key 和 endpoint,配置会迅速失控。我的做法是用统一 Key/API 通道把模型调用收敛到一个入口,这样 PEV 三层共享同一套鉴权,切换模型时只改一处。
TaoToken 在这里扮演的就是这个统一通道。它的 API 地址是https://taotoken.net/api,兼容常见的 OpenAI 风格调用格式,规划层、执行层、验证层都可以指向同一个 base_url。你需要先在控制台创建 API Key,然后把它写进下面的配置文件。控制台入口在https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite,API Keys 管理页在https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite。
注意:Key 只写在环境变量或本地配置文件里,不要硬编码进会提交到仓库的代码。沙盒容器通过环境变量注入,避免 Key 随镜像层泄漏。
如果你还没决定用哪个模型跑规划层,可以先用模型对话页做一次意图分类的快速验证,地址是https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite。确认分类效果后再写进配置,能省掉一轮调试。
对于长期跑编码类 Agent 的场景,Coding Plan 更适合,入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,里面有完整的请求示例和参数说明。
3. 可复制配置:config.toml 与 settings.json 骨架
下面这套配置把 PEV 三层、多智能体角色、沙盒执行器都映射成字段。先看config.toml,它管的是运行时行为——模型通道、沙盒资源、验证阈值。
# config.toml —— PEV 运行时配置骨架 [llm] # 统一 Key/API 通道,PEV 三层共享 base_url = "https://taotoken.net/api" api_key = "${TAOTOKEN_API_KEY}" # 从环境变量注入 planner_model = "gpt-4o" # 规划层:意图分类 + 任务分解 executor_model = "deepseek-coder" # 执行层:代码生成 verifier_model = "gpt-4o" # 验证层:事实/逻辑检查 timeout_seconds = 60 max_retries = 3 [pev] # PEV 回环控制 enable_verification_loop = true max_correction_rounds = 3 # 验证不通过时最多回灌几次 quality_threshold = 0.8 # 综合评分低于此值触发修正 [pev.planning] intent_labels = ["search", "coding", "analysis", "document", "browser"] decompose_strategy = "hierarchical" # 层次化任务分解 build_dependency_graph = true [pev.execution] tool_registry_path = "./tools/registry.json" sandbox_enabled = true sandbox_image = "python:3.11-slim" sandbox_memory_limit = "512m" sandbox_cpu_quota = 50000 subtask_timeout = 120 [pev.verification] fact_check = true logic_check = true completion_check = true reference_sources = ["internal_kb", "web_search"] [agents] # 多智能体角色分工 coordinator = { model = "gpt-4o", role = "project_manager" } search_agent = { model = "gpt-4o", tools = ["web_search"] } coding_agent = { model = "deepseek-coder", tools = ["python_executor"] } browser_agent = { model = "gpt-4o", tools = ["playwright"] } document_agent = { model = "gpt-4o", tools = ["file_processor"] }再看settings.json,它管的是工具注册和沙盒会话参数,和执行层直接相关。
{ "tool_registry": { "python_executor": { "class": "tools.PythonExecutor", "sandbox_required": true, "timeout": 120, "retry_on": ["TimeoutError", "ConnectionError"] }, "web_search": { "class": "tools.WebSearchTool", "sandbox_required": false, "timeout": 30 }, "playwright": { "class": "tools.BrowserAutomation", "sandbox_required": true, "headless": true, "timeout": 90 }, "file_processor": { "class": "tools.FileProcessor", "sandbox_required": true, "supported_formats": ["pdf", "xlsx", "docx", "csv"] } }, "sandbox": { "session_ttl": 600, "mount_workspace": true, "workspace_path": "/workspace", "network_policy": "restricted", "env_passthrough": ["TAOTOKEN_API_KEY"] }, "memory": { "short_term": "sandbox_filesystem", "long_term": "vector_store", "checkpoint_interval": 30 } }这两个文件的分工要记清楚:config.toml决定 PEV 怎么循环、用哪个模型、阈值多少;settings.json决定执行层有哪些工具可用、沙盒怎么起。改模型只动 toml,加工具只动 json,互不干扰。
4. 验证请求:用统一通道接入沙盒执行器并跑通一次 PEV 回环
配置写好后,先别急着跑完整任务,用最小验证动作确认三件事:统一通道能通、沙盒能起、PEV 回环能触发。下面这段 Python 脚本把这三步串起来。
import os import json import tomllib from openai import OpenAI # 1. 读取配置 with open("config.toml", "rb") as f: config = tomllib.load(f) client = OpenAI( base_url=config["llm"]["base_url"], api_key=os.environ["TAOTOKEN_API_KEY"], ) # 2. 规划层:意图分类 + 任务分解 def plan(user_input: str) -> dict: resp = client.chat.completions.create( model=config["llm"]["planner_model"], messages=[ {"role": "system", "content": "你是规划层,把用户指令分解为子任务,返回 JSON。"}, {"role": "user", "content": user_input}, ], response_format={"type": "json_object"}, ) return json.loads(resp.choices[0].message.content) # 3. 执行层:在沙盒里跑代码 def execute_in_sandbox(code: str) -> str: import docker cli = docker.from_env() container = cli.containers.run( config["pev"]["execution"]["sandbox_image"], command=["python", "-c", code], mem_limit=config["pev"]["execution"]["sandbox_memory_limit"], network_disabled=True, remove=True, environment={"TAOTOKEN_API_KEY": os.environ["TAOTOKEN_API_KEY"]}, ) return container.decode("utf-8") # 4. 验证层:检查执行结果 def verify(task: str, result: str) -> dict: resp = client.chat.completions.create( model=config["llm"]["verifier_model"], messages=[ {"role": "system", "content": "你是验证层,检查结果是否满足任务,返回 JSON 含 score 和 passed。"}, {"role": "user", "content": f"任务:{task}\n结果:{result}"}, ], response_format={"type": "json_object"}, ) return json.loads(resp.choices[0].message.content) # 5. 跑一次 PEV 回环 if __name__ == "__main__": task = "计算 1 到 100 的和并输出结果" plan_result = plan(task) print("规划层输出:", plan_result) code = "print(sum(range(1, 101)))" exec_result = execute_in_sandbox(code) print("执行层输出:", exec_result) verify_result = verify(task, exec_result) print("验证层输出:", verify_result) if not verify_result.get("passed"): print("触发修正回环,重新规划...")跑通后你会看到三段输出:规划层返回子任务 JSON,执行层在隔离容器里打印5050,验证层返回{"score": 0.95, "passed": true}。如果验证层返回passed: false,脚本会打印触发修正回环的提示——这就是 PEV 区别于一次性流水线的关键。
沙盒执行器这里用的是network_disabled=True,因为纯计算任务不需要联网。如果子任务要搜索,把network_policy改成restricted并只放行必要域名,别直接开全通。
5. 本篇常见错排查
复现 PEV + 多智能体沙盒时,报错集中在几个固定位置。下面按现象倒查。
报错一:openai.AuthenticationError: Invalid API key先确认TAOTOKEN_API_KEY环境变量在沙盒内外都注入了。沙盒容器默认不继承宿主环境变量,必须在settings.json的env_passthrough里显式列出,或者像上面脚本那样在containers.run时用environment传进去。另一个常见原因是 Key 前后带了空格或换行,从控制台复制时容易带上。
报错二:docker.errors.DockerException: Error while fetching server API versionDocker 守护进程没起,或者当前用户不在 docker 组。Linux 下执行sudo systemctl start docker并sudo usermod -aG docker $USER后重新登录。Windows/macOS 确认 Docker Desktop 在运行。
报错三:规划层返回的子任务 JSON 解析失败response_format={"type": "json_object"}要求提示里必须出现 "JSON" 字样,否则部分模型会返回纯文本。另外规划层提示词里要明确字段名,比如id、description、tool_type、parameters,否则执行层拿不到tool_type就无法从注册中心取工具。
报错四:执行层ValueError: Tool type 'xxx' not foundsettings.json里注册的工具名和规划层输出的tool_type不一致。检查tool_registry的 key 是否和子任务里的tool_type完全匹配,大小写敏感。加工具时记得同步更新config.toml里对应 Agent 的tools列表。
报错五:验证层一直返回passed: false,修正回环停不下来max_correction_rounds设太大加上quality_threshold设太高,会导致无限回灌。先把阈值降到 0.7 观察,确认验证层评分逻辑正常后再调回 0.8。另外验证层的提示词要给出明确的评分维度,否则模型可能给出模糊判断。
报错六:沙盒容器内存溢出被 killmem_limit设太小,或者代码里有死循环。把sandbox_memory_limit调到1g试,同时在执行层加subtask_timeout,超时主动中断。生产环境建议给每个子任务单独起容器,跑完即销毁,别复用。
报错七:多智能体之间上下文串味协调者 Agent 和专业 Agent 共享了同一个上下文窗口,导致信息过载。按 Manus 的做法做上下文隔离,每个专业 Agent 维护独立窗口,只通过结构化消息传递必要信息。settings.json里的memory.short_term设为sandbox_filesystem,把中间结果落盘而不是全塞进上下文。
6. 把原理拆成能跑通的配置,下一步往哪走
PEV 循环和多智能体沙盒的复现难点从来不是某个算法,而是配置骨架有没有把三层的边界划清楚。规划层只管分解和依赖图,执行层只管工具调用和沙盒隔离,验证层只管评分和回灌,三者通过统一 Key/API 通道共享模型能力,通过config.toml和settings.json解耦运行时行为与工具注册。
如果你已经跑通上面的最小回环,下一步可以往两个方向扩展:一是给执行层加更多工具,比如把浏览器自动化和文件处理接进tool_registry,观察规划层如何根据工具清单调整分解策略;二是把验证层的修正计划真正回灌给规划层,跑一个需要多轮修正的复杂任务,看max_correction_rounds和quality_threshold怎么影响最终质量。
长期跑编码类 Agent 的话,Coding Plan 的额度模型更适合高频调用场景,入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。接入过程中遇到鉴权或参数问题,先翻接入文档https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,大部分报错在里面都有对应说明。需要新建或轮换 Key 时走 API Keys 页https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite。
最后留一个我踩过的坑:沙盒里跑代码生成任务时,别让执行层直接执行模型返回的原始字符串,先做一次语法检查再进容器。模型偶尔会返回带 markdown 代码块标记的内容,直接python -c会报SyntaxError,加一步code.strip().strip("")` 清洗能省很多调试时间。