跳转至

LLM Compiler:把任务编译成依赖图

有些任务既不是简单链条,也不是完全独立的一批。

旅游助手写一份杭州报告:先查城市背景,然后基于背景推荐路线,最后综合报告。"查背景"和"推荐路线"之间有依赖,但"查天气"和"查背景"可以并行。你需要知道哪些能同时跑,哪些必须等。

LLM Compiler 让模型输出一个任务图(DAG),Python 按依赖关系调度执行。

一句话说清楚

LLM Compiler 把"顺序执行"改成"依赖 DAG + 调度器",让无依赖的子任务不用互相等待。

解决什么问题

问题 表面看起来 实际风险
所有任务串行跑 简单 独立任务白等
所有任务并行跑 依赖被忽略,下游缺上游结果
依赖关系写在自然语言里 灵活 Python 无法调度和审计

引入什么复杂度

  • 模型需要输出结构化 DAG(task id、instruction、deps 列表)。
  • DAG 可能有错:循环依赖、遗漏依赖、节点太碎。
  • 调度器本身有开销——如果任务只有 2-3 步且都是串行的,不如直接用 Plan & Solve。

和其他模式的关系

模式 谁决定下一步 什么时候用
Prompt Chaining Python 写死线性步骤 顺序已知
ReWOO 排一批独立工具调用 调用大部分独立
LLM Compiler 模型建依赖图 有依赖也有并行
Manager-Worker Manager 动态分配 任务类型在运行时才确定

LLM Compiler = ReWOO + 依赖图 + 并行执行。比 ReWOO 重(要处理依赖),比 Manager-Worker 静态(图在执行前确定)。

这个模式改变了什么

负责什么
Compiler 模型 输出任务节点、依赖关系、最终合并指令
Python 调度器 按依赖拓扑序执行,无依赖的并行跑
Worker 模型/工具 执行每个节点
Final 步骤 把所有依赖结果合并成答案
  • 谁决定下一步:拓扑排序。某节点的所有 deps 完成后才执行。
  • 谁拥有状态:Python 持有 DAG 和每个节点的输出。
  • 什么时候停止:所有节点执行完 + final 步骤返回。

走一遍真实轨迹

用户请求:"写一份杭州三天旅游报告,包含城市背景、天气、推荐路线。"

DAG:
  t1: 查杭州城市背景        deps=[]
  t2: 查杭州天气            deps=[]
  t3: 推荐三天路线          deps=[t1, t2]   ← 需要背景 + 天气
  final: 综合报告           deps=[t1, t2, t3]
阶段 执行 输出
第一批(并行) t1 查背景、t2 查天气 "杭州,浙江省会..."、"Day1 晴..."
第二批 t3 推荐路线(用 t1+t2 结果) "Day1 西湖...Day2 茶博物馆..."
final 综合 t1+t2+t3 完整报告

t1 和 t2 可以并行,t3 必须等两者完成。这就是 DAG 调度的价值。

流程图

flowchart TD
  T["用户任务"] --> C["模型生成 DAG"]
  C --> A["t1: 查背景(无依赖)"]
  C --> B["t2: 查天气(无依赖)"]
  A --> D["t3: 推荐路线(依赖 t1, t2)"]
  B --> D
  D --> F["final: 综合报告"]

完整实现

from __future__ import annotations
from pathlib import Path
from agent_patterns_lab.patterns.llm_compiler import llm_compiler
from agent_patterns_lab.runtime import MockLLM, Tracer


