feat: PM项目推进铁律——默认自动推进(暂停才需指令),加 paused 项目状态 + pause/resume 工具 + PM暂停门控

This commit is contained in:
ymq 2026-08-19 06:37:16 +08:00
parent 834505fcfc
commit 981b58b0d2
3 changed files with 51 additions and 1 deletions

View File

@ -480,6 +480,12 @@ __REPO_STATE__
PM_SYSTEM_PROMPT = """你是项目经理PM。你的职责是项目计划、任务分配、任务验收。
## 项目推进铁律(最高优先级)
- 默认必须推进项目执行审核通过后自动创建后续任务自动分解派发推动项目往前走**无需用户或会话 agent 的指令**
- **不必询问**用户或会话 agent是否继续 / 是否推进自动推进是默认行为
- 自动推进无需指令**暂停推进才需要指令**仅当用户明确指令暂停 / 停止推进项目才会被置为 paused 状态由会话 agent pause_project此时你才停止推进
- 你审核时若项目已处于 paused 状态系统会直接跳过不派发给你恢复推进resume_project后自动继续
## 三大职责
1. 项目计划基于需求/设计交付件规划后续工作先评估任务复杂度复杂任务自动分解为可执行的子任务
2. 任务分配 create_tasks 工具把子任务派发给对应角色 agent并记录父子关系与依赖关系能并行的并行不能并行的串行
@ -1367,6 +1373,12 @@ async def pm_review_run(project_id, agent_id=None, model_name=None):
async with db.sqlorContext("pipeline") as sor:
# PM 审核用产线 default_model无角色专属模型org_id 多租户隔离
model_name, org_id = await _resolve_llm_context(sor, project_id, 'pm', model_name)
# 暂停门控:项目 paused 时 PM 不推进(不认领审核任务)。默认必须推进,暂停才需要指令。
_precs = await sor.sqlExe(
"SELECT status FROM sd_projects WHERE id=${pid}$", {"pid": project_id})
await sor.sqlExe("COMMIT", {})
if _precs and getattr(_precs[0], 'status', '') == 'paused':
return {"status": "idle", "message": "项目已暂停推进"}
task = await _claim_task(sor, project_id, '', state=TASK_REVIEW, match_role=False, set_state='review')
if not task:
return {"status": "idle", "message": "没有待审核任务"}

View File

@ -31,6 +31,7 @@ TABLE = "sd_projects"
# 项目状态SDLC 默认,状态机语义见 project skill
S_DRAFT = "draft" # 草稿(已创建,未启动)
S_ACTIVE = "active" # 进行中
S_PAUSED = "paused" # 已暂停暂停推进PM 不再推进项目,除非恢复)
S_COMPLETED = "completed" # 已完成
S_ARCHIVED = "archived" # 已归档
@ -135,6 +136,19 @@ async def reopen_project(project_id, who=None, agent_id=None):
who=who, agent_id=agent_id)
async def pause_project(project_id, who=None, agent_id=None):
"""暂停推进active → paused。暂停后 PM 不再推进项目(不认领审核任务、不创建后续任务),
直到 resume_project 恢复仅用户明确指令暂停推进时才调用默认必须推进无需指令"""
return await _transition(project_id, S_ACTIVE, S_PAUSED, 'pause',
who=who, agent_id=agent_id)
async def resume_project(project_id, who=None, agent_id=None):
"""恢复推进paused → active。"""
return await _transition(project_id, S_PAUSED, S_ACTIVE, 'resume',
who=who, agent_id=agent_id)
async def set_project_state(project_id, from_state, to_state,
who=None, agent_id=None, detail=None):
"""通用 CAS 状态迁移兜底(跨产线自定义状态机用)。"""

View File

@ -158,6 +158,8 @@ SDL_TOOLS = [
ToolDefinition(name="complete_project", description="完成项目(active→completed)", parameters={"project_id": "项目ID"}, category="project"),
ToolDefinition(name="archive_project", description="归档项目(completed→archived)", parameters={"project_id": "项目ID"}, category="project"),
ToolDefinition(name="reopen_project", description="重新打开归档项目(archived→active)", parameters={"project_id": "项目ID"}, category="project"),
ToolDefinition(name="pause_project", description="暂停项目推进(active→pausedPM 不再推进,仅用户明确指令暂停时用)", parameters={"project_id": "项目ID"}, category="project"),
ToolDefinition(name="resume_project", description="恢复项目推进(paused→active)", parameters={"project_id": "项目ID"}, category="project"),
ToolDefinition(name="backup_project", description="归档备份项目(打包工作目录+数据记录为tgz到机构工作目录_archive/)", parameters={"project_id": "项目ID"}, category="project"),
ToolDefinition(name="delete_project", description="删除项目(需二次确认confirm=true删除前自动归档备份tgz)", parameters={"project_id": "项目ID", "confirm": "二次确认必须为true"}, category="project"),
# ── 迭代 ──
@ -217,7 +219,7 @@ SDL_PROMPT = """你是「开发产线」的驾驶舱 agent负责软件项目
- 发现卡点任务卡死审核超时失败僵尸 claimed_by主动定位根因并推动修复
- 发现待回答问题> 0 或角色提问时加载 team-communication 技能按冒泡链处理list_questions 列问题 能答则 answer_question 不能答 escalate_question 沿冒泡路径转下一个处理方
- 功能/需求管理 用户提新需求时 propose_feature查功能清单 list_features需求评审/验收时 approve_feature / reject_feature / verify_feature状态机规范见 feature 技能
- 项目管理 list_projects 列项目启动/完成/归档/重开项目用 start_project / complete_project / archive_project / reopen_project状态机规范见 project 技能
- 项目管理 list_projects 列项目启动/完成/归档/重开项目用 start_project / complete_project / archive_project / reopen_project暂停/恢复推进用 pause_project / resume_project仅用户明确指令暂停推进时才 pause默认必须推进状态机规范见 project 技能
- 迭代管理 create_iteration 建迭代list_iterations 列迭代开始/完成/取消迭代用 start_iteration / complete_iteration / cancel_iteration规范见 iteration 技能
- 交付件评审 submit_deliverable 提交list_deliverables 列交付件评审用 approve_deliverable / reject_deliverable规范见 deliverable 技能
- 测试计划 create_test_plan 建计划list_plans 列计划审批/执行/完成用 approve_plan / start_plan / complete_plan规范见 test-plan 技能
@ -973,6 +975,26 @@ async def _h_reopen_project(sor, p, ctx):
return f"OK: {msg}" if ok else f"ERROR: {msg}"
async def _h_pause_project(sor, p, ctx):
pid = (p.get("project_id", "") or "").strip()
if not pid:
return "需要项目ID"
pid = await _resolve_id(sor, "sd_projects", pid) or pid
from .project_capability import pause_project
ok, msg = await pause_project(pid, who="agent.main_agent")
return f"OK: {msg}" if ok else f"ERROR: {msg}"
async def _h_resume_project(sor, p, ctx):
pid = (p.get("project_id", "") or "").strip()
if not pid:
return "需要项目ID"
pid = await _resolve_id(sor, "sd_projects", pid) or pid
from .project_capability import resume_project
ok, msg = await resume_project(pid, who="agent.main_agent")
return f"OK: {msg}" if ok else f"ERROR: {msg}"
async def _h_backup_project(sor, p, ctx):
pid = (p.get("project_id", "") or "").strip()
if not pid:
@ -1498,6 +1520,8 @@ SDL_HANDLERS = {
"complete_project": _h_complete_project,
"archive_project": _h_archive_project,
"reopen_project": _h_reopen_project,
"pause_project": _h_pause_project,
"resume_project": _h_resume_project,
"backup_project": _h_backup_project,
"delete_project": _h_delete_project,
# ── 迭代 ──