首页
/ LobeHub Agent Goals 设计解析:如何基于 Task 系统构建可持久化的 7×24 目标闭环

LobeHub Agent Goals 设计解析:如何基于 Task 系统构建可持久化的 7×24 目标闭环

2026-09-07 20:03:48作者:冯梦姬Eddie

导读

在 LobeHub 中,"Agent 持续工作"(7×24 运行)到底意味着什么?既不是一条永远挂着的聊天会话,也不是常驻内存的守护进程,而应当是一套以数据库为最终权威、可随时从持久化状态恢复的目标驱动闭环。本文以 docs/development/agent-goals-design.md 为骨架,完整讲解 Agent Goals 的领域模型、运行时架构、决策状态机、预算与安全、租约与幂等规则以及 MVP 分阶段落地路径,并结合当前仓库中 goals 表结构goalGraph 相关 schema 与测试goal-graph CLI 设计等源码证据做纵深佐证。读完你将掌握:目标(Goal)为什么不能退化成任务(Task)聚合、控制器回合(controller turn)如何做到"有限但可持续"、以及如何在任务树之上叠加验证、预算、唤醒与租约语义。


一、总体设计:Goal 是 Task 之上的"持久契约层"

1.1 核心论断

设计文档开门见山给出判断:Agent Goal 既不应是另一种聊天会话,也不应是一个永远活着的进程。它的正确形态是一个 durable control loop(可持久化控制循环),执行计划由现有的任务树(task tree)承载。文档给出的层级关系如下:

Agent
  -> Goal (intent, success contract, policy, budget, lifecycle)
    -> root Task (current plan)
      -> subtasks + dependencies (execution graph)
        -> Task Topics (individual attempts)
          -> Agent Operations (runtime/cost/trace)

这一结构的分工非常清晰:

  • Agent 是目标的所有者与执行者;
  • Goal 是"长期有效的结果契约"(long-lived outcome contract);
  • Tasks 是"可变更的执行计划"(mutable execution plan);
  • Topics 是"有限的单次尝试"(finite execution attempts);
  • 任何 worker 或 LLM 调用都不在两次尝试之间常驻。

设计的关键意图是"复用任务系统最强的部分,而不是让每个 Task 都变成永久性的 Agent Goal"。这与仓库当前实际落地方向一致:查看 goals 表 schema 注释可知,goal 被定位为"独立的目标实体(independent target entity)",拥有自己的定义(title/requirement)、预算与生命周期状态,并且不绑定在单个任务上——其执行载体是可选的多态链接(subject_type / subject_id),同一个表可承载 task 驱动的 goal、会话中直接声明的 goal 或独立 goal 声明。

1.2 为什么仍然需要一等公民的 Goal

任务系统本身已经提供了非常强的执行能力,文档明确罗列了这些已有能力:

  • 被指派的 Agent(assignee agents);
  • 无界的任务树与依赖边(task trees and dependency edges);
  • 心跳与 cron 自动化(heartbeat and cron automation);
  • 每次运行的历史 topic 与交接摘要(topic-per-run history and handoff summaries);
  • 操作关联、成本数据、文档、评论与简报(operation linkage, cost data, documents, comments, briefs);
  • 人工检查点与暂停/恢复状态(human checkpoints and pause/resume states);
  • 基于 Verify 的交付验收(verify-backed acceptance)。

既然任务已这么强,为什么任务只能当"执行基座"而不能直接当"目标聚合根"?文档给出了属于持久目标、但不应归属于某个计划节点的语义清单:

  1. 跨越重规划的不可变目标(invariant objective that survives replanning);
  2. 显式、可被机器求值的成功契约(explicit machine-evaluable success contract);
  3. 与任务执行状态解耦的控制器生命周期(controller lifecycle);
  4. 来自时间与外部事件的唤醒策略(wake-up policies);
  5. 全局的成本/时间/迭代/并发预算(global budgets);
  6. 一份"控制器为什么继续、等待、重规划或停止"的记录(controller rationale audit);
  7. Agent 级别的所有权,包括当前激活了哪些目标。

文档还点出了问题导向:如果把这些语义全部塞进 tasks.config,后果是——根任务被过度加载(overloaded)、按 Agent 检索目标的能力很弱(weak goal discovery)、状态迁移变得含糊(ambiguous status transitions)、且未来的重规划无法干净地替换根计划。


二、领域模型设计

2.1 agent_goals 表设计(设计文档建议)

