fix: develop不deliver三修——①限制探索轮数+强制产出 ②实际产出兜底(检测write_file/git) ③巨型任务拆小(粒度控制)
This commit is contained in:
parent
b341a43ad6
commit
d84f68dd7e
@ -493,6 +493,7 @@ PM_SYSTEM_PROMPT = """你是项目经理(PM)。你的职责是项目计划
|
||||
|
||||
## 任务分解与编排(先评估,再拆解)
|
||||
- 派发前先评估:当前任务是否「过于复杂」(涉及多个模块/应用、多个独立交付单元、工作量超单 agent 一次产出)。简单任务直接派发单个任务,不必强行拆分。
|
||||
- **任务粒度控制(巨型任务拆小)**:单个任务必须聚焦单一交付单元,禁止派发「一次性实现全部 N 个模块 / 全部表契约 / 脚手架 + DDL 全套」这种巨型任务——任务过大时 agent 会因工作量大、方向迷失而陷入探索死循环(反复 read_file/run_shell 却不 write_file/deliver)。按模块/单元拆成多个小任务(每个模块一个 develop 子任务),能并行就并行(不填 depends_on),有依赖就串行(depends_on)。
|
||||
- 复杂任务 → 自动分解:把大任务拆成多个子任务,每个子任务含 title、role、description;子任务默认挂当前里程碑任务名下(parent_id 自动记录,任务树据此分层)。
|
||||
- 自动编排(能并行并行、不能并行串行):
|
||||
- 无依赖的子任务 → 并行(不填 depends_on,系统同时认领执行)。
|
||||
@ -996,6 +997,48 @@ async def _exec_agent_tool(tool, params, workspace_dir):
|
||||
|
||||
# ── 角色 Agent ──
|
||||
|
||||
# 强制产出轮数:前 5 轮允许探索,第 6 轮起 auto-inject 强制产出。
|
||||
# 治「探索死循环」——重做场景 + 复杂 workspace(已有大量文件/git 历史)时 develop 的 LLM 会迷失方向,
|
||||
# 30 轮全耗在 list_files/read_file/run_shell 反复「了解现状 + 验证已有内容」,从不 write_file/deliver。
|
||||
_FORCE_PRODUCE_TURN = 5
|
||||
|
||||
_FORCE_PRODUCE_HINT = (
|
||||
"⚠️ 你已经探索了足够多轮(已超过 5 轮)。现在必须立即产出并交付:\n"
|
||||
"1. 用 write_file 写出实际交付文件(代码/文档/契约/DDL),不要再 read_file / list_files / run_shell / git_status 等探索或检查类工具。\n"
|
||||
"2. 信息不确定就用你的专业判断给出合理结果,先产出再迭代。\n"
|
||||
"3. 写完后立即调用 deliver 提交交付件,禁止再调用任何探索类工具。"
|
||||
)
|
||||
|
||||
|
||||
async def _build_fallback_deliverable(workspace_dir, written_files, repos_dir, role):
|
||||
"""循环结束未 deliver 时,检测 write_file/git 实际产出,构造真实交付件给 QC(而非占位符「Agent未产出交付件」)。"""
|
||||
lines = []
|
||||
if written_files:
|
||||
lines.append("本任务执行期间 write_file 实际写入的文件:")
|
||||
for f in sorted(set(written_files)):
|
||||
rel = os.path.relpath(f, workspace_dir) if f.startswith(workspace_dir) else f
|
||||
lines.append("- " + rel)
|
||||
if os.path.isdir(repos_dir):
|
||||
for repo_name in sorted(os.listdir(repos_dir)):
|
||||
repo_path = os.path.join(repos_dir, repo_name)
|
||||
if os.path.isdir(os.path.join(repo_path, '.git')):
|
||||
try:
|
||||
r = await _run_shell('git status --short', repo_path, 10)
|
||||
out = (r.get('stdout') or '').strip()
|
||||
if out:
|
||||
lines.append(f"git 仓库 [{repo_name}] 工作区变更:")
|
||||
lines.append(out[:2000])
|
||||
except Exception:
|
||||
pass
|
||||
if not lines:
|
||||
lines.append("(本轮未检测到 write_file 或 git 变更——agent 确实未产出)")
|
||||
return {
|
||||
"result": "\n".join(lines),
|
||||
"summary": "Agent 循环结束未调用 deliver,以下为实际产出检测结果(供 QC 判断是否合格)",
|
||||
"deliverable_type": f"{role}_fallback",
|
||||
}
|
||||
|
||||
|
||||
async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
role, _role_specific, _ = await _resolve_role(project_id, role)
|
||||
if role == 'pm':
|
||||
@ -1049,6 +1092,7 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
|
||||
deliverable = None
|
||||
ask_question = None
|
||||
written_files = [] # 本任务执行期间 write_file 实际写入的文件(无 deliver 时的产出兜底)
|
||||
|
||||
# ── Tool Loop(原生 function calling)──
|
||||
for turn in range(30):
|
||||
@ -1064,6 +1108,9 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
if _st and getattr(_st[0], 'state', '') == 'cancelled':
|
||||
logger.info(f"task cancelled mid-run: {task_id}")
|
||||
return {"status": "cancelled", "task_id": task_id}
|
||||
# 强制产出:第 6 轮起注入,打断「了解现状/反复验证」探索死循环,强制转向 write_file + deliver
|
||||
if turn >= _FORCE_PRODUCE_TURN:
|
||||
msgs.append({"role": "user", "content": _FORCE_PRODUCE_HINT})
|
||||
try:
|
||||
resp = await llm_call_msgs_native(msgs, tools=tools_schema, model=model_name, temperature=0.4, org_id=org_id)
|
||||
except Exception as e:
|
||||
@ -1094,6 +1141,8 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
break
|
||||
|
||||
result = await _exec_agent_tool(tool, params, workspace_dir)
|
||||
if tool == "write_file" and params.get("path"):
|
||||
written_files.append(os.path.join(workspace_dir, params["path"]))
|
||||
msgs.append({"role": "tool", "tool_call_id": tc.get("id", ""), "content": str(result)})
|
||||
logger.info(f"role_agent tool_call: {tool} -> {str(result)[:100]}")
|
||||
if deliverable or ask_question:
|
||||
@ -1113,6 +1162,8 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
tool = act.get('tool', '')
|
||||
params = act.get('params', {})
|
||||
result = await _exec_agent_tool(tool, params, workspace_dir)
|
||||
if tool == 'write_file' and params.get('path'):
|
||||
written_files.append(os.path.join(workspace_dir, params['path']))
|
||||
msgs.append({"role": "assistant", "content": raw})
|
||||
msgs.append({"role": "user", "content": f"工具 {tool} 结果:\n{result}"})
|
||||
logger.info(f"role_agent tool_call(text): {tool} -> {str(result)[:100]}")
|
||||
@ -1131,7 +1182,8 @@ async def role_agent_run(project_id, role, agent_id=None, model_name=None):
|
||||
return {"status": "need_info", "task_id": task_id, "question_id": qid, "question": ask_question}
|
||||
|
||||
if not deliverable:
|
||||
deliverable = {"result": "Agent未产出交付件"}
|
||||
# 兜底:循环结束未 deliver,检测 write_file/git 实际产出构造真实交付件(而非占位符)
|
||||
deliverable = await _build_fallback_deliverable(workspace_dir, written_files, repos_dir, role)
|
||||
|
||||
# ── 处理产出 ──
|
||||
from appPublic.uniqueID import getID
|
||||
|
||||
@ -218,7 +218,7 @@ SDL_PROMPT = """你是「开发产线」的驾驶舱 agent,负责软件项目
|
||||
- 用户未提供时,在会话中逐步追问明确(用 ask_user / list_questions),不要跳过;明确后再创建 requirement 任务。
|
||||
|
||||
## 典型场景(帮助判断何时用哪个工具,非强制)
|
||||
- 用户提出新的开发需求/功能("实现XX""XX系统要做XX")→ 先确认部署环境需求,再用 create_task 创建任务(可按 requirement→design→develop 拆分),再 start_agents 启动。
|
||||
- 用户提出新的开发需求/功能("实现XX""XX系统要做XX")→ 先确认部署环境需求,再用 create_task 创建任务(可按 requirement→design→develop 拆分),再 start_agents 启动。**单个任务不要过大**:涉及多个模块/多个独立交付单元时按模块拆小(每个模块一个 develop 任务),禁止一次性派发「实现全部 N 个模块/全部表契约/脚手架+DDL 全套」这种巨型任务。
|
||||
- 用户问进展/状态 → list_tasks / diagnose_project / check_progress。
|
||||
- 用户问待回答问题 → list_questions / answer_question。
|
||||
- 用户报告异常/故障 → 先用 diagnose_project 定位根因,再用 task_detail 查详情。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user