跳到主要内容

24 · Graph Engineering 图工程

用 Codex、crewAI 源码和 Anthropic 多智能体系统的生产数据讲透图工程:Agent、代码、工具、人四类节点之间怎么组织工作。

本章任务

要回答的问题

任务装不进一个 Loop 时,怎样在 Agent、代码、工具和人之间拆节点与交接?

读完你能

  • 在 Agent、代码、工具和人四类节点中选择执行者
  • 为每条边定义载荷、状态所有者、预算和失败语义
  • 估算委派成本并设置人工门禁
适合现在读
正在判断是否需要 Workflow、多 Agent、人工门禁或异步编排的工程师
先修知识
理解单 Agent Loop;理解状态所有权
实践产物
一份真实任务的节点、边、状态与验收规范
证据边界
案例和生产数据依赖任务与组织上下文,不能外推成多 Agent 的通用收益

先用一个真实任务判断要不要拆图

Section titled “先用一个真实任务判断要不要拆图”

任务:读取 8 家 Agent 平台的官方文档,抽取版本与能力,形成带引用的对比报告,最后把推荐结果发布到团队知识库。

先把所有工作塞进一个 Agent Loop,常见结果是:检索内容挤掉写作上下文、版本抽取反复做、引用遗漏在最后才发现、发布动作与研究动作共享同一权限。此时不要直接说“上多 Agent”,先逐项判断执行者:

子任务首选节点原因
抓取页面、解析版本字段代码或工具节点路径确定、可测试,不值得持续花模型 Token
识别不同平台术语的等价关系Agent 节点需要语义判断,规则难预写完整
检查引用是否覆盖结论代码检查加 Reviewer 节点先做确定性覆盖检查,再处理语义质量
发布到团队知识库人工门禁后的工具节点不可逆外发,执行前需要明确批准

只有满足至少一个条件才值得拆:独立子任务能并行;上下文彼此污染;权限必须分层;或人需要成为正式门禁。否则保留一个 Loop,少一次交接就少一次信息损失。

图工程处理两件事:节点选谁执行,边规定怎样交接。节点只有四类:会即兴但昂贵的 Agent、确定且可测试的代码、面向模型的工具、负责审批和判断的人。每条边至少写清目标、上下文、权限、预算和输出格式。

你的实践产物不是一张漂亮流程图,而是一份可以验收的图规范:每个节点的输入输出、状态所有者、失败去向、预算与人工门禁都能被测试。

四类节点与边上必带的五要素
先用确定性代码处理已知路径,只把无法预写的判断交给 Agent;边负责把五项交接契约说清楚。

边上带什么:三个独立来源给出同一个答案

Section titled “边上带什么:三个独立来源给出同一个答案”

图里最容易被轻视的是边。节点拆得再漂亮,交接时上下文断了、权限漏了、预算没限,图照样崩。有意思的是,三个互相独立的系统对”一条边该带什么”给出了几乎相同的答案。

Hermes 的 delegate_task

delegate_task(goal, context, toolset, max_iterations)
# 目标 上下文 权限白名单 预算

crewAI(commit f15844b)的委派工具,tools/agent_tools/delegate_work_tool.py

class DelegateWorkToolSchema(BaseModel):
task: str = Field(..., description="The task to delegate")
context: str = Field(..., description="The context for the task")
coworker: str = Field(..., description="The role/name of the coworker")

Anthropic 研究系统的调度 prompt 要求每个子任务描述必须包含:objective(目标)、output format(输出格式)、tool guidance(工具指引)、task boundaries(任务边界)。他们还留下了反面教材:早期允许调度者下达”research the semiconductor shortage”这种一句话任务,结果一个子 agent 去查 2021 年汽车芯片危机,另外两个重复调查 2025 年供应链——边上信息不足,工人就会重复劳动或者跑偏

三家合起来,一条边的完整定义是五要素:目标、上下文、权限、预算、输出格式。缺哪样,哪样就在运行时爆。

返程 payload 同样有实现可抄。smolagentsagents.py:868,commit e3a5b89)的 managed agent 被调用时,返回的不是裸结论:结论套进固定的 report 模板,可选再附一段 <summary_of_work> 工作摘要(截断后的关键步骤)。父节点拿到的是”结论加依据”,可以判断质量,而不是只能盲信。

边上不带什么:权限只收窄,不放宽

