跳到主要内容

UFO³ Galaxy:把请求拆成跨设备 DAG 星座并动态编排

30 秒导读: UFO² 是一台 Windows 电脑里的 GUI Agent(见 0104)。UFO³ 的 Galaxy 在它之上加了一层多设备编排层:把用户一句话拆成一张 DAG「星座」(节点 = 绑定目标设备的可执行任务,边 = 依赖),然后按依赖就绪、跨设备并行派发,并且能在运行中动态改图。本章只讲这层「怎么规划 / 怎么调度 / 怎么改图」;跨设备的实际通信是 06 章 AIP,单设备内执行复用 UFO²。


1. 这是什么(零基础也能懂)

一句话定义: Galaxy 是一个编排层——它不亲自点鼠标,而是把一个大请求拆成许多小任务、算清谁依赖谁、把每个任务分给合适的设备,再盯着它们跑完。

它相对 UFO² 新增了什么? 一张表看清楚:

维度UFO²(单设备)UFO³ Galaxy(多设备编排)
作用域一台 Windows,多个应用多台设备,跨平台
任务模型顺序的 ReAct 循环DAG 星座(可并行、有依赖)
顶层 agentHostAgent + AppAgentConstellationAgent + Orchestrator
UFO² 的角色独立自动化工具被当成一个 Windows 设备 agent

上表出自项目 README「UFO² vs UFO³」对比(README.md:139-151)。关键心智模型:Galaxy 把整台 UFO² 当成星座里的一个「Windows 设备节点」来使唤——单机内怎么落地点击、调 API,是 UFO² 的事;Galaxy 只负责「派活、排期、改图」。

一个类比。 把 Galaxy 想成一个项目经理:

  • 你说「帮我把这份报告在 A 电脑上写好,导出 PDF 后发到 B 手机上」;
  • 项目经理(ConstellationAgent)把它拆成两个任务:①写报告(派给 A)②发 PDF(派给 B),并画一条箭头 ①→②(②要等①出 PDF);
  • 调度器(Orchestrator)先让 A 干活,A 一交货就立刻叫 B 上;
  • 中途发现「还要先装个字体」,项目经理随手往图里插一个新任务——图是活的

用起来什么样。 一次会话就是「一个请求 → 一张星座 → 跑完」。GalaxySession 是入口,它内部建好 ConstellationAgentTaskConstellationOrchestrator(galaxy/session/galaxy_session.py:225-277,GalaxySession.__init__)。

本节不碰代码细节。你只要记住三个词:星座(DAG)、规划(拆图)、编排(调度 + 改图)


2. 顶层全景(它大概怎么转)

Galaxy 的运转是一个规划 → 调度 → 反馈 → 改图的闭环。先看结构图,再逐格解释。

怎么读这张图:上半是「控制平面」(谁做决定),下半是「数据/执行」(图和设备)。虚线是事件回流。

用户一句话请求

┌───────────────▼────────────────┐
│ ConstellationAgent │ ← 规划大脑(状态机 + LLM)
│ creation 模式: 拆成星座 │
│ editing 模式: 收到结果就改图 │
└───────┬─────────────────▲────────┘
│ 产出/改写 │ TASK_COMPLETED 事件
▼ │ (回流,驱动"持续演化")
┌───────────────┐ │
│ TaskConstellation │ ← 数据模型:一张 DAG
│ ● TaskStar (节点) │
│ ─ TaskStarLine(边)│
│ 环检测/拓扑/校验 │
└───────┬───────────┘
│ 交给编排

┌──────────────────────────────┐
│ TaskConstellationOrchestrator │ ← 调度器
│ get_ready_tasks → 并行派发 │
└───┬──────────────────┬─────────┘
│ 能力/资源匹配设备 │ 异步 asyncio.create_task
▼ ▼
ConstellationManager DeviceManager ──► 设备A(UFO²) 设备B(手机)…
(round_robin/capability/ (实际通信 = 第06章 AIP)
load_balance 三种策略)

各部件一句话职责:

