Agent Scope Java 2.x 系列【20】Harness:系统提示词
文章目录
分析
AgentScope Harness中System 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.md或MEMORY.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 方法:
WorkspaceContextMiddlewareDynamicSkillMiddlewareHarnessSkillMiddlewarePlanModeMiddlewareTaskReminderMiddleware

在自动默认装载的 8 个中间件中,只有 2 个:
WorkspaceContextMiddlewareHarnessSkillMiddleware
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 + description,Agent 判断相关后通过 load_skill_through_path 拉取完整指令。
技能来源(低优先级 → 高优先级,重名覆盖):
projectGlobalSkillsDir(Path)— 项目全局skillRepository(...)— 市场后端(Git/Nacos/MySQL/classpath)workspace/skills/— 工作区共用<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. 生命周期要点
-
每轮推理前重新组装:修改
AGENTS.md、MEMORY.md、技能仓库后,下一次call()立即生效,不需要重启Agent。 -
Transformer 链是同步阻塞的:
applySystemPromptMiddlewares()最后调用result.block(),这意味着所有onSystemPrompt中的异步操作必须在返回前完成。实际内置Middleware的onSystemPrompt都是同步返回Mono.just()。 -
自定义 Middleware 跑在最前面:
HarnessAgent先注册用户.middleware()的实例,再注册内置Middleware。用户的onSystemPrompt可以看到最原始的sysPrompt。 -
SubagentsMiddleware 的特殊性:它不走
onSystemPromptTransformer链,而是通过onReasoning直接操作消息列表中的SYSTEM消息。这是因为子Agent信息需要在每轮推理前动态刷新(文件系统可能随时新增子Agent声明),而onSystemPrompt链只在seedSystemMsg时执行一次。
更多推荐



所有评论(0)