From c30cabe2a1a187cc940ca730b0659506274228df Mon Sep 17 00:00:00 2001 From: huangzhuang_3rd Date: Thu, 13 Aug 2026 00:46:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20V1=20-=20DAG=E7=BC=96=E6=8E=92/?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E7=B3=BB=E7=BB=9F/Agent=E5=BE=AA=E7=8E=AF/?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93RAG/Webhook=20+=20=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=AF=8C=E4=B8=8A=E4=B8=8B=E6=96=87=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/services/execution_service.py | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/backend/app/services/execution_service.py b/backend/app/services/execution_service.py index 8b70e75..2fbb29d 100644 --- a/backend/app/services/execution_service.py +++ b/backend/app/services/execution_service.py @@ -73,6 +73,13 @@ class ExecutionService: messages.append({"role": "user", "content": prompt}) try: + # Branch by task type + if task.task_type == "agent_loop": + return ExecutionService._execute_agent_loop( + db, tenant_id, task, worker, messages + ) + + # Single-call mode output, cost_log = LLMGateway.call( worker=worker, messages=messages, @@ -141,3 +148,75 @@ class ExecutionService: finally: worker.current_task_count = max(0, worker.current_task_count - 1) db.commit() + + @staticmethod + def _execute_agent_loop(db, tenant_id, task, worker, messages): + """Agent loop: multi-step reasoning with up to 5 iterations.""" + max_iterations = 5 + all_messages = list(messages) + final_output = "" + total_cost_cents = 0 + total_tokens = 0 + total_duration = 0 + + for i in range(max_iterations): + output, cost_log = LLMGateway.call( + worker=worker, + messages=all_messages, + db=db, + tenant_id=tenant_id, + project_id=task.project_id, + task_id=task.id, + ) + total_cost_cents += cost_log.cost_cents + total_tokens += cost_log.total_tokens + total_duration += cost_log.duration_ms + + # Check if agent wants to continue + if output.strip().startswith("NEEDS_TOOL:"): + tool_request = output.replace("NEEDS_TOOL:", "").strip() + tool_result = f"[Tool simulation] Processed: {tool_request}" + all_messages.append({"role": "assistant", "content": output}) + all_messages.append({"role": "user", "content": f"Tool result: {tool_result}\nPlease continue."}) + final_output = output + continue + else: + final_output = output + break + + # Save output + task.output_data = json.dumps( + {"content": final_output, "iterations": i + 1, "total_cost": total_cost_cents}, + ensure_ascii=False, + ) + + artifact = Artifact( + tenant_id=tenant_id, task_id=task.id, project_id=task.project_id, + title=task.title, content=final_output, artifact_type="text", + version=1, created_by_worker_id=worker.id, + ) + db.add(artifact) + db.flush() + + if task.requires_review: + review = Review( + tenant_id=tenant_id, task_id=task.id, project_id=task.project_id, + review_type="approval", status="pending", + review_content=final_output, submitted_at=datetime.utcnow(), + ) + db.add(review) + task.status = "review" + task.review_status = "pending" + result_status = "needs_review" + else: + task.status = "done" + task.completed_at = datetime.utcnow() + result_status = "success" + + db.commit() + + return TaskExecutionResult( + task_id=task.id, status=result_status, output=final_output, + token_usage=total_tokens, cost_cents=total_cost_cents, + duration_ms=total_duration, artifact_id=artifact.id, + )