文档建议为目标聚合新增一张表,并给出了完整的字段设计:

Field Purpose
id Stable goal identity(稳定目标标识)
agentId Agent that owns and pursues the goal(拥有并追求该目标的 Agent)
userId, workspaceId, visibility 沿用现有所有权与工作区规则
rootTaskId 当前根任务/计划入口
title, objective 人可读的结果描述;run 激活期间 objective 不可变
successCriteria 结构化的 Verify 标准与评估器配置
status draft, active, waiting, paused, achieved, blocked, canceled, failed
policy 重规划节奏、重试/熔断、允许的触发器、并发度
budget 最大 USD、激活运行时长、轮数、操作数、可选截止时间
progress 缓存的摘要、评分、最近决策与计数器(供低成本 UI 读取)
nextWakeAt 持久化的基于时间的唤醒提示
leaseOwner, leaseExpiresAt 单一控制器的执行租约
startedAt, completedAt, timestamps 生命周期审计

文档同时给出两条落地约定:

  • 草稿创建阶段 rootTaskId 允许为 NULL;
  • 不要把 goalId 只放进 task 的 JSONB 里,而应新增可空的、带索引的外键 tasks.goalId,这样每个计划节点与操作都能被低成本地查询;根任务就是满足 id === goal.rootTaskId 的那个节点。

并发模型上:一个 Agent 可以拥有多个 Goal,但 MVP 阶段默认一个 Agent 只运行一个激活 Goal;后续支持多激活 Goal 属于"调度策略决策",不需要推翻 schema。

2.2 agent_goal_runs:只追加的控制器决策流

每个控制器决策都应成为一条 append-only run(与 task topic 相区分),字段设计如下:

Field Purpose
goalId, seq 有序的控制器历史与幂等边界
triggerType, triggerId createdtimersignaltask_completedverify_settledmanual
status queuedrunningwaitingcompletedfailedcanceled
decision executedecomposereplanverifywaitachievepauseblock
reason 人可读的控制器决策理由
inputSnapshot, output 可复现的状态与已选动作
operationId 当规划需要 LLM 时,所使用的 Agent Operation
timestamps 延迟与审计

文档特别强调幂等:对持久触发器身份加唯一约束,例如 (goalId, triggerType, triggerId),从而保证 at-least-once 的队列投递不会产生两轮重复决策

2.3 可选的 agent_goal_events

文档明确给出"第一个 migration 不要加这张表"的建议,除非产品需要用户可见的、且无法重建的活动流。Agent Signal 的 traces 加上 agent_goal_runs 已能覆盖最初的审计需求——这正是"表能不加就不加,用已有可推导数据重建"的务实取舍。

2.4 与仓库当前实现的对照

需要特别说明:设计文档描述的是目标架构(agent_goals 聚合 + rootTaskId),而当前仓库中 goals 表已经以"多态载体"的形态先行落地。查看 packages/database/src/schemas/goal.ts,真实表 goals 的字段包括:

  • 归属字段:userIdworkspaceIdagentIdprojectId(冗余存储以便列表查询与访问控制);
  • 目标定义:title(非空)、requirement("什么算完成"的验收需求来源)、configGoalConfig 类型的 jsonb,运行时自动恢复与有界 Work 执行的策略);
  • 外层预算:maxRounds(round 预算,null 表示不设上限)、maxTotalCost(跨所有 rounds 的总 USD 预算,null 表示不设上限);
  • 生命周期:status,默认值为 planning,状态枚举来自 @lobechat/const/goalgoalStatuses
  • 多态执行载体subjectType'task' | 'topic' | 'standalone' | null,枚举来自 goalSubjectTypes)+ subjectId,刻意不加外键,因为载体可能位于不同表;
  • 审计时间列、回收站软删除列(schemas/trash.ts 机制),以及 userId/workspaceId/agentId/projectId/status/subject 六组索引。

从源码注释可以明确读到仓库的设计立场:"一切执行相关的东西(rounds、cost、duration、验收检查)都留在载体及其既有表上"——这与设计文档"任务系统承担执行基座"的思想一脉相承,只是把"执行计划入口"从强约束的 rootTaskId 泛化成了可空的多态引用。仓库中同时存在的 goalGraph.tsgoalTrace.tsgoalGraph 测试,以及配套的 const/goal.ts,说明 goal 相关的图结构与 CLI 已经具备可运行原型。


三、运行时架构:有限控制器回合,而非常驻进程

3.1 "7×24" 的真正含义

