UFO³ Galaxy 中的 Constellation Agent 状态机:四态 FSM 驱动的动态任务编排生命周期详解
2026/9/16 15:06:31 网站建设 项目流程

UFO³ Galaxy 中的 Constellation Agent 状态机:四态 FSM 驱动的动态任务编排生命周期详解

【免费下载链接】UFOUFO³: Weaving the Digital Agent Galaxy项目地址: https://gitcode.com/GitHub_Trending/uf/UFO

UFO³ Galaxy 的 Constellation Agent 是贯穿分布式设备编排的"织网中枢",它依靠一个4 态有限状态机(FSM)来管理自身生命周期:从初始化建图(START)、稳态监控与动态改图(CONTINUE),到成功收尾(FINISH)或异常终止(FAIL)。本文以 documents/docs/galaxy/constellation_agent/state.md 为核心骨架,结合 galaxy/agents/constellation_agent_states.py、galaxy/agents/constellation_agent.py 等源码实现,深入讲解每个状态的处理逻辑、转移矩阵、事件批处理与状态合并机制,并给出状态查询、错误恢复与最佳实践,帮助你掌握这一确定性生命周期控制的完整实现。

为什么需要状态机:把 LLM 推理与确定性控制解耦

在 Constellation Agent 总览 中,Constellation Agent 被描述为"集中式的星座织造者"(Centralized Constellation Weaver),它既要理解用户自然语言意图、生成可执行的 Task Constellation,又要在任务执行过程中根据反馈动态增删改任务与依赖。如果这些行为完全交给大模型自由发挥,就会出现状态不可预期、难以调试的问题。

4 态 FSM 的核心价值在于把 LLM 的语义推理与确定性的控制逻辑分离

  • LLM 负责"想":在创建/编辑两种模式下产出星座结构与修改动作(见 WeavingMode 中的CREATION/EDITING)。
  • FSM 负责"控":确保 Agent 在任何时刻只处于一个明确状态,所有转移都有清晰的触发条件,提升安全性与可调试性。

从源码看,状态机的实现位于 galaxy/agents/constellation_agent_states.py,而驱动它的运行循环位于 galaxy/session/galaxy_session.py,其核心逻辑为:

# Initialize agent in START state self._agent.set_state(StartConstellationAgentState()) # Run agent state machine until completion while not self.is_finished(): # Execute current state await self._agent.handle(self._context) # Transition to next state self.state = self._agent.state.next_state(self._agent) # Update agent state self._agent.set_state(self.state) await asyncio.sleep(0.01) # prevent busy waiting

也就是说,"执行当前状态 → 依据 agent.status 决定下一个状态 → 切换状态"构成了一个确定性的闭环,循环持续直到 Agent 进入终态。

图:Constellation Agent 生命周期状态转换示意(START → CONTINUE → FINISH/FAIL,两个终态无出边)。对应的完整状态转移图与 Mermaid 状态图定义见原文档与下文"状态转换矩阵"章节。

状态空间与状态枚举

四个状态的语义划分

State类型描述进入条件
START初始态初始化并创建星座Agent 实例化、完成后重启
CONTINUE稳态监控事件并处理反馈星座创建成功
FINISH终态成功终止所有任务完成、无需再编辑
FAIL终态错误终止不可恢复错误、校验失败

状态枚举定义

状态枚举ConstellationAgentStatus定义在 galaxy/agents/constellation_agent_states.py:

class ConstellationAgentStatus(Enum): """Galaxy Agent states""" START = "START" CONTINUE = "CONTINUE" FINISH = "FINISH" FAIL = "FAIL"

注意:这里的枚举值是字符串(如"START""CONTINUE"),它们与agent.status字段直接对应。在 ConstellationAgent 构造函数 中,_status初始为"START",并调用self.set_state(StartConstellationAgentState())让 Agent 一开始就处于 START 状态。

状态转移图(Mermaid)

START 状态:初始化与建图阶段

职责

START 是初始化与创建阶段,Agent 在该状态中完成四件事:

  1. 根据用户请求生成初始 Task Constellation;
  2. 校验 DAG 结构的正确性(环路检测);
  3. 启动后台编排任务;
  4. 转移到监控模式。

状态处理实现

