04 - Prompt 组装策略:七段模块化构建
学习目标
理解 CowAgent 的 PromptBuilder 如何实现七段模块化系统提示词组装,以及动态运行时注入机制。
前置知识
系统提示组装的通用原理见 系统提示组装策略。本章聚焦 CowAgent 的具体实现。
项目实践
七段组装顺序
PromptBuilder 按以下顺序组装系统提示词:
每个 section 是可选的——如果对应的子系统未启用(如无 memory_manager),则该 section 为空。
组装顺序的设计理由
- 工具系统最先:工具是 Agent 的核心能力,LLM 需要最先了解可用工具
- 技能紧跟:技能需要用
read工具读取 SKILL.md,所以技能说明紧跟工具之后 - 记忆与知识:记忆检索和知识查询引导在工具说明之后
- 工作空间:定义路径规则和文件加载说明
- 用户身份:用户信息(可选,通常由 AGENT.md 定义)
- 上下文文件:
AGENT.md/USER.md/RULE.md的全文注入 - 运行时信息:动态时间、模型名称等元信息,每次请求重新计算
动态运行时注入
CowAgent 的关键创新是运行时信息的 callable 机制:
# 伪代码 — 动态时间与模型信息def _get_runtime_info(workspace_root): def get_current_time(): now = datetime.datetime.now() return { 'time': now.strftime("%Y-%m-%d %H:%M:%S"), 'weekday': weekday_zh, 'timezone': timezone_name }
def get_model(): return conf().get("model", "unknown")
return { "_get_current_time": get_current_time, # callable "_get_model": get_model, # callable "workspace": workspace_root, "channel": conf().get("channel_type", "unknown"), }在 _build_runtime_section() 中,如果 runtime_info.get("_get_current_time") 是 callable,则每次构建时调用:
if callable(runtime_info.get("_get_current_time")): time_info = runtime_info["_get_current_time"]() time_line = f"当前时间: {time_info['time']} {time_info['weekday']} ({time_info['timezone']})"这确保了即使 Agent 长时间运行,系统提示中的时间也是准确的,而非启动时的静态快照。
上下文文件加载
load_context_files() 从 workspace 加载以下文件:
| 文件 | 用途 |
|---|---|
AGENT.md | Agent 人格与角色定义(灵魂文件) |
USER.md | 用户身份信息 |
RULE.md | 工作空间使用规则 |
MEMORY.md | 长期记忆索引(已加载到上下文) |
BOOTSTRAP.md | 启动引导信息(可选) |
_build_context_files_section() 会为每个文件生成一个带标题的 section,并标注”已加载”状态,告知 LLM 无需再用 read 工具读取。
缓存与回退
Agent.get_full_system_prompt() 采用”构建失败回退到缓存”的策略:
def get_full_system_prompt(self, skill_filter=None) -> str: try: # 刷新技能、加载上下文文件、构建完整 prompt builder = PromptBuilder(...) return builder.build(...) except Exception as e: logger.warning(f"Failed to rebuild system prompt, using cached version: {e}") return self.system_prompt # 使用缓存版本这确保了即使文件系统操作异常(如 workspace 目录不可读),Agent 仍然可以正常运行。
问题与规避
| 问题 | CowAgent 的规避策略 |
|---|---|
| Prompt 过长超出上下文 | 技能 prompt 截断 + 工具描述精简 + 上下文文件按需加载 |
| 运行时 callable 抛异常 | try/except 回退到静态值 |
| 上下文文件不存在 | load_context_files() 返回空列表,对应 section 为空 |
| 构建失败 | 回退到缓存的 self.system_prompt |
设计取舍
为什么不用 Jinja2 等模板引擎?
PromptBuilder 使用简单的字符串拼接而非模板引擎。原因是系统提示词的结构相对固定(七段),每段的内容由子函数生成,不需要模板的条件判断、循环等复杂逻辑。简单字符串拼接更易调试(直接 print 即可),且零依赖。
为什么 AGENT.md 被称为”灵魂文件”?
在 CowAgent 的设计中,AGENT.md 定义了 Agent 的人格、语气、交流风格。PromptBuilder 注释明确标注:“AGENT.md 是你的灵魂文件”。这与传统编程中”代码是灵魂”不同——CowAgent 的 Agent 行为由自然语言定义,AGENT.md 就是这个自然语言定义的主要载体。