首页
/ OpenHuman Agent Orchestration:多 Agent 协作控制平面的架构与实现

OpenHuman Agent Orchestration:多 Agent 协作控制平面的架构与实现

2026-09-09 21:58:29作者:苗圣禹Peter

导读

OpenHuman 是一个本地优先的个人 AI 项目(面向 Mac / Windows / Linux),其核心引擎用 Rust 实现,支持深度研究、Agent 编排与记忆系统。本文聚焦仓库中 src/openhuman/agent/orchestration 模块——它是 Agent 与 Agent 之间协作的高层控制平面(control plane):负责父子 Agent 的血缘关系(lineage)、生命周期状态、等待/关闭/跟进(wait/close/follow-up)语义以及 UI/诊断事件。读完本文,你将掌握:编排层与执行引擎(agent::harness)的分工边界、spawn_subagent / spawn_parallel_agents 等工具的调用契约、子 Agent 状态模型与终态词汇表、Codex 风格控制面(spawn/wait/abort)的落地形态,以及当前进程内持久化设计与未来的落盘方向。

一、定位:控制平面与执行引擎的分层

模块自述文档 开篇即明确了职责边界:

  • agent_orchestration 是高层控制平面,拥有父子 Agent 血缘、生命周期状态、等待/关闭/跟进语义以及 UI/诊断事件;
  • agent::harness 是低层执行引擎,负责 prompt 构造、策略过滤后的工具可见性、模型选择与子 Agent 循环(sub-agent loops)。

这一分层在 mod.rs 的模块文档中进一步展开:执行层面基于 TinyAgents 图(graph) 扇出——workflow_runs 在图形引擎上调度阶段 DAG,agent_teams 通过条件路由图运行成员,delegation 接线可持久化的 plan→execute⇄review→finalize 图,并行扇出走 tinyagents_graph::parallel::map_reduce。而编排模块自身保留的是产品层:持久的 SQL/JSON 运行台账(run ledger)、校验、取消语义、兼容事件,以及 JSON-RPC/工具响应格式化。

换句话说:编排层只决定"谁在什么时候跑、处于什么状态、如何等待与取消",执行层才决定"这个 Agent 具体怎么思考、能调哪些工具"。这个边界是理解整个模块的关键。

二、当前模块清单(Current Inventory)

README 列出了当前可用的编排工具与执行通道:

组件 职责
agent_orchestration::tools::spawn_subagent 运行一个类型化子 Agent,返回折叠后的结果
agent_orchestration::tools::spawn_parallel_agents 扇出多个相互独立的类型化子 Agent 运行
agent_orchestration::tools::spawn_worker_thread 创建持久化的 worker-thread 转写(transcript)
agent::harness::subagent_runner 类型化子 Agent 的规范执行路径
agent::progress::AgentProgress::Subagent*DomainEvent::Subagent* 生命周期与子工具调用的遥测事件

一个重要的限制在 README 中明确标注:当前 spawn_subagent 工具拒绝 dedicated_thread 参数,直到 worker UI 就绪为止。不过从源码看,这一限制已在演进——spawn_subagent_tool_impl.rs 中的 schema 将 dedicated_thread 描述为"Legacy compatibility flag":当父上下文可用时,委派现在总会创建持久化 worker 线程,该参数不再决定线程的创建。

三、控制面操作:Codex 风格的多 Agent 控制

README 明确指出,编排层预期的规范操作(canonical operations)对标 Codex 风格的多 Agent 控制:

  • spawn_agent:在 TinyAgents 的 DetachedTaskRegistry 中注册一个子 Agent,并通过 agent::harness::run_subagent 运行它;
  • wait_agents:等待一个或多个子 Agent 达到终态,可带超时;观察到终态的子 Agent 会被执行 wait 的调用方从注册表中剪除(pruned)
  • abort_all:向每个存活的子 Agent 发布 cancelled 状态并硬中止(hard-abort)其任务。

这一设计在 ops.rs 中得到了完整实现。模块文档明确说明:这个会话层曾经维护一套自研的进程内任务表(HashMap<String, AgentRecord> + JoinHandle + Notify + 手工终态清扫),而 TinyAgents 的 DetachedTaskRegistry 是它的严格超集——自带状态 watch 通道、协作式取消令牌、硬中止句柄、按 owner 作用域的查找、wait/timeout 循环以及软上限终态清扫。因此 spawn_agent / wait_agents / abort_all 现在只是对 register / wait / cancel_all薄产品包装