文档做了一个重要澄清:"7x24 running"指的是目标随时可以从持久状态恢复,绝不意味着一条敞开的 HTTP 响应、一个永无止境的 LLM 循环、或一个纯内存的定时器。

每一个控制器回合(controller turn)都是有限的,其流程为:

wake event
  -> acquire goal lease
  -> load goal + task graph + latest handoffs + verify state + budgets
  -> choose exactly one bounded decision
  -> persist decision and enqueue/execute bounded actions
  -> set nextWakeAt or wait for an event
  -> release lease

执行载体上,文档建议:生产环境应使用持久化队列/工作流(durable queue/workflow);本地开发模式可以使用进程内调度以方便开发者,但必须明确文档化为"跨重启不持久"

3.2 控制器是"窄输出"而非通用聊天 Agent

控制器不应是又一个通用对话 Agent,文档建议给它一个窄的输出 schema:

type GoalDecision =
  | { type: 'execute'; taskIds: string[] }
  | { type: 'decompose'; parentTaskId: string; tasks: ProposedTask[] }
  | { type: 'replan'; mutations: TaskGraphMutation[]; reason: string }
  | { type: 'verify'; subjectTaskId: string }
  | { type: 'wait'; reason: string; wakeAt?: string }
  | { type: 'achieve'; evidence: EvidenceRef[] }
  | { type: 'pause'; reason: string }
  | { type: 'block'; reason: string; requestedInput?: string };

约束规则:

  • 一个回合可以并行启动一组已就绪的任务,但要受目标并发策略约束;
  • 控制器绝不能递归调用自己
  • 任务完成事件负责入队下一个控制器回合。

3.3 唤醒来源:Agent Signal 作为事件路由层

文档建议用 Agent Signal 做事件解释与路由层:

task topic completed / verify settled / document changed / connector event / timer
  -> source event
  -> signal.goal.wake-requested
  -> action.goal.enqueue-turn
  -> durable goal workflow

职责划分的关键原则:

  • Agent Signal 负责判断事件是否重要并去重(dedupe)
  • Goal 控制器负责目标状态迁移与任务计划变更
  • 这样可以避免把每一个 connector 或 task hook 都耦合到目标编排上。

对于时间类唤醒,可以"概念上复用任务调度器",但应当暴露通用的持久 API scheduleGoalWake,而不是把每次唤醒伪装成一次心跳 task topic。延迟消息是一次性的——处理完后由控制器决定是否还需要下一次唤醒。这与 goal-graph CLI 的 tick 语义完全同构:goal tick 只推进一个确定性迁移,goal run 反复调用 tick,遇到人工门禁、预算耗尽或无进展边界即停止。

3.4 任务执行与反馈闭环

现有 TaskRunnerService 仍是启动任务工作的唯一路径。Goal 控制器只负责"选择就绪任务",自己不直接执行工具。当一个 topic 完成时,按以下顺序推进:

  1. Task 生命周期照旧持久化 status、handoff、brief 与 verify 关联;
  2. 一条 source event 记录该持久事实;
  3. 若该任务带 goalId,Goal 策略将其映射为一次唤醒请求;
  4. 控制器重新加载权威 DB 状态并决定下一步。

这套设计让生命周期钩子保持"纯观察性"(observational),从而使重试变得安全。


四、成功、重规划与停止

4.1 成功是被验证出来的,不是自封的

控制器可以提议 achieve,但目标只有在其结构化成功标准通过之后才能进入 achieved。文档明确要求"复用 Verify 作为验收平面(acceptance plane)";证据必须引用持久工件、任务输出、connector 对象或检查结果,而不能只靠一段 LLM 叙述。

文档给出了经典的双层循环结构:

inner loop: one task attempt -> verify/repair that delivery
outer loop: goal progress review -> execute more tasks or replan the task graph

并且点名了既有的 feat/goal-loop-server 探索分支的价值:"一次失败的 verify 结果可以派生一个新 topic,把 handoff 与失败检查项向前携带,并在轮数/成本预算处停止"——这是非常有用的内层原语。Agent 级目标控制器应当做的是把外层循环泛化,而不是在每个任务里复制这套逻辑。

4.2 waitingblockedpaused 三者必须区分

这是容易混淆的状态语义,文档给出精确定义:

  • waiting:健康状态,正在等待一个已知事件或未来时间,可自动恢复
  • blocked:Agent 在缺少新授权/输入的情况下无法取得有意义进展,除非阻塞条件变化,否则绝不自动恢复
  • paused:被用户、策略、熔断或预算显式停止,必须显式 resume 或策略变更才能恢复;
  • failed:重试策略耗尽后仍发生的不可恢复控制器/系统故障。

