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:
parent
917eb3eda9
commit
c848dc1d4c
@ -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——这样模块可整体复用。
|
||||
|
||||
模块划分遵循: 概念相近的功能组合成一个独立模块,每个模块独立仓库。apppublic、sqlor、ahserver、accounting、appbase、rbac 等基础模块已存在、可直接引用,**不必列为待开发模块**。后续 PM 按你划分好的模块 + 依赖关系派发 develop 任务。
|
||||
完成后用 git_commit_push 提交到项目过程仓库 repos/project/,再用 result 输出文档、files 列出文件路径。""",
|
||||
|
||||
'develop': """你是开发工程师。按模块化原则开发:概念相近的功能组合成一个独立模块,每个模块独立仓库。源码写入模块独立仓库 repos/<模块名>/,不在项目过程仓库。
|
||||
|
||||
基础模块复用(必读): apppublic、sqlor、ahserver、accounting、appbase、rbac 等基础模块已存在、可直接引用,**不必再开发**——需要权限走 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/ 下: Dockerfile、nginx配置、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', '')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user