源码中两个值得注意的常量(ops.rs):

  • REGISTRY_SOFT_CAP: usize = 256——每个会话存活子 Agent 的软上限,只有表增长超过该值时才会清扫终态条目;
  • UNBOUNDED_WAIT_CHUNK: Duration = Duration::from_secs(3600)——TinyAgents 的 wait 需要具体截止时间,因此 timeout_ms: None(无限等待)会按 1 小时为一段循环等待,保持旧的"永远等待"契约。

另外有一个行为后果值得使用者注意:wait 一旦观察到终态就会剪除条目,因此一个子 Agent 只能被等待一次。仓库内所有调用方都是"spawn 一次、wait 一次",running_subagents 也遵循同样的契约。

曾经的内存镜像与持久化的替代者

README 特别说明:内存版的 list_agents / message_agent / close_agent / follow_up / resume_agent / events 镜像已被移除,其持久化等价物位于 command_center::control。这一点我们会在第六节展开。

四、状态模型:每个子 Agent 的稳定标识与终态词汇表

README 定义了子 Agent 的状态模型:每个子 Agent 具有稳定的 orchestration_idagent_id、可选的 parent_agent_id、status、prompt、结果摘要、错误、时间戳与元数据。状态类型是 TinyAgents 的 OrchestrationTaskStatus终态(terminal values)为 completedfailedcancelledtimed_outabandoned

这个模型在 types.rs 中被序列化为可持久化的公共数据模型:

pub struct SpawnAgentRequest {
    pub agent_id: String,
    pub prompt: String,
    pub context: Option<String>,      // 可选上下文
    pub toolkit: Option<String>,      // Composio 工具集
    pub model: Option<String>,        // 本次 spawn 的模型覆盖
    pub parent_agent_id: Option<String>,
    pub metadata: BTreeMap<String, String>,
}

pub struct AgentSnapshot {
    pub orchestration_id: String,
    pub agent_id: String,
    pub parent_agent_id: Option<String>,
    pub status: OrchestrationTaskStatus,
    pub prompt: String,
    pub result_summary: Option<String>,
    pub error: Option<String>,
    pub created_at: String,
    pub updated_at: String,
    pub metadata: BTreeMap<String, String>,
}

types.rs 的模块注释揭示了一个历史变迁:编排层曾有一套宿主自有的 AgentStatus 拷贝,控制平面迁移到 crate 的 DetachedTaskRegistry 后即被退役,现在两套词汇表统一为 TinyAgents 的 OrchestrationTaskStatuspub use tinyagents_graph::orchestration::OrchestrationTaskStatus; 这一行就是统一入口。

tinyagents/orchestration.rs 中可以进一步看到这套状态词汇背后的运行时机制:每个子任务在 TaskStore 中经历类型化的生命周期记账(Pending → Running → Completed/Failed/Cancelled/…),取代了早先"自研状态枚举 + watch 通道 + tombstone 集合"的临时方案;DetachedTaskRegistry 则负责进程内状态、取消、硬中止、所有权与转向(steering)机制。

五、策略继承:只加血缘与生命周期,不扩大工具可见性

README 对安全边界给出了明确要求:策略继承被委托给 agent::harness::run_subagent,后者已经从父 ParentExecutionContext 派生子 Agent 的工具、模型路由、沙箱上下文、spawn 深度与进度。编排层只应添加血缘与生命周期语义,绝不能扩大 harness 暴露给子 Agent 的工具可见性

这一约束在代码中体现为"薄包装"式设计:AgentOrchestrationSession 持有的 ChildRegistry 类型是 DetachedTaskRegistry<ChildMetadata, ChildState>ops.rs),ChildMetadata 仅保留 agent_idparent_agent_idprompt、时间戳、元数据与一个 status_tx watch 发送端——没有任何工具列表字段

status_tx 的设计细节值得展开:它让 abort_all 能在 crate 硬中止任务之前,先向正在 wait_agents 的并发等待方发布一个终态 Cancelled。否则取消会直接丢弃 sender,等待方看到的将是一个关闭的通道而不是一次取消。

