分析 AgentScope HarnessSystem Prompt 的组装、注入和生命周期管理机制。

1. 前言

系统提示词System Prompt)是给大模型底层预设的固定指令,优先级高于用户每一轮提问,相当于提前给 AI 定好身份、规则、能力边界、输出规范,全程生效。

在构建 HarnessAgent 时,指定了一个简单的系统提示词:

        HarnessAgent agent = HarnessAgent.builder()
                .name("harness-demo")
                .description("HarnessAgent Demo")
                .sysPrompt("你是一个中文 AI 助手")
                .model(model)
                .workspace(workspacePath)
                .maxIters(5)
                .build();

在进行对话时,可以看到当前 AI 回复的内容,包含了更强大的能力,说明 AgentScope 框架内部对系统提示词进行了增强处理:

在这里插入图片描述


2. 总体流程

System Prompt 通过 Transformer 链逐层组装,最终注入到 LLM 推理的 SYSTEM 消息中。

HarnessAgent.builder().sysPrompt("你是 AI 助手")
         │
         ▼
    ReActAgent.sysPrompt                   ← 存储基础提示词
         │
         ▼
    seedSystemMsg() 被调用(每次推理前)
         │
         ▼
    applySystemPromptMiddlewares(base, ctx) ← Transformer 链逐层变换
         │
         ├─→ custom MW1.onSystemPrompt()
         ├─→ custom MW2.onSystemPrompt()
         ├─→ WorkspaceContextMiddleware    ← Session / Workspace / AGENTS / MEMORY / knowledge
         ├─→ TaskReminderMiddleware        ← todo_write 使用说明
         ├─→ PlanModeMiddleware            ← Plan Mode 横幅
         └─→ HarnessSkillMiddleware        ← 技能列表 (<available_skills>)
         │
         ▼
    SystemMessage → 注入 reasoning 的消息列表首位

关键设计

  • seedSystemMsg()每轮推理前被调用,所以修改 AGENTS.mdMEMORY.md 立即生效
  • Transformer 链通过反射检测——只有真正重写了 onSystemPrompt 的中间件才参与变换,避免不必要的 block() 调用

3. 基础提示词

3.1 设置方式

HarnessAgent.builder()
    .sysPrompt("你是一个有用的 AI 助手,可以用中文回答问题。")
    .build();

3.2 存储位置

sysPrompt 存储在 ReActAgent 实例中:

// ReActAgent.java
private final String sysPrompt;

// Builder
public Builder sysPrompt(String sysPrompt) {
    this.sysPrompt = sysPrompt;
    return this;
}

4. Transformer 链实现

4.1 源码入口

Agent 每轮调用前,都会通知所有钩子函数:智能体即将启动,其中会调用 seedSystemMsg 方法:

/**
 * 获取初始系统消息,用于在钩子执行前填充至{@link PreCallEvent}。
 *
 * <p>默认实现返回{@code null}。子类(例如{@code ReActAgent})会重写该方法,
 * 根据自身配置的{@code sysPrompt}构建系统消息。
 *
 * @param callScope 调用入口捕获的单次调用作用域(可为{@code null});
 * 子类可通过该参数获取本次调用的{@link RuntimeContext},用于系统提示词中间件,
 * 无需读取共享实例字段
 * @return 初始填充用系统消息,无则返回{@code null}
 */
@Override
protected Msg seedSystemMsg(Object callExectution) {
    RuntimeContext rc =
            callExectution instanceof CallExecution ce ? ce.rc : getRuntimeContext();
    String base = sysPrompt != null ? sysPrompt.trim() : "";
    String prompt = applySystemPromptMiddlewares(base, rc);
    if (prompt == null || prompt.isEmpty()) {
        return null;
    }
    return SystemMessage.builder()
            .name("system")
            .content(TextBlock.builder().text(prompt).build())
            .build();
}

seedSystemMsg 方法中会调用私有的 applySystemPromptMiddlewares 方法,按顺序执行所有系统提示词中间件,对原始系统提示词(sysPrompt)做拦截、修改、增强;若无自定义中间件逻辑,直接返回原提示词,避免不必要的异步阻塞。