部件干什么在哪个文件
TaskStar一个 DAG 节点:可执行任务 + 目标设备 + 优先级 + 状态galaxy/constellation/task_star.py:28
TaskStarLine一条依赖边:from→to,带依赖类型/条件galaxy/constellation/task_star_line.py:22
TaskConstellation整张 DAG:增删改 + 环检测 + 拓扑 + 校验galaxy/constellation/task_constellation.py:31
enumsTaskStatus / DeviceType / TaskPriority / DependencyTypegalaxy/constellation/enums.py:14
ConstellationAgent规划大脑:LLM 把请求拆成图、结果驱动改图galaxy/agents/constellation_agent.py:45
TaskConstellationOrchestrator调度器:依赖就绪 → 跨设备并行派发galaxy/constellation/orchestrator/orchestrator.py:31
ConstellationManager设备分配策略 + 分配校验galaxy/constellation/orchestrator/constellation_manager.py:19
ConstellationEditor命令模式改图,支持撤销galaxy/constellation/editor/constellation_editor.py:34
ConstellationModificationSynchronizer观察者:同步「改图」与「执行」,防竞态galaxy/session/observers/constellation_sync_observer.py:45

主线走一遍(高层):

  1. GalaxySession 起一轮,ConstellationAgentSTART 态,用 creation 模式让 LLM 把请求拆成一张 TaskConstellation;
  2. 校验它是合法 DAG(无环),后台起协程交给 Orchestrator;
  3. Orchestrator 循环:取「就绪任务」→ 匹配设备 → asyncio.create_task 并行派发 → 等任一完成;
  4. 任务一完成就发 TASK_COMPLETED 事件,Agent 转 CONTINUE 态、进 editing 模式,决定要不要改图(加/删/改节点边);
  5. 改完的图并回 Orchestrator,循环继续,直到所有节点终态。

这五步就是本章后面五节的顺序。


3. 数据模型:星座是怎么拼出来的

这节讲静态结构——三个类 + 一组枚举,拼成一张有向无环图。先看它们的关系:

TaskConstellation (一张图 = 一个会话的整份计划)
├── _tasks: { task_id → TaskStar } 节点集合
└── _dependencies: { line_id → TaskStarLine } 边集合

TaskStar ──TaskStarLine(from→to)──► TaskStar
节点自己也记着:
_dependencies: set[前驱 task_id] ← 还差几个前驱没完成
_dependents: set[后继 task_id]

3.1 TaskStar:一个绑定了设备的可执行任务

一个 TaskStar 就是 DAG 的一个节点。它把「要做什么」和「谁来做」捆在一起。构造参数一览(galaxy/constellation/task_star.py:42-72,TaskStar.__init__):

字段含义
description / tips自然语言任务描述 + 提示,最终拼成给设备的请求
target_device_id派给哪台设备(核心:节点自带设备归属)
device_type期望设备类型(DeviceType,用于能力匹配)
priority优先级(TaskPriority,就绪时高优先先派)
timeout / retry_count超时与重试次数
_status生命周期状态(TaskStatus,默认 PENDING)

枚举把「状态机」和「设备种类」标准化了(galaxy/constellation/enums.py):

  • TaskStatus:PENDING → RUNNING → COMPLETED/FAILED/CANCELLED,外加 WAITING_DEPENDENCY(enums.py:14);
  • DeviceType:WINDOWS / MACOS / LINUX / ANDROID / IOS / WEB / API(enums.py:65)——这就是「跨设备/跨平台」在数据层的落点;
  • TaskPriority:LOW=1 … CRITICAL=4(enums.py:54),用整数值好排序。

一个节点「能不能跑」是自洽判断的。 关键是这个属性(task_star.py:411-414):

# galaxy/constellation/task_star.py:411 # 真实源码(节选)
@property
def is_ready_to_execute(self) -> bool:
"""没有未完成的前驱、且自己还 PENDING,才算就绪。"""
return self._status == TaskStatus.PENDING and len(self._dependencies) == 0

_dependencies 是一个「还欠着的前驱 id 集合」;每当一个前驱完成,就把它从这个集合里 remove_dependency 掉——集合空了,节点自然「就绪」。这是整个调度的基本信号

