Goal

长期目标变成可追踪、可恢复的轮次目标。

create_goal / get_goal / update_goal。

80 行3 个工具

问题

用户说:"把这个项目从 A 迁移到 B,这是个大事,慢慢来。"

一次对话显然做不完。如果 Agent 在单次回合里硬做,会超出上下文、烧光 token、中途崩溃一切归零。而如果用户"下次继续",Agent 完全忘了目标是什么、做到哪了。

长期工作需要一个持久的目标对象:目标本身、它的状态(进行中/暂停/受阻/完成)、已完成的轮次——都登录在案,跨回合存活。这就是把"任务"从"对话"里独立出来。

解决方案

目标工具三件套:

  • create_goal:把模糊请求固化为一个持久目标(含轮次上限);
  • get_goal:读取当前目标与状态(每轮开始自查);
  • update_goal:更新目标(编辑/暂停/恢复/完成/受阻)。
let goal = null; // { id, objective, phase, rounds, maxRounds, revision }

define("create_goal", { type: "object", properties: { objective: { type: "string" }, max_goal_rounds: { type: "integer" } }, required: ["objective"] },
  ({ objective, max_goal_rounds }) => {
    goal = { id: `goal-${++goalSeq}`, objective, phase: "active", rounds: 0, revision: 1, maxRounds: max_goal_rounds ?? 10 };
    append("goal/change", goal); // 持久化进 s09 的会话日志
    return JSON.stringify(goal);
  });

define("get_goal", { type: "object", properties: {}, required: [] },
  () => goal ? JSON.stringify(goal) : "(无活跃目标)");

define("update_goal", {
  type: "object", properties: { action: { type: "string", enum: ["edit", "pause", "resume", "complete", "blocked"] }, objective: { type: "string" }, blocked_reason: { type: "string" } },
  required: ["action"],
}, ({ action, objective, blocked_reason }) => {
  if (!goal) return "(无活跃目标)";
  if (action === "edit") goal.objective = objective ?? goal.objective;
  if (action === "pause" || action === "complete" || action === "blocked") goal.phase = action;
  if (action === "resume") goal.phase = "active";
  if (action === "blocked") goal.blockedReason = blocked_reason;
  goal.revision++;
  append("goal/change", goal);
  return JSON.stringify(goal);
});

工作原理

第 1 步:自动续轮——目标驱动者每轮结束时检查:目标还在 active、轮次未超限、有进展 → 自动开始下一轮,目标与历史都在:

async function goalRound() {
  while (goal && goal.phase === "active" && goal.rounds < goal.maxRounds) {
    goal.rounds++;
    append("goal/change", goal);
    await agentLoop(`继续推进目标:${goal.objective}`, { resume: true });
  }
}

第 2 步:受阻要交代——同样的阻碍连续 3 轮出现时,模型必须用 blocked 如实报告(并给出具体条件),而不是无限重试。这是"诚实性"的机制化:放弃也是目标生命周期的一部分

第 3 步:暂停/恢复由用户(或模型)控制。目标状态是持久事实,不是会话中的印象——重启进程后 get_goal 仍能回答"我们在干什么、做到哪了"。

试一下

运行 s15:

  • create_goal 创建一个多轮目标(如"逐文件重构并测试"),观察自动续轮
  • update_goal: pause 暂停,再 resume 恢复
  • 构造一个重复受阻的场景,观察模型是否如实 blocked

观察重点:目标驱动与"用户在场"的区别——自动续轮适合后台任务,但需要用户批准的场景应该暂停等待。

以下内容基于 packages/goaldocs/subsystems/goal.md 的核查。DSH 的目标族:

角色
goal 目标状态与生命周期(ctx.goals)——依赖会话日志,独立于具体循环
goal-round-driver 同会话目标续轮驱动
tool-goal 模型可见的目标工具
command-goal 人类命令

关键设计:

  1. 状态属于会话日志:目标状态(goal/change 事件)在日志里,续轮驱动消费它——resume/fork/压缩后目标自动还原(s09 的模式第三次出现)。
  2. 轮次与上限max_goal_rounds 限制自动续轮;blocked 在达到最小轮次前会被拒绝——防止模型过早放弃,也防止无限空转。
  3. 与循环解耦:消费者依赖 dsh-goal,不依赖具体 agent-loop——换循环不换目标机制。

一句话:Goal 是把"长期任务"提升为一等持久对象的机制——目标、轮次、状态、受阻原因都登录在案,对话只是它的执行现场。