// ReActAgent.java:536-569
private String applySystemPromptMiddlewares(String prompt, RuntimeContext ctx) {
    if (middlewares.isEmpty()) {
        return prompt;
    }
    // 通过反射检测哪些 middleware 真正重写了 onSystemPrompt
    boolean hasOverride = false;
    for (MiddlewareBase mw : middlewares) {
        if (mw.getClass().getMethod("onSystemPrompt", Agent.class,
                RuntimeContext.class, String.class).getDeclaringClass()
                != MiddlewareBase.class) {
            hasOverride = true;
            break;
        }
    }
    if (!hasOverride) {
        return prompt;  // 短路:没有任何 middleware 重写,直接返回
    }
    // 从左到右串行接力
    Mono<String> result = Mono.just(prompt);
    for (MiddlewareBase mw : middlewares) {
        result = result.flatMap(p -> mw.onSystemPrompt(this, ctx, p));
    }
    return result.block();
}

applySystemPromptMiddlewares 方法的入参是【基础提示词】、【运行时上下文】:

在这里插入图片描述

4.2 反射检测

通过反射检测哪些 middleware 真正重写了 onSystemPrompt 方法,没有任何 middleware 重写,直接返回:

    // 注释翻译:仅当至少一个中间件重写了onSystemPrompt方法时,才构建响应式异步链路
    // 基类默认实现是直接入参原样返回;该判断是为了避免无意义的block()阻塞调用
    // block()在非阻塞调度器(如Reactor并行调度器)中会抛出异常
    boolean hasOverride = false;
    // 遍历全部中间件
    for (MiddlewareBase mw : middlewares) {
        try {
            // 反射获取 onSystemPrompt(Agent, RuntimeContext, String) 方法
            // getDeclaringClass():获取该方法实际定义的类
            // 如果 != MiddlewareBase,说明子类重写了该方法,存在自定义处理逻辑
            if (mw.getClass()
                            .getMethod(
                                    "onSystemPrompt",
                                    Agent.class,
                                    RuntimeContext.class,
                                    String.class)
                            .getDeclaringClass()
                    != MiddlewareBase.class) {
                hasOverride = true;
                break;
            }
        } catch (NoSuchMethodException ignored) {
            // 极端异常:找不到该方法,视为存在自定义逻辑
            hasOverride = true;
            break;
        }
    }

关键设计:

  • MiddlewareBase 是中间件基类,自带默认 onSystemPrompt,逻辑为直接返回入参 prompt,无任何修改;
  • 用反射判断:中间件子类是否重写了该方法;
  • 如果所有中间件都用基类默认实现 → hasOverride=false,直接返回原始 prompt,不走异步流程;
  • 目的:规避 block() 阻塞,Reactor 异步环境下随意调用 block() 会报错、破坏非阻塞性能。

默认配置下,自动装载了以下中间件:

在这里插入图片描述

4.3 串行执行

当存在重写了 onSystemPrompt 方法的中间件时,会构建异步处理链路,串行执行中间件:

    // 封装原始prompt为响应式Mono流
    Mono<String> result = Mono.just(prompt);
    // 循环串联所有中间件的onSystemPrompt,串行执行
    // flatMap:异步链式调用,上一个中间件输出作为下一个输入
    for (MiddlewareBase mw : middlewares) {
        result = result.flatMap(p -> mw.onSystemPrompt(this, ctx, p));
    }
    // 阻塞等待全部中间件异步处理完成,拿到最终字符串返回
    return result.block();
}

Middleware 列表按注册顺序排列,onSystemPrompt 从左到右串行接力

原始 prompt → mw[0].onSystemPrompt → mw[1].onSystemPrompt → ... → 最终 prompt

自定义 Middleware 跑在最前面,因为 HarnessAgent 先注册用户 Middleware,再注册内置 Middleware


4.4 系统提示词中间件

在所有中间件中,有 5 个重写了 onSystemPrompt 方法:

  • WorkspaceContextMiddleware
  • DynamicSkillMiddleware
  • HarnessSkillMiddleware
  • PlanModeMiddleware
  • TaskReminderMiddleware

在这里插入图片描述

在自动默认装载的 8 个中间件中,只有 2 个:

  • WorkspaceContextMiddleware
  • HarnessSkillMiddleware

4.4.1 WorkspaceContextMiddleware

核心作用:自动向原始系统提示词尾部拼接工作区上下文片段(工作空间路径、Agent 配置、记忆、知识库、会话信息等),同时做 Token 截断控长,避免超出模型上下文窗口。

重写 onSystemPrompt 钩子方法:

