跳转到内容

04 - Prompt 组装策略:七段模块化构建

学习目标

理解 CowAgent 的 PromptBuilder 如何实现七段模块化系统提示词组装,以及动态运行时注入机制。

前置知识

系统提示组装的通用原理见 系统提示组装策略。本章聚焦 CowAgent 的具体实现。

项目实践

七段组装顺序

PromptBuilder 按以下顺序组装系统提示词:

每个 section 是可选的——如果对应的子系统未启用(如无 memory_manager),则该 section 为空。

组装顺序的设计理由

  1. 工具系统最先:工具是 Agent 的核心能力,LLM 需要最先了解可用工具
  2. 技能紧跟:技能需要用 read 工具读取 SKILL.md,所以技能说明紧跟工具之后
  3. 记忆与知识:记忆检索和知识查询引导在工具说明之后
  4. 工作空间:定义路径规则和文件加载说明
  5. 用户身份:用户信息(可选,通常由 AGENT.md 定义)
  6. 上下文文件AGENT.md / USER.md / RULE.md 的全文注入
  7. 运行时信息:动态时间、模型名称等元信息,每次请求重新计算

动态运行时注入

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.mdAgent 人格与角色定义(灵魂文件)
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 就是这个自然语言定义的主要载体。

参考来源