def main() -> None:
    tracer = Tracer()

    model = MockLLM(
        [
            # 第一次调用:生成 DAG
            '{"tasks":['
            '{"id":"t1","instruction":"查杭州城市背景","deps":[]},'
            '{"id":"t2","instruction":"查杭州未来三天天气","deps":[]},'
            '{"id":"t3","instruction":"基于背景和天气推荐三天路线","deps":["t1","t2"]}'
            '],"final":{"instruction":"综合所有信息写旅游报告"}}',
            # t1 输出
            "杭州,浙江省会,西湖闻名,丝绸之府,茶叶之都。人口约 1200 万。",
            # t2 输出
            "Day1 晴 28°C,Day2 多云转小雨 25°C,Day3 晴 29°C。",
            # t3 输出(会用到 t1 和 t2 的结果)
            (
                "Day1:西湖环湖(天气好适合户外)→ 灵隐寺\n"
                "Day2:中国茶叶博物馆(室内为主,防小雨)→ 河坊街\n"
                "Day3:良渚博物院 → 返程"
            ),
            # final 综合
            (
                "【杭州三天旅游报告】\n"
                "城市简介:杭州是浙江省会,以西湖闻名。\n"
                "天气:前三天以晴为主,Day2 有小雨。\n"
                "推荐路线:\n"
                "  Day1:西湖环湖 → 灵隐寺\n"
                "  Day2:茶博物馆 → 河坊街\n"
                "  Day3:良渚博物院 → 返程\n"
                "注意事项:Day2 带伞。"
            ),
        ]
    )

    out = llm_compiler(
        model,
        task="写一份杭州三天旅游报告,包含城市背景、天气、推荐路线。",
        tracer=tracer,
    )
    print(out)

    trace_path = tracer.export_jsonl(Path(".traces") / "53_llm_compiler_zh.jsonl")
    print(f"\n[trace] {trace_path}")


if __name__ == "__main__":
    main()

运行:

PYTHONPATH=src python examples/53_llm_compiler.py

完整运行记录

【杭州三天旅游报告】
城市简介:杭州是浙江省会,以西湖闻名。
天气:前三天以晴为主,Day2 有小雨。
推荐路线:
  Day1:西湖环湖 → 灵隐寺
  Day2:茶博物馆 → 河坊街
  Day3:良渚博物院 → 返程
注意事项:Day2 带伞。

[trace] .traces/53_llm_compiler_zh.jsonl

trace 事件序列:

llm_compiler.dag  tasks=3
llm_compiler.execute  id=t1  deps=[]
llm_compiler.execute  id=t2  deps=[]
llm_compiler.execute  id=t3  deps=[t1, t2]
llm_compiler.final

注意 t1 和 t2 没有依赖,调度器可以并行执行。

踩坑与诊断

现象 trace 信号 修法
依赖写错 t3 缺上游结果 t3 执行时 deps 列表比预期短 DAG 校验:检查所有被引用的 id 是否存在
循环依赖 调度器死锁 无节点可执行 构建 DAG 时检测环(拓扑排序失败则报错)
节点太碎 10+ 节点,每个只做一句话 节点数远超预期 合并小任务
final 丢信息 报告漏掉某个节点的内容 final prompt 里没带某节点输出 确保 final 能看到所有依赖节点的结果

关键诊断技巧:在 trace 里看每个节点的 deps 和实际执行顺序。如果 t3 在 t1 之前执行了,说明调度器有 bug。

工程备忘

成本公式

模型调用 = 1(编译 DAG) + N(节点) + 1(final) = N + 2
延迟 ≈ 模型延迟 × (DAG 最长路径长度 + 2)

DAG 的价值在延迟——如果 3 个节点中 2 个可并行,总延迟从 3 步降到 2 步。任务越多、并行度越高,收益越大。

生产注意事项

  • DAG 校验必须在执行前做:检查循环、检查 id 唯一性、检查 deps 引用合法。
  • 当前实现是串行按拓扑序执行。生产环境改成 asyncio 并行可以拿到真正的延迟收益。
  • 如果任务 DAG 经常需要执行中修改(比如 t1 结果改变了 t3 的指令),LLM Compiler 不够用,考虑 PER 或 Magentic Orchestration。
  • 节点数建议不超过 6。超过 6 个节点时模型生成的 DAG 质量下降明显。

读完以后

LLM Compiler 适合有明确依赖关系的多步任务。

  • 如果步骤都是线性的,退回 Plan & Solve
  • 如果工具调用都独立,用更简单的 ReWOO
  • 如果需要动态委派专家,读 Manager-Worker

参考资料