Section titled “边上不带什么:权限只收窄,不放宽”

这条纪律在 Codex 源码里是白纸黑字。codex-rs/core/codex_delegate.rs(commit fa1d4c4)spawn 子 agent 时:

inherited_exec_policy: Some(Arc::clone(&parent_session.services.exec_policy)), // L130
inherited_multi_agent_version: Some(MultiAgentVersion::Disabled), // L143

第一行:子代理强制继承父的执行策略,没有”开个子代理绕过权限”的后门。第二行更狠:子代理的多 agent 能力直接置为 Disabled——子代理天生不能再派子代理,递归禁令不靠 prompt 恳求,靠构造函数参数。

Hermes 的对应做法是从子节点工具集里硬删 5 个工具:再派活、反问用户、写长期记忆、跨通道发消息、执行代码。两家共识一句话:边是权限收窄的地方,不是放宽的地方。

组织方式收敛到五种形状。前四种在 crewAI 和 Anthropic 系统里都有可以直接读的实现。

流水线加闸门。 固定顺序的步骤串行,步间放确定性检查。crewAI 的 Process.sequential 就是它:任务列表按序执行,前一个任务的产出作为后一个的上下文。适用前提是步骤能预先写死。

分诊路由。 入口用便宜节点分类,再送对应专门节点。每条路的 prompt 和工具做窄做专;路由不准比不分诊更糟。

并行扇出。 独立子任务同时跑:切分(各管一块再拼)或投票(同题多跑比对)。Anthropic 的数据:调度者并行 spawn 3-5 个子 agent、每个子 agent 并行调 3 个以上工具,复杂查询的耗时降了 90%。代价是聚合逻辑——结果冲突谁裁决,要提前定。

调度者加工人。 子任务没法预先列出时,一个 agent 节点当调度者,运行时决定拆几个活、派给谁。crewAI 的 Process.hierarchical 是这个拓扑的显式实现,而且框架用校验器强制了它的前提——crew.pycheck_manager_llm:不给 manager_llmmanager_agent 直接报错。调度者不是可选装饰,是这个拓扑的定义

调度还有个预算刻度问题:多大的活配多少工人?Anthropic 把刻度直接写进 prompt:简单事实查询 1 个 agent、3-10 次工具调用;直接对比 2-4 个子 agent、每个 10-15 次调用;复杂研究 10 个以上子 agent 分工明确。这条刻度值得抄——他们加它是因为早期系统会为一个简单问题 spawn 50 个子 agent

评审回路。 一个节点干活,另一个挑毛病,循环到通过。图里合法的环,但必须带出口:轮次上限加”意见不再变化就停”。

真实系统是组合拓扑。Anthropic 研究系统完整走一遍是:调度者-工人(并行检索)→ 结果汇总 → 流水线尾巴上接一个确定性的 CitationAgent 专门补引用。三种形状拼成一张图。

多节点干活,马上遇到状态问题:中间结果放哪、谁能改。

两种基本方案。共享黑板:所有节点读写同一份状态。协作方便,但写权限必须管——Hermes 禁止子节点写长期记忆:子节点的结论没经验证,直接写进父的记忆等于污染水源。消息传递:只通过边上的 payload 交换信息,状态私有。隔离好,代价是每条边都要认真设计。

实践是混合。Anthropic 有个细节值得注意:调度者在 spawn 工人之前先把研究计划写进 Memory——因为上下文超 20 万 token 会被截断,计划必须存在循环外部才能活过截断。这就是黑板的正确用法:放计划这类”必须活得比上下文长”的东西。任务清单(todo list)同理,本站第 21 章拆了四种实现。

原则层面是 12-Factor 的 Factor 5:执行状态和业务状态尽量统一。图跑到哪一步和业务数据什么状态分两处存,迟早对不上账。事件日志是同时承载两者的好载体——每个 agent 节点的日志拼起来就是整张图的执行历史(见 Loop Engineering 部件 4)。

多数系统把人的介入做成特例:弹窗、卡死等输入。图工程把人当正规节点:有触发条件、输入输出、超时行为。

手段是 12-Factor 的 Factor 7:模型通过工具调用联系人。request_approval(action, reason)ask_human(question) 是普通工具,模型自己决定何时调。人工介入由此进了事件日志,可回放可统计。

