- 人工智能
- AI Agent
- 多智能体
- MCP 服务
- 工具调用
- 浏览器控制
【免费下载链接】hive
Multi-Agent Harness for Production AI
本文档是 hive(Multi-Agent Harness for Production AI)前端在触发器(Trigger)系统大幅简化后的清理与迁移指南。后端已经彻底塌缩了 colony-lifecycle(Colony 生命周期)这一层:加载一个 colony 的会话(session)即视为"激活",关闭即视为"停用",不再存在独立的激活/停用状态机。随之而来的是一批前端 UI 变为死代码需要删除、一个事件被重命名,同时新增了一个"错过触发器握手(missed-trigger handshake)"的小型交互组件,用于在会话加载时追补会话关闭期间错过的定时触发。阅读完本文,你将掌握:哪些端点/事件/字段/组件必须从现有前端代码中移除,哪些触发器能力保持不变,以及如何从零实现并接入missed_triggers事件与POST /colony/resolve_missed追补接口。
全文按照原文档的Remove(删除)→ Keep(保留)→ Add(新增)→ Migration checklist(迁移清单)骨架组织,并逐节结合当前仓库的后端实现(core/framework/server/routes_sessions.py、core/framework/tools/queen_lifecycle_tools.py、core/framework/host/triggers.py、core/framework/host/event_bus.py)与测试用例(core/tests/test_missed_triggers.py、core/tests/test_missed_triggers_http_e2e.py)给出源码级佐证。
背景:为什么这次简化会发生
简化的本质是一个状态模型的收敛。旧前端需要在四个状态之间做对账:
| 状态来源 | 含义 |
|---|---|
loaded | session 是否已加载 |
metadata.active | 磁盘上写死的激活标记 |
per-triggeractive | 每个触发器自己的激活标记 |
is_executing | 会话当前是否正在执行 |
这四个状态塌缩成了一个用户真正关心的问题——"该 colony 的会话是否已加载?"——外加一个面向高级用户的、每个触发器独立的enabled配置开关。其余全部是人为分层:加载 colony 本身就是激活,关闭 colony 本身就是停用,没有"已暂停但已加载(paused but loaded)"这种中间态。
由此得到触发系统的核心不变量,也是全文所有改动的前提:
触发器仅在 colony 的会话已加载、且该触发器的
enabled为 true 时才会触发。不存在额外的生命周期开关。
Remove:必须删除的前端代码
已消失的端点(全部返回 404)
以下三个端点已从后端路由注册表中移除。在 routes_sessions.py 的路由注册区段中已搜索不到任何colony/state、colony/activate、colony/deactivate注册项,前端凡是还在请求这些 URL 的代码都会收到 404,必须一并删除。
| Endpoint | 状态 |
|---|---|
GET /api/sessions/{id}/colony/state | 404 |
POST /api/sessions/{id}/colony/activate | 404 |
POST /api/sessions/{id}/colony/deactivate | 404 |
这些端点暴露的信息(loaded / active / busy)要么与现有 session 信号冗余,要么是人为构造的,没有独立存在的必要。
已消失的事件
| 事件类型 | 状态 |
|---|---|
colony_activated | 不再发出 |
colony_deactivated | 不再发出 |
activation_missed_triggers | 更名为missed_triggers |
需要取消订阅并删除前两个事件的全部 handler;第三个事件则按新名字missed_triggers重新订阅(细节见下文 Add 部分)。在 core/framework/host/event_bus.py 的事件类型定义中,当前保留的触发器事件族为trigger_available、trigger_activated、trigger_deactivated、trigger_fired、trigger_removed、trigger_updated(event_bus.py),而missed_triggers作为新增事件独立存在(event_bus.py)——colony_activated/colony_deactivated/activation_missed_triggers均不在其中。
已消失的 tool 响应字段
queen 的set_trigger工具不再在响应中返回colony_active,也不再返回"queued_for_next_activation"状态字符串;trigger_activated事件负载同样去掉了colony_active字段。
当前set_trigger的响应已经统一为{ "status": "activated", ... }——触发器已注册,并且(因为 queen 能调用该工具时会话必然已加载)正在触发中。源码可以佐证这一契约:queen_lifecycle_tools.py 中 timer 分支返回的负载只有status、trigger_id、trigger_type、trigger_config;webhook 分支也只是追加了webhook_url(queen_lifecycle_tools.py)。同时 routes_sessions.py 广播的TRIGGER_ACTIVATED事件数据仅包含trigger_id、trigger_type、trigger_config、name、entry_node等字段,PATCH 后的TRIGGER_UPDATED同样不含colony_active。
必须删除的 UI 组件
- colony 头部的 Activate / Deactivate 切换开关。加载 colony 视图本身就是激活,关闭它本身就是停用。该开关只是在重复已有的打开/关闭 colony 交互。
- 由
metadata.last_active_at驱动的 "Active since …" 徽章。该字段已不再落盘。如果确实需要一个"会话开始于"的时间戳,现有 session 响应中的loaded_at即可覆盖。 - queen 对话记录中的 "Queued for next activation" 指示卡片。不再存在"排队"中间态:一个触发器要么已配置(会话打开期间运行),要么已禁用/被移除。
- 侧边栏上区分 "loaded" 与 "active" 的圆点。收敛为单一圆点:仅表示该 session 当前是否已加载。
已消失的metadata.json字段
metadata.active与metadata.last_active_at不再由后端写入。任何在前端读取它们的逻辑都应删除——它们已不属于契约的一部分。
Keep:触发器系统本身不变
触发器系统本身完全未变,以下能力照旧工作:
- 触发器 CRUD UI:通过 queen 的
set_trigger/remove_trigger/list_triggers工具,以及现有的每触发器 HTTP 路由(/triggers/{id}/activate、/triggers/{id}/deactivate、/triggers/{id}/run、PATCH /triggers/{id})。这些路由在 routes_sessions.py 中完整注册,分别对应handle_list_triggers(L1154)、handle_create_trigger(L1173)、handle_update_trigger_task(L1304)、handle_run_trigger(L1450)、handle_activate_trigger(L1551)与handle_deactivate_trigger(L1628)。 trigger_fired事件:只要配置的触发器触发(或resolve_missed注入追补事件)就会发出,前端照旧渲染即可。源码侧由_emit_trigger_fired负责广播(参见 routes_sessions.py 与 queen_lifecycle_tools.py)。trigger_available、trigger_activated、trigger_deactivated、trigger_removed、trigger_updated:触发器 CRUD 时仍然发出,数据形状不变,仅去掉上文提及的colony_active字段。triggers.json中每个触发器的字段:id、name、trigger_type、trigger_config、task、enabled、last_fired_at、next_due_at。注意字段名是enabled(旧的每触发器active字段名已废弃)。源码中set_trigger激活后即设置tdef.enabled = True(queen_lifecycle_tools.py),与此一致。
另外,触发器列表的权威读取入口是GET /api/sessions/{session_id}/triggers,它通过 core/framework/host/triggers.py 的build_trigger_view把持久化的triggers.json定义与运行时状态(enabled、下次触发倒计时、触发统计)合并成 UI 就绪的视图,SSE 的trigger_*事件只负责在其上叠加实时增量。
Add:新增的 missed-trigger 握手
会话关闭期间,定时触发器不会触发。当会话重新加载时,后端会计算这段时间错过的触发(missed ticks),并在 SSE 流上推送一个新事件,前端用一个弹窗让用户决定每个错过的触发器如何处理,最后把决定 POST 回去。
订阅missed_triggers事件
在会话加载时,如果任一启用的 timer 触发器存在过期的last_fired_at(即 cron / interval 的 tick 本应在会话关闭期间触发),会话的 SSE 流上就会落下一个missed_triggers事件:
{ "type": "missed_triggers", "stream_id": "queen", "data": { "colony_id": "...", "missed": [ { "trigger_id": "daily_outreach", "trigger_type": "timer", "count": 3, "ticks": [ "2026-05-19T09:00:00+00:00", "2026-05-20T09:00:00+00:00", "2026-05-21T09:00:00+00:00" ], "next_due_at": "2026-05-22T09:00:00+00:00" } ] } }字段语义与渲染注意事项:
count是错过 tick 的真实总数,必须忠实展示。ticks列表最多截断到 100 条——展示总数时如实呈现count,但如果逐条展示时间戳,列表需要截断。next_due_at是用户不做任何操作时下一次未来触发的时间。- webhook 触发器永远不会被报告(事件驱动型,没有可重建的调度)。
- 所有时间戳都是带显式
+00:00/Z后缀的 UTC ISO 8601。渲染时用new Date(iso).toLocaleString()转换为本地时间,不要直接展示原始 UTC 字符串。
源码与测试佐证:
- 计算逻辑位于 core/framework/host/triggers.py 的
compute_missed:只考虑enabled=true的条目;非timer类型(webhook)直接跳过;没有last_fired_at(注册后从未触发过)的触发器贡献 0 个 missed tick(不会为注册之前的时段追溯触发);cron 与interval_minutes两种调度各自计算count、ticks、next_due_at。函数是纯函数、无副作用。 - 事件类型在 event_bus.py 中有明确注释说明:会话加载时若某触发器的
last_fired_at早于调度期望,即会话在若干次调度触发期间处于关闭状态,UI 弹出逐触发器的握手(fire 一次追补 / skip / reschedule),并把决定 POST 到/api/sessions/{id}/colony/resolve_missed。 - test_missed_triggers.py 验证了截断行为:cron 每 6 秒一次、缺口 1 天时
count == 14400,而len(ticks) == 100(截断生效);另有针对禁用触发器、webhook 触发器、从未触发触发器、非法 cron 的各种跳过用例(test_missed_triggers.py)。
展示 "Catch up?" 弹窗
推荐形态:
- 标题:Catch up while you were away?(离开期间要追补吗?)
- 副标题:简要说明 colony 关闭期间触发器不会触发。
missed数组的每一行:- 触发器名称、错过次数、"next due at <本地时间>"。
- 三个按钮:Fire latest(触发最新一次)/Skip(跳过)/Reschedule(重新排期)。
- 一个Apply按钮,把收集到的全部决定一次性 POST 出去。
前端侧已经预留了实现锚点:在 core/frontend/src/pages/colony-chat.tsx 中有一段明确注释,说明后端会在会话加载后立刻发出missed_triggersSSE 事件,弹窗打开后用户为每个触发器选择fire_latest/skip/reschedule,随后前端 POST/colony/resolve_missed。
POST 到/colony/resolve_missed
POST /api/sessions/{session_id}/colony/resolve_missed Body: { "decisions": { "<trigger_id>": "fire_latest" | "skip" | "reschedule", ... } }每个触发器的决策语义:
fire_latest— 向 queen 注入一次追补触发器事件(负载带catch_up: true,queen 据此压缩工作量),并把last_fired_at推进到当前时间。skip— 不触发,仅把last_fired_at推进到当前时间。reschedule— 把last_fired_at推进到当前时间,并从当前时刻重新计算next_due_at到下一个未来 tick。不触发。
响应 200:
{ "results": { "daily_outreach": "fired", "hourly_check": "skipped", "ghost": "unknown_trigger", "bad": "invalid_decision:explode" } }注意:handler 永远不会因为某一行的坏数据而让整个请求失败——前端应展示"部分成功"而非整体中止。
状态码:
| 状态码 | 含义 |
|---|---|
200 | 请求已处理(逐触发器读取results) |
400 | decisions不是对象 |
404 | 未知 session |
409 | session 未绑定 colony |
源码级实现证据:
- 路由注册位于 routes_sessions.py,处理函数为
handle_resolve_missed_triggers(L1509)。其中:session 解析失败返回对应错误(未知 session → 404);session.colony_id is None返回409(L1523-L1524);JSON 解析失败返回 400(L1528-L1529);decisions非 dict 返回 400(L1531-L1536);内部异常返回 500 并记录日志(L1542-L1547)。 - 核心决策执行位于 queen_lifecycle_tools.py 的
resolve_missed:决策白名单为{"fire_latest", "skip", "reschedule"}(L671);未知决策返回invalid_decision:<value>、未知触发器返回unknown_trigger;fire_latest会调用_inject_catch_up(L696-L721)构造一个带catch_up: True负载的TriggerEvent注入 queen 节点,并广播TRIGGER_FIRED;reschedule通过_next_due_from(L674-L693)用 croniter(cron 表达式)或interval_minutes加法重新计算next_due_at。 - HTTP 层行为有端到端测试覆盖:test_missed_triggers_http_e2e.py 验证了 200(skip / fire_latest / 逐触发器标记)、400(
decisions非 dict)、404(未知 session)、409(session 无 colony)全部分支;单测 test_missed_triggers.py 验证fire_latest注入追补(inject_trigger被调用一次)、skip不触发、reschedule设置未来next_due_at、unknown_trigger与invalid_decision的标记返回。
Migration checklist(迁移清单)
- 删除所有对
/api/sessions/{id}/colony/state、/activate、/deactivate的调用。 - 取消订阅
colony_activated、colony_deactivated、activation_missed_triggers。 - 订阅
missed_triggers(注意事件已重命名)。 - 删除 Activate/Deactivate 切换开关组件。
- 删除 "Active since…" 徽章。
- 删除 queen 对话记录中的 "queued for next activation" 卡片;queen 工具不再返回该状态字符串。
- 停止读取
metadata.active/metadata.last_active_at。 - 停止从
trigger_activated与set_trigger响应中读取colony_active。 - 侧边栏圆点简化成单一状态(session 已加载 vs 未加载),不再有每 colony 生命周期。
- 新增 missed-trigger 握手弹窗,并接入
POST /colony/resolve_missed。
为什么移除它
一句话总结:旧前端需要调和四个状态(loaded、metadata.active、每触发器active、is_executing),如今它们塌缩为一个面向用户的单一问题——"该 colony 的会话是否已加载?"——外加一个面向高级用户的每触发器enabled配置开关,其余都是人为分层。加载 colony 本身就是激活,关闭它就是停用。
触发器的触发条件只有两个:colony 的会话已加载且触发器的
enabled为 true。没有额外的生命周期开关,也没有"已暂停但已加载"的状态。
对前端开发者而言,本次清理的收益是状态机的整体瘦身:删除四个组件/字段来源的冗余逻辑之后,触发器 UI 只剩下"CRUD + 实时状态 + 错过追补"三块,而后两块都已有清晰的后端契约(SSE 事件 + 单一 POST 接口)与测试兜底,可放心重构。
相关源码与测试索引
- 前端握手实现锚点:core/frontend/src/pages/colony-chat.tsx
- HTTP 路由与处理函数:core/framework/server/routes_sessions.py、路由注册 L3060-L3093
- 决策执行与追补注入:core/framework/tools/queen_lifecycle_tools.py、L696-L721
- 错过计算:core/framework/host/triggers.py
- 事件类型定义:core/framework/host/event_bus.py
- 单元测试:core/tests/test_missed_triggers.py
- HTTP 端到端测试:core/tests/test_missed_triggers_http_e2e.py、core/tests/test_missed_triggers_routes_registered.py
- 人工智能
- AI Agent
- 多智能体
- MCP 服务
- 工具调用
- 浏览器控制
【免费下载链接】hive
Multi-Agent Harness for Production AI
相关推荐
Sir Trevor事件系统与扩展开发:掌握编辑器的生命周期
Sir Trevor事件系统与扩展开发:掌握编辑器的生命周期 Sir Trevor是一款专为Web设计的富内容编辑器,其核心优势在于强大的事件系统和灵活的生命周
前端UI组件mojs动画事件系统:从触发到完成的全生命周期
mojs动画事件系统:从触发到完成的全生命周期 动画交互是现代Web应用提升用户体验的核心手段,但要实现流畅自然的动画效果,离不开对事件生命周期的精准控制。mo
前端终极指南:掌握bootstrap-fileinput事件系统全生命周期
终极指南:掌握bootstrap fileinput事件系统全生命周期 🚀 想要构建功能强大的文件上传界面吗?bootstrap fileinput事件系统就
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考