Skip to content

什么是目标

目标(goal)是附着于现有会话的一条持久化完成目标。它是状态,而非调度器——会话日志是其事实来源,而一个独立的目标轮驱动(goal-round driver)决定一个已武装目标何时真正续作。目标家族位于 packages/goal/ 下并干净地切成四个包:

角色ctx 键
@deepseek-ai/dsh-goal目标状态、生命周期与重放折叠ctx.goals
@deepseek-ai/dsh-goal-round-driver同会话续作驱动
@deepseek-ai/dsh-tool-goal面向模型的 get_goal/create_goal/update_goal 工具注册于 ctx.tools
@deepseek-ai/dsh-command-goal面向人类的 /goal 命令注册于 ctx.commands

消费者依赖 @deepseek-ai/dsh-goal,从不依赖具体的 agent 循环。其子系统参考是 docs/subsystems/goal.md

领域模型

宿主词汇表位于 packages/goal/goal/src/domain.ts(宿主侧),其类型位于 src/types.ts。关键类型:

GoalId        —— 一个目标跨其持久修订的品牌化 id
GoalRef       —— { id, revision } 比较并设置(CAS)围栏
GoalPhase     —— 'active' | 'paused' | 'blocked' | 'complete'
GoalBlockReason — { code: string, message: string }(仅当 blocked 时出现)
GoalSnapshot  —— objective、phase、blockedReason?、maxGoalRounds(= GoalRef + 这些)
GoalView      —— GoalSnapshot + { roundsStarted, createdAt, updatedAt, activation }
GoalActivation — 'armed' | 'disarmed'(进程本地,永不持久化)
GoalOperation —— 'create' | 'edit' | 'pause' | 'resume' | 'complete' | 'block' | 'clear'

GoalPhase 是持久阶段;GoalActivation 刻意独立且进程本地roundsStarted 是已准入的最高续作轮;maxGoalRounds 是总上限。blockedReason.code 是由阻塞策略选定的稳定小写连字符分类(模型工具总用 model-reported);blockedReason.message 是给人类/模型的解释。

生命周期转换与权限

ctx.goals 只接受注册于其 id 下的精确活跃 Agent 实例。变更使用 GoalRef { id, revision } 比较并设置围栏,并拒绝陈旧 ref。每次变更都会追加一条持久化的 goal/change 会话事件,携带完整变更后快照(或修订版清除墓碑),因此目标状态从不依赖收件箱位置、认领、准入或丢弃。

转换及其执行人:

操作效果
create活跃的一版目标,已武装模型工具 / /goal
edit保留阶段、阻塞原因与激活;编辑已完成目标则创建全新的活跃目标模型工具 / /goal
pause已暂停 + 解除武装模型工具 / /goal
resume停止阶段 或 已解除武装的活跃 → 活跃 + 已武装,清除阻塞者,仅在余量够时模型工具 / 人类授权 /goal
complete已完成 + 解除武装模型工具(轮次授权)/ 人类
block已阻塞 + 解除武装,记录码与解释模型工具(轮次授权)/ /goal
clear墓碑;保留历史/goal

阻塞使用一个持久阶段,而非增殖生命周期状态。resume 是唯一的重新武装路径:会话恢复、fork 或驱动替换后,活跃目标会自动 disarmed;只有显式的人类授权 resume(通过 /goal 或模型工具)才能重新武装续作。disarm() 本身是仅生命周期例外——它移除进程本地的续作权限,不写修订、不发变更事件。

激活:armed 与 disarmed

激活从不持久化。全新缓存与每个 agent/session-start 边都会解除武装,哪怕重放发现一个活跃的持久阶段;续作驱动也会在卸载前或持久性不确定之后调用 disarm()。这正是激活不在持久重放中的原因:持久阶段说的是“此目标打开着”,但只有活跃、已武装的进程才有权准入下一轮。

目标轮次

目标轮(goal round)是为当前目标准入的一个续作循环。驱动把它具体化为一个目标来源的轮次(turn),其中可含零个或多个步骤。术语表做出两个关键澄清:

  • 只有已准入、目标来源的 user/message 才使 roundsStarted 前进;
  • 同一会话中无关的人类轮次不消耗目标轮上限

goal-round 驱动

@deepseek-ai/dsh-goal-round-driver 是同会话续作驱动,无可调配置——maxGoalRounds 属于目标定义,而面向模型的阻塞阈值属于 dsh-tool-goal,因此驱动不复制二者。

轮契约(来自 packages/goal/goal-round-driver/src/index.ts):

  1. 当精确活跃 agent 空闲且持有活跃、已武装、有余量的目标时,它先检查点待决目标变更(等待 ctx.sessions.flush(),并在等待后复查修订与竞争输入);
  2. 为当前 { goalId, revision } 预留 roundsStarted + 1(陈旧预留消耗轮号);
  3. 排队一条 <goal_round> 提示词,携带 GoalMessageSource { kind: 'goal', goalId, revision, round }
  4. agent/pre-step 监听器在下游监听器前后都验证认领记录与当前目标;只有已进入的 user/message 才使 roundsStarted 前进。

保留的提示词命名 JSON 引用的目标与 round/maxGoalRounds,把当前工作区、工具结果与持久会话状态当作权威,并要求有证据才完成,且当仍有工作时告诉模型保持目标激活。经 agent/error 到达的冲刷失败会在另一轮开始前解除武装续作。若人类工作先进入收件箱,自动工作会让路直至 agent 空闲。在下一个空闲检查点,持有一个已被预留或已尝试工作、但其工作被取消的目标会被暂停,从而取消无法自动重启它。