节点真正执行时,execute() 并不自己点鼠标,而是把描述交给 device_manager 派到目标设备(task_star.py:190-218,TaskStar.execute),请求文本由 to_request_string() 拼成(task_star.py:551)。跨设备通信细节留给第 06 章。

3.2 TaskStarLine:一条会「看条件」的依赖边

边不是简单的「A 先于 B」,它带依赖类型,决定「前驱怎样算数才放行后继」(galaxy/constellation/enums.py:27,DependencyType):

依赖类型语义
UNCONDITIONAL前驱一完成就放行
SUCCESS_ONLY前驱成功才放行
COMPLETION_ONLY前驱完成即可,不管成败
CONDITIONAL跑一个自定义 condition_evaluator(前驱结果) 判断

判定逻辑集中在一处(galaxy/constellation/task_star_line.py:182-212,TaskStarLine.evaluate_condition):SUCCESS_ONLY 判「结果非空」,CONDITIONAL 调用注入的函数,异常一律吞成 False(task_star_line.py:214-218)——宁可不放行,也不让一条坏边把图卡死。工厂方法 create_unconditional / create_success_only / create_conditional 让构造更直观(task_star_line.py:458-525)。

3.3 TaskConstellation:守住「这是一张合法 DAG」

整张图的类要做三件硬事:加边不成环、给出拓扑序、算就绪集

① 加边即时防环。 每次 add_dependency 都先查会不会成环,成环直接抛错(galaxy/constellation/task_constellation.py:210-227,add_dependency)。查法是从新边终点做 DFS,看能不能回到起点(task_constellation.py:1158-1179,_would_create_cycle):

# galaxy/constellation/task_constellation.py:1158 # 真实源码(节选)
def _would_create_cycle(self, from_task_id, to_task_id) -> bool:
# 若 to→…→from 已经有路径,再加 from→to 就成环
def has_path(current, target):
if current == target:
return True
...
return has_path(to_task_id, from_task_id)

② 拓扑排序用 Kahn 算法。 按入度削减,若排完的节点数 ≠ 总节点数,说明有环(task_constellation.py:508-547,get_topological_order)。validate_dag() 就靠它 + 「边引用的节点都存在」两条校验(task_constellation.py:483-506)。

③ 就绪集 = 调度的输入。 get_ready_tasks() 收集所有 is_ready_to_execute 的节点,再双保险核对依赖是否真的满足,最后按优先级从高到低排序返回(task_constellation.py:279-294)。调度器每一轮就吃这个列表。

任务完成时的连锁反应mark_task_completed() 处理:标记该节点终态,然后遍历它的出边,对每个后继「评估依赖条件 → 满足就从后继的 _dependencies 里划掉自己 → 后继若因此就绪则加入返回列表」(task_constellation.py:436-481)。这一步把「一个节点完成」变成「一批新节点就绪」。

巧妙处:图自带并行度指标。 get_parallelism_metrics() 会算临界路径长 L、总工作量 W、并行比 P=W/L(task_constellation.py:737-797);任务还没跑完时用节点数估算,全部终态后改用真实执行时长。这让「这张图理论上能并行到多宽」变成可量化的数字(get_longest_path/get_max_width,task_constellation.py:549-649)。


4. 规划:LLM 怎么把一句话拆成一张星座

这节讲动态生成:ConstellationAgent 如何驱动 LLM 产出 §3 那张图。它复用了 UFO² 的 agent 基类与状态机骨架(见 01 章),但状态与产物是星座专属的。

4.1 两种「编织模式」

Agent 用一个枚举区分自己在干嘛(galaxy/agents/schema.py:117,WeavingMode):

  • CREATION —— 从零把请求拆成图(process_creation,galaxy/agents/constellation_agent.py:307);
  • EDITING —— 收到执行结果后改图(process_editing,constellation_agent.py:340)。

它同时实现两个接口 IRequestProcessor(拆图)和 IResultProcessor(改图)(接口定义见 galaxy/core/interfaces.py:482:498),把「规划」和「重规划」这两件事显式拆开——这是 Interface Segregation 的体现。