文档特别提出了一条经验法则:目标不应因一次薄弱尝试就进入 blocked。应持久化"重复条件指纹"(repeated-condition fingerprint),只有当同一条件持续触发到配置的控制器回合数时才真正 block。

4.3 预算与安全:硬性服务端限额

每个激活目标都必须有服务端强制执行的硬限制:

  • 总成本与每日成本(total cost and per-day cost);
  • 最大控制器回合数与最大任务尝试数;
  • 最大并发 task topics;
  • 可选的截止时间与活跃小时窗口(active-hours window);
  • 连续运行时故障熔断(consecutive runtime failure fuse);
  • 允许的 tool/plugin 策略与人工审批策略。

两条执行纪律值得注意:

  1. 预算检查既要发生在入队前,也要发生在真正执行前——因为数据库才是权威(queued work 可能已经过期)。
  2. 绝不承诺字面上的不间断执行:计费失败、凭据被吊销、connector 中断、审批要求、基础设施事故,都必须迁移到"可见状态 + brief",而不是无限空转。

这一思想与 goal-graph CLI 的场景完全对应:lh goal set-budget <goal-id> --max-rounds 20 --max-cost 10 后,预算耗尽会暂停协调,但 lh goal resume + lh goal run 即可恢复。


五、租约与幂等规则

文档假定投递语义为 at-least-once,因此必须自己保证幂等,规则如下:

  1. 在创建 run 之前,先获取以 goalId 为键的短时 DB/Redis 租约
  2. 用唯一触发器键插入 run;
  3. 获取租约后重新读取目标状态与预算
  4. 使用稳定的动作幂等键,例如 goal:{goalId}:run:{seq}:task:{taskId}:execute
  5. 任务执行仍依赖其既有的 in-flight topic 冲突防护;
  6. 发布延迟唤醒之前就持久化 nextWakeAt;若支持取消,则保存返回的队列消息 id;
  7. 过期唤醒是无害的:当 status、generation 或 nextWakeAt 不再匹配时,处理器直接退出。

文档还建议:如果"编辑/恢复"需要廉价地作废所有排队工作,就给目标加一个 generation 整数

这套规则与 CLI 的"可安全重入"设计互为印证:goal run 在重启后可以再次安全调用,因为生命周期状态存在 PostgreSQL 里,而不是 CLI 进程里


六、MVP 分阶段落地路线

Phase 0:先对齐既有的任务级 goal loop

在引入聚合之前,先完成/复用既有的 feat/goal-loop-server 工作:

  • TaskGoalConfig(放在 tasks.config.goal);
  • verify 失败后延续到新的 task topic;
  • round 与 USD 预算;
  • 失败检查与 handoff 的携带传递;
  • goal 工具卡片的状态投影(tool-card state projection)。

注意它的定位:这只是一个"任务级执行原语",不是最终的 Agent-Goal 模型。

Phase 1:一等聚合 + 手动控制器

  • 新增 agent_goalsagent_goal_runs 以及带索引的 tasks.goalId
  • 新增 Goal 的 model/service 以及 CRUD/状态迁移;
  • 原子地创建 Goal 及其根任务
  • 实现纯决策校验的"手动调用控制器回合";
  • 支持一个 Agent 一个激活 Goal,并设置小的并发上限;
  • 复用 TaskRunner 与 Verify,暂不加事件驱动唤醒

文档评价:这一阶段证明了语义与 UI 的价值,"但不宣称 7×24 持久性"。

Phase 2:持久的时间与生命周期唤醒

  • 新增持久 goal workflow 与租约/幂等;
  • 发射 task-completion 与 verify-settled source events;
  • 新增 Agent Signal 的 goal 唤醒策略与一次性 timer 唤醒;
  • 新增重启恢复扫描:找出 nextWakeAt 已过期且无有效租约的激活目标;
  • 补齐可观测性:队列延迟、控制器延迟、决策计数、预算使用、卡死目标。

文档评价:从这一阶段起,系统才可以在队列模式下诚实宣称无人值守的持续追求

Phase 3:外部信号与重规划

  • 允许 connector/domain 事件唤醒选定的目标;
  • 新增受约束的任务图变更操作(task-graph mutation);
  • 新增证据感知的进度评估与重规划提示词;
  • 支持一个 Agent 多个目标,并引入显式的优先级/公平性策略。

