refactor(agent_loop): 删除 v1 硬编码角色定义,统一走能力包

- 删 ROLE_SPECIFICS(角色 prompt 硬编码,已被 sdlc_ability.SDL_ROLES 完整覆盖)
- 删 ROLE_CHAIN(任务链硬编码,已被 RoleSpec.next_role 覆盖)
- _resolve_role/_get_next_role 去掉 fallback,只走 get_role_spec
- 删死代码 PM_PROMPT/_is_safe_workdir/_parse_result(0 引用)
- 保留 ROLE_ALIASES:服务 _normalize_role 的 who 规范化(27 处调用点依赖),
  与能力包角色 prompt 是不同职责
This commit is contained in:
ymq 2026-08-20 14:37:14 +08:00
parent 917eb3eda9
commit c848dc1d4c

View File

@ -36,15 +36,6 @@ ROLE_ALIASES = {
'pm': 'agent.pm', 'project_manager': 'agent.pm', '项目经理': 'agent.pm', 'manager': 'agent.pm',
}
ROLE_CHAIN = {
'agent.requirement': 'agent.design',
'agent.design': 'agent.develop',
'agent.develop': 'agent.deploy_test',
'agent.deploy_test': 'agent.test',
'agent.test': 'agent.deploy_prod',
'agent.deploy_prod': None,
}
TASK_REVIEW = 'review'
TASK_APPROVED = 'approved'
@ -91,10 +82,7 @@ async def _resolve_pipeline_id(project_id):
async def _resolve_role(project_id, role):
"""从能力包解析角色定义,返回 (normalized_role, role_specific_prompt, next_role)。
能力包未定义该角色时 fallback 到硬编码 ROLE_SPECIFICS/ROLE_CHAIN
"""
"""从能力包解析角色定义,返回 (normalized_role, role_specific_prompt, next_role)。"""
pid = await _resolve_pipeline_id(project_id)
try:
from pipeline_core import get_role_spec
@ -104,7 +92,7 @@ async def _resolve_role(project_id, role):
except Exception:
pass
norm = _normalize_role(role)
return norm, ROLE_SPECIFICS.get(norm, ROLE_SPECIFICS.get('develop', '')), ROLE_CHAIN.get(norm)
return norm, "", ""
async def _resolve_llm_context(sor, project_id, role, model_name=None):
@ -181,16 +169,6 @@ def _resolve_workspace(workspace_dir):
# ── Agent 工具函数 ──
def _is_safe_workdir(workdir):
"""检查目录是否在允许范围内(同步版,仅用模块级白名单兜底)。"""
wd = os.path.abspath(workdir)
for allowed in _ALLOWED_WORKDIRS:
awd = os.path.abspath(os.path.expanduser(allowed))
if wd.startswith(awd):
return True
return False
_allowed_workdirs_cache = None
_allowed_workdirs_cache_time = 0
@ -408,94 +386,6 @@ __TOOLS__
注意每次只输出一个JSON收到工具结果后再决定下一步"""
ROLE_SPECIFICS = {
'requirement': """你是需求分析师。按 SDLC 仓库标准产出文档。
产出路径项目过程仓库 repos/project/ :
- docs/00-requirement/requirement-spec.md
- apps/<应用名>.md应用 = 部署单元至少一个含端口/环境
内容: 项目概述用户角色及权限功能列表(每个功能:输入/处理/输出/验收标准)非功能需求业务流程
注意: 需求阶段只识别应用部署单元**不划分模块**模块划分是架构决策 design 阶段的设计师完成
完成后用 git_commit_push 提交到项目过程仓库 repos/project/再用 result 输出文档files 列出文件路径""",
'design': """你是系统设计师。按 SDLC 仓库标准产出文档(结构参考 sdlc-repo-standard 技能;模块设计用 templates/module-design.md 模板,模块技能用 templates/module-skill.md 模板,保证 develop 读到一致结构)。
应用级设计产出到项目过程仓库 repos/project/docs/01-design/:
- architecture.md: 系统架构技术选型理由模块划分模块间依赖关系开发顺序
- ui-design.md: 视觉风格(配色/字体/间距)主页面布局用户交互模式弹窗/表单规范
模块级设计每个模块自包含可复用产出到 repos/project/modules/:
- modules/<模块名>.md: 模块功能仓库URL(先填规划地址)技术栈依赖开发顺序
- modules/<模块名>/design.md: 该模块的数据设计(表结构DDL) + CRUD 定义 + 处理逻辑(接口)
- modules/<模块名>/skill/SKILL.md: 该模块技能文档
数据CRUD处理逻辑skills 都属于模块本身不放在应用级 design这样模块可整体复用
模块划分遵循: 概念相近的功能组合成一个独立模块每个模块独立仓库apppublicsqlorahserveraccountingappbaserbac 等基础模块已存在可直接引用**不必列为待开发模块**后续 PM 按你划分好的模块 + 依赖关系派发 develop 任务
完成后用 git_commit_push 提交到项目过程仓库 repos/project/再用 result 输出文档files 列出文件路径""",
'develop': """你是开发工程师。按模块化原则开发:概念相近的功能组合成一个独立模块,每个模块独立仓库。源码写入模块独立仓库 repos/<模块名>/,不在项目过程仓库。
基础模块复用必读: apppublicsqlorahserveraccountingappbaserbac 等基础模块已存在可直接引用**不必再开发**需要权限走 rbac需要数据访问走 sqlor需要 HTTP 框架走 ahserver需要配置走 appbase需要工具函数走 apppublic
准备工作:
1. read_file repos/project/docs/01-design/ 下架构与 UI 设计文档
2. read_file repos/project/modules/<模块名>.md 获取模块仓库 URL repos/project/modules/<模块名>/design.md 获取数据设计/CRUD/处理逻辑
3. git_clone 克隆模块仓库到 repos/
开发流程:
4. write_file 写代码到模块仓库目录
5. run_shell 编译/运行验证
6. git_commit_push 提交模块仓库 "develop: <简述>"
7. write_file 更新 repos/project/modules/<模块名>.md 状态为已完成
8. write_file repos/project/docs/02-develop/dev-notes.md 记录开发内容 git_commit_push 提交项目过程仓库
9. deliver 交付files 列出所有产出文件路径
PM审核: git_status 检查模块仓库有提交记录""",
'test': """你是测试工程师。按 SDLC 仓库标准产出文档。
产出路径项目过程仓库 repos/project/docs/03-test/:
- test-plan.md: 测试策略(单元/集成/端到端)
- test-cases.md: 用例清单(编号/前置条件/步骤/预期结果)
- test-report.md: 执行结果Bug清单覆盖率
完成后用 git_commit_push 提交到项目过程仓库 repos/project/""",
'deploy': """你是部署运维工程师。按 SDLC 仓库标准产出文档和配置。
产出路径项目过程仓库 repos/project/ :
- docs/04-deploy/deploy-guide.md: 环境要求部署步骤回滚方案
- docs/04-deploy/release-notes.md: 发布说明
- config/ : Dockerfilenginx配置docker-compose.yml
完成后用 git_commit_push 提交到项目过程仓库 repos/project/再用 result 输出部署说明files 列出配置文件路径""",
}
PM_PROMPT = """你是项目经理PM。审核交付件并推动项目前进。
## 项目
工作目录__WORKSPACE__
关联仓库__REPOS__
## 待审核任务
标题__TITLE__
角色__ROLE__
交付件摘要__SUMMARY__
交付件内容(前6000字)__DELIVERABLE__
## 仓库状态
__REPO_STATE__
## 决策
输出纯JSON
1. 通过{"status":"approved","comment":"审核意见","next_task_title":"下阶段标题","next_task_description":"详细描述"}
2. 驳回{"status":"rejected","comment":"驳回原因","questions":"修改要求"}
3. 完成{"status":"completed","comment":"项目总结"}
审核标准
- requirement需求是否清晰完整可量化
- design方案合理覆盖需求技术可行
- develop代码文件是否实际产出目录结构是否合理是否可编译运行
- test测试覆盖充分发现问题记录完整
- deploy部署配置完整可一键部署"""
PM_SYSTEM_PROMPT = """你是项目经理PM。你的职责是项目计划、任务分配、任务验收。
## 项目推进铁律(最高优先级)
@ -700,19 +590,6 @@ async def _build_qna_section(sor, task_id, role, agent_id=None):
return "\n".join(lines)
def _parse_result(raw):
raw = (raw or "").strip()
if raw.startswith("```"):
raw = raw.split("\n", 1)[1].rsplit("```", 1)[0]
try:
d = json.loads(raw)
if isinstance(d, dict):
return d
except (json.JSONDecodeError, ValueError):
pass
return {"status": "done", "result": raw}
async def _get_deliverable_content(sor, task_id):
recs = await sor.sqlExe(
"SELECT content, deliverable_type FROM pipeline_deliverables "
@ -791,7 +668,7 @@ async def _get_repo_state(workspace_dir):
async def _get_next_role(current_role, project_id=""):
"""任务链下一角色:优先从能力包取fallback 硬编码 ROLE_CHAIN"""
"""任务链下一角色:从能力包取。"""
if project_id:
try:
pid = await _resolve_pipeline_id(project_id)
@ -801,7 +678,7 @@ async def _get_next_role(current_role, project_id=""):
return spec.next_role # 可能是 ""(终结)
except Exception:
pass
return ROLE_CHAIN.get(_normalize_role(current_role))
return ""
async def _create_next_task(sor, project_id, task, next_role, pm_comment=''):
@ -1615,7 +1492,13 @@ async def pm_review_run(project_id, agent_id=None, model_name=None):
elif tool in ('cancel_task', 'cancel'):
result = await _pm_cancel_task(sor, project_id, params)
else:
result = await _exec_agent_tool(tool, params, workspace_dir)
if turn >= 3:
# 硬约束最后两轮拒绝执行探索类工具read_file/git_status/list_files/run_shell 等),
# 强制 PM 立即输出决策——否则 deepseek 无视软提示持续检查、5 轮耗尽 → 审核超时。
result = (f"已到最后收尾阶段(第 {turn + 1}/5 轮),拒绝执行探索类工具 {tool}"
f"请立即输出 review_approve / review_reject / review_complete / review_rollback 之一,不要再调用工具。")
else:
result = await _exec_agent_tool(tool, params, workspace_dir)
msgs.append({"role": "assistant", "content": raw})
msgs.append({"role": "user", "content": f"工具 {tool} 结果:\n{result}"})
else:
@ -1630,7 +1513,17 @@ async def pm_review_run(project_id, agent_id=None, model_name=None):
continue
if not decision:
decision = {'status': 'rejected', 'comment': '审核超时'}
# 超时智能处理PM 超时前可能已 create_tasks 派发后续子任务parent_id 指向本任务),
# 说明 PM 实质已认可本任务approve 行为)。此时默认 approved 而非 rejected——
# 否则 design 被无意义的「审核超时」驳回 → 重做写占位文件 → QC 死循环2026-08 实测)。
child_recs = await sor.sqlExe(
"SELECT COUNT(*) as c FROM pipeline_tasks WHERE parent_id=${tid}$", {"tid": task_id})
await sor.sqlExe("COMMIT", {})
child_cnt = getattr(child_recs[0], 'c', 0) if child_recs else 0
if child_cnt > 0:
decision = {'status': 'approved', 'comment': '审核超时,但 PM 已派发后续任务create_tasks视为认可通过'}
else:
decision = {'status': 'rejected', 'comment': '审核超时'}
status = decision['status']
comment = decision.get('comment', '')