OpenManus框架解析(一)完整 Prompt 组装架构与全链路流程
在 AI Agent 开发中,Prompt 组装是决定 Agent 推理能力、工具调用准确性、场景适配性的核心底层逻辑。很多 Agent 项目看似功能复杂,其核心底层均依靠一套分层、动态、可扩展的 Prompt 组装机制。
本文将基于完整的项目源码逻辑,层层拆解AI Agent 三段式 Prompt 组装架构,涵盖模板定义、动态注入、LLM 格式化全流程,附源码片段、执行时序、数据流图与核心设计思想,帮你彻底吃透 Agent Prompt 运行原理。
一、核心整体架构:三层分层设计
项目将 Prompt 组装拆分为三层独立职责,各层各司其职、解耦协作,兼顾静态模板统一性与动态场景灵活性:
Layer 1:模板定义层(app/prompt/*.py)
静态字符串模板,为每一类 Agent 独立配置专属的系统提示词(SYSTEM_PROMPT)和下一步思考提示词(NEXT_STEP_PROMPT),是所有 Prompt 的基础模板。
Layer 2:Agent 注入层(think() 方法)
核心动态逻辑层,负责读取静态模板、填充动态占位符、将下一步提示词注入对话记忆(memory),实现每轮推理的动态引导。
Layer 3:LLM 格式化层(ask_tool / format_messages)
API 适配层,将项目自定义的 Message 对象转为 OpenAI API 兼容字典格式,专门处理多模态图片、Token 计算、消息格式标准化。
架构极简总结:静态模板定义 → 动态内容注入 → 标准化 API 格式化
二、模板定义层:各类型 Agent 专属 Prompt 模板
项目中不同功能的 Agent 均拥有独立的 Prompt 模板文件,区分系统定位(SYSTEM_PROMPT)和迭代思考引导(NEXT_STEP_PROMPT),部分模板支持动态占位符。所有 Agent 模板配置汇总如下:
|
文件 |
Agent 类型 |
SYSTEM_PROMPT(系统定位) |
NEXT_STEP_PROMPT(下一步引导) |
|---|---|---|---|
|
toolcall.py |
ToolCallAgent |
"You are an agent that can execute tool calls" |
"If you want to stop interaction, use terminate tool" |
|
manus.py |
Manus |
"You are OpenManus, an all-capable AI assistant..." 含 {directory} 动态占位符 |
主动选择工具、拆解任务、解释执行结果 |
|
browser.py |
BrowserAgent |
详细浏览器自动化规则(JSON响应、元素交互、导航、错误处理) |
含 {url_placeholder} 等5个动态占位符 |
|
mcp.py |
MCPAgent |
MCP协议说明、动态工具适配、异常错误处理规则 |
思考并选择最优 MCP 工具执行下一步操作 |
|
swe.py |
SWEAgent |
命令行编程环境专属定位,限制单次仅调用一个工具 |
空字符串(无需额外引导) |
|
planning.py |
PlanningAgent |
结构化任务规划专家定位 |
校验计划完整性,执行规划的下一步任务 |
|
visualization.py |
VisualizationAgent |
数据分析与可视化专属定位,含 {directory} 占位符 |
单次仅选一个工具,遇到报错自动修复重试 |
三、Agent 注入层:think() 核心双路径逻辑
注入层是 Prompt 动态组装的核心,通过 think() 方法实现两条核心路径:系统提示词独立前置、下一步提示词入库迭代,两者逻辑完全隔离。
3.1 System Prompt:永久前置,不参与 Memory 截断
系统提示词用于定义 Agent 核心身份与能力,设计上不存入对话记忆 memory,每次调用 LLM 时单独前置拼接,永久不受 max_messages=100 截断规则影响,保证 Agent 身份定位全程不变。
核心源码(toolcall.py 47-55行):
response = await self.llm.ask_tool(
messages=self.messages, # 对话历史(不含 system prompt)
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,
)
LLM 内部拼接逻辑(llm.py 684-688行):
核心特性:System 消息永远位于消息列表最首位,全程保留,不会被对话历史截断。
3.2 Next Step Prompt:逐轮注入 Memory,引导迭代思考
下一步提示词是每轮推理的引导核心,会以伪用户消息的形式追加到 memory 末尾,强制引导 LLM 基于当前对话状态思考后续操作,实现任务迭代。
核心源码(toolcall.py 41-43行):
if self.next_step_prompt:
user_msg = Message.user_message(self.next_step_prompt)
self.messages += [user_msg] # 追加到 memory 对话历史末尾
执行效果:每一轮 Agent 推理,都会新增一条用户侧引导消息,让 LLM 持续对齐任务目标、推进流程。
四、LLM 格式化层:统一 API 消息格式
format_messages() 是底层适配核心,负责将项目自定义的 Pydantic Message 对象,转为标准 OpenAI API 字典格式,同时专门处理多模态图片资源,兼容图文混合推理。
核心源码(llm.py 267-352行):
@staticmethod
def format_messages(messages, supports_images=False) -> List[dict]:
formatted_messages = []
for message in messages:
# Pydantic 对象转为字典
if isinstance(message, Message):
message = message.to_dict()
# 处理 base64 图片,适配 OpenAI 多模态格式
if supports_images and message.get("base64_image"):
message["content"] = [
{"type": "text", "text": message["content"]},
{"type": "image_url", "image_url": {
"url": f"data:image/jpeg;base64,{message['base64_image']}"
}}
]
del message["base64_image"]
# 过滤有效消息
if "content" in message or "tool_calls" in message:
formatted_messages.append(message)
return formatted_messages
核心能力:自动兼容纯文本/图文多模态场景,统一 API 入参格式,上层业务无需感知底层适配逻辑。
五、动态 Prompt 占位符机制:场景化实时适配
静态模板无法适配动态场景(如浏览器 URL、页面滚动位置、标签页数量),因此项目设计了动态占位符填充机制,以 BrowserAgent、Manus Agent 为典型代表。
5.1 BrowserAgent 动态 Prompt 逻辑
BrowserAgent 每次推理前,会实时获取浏览器状态,填充 5 个专属占位符,同时支持截图注入多模态消息。
think() 主流程源码(browser.py 120-125行):
async def think(self) -> bool:
self.next_step_prompt = (
await self.browser_context_helper.format_next_step_prompt()
)
return await super().think()
占位符填充核心逻辑(browser.py 47-79行):
async def format_next_step_prompt(self) -> str:
# 1. 获取浏览器实时状态
browser_state = await self.get_browser_state()
# 2. 提取状态信息,组装占位符内容
url_info = f"URL: {browser_state.url}, Title: {browser_state.title}"
tabs_info = f"{len(tabs)} tab(s) available"
content_above_info = f"({pixels_above} pixels)" if pixels_above else ""
content_below_info = f"({pixels_below} pixels)" if pixels_below else ""
# 3. 截图注入:将浏览器截图转为多模态用户消息
if self._current_base64_image:
image_message = Message.user_message(
content="Current browser screenshot:",
base64_image=self._current_base64_image,
)
self.agent.memory.add_message(image_message)
# 4. 批量替换模板占位符
return NEXT_STEP_PROMPT.format(
url_placeholder=url_info,
tabs_placeholder=tabs_info,
content_above_placeholder=content_above_info,
content_below_placeholder=content_below_info,
results_placeholder=results_info,
)
5.2 Manus Agent 动态切换 Prompt
Manus 作为通用 Agent,支持场景自适应 Prompt 切换:检测到使用浏览器工具时,自动切换为浏览器专属动态 Prompt,执行完成后恢复默认模板。
核心源码(manus.py 140-165行):
async def think(self) -> bool:
# 检测最近消息是否调用浏览器工具
browser_in_use = any(
tc.function.name == BrowserUseTool().name
for msg in recent_messages if msg.tool_calls
for tc in msg.tool_calls
)
# 浏览器场景动态替换Prompt
if browser_in_use:
self.next_step_prompt = (
await self.browser_context_helper.format_next_step_prompt()
)
result = await super().think()
# 执行后恢复默认Prompt
self.next_step_prompt = original_prompt
return result
六、完整 Prompt 组装时序案例
以 Manus Agent 执行「搜索 Python 最新版本」 任务为例,完整还原两轮推理的 Prompt 组装、消息入库、API 拼接全流程。
初始状态
Memory.messages = []
Step 1:第一轮推理
1、think() 注入下一步引导 Prompt,memory 新增伪用户消息:
Memory.messages = [
Message(role=user, content="Based on user needs, proactively select...")
]
2、ask_tool 完成最终消息拼接(系统消息前置):
# 前置系统消息
Message(role=system, content="You are OpenManus, an all-capable AI assistant...")
# 后续对话历史
Message(role=user, content="Based on user needs..")
# 附带全局工具列表
tools: [{name:"python_execute",...}, {name:"browser_use",...}, ...]
3、LLM 返回工具调用结果,memory 存入助手消息:
Message(role=assistant, content="我来搜索一下", tool_calls=[{name:"web_search", args:{query:"Python latest version"}}])
4、act() 执行工具,存入工具返回结果消息,第一轮结束:
Memory.messages = [
Message(role=user, content="Based on user needs..."),
Message(role=assistant, content="我来搜索一下", tool_calls=[...]),
Message(role=tool, content="Observed output of cmd `web_search`...", tool_call_id="call_1")
]
Step 2:第二轮推理
1、再次执行 think(),注入新的下一步 Prompt:
Memory.messages = [
# 上一轮历史消息
Message(role=user, content="Based on user needs..."),
Message(role=assistant, content="我来搜索一下", tool_calls=[...]),
Message(role=tool, content="Observed output..."),
# 本轮新增引导消息
Message(role=user, content="Based on user needs...")
]
2、最终 API 消息结构:系统消息永久前置,完整拼接历史对话
七、全链路数据流总图
为方便整体理解,梳理从模板定义到 API 调用的全链路闭环流程:
① 模板定义:app/prompt/ 下定义静态 SYSTEM、NEXT_STEP 模板,预留动态占位符
② Agent 初始化:实例化 Agent,填充固定占位符(如 workspace 目录),绑定 Prompt 模板
③ 每轮 think() 执行:动态替换场景占位符、注入下一步引导 Prompt、追加截图多模态消息
④ LLM 格式化组装:系统消息前置、历史消息标准化、图片格式转换、Token 校验、挂载工具列表
⑤ API 调用:组装完成的标准消息体、工具参数传入 OpenAI 接口,获取推理结果
八、核心设计决策与底层原理
这套 Prompt 架构的所有设计,均为解决 Agent 推理稳定性、场景适配性、可扩展性问题,核心设计决策汇总:
|
设计点 |
具体实现方式 |
核心设计原因 |
|---|---|---|
|
System Prompt 不入库 Memory |
单独传递 system_msgs,调用时前置拼接 |
不受 max_messages 截断影响,永久保留 Agent 身份定位 |
|
Next Step Prompt 逐轮入库 |
每轮以伪 user 消息追加至对话历史 |
贴合 LLM 对话交互逻辑,精准引导迭代思考 |
|
原生 str.format() 占位符 |
自定义 {key} 占位符,代码动态填充 |
无需引入 Jinja2 等模板引擎,轻量化、低依赖、可读性强 |
|
截图独立多模态传递 |
base64 字段单独存储,格式化时转为标准图片格式 |
不污染文本对话历史,最大化利用多模态模型能力 |
|
工具列表动态生成 |
每次 think() 调用 to_params() 重新生成工具参数 |
适配 MCPAgent 动态增减工具的场景,保证工具列表实时准确 |
|
无复杂模板语法 |
纯字符串 + format 替换实现所有动态能力 |
降低项目复杂度,便于 Prompt 调试、修改、维护 |
九、Memory 最终消息形态(实战落地)
一次完整的 Manus 任务执行后,Memory 中真实存储的对话序列(无 System 消息,仅存交互历史):
|
索引 |
角色 |
内容说明 |
消息来源 |
|---|---|---|---|
|
0 |
user |
帮我搜索 Python 最新版本 |
用户原始输入 |
|
1 |
user |
Based on user needs, proactively... |
第一轮 Next Step 注入 |
|
2 |
assistant |
我来搜索 Python 最新版本 + 工具调用 |
第一轮 LLM 推理返回 |
|
3 |
tool |
网页搜索结果数据 |
第一轮工具执行返回 |
|
4 |
user |
Based on user needs, proactively... |
第二轮 Next Step 注入 |
|
5 |
assistant |
输出结果 + terminate 结束工具 |
第二轮 LLM 推理返回 |
|
6 |
tool |
任务结束提示 |
第二轮工具执行返回 |
十、总结
这套三层 Prompt 组装架构,是 AI Agent 实现智能化、动态化、场景化推理的核心基石:
1、分层解耦:静态模板、动态注入、API 格式化三层分离,各司其职;
2、设计精巧:系统提示永久常驻、迭代提示逐轮更新,兼顾稳定性与灵活性;
3、场景适配:支持动态占位符、多模态图文、场景自适应 Prompt 切换;
4、轻量化可维护:无冗余模板依赖,逻辑清晰,便于迭代优化。
理解这套机制,就能彻底掌握 AI Agent 「如何思考、如何调用工具、如何迭代任务」的底层核心逻辑。
更多推荐



所有评论(0)