codev / docs /coordinator /goals-auto-mode.md
chenbhao's picture
chore: docs v1.0
eeeb2b6
|
Raw
History Blame Contribute Delete
8.14 kB

目标系统与自动模式

Goal 系统

Goal(目标)系统允许用户通过 /goal <objective> 命令设定一个长期目标,让 Agent 在多个对话轮次中持续追寻该目标,直至完成、受阻或暂停。

状态机

Goal 的生命周期由以下状态构成:

pursuing → paused → achieved / blocked
         → usage-limited / budget-limited
  • pursuing(追寻中):初始状态,Agent 正在积极地朝目标推进。
  • paused(已暂停):用户使用 /goal pause 暂停自动续跑,可用 /goal resume 恢复。
  • achieved(已完成):Agent 认定目标已完全达成。需经过严格的完成审计(Completion Audit)。
  • blocked(已阻塞):Agent 遇到无法继续的障碍,需要用户介入。
  • usage-limited / budget-limited:因 token 预算或费用限制而停止。

核心文件

文件 路径 用途
goal.tsx src/commands/goal/goal.tsx /goal 命令的 JSX 实现(React Ink 渲染)
index.ts src/commands/goal/index.ts 命令注册入口
goal.ts src/utils/goal.ts 工具函数:构建续跑提示、状态格式化
useGoalAutoContinue.ts src/hooks/useGoalAutoContinue.ts React Hook:在每轮对话结束后自动注入续跑提示

持续提示

buildContinuationPrompt()(定义于 src/utils/goal.ts)在每轮自动续跑时注入完整的上下文提示,包含:

  • 目标描述<objective> XML 标签包裹)
  • 续跑行为指导:保持目标完整性,不允许缩小范围
  • 基于证据的工作原则:依赖当前工作区状态而非对话记忆
  • 完成审计要求:必须逐项验证所有需求
  • 阻塞审计规则:连续 3 轮相同阻塞条件才允许标记 blocked

useGoalAutoContinue hook(src/hooks/useGoalAutoContinue.ts)监听 QueryGuard 状态:

  • 当查询从活跃(running)转为空闲(idle)时触发
  • 仅在 goal 状态为 pursuing 且非 plan 模式时注入
  • 自动清理旧目标的排队续跑,防止冲突
  • 每次续跑递增 continuationCount

完成审计(Completion Audit)

Agent 在决定标记目标为 achieved 前,必须执行严格的完成审计:

  1. 从目标描述中推导具体的、可验证的需求
  2. 不重新定义成功标准——保留原始范围
  3. 对每个需求,检查当前状态中的权威证据
  4. 确认证据足以证明完成,而非仅未发现未完成的工作
  5. 只有在当前证据能够经受逐项审查时,才标记为已达成

Blocked 审计

防止 Agent 过早放弃的机制(src/utils/goal.tsBLOCKED_AUDIT_TURNS = 3):

  • 阻塞条件必须连续出现 至少 3 轮 才能标记 blocked
  • 用户恢复已阻塞的目标后,阻塞审计重置
  • 仅在确实无法推进、需要用户输入或外部状态变更时才使用
  • 不因任务困难、缓慢或需要澄清而标记 blocked

预算跟踪

checkTokenBudget()(定义于 src/query/tokenBudget.ts)根据 token 消耗决定是否继续自动续跑:

  • 使用 createBudgetTracker() 跟踪每次续跑的 token 消耗
  • COMPLETION_THRESHOLD = 0.9:token 消耗达到预算的 90% 时停止
  • DIMINISHING_THRESHOLD = 500:连续 3 次续跑且每次增量 < 500 token 时视为收益递减
  • 返回 continuestop 决策

Auto Mode(自动模式)

激活方式

  • 用户通过 /goal <objective> 设置目标后自动进入自动模式
  • Plan 模式下自动模式被禁用(inPlanMode 检查)

自动续跑机制

每次 Agent 响应完成后,useGoalAutoContinue hook 自动向命令队列注入 [goal] Continue 提示:

const prompt = buildContinuationPrompt(goal, now)
enqueue({
  mode: 'prompt',
  value: prompt,
  priority: 'later',
  isMeta: true,
})
  • 只在 pursuing 状态下触发
  • 跳过 plan 模式
  • 用户输入优先于自动续跑(有阻塞性用户输入时不注入)
  • 避免为同一目标重复排队

