YAML 驱动的声明式 Agent 编排
YAML 驱动的声明式 Agent 编排
学习目标
读完本章后,你将能够:
- 理解 YAML 作为 Agent 编排 DSL 的设计动机与行业趋势
- 掌握声明式图定义的核心要素:节点类型、边、条件、记忆
- 分析 YAML 配置到运行时图的编译流程
- 评估 YAML 编排与代码式编排的取舍
1. 为什么用 YAML 编排 Agent
1.1 行业趋势
YAML 正成为多 Agent 编排的事实标准声明格式。多个主流框架都采用了这一方向:
| 框架 | YAML 编排能力 |
|---|---|
| Microsoft Agent Framework | 官方支持 YAML 定义工作流,替代纯代码式图构建 |
| Duckflux | 轻量级、确定性的 YAML DSL,运行时无关 |
| Serverless Workflow DSL | CNCF 标准,YAML/JSON 双格式 |
| LangGraph | 通过 YAML 导入导出实现持久化编排 |
| Temporal | YAML DSL 作为 LLM 生成工作流的目标格式 |
共同动机:
- 零代码用户友好:非开发者可通过配置定义复杂 Agent 协作
- 版本控制友好:YAML diff 清晰,易于 Code Review
- LLM 可生成:Agent 可以直接输出 YAML 来创建新工作流
- 可视化编辑基础:YAML 到图形的双向转换
1.2 声明式 vs 命令式编排
| 维度 | 声明式 (YAML) | 命令式 (代码) |
|---|---|---|
| 表达能力 | 受 Schema 约束 | 图灵完备 |
| 可审计性 | 高(纯配置) | 中(需读代码) |
| IDE 支持 | Schema 自动补全 | 语言服务 |
| 调试难度 | 高(错误定位难) | 低(断点调试) |
| LLM 生成 | 天然适合 | 需代码沙箱 |
2. 核心概念
2.1 图的基本要素
一个 YAML Agent 图由三个核心部分组成:
2.2 节点类型系统
每种节点类型定义了不同的执行语义:
| 节点类型 | 执行行为 | 输入 | 输出 |
|---|---|---|---|
agent | 调用 LLM + 可选工具 | 上游消息 | 模型响应 |
human | 阻塞等待用户输入 | 上游消息 | 用户回复 |
python | 执行 Python 脚本 | 上游消息(作为参数) | 脚本 stdout/返回值 |
subgraph | 递归执行子图 | 上游消息 | 子图最终输出 |
passthrough | 直接转发 | 上游消息 | 原样转发 |
literal | 注入静态内容 | 无 | 预定义文本 |
loop_counter | 计数 + 循环控制 | 上游消息 | 是否继续的元数据 |
2.3 边的语义
边不仅是连接关系,还承载数据流控制:
- from: node_a to: node_b trigger: true # 是否激活此边 condition: # 条件评估 type: keyword config: any: ["ROUTE: Node B"] carry_data: true # 是否携带数据 keep_message: false # 是否保留消息到上下文 clear_context: false # 是否清理目标节点的上下文 process: # 数据变换 type: regex_extract config: pattern: "<result>(.*)</result>" dynamic: # 运行时分裂 type: map split: type: regex config: pattern: "<Query>:\\s*(.*)" max_parallel: 32.4 记忆配置
记忆可以定义在两个层级:
| 层级 | 作用域 | 示例 |
|---|---|---|
| 全局记忆 | 整个图共享 | graph.memory[] |
| 节点级记忆 | 仅该节点可访问 | node.config.memories[] |
每种记忆类型有不同的存储策略:
- SimpleMemory:内存 + FAISS 向量索引,适合短文本语义检索
- FileMemory:文件索引 + 向量检索,适合文档库检索
- Blackboard:共享黑板 + 最大条目限制,适合多 Agent 共享状态
3. 编译流程
从 YAML 到运行时图的编译分为四个阶段:
3.1 阶段 1:YAML 解析
yaml_template/design.yaml (Schema 模板) ↓yaml_instance/xxx.yaml (用户工作流实例) ↓load_config() → 解析 ${VAR} → Pydantic 模型3.2 阶段 2:配置校验
双层校验策略:
| 校验层 | 时机 | 工具 |
|---|---|---|
| 静态校验 | 编写时 | make validate-yamls(JSON Schema 验证) |
| 运行时校验 | 执行前 | Pydantic model_validate() |
3.3 阶段 3:图构建
GraphConfig → GraphContext → GraphExecutor ↓ ↓ ↓ 配置定义 运行时上下文 执行引擎 (静态) (workspace等) (策略选择)3.4 阶段 4:拓扑分析与执行
拓扑分析器根据图结构自动选择执行策略:
4. 陷阱与对策
| 陷阱 | 表现 | 对策 |
|---|---|---|
| 变量未定义 | 运行时 ${VAR} 无法解析 | 确保 .env 完整,使用默认值 |
| 循环无退出条件 | 无限执行 | 配置 loop_counter.max_iterations |
| 嵌套子图过深 | 栈溢出 / 内存耗尽 | 限制递归深度 |
| Schema 版本不兼容 | 旧 YAML 在新版本无法解析 | 维护 version 字段 + 迁移脚本 |
| 边条件永远为假 | 目标节点永不执行 | 配置 trigger: true 的默认边 |
| 记忆检索返回空 | Agent 缺少上下文 | 配置 retrieve_stage 确保检索时机正确 |
5. 设计权衡
YAML 编排的优势
- 零代码:领域专家可直接定义工作流
- 可审计:配置变更通过 Git diff 清晰可见
- LLM 可生成:Agent 可以自主创建/修改工作流
- 可视化友好:YAML → 图形双向转换简单
YAML 编排的局限
- 表达能力受限:复杂逻辑需自定义函数
- 调试困难:运行时错误定位不如代码断点
- Schema 演化成本:向后兼容需要精心设计
混合方案
一种常见的混合策略是:YAML 定义骨架 + Function Calling 扩展能力:
YAML 骨架 (结构) + Function Tools (行为扩展) ↓ ↓ 节点/边的定义 自定义 Python 函数 由系统解析 由开发者编写