【架构解析】OpenManus-Prompt模块化设计:如何构建可维护的Agent提示词系统
1. 为什么你的Agent提示词总是“一锅粥”从OpenManus的模块化设计说起不知道你有没有这样的经历花了好几天精心调教出一个能帮你写代码、分析数据的Agent一开始用着还挺顺手。但过了一周产品经理跑来说“我们想给这个Agent加个新功能让它能处理一下客服场景。”你打开那个写满了提示词的Python文件看着里面几百行混杂着角色定义、工具说明、步骤指令的字符串瞬间头大。改这里怕影响那里加新功能又不知道从何下手最后只能硬着头皮复制粘贴结果Agent的行为开始变得诡异回复前言不搭后语。这其实就是典型的“提示词面条式代码”问题。早期的Agent开发大家往往把所有的指令都塞进一个巨大的字符串里或者散落在各个业务逻辑的函数中。这种写法初期跑起来没问题但随着迭代、团队协作、A/B测试需求的增加维护成本会指数级上升。我见过不少项目就是因为提示词管理失控最后不得不推倒重来。OpenManus的Prompt组件正是为了解决这个痛点而生的。它不是什么高深莫测的黑科技其核心思想非常朴素但极其有效把提示词当成代码来管理进行职责分离和模块化设计。简单来说它不再把提示词看作是附属于Agent的“注释”或“配置字符串”而是将其提升为系统的一等公民拥有独立的生命周期和清晰的接口。这种设计让提示词的编写、测试、部署和迭代变得像我们管理函数和类一样清晰可控。想象一下如果你的Agent系统里定义“角色”的提示词、指导“每一步行动”的提示词、以及那些需要动态填充内容的提示词都被分门别类地放在不同的模块里并且通过明确的接口与Agent核心逻辑交互。那么当你想调整Agent的沟通风格时你只需要去修改“角色定义”模块当你想优化它在某个工具上的使用逻辑时你只需要调整对应的“步骤指令”模块。彼此之间影响降到最低团队里不同成员也可以并行修改不同部分的提示词而不用担心冲突。这就是OpenManus-Prompt模块化设计带来的最直接好处可维护性和团队协作效率的大幅提升。接下来我们就一层层剥开它的设计看看具体是怎么实现的。2. 核心模块拆解系统、步骤与模板各司其职OpenManus的提示词体系主要围绕三种核心类型来构建每种类型都有其明确的职责和适用场景这种划分是模块化设计的基石。理解它们就像理解一个项目里models.py、views.py和utils.py的分工一样重要。2.1 系统提示词定下基调的“宪法”我把系统提示词System Prompt比喻成Agent的“宪法”。它是在Agent诞生之初就被赋予的、最根本的行为准则和身份定义在整个会话生命周期内通常保持稳定不会轻易改变。它的核心职责是什么定义角色与身份告诉LLM“你是谁”。比如“你是一个资深的全栈软件工程师擅长Python和系统架构设计。”划定能力与边界明确Agent能做什么不能做什么。例如“你只能使用提供的工具来操作文件系统不能自行想象或执行未授权的命令。”设定行为风格与基调塑造Agent的交互个性。比如“请用简洁、专业的语言回复专注于解决问题避免冗长的客套话。”在OpenManus中系统提示词通常被设计得比较精炼和稳定。它不关心具体的任务细节只关心Agent的“人设”和“基本原则”。在代码层面它通常在Agent类初始化时被设置并作为“系统消息”在每次调用大模型时传递。这种设计的好处是当你需要调整Agent的“性格”或“专业领域”时你只需要修改这一个地方所有基于该Agent的交互都会立即生效无需改动任何任务逻辑。一个典型的应用场景是你有一个“代码评审Agent”。它的系统提示词可能固定为“你是一个严格且注重代码质量的评审专家专注于发现代码中的潜在bug、性能问题和不符合规范的写法。你的反馈应当直接、有建设性并引用相关的编程最佳实践。” 无论用户提交的是Python代码还是JavaScript代码这个根本的评审立场和风格都不会变。2.2 下一步提示词指导行动的“任务清单”如果说系统提示词是宪法那么下一步提示词Next Step Prompt就是一份详细的“任务清单”或“操作手册”。它是动态的随着Agent执行任务的步骤而变化告诉Agent在当前这个具体时刻应该关注什么、有哪些工具可用、以及如何做出决策。它的设计特点包括步骤特异性内容与当前任务阶段强相关。例如在文件操作任务中当Agent需要读取文件时提示词会强调文件路径和读取模式当需要写入时则会强调内容格式和写入权限。工具上下文会明确列出当前步骤下所有可用的工具及其详细的使用方法、参数说明。这相当于把工具说明书直接放在了Agent手边。决策指南提供简单的推理框架或约束条件引导Agent做出合理选择。比如“根据用户的目标优先考虑使用A工具如果失败则回退到B方案。”在OpenManus的调用流程中下一步提示词会在Agent每次执行“思考”think前被动态地添加到用户消息历史中。这意味着Agent的每一步行动都能获得最精准的上下文指导。这种设计将复杂的任务逻辑分解为一系列可管理的步骤每个步骤都有量身定制的提示极大地提高了任务执行的准确性和可控性。举个例子一个“数据分析Agent”在接到“分析销售数据趋势”的任务后它的下一步提示词可能会依次是步骤1“请使用read_csv工具加载./data/sales_q3.csv文件并查看前5行和数据概览。”步骤2“数据已加载。现在请使用calculate_statistics工具计算每月销售额的平均值、中位数和标准差。”步骤3“统计计算完成。请使用plot_trend_chart工具生成销售额随时间变化的折线图。” 你看每一步的指令都非常具体、可操作完全依赖于上一步的结果和当前的状态。2.3 模板提示词灵活应变的“填空题”在实际项目中很多提示词的内容不是静态的需要根据运行时的状态动态生成。这就是模板提示词Template Prompt的用武之地。它本质上是一个带有占位符的字符串模板就像一份待填写的表格。它的核心价值在于“动态化”运行时填充占位符如{working_dir},{file_name},{step_count}会在程序执行过程中被真实的变量值替换。提高复用性一份模板可以适用于多种相似但数据不同的场景避免了编写大量几乎重复的提示词。保持结构一致确保了即使内容变化提示词的整体指令结构和格式仍然是统一的。OpenManus中大量使用了Python的格式化字符串f-string或str.format()方法来实现这一功能。这在需要将环境信息、用户输入、或上一步执行结果嵌入到提示词中的场景下尤其有用。一个经典的例子是SWE Agent软件工程智能体中的工作目录提示。它的下一步提示词可能是一个模板NEXT_STEP_PROMPT_TEMPLATE 你当前的工作目录是{current_dir}。 目录中包含以下文件{file_list}。 请根据用户的请求在此目录下进行代码编写或文件操作。 记住所有文件路径都应该是相对于当前目录的。 在每次思考前Agent会执行pwd命令获取当前目录并列出文件然后将这些值填充到模板中生成一个完全情境化的下一步提示词。这样Agent始终能“知道”自己身在何处避免了操作文件时路径错误的尴尬。3. 从代码看架构提示词如何与Agent优雅共舞理解了理论我们直接上代码看看OpenManus是如何将这些模块化的提示词编织到Agent的生命周期中的。这是最体现其设计精妙的地方。3.1 定义与装配声明式的提示词管理首先OpenManus鼓励一种声明式的提示词定义方式。提示词不再隐藏在函数深处而是作为Agent类的清晰属性。这大大提升了代码的可读性和可配置性。# 首先在专门的prompts模块中定义常量实现关注点分离 # prompts/coding_agent.py SYSTEM_PROMPT 你是一个专业的Python编程助手。你精通Python标准库和常用框架代码风格优雅且符合PEP8规范。你的回答应专注于提供可直接运行的代码或具体的修改建议。 NEXT_STEP_PROMPT_TEMPLATE 用户的问题是{user_query}。 当前代码文件{filename}的内容如下{code_content}请分析代码并提供优化、修复或实现新功能的建议。如果需要修改代码请直接给出完整的代码块。 # 然后在Agent类中引入这些提示词 class CodingAgent(ToolCallAgent): # 像定义类属性一样定义提示词清晰明了 system_prompt: str SYSTEM_PROMPT # 下一步提示词初始化为模板 next_step_prompt_template: str NEXT_STEP_PROMPT_TEMPLATE def __init__(self, **kwargs): super().__init__(**kwargs) # 可以在初始化时根据传入参数微调提示词 if kwargs.get(style) detailed: self.system_prompt 请为你的代码提供详细的注释和解释。这种模式的好处显而易见可维护性所有提示词集中在独立的文件或模块中方便查找和批量修改。可测试性你可以单独对SYSTEM_PROMPT或模板进行单元测试检查其语法和有效性。可继承与覆盖如果你需要创建一个更特殊的“Django专家Agent”你可以直接继承CodingAgent然后只覆盖system_prompt属性添加Django相关的专业知识其他行为全部复用。3.2 思考循环中的动态集成提示词的生命力体现在Agent的“思考-行动”循环中。OpenManus的ToolCallAgent基类提供了标准的集成点。# 这是基类中think方法的核心逻辑简化版 async def think(self) - bool: 执行一次思考基于当前状态和提示词决定下一步行动。 # 1. 准备消息历史将动态生成的下一步提示词加入 messages_for_llm [] # 始终注入系统提示词定义本次调用的“基调” if self.system_prompt: system_msg Message.system_message(self.system_prompt) messages_for_llm.append(system_msg) # 如果有下一步提示词通常是动态生成或填充后的作为用户指令加入 if self.next_step_prompt: user_msg Message.user_message(self.next_step_prompt) messages_for_llm.append(user_msg) # 也会加入之前的对话历史context messages_for_llm.extend(self.message_history) # 2. 调用大模型传入消息、可用工具列表等 response await self.llm.ask_tool( messagesmessages_for_llm, toolsself.available_tools.to_params(), # 将工具转化为LLM能理解的格式 tool_choiceauto, # 或 required, none ) # 3. 解析响应执行工具调用或生成回复... return self._parse_and_act(response)这个流程清晰地展示了两种核心提示词是如何协作的system_prompt在幕后设定规则next_step_prompt在台前指挥具体动作。这种分离使得我们可以独立地优化“角色设定”和“任务指导”比如你可以保持Agent“严谨工程师”的人设不变但不断优化它“如何调试代码”的具体步骤提示。3.3 高级模式动态模板与流程编排对于更复杂的AgentOpenManus的模式展现了强大的灵活性。以SWE Agent为例它需要在执行过程中不断更新自己的工作目录信息。class SWEAgent(CodingAgent): async def think(self) - bool: # 在执行父类的think逻辑前先更新上下文 # 1. 动态获取环境状态 self.working_dir await self.bash.execute(pwd) file_list_result await self.bash.execute(ls -la) # 2. 使用实时数据填充模板生成具体的下一步提示词 self.next_step_prompt self.next_step_prompt_template.format( current_dirself.working_dir, file_listfile_list_result, user_queryself.current_task, # 假设这是当前任务描述 filenameself.current_file # 假设这是当前聚焦的文件 ) # 3. 调用父类的think方法此时next_step_prompt已是情境化的 return await super().think()这种“子类预处理 父类标准执行”的模式是面向对象设计原则的完美体现。它允许不同的Agent子类拥有高度定制化的提示词准备逻辑同时复用核心的思考与行动框架。另一个高级场景是规划流程Planning Flow它会为生成计划这个特定子任务创建一套临时的、高度定制化的提示词包括系统消息和用户消息完全独立于Agent主体使用的提示词。这就像为了完成一个专项任务临时成立一个项目组并制定一套专门的工作章程。4. 实战从零设计一个可维护的客服工单分类Agent光说不练假把式。让我们假设一个场景你需要构建一个能自动处理用户工单并进行初步分类和转派的客服Agent。我们将借鉴OpenManus的模块化思想从头设计它的提示词系统。4.1 第一步拆解职责定义模块首先我们分析这个Agent需要哪些提示词系统提示词定义Agent作为“客服工单预处理专员”的角色、职责和沟通原则。下一步提示词模板根据工单的不同处理阶段如“初次分类”、“信息追问”、“转派建议”动态变化并需要嵌入工单的具体内容。工具说明描述Agent可以调用的工具如classify_ticket分类、query_knowledge_base查知识库、escalate_to_human转人工。我们为它们创建独立的文件结构my_customer_service_agent/ ├── agents/ │ └── ticket_agent.py # Agent主类 ├── prompts/ # 提示词模块目录 │ ├── __init__.py │ ├── system_prompts.py # 存放所有系统提示词 │ └── next_step_templates.py # 存放所有下一步提示词模板 └── tools/ └── ticket_tools.py # 工具定义4.2 第二步编写模块化提示词在prompts/system_prompts.py中TICKET_AGENT_SYSTEM_PROMPT 你是“智能工单预处理系统”。你的职责是 1. **分析用户提交的工单内容**理解其核心问题。 2. **进行初步分类**如技术问题、账单咨询、账号异常、产品建议。 3. **若信息不足**向用户提出清晰、有针对性的追问以获取必要细节。 4. **根据分类和已有知识**提供初步解决方案或明确转派给哪个专业团队。 你的行为准则 - 态度始终专业、友善、耐心。 - 回复简洁、清晰直接针对问题。 - 如果问题复杂或超出你的处理范围应果断建议转人工客服。 - 不要编造信息对于不确定的事情要说明。 在prompts/next_step_templates.py中# 模板1用于初次分类 INITIAL_CLASSIFICATION_PROMPT 请分析以下用户工单内容 【工单内容】 {ticket_content} 【用户信息】 用户ID{user_id} 产品{product} 请执行以下操作 1. 判断问题类型技术/账单/账号/建议/其他。 2. 提取关键实体如订单号、错误代码、功能名称。 3. 评估紧急程度高/中/低。 4. 调用classify_ticket工具传入你的判断结果。 # 模板2用于信息不足时追问 REQUEST_MORE_INFO_PROMPT 根据当前分析要解决用户问题还需要以下关键信息 {missing_info_list} 请向用户提出一个友好、清晰的问题引导用户提供上述信息。你的提问应该 - 一次只问一个最关键的信息点避免让用户感到 overwhelmed。 - 解释为什么需要这个信息。 4.3 第三步在Agent中集成与使用在agents/ticket_agent.py中from prompts.system_prompts import TICKET_AGENT_SYSTEM_PROMPT from prompts.next_step_templates import * class TicketClassificationAgent(ToolCallAgent): system_prompt: str TICKET_AGENT_SYSTEM_PROMPT # 初始提示词设为分类模板 next_step_prompt: str INITIAL_CLASSIFICATION_PROMPT def __init__(self, ticket_data: dict): super().__init__() self.ticket_data ticket_data self.current_step initial_classification async def prepare_next_step(self): 根据当前步骤动态准备下一步提示词 if self.current_step initial_classification: # 填充初次分类模板 self.next_step_prompt INITIAL_CLASSIFICATION_PROMPT.format( ticket_contentself.ticket_data[content], user_idself.ticket_data[user_id], productself.ticket_data[product] ) elif self.current_step request_more_info: # 填充追问模板 missing_info self._analyze_missing_info() # 假设这个方法分析出缺失信息 self.next_step_prompt REQUEST_MORE_INFO_PROMPT.format( missing_info_list\n- .join(missing_info) ) # ... 其他步骤的处理 async def run(self): 主运行循环 while not self.is_finished: await self.prepare_next_step() # 1. 动态准备提示词 await self.think() # 2. 思考并调用工具 self._update_current_step() # 3. 根据结果更新状态通过这样的设计当业务规则变化时比如新增一种工单类型“物流查询”我们只需要在system_prompts.py的分类列表中加上“物流查询”。在next_step_templates.py中可以为这种新类型创建一个专用的处理模板如果需要。在Agent的状态逻辑里添加对新类型的处理分支。 整个过程对核心的Agent思考逻辑think()方法几乎没有影响真正做到了“对修改封闭对扩展开放”。5. 超越OpenManus模块化设计的进阶模式与最佳实践OpenManus的设计给了我们一个优秀的起点但在实际的大型、长期项目中我们还可以在此基础上做更多增强让提示词系统更加健壮和易用。5.1 引入“提示词版本管理”与A/B测试当团队有多人参与提示词优化时版本控制至关重要。我们可以像管理代码一样管理提示词。Git管理将prompts/目录纳入Git仓库。每次对提示词的修改都通过Pull Request进行便于代码审查和追溯历史。配置文件化对于需要频繁调整的参数如温度值、分类标签列表可以将其从提示词字符串中抽离放入YAML或JSON配置文件。这样非开发人员如产品经理也能在一定的安全约束下进行调整。# config/prompt_config.yaml classification: categories: [技术问题, 账单咨询, 账号异常, 产品建议, 物流查询] urgency_levels: [高, 中, 低] system_prompt_tone: 专业且友善 # 可选项专业、亲切、简洁A/B测试框架构建一个简单的实验系统。例如为“初次分类”步骤准备两个不同版本的提示词模板A版更注重效率B版更注重细节。class ExperimentTicketAgent(TicketClassificationAgent): async def prepare_next_step(self): # 根据实验分组如user_id哈希决定使用哪个版本的提示词 experiment_group self._assign_to_group(self.ticket_data[user_id]) if experiment_group A: template INITIAL_CLASSIFICATION_PROMPT_VARIANT_A else: template INITIAL_CLASSIFICATION_PROMPT_VARIANT_B self.next_step_prompt template.format(...) # 记录实验分组和后续结果用于分析 self.log_experiment(experiment_group)5.2 构建“提示词库”与组合复用对于高度复杂的系统可以建立企业级的提示词库Prompt Library。原子化提示词片段将常用的指令、约束、示例拆分成更小的、可复用的片段。# prompts/fragments.py ROLE_DEFINITION_FRAGMENT 你是一个{role}拥有{expertise}领域的专业知识。 RESPONSE_STYLE_FRAGMENT 请用{style}的风格进行回复并{constraint}。 SAFETY_GUARDRAIL_FRAGMENT 你必须遵守以下原则{principles}。绝对不能{prohibited_actions}。组合式构建在运行时或初始化时像搭积木一样组合这些片段生成完整的提示词。def build_system_prompt(role, expertise, style, constraint): return f {ROLE_DEFINITION_FRAGMENT.format(rolerole, expertiseexpertise)} {RESPONSE_STYLE_FRAGMENT.format(stylestyle, constraintconstraint)} {SAFETY_GUARDRAIL_FRAGMENT.format(principles..., prohibited_actions...)} 这种方法特别适合需要快速创建大量垂直领域Agent的场景比如为公司的每个产品线都配置一个客服Agent它们共享相同的安全准则和回复风格片段但角色和专业知识片段不同。5.3 监控、评估与持续迭代一个可维护的系统离不开监控和反馈。对于提示词我们需要建立评估机制。关键指标监控记录每个提示词版本下Agent的任务完成率、工具调用准确率、人工接管率需要人工干预的比例以及用户满意度评分如果有。日志与溯源在日志中不仅记录Agent的输入输出也记录本次会话所使用的具体提示词版本或内容哈希。这样当出现一次糟糕的交互时你能快速定位是哪个提示词出了问题。定期回顾与优化设立“提示词评审会”像代码评审一样定期回顾高频使用或效果不佳的提示词。基于监控数据和实际案例讨论并优化其措辞、结构或包含的信息。我在实践中发现最有效的优化往往来自于真实的失败案例。建立一个渠道让终端用户或测试人员可以方便地标记“Agent回答不佳”的对话这些案例是优化提示词最宝贵的素材。模块化设计使得针对某个具体问题的提示词进行定点优化变得非常容易而不用担心“牵一发而动全身”。说到底OpenManus的Prompt模块化设计其精髓不在于用了多炫酷的技术而在于它引入了一种软件工程的思维来管理提示词。它告诉我们提示词不是魔法咒语而是可设计、可测试、可迭代的软件组件。当你开始用管理代码的方式管理提示词时你会发现构建稳定、可靠且易于演进的Agent系统突然就变成了一条清晰可见的道路。