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 报错 | 仅使用 openai 和 gemini(内置) |
| 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 慢,且缩进敏感