update_goal 工具

Agent 可通过 update_goal 工具报告进度:

  • status='complete':目标达成,停止续跑
  • status='blocked':受阻,需用户介入
  • 调用时必须包含 goal_id(与目标 ID 匹配)

目标管理命令

命令 功能
/goal <objective> 设置新目标(若已有活跃目标则提示覆盖确认)
/goal set <objective> 同上
/goal pause 暂停当前目标
/goal resume 恢复被暂停的目标(重置续跑计数和预算窗口)
/goal edit <new objective> 编辑目标描述
/goal clear 清除当前目标
/goal(无参数) 查看当前目标状态

查询循环(query.ts)

queryLoop() 主循环

src/query.ts 中的 queryLoop() 是 Agent 的核心执行循环,在 QueryEngine.ts 中被调用。每次迭代处理一个完整的模型调用周期:

模型调用 → 流式响应处理 → 工具执行 → stop hooks → 继续决策
  1. 模型调用queryModelWithStreaming() 发送消息到 API
  2. 流处理:处理 content_block_start/delta 事件
  3. 工具执行:检测 tool_use 并执行
  4. stop hooks:执行 handleStopHooks()(记忆提取、自动 dream、prompt 建议等)
  5. 继续决策:检查是否需要继续循环(工具调用、max_output_tokens 恢复、goal 续跑)

max_output_tokens 恢复

当模型响应被 max_output_tokens 截断时,queryLoop 提供最多 3 次 恢复尝试(MAX_OUTPUT_TOKENS_RECOVERY_LIMIT):

  1. 注入恢复提示,指导模型从中断处继续
  2. 若 8K 默认输出限制触发,自动升级到 64K(一次,tengu_otk_slot_v1 功能门控)
  3. 恢复尝试耗尽后,将错误信息输出给用户

相关变量(src/query.ts):

  • maxOutputTokensRecoveryCount:当前恢复尝试次数
  • MAX_OUTPUT_TOKENS_RECOVERY_LIMIT = 3:最大恢复次数
  • ESCALATED_MAX_TOKENS:升级后的输出 token 上限

Reactive Compaction

当上下文超出 API 限制时自动触发压缩:

  • hasAttemptedReactiveCompact:防止在同一轮迭代中重复压缩
  • 上下文超时(prompt-too-long)触发的压缩流程
  • 压缩后保持 stop hooks 的阻塞错误处理
  • 保留反应式压缩保护(不因 stop hook 错误而重置,防止无限循环)

Task 系统

Task(任务)系统管理 Agent 派发的异步子任务。

Task 类型

定义于 src/Task.ts

类型 标识 用途
local_bash b 本地 Shell 命令执行
local_agent a 本地子 Agent
remote_agent r 远程 Agent
in_process_teammate t 进程内队友
local_workflow w 本地工作流脚本
dream d 记忆整理任务
monitor_mcp m MCP 监控

状态

pending → running → completed / failed / killed
  • pending:等待分配
  • running:正在执行
  • completed:正常完成
  • failed:执行失败
  • killed:被终止

后台/前台任务

任务系统区分前台任务(阻塞用户交互)和后台任务(在后台运行),但 Task 类型本身不区分配置字段;区分体现在调用上下文中。

任务 ID 生成

generateTaskId()src/Task.ts)生成格式为 {类型前缀}{8位随机字母数字} 的 ID:

  • 使用 randomBytes(8) 生成安全随机数
  • 36 进制字母表(数字 + 小写字母)
  • 36^8 ≈ 2.8 万亿种组合,抗暴力枚举

stopTask(优雅终止)

Task 的 kill() 方法实现优雅终止,每个 Task 类型提供独立的 kill 实现(src/tasks.ts):

export type Task = {
  name: string
  type: TaskType
  kill(taskId: string, setAppState: SetAppState): Promise<void>
}

任务注册

src/tasks.ts 中的 getAllTasks() 收集所有可用 Task:

  • 始终注册:LocalShellTask, LocalAgentTask, RemoteAgentTask, DreamTask
  • 条件注册(通过 feature gate):LocalWorkflowTask, MonitorMcpTask