StartConstellationAgentState的实现(galaxy/agents/constellation_agent_states.py)核心逻辑如下:

@ConstellationAgentStateManager.register class StartConstellationAgentState(ConstellationAgentState): async def handle(self, agent, context) -> None: # 已处于终态则直接返回(No-op) if agent.status in [ ConstellationAgentStatus.FINISH.value, ConstellationAgentStatus.FAIL.value, ]: return timing_info = {} # 若还没有星座,则进入创建模式 if not agent.current_constellation: context.set(ContextNames.WEAVING_MODE, WeavingMode.CREATION) agent._current_constellation, timing_info = ( await agent.process_creation(context) ) # 启动后台编排(非阻塞) if agent.current_constellation: asyncio.create_task( agent.orchestrator.orchestrate_constellation( agent.current_constellation, metadata=timing_info ) ) agent.status = ConstellationAgentStatus.CONTINUE.value elif agent.status == ConstellationAgentStatus.CONTINUE.value: agent.status = ConstellationAgentStatus.FAIL.value

关键点分析:

  • 幂等保护:如果 Agent 已经处于 FINISH/FAIL 终态,handle直接返回,避免重复创建。
  • 创建模式标记:通过context.set(ContextNames.WEAVING_MODE, WeavingMode.CREATION)把上下文切到创建模式,随后process_creation会经过 ConstellationAgent.process_creation:初始化 prompter → 加载 MCP 上下文 → 交给ConstellationAgentProcessor处理 → 通过_validate_and_update_constellation执行constellation.validate_dag()校验。
  • 非阻塞编排asyncio.create_task()orchestrate_constellation作为后台任务启动,Agent 随即把状态置为CONTINUE,立即进入监控。注意timing_info初始化在前(源码注释明确"Initialize timing_info to avoid UnboundLocalError"),否则星座已存在时该变量未定义会抛错。

行为与错误处理

场景动作下一状态
首次执行通过 LLM 生成星座CONTINUE(成功)/FAIL(出错)
重启触发复用已有星座CONTINUE
创建失败记录错误,无星座产生FAIL
校验失败DAG 含环或结构非法FAIL
已处于终态空操作,立即返回保持原状态

错误处理采用多层try/exceptAttributeError(如上下文字段缺失)、KeyError(如字典缺键)、兜底Exception,三者均把状态置为FAIL并输出完整 traceback(源码实现)。

CONTINUE 状态:稳态监控与动态改图

职责

CONTINUE 是稳态监控与编辑阶段,Agent 在此状态中:

  1. 等待编排器发来的任务完成/失败事件;
  2. 从队列批量收集事件;
  3. 把编排器星座与自身最新修改进行状态合并;
  4. 处理事件并应用编辑;
  5. 循环直到全部任务完成或发生致命错误。

事件批处理:为什么一次只调用一次 LLM

ContinueConstellationAgentState.handle(galaxy/agents/constellation_agent_states.py)的第一步是先阻塞等待至少一个事件,再非阻塞地收走队列中所有积压事件

# Wait for at least one event (blocking) first_event = await agent.task_completion_queue.get() completed_task_events.append(first_event) # Collect other pending events (non-blocking) while not agent.task_completion_queue.empty(): try: event = agent.task_completion_queue.get_nowait() completed_task_events.append(event) except asyncio.QueueEmpty: break

为什么要批量处理?当多个任务并行完成时(例如 3 个任务几乎同时结束):

  • 不批处理:3 次 LLM 调用、3 次编辑会话;
  • 批处理:1 次 LLM 调用、1 次编辑会话处理全部 3 个事件。

带来的收益是:单次 LLM 调用即可反映多个完成事件、修改具备原子性、降低延迟与 API 成本。源码随后把task_ids一次性传给process_editing,并在日志中输出收集到的任务数。

事件源在 galaxy/core/events.py:TaskEventevent_type只允许TASK_COMPLETEDTASK_FAILED进入任务完成队列(由 add_task_completion_event 做类型与事件类型双重校验),这保证了队列内容的合法性。

状态合并:避免编辑"看到旧状态"

在批处理事件之后,Agent 不会直接用编排器快照,而是通过modification synchronizer做实时合并:

async def _get_merged_constellation(self, agent, orchestrator_constellation): synchronizer = agent.orchestrator._modification_synchronizer if not synchronizer: return orchestrator_constellation merged_constellation = synchronizer.merge_and_sync_constellation_states( orchestrator_constellation=orchestrator_constellation ) agent.logger.info( f"🔄 Real-time merged constellation for editing. " f"Tasks before: {len(orchestrator_constellation.tasks)}, " f"Tasks after merge: {len(merged_constellation.tasks)}" ) return merged_constellation

为什么合并至关重要?考虑如下竞态场景:

  1. 任务 A 完成 → Agent 编辑星座(新增任务 C);
  2. 任务 B 在编辑进行中完成;
  3. 不合并:任务 B 的编辑基于旧状态(看不到任务 C),可能产生冲突修改;
  4. 合并:任务 B 的编辑基于合并后的状态(包含任务 C),保证全局一致。

从源码结构看,合并由orchestrator._modification_synchronizer提供,相关实现分布在 galaxy/constellation/orchestrator/orchestrator.py 与 galaxy/session/observers/constellation_sync_observer.py 中,这正是文档强调的"状态同步是关键"的实现依据。

编辑处理与转移判定

合并后,Agent 调用process_editing(context, task_ids, before_constellation=merged_constellation)。编辑完成后,Agent 根据分析结果设置状态:

if constellation.is_complete() and no_more_edits_needed: agent.status = ConstellationAgentStatus.FINISH.value elif critical_error_occurred: agent.status = ConstellationAgentStatus.FAIL.value elif new_constellation_needed: agent.status = ConstellationAgentStatus.START.value else: agent.status = ConstellationAgentStatus.CONTINUE.value # Keep monitoring