4.2 LLM 的输出被 schema 兜住

LLM 不是自由输出文本,而是被要求填一个 Pydantic 结构 ConstellationAgentResponse(galaxy/agents/schema.py:361):

# galaxy/agents/schema.py:361 # 真实源码(节选)
class ConstellationAgentResponse(BaseModel):
thought: str # 推理过程
status: str # CONTINUE / FINISH / FAIL
constellation: Optional[TaskConstellationSchema] = None # 产出的整张图
action: Optional[List[ActionCommandInfo]] = None

其中 TaskConstellationSchema(schema.py:229)是 §3 那些运行时类的序列化镜像:它能把 LLM 给的 tasks/dependencies——无论是 list 还是 dict——统一成 dict(convert_lists_to_dicts,schema.py:271-312),自动补 id,并在 validate_unique_ids 里查重(schema.py:314-339)。id 由一个线程安全的单例 IDManager 按星座上下文顺序发号(task_001line_001…,schema.py:13-114),这样 LLM 就算不给 id 也不会撞车。

从 schema 到运行时对象靠一组 from_basemodel 转换(如 TaskConstellation.from_basemodel,task_constellation.py:1104-1119)。这样 LLM 的 JSON 产物就变成了可校验、可执行的真图。

4.3 状态机:START → CONTINUE → FINISH/FAIL

规划不是一次性的,而是嵌在一个状态机里循环跑(galaxy/agents/constellation_agent_states.py)。四个状态(constellation_agent_states.py:24,ConstellationAgentStatus):

┌─────────┐ 首次:process_creation 拆图
│ START │ 校验是合法 DAG,后台起 orchestrate 协程
└────┬────┘ status = CONTINUE

┌──────────┐ 阻塞等 task_completion_queue
│ CONTINUE │ 收到 TASK_COMPLETED → process_editing 改图
└────┬─────┘ 改完并回 orchestrator,继续等
│ (图整体完成)
┌─────┴─────┐
▼ ▼
┌────────┐ ┌──────┐
│ FINISH │ │ FAIL │ 终态
└────────┘ └──────┘

START 态做三件事:没有图就 process_creation 拆一张、校验、然后非阻塞地把编排丢到后台协程(constellation_agent_states.py:80-100):

# galaxy/agents/constellation_agent_states.py:91 # 真实源码(节选)
asyncio.create_task(
agent.orchestrator.orchestrate_constellation(
agent.current_constellation, metadata=timing_info
)
)
agent.status = ConstellationAgentStatus.CONTINUE.value

CONTINUE 态是「持续演化」的心跳:它阻塞等 task_completion_queue 拿到至少一个完成事件,再顺手把队列里其余事件也收干净(批量处理),然后进 editing 模式改图(constellation_agent_states.py:182-230)。改图前它还会先拿一份「合并后的最新图」(_get_merged_constellation,:147),确保看到的是别的编辑轮次可能已经做过的结构改动——这引出了 §7 的同步机制。


5. 调度执行:按依赖就绪、跨设备并行派发

这节讲 TaskConstellationOrchestrator(galaxy/constellation/orchestrator/orchestrator.py:31)——它只管执行流控,把设备/状态管理甩给 ConstellationManager

5.1 主循环:取就绪 → 派发 → 等完成

编排入口 orchestrate_constellation 分五步:校验 → 分配设备 → 发「开始」事件 → 跑主循环 → 收尾发「完成」事件(orchestrator.py:143-220)。心脏是 _run_execution_loop(orchestrator.py:394-432):

while 图没全部终态:
① 检查取消标志(要取消就把图标 CANCELLED,break)
② _sync_constellation_modifications ← 等改图落定、并回最新图(§7)
③ _validate_existing_device_assignments ← 每个任务都得有合法设备
④ ready = constellation.get_ready_tasks()
⑤ _schedule_ready_tasks(ready) ← 对每个就绪任务 create_task 并行跑
⑥ _wait_for_task_completion() ← asyncio.wait(FIRST_COMPLETED)

