运维场景里有一类痛点反复出现——AI 工作流跑完一步就把前一步的结论忘了。排查告警时,第一步已经定位到异常主机,第二步却要从头再问一遍;执行修复时,第三步不知道第二步改了什么配置。每次节点之间只能靠硬编码的参数传递,遇到分支和循环就更棘手。
Blueking Lite 本周更新的 记忆节点(Memory Node) 正是为了解决这个问题:在工作流中显式地读写持久化记忆,让后续节点能拿到前序节点的结论、中间状态甚至历史执行记录,从而编排出真正确定性的运维场景。
记忆节点解决什么问题
传统 AI 工作流的节点间通信方式大致有两种:
- 参数硬传递:上游节点把输出塞进下游节点的入参,链式依赖,一改全改。
- 隐式上下文:靠 LLM 的对话历史"猜"前序信息,不可控、不可校验。
记忆节点引入了第三条路——显式、可读写的结构化记忆存储。你可以把它理解成工作流级别的"变量表"或"黑板":
- 任何节点执行完毕后,可以把关键结论写入记忆(如
affected_hosts: ["10.0.1.3", "10.0.1.7"])。 - 后续节点按 key 读取,不需要知道是谁写的,只需要知道 key 名。
- 记忆跨执行周期持久化,同一工作流的多次运行可以累积经验(比如"上次这个告警的根因是磁盘满")。
这对确定性编排至关重要。确定性场景要求:同样的输入 → 同样的决策路径 → 同样的输出。如果中间状态靠 LLM 自由发挥,确定性就崩了;记忆节点把中间状态钉在结构化存储里,后续节点拿到的不再是"LLM 觉得大概是啥",而是"上一个节点明确写下来的值"。
记忆节点的读写模型
记忆节点提供两个核心操作:
| 操作 | 说明 | 典型用途 |
|---|---|---|
| Write | 将键值对写入记忆空间 | 节点执行结论、中间变量、决策标记 |
| Read | 按 key 从记忆空间读取值 | 后续节点获取前序结论、条件分支判断 |
记忆空间按工作流实例隔离——同一工作流的不同执行实例互不干扰,但同一实例内的所有节点共享同一份记忆。这意味着:
- 并行分支可以各自写入不同 key,汇聚节点一次性读取全部。
- 循环场景中,每次迭代可以追加写入(如
checked_hosts列表逐步增长)。
下面用一个实际运维场景来演示完整编排。
实践:告警根因分析 + 自动修复工作流
假设我们要编排一个"CPU 告警 → 根因定位 → 自动修复"的确定性工作流。整个流程分四步,记忆节点贯穿始终:
workflow:
name: cpu_alert_diagnosis_and_fix
description: CPU 告警根因分析与自动修复,通过记忆节点传递中间结论
nodes:
# 第一步:接收告警,提取关键信息写入记忆
- id: alert_receiver
type: llm
prompt: |
分析以下 CPU 告警信息,提取受影响主机 IP、告警阈值、当前 CPU 使用率。
输出格式严格为 JSON:
{"affected_ip": "...", "threshold": ..., "current_cpu": ...}
input:
alert_text: "{{ trigger.alert_message }}"
next: memory_write_alert
# 记忆节点:写入告警摘要,供后续节点读取
- id: memory_write_alert
type: memory_write
entries:
- key: affected_ip
value: "{{ alert_receiver.output.affected_ip }}"
- key: threshold
value: "{{ alert_receiver.output.threshold }}"
- key: current_cpu
value: "{{ alert_receiver.output.current_cpu }}"
- key: alert_time
value: "{{ trigger.timestamp }}"
next: root_cause_analysis
# 第二步:根因分析,从记忆读取主机 IP,写入根因结论
- id: root_cause_analysis
type: llm
prompt: |
主机 {{ memory_read.affected_ip }} 的 CPU 使用率达到 {{ memory_read.current_cpu }}%。
请判断根因,只返回以下之一:
- "process_stuck":某个进程卡死
- "traffic_spike":流量突增
- "cron_storm":定时任务集中执行
输出格式:{"root_cause": "...", "suspect_process": "..."}
input:
memory_read.affected_ip: "{{ memory_write_alert.entries.affected_ip }}"
memory_read.current_cpu: "{{ memory_write_alert.entries.current_cpu }}"
next: memory_write_rootcause
# 记忆节点:写入根因,决定后续分支
- id: memory_write_rootcause
type: memory_write
entries:
- key: root_cause
value: "{{ root_cause_analysis.output.root_cause }}"
- key: suspect_process
value: "{{ root_cause_analysis.output.suspect_process }}"
next: branch_by_rootcause
# 第三步:条件分支——根据记忆中的根因走不同修复路径
- id: branch_by_rootcause
type: switch
conditions:
- when: "{{ memory_read.root_cause }} == 'process_stuck'"
next: kill_stuck_process
- when: "{{ memory_read.root_cause }} == 'traffic_spike'"
next: scale_out_service
- when: "{{ memory_read.root_cause }} == 'cron_storm'"
next: reschedule_crons
input:
memory_read.root_cause: "{{ memory_write_rootcause.entries.root_cause }}"
# 第四步(分支示例):杀卡死进程
- id: kill_stuck_process
type: script
script_type: bash
script_content: |
# 从记忆读取目标主机和可疑进程名
TARGET_HOST="{{ memory_read.affected_ip }}"
SUSPECT_PROC="{{ memory_read.suspect_process }}"
echo "正在处理主机 $TARGET_HOST 上的卡死进程 $SUSPECT_PROC"
# 通过 SSH 执行远程命令(实际环境中使用 Blueking 的作业平台)
ssh ops-agent@$TARGET_HOST "ps aux | grep '$SUSPECT_PROC' | grep -v grep | awk '{print \$2}' | head -1 | xargs -r kill -9"
# 写入执行结果到记忆,供汇报节点使用
echo "进程 $SUSPECT_PROC 已终止"
input:
memory_read.affected_ip: "{{ memory_write_alert.entries.affected_ip }}"
memory_read.suspect_process: "{{ memory_write_rootcause.entries.suspect_process }}"
next: memory_write_fix_result
# 记忆节点:记录修复动作,形成完整闭环
- id: memory_write_fix_result
type: memory_write
entries:
- key: fix_action
value: "killed_stuck_process"
- key: fix_target
value: "{{ memory_read.affected_ip }}"
- key: fix_status
value: "success"
next: final_report
# 最终汇报:从记忆中拼装完整上下文
- id: final_report
type: llm
prompt: |
请生成运维处理报告,以下信息来自工作流记忆:
- 告警时间:{{ memory_read.alert_time }}
- 受影响主机:{{ memory_read.affected_ip }}
- CPU 使用率:{{ memory_read.current_cpu }}%(阈值 {{ memory_read.threshold }}%)
- 根因:{{ memory_read.root_cause }}
- 修复动作:{{ memory_read.fix_action }}
- 修复状态:{{ memory_read.fix_status }}
input:
memory_read.alert_time: "{{ memory_write_alert.entries.alert_time }}"
memory_read.affected_ip: "{{ memory_write_alert.entries.affected_ip }}"
memory_read.current_cpu: "{{ memory_write_alert.entries.current_cpu }}"
memory_read.threshold: "{{ memory_write_alert.entries.threshold }}"
memory_read.root_cause: "{{ memory_write_rootcause.entries.root_cause }}"
memory_read.fix_action: "{{ memory_write_fix_result.entries.fix_action }}"
memory_read.fix_status: "{{ memory_write_fix_result.entries.fix_status }}"
说明:以上 YAML 是基于 Blueking Lite 工作流编排逻辑的示意结构,具体字段名以平台实际版本为准。核心思路是——每个关键节点后紧跟一个
memory_write,后续节点通过memory_read按 key 取值,而不是把上游 output 直接硬塞进下游 input。
这个编排的确定性体现在:
- 告警信息只提取一次,写入记忆后所有节点共享,不会重复解析。
- 根因判断结果被钉住,switch 分支读的是记忆里的确定值,不是 LLM 的二次猜测。
- 修复动作和结果有据可查,最终汇报节点从记忆拼装完整链路,不需要 LLM 去"回忆"。
记忆节点 vs 纯参数传递:什么时候该用
| 场景 | 纯参数传递够用 | 需要记忆节点 |
|---|---|---|
| 简单线性流程(A→B→C) | ✅ 直接链式传参即可 | ❌ 过度设计 |
| 多分支汇聚(A→B1/B2→C) | ⚠️ 参数合并复杂 | ✅ 各分支写不同 key,C 一次读取 |
| 循环/迭代场景 | ❌ 参数无法累积 | ✅ 每轮追加写入 |
| 跨执行周期复用经验 | ❌ 参数生命周期仅当次运行 | ✅ 记忆可持久化 |
| 需要审计追溯 | ⚠️ 要翻每个节点的 log | ✅ 记忆本身就是结构化日志 |
简单说:线性短链用参数,有分支、循环、跨周期就用记忆。别在两节点直连的场景里硬塞一个记忆节点,那只是徒增复杂度。
本周其他更新:Admin 密码到期机制优化
除了记忆节点,本周还优化了系统管理层面的一个实际问题——admin 账号密码到期不再直接锁定。
旧机制:密码到期 → 账号锁定 → 管理员必须找另外的管理员或走后台解锁流程。如果只有一个 admin 账号,就彻底卡死了。
新机制:密码到期 → 登录时强制引导修改密码 → 改完即恢复使用。账号始终可登录,只是不改密码就进不了系统。
这个改动看似小,实际影响不小——单 admin 环境下不会再出现"密码过期把自己锁门外"的死锁场景。多 admin 环境下也减少了"帮解锁"的运维负担。
采用建议与注意事项
记忆节点上线前的检查清单:
- Key 命名规范:建议用
节点id_字段名格式(如alert_receiver_affected_ip),避免不同节点写入同名 key 导致覆盖冲突。当前记忆空间是扁平 key-value,没有命名空间隔离。 - 写入时机:只在"结论已确定"的节点后写记忆。LLM 节点的中间推理过程不要写——那部分不确定,写了反而污染后续判断。
- 读取校验:下游节点读到记忆值后,加一层基础校验(IP 格式、数值范围等),防止上游写入异常值导致连锁错误。
- 记忆清理:长期运行的工作流会累积记忆条目。确认平台是否有自动过期机制;如果没有,在最终节点加一个
memory_clear或手动设置 TTL。 - 敏感信息:密码、密钥等不要写入记忆——记忆存储未必加密,且最终汇报节点可能把记忆内容输出到聊天窗口。
密码到期机制切换注意:
- 如果现有环境已有因密码过期被锁定的 admin 账号,升级后需要确认这些账号的状态是否自动解除锁定,还是仍需手动处理。
- 新机制下,强制改密码的引导页面需要确保密码复杂度策略清晰展示,否则用户可能反复提交不符合要求的密码而无法进入系统。
记忆节点把 AI 工作流从"每步重新猜"拉到了"每步有据可查"的状态。对于运维这种对确定性要求极高的场景,这个能力补上了编排链条里最薄弱的一环——中间状态的可靠传递。如果你已经在 Blueking Lite 上跑 AI 工作流,优先把有分支和循环的场景改成记忆节点驱动;如果还在评估阶段,可以从一个两分支的告警处理流程开始试,感受参数传递和记忆传递的差异。