process_editing内部([galaxy/agents/constellation_agent.py#L340-L415])还包含一条重要的重启链路_handle_constellation_completion会在旧星座完成但新星座未完成时,把状态重置为START,从而触发新一轮"创建/复用星座 → 后台编排"的循环。

CONTINUE 行为表

场景动作下一状态
任务完成处理事件、应用编辑CONTINUE
多个任务完成批量处理、单次编辑会话CONTINUE
全部任务完成Agent 判定结束FINISH
致命错误处理期间抛异常FAIL
需要重启需要新星座START

FINISH 状态:成功终止

职责与实现

FINISH 表示成功终止,前提是:星座内所有任务成功完成、无需进一步编辑、用户目标已达成。

@ConstellationAgentStateManager.register class FinishConstellationAgentState(ConstellationAgentState): async def handle(self, agent, context=None) -> None: agent.logger.info("Galaxy task completed successfully") agent._status = ConstellationAgentStatus.FINISH.value def next_state(self, agent) -> AgentState: return self # Terminal state - no transitions def is_round_end(self) -> bool: return True def is_subtask_end(self) -> bool: return True

关键特性next_state返回自身,且is_round_end()is_subtask_end()均为True——这意味着 FINISH 是终态,执行轮次与子任务全部收尾,不会有任何出边。

进入条件示例(LLM 决策)

LLM 基于星座状态决定结束,例如:

{ "thought": "All tasks completed successfully. No further actions needed.", "status": "FINISH", "result": { "summary": "Dataset downloaded, model trained, deployed to production", "total_tasks": 5, "completed": 5, "failed": 0 } }

优雅关闭:FINISH 状态保证资源全部释放、最终结果聚合、记忆日志持久化、成功指标记录。

FAIL 状态:错误终止

职责与实现

FAIL 表示错误终止,适用于:创建/编辑阶段出现不可恢复错误、DAG 校验失败、系统级致命故障。

@ConstellationAgentStateManager.register class FailConstellationAgentState(ConstellationAgentState): async def handle(self, agent, context=None) -> None: agent.logger.error("Galaxy task failed") agent._status = ConstellationAgentStatus.FAIL.value def next_state(self, agent) -> AgentState: return self # Terminal state - no transitions def is_round_end(self) -> bool: return True def is_subtask_end(self) -> bool: return True

与 FINISH 相同,FAIL 也是终态,next_state返回自身,避免 Agent 在失败后"意外复活"。

失败场景与恢复策略

场景触发原因恢复方式
创建失败LLM 无法分解请求用户重新表述请求
校验失败生成的 DAG 含环Agent 重试或人工修复
致命异常意外系统错误查日志、重启 Agent
超时处理超出限制增大超时或简化任务

在星座层面,任务还有独立的失败处理机制:TaskStatus枚举(galaxy/constellation/enums.py)定义了PENDINGRUNNINGCOMPLETEDFAILEDCANCELLEDWAITING_DEPENDENCY六种状态,而整个星座的ConstellationState则包括COMPLETEDFAILEDPARTIALLY_FAILED等,供编辑模式判断是否需要新增诊断任务。

状态转移机制

转移矩阵

From ↓ / To →STARTCONTINUEFINISHFAIL
START✅ (success)✅ (error)
CONTINUE✅ (restart)✅ (loop)✅ (done)✅ (error)
FINISH✅ (stay)
FAIL✅ (stay)

转移规则:由状态而非动作驱动

与常见"动作驱动"的 FSM 不同,这里的转移是状态驱动的:next_state只读取agent.status,再交给状态管理器解析出对应的状态对象(galaxy/agents/constellation_agent_states.py):

class ConstellationAgentState(AgentState): def next_state(self, agent) -> AgentState: status = agent.status state = ConstellationAgentStateManager().get_state(status) return state

状态管理器与 @register 装饰器

class ConstellationAgentStateManager(AgentStateManager): _state_mapping: Dict[str, Type[AgentState]] = {} @property def none_state(self) -> AgentState: return StartConstellationAgentState()

状态类通过@register装饰器模式自动注册进_state_mapping,键为状态名(如"START""CONTINUE")。该机制继承自 ufo/agents/states/basic.py 中的AgentStateManagerregisterstate_class.name()映射到类本身,get_state采用懒加载——首次访问才实例化并缓存。none_state默认指向StartConstellationAgentState,因此未知状态会回退到 START。

@ConstellationAgentStateManager.register class StartConstellationAgentState(ConstellationAgentState): @classmethod def name(cls) -> str: return ConstellationAgentStatus.START.value

状态接口参考:AgentState 基类

所有状态都实现AgentState抽象基类(ufo/agents/states/basic.py):

class AgentState(ABC): @abstractmethod async def handle(self, agent, context) -> None: """执行状态专属逻辑""" def next_state(self, agent) -> AgentState: """基于 agent.status 决定下一状态""" def next_agent(self, agent): """多 Agent 场景下的下一 Agent""" return agent @abstractmethod def is_round_end(self) -> bool: """该状态是否标记轮次结束""" @abstractmethod def is_subtask_end(self) -> bool: """该状态是否标记子任务结束""" @classmethod @abstractmethod def name(cls) -> str: """状态标识"""

其中next_agent默认返回当前 Agent,说明该状态机是单 Agent 自循环模型(星座内部的多设备执行交给编排器与各设备 Agent,而非状态机跳转)。is_round_end/is_subtask_end则被上层会话用于判断整个执行是否终结。

状态度量与典型耗时

执行时间线(Gantt 示意)

典型耗时

状态典型耗时影响因素
START2-5 秒LLM 响应时间、校验复杂度
CONTINUE可变(10 秒 - 10 分钟)任务执行时长、并行度
FINISH< 1 秒日志与清理
FAIL< 1 秒错误日志

从源码推断,START 的耗时主体是process_creation中的 LLM 推理与 DAG 校验(validate_dag为 O(n+e) 的环检测),CONTINUE 的耗时则取决于任务执行与事件批处理节奏。

状态查询与可观测性

运行时状态查询

# 查看当前状态对象 current_state = agent.current_state print(f"State: {current_state.name()}") # 判断是否轮次结束(终态) if current_state.is_round_end(): print("Agent execution completed") # 直接读取状态字符串 status = agent.status print(f"Status: {status}") # "START", "CONTINUE", "FINISH", or "FAIL"

状态历史记录

Agent 在记忆日志中维护状态转移历史,每条记录包含步骤、状态、时间戳与星座 ID:

{ "step": 1, "state": "START", "timestamp": "2024-01-01T10:00:00", "constellation_id": "constellation_abc123" }

配合 galaxy/session/galaxy_session.py 中每次转移的日志("Transitioning from X to Y")与 galaxy/core/events.py 的CONSTELLATION_MODIFIED事件(携带编辑前后快照、修改类型、涉及的 task_ids 与 timing 信息),可实现对状态与修改的全链路追溯。

错误处理与恢复策略

异常层级

状态处理统一采用"捕获 → 记录 traceback → 置 FAIL"的模式,START 状态尤为典型:

try: constellation, timing = await agent.process_creation(context) except AttributeError as e: agent.logger.error(f"Attribute error: {e}") agent.status = ConstellationAgentStatus.FAIL.value except KeyError as e: agent.logger.error(f"Missing key: {e}") agent.status = ConstellationAgentStatus.FAIL.value except Exception as e: agent.logger.error(f"Unexpected error: {e}") agent.status = ConstellationAgentStatus.FAIL.value

CONTINUE 状态同样把异常统一导向 FAIL(源码),确保任何未预期异常都不会让 Agent 停留在"半死不活"的中间态。

分类型恢复策略

错误类型所处状态恢复动作
临时网络故障CONTINUE带退避的重试
LLM 响应非法CONTINUE带示例重新提示
DAG 检测到环START快速失败,需人工干预
任务执行超时CONTINUE标记任务失败,星座继续
致命系统错误任意立即转 FAIL

最佳实践与常见陷阱

状态机设计建议

  1. 保持状态聚焦:每个状态只承担单一清晰职责;
  2. 最小化转移:转移越少,调试越简单;
  3. 记录所有转移:带上下文记录状态变更;
  4. 显式处理错误:不要依赖隐式错误传播;
  5. 使用终态:确保执行不会意外恢复。

常见陷阱规避

  • CONTINUE 中的死循环:务必检查终止条件(is_complete()等);
  • 缺失错误处理:未捕获异常会导致状态不可预期;
  • 阻塞操作:使用 async/await 防止死锁(如task_completion_queue.get()必须 await);
  • 状态污染:不要在状态处理器之外随意修改 Agent 状态。

转移日志示例

agent.logger.info( f"State transition: {old_state.name()} → {new_state.name()}" )

测试验证:状态机行为的单元测试覆盖

状态机的行为在仓库中有完整的测试支撑,见 tests/unit/galaxy/agents/test_galaxy_agent_states.py,覆盖了:

  • START 成功路径:星座创建后置为执行态,编排任务被启动一次;
  • START 失败路径:创建返回None或抛出异常时置为 FAIL;
  • CONTINUE 事件处理:任务完成事件驱动编辑、异常时置 FAIL;
  • CONTINUE 转移判定:Agent 决定继续 → 回 START(重启),决定结束 → 转 FINISH;
  • 终态语义:FINISH / FAIL 的is_round_end()is_subtask_end()均为True
  • 状态管理器none_state回退到StartConstellationAgentState@register注册映射正确;
  • 超时配置:不同优先级任务分配不同超时(如GALAXY_TASK_TIMEOUT=1800.0GALAXY_CRITICAL_TASK_TIMEOUT=3600.0)。

此外,tests/unit/galaxy/session 下的会话测试覆盖了"START → CONTINUE → FINISH"的完整状态周期与带延续的重启周期,与本文所述的循环驱动模型互相印证。

总结

Constellation Agent 的四态 FSM 是 UFO³ Galaxy 分布式编排中"确定性控制"的基石:START 建图、CONTINUE 稳态改图、FINISH/FAIL 双终态收口,配合事件批处理、状态合并(modification synchronizer)与@register装饰器注册机制,既保证了 LLM 拥有充分的动态适应能力,又确保了生命周期的可预期、可审计与可恢复。深入理解这套状态机,是掌握 Galaxy 多设备任务编排、排查运行问题、乃至扩展自定义 Agent 行为的必经之路。

相关文档

  • Constellation Agent 总览 — 双模式(创建/编辑)控制循环与整体架构
  • Prompter 实现细节 — Prompter 架构
  • 命令参考 — MCP 工具规格
  • Task Constellation 概览 — DAG 数据模型
  • Constellation Orchestrator 概览 — 任务执行引擎

【免费下载链接】UFOUFO³: Weaving the Digital Agent Galaxy项目地址: https://gitcode.com/GitHub_Trending/uf/UFO

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询