并行的关键_schedule_ready_tasks:对每个就绪任务起一个独立协程,登记进 _execution_tasks 字典去重(orchestrator.py:468-482):

# galaxy/constellation/orchestrator/orchestrator.py:477 # 真实源码(节选)
for task in ready_tasks:
if task.task_id not in self._execution_tasks: # 不重复调度
task_future = asyncio.create_task(
self._execute_task_with_events(task, constellation)
)
self._execution_tasks[task.task_id] = task_future

于是「同一层无依赖的任务」会同时在不同设备上跑——这就是 DAG 相对 UFO² 顺序循环的核心增益。

单个任务的执行与回报_execute_task_with_events(orchestrator.py:603-696):发 TASK_STARTEDtask.execute(device_manager) 派到设备 → 用 constellation.mark_task_completed 更新图并拿到「新就绪的一批」→ 发 TASK_COMPLETED/TASK_FAILED(带上新就绪列表和整张图)。这个完成事件正是 §4 里 CONTINUE 态在等的东西——执行反馈回流给规划大脑,闭环成立。

5.2 把任务匹配到设备:三种策略

任务要跑,先得有 target_device_idConstellationManager.assign_devices_automatically 支持三种策略(galaxy/constellation/orchestrator/constellation_manager.py:132-185):

策略怎么分代码
round_robin轮流发,雨露均沾_assign_round_robin,constellation_manager.py:187
capability_matchtask.device_type 找匹配设备,匹配不上退回任意设备_assign_capability_match,:212
load_balance每次挑当前负载最低的设备_assign_load_balance,:249

三种都优先尊重显式 preferences(某任务钦点某设备)。可用设备清单来自 device_manager 的已连接设备 + 注册表里的能力信息(_get_available_devices,constellation_manager.py:314-344)。若既没给手动分配也没给策略,Orchestrator 会强制校验「每个任务都已有合法设备」,否则报错列出缺失/非法的任务(_validate_existing_device_assignments,orchestrator.py:298-355)。

真正把任务送上设备的是 device_manager.assign_task_to_device:设备忙就排队等 future,空闲就立即执行(galaxy/client/device_manager.py:543-598)。设备之间怎么用 WebSocket 通信——第 06 章 AIP。


6. 动态重写:用命令模式在运行中改图,还能撤销

这节对应 UFO³ 的设计principle「结果驱动的持续演化」:图不是画完就冻住,而是随执行反馈被增删改。改图不用裸操作 TaskConstellation,而是走一层命令模式(galaxy/constellation/editor/),换来两样东西:原子回滚撤销/重做

6.1 命令模式的四个角色

ConstellationEditor ── 高层门面:add_task / remove_dependency / build_constellation …
│ 每个操作包成一个 Command 对象

CommandInvoker ── 执行 command,成功则压入历史

├─► Command (AddTaskCommand / RemoveTaskCommand / UpdateTask… / BuildConstellation…)
│ execute() 先 _create_backup() 存整图快照,失败即 _restore_backup()

└─► CommandHistory ── 栈式历史,支持 undo / redo
  • Editor(galaxy/constellation/editor/constellation_editor.py:34)是门面,提供 add_task/update_task/add_dependency/build_constellation/clear_constellation 等高层方法,每个都构造一个命令交给 invoker。
  • 每个命令继承 BaseConstellationCommand(galaxy/constellation/editor/commands.py:21),自带 can_execute() 前置校验、execute()undo()
  • Invoker(command_invoker.py:16)只做一件事:can_execute 通过才执行,执行成功且命令可撤销就存进历史(command_invoker.py:35-65)。
  • History(command_history.py:15)是带「当前指针」的栈,undo 往回走、redo 往前走,超上限丢最旧(command_history.py:33-112)。

6.2 每次改图都「先拍快照,坏了就还原」

命令模式在这里最实用的地方,是每个操作都是原子事务。以加节点为例(commands.py:134-162,AddTaskCommand.execute):

