File size: 8,138 Bytes
eeeb2b6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | # 目标系统与自动模式
## 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.ts` 中 `BLOCKED_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 时视为收益递减
- 返回 `continue` 或 `stop` 决策
---
## Auto Mode(自动模式)
### 激活方式
- 用户通过 `/goal <objective>` 设置目标后自动进入自动模式
- Plan 模式下自动模式被禁用(`inPlanMode` 检查)
### 自动续跑机制
每次 Agent 响应完成后,`useGoalAutoContinue` hook 自动向命令队列注入 `[goal] Continue` 提示:
```typescript
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`):
```typescript
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`
|