auto-sync: 2026-05-29 01:12:43
This commit is contained in:
+168
-1
@@ -363,9 +363,176 @@ class Ticker:
|
||||
return refreshed
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 依赖推进
|
||||
# 一轮结束检测 + 庞统 review (v2.9 #01)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
MAX_ROUNDS = 5 # §4.5 防无限循环
|
||||
|
||||
async def _check_round_complete(self, db_path: Path,
|
||||
project_id: str) -> List[str]:
|
||||
"""检测 parent task 下所有 sub task 终态 → spawn 庞统 review
|
||||
|
||||
流程(§4.4):
|
||||
1. 扫描所有 parent task
|
||||
2. 对每个 parent 检查 sub task 是否全部终态
|
||||
3. 检查 round_count 上限
|
||||
4. increment round_count
|
||||
5. spawn 庞统 review
|
||||
"""
|
||||
if not self.dispatcher or not self.spawner:
|
||||
return []
|
||||
|
||||
bb = Blackboard(db_path)
|
||||
reviewed: List[str] = []
|
||||
|
||||
# 找所有 parent task(有子 task 的)
|
||||
conn = get_connection(db_path)
|
||||
try:
|
||||
parent_rows = conn.execute(
|
||||
"SELECT DISTINCT parent_task FROM tasks WHERE parent_task IS NOT NULL"
|
||||
).fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
for row in parent_rows:
|
||||
parent_id = row["parent_task"]
|
||||
try:
|
||||
summary = bb.get_subtasks_summary(parent_id)
|
||||
if not summary or not summary["all_terminal"]:
|
||||
continue
|
||||
|
||||
# 检查 parent 自身状态:只有 done 状态的 parent 才触发
|
||||
# (聚合后 parent 状态为 done 说明所有 sub 都完了)
|
||||
if summary["parent_status"] != "done":
|
||||
continue
|
||||
|
||||
# 检查 round_count 上限
|
||||
if summary["round_count"] >= self.MAX_ROUNDS:
|
||||
logger.warning(
|
||||
"Parent %s reached max rounds (%d), skipping review",
|
||||
parent_id, self.MAX_ROUNDS)
|
||||
continue
|
||||
|
||||
# 递增 round_count
|
||||
new_round = bb.increment_round_count(parent_id)
|
||||
|
||||
# 构建庞统 review 上下文
|
||||
outputs = bb.get_aggregate_outputs(parent_id)
|
||||
comments = bb.get_round_comments(parent_id)
|
||||
parent_task = bb.get_task(parent_id)
|
||||
|
||||
if not parent_task:
|
||||
continue
|
||||
|
||||
# spawn 庞统 review
|
||||
review_prompt = self._build_review_prompt(
|
||||
parent_task, summary, outputs, comments, new_round
|
||||
)
|
||||
|
||||
spawned = await self._spawn_pangtong_review(
|
||||
parent_task, review_prompt, project_id
|
||||
)
|
||||
if spawned:
|
||||
reviewed.append(parent_id)
|
||||
logger.info(
|
||||
"Round %d review spawned for parent %s (subs: %s)",
|
||||
new_round, parent_id, summary
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("Round check error for parent %s", parent_id)
|
||||
|
||||
return reviewed
|
||||
|
||||
def _build_review_prompt(self, parent_task, summary: dict,
|
||||
outputs: list, comments: list,
|
||||
round_num: int) -> str:
|
||||
"""构建庞统 review prompt(§4.2 三问框架)"""
|
||||
goal = parent_task.description or parent_task.title
|
||||
must_haves = parent_task.must_haves or "{}"
|
||||
|
||||
# 成果物摘要(限制 token)
|
||||
output_lines = []
|
||||
for o in outputs[:20]: # 最多 20 个
|
||||
output_lines.append(
|
||||
f"- [{o.get('task_title', '?')}] {o.get('title', '?')} "
|
||||
f"({o.get('output_type', '?')}) by {o.get('agent', '?')}"
|
||||
)
|
||||
|
||||
# 讨论摘要(限制 50 条)
|
||||
comment_lines = []
|
||||
for c in comments[:50]:
|
||||
comment_lines.append(
|
||||
f"[{c.get('created_at', '?')[:16]}] {c.get('author', '?')}: {c.get('body', '?')[:200]}"
|
||||
)
|
||||
|
||||
return f"""## 庞统 Review(第 {round_num} 轮)
|
||||
|
||||
### Goal
|
||||
{goal}
|
||||
|
||||
### 验收标准
|
||||
{must_haves}
|
||||
|
||||
### 本轮 Sub Task 状态
|
||||
- 完成: {summary['done']}
|
||||
- 失败: {summary['failed']}
|
||||
- 取消: {summary['cancelled']}
|
||||
- 总计: {summary['total']}
|
||||
|
||||
### 成果物
|
||||
{chr(10).join(output_lines) if output_lines else '无'}
|
||||
|
||||
### 黑板讨论
|
||||
{chr(10).join(comment_lines) if comment_lines else '无'}
|
||||
|
||||
### 三问
|
||||
1. Goal 还清晰吗?(是否有 goal drift)
|
||||
2. 成果物覆盖 goal 了吗?(逐条检查验收标准)
|
||||
3. 下一轮需要做什么?(创建新 sub tasks / 标记完成 / 调整方向)
|
||||
|
||||
### 失败处理
|
||||
{f'有 {summary["failed"]} 个 sub task failed,优先判断是应该重试、换人、还是调整方案。' if summary['failed'] > 0 else '无失败'}
|
||||
|
||||
### 你可以
|
||||
- 创建新一轮 sub tasks(通过 API: POST /api/projects/{{pid}}/tasks)
|
||||
- 调整 goal(更新 parent task description/must_haves)
|
||||
- 标记完成(如果 goal 已达成,回复 GOAL_ACHIEVED)
|
||||
|
||||
Round 上限: {self.MAX_ROUNDS}(当前第 {round_num} 轮)
|
||||
"""
|
||||
|
||||
async def _spawn_pangtong_review(self, parent_task,
|
||||
review_prompt: str,
|
||||
project_id: str) -> bool:
|
||||
"""Spawn 庞统进行 review"""
|
||||
try:
|
||||
agent_id = "pangtong-fujunshi"
|
||||
session_id = f"review-{parent_task.id}-r{parent_task.round_count + 1}"
|
||||
|
||||
# 构建上下文
|
||||
context = self.spawner.build_context(
|
||||
agent_id=agent_id,
|
||||
task_id=parent_task.id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
# 拼接 prompt
|
||||
full_prompt = f"{context}\n\n{review_prompt}"
|
||||
|
||||
# spawn
|
||||
result = await self.spawner.spawn(
|
||||
agent_id=agent_id,
|
||||
session_id=session_id,
|
||||
prompt=full_prompt,
|
||||
project_id=project_id,
|
||||
task_id=parent_task.id,
|
||||
)
|
||||
|
||||
return result is not None
|
||||
except Exception as e:
|
||||
logger.exception("Failed to spawn pangtong review for %s", parent_task.id)
|
||||
return False
|
||||
|
||||
def _advance_dependencies(self, db_path: Path) -> List[str]:
|
||||
"""检查 blocked 任务,若所有依赖已完成则推进为 pending
|
||||
|
||||
|
||||
Reference in New Issue
Block a user