fix: spawn_workers 返回 Agent 指令(非 Skill)+ run_in_background

根因:Skill 工具只加载指令到当前上下文,不会创建子 Agent。
      Agent 工具默认阻塞调用,Eng 派发后卡住无法进入 monitor。

修复:
- spawn_workers() 返回 Agent 调用参数:{description, subagent_type, prompt, run_in_background}
- run_in_background: true 确保 Eng 不被子 Worker 阻塞
- commands/eng.md 明确标注 Agent 工具 + run_in_background

派发模式:Eng → Agent(run_in_background:true) × N → monitor 轮询 → merge

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
AirLongDian
2026-06-11 15:24:30 +08:00
parent f3f991de2a
commit cda601a73a
2 changed files with 78 additions and 16 deletions

View File

@@ -615,8 +615,12 @@ def _select_ready_tasks(project_root: Path, max_count: int) -> list[str]:
def spawn_workers(project_root: Path, task_ids: list[str]) -> list[dict]:
"""T-1.21: 为每个 ready 任务准备 spawn 指令,返回 Agent 可直接消费的 Skill 调用参数列表
不实际启动子进程——启动由 Agent 框架的 Skill 工具完成。
"""T-1.21: 为每个 ready 任务准备 Agent 派发指令
使用 Agent 工具(非 Skill 工具spawn 隔离子 Agent。
每个子 Agent 有自己的上下文,不继承 Eng 的完整对话。这正是 INV-2fork_context=false
返回 Agent 调用参数列表Eng Agent 遍历列表逐个调用。
"""
tg_json = airplan_root(project_root) / "state" / "airarc" / "reviews" / "task-graph.json"
graph = TaskGraph.load(tg_json) if tg_json.exists() else TaskGraph()
@@ -624,9 +628,40 @@ def spawn_workers(project_root: Path, task_ids: list[str]) -> list[dict]:
for tid in task_ids:
node = graph.nodes.get(tid)
task_text = node.task if node else ""
files = node.files_dirs if node else ""
done_when = node.done_when if node else ""
prompt_parts = [
f"你是 AirDo Worker任务 ID: {tid}",
f"项目路径: {project_root}",
"",
f"## 任务",
f"{task_text}",
"",
f"## 文件范围",
f"{files}" if files else "(无限制)",
"",
f"## 完成标准",
f"{done_when}" if done_when else "编译通过,无回归",
"",
"## 工作流程",
"1. 先运行 `python scripts/airplan.py --mode do --sub enter --task-id {tid} --task-text '{task_text}' --project {project_root}` 初始化 Worker 状态",
"2. 读取项目文件,理解现有代码结构",
"3. 实现任务需求,修改/创建源代码文件",
"4. 完成后运行 `python scripts/airplan.py --mode do --sub finish --task-id {tid} --result AirPlan/state/airdo/tasks/{tid}/result.json`",
"",
"## 约束",
"- 只修改属于此任务的文件",
"- 完成后必须运行 finish 命令",
"- 遇到无法解决的问题时返回 blocked 状态",
]
prompt = "\n".join(prompt_parts).format(tid=tid, task_text=task_text, project_root=project_root)
instructions.append({
"skill": "airplan-v2:do",
"args": f"--sub enter --task-id {tid} --task-text '{task_text}' --project .",
"description": f"Do Worker: {tid}",
"subagent_type": "general-purpose",
"prompt": prompt,
"run_in_background": True, # 关键后台运行Eng 不阻塞
"taskId": tid,
"taskText": task_text,
})