此外,tinyagents/orchestration.rs 中的 SteeringRunClass 从另一个角度强化了安全边界:

  • Interactive(用户实时聊天轮次):只允许 InjectMessage 与协作式 Pause,其余全部拒绝;
  • Background(分离的后台子 Agent 运行):额外允许 ResumeCancelRedirect——这是比硬 AbortHandle 取消更优雅、且只在安全循环边界生效(每次模型调用前排空)的替代方案。

不在白名单内的转向命令会被 crate 以 TinyAgentsError::Steering 拒绝并中止运行。

六、持久化:进程内先行,可序列化面向未来

README 明确指出:首个实现是进程内的(process-local),但状态形状是可序列化的,因此后续 PR 可以在不改动调用方的前提下,将编排会话持久化到应用重启、cron 恢复与线程延续(thread continuation)场景。

当前"持久化方向"的最具体落地是 command_center::controlcontrol.rs)。它提供的四个可持久化控制动词直接对应被移除的内存镜像:

动词 语义 目标状态
stop 取消非终态运行 cancelled
retry 重新排队以错误结束的运行 failed / cancelled / interruptedpending
continue 回答 awaiting_user 运行使其恢复 running
follow_up 记录跟进指令,状态保持不变 不变

每个动词都是对持久化运行台账(tinyagents_session::run_ledger)的一次持久化状态迁移:通过 transition_agent_run_status 写入新状态(可清除 error / completed_at,这是 upsert 路径做不到的),并追加一条 run_event 记录该动作进入运行时间线。允许的迁移矩阵由纯函数 plan_transition 定义并被无数据库的单元测试覆盖——例如"stoppending 合法、对 completed 不合法"这类规则全部可测试。

与之配套的只读投影在 command_center/types.rs 中:后台 Agent 命令中心把运行台账细粒度的 AgentRunStatus 归并为五个用户可见分组needs_input / working / completed / failed / stopped),显示顺序把"需要输入"放在最前,让阻塞中的工作最显眼;AgentWorkRow 则携带 run_id、kind、agent_id、bucket、父线程 id、worker 线程 id、摘要、错误、时间戳、耗时、token 消耗与费用(USD)等遥测字段。

七、源码级深入:两大核心工具的调用契约

spawn_subagent:单任务委派

工具实现位于 spawn_subagent.rs 与其实现文件 spawn_subagent_tool_impl.rs。其语义是:编排器(或任何注册了该工具的父 Agent)调用它把一个聚焦子任务交给专门化的子 Agent——查找全局 AgentDefinitionRegistry 中的定义、按定义过滤父工具注册表、构建窄化的系统 prompt、用父 Provider 跑内层工具循环,最后把子 Agent 的循环历史折叠成单段文本结果作为普通 tool_result 返回给父 Agent。

参数 schema 要点(均可从源码确认):

参数 说明
agent_id 必填。子 Agent id,schema 从全局注册表动态生成枚举;archetype 是已弃用的向后兼容别名
prompt 必填。子 Agent 对父对话无记忆,需包含其行动所需的全部上下文
context 可选。此前任务结果的上下文块,渲染为 prompt 前的 [Context]
model 可选。仅本次 spawn 生效的精确模型 id,保留父 Provider/路由但固定子模型
toolkit 可选。Composio 工具集 slug(如 gmailnotionslack);agent_id = "integrations_agent" 时必填,用于收窄子 Agent 可见的 Composio 动作
blocking 默认 false;传 true 则内联运行并直接返回子 Agent 最终输出
task_key 可选。可复用异步委派的确定性标识键,默认取归一化的 prompt/标题
fresh true 时绕过可复用子 Agent 匹配,创建全新持久 worker

工具的 description 还包含一条重要的使用纪律:不要在循环里反复调用 spawn_subagent 来扇出——每次调用只委派单个任务、不会并发启动 worker,会把整个请求串行化;要并发请用 spawn_parallel_agents 一次调用启动多个 worker。

工具对失败的分类也值得借鉴(classify_subagent_failure):当错误消息包含 "no healthy upstream"、"upstream_unhealthy"、"provider call failed: all providers/models failed" 等特征时,会明确标注为上游推理不可用(LLM Provider 故障/容量问题),而非集成认证问题,并建议避免立即重试。

