Files
SuperBizAgent-java/docs/learning/03-核心疑问解答.md
T
2026-05-31 21:45:14 +08:00

8.4 KiB
Raw Blame History

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),验证这些机制。