# galaxy/constellation/editor/commands.py:134 # 真实源码(节选)
def execute(self) -> TaskStar:
self._create_backup() # ① 存整图快照
try:
self._constellation.add_task(self._task)
is_valid, errors = self._constellation.validate_dag() # ② 加完还是合法 DAG 吗
if not is_valid:
self._restore_backup() # ③ 不合法 → 回滚
raise CommandExecutionError(self, f"...rolled back. {errors}")
...
except Exception as e:
self._restore_backup() # 任何异常都回滚
raise CommandExecutionError(self, ...)

快照就是 constellation.to_dict(),还原就是 TaskConstellation.from_dict() 后把内部字典/状态整体拷回去(commands.py:55-95,_create_backup/_restore_backup)。所以改图永远不会把图改成一张带环的坏图——要么成功、要么当作没发生。这一原子性对「运行中改图」至关重要:调度器随时在读这张图,半成品状态是不能被看到的。

6.3 命令注册表:让改图能力可被发现/按名调用

所有命令类用装饰器 @register_command(name=..., category=...) 注册进一个全局 CommandRegistry(galaxy/constellation/editor/command_registry.py:13:158)。于是 Editor 能 list_available_commands() 列出全部改图能力、按名字 execute_command_by_name(...) 调用(constellation_editor.py:579-614)。命令分成 task_management / dependency_management / bulk_operations / file_operations 四类——这正好覆盖「对运行中的 DAG 增删改」的全部动作面。

注意分工:Editor/命令是「怎么安全地改」;要不要改、改成什么样,是 §4 的 LLM 决定的。ConstellationAgent 在 editing 模式下产出新图后,通过 MCP 的 build_constellation 工具把它同步下去(constellation_agent.py:187-207,_sync_constellation_to_mcp)。


7. 会话与观察:把「改图」和「执行」拧成一股绳

有了「规划(§4)+ 调度(§5)+ 改图(§6)」,还差一个把它们编织在同一次会话里、并防止互相踩脚的机制。这就是 GalaxySession 和它的观察者们。

7.1 GalaxySession:一次会话的总装

GalaxySession.__init__ 把所有部件装配起来:建 Orchestrator、建 ConstellationAgent、注册一组观察者(galaxy/session/galaxy_session.py:170-245)。观察者通过一个事件总线订阅 TASK_* / CONSTELLATION_* 事件(_setup_observers,galaxy_session.py:279-322):

观察者职责
ConstellationProgressObserver把任务完成事件喂回 agent 的队列
SessionMetricsObserver收集性能指标
DAGVisualizationObserver实时可视化这张图
AgentOutputObserver渲染 agent 的思考/动作
ConstellationModificationSynchronizer同步改图与执行,防竞态(下节)

一轮的驱动在 GalaxyRound.run:反复 agent.handle(context) 跑当前状态、next_state 转移,直到状态机进终态(galaxy_session.py:71-134)。这跟 UFO² 的 Round 循环(01 章)同构,只是这里的「一步」是一次规划或一次改图。

7.2 关键竞态:改图 vs 派发

问题很实在(注释直接写在 constellation_sync_observer.py:45-56):

任务 A 完成 ──► 触发"改图"(agent 要动 B 或 B 的依赖)
同时
调度器 ──► 立刻取就绪任务 ──► 可能马上就把 B 派出去了
结果:agent 还没改完,B 已经用旧计划跑了 ✗

ConstellationModificationSynchronizer(galaxy/session/observers/constellation_sync_observer.py:45)用「待处理修改」的 future 表来解决:

  1. 收到 TASK_COMPLETED,就给这个 task 登记一个未完成的 Future(_handle_task_event,:100);
  2. agent 改完图发 CONSTELLATION_MODIFIED,就把对应 future 置完成(_handle_constellation_event,:152);
  3. 调度器每轮开头调 wait_for_pending_modifications() 阻塞等所有 future 落定才继续取就绪任务(:247,并由 orchestrator.py:434-466_sync_constellation_modifications 调用)。