spawn_parallel_agents:并发扇出

spawn_parallel_agents.rs 实现并发扇出,schema 要求 tasks 数组(minItems: 2),每个任务包含:

任务字段 说明
agent_id / prompt 必填,同上
context / toolkit 可选,同上
ownership 可选。该 worker 的互斥文件/模块/职责边界
isolation none(默认,共享工作区)或 worktree(为可编辑 worker 提供独立 git worktree 检出,避免并行编辑冲突)
base_ref isolation = worktree 时使用:head(默认,从当前 HEAD 分叉)或 fresh(从仓库默认分支分叉)

核心规则:只读与 worktree 隔离的 worker 可并行;共享工作区且带写能力工具的 worker 要求互斥 files: 所有权,否则走串行回退。该工具还绕过了全局的逐工具墙钟截止时间(这是长时间并行研究任务的设计选择),并通过 spawn_parallel_graph 实现了带取消与工作区所有权边界(with_ownership_boundary)的图执行。

后台运行与转向:running_subagentsspawn_worker_thread

running_subagents.rs 记录了在飞(in-flight)异步子 Agent 的注册表:每个异步子 Agent 在 TinyAgents 的 DetachedTaskRegistry 中以 task_id 为键注册,并携带:

  • Arc<RunQueue>——转向通道,使 steer_subagent 能在没有 crate 原生转向句柄时注入消息;
  • TinyAgents SteeringHandle——子运行激活期间注册到进程内 SteeringRegistry
  • watch::Receiver<SubagentStatus>——使 wait_subagent 能阻塞到子 Agent 达到终态;
  • AbortHandle——供 subagent_cancel / close_subagent 停止分离工作。

同时每个分离子 Agent 还会作为 OrchestrationTaskKind::SubAgent 写入进程级 TinyAgents TaskStorePending → Running,随后镜像为 Completed/Failed/Awaiting,取消路径记录 Cancelled),提供类型化、可查询的生命周期记录(task_records)。

spawn_worker_thread 的实现(spawn_subagent.rs 中的 persist_worker_thread)会把 prompt 折叠为线程标题(上限 WORKER_THREAD_TITLE_MAX_CHARS = 80,与 UI 线程列表的可见字符上限保持一致),创建带 tasks 标签的持久线程并写入用户消息。

八、面向未来:声明式工作流与多 Agent 团队

编排目录下还有两块面向长期演进的模块值得关注:

  • workflow_runstypes.rs):声明式的 WorkflowDefinition 阶段图——每个阶段命名要扇出的 Agent 与其依赖的阶段,运行器按依赖顺序以有界并发调度;WorkflowSafetyTierread_only / standard / edit_capable)决定子 Agent 权限,当前仅 read_only 先行落地,可编辑层级等待引擎落地后以显式用户批准为门槛。保持声明式是为了规避任意进程内脚本执行带来的安全面。
  • agent_teams:通过条件路由图运行团队成员,并提供注册控制器 schema(all_agent_team_controller_schemas 等)。
  • subagent_sessions / parent_context:子 Agent 会话存储与父执行上下文构造(builder.rs),是策略继承与血缘追踪的基础设施。

README 还指出了进一步的上游迁移方向:running_subagents 将更多分离子 Agent 生命周期移植到 TinyAgents 任务存储,这一工作跟踪于 docs/tinyagents-migration-plan-2026-07-22.md 的 WP-5。

小结

OpenHuman 的 agent_orchestration 模块展示了一条清晰的分层演进路线:把"编排"与"执行"彻底分离,把进程内自研机制逐步收敛到 TinyAgents 的类型化原语之上。当前形态下,开发者可以依赖的稳定接口包括:spawn_subagent(单任务委派,参数 schema 动态生成)、spawn_parallel_agents(带所有权/隔离语义的并发扇出)、wait / cancel 语义(终态剪除、一次等待)、五个终态词汇(completed / failed / cancelled / timed_out / abandoned),以及通过 command_center::control 提供的四个持久化控制动词(stop / retry / continue / follow_up)。无论未来是否引入跨重启持久化,可序列化的 AgentSnapshot 状态形状都保证了调用方无需改动即可平滑迁移。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
932
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.95 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23