面向模型的工具:dsh-tool-goal

@deepseek-ai/dsh-tool-goal 注册 get_goalcreate_goalupdate_goal

  • get_goal() → 当前目标或 null,含 id/revision、持久阶段、已准入/上限轮次、阻塞原因与实时激活。
  • create_goal(objective, max_goal_rounds?) —— 从直接顶层人类轮次创建一个目标;非人类轮次与子代理会在执行时被拒。
  • update_goal(goal_id, revision, action, objective?, max_goal_rounds?, blocked_reason?) —— edit/pause/resume/complete/blockedblocked_reason 仅对 blocked 必填,并以稳定码 model-reported 持久化。

权限。 执行要求精确活跃的 exec.agent,且 create/edit/pause/resume 另外要求运行时根 agent 当前轮次中有一条已接受的 { kind: 'user' } 消息或导航事件。{ kind: 'user' } 是宿主凭据:调用方省略来源时,Agent.followup()steer() 会赋予它,因此插件/调度器/非人类生产者必须传递自己的来源,而非继承人类权限。Complete 与 blocked 也接受当前精确的目标轮(id + revision + round 都与折叠目标相等),但目标轮 blocked 调用会在 blockedAfterConsecutiveRounds(默认 3)前被机械拒绝。

一个自主目标轮成功上报 completeblocked 时会调用 concludeTurn(),在该步骤之后停止物理轮次;直接人类变更从不会促成这种停止。配置:blockedAfterConsecutiveRounds(正整数)。这些工具把一条固定目标策略渲染进系统提示词。

/goal 人类命令

@deepseek-ai/dsh-command-goalctx.commands 上注册一个全局命令:

输入结果
/goal显示目标、阶段、轮次计数/上限、激活、合法下级命令
/goal <目标>创建并武装一个目标 / 用全新 id 替换已完成目标
/goal edit <目标>不改阶段或激活地编辑目标
/goal pause暂停 + 解除武装
/goal resume在余量内恢复 / 重新武装
/goal clear清除指针,保留持久历史 + 墓碑

控制词仅在作为完整输入时大小写不敏感;其他任何非空后缀都是目标。由于命令平面没有模态编辑器,edit 接受内联替换。

目标在哪里持久化

会话日志是唯一持久权威。每次变更都追加 goal/change;清除追加修订版墓碑。严格重放只从这些事件派生生命周期状态,拒绝畸形形状、不连续修订、非法转换、非单调时间戳与非顺序的已准入轮次。goal/changed(scoped emit)在持久事件提交后触发。这里不涉及 @deepseek-ai/dsh-goal/checkpoint 风格的辅助——会话引用使用的检查点标记是另一个概念(dsh-compaction)。

重放、scoped 事件与错误码

严格重放纯粹从 goal/change 事件重算 FoldedGoal——即目标加 roundsStartedcreatedAtupdatedAt 与最新 GoalRef。它拒绝畸形形状、不连续修订、非法生命周期转换、非单调时间戳与非顺序的已准入轮次;增量重放把游标停在首个损坏事件,因此坏记录会停止折叠而不污染内存。流程:

text
持久化 goal/change 事件 ──► 严格重放折叠
        │                       ─ 拒绝畸形 / 非法 / 非单调

   FoldedGoal ──► 实时 GoalView(加上激活、roundsStarted 计数)


   变更请求 ──► GoalRef { id, revision } CAS 围栏
                  └ 陈旧 ref 被拒(GOAL_STALE_REVISION)


   提交 goal/change ──► goal/changed(scoped emit)

该服务暴露稳定的 GoalErrorCode 原因:GOAL_AGENT_NOT_LIVEGOAL_NOT_FOUNDGOAL_ALREADY_EXISTSGOAL_STALE_REVISIONGOAL_INVALID_OBJECTIVEGOAL_INVALID_MAX_ROUNDSGOAL_INVALID_BLOCK_REASONGOAL_INVALID_EDITGOAL_INVALID_TRANSITIONdisarm() 是唯一只变更进程本地权限的操作:它移除续作权限而不写 goal/change 或不推进修订,因此从不出现在重放中。

推荐组合

随附的 dsh 基底启用持久化目标栈 + /goal;ACP 自动化应用启用领域 + 模型工具而无需命令适配器;无 UI 的 agent-spine-demo 要求显式 goals: {},以免无头一次性调用方悄然从单物理轮次变成多轮操作。

yaml
- id: goal
  name: '@deepseek-ai/dsh-goal'
- id: tool-goal
  name: '@deepseek-ai/dsh-tool-goal'
- id: goal-round-driver
  name: '@deepseek-ai/dsh-goal-round-driver'

进一步阅读

  • 上下文来源 — 目标轮提示词所在其中持久化消息。
  • 上下文压缩(Compaction) — 累积的目标轮提示词最终如何被遮蔽。
  • 交互:命令、提问与审批/goal 与目标工具所依赖的命令平面与工具权限。
  • packages/goal/goal/src/domain.tssrc/types.ts — 精确领域类型与 goal/change 载荷。
  • docs/glossary.mdgoalgoal roundgoal activation 的定义。
  • .agents/notes/implemented/feature/2026-07-19-persisted-same-session-goal-domain.md — 目标领域设计决策记录。