三个设计要点。路由:审批送谁面前?Codex 的答案是一律回父 session,子代理永不弹 UI——用户面对三个弹窗分不清哪个是哪个。超时:人不在怎么办?降级方案(保守默认、挂起任务)预先定义,别让整张图挂在一个人的下班时间上。粒度:按不可逆程度分级——读操作放行,可逆写事后汇报,不可逆操作(外发、删数据、花钱)事前审批。

静态图(代码定边)和动态图(模型选边)是同一张图的不同区域,边界随时间移动。健康方向是从动态往静态冻

判断标准一个问题:这段路径最近 100 次执行走的是不是同一条路?是,就把它从 agent 节点改写成代码节点——不花 token、不抽风、可写单测。Anthropic 的 CitationAgent 是现成例子:补引用这件事路径固定,就从主循环里拆出来变成流水线上一个专职节点。12-Factor 作者的观察同向:生产里最好的”AI 产品”大多是确定性代码,LLM 步骤只出现在真正需要即兴的那几个点。

反方向也成立:写死的流程异常分支多到 if-else 写不完,说明它需要即兴,让给 agent 节点。图工程是持续再平衡。

动态图里模型能自己开节点,防护栏必须写进代码,不是写进 prompt。

  • 深度上限。 Hermes 硬编码 MAX_DEPTH=2;Codex 直接给子代理 MultiAgentVersion::Disabled(上文源码),深度上限做成了类型系统层面的 1。
  • 递归禁令。 派活工具不下发给子节点。防子派孙、孙派曾孙的指数爆炸——Anthropic 那个”50 个子 agent”事故就是没有数量闸门的下场。
  • 并发上限加心跳。 活跃节点数封顶;长任务节点定期心跳(Hermes 每 30 秒),外层才分得清”还在干”和”已经死了”。
  • 预算随边下发。 每条边带 max_iterations,父预算是所有子预算的硬上界。

诚实的答案:多数时候不需要。每条边都是一次上下文复述,每次复述都丢信息;多 agent 的 token 消耗是成倍的(这正是它有效的原因,也是它昂贵的原因)。Anthropic 明确说他们的架构只对”广度优先、多方向并行”的查询占优。

该不该拆图的决策树
默认答案是不拆。四个信号各对应一种拓扑;一旦拆分,防护栏全部写进代码。

三个信号出现再拆:

  1. 上下文互相污染。 一个窗口里两摊事互相挤占(边查资料边写代码),拆成两个节点各管一摊。
  2. 权限需要分层。 一部分工作放开跑、另一部分必须收紧,边天生是权限边界。
  3. 有人要进来。 有审批点就建模成人工节点,比循环里挂弹窗干净。

拆的顺序:先写清每条边的五要素(目标、上下文、权限、预算、输出格式),再定状态归属(计划上黑板、细节走消息),最后装防护栏(深度、并发、递归禁令)。节点内部最不用操心——每个 agent 节点就是一个标准循环,Loop Engineering 的部件原样适用。

为了图而图。 一个循环能干的活拆成七个节点,得到的是七次交接损耗。节点数是成本,不是成熟度。

一句话委派。 “research the semiconductor shortage”式的模糊任务让 Anthropic 的工人们重复劳动、互相踩线。边的五要素一个都不能省,输出格式尤其容易忘。

边上只传结论不传依据。 学 smolagents:report 模板加可选的工作摘要。父节点要能判断子结论的质量。

防护栏写进 prompt。 “请不要递归调用自己”是请求,MultiAgentVersion::Disabled 是构造函数参数。模型会在长上下文里忘掉请求,绕不过硬编码。

人工节点没有超时。 审批发出去,人在度假,图挂一周。每个人工节点都要超时和降级路径。

所有节点用同一个模型。 Anthropic 的配方就是异构的:Opus 当调度者,Sonnet 当工人。节点拆开的直接红利是按节点选型号。

先保留一个 Loop,直到出现并行价值、上下文污染、权限分层或人工门禁。拆分后,每条边都必须携带目标、上下文、权限、预算和输出格式;节点数、Agent 数和图的复杂度都不是成熟度指标。

下一步实验就用开篇的 8 平台调研任务:先跑单 Loop 基线,再只拆出一个确定性版本抽取节点和一个引用 Reviewer。比较总 Token、墙钟时间、重复检索数、缺失引用数与人工修正时间。只有至少一个质量或时间指标改善,并且新增成本可解释,才继续拆更多节点。

