OpenHuman Agent Orchestration:多 Agent 协作控制平面的架构与实现
导读
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_id、agent_id、可选的 parent_agent_id、status、prompt、结果摘要、错误、时间戳与元数据。状态类型是 TinyAgents 的 OrchestrationTaskStatus,终态(terminal values)为 completed、failed、cancelled、timed_out、abandoned。
这个模型在 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 的 OrchestrationTaskStatus。pub 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_id、parent_agent_id、prompt、时间戳、元数据与一个 status_tx watch 发送端——没有任何工具列表字段。
status_tx 的设计细节值得展开:它让 abort_all 能在 crate 硬中止任务之前,先向正在 wait_agents 的并发等待方发布一个终态 Cancelled。否则取消会直接丢弃 sender,等待方看到的将是一个关闭的通道而不是一次取消。
此外,tinyagents/orchestration.rs 中的 SteeringRunClass 从另一个角度强化了安全边界:
Interactive(用户实时聊天轮次):只允许InjectMessage与协作式Pause,其余全部拒绝;Background(分离的后台子 Agent 运行):额外允许Resume、Cancel、Redirect——这是比硬AbortHandle取消更优雅、且只在安全循环边界生效(每次模型调用前排空)的替代方案。
不在白名单内的转向命令会被 crate 以 TinyAgentsError::Steering 拒绝并中止运行。
六、持久化:进程内先行,可序列化面向未来
README 明确指出:首个实现是进程内的(process-local),但状态形状是可序列化的,因此后续 PR 可以在不改动调用方的前提下,将编排会话持久化到应用重启、cron 恢复与线程延续(thread continuation)场景。
当前"持久化方向"的最具体落地是 command_center::control(control.rs)。它提供的四个可持久化控制动词直接对应被移除的内存镜像:
| 动词 | 语义 | 目标状态 |
|---|---|---|
stop |
取消非终态运行 | cancelled |
retry |
重新排队以错误结束的运行 | failed / cancelled / interrupted → pending |
continue |
回答 awaiting_user 运行使其恢复 |
running |
follow_up |
记录跟进指令,状态保持不变 | 不变 |
每个动词都是对持久化运行台账(tinyagents_session::run_ledger)的一次持久化状态迁移:通过 transition_agent_run_status 写入新状态(可清除 error / completed_at,这是 upsert 路径做不到的),并追加一条 run_event 记录该动作进入运行时间线。允许的迁移矩阵由纯函数 plan_transition 定义并被无数据库的单元测试覆盖——例如"stop 对 pending 合法、对 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(如 gmail、notion、slack);当 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_subagents 与 spawn_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 TaskStore(Pending → 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_runs(types.rs):声明式的WorkflowDefinition阶段图——每个阶段命名要扇出的 Agent 与其依赖的阶段,运行器按依赖顺序以有界并发调度;WorkflowSafetyTier(read_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 状态形状都保证了调用方无需改动即可平滑迁移。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python270
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java321
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java220
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript220
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python300