结构与任务图:Agent / Pipeline / Workflow 怎么调度任务
30 秒导读: Griptape 把「一次 AI 编排」拆成两层——Structure(编排层)和 Task(执行单元)。Structure 把若干 Task 连成一张有向图(DAG),再决定「按什么顺序、要不要并行」地跑它们。Agent / Pipeline / Workflow 是三个 Structure 子类,共享同一套
run骨架,只在调度策略上分道扬镳:单任务、一条链、一张图。本章只讲这条「编排 Task 的执行模型」骨架;单个 Task 里 LLM 怎么循环,是 02 章 的事。
本章属于 Griptape 系列,总览与阅读地图见 index.md。
1. 这是什么(零基础也能懂)
一句话定义: Structure 是一个「任务调度器」——你把要做的活拆成一个个 Task,声明它们谁先谁后,Structure 负责按依赖关系把它们跑完,并收集结果。
它解决什么问题。 真实的 AI 应用很少是「问一句、答一句」。更常见的是一条流水:先检索资料 → 再让模型总结 → 再翻译成三种语言 → 最后汇总。这些步骤有先后、有分叉、有汇合。Structure 就是用 来描述并执行这种「多步、有依赖」的编排的。
三种编排,对应三种形状。 你不必自己写调度循环,选一个现成的 Structure 即可:
| Structure | 任务形状 | 白话 | 典型场景 |
|---|---|---|---|
Agent | 单个任务 | 就一个活,直接干 | 一个带工具的对话智能体 |
Pipeline | 一条链 | A→B→C 顺序做 | 检索→总结→翻译 的流水线 |
Workflow | 一张图 | 有分叉汇合,能并行 | 一份资料 fan-out 成多路加工再汇总 |
用起来什么样。 下面这段感受一下「声明图、然后 run」的手感:
# 示意,非源码:用 >> 运算符声明「谁接谁」,再交给 Workflow 跑
from griptape.structures import Workflow
from griptape.tasks import PromptTask
research = PromptTask("查资料:{{ args[0] }}", id="research")
summary = PromptTask("总结:{{ parents_output_text }}", id="summary")
translate = PromptTask("翻译成中文:{{ parents_output_text }}", id="translate")
research >> summary # research 是 summary 的父任务
research >> translate # 也是 translate 的父任务(分叉)
flow = Workflow(tasks=[research, summary, translate])
flow.run("griptape 是什么") # summary 和 translate 会并行跑
一句话直觉: 把 Structure 当流程引擎,把 Task 当流程里的一个节点。Structure 不关心节点内部干了啥(调 LLM?查数据库?),它只关心「这个节点的父节点跑完了没、该不该轮到它」。
2. 顶层全景(它大概怎么转)
2.1 两层四类
整个编排层就四个主角:一个抽象基类 Structure、它的三个子类、以及被调度的 BaseTask。
| 部件 | 职责 | 文件 |
|---|---|---|
Structure | 抽象编排基类:装任务、补全图关系、跑 run 生命周期 | structures/structure.py |
Agent | 只容一个 Task 的最简 Structure | structures/agent.py |
Pipeline | 顺序链式调度 | structures/pipeline.py |
Workflow | 拓扑排序 + 线程池并行调度 | structures/workflow.py |
BaseTask | 执行单元:状态机、父子图关系、run 契约 | tasks/base_task.py |
2.2 一次 run 大致走的路
不进代码,先看主线。三个子类的前后段完全一样,只有中间「怎么跑」不同:
Structure.run(*args) ← 模板方法,基类里写死
│
├─ before_run(args) ← 重置所有 Task、补全父子关系、发 Start 事件
│
├─ try_run(*args) ★ 抽象方法 ★ ← 唯一由子类实现的一步
│ ├─ Agent: 就 task.run()
│ ├─ Pipeline: 从头顺着链递归
│ └─ Workflow: 拓扑排序 + 线程池并行
│
└─ after_run() ← 写对话记忆、发 Finish 事件
怎么读: 上下两头(
before_run/after_run)是共用骨架,中间那颗星(try_run)是唯一的变量。这正是「模板方法」模式——基类定好流程骨架,把会变的那一步留成抽象方法给子类填。
run 的骨架在 structure.py:200-208:先 before_run(args),再 try_run(*args),最后 after_run()。try_run 是抽象方法(structure.py:229-231),三个子类各给一份实现。