feat(complexity): 新增 AUTO·由模型判断 复杂度档位 (tsk_zacmwH3chZpG)

新建任务时除手选 hard/medium/easy 外,新增 AUTO 档:先以 medium 落库
占位,建任务后异步用 sonnet 起一次轻量分类调用回填三档结果并记录理由,
不阻塞建任务返回;模型不可用/解析失败一律兜底 medium,绝不报错。

- web 看板:复杂度 seg 增加第四档 AUTO(默认选中,cyan 样式),提交 complexity='auto'
- src/api/server.ts:POST tasks 接受 'auto' → medium 占位 + 异步 classify 回填(可注入 classify 测试)
- src/executor/classify.ts(新增):buildClassifyPrompt/parseClassification/classifyComplexity
  复用 cc.ts/models.ts,sonnet 省额度,无工具、限轮限时
- src/executor/models.ts:pickClassifierModel(复用 executor easy 档 + MAESTRO_MODEL_EASY 覆盖)
- src/store/store.ts:applyAutoComplexity 回填复杂度+重置状态+理由入产出字段+广播 task.updated
- test/classify.test.ts:解析映射/兜底、prompt、回填、建任务集成(13 例)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-13 10:46:37 +08:00
parent 872354cf7c
commit 13572a5416
7 changed files with 348 additions and 4 deletions
+34
View File
@@ -375,6 +375,40 @@ export class Store {
return this.getTask(taskId)!;
}
/**
* AUTO 复杂度回填:模型判定后设置任务复杂度并记录判定理由(建任务时占位 medium,此处异步回填)。
* - 判定档与占位不同且仍可改(COMPLEXITY_EDITABLE)→ 改复杂度 + 重置状态(同 patchTask 落位逻辑,含依赖);
* - 理由写入该复杂度对应产出字段(hard→plan / medium→spec / easy→operations)的备注,仅在该字段为空时写,避免覆盖;
* - 始终广播 task.updatedpayload 带判定档与理由),便于看板/用户复核。
*/
applyAutoComplexity(taskId: string, complexity: Complexity, reason: string): Task {
const row = this.getTaskRow(taskId);
if (!row) throw new StoreError(`任务不存在: ${taskId}`);
const cur = row.status as TaskStatus;
let statusChange: { from: TaskStatus; to: TaskStatus } | null = null;
if (complexity !== row.complexity && COMPLEXITY_EDITABLE.has(cur)) {
const to = this.resolveReady(row, initialNextStatus(complexity));
this.db.prepare(`UPDATE tasks SET complexity = ?, status = ?, updated_at = ? WHERE id = ?`)
.run(complexity, to, now(), taskId);
if (to !== cur) statusChange = { from: cur, to };
}
// 理由写入对应产出字段(仅当为空),便于用户与执行 agent 复核
const field = complexity === 'hard' ? 'plan' : complexity === 'medium' ? 'spec' : 'operations';
const fresh = this.getTaskRow(taskId)!;
if (!fresh[field]) {
const note = `> AUTO·模型判定复杂度:${complexity}\n> 理由:${reason}`;
this.db.prepare(`UPDATE tasks SET ${field} = ?, updated_at = ? WHERE id = ?`).run(note, now(), taskId);
}
this.emit(row.project_id, taskId, 'task.updated', { auto: 'classify', complexity, reason });
if (statusChange) {
this.emit(row.project_id, taskId, 'status.changed', { ...statusChange, reason: 'complexity.auto-classified' });
}
return this.getTask(taskId)!;
}
// ---------- 旧 todo 来源映射(sync 引擎用) ----------
setSourceRef(taskId: string, ref: string): void {
const row = this.getTaskRow(taskId);