六个检查问题与三道练习
Q1 · 基础:图工程和 LangGraph 这类图框架是什么关系?一句话说清图工程在管什么。

图框架是实现手段,图工程是设计决策——用不用框架都绕不开。图工程管两件事:节点怎么分(哪段活给模型、哪段给确定性代码、哪段给人)和边怎么连(交接时带什么、结果怎么回来、失败归谁)。

四类节点是完备的选项集:Agent 节点(贵、慢、会即兴,用在路径没法预写的地方)、代码节点(便宜、可测试,用在路径已知的地方)、工具节点(面向模型的代码接口)、人工节点(审批和拿主意)。任何”多智能体架构”拆到底都是这四类的组合。

源码:本章研究底稿里 crewAI 和 Codex 的对应实现。 追问:「那 LangGraph 的 StateGraph 对应图工程的哪部分?」它替你实现了边的执行机制(消息传递、checkpoint),但节点怎么分、边上带什么,仍然是你的设计决策——框架不替你回答。

Q2 · 边设计:一条委派边应该带哪五要素?漏掉任何一个会出什么事故?

目标、上下文、权限、预算、输出格式。三个独立来源收敛到同一答案:Hermes 的 delegate_task(goal, context, toolset, max_iterations)、crewAI 的 DelegateWorkTool(task, context, coworker)、Anthropic 调度 prompt 的四要素(objective / output format / tool guidance / task boundaries)。

事故对号入座:缺目标会跑偏;缺上下文会瞎猜;缺权限会越权;缺预算会跑飞;缺输出格式会重复劳动——Anthropic 有真实案例:一句话委派”research the semiconductor shortage”,一个工人跑去查 2021 年芯片危机,另两个重复调查 2025 年供应链。

返程也是边的一部分:结论必须带依据。smolagents 的 managed agent 把答案套进 report 模板再附工作摘要,父节点才有能力判断子结论的质量。

源码research/crewAI/.../delegate_work_tool.pyf15844b);smolagents agents.py:868追问:「五要素里最常被漏的是哪个?」输出格式。目标和上下文人人会写,格式不约定,父节点就要写解析代码兜各种自由发挥。

Q3 · 权限:为什么说”边是权限收窄的地方,不是放宽的地方”?给出一个代码层面的证据。

因为子节点的行为父节点无法逐步监督,任何放宽都等于开了无人看守的口子。经典事故:子代理拿到父没有的工具(提权)、子代理再派子代理(递归爆炸)、子代理写父的长期记忆(未验证的结论污染水源)。

代码证据在开源 Codex 的 codex_delegate.rs(commit fa1d4c4):L130 inherited_exec_policy 强制子代理继承父的执行策略——没有绕过通道;L143 inherited_multi_agent_version: Some(MultiAgentVersion::Disabled)——子代理在构造时就被禁用再派活能力,递归禁令是构造函数参数,不是 prompt 恳求。

Hermes 的等价做法:从子节点工具集硬删 5 个工具(再派活、反问用户、写记忆、跨通道发消息、执行代码)。

源码research/codex/codex-rs/core/src/codex_delegate.rs L130/L143。 追问:「为什么防护栏不能写在 prompt 里?」模型会在长上下文里忘掉请求,但绕不过硬编码常量。“请不要递归”是请求,Disabled 是法律。

Q4 · 成本判断:Anthropic 的数据说多 agent 比单 agent 好 90.2%,为什么你仍然应该默认不拆?

看归因:那 90.2% 的提升里,token 用量一项解释了 80% 的性能方差。多 agent 有效的主因是多个独立上下文窗口让系统在一个任务上花更多 token——本质是用钱换性能,不是架构魔法。

所以判断标准是经济的:任务价值配得上成倍的 token 账单吗?子任务真的能并行吗?Anthropic 自己划的适用边界是”广度优先、多方向并行”的查询;线性任务拆成图只会增加交接损耗(每条边都是一次上下文复述,每次复述都丢信息)。

正确的触发信号有三个:上下文互相污染、权限需要分层、有人要进来审批。没有这三个信号,一个循环加好工具就是最优解。

源码:Anthropic multi-agent research system 报告的 BrowseComp 归因分析。 追问:「调度者的预算刻度怎么定?」抄 Anthropic 写进 prompt 的刻度:简单事实 1 agent 3-10 次调用;对比类 2-4 工人各 10-15 次;复杂研究 10+ 工人明确分工。他们加这条是因为早期系统给简单问题 spawn 了 50 个子 agent。

