# outputKey 深度解析 > 创建日期:2026-05-30 > 相关文件:`AiOpsService.java` > 核心概念:Agent 状态共享机制 --- ## 🎯 outputKey 是什么? **一句话总结**:`outputKey` 是 **Agent 状态共享的关键机制**,就像是一个**共享内存的地址**。 --- ## 📚 核心机制 ``` ┌─────────────────────────────────────────────────────────┐ │ OverAllState │ │ (类似一个全局的 Map) │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Key Value │ │ │ ├────────────────────────────────────────────────────┤ │ │ │ "planner_plan" → Planner 的输出 (AssistantMessage)│ │ │ │ "executor_feedback" → Executor 的输出 (JSON) │ │ │ │ "input" → 最初的任务输入 │ │ │ └────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` --- ## 🔄 工作流程(3 步) ### Step 1: Planner 写入 **代码**: ```java // AiOpsService.java:108 ReactAgent plannerAgent = ReactAgent.builder() .outputKey("planner_plan") // ← 声明:我要写入 "planner_plan" 这个 key .build(); // 执行后,Planner 的输出会自动写入到: // state.put("planner_plan", plannerAgent的输出) ``` --- ### Step 2: Executor 读取 & 写入 **Prompt 中引用**: ```java // AiOpsService.java:147 - Planner 的 Prompt 中 "1. 读取当前输入任务 {input} 以及 Executor 的最近反馈 {executor_feedback}。" // ^^^^^^^^^^^^^^^^^^^^^ // 这是从 state 中读取的! ``` **关键点**:Prompt 中的 `{executor_feedback}` 会被自动替换为: ```java state.get("executor_feedback") ``` **Executor 写入**: ```java // AiOpsService.java:123 ReactAgent executorAgent = ReactAgent.builder() .outputKey("executor_feedback") // ← Executor 写入这个 key .build(); ``` --- ### Step 3: 从 state 中提取最终结果 **代码**: ```java // AiOpsService.java:83 Optional plannerFinalOutput = state.value("planner_plan") .filter(AssistantMessage.class::isInstance) .map(AssistantMessage.class::cast); String reportText = plannerFinalOutput.get().getText(); ``` --- ## ⏱️ 完整时间线示例 ``` 时间线 ───────────────────────────────────────────────────► 1️⃣ Supervisor 启动 state = {} 2️⃣ Supervisor 调用 Planner Planner: "需要查询告警,decision=EXECUTE" state = { "planner_plan": "需要查询告警,decision=EXECUTE" } 3️⃣ Supervisor 读取 decision=EXECUTE,调用 Executor Executor 读取: {planner_plan} = "需要查询告警,decision=EXECUTE" Executor 调用工具: queryPrometheusAlerts() Executor: "查询成功,发现 3 个告警" state = { "planner_plan": "需要查询告警,decision=EXECUTE", "executor_feedback": "查询成功,发现 3 个告警" ← 新增 } 4️⃣ Supervisor 再次调用 Planner(重新规划) Planner 读取: {executor_feedback} = "查询成功,发现 3 个告警" Planner: "需要查询日志,decision=EXECUTE" state = { "planner_plan": "需要查询日志,decision=EXECUTE", ← 更新 "executor_feedback": "查询成功,发现 3 个告警" } 5️⃣ Supervisor 调用 Executor Executor 读取: {planner_plan} = "需要查询日志,decision=EXECUTE" Executor 调用工具: queryLogs() Executor: "查询成功,找到 OOM 日志" state = { "planner_plan": "需要查询日志,decision=EXECUTE", "executor_feedback": "查询成功,找到 OOM 日志" ← 更新 } 6️⃣ Supervisor 再次调用 Planner(最终生成报告) Planner 读取: {executor_feedback} = "查询成功,找到 OOM 日志" Planner: "decision=FINISH,输出完整 Markdown 报告" state = { "planner_plan": "# 告警分析报告\n...", ← 最终报告 "executor_feedback": "查询成功,找到 OOM 日志" } 7️⃣ Supervisor 结束,返回 state 8️⃣ Controller 提取报告 finalReport = state.value("planner_plan") ``` --- ## 🤔 为什么需要 outputKey? | 场景 | 没有 outputKey | 有 outputKey | |------|---------------|--------------| | **Agent 间通信** | 无法传递数据 | ✅ 通过 state 共享 | | **重新规划** | Planner 读不到 Executor 的结果 | ✅ 读取 `{executor_feedback}` | | **最终提取** | 不知道从哪里读取报告 | ✅ `state.value("planner_plan")` | | **调试** | 无法追踪中间状态 | ✅ 可以打印整个 state | --- ## 💻 等价代码理解 如果你熟悉 JavaScript,可以这样理解: ```javascript // 没有 outputKey 的版本(行不通) const plannerOutput = plannerAgent.invoke(input); const executorOutput = executorAgent.invoke(???); // 😱 怎么传递 plannerOutput? // 有 outputKey 的版本 const state = {}; plannerAgent.invoke(input, state); // 写入 state["planner_plan"] executorAgent.invoke(state); // 读取 state["planner_plan"],写入 state["executor_feedback"] plannerAgent.invoke(state); // 读取 state["executor_feedback"],更新 state["planner_plan"] ``` --- ## 📖 Prompt 中的占位符替换 ### 原始 Prompt ```java // AiOpsService.java:147 "1. 读取当前输入任务 {input} 以及 Executor 的最近反馈 {executor_feedback}。" ``` ### 替换后的实际 Prompt(发送给 LLM) ``` 1. 读取当前输入任务 你是企业级 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 | --- ## 🎯 核心洞察 **outputKey 的本质**: 1. **写入地址**:Agent 把输出写入 `state[outputKey]` 2. **读取地址**:Prompt 中的 `{outputKey}` 会被替换为 `state[outputKey]` 3. **共享内存**:所有 Agent 共享同一个 `OverAllState` 对象 **类比**: - `outputKey` 就像文件系统的路径 - `OverAllState` 就像文件系统本身 - Planner 写入 `/planner_plan` - Executor 读取 `/planner_plan`,写入 `/executor_feedback` - Supervisor 协调读写顺序 --- ## 💡 实践建议 ### 1. 命名规范 ```java // ✅ 好的命名(表达角色 + 数据类型) .outputKey("planner_plan") // Planner 的计划 .outputKey("executor_feedback") // Executor 的反馈 .outputKey("reviewer_verdict") // Reviewer 的判决 // ❌ 差的命名 .outputKey("output") // 太泛,不知道谁的输出 .outputKey("data") // 太泛 .outputKey("result1") // 没有语义 ``` ### 2. 在 Prompt 中引用 ```java // Executor 的 Prompt "读取 Planner 最新输出 {planner_plan},只执行其中的第一步。" // ^^^^^^^^^^^^^^^ // 会被自动替换为 state.get("planner_plan") // Planner 的 Prompt (Replanner 角色) "读取 Executor 的最近反馈 {executor_feedback}。" // ^^^^^^^^^^^^^^^^^^^ // 会被自动替换为 state.get("executor_feedback") ``` ### 3. 提取最终结果 ```java // 从 state 中提取 Optional finalOutput = state.value("planner_plan") .filter(AssistantMessage.class::isInstance) .map(AssistantMessage.class::cast); String reportText = finalOutput.get().getText(); ``` --- ## 🔍 调试技巧 在 `AiOpsService.java:70` 的 `invoke` 调用后打印 state: ```java Optional stateOptional = supervisorAgent.invoke(taskPrompt); // 添加调试代码 if (stateOptional.isPresent()) { OverAllState state = stateOptional.get(); logger.debug("Final State Keys: {}", state.keys()); // 打印所有 key logger.debug("Planner Plan: {}", state.value("planner_plan")); logger.debug("Executor Feedback: {}", state.value("executor_feedback")); } ``` 你会看到类似: ``` Final State Keys: [input, planner_plan, executor_feedback] Planner Plan: Optional[AssistantMessage{text="# 告警分析报告..."}] Executor Feedback: Optional[AssistantMessage{text="{"status":"SUCCESS",...}"}] ``` --- ## 📚 相关阅读 - [AI Ops 核心设计 - Essence 报告](./01-AI-Ops-核心设计-Essence报告.md) - [3个核心疑问解答](./03-核心疑问解答.md) --- > 💡 **总结**:outputKey 是 Agent 间通信的桥梁,没有它,3 个 Agent 就无法协同工作。