七、建议的代码边界

文档给出了清晰的模块划分建议,核心原则是控制器绝不放进 TaskLifecycleService——生命周期完成回调必须保持有界且健壮,只负责"发射/入队然后返回"。

packages/database/src/schemas/agentGoal.ts
packages/database/src/schemas/models/agentGoal.ts
packages/database/src/models/agentGoalRun.ts
packages/types/src/agentGoal/

apps/server/src/services/agentGoal/
  AgentGoalService.ts       # CRUD and lifecycle invariants
  GoalController.ts         # bounded decide/apply turn
  GoalContextBuilder.ts     # goal + task graph + evidence snapshot
  GoalBudgetService.ts
  GoalLeaseService.ts
  decisions.ts              # schema and validation

apps/server/src/workflows/agentGoal/
apps/server/src/services/agentSignal/policies/goalWake/
apps/server/src/routers/lambda/agentGoal.ts

对照仓库现状,这部分"建议路径"与真实的代码组织方式略有差异——例如当前 goals 的 schema 与类型分别落在 packages/database/src/schemas/goal.tspackages/types/src/goal.ts,Graph/Trace 相关 schema 也在 schemas 目录下与 goal.ts 平级。可以推断:正式落地时会把"设计文档中的聚合模型"进一步收敛到与既有 goal/Graph/Trace schema 共存的命名与边界中,且 packages/database/src/schemas/goalGraph.tsgoalTrace.ts 已被 tests/goalGraph.test.ts 覆盖,说明 goal 图结构的持久化路径已经可测试、可运行。


八、MVP 验收标准(可直接用作测试清单)

文档定义首个持久化版本"完成"的十项标准,可直接转化为验收测试:

  1. 创建 Goal 会原子地创建 agent 拥有的 Goal 与根任务;
  2. 一次控制器回合能把根任务分解成任务依赖,且只启动就绪任务;
  3. 重复唤醒投递不能产生重复 run 或重复 task topics;
  4. 队列模式下,服务重启不会丢失激活目标的未来唤醒;
  5. task/verify 完成能在没有前台客户端连接的情况下唤醒目标;
  6. 验证通过是进入 achieved 的唯一自动路径
  7. 成本、尝试次数、并发、暂停与取消限制在服务端强制执行;
  8. waitingblocked、预算暂停、运行失败与 achieved 状态在 DB 与 UI 中可区分;
  9. 每个控制器决策都可被审计:含触发器、理由、任务变更、operation 与成本;
  10. 本地模式明确提示:定时器仅用于开发、不具备持久性。

其中第 6 条与文档"成功是被验证出来的"原则首尾呼应,是最关键的一条产品语义;第 10 条则对应 3.1 中"本地模式必须文档化非持久"的要求。


九、产品形态:用户看到一张"Current Goal"卡片

最终面向 Agent 的主界面应该呈现一张 "Current goal" 卡片,包含:

  • 目标与已验证进度(objective and verified progress);
  • 当前计划(即现有任务树);
  • 当前状态与下一个预期唤醒时刻;
  • 花费/轮数/截止预算;
  • 最近的控制器决策理由;
  • pause、resume、编辑预算、cancel 控制按钮。

文档最后点出设计哲学:用户不需要理解控制器回合。他们只需要看到:Agent 正在追求什么、现在在做什么、在等什么,以及"什么证据才算完成"。这一产品表达在 goal-graph CLI 的迭代式操作中已有雏形——lh goal graph <goal-id> 看结构、lh goal decisions <goal-id> 看历史决策、lh goal decide 处理人工门禁,整套操作都以 PostgreSQL 中的持久生命周期为底,天然支持重启后继续。


结语

Agent Goals 设计文档回答了 LobeHub"让 Agent 7×24 干活"这一命题的架构本质:把不可变的目标契约、可变更的任务计划与有限的执行尝试三层解耦,用"持久租约 + 唯一触发器键 + 服务端预算"换取安全的重试与恢复,用"Verify 是唯一通往 achieved 的路径"守住结果可信度。仓库中已经存在的 goals 表与 goalGraph/goalTrace 体系,说明这一方向正在从设计走向实现——读者若想深入,可以继续阅读 goal 常量定义goal 类型定义以及 goal-graph CLI 原型文档,从"状态枚举"与"CLI 生命周期"两侧补全对整套目标系统的理解。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388