Q5 · 演化:什么叫”从动态往静态冻结”?给出可操作的判断标准和一个真实例子。

动态区域(模型选边)和静态区域(代码定边)共存于一张图,边界随时间移动。健康方向是把跑稳的动态路径改写成确定性代码:不花 token、不抽风、可写单测。

可操作的判断标准一句话:这段路径最近 100 次执行走的是不是同一条路?是,就冻结成代码节点。

真实例子:Anthropic 研究系统的 CitationAgent。补引用这件事路径固定(拿结论和文档、定位出处、插标注),于是从主循环里拆出来,变成流水线尾部的专职节点。反方向同样成立:写死的流程异常分支多到 if-else 写不完,说明它需要即兴,让给 agent 节点。

源码:Anthropic 报告的架构图(LeadResearcher → Subagents → CitationAgent)。 追问:「冻结会不会把 agent 的灵活性冻没了?」冻的是已经不需要灵活性的路径。图工程是持续再平衡——每个季度看一次哪些动态路径已经走成了直线。

Q6 · 开放题:设计一个”自动处理用户退款申请”的系统,画出你的节点和边。哪里用 agent,哪里用代码,哪里用人?

一个参考答案(不唯一,考察的是分工理由):

入口分诊用代码节点(或小模型):按金额和订单状态分流——规则清楚,不需要即兴。小额且符合政策的走代码节点直接退:路径 100% 固定,用 agent 是浪费。争议单走agent 节点:读聊天记录、查物流、判断责任——路径没法预写。超过阈值的金额进人工节点审批:不可逆动作(打钱)事前审批,走工具调用 request_approval(order, reason),超时 24h 自动挂起转人工队列。

边的设计:agent 节点拿到的 toolset 只有查询类工具,不给打款工具(权限收窄);预算 15 步;输出格式固定为「结论 + 三条依据 + 建议动作」。打款永远由代码节点执行,agent 只产出建议。

这个设计里 agent 只出现在一个节点——这是对的。图工程的成熟度看的是每类节点用得是否恰当,不是 agent 数量。

源码:拓扑对照 crewAI Process 两种模式;审批路由对照 Codex 回父 session 的设计。 追问:「如果争议单 agent 的结论质量不稳定怎么办?」加评审回路:第二个便宜模型按 checklist 审一遍,通过才放行,轮次上限 2。这比换更贵的主模型便宜。

  1. 读源码:打开 research/crewAI/lib/crewai/src/crewai/crew.py,找到 check_manager_llm 校验器,回答:为什么 hierarchical 模式强制要求 manager,而 sequential 不需要?
  2. 改设计:把本章退款系统(Q6 答案)里的人工节点加上完整定义:触发条件、输入输出、超时时长、降级路径。写成一个工具的 JSON schema。
  3. 审计题:找一个你用过的多 agent 框架(或你自己写的委派逻辑),对照五要素检查它的委派边:缺哪个?防护栏是在 prompt 里还是在代码里?

本章结论基于以下一手材料(代码库已 clone 到本仓库 research/ 目录,行号以对应 commit 为准):

来源版本读了什么
openai/codexfa1d4c4codex-rs/core/src/codex_delegate.rs(权限继承 L130、递归禁令 L143)、protocol/src/protocol.rs SubAgentSource(L2822)
crewAIInc/crewAIf15844bprocess.py(sequential/hierarchical)、crew.py check_manager_llm、tools/agent_tools/delegate_work_tool.py
huggingface/smolagentse3a5b89agents.py managed agents 的 __call__(task 模板、report 模板、summary_of_work)
How we built our multi-agent research systemAnthropic90.2% 提升、token 解释 80% 方差、委派四要素、预算刻度、50-subagent 事故、CitationAgent
Building Effective AgentsAnthropicworkflow/agent 区分、五种模式原始出处
12-Factor AgentsHumanLayerFactor 5 / 7 / 10

四个 harness 的 subagent 机制横向对照(含 OpenClaw 的 push 事件、Hermes 的 5 工具硬禁源码),见本站第 10 章第 12 章——那里是逐行对照的实现细节,本章是方法。

每个节点内部怎么跑得稳,回到 Loop Engineering