8.4 KiB
8.4 KiB
3个核心疑问解答
创建日期:2026-05-30
主题:Prompt 占位符、outputKey 冲突、多 key 读取
相关文件:AiOpsService.java
❓ 疑问 1:Prompt 中的 {} 占位符如何替换?
机制
Spring AI Agent Framework 的模板引擎自动替换
示例
原始 Prompt:
// AiOpsService.java:147
"读取当前输入任务 {input} 以及 Executor 的最近反馈 {executor_feedback}。"
执行时的替换过程:
1. Agent Framework 扫描 Prompt 中的 {} 占位符
2. 从 OverAllState 中查找对应的 key
3. 替换为实际值
实际发送给 LLM 的 Prompt:
读取当前输入任务 你是企业级 SRE,接到了自动化告警排查任务... 以及 Executor 的最近反馈 {"status":"SUCCESS","summary":"查询成功,发现3个告警"}。
替换规则
| 占位符 | 查找位置 | 值来源 |
|---|---|---|
{input} |
state.value("input") |
Supervisor 初始调用时的 taskPrompt |
{planner_plan} |
state.value("planner_plan") |
Planner Agent 的 outputKey |
{executor_feedback} |
state.value("executor_feedback") |
Executor Agent 的 outputKey |
等价代码(简化版)
// 如果你想看替换后的实际 Prompt,可以在 Agent 执行前打印:
ReactAgent plannerAgent = buildPlannerAgent(chatModel, toolCallbacks);
// 内部会做类似这样的事情(简化版):
String prompt = buildPlannerPrompt(); // 含 {executor_feedback}
String actualPrompt = prompt.replace(
"{executor_feedback}",
state.get("executor_feedback").toString()
);
// 然后发送给 LLM
代码证据
Planner Prompt 中引用 2 个 key:
// AiOpsService.java:147
"1. 读取当前输入任务 {input} 以及 Executor 的最近反馈 {executor_feedback}。"
// ^^^^^^^ ^^^^^^^^^^^^^^^^^^^
// 第1个key 第2个key
Executor Prompt 中引用 1 个 key:
// AiOpsService.java:243
"你是 Executor Agent,负责读取 Planner 最新输出 {planner_plan},只执行其中的第一步。"
// ^^^^^^^^^^^^^^^
// 从 state 读取
❓ 疑问 2:如果两个 Agent 用同一个 outputKey 会怎样?
后果
后执行的 Agent 会覆盖先执行的 Agent 的输出 ⚠️
错误示例
// ❌ 错误示例
ReactAgent agent1 = ReactAgent.builder()
.name("agent1")
.outputKey("shared_key") // ← 相同的 key
.build();
ReactAgent agent2 = ReactAgent.builder()
.name("agent2")
.outputKey("shared_key") // ← 相同的 key
.build();
// 执行顺序:
// 1. agent1.invoke() → state["shared_key"] = "agent1的输出"
// 2. agent2.invoke() → state["shared_key"] = "agent2的输出" (覆盖!)
//
// 最终结果:agent1 的输出丢失了!
正确做法
// ✅ 正确示例
ReactAgent agent1 = ReactAgent.builder()
.name("agent1")
.outputKey("agent1_output") // ← 不同的 key
.build();
ReactAgent agent2 = ReactAgent.builder()
.name("agent2")
.outputKey("agent2_output") // ← 不同的 key
.build();
// 执行后:
// state["agent1_output"] = "agent1的输出"
// state["agent2_output"] = "agent2的输出"
// 两者都保留!
实际案例
在 AiOpsService.java 中:
- Planner 用
"planner_plan"(第 108 行) - Executor 用
"executor_feedback"(第 123 行) - 绝对不能重复,否则 Supervisor 无法正确调度
代码证据:
// AiOpsService.java:100-109
ReactAgent plannerAgent = ReactAgent.builder()
.name("planner_agent")
.outputKey("planner_plan") // ← Planner 的 key
.build();
// AiOpsService.java:115-124
ReactAgent executorAgent = ReactAgent.builder()
.name("executor_agent")
.outputKey("executor_feedback") // ← Executor 的 key(不同)
.build();
调试技巧
如果怀疑 outputKey 冲突,可以在 Supervisor 调用后打印 state:
Optional<OverAllState> stateOptional = supervisorAgent.invoke(taskPrompt);
if (stateOptional.isPresent()) {
OverAllState state = stateOptional.get();
logger.debug("State keys: {}", state.keys()); // 查看有哪些 key
// 检查是否有意外覆盖
state.keys().forEach(key -> {
logger.debug("{} = {}", key, state.value(key));
});
}
❓ 疑问 3:如何在 Prompt 中读取多个 key?
答案
直接在 Prompt 中使用多个 {} 占位符即可
示例:读取 3 个 key
// 示例:Planner 需要读取 3 个 key
private String buildPlannerPrompt() {
return """
你是 Planner Agent,负责:
1. 读取用户任务:{input}
2. 读取 Executor 的反馈:{executor_feedback}
3. 读取历史分析记录:{history}
根据以上信息,制定下一步计划...
""";
}
// 执行时自动替换为:
// 1. 读取用户任务:你是企业级 SRE,接到了...
// 2. 读取 Executor 的反馈:{"status":"SUCCESS"...}
// 3. 读取历史分析记录:[上一次分析的内容]
实际应用
在 AiOpsService.java:147 中,Planner 的 Prompt 就读取了 2 个 key:
"1. 读取当前输入任务 {input} 以及 Executor 的最近反馈 {executor_feedback}。"
// ^^^^^^^ ^^^^^^^^^^^^^^^^^^^
// 第1个key 第2个key
替换后:
1. 读取当前输入任务 [taskPrompt的内容] 以及 Executor 的最近反馈 [executor的JSON反馈]。
高级技巧:条件读取(模板引擎语法)
如果某个 key 可能不存在,可以在 Prompt 中加判断逻辑:
private String buildPlannerPrompt() {
return """
你是 Planner Agent,负责:
1. 读取用户任务:{input}
{% if executor_feedback %}
2. 参考 Executor 的反馈:{executor_feedback}
{% else %}
2. 这是第一次规划,没有反馈
{% endif %}
""";
}
注意:Spring AI Agent Framework 使用的模板引擎(可能是 Freemarker 或 Velocity),具体语法细节需要查阅官方文档。
代码证据
Executor Prompt 读取 1 个 key:
// AiOpsService.java:243
"你是 Executor Agent,负责读取 Planner 最新输出 {planner_plan},只执行其中的第一步。"
// ^^^^^^^^^^^^^^^
// 读取 Planner 的输出
Planner Prompt 读取 2 个 key:
// AiOpsService.java:147
"1. 读取当前输入任务 {input} 以及 Executor 的最近反馈 {executor_feedback}。"
// ^^^^^^^ ^^^^^^^^^^^^^^^^^^^
// key1 key2
🎯 总结
| 疑问 | 核心答案 | 关键点 |
|---|---|---|
| 1. Prompt 占位符如何替换? | Spring AI 自动从 state 中读取 |
{key} → state.get("key") |
| 2. 两个 Agent 用同一个 outputKey? | 后者覆盖前者,数据丢失 | 必须保证 outputKey 唯一 |
| 3. 如何读取多个 key? | 直接用多个 {} 占位符 |
无数量限制,按需引用 |
📊 快速参考表
Prompt 占位符替换规则
| 占位符 | 替换为 | 代码位置 |
|---|---|---|
{input} |
state.value("input") |
Supervisor.invoke(taskPrompt) |
{planner_plan} |
state.value("planner_plan") |
Planner outputKey |
{executor_feedback} |
state.value("executor_feedback") |
Executor outputKey |
outputKey 命名规范
| 风格 | 示例 | 推荐度 |
|---|---|---|
<角色>_<数据类型> |
planner_plan, executor_feedback |
⭐️⭐️⭐️ 推荐 |
<角色>_output |
agent1_output, agent2_output |
⭐️⭐️ 可用 |
<数据类型> |
plan, feedback, result |
⭐️ 不推荐(易冲突) |
| 泛化命名 | output, data, result1 |
❌ 避免 |
🔗 相关文档
💡 下一步:尝试在自己的项目中实现一个简单的 2-Agent 协同(Planner + Executor),验证这些机制。