跳转到内容

YAML 驱动的声明式 Agent 编排

YAML 驱动的声明式 Agent 编排

学习目标

  • 理解 ChatDev 如何将 YAML 配置编译为可执行的 Agent 图
  • 掌握 ChatDev 的七种节点类型及其在 YAML 中的配置方式
  • 了解 ChatDev 的双层校验策略(静态 + 运行时)
  • 能够阅读和编写 ChatDev 工作流 YAML 文件

前置知识

本章涉及 YAML 驱动声明式编排的通用原理,建议先阅读:

下文假设你已理解声明式编排的核心概念,直接聚焦 ChatDev 的具体实现。


项目实践

ChatDev 的 YAML 层次结构

ChatDev 的 YAML 工作流定义由四个层级构成:

一个完整的工作流 YAML 包含:

version: 0.0.0 # Schema 版本
vars: # 变量定义,通过 ${VAR} 引用
MODEL_NAME: gemini-3-flash-preview
graph:
id: deep_research_v1
nodes: [...] # 节点列表
edges: [...] # 边列表
memory: [...] # 全局记忆
start: ["START"] # 起始节点
end: [] # 终止节点(空表示自动推断)

节点类型的 YAML 配置

ChatDev 定义了七种节点类型,每种类型有独特的 config 结构:

Agent 节点

Agent 是最核心的节点类型,配置 LLM 调用、工具、记忆和重试:

- id: Planner
type: agent
config:
name: ${MODEL_NAME} # 模型名(支持变量插值)
provider: gemini # Provider 注册名
base_url: ${BASE_URL}
api_key: ${API_KEY}
role: |- # System Prompt
**Role:** You are the Chief Research Strategist...
params: {} # 模型参数(temperature, max_tokens 等)
tooling: # 工具配置
- type: function
config:
tools:
- name: deep_research:All # 工具组通配符
- name: get_current_time # 单个工具
timeout:
thinking: # 思考配置(null 表示不启用)
memories: [] # 节点级记忆
retry: # 重试配置

Subgraph 节点

子图支持两种方式引用:

# 方式 1: 引用外部 YAML 文件
- id: Executor
type: subgraph
config:
type: file
config:
path: deep_research_executor_sub.yaml
# 方式 2: 内联定义子图(递归结构)
- id: InlineSubgraph
type: subgraph
config:
type: config
config:
id: inline_subgraph
nodes: [...]
edges: [...]

控制流节点

# Loop Counter: 循环计数器
- id: Test Phase Loop Counter
type: loop_counter
config:
max_iterations: 3 # 最大迭代次数
reset_on_emit: true # 有输出时重置计数
# Passthrough: 消息透传
- id: USER
type: passthrough
config: {}
# Literal: 静态内容注入
- id: SystemPrompt
type: literal
config:
content: "你是一个 AI 助手..."
role: system

边的 YAML 配置

边不仅定义连接关系,还承载数据流控制:

# 无条件边:始终触发
- from: START
to: Demand Analyst
trigger: true
condition: 'true'
carry_data: true
keep_message: false
clear_context: false
process: null
dynamic: null
# 关键词条件边
- from: Quality Reviewer
to: Report Writer
trigger: true
condition:
type: keyword
config:
any:
- 'ROUTE: Report Writer'
none: []
regex: []
case_sensitive: true
# 动态边:运行时分裂
- from: Planner
to: Executor
trigger: true
condition: 'true'
dynamic:
type: map
split:
type: regex
config:
pattern: "<Query>:\\s*(.*)"
config:
max_parallel: 3

双层校验策略

ChatDev 实现了两层 YAML 校验:

校验层实现触发时机
静态校验make validate-yamls → JSON Schema 验证开发者手动执行
运行时校验entity/configs/ Pydantic 模型 → model_validate()工作流加载时

Schema 模板文件 yaml_template/design.yaml 定义了完整的 YAML 结构,校验器据此生成 JSON Schema 进行静态验证。

变量插值机制

ChatDev 使用 ${VAR} 语法引用环境变量和自定义变量:

vars:
MODEL_NAME: gemini-3-flash-preview
# ${BASE_URL} 和 ${API_KEY} 来自 .env 文件
# 在节点配置中引用
config:
name: ${MODEL_NAME}
base_url: ${BASE_URL}
api_key: ${API_KEY}

问题与规避

问题表现规避策略
变量未定义${UNDEFINED} 运行时为空确保 .env 包含所有必需变量;在 vars 中定义默认值
嵌套子图路径错误path: nonexistent.yaml 加载失败先用 make validate-yamls 静态检查
工具名不存在节点执行时报工具找不到工具名必须在 functions/function_calling/ 中存在
Provider 未注册provider: unknown 报错仅使用 openaigemini(内置)
Schema 版本不兼容旧 YAML 在新版本无法解析跟踪 version 字段变更

设计取舍

为什么选 YAML 而非代码

ChatDev 1.0 使用纯 Python 代码定义工作流(ChatChainConfig.json + 硬编码),2.0 转向 YAML 的原因:

维度1.0 (Python)2.0 (YAML)
用户门槛需要编程能力零代码配置
LLM 生成困难(需生成完整 Python)天然适合(结构化文本)
可视化编辑困难简单(YAML ↔ 图形双向转换)
表达能力图灵完备受 Schema 约束

YAML vs JSON 的选择

ChatDev 选择 YAML 而非 JSON:

  • 可读性:YAML 的缩进语法比 JSON 的大括号更简洁
  • 注释支持:YAML 支持 # 注释,JSON 不支持
  • 多行字符串|- 块语法适合 System Prompt
  • 代价:YAML 解析比 JSON 慢,且缩进敏感

参考来源