@Override
public Mono<String> onSystemPrompt(Agent agent, RuntimeContext ctx, String currentPrompt) {
    // 兜底空上下文:传入ctx为空则使用空白运行时上下文
    RuntimeContext rc = ctx != null ? ctx : RuntimeContext.empty();
    // 组装工作区完整上下文段落
    String section = buildWorkspaceSection(rc);
    // 无上下文内容,直接返回原始提示词,不做修改
    if (section.isEmpty()) {
        return Mono.just(currentPrompt);
    }
    // 原始提示词兜底为空串
    String base = currentPrompt != null ? currentPrompt : "";
    // 换行分隔符:原始文本末尾已有换行则不加,否则补换行,排版整洁
    String separator = base.isEmpty() || base.endsWith("\n") ? "" : "\n";
    // 原始提示词 + 分隔换行 + 工作区上下文段落,返回异步Mono
    return Mono.just(base + separator + section);
}

核心上下文构建逻辑:

  • 读取各类静态资源文件内容
  • Token 预算计算 & 记忆内容截断(关键控长逻辑)
  • 拼装所有模块,生成完整附加段落
private String buildWorkspaceSection(RuntimeContext rc) {
    // 读取工作区 agents.md、memory.md、knowledge.md 内容并去除首尾空白
    String agentsContent = workspaceManager.readAgentsMd(rc).strip();
    String memoryContent = workspaceManager.readMemoryMd(rc).strip();
    String knowledgeContent = workspaceManager.readKnowledgeMd(rc).strip();
    // 获取当前工作空间根路径
    Path workspace = workspaceManager.getWorkspace();
    // 构建会话专属上下文片段
    String sessionContext = buildSessionContextSection(workspace, rc);

    // 封装知识库块文本
    String knowledgeBlock = buildKnowledgeBlock(rc, knowledgeContent, workspace);
    // 扩展附加上下文(自定义全局变量、权限、用户信息等)
    String additionalBlock = buildAdditionalContextBlock(rc);
    // 计算固定占用Token:会话信息、Agent配置、知识库、附加上下文
    int fixedTokens =
            estimateTokens(sessionContext)
                    + estimateTokens(agentsContent)
                    + estimateTokens(knowledgeBlock)
                    + estimateTokens(additionalBlock);
    // 记忆文件单独Token消耗
    int memoryTokens = estimateTokens(memoryContent);
    // 剩余可用Token额度 = 最大上下文Token上限 - 固定内容占用量
    int available = maxContextTokens - fixedTokens;
    // 剩余额度充足,但记忆文本超量 → 截断记忆内容至可用Token上限
    if (available > 0 && memoryTokens > available) {
        memoryContent = truncateToTokenBudget(memoryContent, available);
        // 工作空间描述段落:路径、文件系统信息
        String workspaceParagraph =
                buildWorkspaceParagraph(workspace, workspaceManager.getFilesystem());
        // 整合四大块加载资源:Agent配置、裁剪后记忆、知识库、附加上下文
        String loadedContext =
                buildLoadedContextSection(
                        agentsContent, memoryContent, knowledgeBlock, additionalBlock, rc);
        // 最终拼接:会话上下文 + 固定引导模板 + 工作空间说明 + 所有加载资源
        return assembleSection(
                sessionContext, GUIDANCE_TEMPLATE, workspaceParagraph, loadedContext);
    }

组装结构

## AgentStateStore Context
This is the HarnessAgent. Today's date is <日期>.
My operating system is: <OS>
The workspace directory is: <路径>
The project's temporary directory is: <临时目录>

## Domain Knowledge / Memory Recall / Memory Persistence 引导段
(内置模板,教模型如何使用记忆系统和知识库)

## Workspace
(按 filesystem 模式分支:本机 / 沙箱 / 远端)

## Workspace Files (Injected)
The following files were loaded from your workspace:

<loaded_context>
  <agents_context>
    (AGENTS.md 全文)
  </agents_context>

  <memory_context>
    (MEMORY.md,超出 maxContextTokens 则截断)
  </memory_context>

  <domain_knowledge_context>
    (KNOWLEDGE.md 全文 + knowledge/ 下所有文件路径清单)
  </domain_knowledge_context>

  <soul_md>(additionalContextFile 注入的额外文件)</soul_md>
</loaded_context>

关键参数maxContextTokens 默认 8000,控制 MEMORY.md 的注入预算。

4.4.2 TaskReminderMiddleware

仅在 Builder 未禁用 Task 功能时注册。

onSystemPrompt:注入 todo_write 工具的静态使用说明:

## Task List
You have a `todo_write` tool that maintains a structured task list for this session.
Use it for multi-step work: capture the plan as todos, keep exactly one task
`in_progress`, and update the whole list as you make progress...

onReasoning:每轮推理前注入当前 Todo 列表的 <system-reminder> 块。

4.4.3 PlanModeMiddleware

PLAN 模式:注入只读设计阶段的提示横幅:

<system-reminder>
PLAN MODE is active (read-only). Plan file: plans/PLAN.md
Investigate the problem and draft a plan, but do NOT modify files...
</system-reminder>

BUILD 模式(刚从 PLAN 切出)注入执行提示:

<system-reminder>You have switched from PLAN to BUILD mode; the read-only
restriction is lifted. An approved plan exists at plans/PLAN.md...
</system-reminder>

4.4.4 HarnessSkillMiddleware

注入当前可见技能列表,渲染为 <available_skills> 块:

<available_skills>
- code-reviewer: 代码评审专家
- pdf-extractor: Extract text from PDF documents
</available_skills>

只列 name + descriptionAgent 判断相关后通过 load_skill_through_path 拉取完整指令。

技能来源(低优先级 → 高优先级,重名覆盖):

  1. projectGlobalSkillsDir(Path) — 项目全局
  2. skillRepository(...) — 市场后端(Git / Nacos / MySQL / classpath
  3. workspace/skills/ — 工作区共用
  4. <userId>/skills/ — 用户隔离

4.4.5 SubagentsMiddleware(通过 onReasoning)

不走 onSystemPrompt Transformer 链,而是通过 onReasoning 直接修改 SYSTEM 消息内容——将子 Agent 使用指南 prepend 到消息列表首条 SYSTEM 消息中:

## Subagents
You have access to subagent tools for spawning and coordinating isolated subagents.

### Agent Tools
- agent_spawn / agent_send / agent_list

### Available agent ids
- reviewer: 代码评审专家
- researcher: 技术调研助手

### When to use subagents / When NOT to use...

4.5 最终 System Prompt 结构

经过所有 Transformer 链处理后,一次典型推理的最终 SYSTEM 消息结构如下:

[HarnessAgent.builder().sysPrompt(...)]               ← 基础提示词

[自定义 Middleware 注入]                                ← 如果有

## AgentStateStore Context                             ← WorkspaceContextMiddleware
Today's date is ... My operating system is ...

## Domain Knowledge                                   ← WorkspaceContextMiddleware
## Memory Recall
## Memory Persistence

## Workspace                                          ← WorkspaceContextMiddleware
Project: ... Workspace: ...

## Workspace Files (Injected)                          ← WorkspaceContextMiddleware
<loaded_context>
  <agents_context>AGENTS.md 全文</agents_context>
  <memory_context>MEMORY.md 全文(受预算截断)</memory_context>
  <domain_knowledge_context>KNOWLEDGE.md + 路径清单</domain_knowledge_context>
</loaded_context>

## Task List                                           ← TaskReminderMiddleware
(todo_write 使用说明)

<system-reminder>PLAN MODE is active...</system-reminder>   ← PlanModeMiddleware(如开启)

<available_skills>                                     ← HarnessSkillMiddleware
- skill-a: ...
- skill-b: ...
</available_skills>

中文翻译后:

# 译文
你是一名中文AI助手
## 智能体状态存储上下文
当前为测试套件演示环境,正在初始化对话运行上下文。
今日日期:2026年6月16日,星期二
用户操作系统:Windows 10 版本10.0
工作空间目录:C:\Users\Administrator\.agentscope\workspace\demo-agent
项目临时目录:C:\Users\ADMINI~1\AppData\Local\Temp\
智能体状态存储唯一标识:harness-1781587384200

## 领域知识库
工作目录下的`knowledge/`文件夹存放大量细分参考文档(并非仅有一份汇总文件)。当任务需要技术规范、操作流程、数据结构定义或行业业务事实时,请以此目录作为权威信息来源。
下文`<domain_knowledge_context>`标签内已注入检索该目录所需信息:包含`knowledge/KNOWLEDGE.md`(若文件存在),以及`knowledge/`目录下**全部文件完整路径清单**,请将这份清单视作知识库文件目录索引,用于定位文件。
对于未直接内嵌展示的文件内容,仅按需调用文件读取、内容检索、文件匹配工具获取对应文件;优先精准读取目标文件,禁止一次性加载整个知识库目录全部内容并输出。

## 历史记忆检索规则
在回答涉及过往操作、决策、日期、人物、用户偏好相关问题前:
1. 对`MEMORY.md`与`memory/*.md`执行记忆检索;
2. 读取检索命中的目标内容片段;
回答中如有必要,需标注信息来源:文件路径+行号。

## 记忆持久化规则
系统提供持久化存储文件`MEMORY.md`,出现以下场景需主动更新该文件:
- 用户告知个人偏好、项目背景、业务决策;
- 产生关键执行结果、待办任务事项。
使用文件编辑/写入工具追加简洁条目,**严禁重复写入已有记录**。对话结束时系统也会自动提取对话信息存入记忆。

## 工作空间说明
项目目录(你协助处理的用户代码根目录):E:\TD\icloud\study-spring-ai
AI专属工作空间(存放记忆、会话、能力脚本、运行时数据):C:\Users\Administrator\.agentscope\workspace\demo-agent

### 路径访问安全策略
根目录限定访问:仅允许读取、修改上述两个根目录内的绝对路径文件;若传入超出根目录范围的绝对路径,文件工具将返回安全拦截错误。
相对路径解析逻辑:优先匹配AI工作空间目录,未匹配到文件时自动降级读取项目目录文件,采用覆盖、写时复制机制。
执行Shell命令时,工作目录默认切换至项目根目录。
`AGENTS.md`文件定义AI身份人设与本地开发规范,输出内容需遵守该文件规则,且不得违反安全策略。

## 已加载工作空间文件
下方`<loaded_context>`区块为从工作目录自动加载的文件内容,包含`AGENTS.md`、`MEMORY.md`、`knowledge/KNOWLEDGE.md`等文件,存储长期记忆、业务事实、用户偏好、执行规范,以及历史对话沉淀的用户专属信息。

<loaded_context>
  <agents_context></agents_context>
  <memory_context>
```markdown
# MEMORY.md — 整理后的长期记忆

## 身份与运行上下文
- **助手定位**:面向Spring AI开发人员的记忆整理助手。核心职责:沉淀长效开发知识、去重梳理业务见解、维护跨会话统一高价值参考信息。
- **用户运行环境**
  - 项目根目录:`E:\TD\icloud\study-spring-ai`
  - AI工作空间:`C:\Users\Administrator\.agentscope\workspace\demo-agent`
- **服务支持范围**:聚焦Spring AI相关开发场景,包含:
  - 官方文档解读、源码工程分析(如spring-ai-core、spring-ai-openai模块)
  - 代码实现逻辑、配置写法、单元测试方案
  - 示例代码片段、YAML/Properties配置、测试用例生成
  - 依赖与构建工程排查(Maven/Gradle、版本兼容、依赖冲突问题)
  - `knowledge/`目录结构化知识库整理
  - 通过`MEMORY.md`持续记录开发决策、使用偏好、待办事项

## 时间基准锚点
- 首条记忆记录创建时间:2026年06月16日,星期二

  </memory_context>
  <domain_knowledge_context></domain_knowledge_context>
</loaded_context>


5. 生命周期要点

  1. 每轮推理前重新组装:修改 AGENTS.mdMEMORY.md、技能仓库后,下一次 call() 立即生效,不需要重启 Agent

  2. Transformer 链是同步阻塞的applySystemPromptMiddlewares() 最后调用 result.block(),这意味着所有 onSystemPrompt 中的异步操作必须在返回前完成。实际内置 MiddlewareonSystemPrompt 都是同步返回 Mono.just()

  3. 自定义 Middleware 跑在最前面HarnessAgent 先注册用户 .middleware() 的实例,再注册内置 Middleware。用户的 onSystemPrompt 可以看到最原始的 sysPrompt

  4. SubagentsMiddleware 的特殊性:它不走 onSystemPrompt Transformer 链,而是通过 onReasoning 直接操作消息列表中的 SYSTEM 消息。这是因为子 Agent 信息需要在每轮推理前动态刷新(文件系统可能随时新增子 Agent 声明),而 onSystemPrompt 链只在 seedSystemMsg 时执行一次。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