还有一层状态合并防丢结果:agent 的图带着「结构改动」,orchestrator 的图带着「执行进度」,直接互相覆盖会丢掉某任务刚变成的 COMPLETEDmerge_and_sync_constellation_states 以 agent 图为骨架,但对每个任务保留 orchestrator 那份更靠前的状态(:384-451),推进等级由 _is_state_more_advanced 定义(PENDING<WAITING<RUNNING<终态,:453-478)。为防死锁,每个待处理修改还有 600s 超时兜底自动放行(_auto_complete_on_timeout,:218)。

这一节就是 Galaxy「持续演化」能真正跑起来的安全垫:让「一边改图、一边执行」不打架。


8. 边界与局限(诚实)

  • 不做跨设备实际通信。 编排层只产出「派给谁」的意图;真正把请求送达设备、收回结果,是 DeviceManager + AIP(第 06 章)。本层看不到 socket。
  • 不做单设备内的落地执行。 一个 TaskStar 在设备上怎么点按钮、调 API,全交给 UFO²(0204)。Galaxy 只把 UFO² 当一个黑箱设备 agent。
  • 规划质量依赖 LLM。 图拆得好不好、依赖连得对不对,取决于 ConstellationAgent 背后的模型;schema 只保证「结构合法/无环/id 唯一」,不保证「语义正确」。
  • 改图有安全窗口。 只有 PENDING/WAITING_DEPENDENCY 的任务和「目标任务未开跑」的边可安全修改(get_modifiable_tasks/get_modifiable_dependencies,task_constellation.py:332-358);已 RUNNING 的节点改不动(会抛错,task_star.py 各 setter)。
  • 同步靠超时兜底。 改图未及时完成时,同步器 600s 后强制放行(constellation_sync_observer.py:228),极端情况下可能用了略旧的图。

9. 代码地图(导航索引)

主题文件路径关键符号
DAG 节点(任务)galaxy/constellation/task_star.pyTaskStaris_ready_to_executeexecuteto_request_string
DAG 边(依赖)galaxy/constellation/task_star_line.pyTaskStarLineevaluate_conditioncreate_success_only
整张 DAGgalaxy/constellation/task_constellation.pyTaskConstellationadd_dependency_would_create_cycleget_topological_orderget_ready_tasksmark_task_completedget_parallelism_metrics
枚举galaxy/constellation/enums.pyTaskStatusDeviceTypeTaskPriorityDependencyTypeConstellationState
规划大脑galaxy/agents/constellation_agent.pyConstellationAgentprocess_creationprocess_editing_sync_constellation_to_mcp
序列化 schemagalaxy/agents/schema.pyTaskConstellationSchemaConstellationAgentResponseWeavingModeIDManager
规划状态机galaxy/agents/constellation_agent_states.pyStartConstellationAgentStateContinueConstellationAgentStateConstellationAgentStatus
调度器galaxy/constellation/orchestrator/orchestrator.pyTaskConstellationOrchestrator_run_execution_loop_schedule_ready_tasks_execute_task_with_events_sync_constellation_modifications
设备分配galaxy/constellation/orchestrator/constellation_manager.pyConstellationManagerassign_devices_automatically_assign_capability_match_assign_load_balance
改图门面galaxy/constellation/editor/constellation_editor.pyConstellationEditorbuild_constellationundo/redo
命令实现galaxy/constellation/editor/commands.pyBaseConstellationCommandAddTaskCommand_create_backup/_restore_backup
命令执行/历史/注册galaxy/constellation/editor/command_invoker.py · command_history.py · command_registry.pyCommandInvokerCommandHistoryCommandRegistryregister_command
改图/执行同步galaxy/session/observers/constellation_sync_observer.pyConstellationModificationSynchronizerwait_for_pending_modificationsmerge_and_sync_constellation_states
会话总装galaxy/session/galaxy_session.pyGalaxySessionGalaxyRound_setup_observers
接口定义galaxy/core/interfaces.pyITaskIConstellationIRequestProcessorIResultProcessor

相邻章节: 全景导读见 index.md;UFO² 主循环 01;一步怎么走 02;感知定位 03;混合执行 04;设备互联的 WebSocket 通信与容错 06